using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Timers; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BloodCraftHub.Behaviors; using BloodCraftHub.Config; using BloodCraftHub.Resources; using BloodCraftHub.Services; using BloodCraftHub.UI; using BloodCraftHub.UI.Forms; using BloodCraftHub.UI.Framework.CustomLib; using BloodCraftHub.UI.Framework.CustomLib.Controls; using BloodCraftHub.UI.Framework.CustomLib.Panel; using BloodCraftHub.UI.Framework.CustomLib.Util; using BloodCraftHub.UI.Framework.ModernLib; using BloodCraftHub.UI.Framework.UniverseLib.UI; using BloodCraftHub.UI.Framework.UniverseLib.UI.Models; using BloodCraftHub.UI.Framework.UniverseLib.UI.ObjectPool; using BloodCraftHub.UI.Framework.UniverseLib.UI.Panels; using BloodCraftHub.UI.Framework.UniverseLib.UI.Widgets; using BloodCraftHub.UI.Framework.UniverseLib.UI.Widgets.ScrollView; using BloodCraftHub.UI.ModContent; using BloodCraftHub.UI.ModContent.Data; using BloodCraftHub.Utils; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppSystem; using Il2CppSystem.Collections; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using ProjectM; using ProjectM.Network; using ProjectM.Scripting; using ProjectM.UI; using Stunlock.Core; using TMPro; using Unity.Collections; using Unity.Entities; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; 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("kdpen")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Unified client UI for V Rising's Bloodcraft mod. Surfaces every Bloodcraft, KindredCommands, and KindredLogistics chat command as buttons + forms.")] [assembly: AssemblyFileVersion("0.14.0.0")] [assembly: AssemblyInformationalVersion("0.14.0+b0b839b6e4b535a43e18461164d7b185266c4a63")] [assembly: AssemblyProduct("BloodCraftHub")] [assembly: AssemblyTitle("BloodCraftHub")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.14.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 BloodCraftHub { internal static class Core { private static World _client; public static World ClientWorld => _client; public static EntityManager EntityManager => _client.EntityManager; public static ManualLogSource Log => Plugin.LogInstance; public static bool HasInitialized { get; private set; } public static Entity LocalCharacter { get; set; } = Entity.Null; public static Entity LocalUser { get; set; } = Entity.Null; public static byte[] SharedKey { get; set; } public static void Initialize(World clientWorld) { if (!HasInitialized) { _client = clientWorld; HasInitialized = true; Log.LogInfo((object)"Core initialized on client world."); } } public static void Reset() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) _client = null; LocalCharacter = Entity.Null; LocalUser = Entity.Null; SharedKey = null; HasInitialized = false; } } [BepInProcess("VRising.exe")] [BepInPlugin("BloodCraftHub", "BloodCraftHub", "0.14.0")] public class Plugin : BasePlugin { public const bool IS_TESTING = false; private static World _client; private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource LogInstance => ((BasePlugin)Instance).Log; public static Settings Settings { get; private set; } public static BCHubUIManager UIManager { get; private set; } public static CoreUpdateBehavior CoreUpdateBehavior { get; private set; } public static bool IsClient { get; private set; } public static bool IsInitialized { get; private set; } public static bool IsGameDataInitialized { get; set; } public static EntityManager EntityManager => _client.EntityManager; public static Entity LocalCharacter { get; set; } = Entity.Null; public static bool IsClientNull() { return _client == null; } public override void Load() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Expected O, but got Unknown Instance = this; IsClient = Application.productName != "VRisingServer"; LogUtils.Init(((BasePlugin)this).Log); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val; if (!IsClient) { ManualLogSource log = ((BasePlugin)this).Log; val = new BepInExInfoLogInterpolatedStringHandler(45, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("BloodCraftHub"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("0.14.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] is a client mod — not loading on server ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Application.productName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } log.LogInfo(val); return; } Settings = new Settings().InitConfig(); Theme.UIFontMultiplier = Settings.UITextScale; Theme.OverlayFontMultiplier = Settings.OverlayTextScale; EclipseProtocolService.Initialize(); UIManager = new BCHubUIManager(); CoreUpdateBehavior = new CoreUpdateBehavior(); CoreUpdateBehavior.Setup(); CoreUpdateBehavior.Actions.Add(MessageService.ProcessAllMessages); CoreUpdateBehavior.Actions.Add(MessageService.TickInterceptTimeouts); CoreUpdateBehavior.Actions.Add(VBloodScannerService.Tick); CoreUpdateBehavior.Actions.Add(ShiftCooldownService.Tick); CoreUpdateBehavior.Actions.Add(TooltipHover.TickAll); CoreUpdateBehavior.Actions.Add(FormDropdownRegistry.TickCloseOnOutsideClick); CoreUpdateBehavior.Actions.Add(SliderClickRegistry.TickClickOnTrack); _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "BloodCraftHub"); IsInitialized = true; ManualLogSource log2 = ((BasePlugin)this).Log; val = new BepInExInfoLogInterpolatedStringHandler(17, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("BloodCraftHub"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("0.14.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded."); } log2.LogInfo(val); } public override bool Unload() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } return true; } public static void UIOnInitialize() { if (!UIManager.IsInitialized) { UIManager.SetupAndShowUI(); UIManager.RestoreOverlaysFromSettings(); VBloodScannerService.Initialize(); LogUtils.LogInfo("UI Manager initialized."); } } public static void GameDataOnInitialize(World world) { if (!IsGameDataInitialized && IsClient) { _client = world; IsGameDataInitialized = true; LogUtils.LogInfo("Client world bound; game data initialized."); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "BloodCraftHub"; public const string PLUGIN_NAME = "BloodCraftHub"; public const string PLUGIN_VERSION = "0.14.0"; } } namespace BloodCraftHub.Utils { public static class Extensions { private static EntityManager EntityManager => Plugin.EntityManager; public static Color GetTransparent(this Color baseColor, float alpha = 0.7f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return new Color(baseColor.r, baseColor.g, baseColor.b, alpha); } public static bool Has(this Entity entity) { //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_000f: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = EntityManager; return ((EntityManager)(ref entityManager)).HasComponent(entity, new ComponentType(Il2CppType.Of(), (AccessMode)0)); } public unsafe static T Read(this Entity entity) where T : struct { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) TypeIndex typeIndex = new ComponentType(Il2CppType.Of(), (AccessMode)0).TypeIndex; EntityManager entityManager = EntityManager; return Marshal.PtrToStructure(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, typeIndex))); } public unsafe static void Write(this Entity entity, T componentData) where T : struct { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) TypeIndex typeIndex = new ComponentType(Il2CppType.Of(), (AccessMode)0).TypeIndex; byte[] array = StructureToByteArray(componentData); int num = Marshal.SizeOf(); fixed (byte* ptr = array) { EntityManager entityManager = EntityManager; ((EntityManager)(ref entityManager)).SetComponentDataRaw(entity, typeIndex, (void*)ptr, num); } } public static bool HasValue(this Entity entity) { //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) return entity != Entity.Null; } private static byte[] StructureToByteArray(T structure) where T : struct { int num = Marshal.SizeOf(structure); byte[] array = new byte[num]; IntPtr intPtr = Marshal.AllocHGlobal(num); try { Marshal.StructureToPtr(structure, intPtr, fDeleteOld: true); Marshal.Copy(intPtr, array, 0, num); return array; } finally { Marshal.FreeHGlobal(intPtr); } } } public static class LogUtils { private static ManualLogSource _log; public static void Init(ManualLogSource log) { _log = log; } public static void LogInfo(object msg) { ManualLogSource log = _log; if (log != null) { log.LogInfo(msg); } } public static void LogWarning(object msg) { ManualLogSource log = _log; if (log != null) { log.LogWarning(msg); } } public static void LogError(object msg) { ManualLogSource log = _log; if (log != null) { log.LogError(msg); } } public static void LogDebug(object msg) { ManualLogSource log = _log; if (log != null) { log.LogDebug(msg); } } } } namespace BloodCraftHub.UI { public class BCHubUIManager : UIManagerBase { private readonly List _panels = new List(); private FloatingButtonPanel _floatingButton; private MainPanel _mainPanel; private ExperienceOverlayPanel _experienceOverlay; private FamiliarOverlayPanel _familiarOverlay; private FamiliarBrowserOverlayPanel _familiarBrowserOverlay; private DailyQuestOverlayPanel _dailyQuestOverlay; private ProfessionOverlayPanel _professionOverlay; private ShiftSpellOverlayPanel _shiftSpellOverlay; private CombinedOverlayPanel _combinedOverlay; private bool _overlaysSuppressed; private Action _deferredMainPanelRebuild; private Action _deferredOverlayRebuild; public bool IsMainPanelOpen { get { if (_mainPanel != null) { return _mainPanel.Enabled; } return false; } } public MainPanel MainPanel => _mainPanel; public ExperienceOverlayPanel ExperienceOverlay => _experienceOverlay; public FamiliarOverlayPanel FamiliarOverlay => _familiarOverlay; public FamiliarBrowserOverlayPanel FamiliarBrowserOverlay => _familiarBrowserOverlay; public DailyQuestOverlayPanel DailyQuestOverlay => _dailyQuestOverlay; public ProfessionOverlayPanel ProfessionOverlay => _professionOverlay; public ShiftSpellOverlayPanel ShiftSpellOverlay => _shiftSpellOverlay; public CombinedOverlayPanel CombinedOverlay => _combinedOverlay; public bool AreOverlaysSuppressed => _overlaysSuppressed; public void ApplyOverlayLockState() { bool lockOverlays = Settings.LockOverlays; ApplyPinnedTo(_experienceOverlay, lockOverlays); ApplyPinnedTo(_familiarOverlay, lockOverlays); ApplyPinnedTo(_familiarBrowserOverlay, lockOverlays); ApplyPinnedTo(_dailyQuestOverlay, lockOverlays); ApplyPinnedTo(_professionOverlay, lockOverlays); ApplyPinnedTo(_shiftSpellOverlay, lockOverlays); ApplyPinnedTo(_combinedOverlay, lockOverlays); } private static void ApplyPinnedTo(ResizeablePanelBase panel, bool pinned) { if (panel != null) { panel.IsPinned = pinned; } } public override void Reset() { base.Reset(); foreach (IPanelBase panel in _panels) { if (panel is ResizeablePanelBase resizeablePanelBase) { resizeablePanelBase.Reset(); } panel.Destroy(); } _panels.Clear(); _floatingButton = null; _mainPanel = null; _experienceOverlay = null; _familiarOverlay = null; _familiarBrowserOverlay = null; _dailyQuestOverlay = null; _professionOverlay = null; _combinedOverlay = null; _shiftSpellOverlay = null; } protected override void AddMainContentPanel() { _floatingButton = new FloatingButtonPanel(base.UiBase); _panels.Add(_floatingButton); } public override void SetActive(bool active) { _floatingButton?.SetActive(active); _mainPanel?.SetActive(active && IsMainPanelOpen); _experienceOverlay?.SetActive(active && (_experienceOverlay?.Enabled ?? false)); _familiarOverlay?.SetActive(active && (_familiarOverlay?.Enabled ?? false)); _familiarBrowserOverlay?.SetActive(active && (_familiarBrowserOverlay?.Enabled ?? false)); _dailyQuestOverlay?.SetActive(active && (_dailyQuestOverlay?.Enabled ?? false)); _professionOverlay?.SetActive(active && (_professionOverlay?.Enabled ?? false)); _shiftSpellOverlay?.SetActive(active && (_shiftSpellOverlay?.Enabled ?? false)); } public void ToggleMainPanel() { EnsureMainPanel(); _mainPanel.SetActive(!_mainPanel.Enabled); } public void ShowTab(PanelType tab) { EnsureMainPanel(); _mainPanel.SetActive(active: true); _mainPanel.ShowTab(tab); } public void ToggleOverlay(PanelType overlay) { switch (overlay) { case PanelType.ExperienceOverlay: EnsureExperienceOverlay(); _experienceOverlay.SetActive(!_experienceOverlay.Enabled); Settings.SetShowExperienceOverlay(_experienceOverlay.Enabled); break; case PanelType.FamiliarOverlay: EnsureFamiliarOverlay(); _familiarOverlay.SetActive(!_familiarOverlay.Enabled); Settings.SetShowFamiliarOverlay(_familiarOverlay.Enabled); break; case PanelType.FamiliarBrowserOverlay: EnsureFamiliarBrowserOverlay(); _familiarBrowserOverlay.SetActive(!_familiarBrowserOverlay.Enabled); Settings.SetShowFamiliarBrowser(_familiarBrowserOverlay.Enabled); break; case PanelType.DailyQuestOverlay: EnsureDailyQuestOverlay(); _dailyQuestOverlay.SetActive(!_dailyQuestOverlay.Enabled); Settings.SetShowDailyQuestOverlay(_dailyQuestOverlay.Enabled); break; case PanelType.ProfessionOverlay: EnsureProfessionOverlay(); _professionOverlay.SetActive(!_professionOverlay.Enabled); Settings.SetShowProfessionOverlay(_professionOverlay.Enabled); break; case PanelType.ShiftSpellOverlay: EnsureShiftSpellOverlay(); _shiftSpellOverlay.SetActive(!_shiftSpellOverlay.Enabled); Settings.SetShowShiftSpellOverlay(_shiftSpellOverlay.Enabled); break; case PanelType.CombinedOverlay: Settings.SetShowCombinedOverlay(!(_combinedOverlay?.Enabled ?? false)); ApplyCombinedOverlayMutualExclusion(); break; default: throw new ArgumentOutOfRangeException("overlay", overlay, "Not a secondary overlay."); } } public void ApplyCombinedOverlayMutualExclusion() { if (Settings.ShowCombinedOverlay) { EnsureCombinedOverlay(); _combinedOverlay.SetActive(active: true); EnsureExperienceOverlay(); _experienceOverlay.SetActive(active: false); _familiarOverlay?.SetActive(active: false); _dailyQuestOverlay?.SetActive(active: false); _professionOverlay?.SetActive(active: false); return; } _combinedOverlay?.SetActive(active: false); if (Settings.ShowExperienceOverlay) { EnsureExperienceOverlay(); _experienceOverlay.SetActive(active: true); } if (Settings.ShowFamiliarOverlay) { EnsureFamiliarOverlay(); _familiarOverlay.SetActive(active: true); } if (Settings.ShowDailyQuestOverlay) { EnsureDailyQuestOverlay(); _dailyQuestOverlay.SetActive(active: true); } if (Settings.ShowProfessionOverlay) { EnsureProfessionOverlay(); _professionOverlay.SetActive(active: true); } } public void RefreshCombinedOverlaySections() { _combinedOverlay?.RefreshSections(); } public void ToggleAllOverlaysSuppressed() { _overlaysSuppressed = !_overlaysSuppressed; Settings.OverlaysSuppressedByUser = _overlaysSuppressed; ApplyOverlaySuppression(); } private void ApplyOverlaySuppression() { if (_overlaysSuppressed) { _experienceOverlay?.SetActive(active: false); _familiarOverlay?.SetActive(active: false); _familiarBrowserOverlay?.SetActive(active: false); _dailyQuestOverlay?.SetActive(active: false); _professionOverlay?.SetActive(active: false); _shiftSpellOverlay?.SetActive(active: false); _combinedOverlay?.SetActive(active: false); return; } if (Settings.ShowExperienceOverlay) { EnsureExperienceOverlay(); _experienceOverlay.SetActive(active: true); } if (Settings.ShowFamiliarOverlay) { EnsureFamiliarOverlay(); _familiarOverlay.SetActive(active: true); } if (Settings.ShowFamiliarBrowser) { EnsureFamiliarBrowserOverlay(); _familiarBrowserOverlay.SetActive(active: true); } if (Settings.ShowDailyQuestOverlay) { EnsureDailyQuestOverlay(); _dailyQuestOverlay.SetActive(active: true); } if (Settings.ShowProfessionOverlay) { EnsureProfessionOverlay(); _professionOverlay.SetActive(active: true); } if (Settings.ShowShiftSpellOverlay) { EnsureShiftSpellOverlay(); _shiftSpellOverlay.SetActive(active: true); } } public void RestoreOverlaysFromSettings() { if (Settings.ShowCombinedOverlay) { ApplyCombinedOverlayMutualExclusion(); } else { if (Settings.ShowExperienceOverlay) { EnsureExperienceOverlay(); _experienceOverlay.SetActive(active: true); } if (Settings.ShowFamiliarOverlay) { EnsureFamiliarOverlay(); _familiarOverlay.SetActive(active: true); } if (Settings.ShowDailyQuestOverlay) { EnsureDailyQuestOverlay(); _dailyQuestOverlay.SetActive(active: true); } if (Settings.ShowProfessionOverlay) { EnsureProfessionOverlay(); _professionOverlay.SetActive(active: true); } } if (Settings.ShowFamiliarBrowser) { EnsureFamiliarBrowserOverlay(); _familiarBrowserOverlay.SetActive(active: true); } if (Settings.ShowShiftSpellOverlay) { EnsureShiftSpellOverlay(); _shiftSpellOverlay.SetActive(active: true); } if (Settings.ShowCombinedOverlay) { ApplyCombinedOverlayMutualExclusion(); } } public bool IsOverlayOpen(PanelType overlay) { return overlay switch { PanelType.ExperienceOverlay => _experienceOverlay?.Enabled ?? false, PanelType.FamiliarOverlay => _familiarOverlay?.Enabled ?? false, PanelType.FamiliarBrowserOverlay => _familiarBrowserOverlay?.Enabled ?? false, PanelType.DailyQuestOverlay => _dailyQuestOverlay?.Enabled ?? false, PanelType.ProfessionOverlay => _professionOverlay?.Enabled ?? false, PanelType.ShiftSpellOverlay => _shiftSpellOverlay?.Enabled ?? false, PanelType.CombinedOverlay => _combinedOverlay?.Enabled ?? false, _ => false, }; } private void EnsureMainPanel() { if (_mainPanel == null) { _mainPanel = new MainPanel(base.UiBase); _panels.Add(_mainPanel); _mainPanel.SetActive(active: false); } } private void EnsureExperienceOverlay() { if (_experienceOverlay == null) { _experienceOverlay = new ExperienceOverlayPanel(base.UiBase); _panels.Add(_experienceOverlay); _experienceOverlay.SetActive(active: false); } } private void EnsureFamiliarBrowserOverlay() { if (_familiarBrowserOverlay == null) { _familiarBrowserOverlay = new FamiliarBrowserOverlayPanel(base.UiBase); _panels.Add(_familiarBrowserOverlay); _familiarBrowserOverlay.SetActive(active: false); } } private void EnsureDailyQuestOverlay() { if (_dailyQuestOverlay == null) { _dailyQuestOverlay = new DailyQuestOverlayPanel(base.UiBase); _panels.Add(_dailyQuestOverlay); _dailyQuestOverlay.SetActive(active: false); } } private void EnsureFamiliarOverlay() { if (_familiarOverlay == null) { _familiarOverlay = new FamiliarOverlayPanel(base.UiBase); _panels.Add(_familiarOverlay); _familiarOverlay.SetActive(active: false); } } private void EnsureProfessionOverlay() { if (_professionOverlay == null) { _professionOverlay = new ProfessionOverlayPanel(base.UiBase); _panels.Add(_professionOverlay); _professionOverlay.SetActive(active: false); } } private void EnsureShiftSpellOverlay() { if (_shiftSpellOverlay == null) { _shiftSpellOverlay = new ShiftSpellOverlayPanel(base.UiBase); _panels.Add(_shiftSpellOverlay); _shiftSpellOverlay.SetActive(active: false); } } private void EnsureCombinedOverlay() { if (_combinedOverlay == null) { _combinedOverlay = new CombinedOverlayPanel(base.UiBase); _panels.Add(_combinedOverlay); _combinedOverlay.SetActive(active: false); } } public void RefreshAllOpacities() { _experienceOverlay?.RefreshOpacity(); _familiarOverlay?.RefreshOpacity(); _familiarBrowserOverlay?.RefreshOpacity(); _dailyQuestOverlay?.RefreshOpacity(); _professionOverlay?.RefreshOpacity(); _shiftSpellOverlay?.RefreshOpacity(); _combinedOverlay?.RefreshOpacity(); _mainPanel?.RefreshOpacity(); _floatingButton?.RefreshOpacity(); } public void RefreshAllPanelBackgrounds() { _mainPanel?.RefreshBackgroundColor(); _experienceOverlay?.RefreshBackgroundColor(); _familiarOverlay?.RefreshBackgroundColor(); _familiarBrowserOverlay?.RefreshBackgroundColor(); _dailyQuestOverlay?.RefreshBackgroundColor(); _professionOverlay?.RefreshBackgroundColor(); _shiftSpellOverlay?.RefreshBackgroundColor(); _combinedOverlay?.RefreshBackgroundColor(); } public void RefreshScopedInnerBackgrounds() { _mainPanel?.RefreshInnerBackgroundColor(); _familiarBrowserOverlay?.RefreshInnerBackgroundColor(); } public void RefreshProfessionOverlay() { _professionOverlay?.Refresh(); } public void RequestRebuildMainPanel() { if (_mainPanel != null) { CoreUpdateBehavior.Actions.Add(delegate { CoreUpdateBehavior.Actions.Remove(_deferredMainPanelRebuild); _deferredMainPanelRebuild = null; RebuildMainPanelNow(); }); } } private void RebuildMainPanelNow() { if (_mainPanel != null) { bool enabled = _mainPanel.Enabled; PanelType activeTab = _mainPanel.ActiveTab; _mainPanel?.Reset(); _mainPanel.Destroy(); _panels.Remove(_mainPanel); _mainPanel = null; if (enabled) { EnsureMainPanel(); _mainPanel.SetActive(active: true); _mainPanel.ShowTab(activeTab); } } } public void RequestRebuildAllOverlays() { CoreUpdateBehavior.Actions.Add(delegate { CoreUpdateBehavior.Actions.Remove(_deferredOverlayRebuild); _deferredOverlayRebuild = null; RebuildAllOverlaysNow(); }); } private void RebuildAllOverlaysNow() { bool showCombinedOverlay = Settings.ShowCombinedOverlay; RebuildOverlay(ref _experienceOverlay, !showCombinedOverlay && Settings.ShowExperienceOverlay, (UIBase b) => new ExperienceOverlayPanel(b)); RebuildOverlay(ref _familiarOverlay, !showCombinedOverlay && Settings.ShowFamiliarOverlay, (UIBase b) => new FamiliarOverlayPanel(b)); RebuildOverlay(ref _familiarBrowserOverlay, Settings.ShowFamiliarBrowser, (UIBase b) => new FamiliarBrowserOverlayPanel(b)); RebuildOverlay(ref _dailyQuestOverlay, !showCombinedOverlay && Settings.ShowDailyQuestOverlay, (UIBase b) => new DailyQuestOverlayPanel(b)); RebuildOverlay(ref _professionOverlay, !showCombinedOverlay && Settings.ShowProfessionOverlay, (UIBase b) => new ProfessionOverlayPanel(b)); RebuildOverlay(ref _shiftSpellOverlay, Settings.ShowShiftSpellOverlay, (UIBase b) => new ShiftSpellOverlayPanel(b)); RebuildOverlay(ref _combinedOverlay, showCombinedOverlay, (UIBase b) => new CombinedOverlayPanel(b)); _mainPanel?.RefreshAllOverlayToggleStates(); } private void RebuildOverlay(ref T slot, bool wasVisibleByConfig, Func factory) where T : ResizeablePanelBase { if (slot != null) { slot.Reset(); slot.Destroy(); _panels.Remove(slot); slot = null; if (wasVisibleByConfig) { T val = factory(base.UiBase); _panels.Add(val); val.SetActive(active: true); slot = val; } } } } public static class TooltipHover { private struct Binding { public RectTransform Rt; public string Text; } private static readonly List _bindings = new List(); public static TextMeshProUGUI Sink; public static string IdlePlaceholder = "Hint: hover any control for help."; private static bool _firstHitLogged; public static int BindingCount => _bindings.Count; public static void Attach(GameObject target, string text) { if (!((Object)(object)target == (Object)null) && !string.IsNullOrEmpty(text)) { RectTransform component = target.GetComponent(); if (!((Object)(object)component == (Object)null)) { _bindings.Add(new Binding { Rt = component, Text = text }); } } } public static void TickAll() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Sink == (Object)null) { return; } Vector2 val = Vector2.op_Implicit(Input.mousePosition); string text = null; for (int num = _bindings.Count - 1; num >= 0; num--) { Binding binding = _bindings[num]; if ((Object)(object)binding.Rt == (Object)null) { _bindings.RemoveAt(num); } else if (((Component)binding.Rt).gameObject.activeInHierarchy) { Canvas componentInParent = ((Component)binding.Rt).GetComponentInParent(); Camera val2 = null; if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) { val2 = componentInParent.worldCamera; } if (RectTransformUtility.RectangleContainsScreenPoint(binding.Rt, val, val2)) { text = binding.Text; } } } ((TMP_Text)Sink).text = text ?? IdlePlaceholder; if (text == null || _firstHitLogged) { return; } _firstHitLogged = true; try { LogUtils.LogInfo($"TooltipHover: first hover registered ({_bindings.Count} bindings tracked)."); } catch { } } } } namespace BloodCraftHub.UI.ModContent { public class CombinedOverlayPanel : ResizeablePanelBase { private static readonly Color COL_XP = new Color(0.55f, 0.95f, 0.55f, 1f); private static readonly Color COL_FAMILIAR = new Color(1f, 0.78f, 0.3f, 1f); private static readonly Color COL_EXPERTISE = new Color(0.85f, 0.85f, 0.85f, 1f); private static readonly Color COL_LEGACY = new Color(1f, 0.45f, 0.45f, 1f); private static readonly Color COL_PROFS = new Color(0.95f, 0.75f, 0.35f, 1f); private static readonly Color COL_QUEST_D = new Color(0f, 1f, 1f, 1f); private static readonly Color COL_QUEST_W = new Color(1f, 0.85f, 0.3f, 1f); private GameObject _xpSection; private GameObject _familiarSection; private GameObject _expertiseSection; private GameObject _legacySection; private GameObject _profSection; private GameObject _questSection; private TextMeshProUGUI _xpLine; private TextMeshProUGUI _famNameLine; private TextMeshProUGUI _famStatsLine; private TextMeshProUGUI _wepLine; private TextMeshProUGUI _wepStatsLine; private TextMeshProUGUI _wepBonusValuesLine; private TextMeshProUGUI _wepCounterLine; private TextMeshProUGUI _blLine; private TextMeshProUGUI _blStatsLine; private TextMeshProUGUI _blBonusValuesLine; private TextMeshProUGUI _blCounterLine; private TextMeshProUGUI _profLine; private GameObject _profWrapMode; private GameObject _profRowMode; private TextMeshProUGUI _profRowEnchanting; private TextMeshProUGUI _profRowAlchemy; private TextMeshProUGUI _profRowHarvesting; private TextMeshProUGUI _profRowBlacksmithing; private TextMeshProUGUI _profRowTailoring; private TextMeshProUGUI _profRowWoodcutting; private TextMeshProUGUI _profRowMining; private TextMeshProUGUI _profRowFishing; private GameObject _profBarEnchanting; private GameObject _profBarAlchemy; private GameObject _profBarHarvesting; private GameObject _profBarBlacksmithing; private GameObject _profBarTailoring; private GameObject _profBarWoodcutting; private GameObject _profBarMining; private GameObject _profBarFishing; private RectTransform _profBarFillEnchanting; private RectTransform _profBarFillAlchemy; private RectTransform _profBarFillHarvesting; private RectTransform _profBarFillBlacksmithing; private RectTransform _profBarFillTailoring; private RectTransform _profBarFillWoodcutting; private RectTransform _profBarFillMining; private RectTransform _profBarFillFishing; private TextMeshProUGUI _dailyLine; private TextMeshProUGUI _weeklyLine; private GameObject _xpBar; private RectTransform _xpBarFill; private GameObject _famBar; private RectTransform _famBarFill; private GameObject _wepBar; private RectTransform _wepBarFill; private GameObject _blBar; private RectTransform _blBarFill; private bool _subscribed; public override string PanelId => "CombinedOverlay"; public override PanelType PanelType => PanelType.CombinedOverlay; public override int MinWidth => 360; public override int MinHeight { get { int num = ResolveRowHeight(Theme.ScaledOverlay(11)); if (Settings.ShowExperienceOverlay) { num += sectionHeight(1, Settings.ShowProgressBarXP); } if (Settings.ShowFamiliarOverlay) { num += sectionHeight(3, Settings.ShowProgressBarFamiliar); } int num2 = 3; if (Settings.ShowOverlayBonusStats) { num2 += 2; } if (Settings.ShowOverlayXpCounter) { num2++; } int num3 = 3; if (Settings.ShowOverlayBonusStats) { num3 += 2; } if (Settings.ShowOverlayXpCounter) { num3++; } if (Settings.CombinedOverlayShowExpertise) { num += sectionHeight(num2, Settings.ShowProgressBarExpertise); } if (Settings.CombinedOverlayShowLegacy) { num += sectionHeight(num3, Settings.ShowProgressBarLegacy); } if (Settings.ShowProfessionOverlay) { if (Settings.ShowProgressBarProfessions) { int num4 = 0; if (Settings.ShowProfessionEnchanting) { num4++; } if (Settings.ShowProfessionAlchemy) { num4++; } if (Settings.ShowProfessionHarvesting) { num4++; } if (Settings.ShowProfessionBlacksmithing) { num4++; } if (Settings.ShowProfessionTailoring) { num4++; } if (Settings.ShowProfessionWoodcutting) { num4++; } if (Settings.ShowProfessionMining) { num4++; } if (Settings.ShowProfessionFishing) { num4++; } num += ResolveRowHeight(Theme.ScaledOverlay(12)) + num4 * (ResolveRowHeight(Theme.ScaledOverlay(12)) + Settings.ProgressBarHeight + 2); } else { num += sectionHeight(3, barVisible: false); } } if (Settings.ShowDailyQuestOverlay) { num += sectionHeight(2, barVisible: false); } return num; static int sectionHeight(int bodyLines, bool barVisible) { return ResolveRowHeight(Theme.ScaledOverlay(12)) + bodyLines * ResolveRowHeight(Theme.ScaledOverlay(13)) + (barVisible ? (Settings.ProgressBarHeight + 2) : 0); } } } public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2((0f - base.Owner.Scaler.m_ReferenceResolution.x) * 0.5f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f - 30f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.CombinedOverlayTransparency); public override bool UsesCustomBackgroundColor => true; public CombinedOverlayPanel(UIBase owner) : base(owner) { } protected override void LateConstructUI() { base.LateConstructUI(); if ((Object)(object)base.Rect != (Object)null) { base.Rect.SetSizeWithCurrentAnchors((Axis)1, (float)MinHeight); } } protected override void ConstructPanelContent() { base.ConstructPanelContent(); BuildXPSection(); BuildFamiliarSection(); BuildExpertiseSection(); BuildLegacySection(); BuildProfessionSection(); BuildQuestSection(); ApplySectionVisibility(); RenderAll(); if (!_subscribed) { PlayerStateService.ExperienceChanged += OnAnyChanged; PlayerStateService.FamiliarChanged += OnAnyChanged; PlayerStateService.ExpertiseChanged += OnAnyChanged; PlayerStateService.LegacyChanged += OnAnyChanged; PlayerStateService.ProfessionChanged += OnAnyChanged; PlayerStateService.QuestChanged += OnAnyChanged; PlayerStateService.LastResponseChanged += OnAnyChanged; PlayerStateService.BloodInfoChanged += OnAnyChanged; _subscribed = true; } } public void RefreshSections() { ApplySectionVisibility(); RenderAll(); if ((Object)(object)base.Rect != (Object)null) { base.Rect.SetSizeWithCurrentAnchors((Axis)1, (float)MinHeight); EnsureValidSize(); EnsureValidPosition(); if (base.Dragger != null) { base.Dragger.OnEndResize(); } } } private static int ResolveRowHeight(int fontSize) { return Mathf.Max(20, Mathf.RoundToInt((float)fontSize * 1.45f)); } private void ApplySectionVisibility() { if ((Object)(object)_xpSection != (Object)null) { _xpSection.SetActive(Settings.ShowExperienceOverlay); } if ((Object)(object)_familiarSection != (Object)null) { _familiarSection.SetActive(Settings.ShowFamiliarOverlay); } if ((Object)(object)_expertiseSection != (Object)null) { _expertiseSection.SetActive(Settings.CombinedOverlayShowExpertise); } if ((Object)(object)_legacySection != (Object)null) { _legacySection.SetActive(Settings.CombinedOverlayShowLegacy); } if ((Object)(object)_profSection != (Object)null) { _profSection.SetActive(Settings.ShowProfessionOverlay); } if ((Object)(object)_questSection != (Object)null) { _questSection.SetActive(Settings.ShowDailyQuestOverlay); } } private void OnAnyChanged() { RenderAll(); } private void RenderAll() { RenderXP(); RenderFamiliar(); RenderExpertise(); RenderLegacy(); RenderProfessions(); RenderQuests(); } private GameObject BuildSectionRoot(string name) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateVerticalGroup(base.ContentRoot, name, forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 1, new Vector4(2f, 2f, 2f, 2f), (Color?)new Color(0f, 0f, 0f, 0f), (TextAnchor?)null, 1f); UIFactory.SetLayoutElement(obj, 260, preferredWidth: 280, flexibleWidth: 1, minHeight: Theme.ScaledOverlayHeight(24), flexibleHeight: 0); return obj; } private TextMeshProUGUI AddSectionHeader(GameObject section, string title, Color color) { //IL_0012: 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) LabelRef labelRef = UIFactory.CreateLabel(section, ((Object)section).name + "_Hdr", title, Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledOverlayHeight(18), flexibleWidth: num, preferredHeight: Theme.ScaledOverlayHeight(20), gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((Graphic)labelRef.TextMesh).color = color; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; return labelRef.TextMesh; } private TextMeshProUGUI AddSectionBody(GameObject section, string name, string initialText, int fontSize = 13, bool wrap = false) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(section, name, initialText, Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(fontSize)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledOverlayHeight(18), flexibleWidth: num, preferredHeight: Theme.ScaledOverlayHeight(20), gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = wrap; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; if (wrap) { ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; } return labelRef.TextMesh; } private GameObject AddMiniBar(GameObject section, string name, Color fillColor, out RectTransform fill) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) GameObject obj = MiniBar.Create(section, name, out fill, fillColor, Settings.ProgressBarHeight); obj.SetActive(false); return obj; } private void BuildXPSection() { //IL_001d: 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) _xpSection = BuildSectionRoot("XPSection"); AddSectionHeader(_xpSection, "EXPERIENCE", COL_XP); _xpLine = AddSectionBody(_xpSection, "XPLine", "Lv —"); _xpBar = AddMiniBar(_xpSection, "XPBar", new Color(0.55f, 0.95f, 0.55f, 0.95f), out _xpBarFill); } private void BuildFamiliarSection() { //IL_001d: 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) _familiarSection = BuildSectionRoot("FamiliarSection"); AddSectionHeader(_familiarSection, "FAMILIAR", COL_FAMILIAR); _famNameLine = AddSectionBody(_familiarSection, "FamName", "(no familiar bound)"); _famBar = AddMiniBar(_familiarSection, "FamBar", new Color(1f, 0.6f, 0.2f, 0.95f), out _famBarFill); _famStatsLine = AddSectionBody(_familiarSection, "FamStats", "—", 12, wrap: true); } private void BuildExpertiseSection() { //IL_001d: 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) _expertiseSection = BuildSectionRoot("ExpertiseSection"); AddSectionHeader(_expertiseSection, "WEAPON EXPERTISE", COL_EXPERTISE); _wepLine = AddSectionBody(_expertiseSection, "WepLine", "—"); _wepBar = AddMiniBar(_expertiseSection, "WepBar", new Color(0.75f, 0.75f, 0.85f, 0.95f), out _wepBarFill); _wepStatsLine = AddSectionBody(_expertiseSection, "WepStats", "—", 12, wrap: true); _wepBonusValuesLine = AddSectionBody(_expertiseSection, "WepBonusValues", "", 11, wrap: true); ((TMP_Text)_wepBonusValuesLine).fontStyle = (FontStyles)2; ((Component)_wepBonusValuesLine).gameObject.SetActive(false); _wepCounterLine = AddSectionBody(_expertiseSection, "WepCounter", "", 11); ((TMP_Text)_wepCounterLine).fontStyle = (FontStyles)2; ((Component)_wepCounterLine).gameObject.SetActive(false); } private void BuildLegacySection() { //IL_001d: 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) _legacySection = BuildSectionRoot("LegacySection"); AddSectionHeader(_legacySection, "BLOOD LEGACY", COL_LEGACY); _blLine = AddSectionBody(_legacySection, "BlLine", "—"); _blBar = AddMiniBar(_legacySection, "BlBar", new Color(0.85f, 0.3f, 0.3f, 0.95f), out _blBarFill); _blStatsLine = AddSectionBody(_legacySection, "BlStats", "—", 12, wrap: true); _blBonusValuesLine = AddSectionBody(_legacySection, "BlBonusValues", "", 11, wrap: true); ((TMP_Text)_blBonusValuesLine).fontStyle = (FontStyles)2; ((Component)_blBonusValuesLine).gameObject.SetActive(false); _blCounterLine = AddSectionBody(_legacySection, "BlCounter", "", 11); ((TMP_Text)_blCounterLine).fontStyle = (FontStyles)2; ((Component)_blCounterLine).gameObject.SetActive(false); } private void BuildProfessionSection() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) _profSection = BuildSectionRoot("ProfSection"); AddSectionHeader(_profSection, "PROFESSIONS", COL_PROFS); _profWrapMode = UIFactory.CreateVerticalGroup(_profSection, "ProfWrapMode", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 0, new Vector4(0f, 0f, 0f, 0f), (Color?)new Color(0f, 0f, 0f, 0f), (TextAnchor?)null, 1f); UIFactory.SetLayoutElement(_profWrapMode, 240, preferredWidth: 320, flexibleWidth: 1, minHeight: Theme.ScaledOverlayHeight(18), flexibleHeight: 0); LabelRef labelRef = UIFactory.CreateLabel(_profWrapMode, "ProfLine", "—", Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(12)); UIFactory.SetLayoutElement(labelRef.GameObject, 240, preferredWidth: 320, flexibleWidth: 1, minHeight: Theme.ScaledOverlayHeight(18), flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; _profLine = labelRef.TextMesh; _profRowMode = UIFactory.CreateVerticalGroup(_profSection, "ProfRowMode", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 1, new Vector4(0f, 0f, 0f, 0f), (Color?)new Color(0f, 0f, 0f, 0f), (TextAnchor?)null, 1f); UIFactory.SetLayoutElement(_profRowMode, 240, preferredWidth: 320, flexibleWidth: 1, minHeight: Theme.ScaledOverlayHeight(18), flexibleHeight: 0); Color fillColor = default(Color); ((Color)(ref fillColor))..ctor(0.95f, 0.75f, 0.35f, 0.95f); _profRowEnchanting = AddProfRow(_profRowMode, "ProfRow_Enchanting", "Enchanting —"); _profBarEnchanting = AddMiniBar(_profRowMode, "ProfBar_Enchanting", fillColor, out _profBarFillEnchanting); _profRowAlchemy = AddProfRow(_profRowMode, "ProfRow_Alchemy", "Alchemy —"); _profBarAlchemy = AddMiniBar(_profRowMode, "ProfBar_Alchemy", fillColor, out _profBarFillAlchemy); _profRowHarvesting = AddProfRow(_profRowMode, "ProfRow_Harvesting", "Harvesting —"); _profBarHarvesting = AddMiniBar(_profRowMode, "ProfBar_Harvesting", fillColor, out _profBarFillHarvesting); _profRowBlacksmithing = AddProfRow(_profRowMode, "ProfRow_Blacksmithing", "Blacksmithing —"); _profBarBlacksmithing = AddMiniBar(_profRowMode, "ProfBar_Blacksmithing", fillColor, out _profBarFillBlacksmithing); _profRowTailoring = AddProfRow(_profRowMode, "ProfRow_Tailoring", "Tailoring —"); _profBarTailoring = AddMiniBar(_profRowMode, "ProfBar_Tailoring", fillColor, out _profBarFillTailoring); _profRowWoodcutting = AddProfRow(_profRowMode, "ProfRow_Woodcutting", "Woodcutting —"); _profBarWoodcutting = AddMiniBar(_profRowMode, "ProfBar_Woodcutting", fillColor, out _profBarFillWoodcutting); _profRowMining = AddProfRow(_profRowMode, "ProfRow_Mining", "Mining —"); _profBarMining = AddMiniBar(_profRowMode, "ProfBar_Mining", fillColor, out _profBarFillMining); _profRowFishing = AddProfRow(_profRowMode, "ProfRow_Fishing", "Fishing —"); _profBarFishing = AddMiniBar(_profRowMode, "ProfBar_Fishing", fillColor, out _profBarFillFishing); } private TextMeshProUGUI AddProfRow(GameObject parent, string name, string initial) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(parent, name, initial, Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledOverlayHeight(18), flexibleWidth: num, preferredHeight: Theme.ScaledOverlayHeight(20), gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; return labelRef.TextMesh; } private void BuildQuestSection() { //IL_001d: 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) _questSection = BuildSectionRoot("QuestSection"); AddSectionHeader(_questSection, "DAILY / WEEKLY QUEST", COL_QUEST_D); _dailyLine = AddSectionBody(_questSection, "DailyLine", "Daily: —"); _weeklyLine = AddSectionBody(_questSection, "WeeklyLine", "Weekly: —"); ((Graphic)_weeklyLine).color = COL_QUEST_W; } private static void SyncBar(GameObject bar, RectTransform fill, float progress, bool show) { if (!((Object)(object)bar == (Object)null)) { if (bar.activeSelf != show) { bar.SetActive(show); } if (show && (Object)(object)fill != (Object)null) { MiniBar.SetProgress(fill, progress); } } } private void RenderXP() { if (!((Object)(object)_xpLine == (Object)null)) { PlayerStateService.ExperienceState experience = PlayerStateService.Experience; string value = ((experience.Class == PlayerStateService.PlayerClass.None) ? "(no class)" : experience.Class.ToString()); string value2 = ((experience.Prestige > 0) ? $" P{experience.Prestige}" : ""); ((TMP_Text)_xpLine).text = $"Lv {experience.Level} ({experience.Progress * 100f:0.#}%){value2} {value}"; SyncBar(_xpBar, _xpBarFill, experience.Progress, Settings.ShowProgressBarXP); } } private void RenderFamiliar() { if (!((Object)(object)_famNameLine == (Object)null)) { PlayerStateService.FamiliarState familiar = PlayerStateService.Familiar; if (!familiar.HasActive) { ((TMP_Text)_famNameLine).text = "(no familiar bound)"; ((TMP_Text)_famStatsLine).text = "—"; SyncBar(_famBar, _famBarFill, 0f, show: false); return; } string value = ((familiar.Prestige > 0) ? $" P{familiar.Prestige}" : ""); ((TMP_Text)_famNameLine).text = $"{familiar.Name} Lv {familiar.Level} ({familiar.Progress * 100f:0.#}%){value}"; ((TMP_Text)_famStatsLine).text = $"HP {familiar.MaxHealth} PP {familiar.PhysicalPower} SP {familiar.SpellPower}"; SyncBar(_famBar, _famBarFill, familiar.Progress, Settings.ShowProgressBarFamiliar); } } private void RenderExpertise() { if (!((Object)(object)_wepLine == (Object)null)) { PlayerStateService.ExpertiseState expertise = PlayerStateService.Expertise; string value = ((expertise.Prestige > 0) ? $" P{expertise.Prestige}" : ""); ((TMP_Text)_wepLine).text = $"{expertise.Type} Lv {expertise.Level} ({expertise.Progress * 100f:0.#}%){value}"; List list = PlayerStateService.DecodeWeaponBonusStats(expertise.BonusStatsRaw); ((TMP_Text)_wepStatsLine).text = ((list == null || list.Count == 0) ? "Stats: (none chosen)" : ("Stats: " + string.Join(", ", list))); SyncBar(_wepBar, _wepBarFill, expertise.Progress, Settings.ShowProgressBarExpertise); RenderWepBonusValuesSubRow(); RenderWepCounterSubRow(); } } private void RenderLegacy() { if (!((Object)(object)_blLine == (Object)null)) { PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; string value = ((legacy.Prestige > 0) ? $" P{legacy.Prestige}" : ""); ((TMP_Text)_blLine).text = $"{legacy.Type} Lv {legacy.Level} ({legacy.Progress * 100f:0.#}%){value}"; List list = PlayerStateService.DecodeBloodBonusStats(legacy.BonusStatsRaw); ((TMP_Text)_blStatsLine).text = ((list == null || list.Count == 0) ? "Stats: (none chosen)" : ("Stats: " + string.Join(", ", list))); SyncBar(_blBar, _blBarFill, legacy.Progress, Settings.ShowProgressBarLegacy); RenderBlBonusValuesSubRow(); RenderBlCounterSubRow(); } } private void RenderWepBonusValuesSubRow() { if (!((Object)(object)_wepBonusValuesLine == (Object)null)) { bool showOverlayBonusStats = Settings.ShowOverlayBonusStats; ExperienceOverlayPanel experienceOverlayPanel = Plugin.UIManager?.ExperienceOverlay; List list = ((!showOverlayBonusStats) ? null : experienceOverlayPanel?.BuildCleanedWepGetStatsLines()); bool flag = list != null && list.Count > 0; if (((Component)_wepBonusValuesLine).gameObject.activeSelf != (showOverlayBonusStats && flag)) { ((Component)_wepBonusValuesLine).gameObject.SetActive(showOverlayBonusStats && flag); } if (showOverlayBonusStats && flag) { ((TMP_Text)_wepBonusValuesLine).text = string.Join("\n", list); } } } private void RenderWepCounterSubRow() { if (!((Object)(object)_wepCounterLine == (Object)null)) { bool showOverlayXpCounter = Settings.ShowOverlayXpCounter; ExperienceOverlayPanel experienceOverlayPanel = Plugin.UIManager?.ExperienceOverlay; bool flag = showOverlayXpCounter && (experienceOverlayPanel?.WepGetHasData ?? false); if (((Component)_wepCounterLine).gameObject.activeSelf != flag) { ((Component)_wepCounterLine).gameObject.SetActive(flag); } if (flag) { int wepGetRawExpertise = experienceOverlayPanel.WepGetRawExpertise; float wepGetProgressPct = experienceOverlayPanel.WepGetProgressPct; int num = ((wepGetProgressPct > 0.01f) ? Mathf.RoundToInt((float)wepGetRawExpertise * 100f / wepGetProgressPct) : 0); ((TMP_Text)_wepCounterLine).text = ((num > 0) ? $"Exp: {wepGetRawExpertise} / {num} ({wepGetProgressPct:0.0}%)" : $"Exp: {wepGetRawExpertise} ({wepGetProgressPct:0.0}%)"); } } } private void RenderBlBonusValuesSubRow() { if (!((Object)(object)_blBonusValuesLine == (Object)null)) { bool showOverlayBonusStats = Settings.ShowOverlayBonusStats; PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; bool flag = bloodInfoLatest.StatLines != null && bloodInfoLatest.StatLines.Count > 0; if (((Component)_blBonusValuesLine).gameObject.activeSelf != (showOverlayBonusStats && flag)) { ((Component)_blBonusValuesLine).gameObject.SetActive(showOverlayBonusStats && flag); } if (showOverlayBonusStats && flag) { ((TMP_Text)_blBonusValuesLine).text = string.Join("\n", bloodInfoLatest.StatLines); } } } private void RenderBlCounterSubRow() { if ((Object)(object)_blCounterLine == (Object)null) { return; } bool showOverlayXpCounter = Settings.ShowOverlayXpCounter; PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; bool flag = showOverlayXpCounter && !string.IsNullOrEmpty(bloodInfoLatest.Essence) && !string.IsNullOrEmpty(bloodInfoLatest.ProgressPct); if (((Component)_blCounterLine).gameObject.activeSelf != flag) { ((Component)_blCounterLine).gameObject.SetActive(flag); } if (flag) { if (float.TryParse(bloodInfoLatest.ProgressPct, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && int.TryParse(bloodInfoLatest.Essence, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { int num = ((result > 0.01f) ? Mathf.RoundToInt((float)result2 * 100f / result) : 0); ((TMP_Text)_blCounterLine).text = ((num > 0) ? $"Ess: {result2} / {num} ({result:0.0}%)" : $"Ess: {result2} ({result:0.0}%)"); } else { ((TMP_Text)_blCounterLine).text = $"Ess: {bloodInfoLatest.Essence} ({bloodInfoLatest.ProgressPct}%)"; } } } private void RenderProfessions() { if ((Object)(object)_profLine == (Object)null) { return; } PlayerStateService.ProfessionState profession = PlayerStateService.Profession; bool showProgressBarProfessions = Settings.ShowProgressBarProfessions; if ((Object)(object)_profWrapMode != (Object)null && _profWrapMode.activeSelf != !showProgressBarProfessions) { _profWrapMode.SetActive(!showProgressBarProfessions); } if ((Object)(object)_profRowMode != (Object)null && _profRowMode.activeSelf != showProgressBarProfessions) { _profRowMode.SetActive(showProgressBarProfessions); } if (!showProgressBarProfessions) { List list = new List(8); if (Settings.ShowProfessionEnchanting) { list.Add($"Enchanting {profession.EnchantingLevel}"); } if (Settings.ShowProfessionAlchemy) { list.Add($"Alchemy {profession.AlchemyLevel}"); } if (Settings.ShowProfessionHarvesting) { list.Add($"Harvesting {profession.HarvestingLevel}"); } if (Settings.ShowProfessionBlacksmithing) { list.Add($"Blacksmithing {profession.BlacksmithingLevel}"); } if (Settings.ShowProfessionTailoring) { list.Add($"Tailoring {profession.TailoringLevel}"); } if (Settings.ShowProfessionWoodcutting) { list.Add($"Woodcutting {profession.WoodcuttingLevel}"); } if (Settings.ShowProfessionMining) { list.Add($"Mining {profession.MiningLevel}"); } if (Settings.ShowProfessionFishing) { list.Add($"Fishing {profession.FishingLevel}"); } ((TMP_Text)_profLine).text = ((list.Count == 0) ? "(all professions hidden in Settings)" : string.Join(" ", list)); } else { RenderProfRow(_profRowEnchanting, _profBarEnchanting, _profBarFillEnchanting, Settings.ShowProfessionEnchanting, "Enchanting", profession.EnchantingLevel, profession.EnchantingProgress); RenderProfRow(_profRowAlchemy, _profBarAlchemy, _profBarFillAlchemy, Settings.ShowProfessionAlchemy, "Alchemy", profession.AlchemyLevel, profession.AlchemyProgress); RenderProfRow(_profRowHarvesting, _profBarHarvesting, _profBarFillHarvesting, Settings.ShowProfessionHarvesting, "Harvesting", profession.HarvestingLevel, profession.HarvestingProgress); RenderProfRow(_profRowBlacksmithing, _profBarBlacksmithing, _profBarFillBlacksmithing, Settings.ShowProfessionBlacksmithing, "Blacksmithing", profession.BlacksmithingLevel, profession.BlacksmithingProgress); RenderProfRow(_profRowTailoring, _profBarTailoring, _profBarFillTailoring, Settings.ShowProfessionTailoring, "Tailoring", profession.TailoringLevel, profession.TailoringProgress); RenderProfRow(_profRowWoodcutting, _profBarWoodcutting, _profBarFillWoodcutting, Settings.ShowProfessionWoodcutting, "Woodcutting", profession.WoodcuttingLevel, profession.WoodcuttingProgress); RenderProfRow(_profRowMining, _profBarMining, _profBarFillMining, Settings.ShowProfessionMining, "Mining", profession.MiningLevel, profession.MiningProgress); RenderProfRow(_profRowFishing, _profBarFishing, _profBarFillFishing, Settings.ShowProfessionFishing, "Fishing", profession.FishingLevel, profession.FishingProgress); } } private static void RenderProfRow(TextMeshProUGUI label, GameObject bar, RectTransform fill, bool show, string name, int level, float progress) { if ((Object)(object)label != (Object)null) { if (((Component)label).gameObject.activeSelf != show) { ((Component)label).gameObject.SetActive(show); } if (show) { ((TMP_Text)label).text = $"{name}: Lv {level} ({progress * 100f:0.#}%)"; } } SyncBar(bar, fill, progress, show); } private void RenderQuests() { if (!((Object)(object)_dailyLine == (Object)null)) { PlayerStateService.QuestState dailyQuest = PlayerStateService.DailyQuest; PlayerStateService.QuestState weeklyQuest = PlayerStateService.WeeklyQuest; ((TMP_Text)_dailyLine).text = FormatQuest("Daily", dailyQuest); ((TMP_Text)_weeklyLine).text = FormatQuest("Weekly", weeklyQuest); } } private static string FormatQuest(string label, PlayerStateService.QuestState q) { if (string.IsNullOrEmpty(q.TargetName)) { return label + ": —"; } if (q.Goal <= 0 || q.Progress < q.Goal) { return $"{label}: {q.TargetName} ({q.Progress}/{q.Goal})"; } return label + ": " + q.TargetName + " — Complete!"; } internal override void Reset() { if (_subscribed) { PlayerStateService.ExperienceChanged -= OnAnyChanged; PlayerStateService.FamiliarChanged -= OnAnyChanged; PlayerStateService.ExpertiseChanged -= OnAnyChanged; PlayerStateService.LegacyChanged -= OnAnyChanged; PlayerStateService.ProfessionChanged -= OnAnyChanged; PlayerStateService.QuestChanged -= OnAnyChanged; PlayerStateService.LastResponseChanged -= OnAnyChanged; PlayerStateService.BloodInfoChanged -= OnAnyChanged; _subscribed = false; } } } public class DailyQuestOverlayPanel : ResizeablePanelBase { private LabelRef _dailyTitleLabel; private LabelRef _dailyTargetLabel; private LabelRef _dailyProgressLabel; private LabelRef _weeklyTitleLabel; private LabelRef _weeklyTargetLabel; private LabelRef _weeklyProgressLabel; private bool _subscribed; public override string PanelId => "DailyQuestOverlay"; public override PanelType PanelType => PanelType.DailyQuestOverlay; public override int MinWidth => 260; public override int MinHeight => 130; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2((0f - base.Owner.Scaler.m_ReferenceResolution.x) * 0.5f + 280f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f - 40f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.DailyQuestTransparency); public override bool UsesCustomBackgroundColor => true; public DailyQuestOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) base.ConstructPanelContent(); _dailyTitleLabel = AddRow("DailyTitle", "Daily Quest", (FontStyles)1, Theme.ScaledOverlay(14)); ((Graphic)_dailyTitleLabel.TextMesh).color = new Color(0f, 1f, 1f); ApplyStrongOutline(_dailyTitleLabel.TextMesh); _dailyTargetLabel = AddRow("DailyTarget", "—", (FontStyles)0, Theme.ScaledOverlay(13)); _dailyProgressLabel = AddRow("DailyProgress", "—", (FontStyles)2, Theme.ScaledOverlay(13)); AddSpacer(6); _weeklyTitleLabel = AddRow("WeeklyTitle", "Weekly Quest", (FontStyles)1, Theme.ScaledOverlay(14)); ((Graphic)_weeklyTitleLabel.TextMesh).color = new Color(1f, 0.85f, 0.3f); ApplyStrongOutline(_weeklyTitleLabel.TextMesh); _weeklyTargetLabel = AddRow("WeeklyTarget", "—", (FontStyles)0, Theme.ScaledOverlay(13)); _weeklyProgressLabel = AddRow("WeeklyProgress", "—", (FontStyles)2, Theme.ScaledOverlay(13)); Render(); if (!_subscribed) { PlayerStateService.QuestChanged += OnQuestChanged; _subscribed = true; } } private LabelRef AddRow(string name, string text, FontStyles style, int fontSize) { //IL_0008: 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) LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, name, text, Theme.OverlayMidlineAlignment(), null, fontSize); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = style; return labelRef; } private void AddSpacer(int height) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = UIFactory.CreateUIObject("Spacer", base.ContentRoot); int? minHeight = height; int? preferredHeight = height; int? flexibleHeight = 0; int? flexibleWidth = 1; UIFactory.SetLayoutElement(gameObject, null, minHeight, flexibleWidth, flexibleHeight, null, preferredHeight); } private void OnQuestChanged() { Render(); } private void Render() { if (_dailyTitleLabel != null) { FillRow(_dailyTargetLabel, _dailyProgressLabel, PlayerStateService.DailyQuest, ".quest r d"); FillRow(_weeklyTargetLabel, _weeklyProgressLabel, PlayerStateService.WeeklyQuest, ".quest r w"); } } private static void FillRow(LabelRef target, LabelRef progress, PlayerStateService.QuestState s, string rerollHint) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(s.TargetName) && s.Goal <= 0) { ((TMP_Text)target.TextMesh).text = "(none yet)"; ((TMP_Text)progress.TextMesh).text = "Check back after refresh."; ((Graphic)progress.TextMesh).color = Color.white; return; } ((TMP_Text)target.TextMesh).text = (s.IsVBlood ? ("⚔ " + s.TargetName + " (V Blood)") : ("⚔ " + s.TargetName)); if (s.Goal > 0 && s.Progress >= s.Goal) { ((TMP_Text)progress.TextMesh).text = "Complete! (" + rerollHint + " to reroll)"; ((Graphic)progress.TextMesh).color = new Color(0.6f, 1f, 0.6f); return; } ((TMP_Text)progress.TextMesh).text = $"Progress: {s.Progress} / {s.Goal}"; ((Graphic)progress.TextMesh).color = Color.white; } private static void ApplyStrongOutline(TextMeshProUGUI t) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)t == (Object)null) { return; } try { ((TMP_Text)t).outlineColor = Color32.op_Implicit(Color.black); ((TMP_Text)t).outlineWidth = 0.25f; } catch { } } internal override void Reset() { if (_subscribed) { PlayerStateService.QuestChanged -= OnQuestChanged; _subscribed = false; } } } public class ExperienceOverlayPanel : ResizeablePanelBase { private LabelRef _levelLabel; private LabelRef _progressLabel; private GameObject _xpBar; private RectTransform _xpBarFill; private RectTransform _xpBarSubFill; private LabelRef _classLabel; private LabelRef _weaponLabel; private LabelRef _weaponCounterLabel; private GameObject _weaponBar; private RectTransform _weaponBarFill; private RectTransform _weaponBarSubFill; private LabelRef _weaponStatsLabel; private LabelRef _legacyLabel; private LabelRef _legacyCounterLabel; private GameObject _legacyBar; private RectTransform _legacyBarFill; private RectTransform _legacyBarSubFill; private LabelRef _legacyStatsLabel; private LabelRef _exoLabel; private int _wepGetRawExpertise; private float _wepGetProgressPct; private bool _wepGetHasData; private bool _subscribed; private bool _expertiseSubscribed; private bool _legacySubscribed; private bool _prestigeSubscribed; private bool _lastResponseSubscribed; private bool _bloodInfoSubscribed; private bool _exoFetchScheduled; private int _exoLevel; private int _exoMaxLevel; private List _cachedWepGetLines; private Action _bonusStatsTicker; private double _lastBonusStatsFetchAt; private const double OVERLAY_BONUS_REFRESH_SECONDS = 10.0; private bool _bonusStatsFetchToggle; private PlayerStateService.WeaponType _lastSeenWeaponType; private PlayerStateService.BloodType _lastSeenBloodType; private bool _seenWeaponTypeBaseline; private bool _seenBloodTypeBaseline; private static readonly Regex _wepGetPreambleRegex = new Regex("Your weapon expertise is \\[\\d+\\]\\[\\d+\\] and you have (?[^<]+) expertise \\((?[^<%]+)%?\\) with", RegexOptions.Compiled); private static readonly Regex _tmpTagStripRegex = new Regex("]*)?>", RegexOptions.Compiled); public override string PanelId => "ExperienceOverlay"; public override PanelType PanelType => PanelType.ExperienceOverlay; public override int MinWidth => 240; public override int MinHeight { get { int num = ResolveRowHeight(Theme.ScaledOverlay(16)); int num2 = ResolveRowHeight(Theme.ScaledOverlay(13)); int num3 = ResolveRowHeight(Theme.ScaledOverlay(11)); int num4 = ResolveRowHeight(Theme.ScaledOverlay(11)) * 2; int num5 = num + 5 * num2 + 12; int num6 = 0; if (Settings.ShowProgressBarXP) { num6++; } if (Settings.ShowProgressBarExpertise) { num6++; } if (Settings.ShowProgressBarLegacy) { num6++; } if (num6 > 0) { num5 += num6 * (Settings.ProgressBarHeight + 2); } if (Settings.ShowOverlayBonusStats) { num5 += 2 * num4; } if (Settings.ShowOverlayXpCounter) { num5 += 2 * num3; } return num5; } } public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2((0f - base.Owner.Scaler.m_ReferenceResolution.x) * 0.5f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.XPOverlayTransparency); public override bool UsesCustomBackgroundColor => true; public bool WepGetHasData => _wepGetHasData; public int WepGetRawExpertise => _wepGetRawExpertise; public float WepGetProgressPct => _wepGetProgressPct; public IReadOnlyList CachedWepGetLines => _cachedWepGetLines; public List BuildCleanedWepGetStatsLines() { if (_cachedWepGetLines == null || _cachedWepGetLines.Count == 0) { return null; } List list = new List(_cachedWepGetLines.Count); foreach (string cachedWepGetLine in _cachedWepGetLines) { string text = _tmpTagStripRegex.Replace(cachedWepGetLine, string.Empty).Trim(); if (!string.IsNullOrEmpty(text) && !IsWepGetPreambleOrPlaceholder(text)) { list.Add(text); } } if (list.Count <= 0) { return null; } return list; } public ExperienceOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) base.ConstructPanelContent(); _levelLabel = AddRow("LevelLabel", "Level —", (FontStyles)1, Theme.ScaledOverlay(16)); _progressLabel = AddRow("ProgressLabel", "XP — %", (FontStyles)0, Theme.ScaledOverlay(14)); _xpBar = MiniBar.CreateWithSubLine(base.ContentRoot, "XpBar", out _xpBarFill, out _xpBarSubFill, new Color(0.4f, 0.85f, 1f, 0.95f), ResolveBarHeight()); _xpBar.SetActive(false); _classLabel = AddRow("ClassLabel", "Class —", (FontStyles)2, Theme.ScaledOverlay(13)); _weaponLabel = AddRow("WeaponLabel", "Weapon —", (FontStyles)0, Theme.ScaledOverlay(13)); _weaponCounterLabel = AddRow("WeaponCounterLabel", "", (FontStyles)2, Theme.ScaledOverlay(11)); _weaponCounterLabel.GameObject.SetActive(false); _weaponBar = MiniBar.CreateWithSubLine(base.ContentRoot, "WeaponXpBar", out _weaponBarFill, out _weaponBarSubFill, new Color(1f, 0.45f, 0.3f, 0.95f), ResolveBarHeight()); _weaponBar.SetActive(false); _weaponStatsLabel = AddRow("WeaponStatsLabel", "", (FontStyles)2, Theme.ScaledOverlay(11)); ConfigureBonusStatsLabel(_weaponStatsLabel); _weaponStatsLabel.GameObject.SetActive(false); _legacyLabel = AddRow("LegacyLabel", "Legacy —", (FontStyles)0, Theme.ScaledOverlay(13)); _legacyCounterLabel = AddRow("LegacyCounterLabel", "", (FontStyles)2, Theme.ScaledOverlay(11)); _legacyCounterLabel.GameObject.SetActive(false); _legacyBar = MiniBar.CreateWithSubLine(base.ContentRoot, "LegacyXpBar", out _legacyBarFill, out _legacyBarSubFill, new Color(0.75f, 0.25f, 0.45f, 0.95f), ResolveBarHeight()); _legacyBar.SetActive(false); _legacyStatsLabel = AddRow("LegacyStatsLabel", "", (FontStyles)2, Theme.ScaledOverlay(11)); ConfigureBonusStatsLabel(_legacyStatsLabel); _legacyStatsLabel.GameObject.SetActive(false); _exoLabel = AddRow("ExoLabel", "EXO Prestige —", (FontStyles)2, Theme.ScaledOverlay(13)); Render(PlayerStateService.Experience); TryRenderExoFromState(); if (!_subscribed) { PlayerStateService.ExperienceChanged += OnExperienceChanged; _subscribed = true; } if (!_expertiseSubscribed) { PlayerStateService.ExpertiseChanged += OnExpertiseChanged; _expertiseSubscribed = true; } if (!_legacySubscribed) { PlayerStateService.LegacyChanged += OnLegacyChanged; _legacySubscribed = true; } if (!_prestigeSubscribed) { PlayerStateService.PrestigeInfoChanged += OnPrestigeInfoChanged; _prestigeSubscribed = true; } if (!_lastResponseSubscribed) { PlayerStateService.LastResponseChanged += OnLastResponseChanged; _lastResponseSubscribed = true; } if (!_bloodInfoSubscribed) { PlayerStateService.BloodInfoChanged += OnBloodInfoChanged; _bloodInfoSubscribed = true; } RenderWeapon(PlayerStateService.Expertise); RenderLegacy(PlayerStateService.Legacy); PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; if (lastResponse.Command == ".wep get" && lastResponse.Lines != null && lastResponse.Lines.Count > 0) { _cachedWepGetLines = new List(lastResponse.Lines); } ScheduleExoFetch(); if (_bonusStatsTicker == null) { _bonusStatsTicker = BonusStatsTick; CoreUpdateBehavior.Actions.Add(_bonusStatsTicker); } } private void ScheduleExoFetch() { if (_exoFetchScheduled) { return; } _exoFetchScheduled = true; Action ticker = null; ticker = delegate { if (!MessageService.IsInitialized) { return; } CoreUpdateBehavior.Actions.Remove(ticker); try { MessageService.EnqueueMessage(".prestige get Exo"); } catch (Exception ex) { LogUtils.LogWarning("ExperienceOverlay: auto .prestige get Exo failed — " + ex.Message); } }; CoreUpdateBehavior.Actions.Add(ticker); } private void ConfigureBonusStatsLabel(LabelRef lbl) { if (lbl != null) { ((TMP_Text)lbl.TextMesh).enableWordWrapping = true; ((TMP_Text)lbl.TextMesh).overflowMode = (TextOverflowModes)0; GameObject gameObject = lbl.GameObject; int? minWidth = 220; int? preferredWidth = 240; int? num = 1; UIFactory.SetLayoutElement(minHeight: 16, flexibleWidth: num, preferredHeight: -1, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ContentSizeFitter obj = lbl.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; } } private LabelRef AddRow(string name, string text, FontStyles style, int fontSize) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) int value = ResolveRowHeight(fontSize); LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, name, text, Theme.OverlayMidlineAlignment(), null, fontSize); GameObject gameObject = labelRef.GameObject; int? minWidth = 220; int? preferredWidth = 240; int? num = 1; UIFactory.SetLayoutElement(minHeight: value, flexibleWidth: num, preferredHeight: value, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = style; return labelRef; } private static int ResolveRowHeight(int fontSize) { return Mathf.Max(20, Mathf.RoundToInt((float)fontSize * 1.45f)); } private void OnExperienceChanged() { Render(PlayerStateService.Experience); } private void OnExpertiseChanged() { PlayerStateService.ExpertiseState expertise = PlayerStateService.Expertise; if (_seenWeaponTypeBaseline && expertise.Type != _lastSeenWeaponType) { _lastBonusStatsFetchAt = 0.0; _bonusStatsFetchToggle = true; _cachedWepGetLines = null; } _lastSeenWeaponType = expertise.Type; _seenWeaponTypeBaseline = true; RenderWeapon(expertise); RenderWeaponStats(); } private void OnLegacyChanged() { PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; if (_seenBloodTypeBaseline && legacy.Type != _lastSeenBloodType) { _lastBonusStatsFetchAt = 0.0; _bonusStatsFetchToggle = false; } _lastSeenBloodType = legacy.Type; _seenBloodTypeBaseline = true; RenderLegacy(legacy); RenderLegacyStats(); } private void OnLastResponseChanged() { PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; if (!(lastResponse.Command != ".wep get") && lastResponse.Lines != null) { _cachedWepGetLines = new List(lastResponse.Lines); ParseWepGetPreamble(_cachedWepGetLines); RenderWeaponStats(); RenderWeaponCounter(); } } private void ParseWepGetPreamble(List lines) { if (lines == null || lines.Count == 0) { _wepGetHasData = false; return; } foreach (string line in lines) { Match match = _wepGetPreambleRegex.Match(line); if (match.Success && int.TryParse(match.Groups["xp"].Value.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && float.TryParse(match.Groups["pct"].Value.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { _wepGetRawExpertise = result; _wepGetProgressPct = result2; _wepGetHasData = true; break; } } } private void OnBloodInfoChanged() { RenderLegacyStats(); RenderLegacyCounter(); } private void RenderWeapon(PlayerStateService.ExpertiseState e) { if (_weaponLabel == null) { return; } ApplyBarChrome(); if (e.Level <= 0 && e.Type == PlayerStateService.WeaponType.Sword) { ((TMP_Text)_weaponLabel.TextMesh).text = "Weapon —"; if ((Object)(object)_weaponBar != (Object)null && _weaponBar.activeSelf) { _weaponBar.SetActive(false); } RenderWeaponCounter(); return; } string text = ((e.Prestige > 0) ? $"Weapon: {e.Type} Lv {e.Level} ({e.Progress * 100f:0.#}%) Pr {e.Prestige}" : $"Weapon: {e.Type} Lv {e.Level} ({e.Progress * 100f:0.#}%)"); ((TMP_Text)_weaponLabel.TextMesh).text = text; bool showProgressBarExpertise = Settings.ShowProgressBarExpertise; if ((Object)(object)_weaponBar != (Object)null && _weaponBar.activeSelf != showProgressBarExpertise) { _weaponBar.SetActive(showProgressBarExpertise); } if (showProgressBarExpertise) { MiniBar.SetProgress(_weaponBarFill, e.Progress); int maxExpertiseLevel = PlayerStateService.Config.MaxExpertiseLevel; float progress = ((maxExpertiseLevel > 0) ? Mathf.Clamp01((float)e.Level / (float)maxExpertiseLevel) : 0f); ApplySubLine(_weaponBar, _weaponBarSubFill, Settings.ShowPrestigeSubLine && maxExpertiseLevel > 0, progress); } RenderWeaponCounter(); } private void RenderLegacy(PlayerStateService.LegacyState l) { if (_legacyLabel == null) { return; } ApplyBarChrome(); if (l.Level <= 0 && l.Type == PlayerStateService.BloodType.Worker) { ((TMP_Text)_legacyLabel.TextMesh).text = "Legacy —"; if ((Object)(object)_legacyBar != (Object)null && _legacyBar.activeSelf) { _legacyBar.SetActive(false); } RenderLegacyCounter(); return; } string text = ((l.Prestige > 0) ? $"Legacy: {l.Type} Lv {l.Level} ({l.Progress * 100f:0.#}%) Pr {l.Prestige}" : $"Legacy: {l.Type} Lv {l.Level} ({l.Progress * 100f:0.#}%)"); ((TMP_Text)_legacyLabel.TextMesh).text = text; bool showProgressBarLegacy = Settings.ShowProgressBarLegacy; if ((Object)(object)_legacyBar != (Object)null && _legacyBar.activeSelf != showProgressBarLegacy) { _legacyBar.SetActive(showProgressBarLegacy); } if (showProgressBarLegacy) { MiniBar.SetProgress(_legacyBarFill, l.Progress); int maxLegacyLevel = PlayerStateService.Config.MaxLegacyLevel; float progress = ((maxLegacyLevel > 0) ? Mathf.Clamp01((float)l.Level / (float)maxLegacyLevel) : 0f); ApplySubLine(_legacyBar, _legacyBarSubFill, Settings.ShowPrestigeSubLine && maxLegacyLevel > 0, progress); } RenderLegacyCounter(); } private void RenderWeaponStats() { if (_weaponStatsLabel == null) { return; } if (!Settings.ShowOverlayBonusStats || _cachedWepGetLines == null || _cachedWepGetLines.Count == 0) { if (_weaponStatsLabel.GameObject.activeSelf) { _weaponStatsLabel.GameObject.SetActive(false); } return; } List list = new List(_cachedWepGetLines.Count); foreach (string cachedWepGetLine in _cachedWepGetLines) { string text = _tmpTagStripRegex.Replace(cachedWepGetLine, string.Empty).Trim(); if (!string.IsNullOrEmpty(text) && !IsWepGetPreambleOrPlaceholder(text)) { list.Add(text); } } if (list.Count == 0) { if (_weaponStatsLabel.GameObject.activeSelf) { _weaponStatsLabel.GameObject.SetActive(false); } return; } ((TMP_Text)_weaponStatsLabel.TextMesh).text = string.Join("\n", list); if (!_weaponStatsLabel.GameObject.activeSelf) { _weaponStatsLabel.GameObject.SetActive(true); } } private static bool IsWepGetPreambleOrPlaceholder(string strippedLine) { if (!strippedLine.StartsWith("Your weapon expertise is", StringComparison.Ordinal) && !strippedLine.StartsWith("No bonuses from currently equipped", StringComparison.Ordinal)) { return strippedLine.StartsWith("You haven't gained any expertise for", StringComparison.Ordinal); } return true; } private void RenderWeaponCounter() { if (_weaponCounterLabel == null) { return; } if (!Settings.ShowOverlayXpCounter || !_wepGetHasData) { if (_weaponCounterLabel.GameObject.activeSelf) { _weaponCounterLabel.GameObject.SetActive(false); } return; } int num = ((_wepGetProgressPct > 0.01f) ? Mathf.RoundToInt((float)_wepGetRawExpertise * 100f / _wepGetProgressPct) : 0); string text = ((num > 0) ? $"Exp: {_wepGetRawExpertise} / {num} ({_wepGetProgressPct:0.0}%)" : $"Exp: {_wepGetRawExpertise} ({_wepGetProgressPct:0.0}%)"); ((TMP_Text)_weaponCounterLabel.TextMesh).text = text; if (!_weaponCounterLabel.GameObject.activeSelf) { _weaponCounterLabel.GameObject.SetActive(true); } } private void RenderLegacyCounter() { if (_legacyCounterLabel == null) { return; } bool showOverlayXpCounter = Settings.ShowOverlayXpCounter; PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; if (!showOverlayXpCounter || string.IsNullOrEmpty(bloodInfoLatest.BloodType)) { if (_legacyCounterLabel.GameObject.activeSelf) { _legacyCounterLabel.GameObject.SetActive(false); } return; } if (!int.TryParse(bloodInfoLatest.Essence ?? "", NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { if (!float.TryParse(bloodInfoLatest.Essence ?? "", NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { if (_legacyCounterLabel.GameObject.activeSelf) { _legacyCounterLabel.GameObject.SetActive(false); } return; } result = Mathf.RoundToInt(result2); } if (!float.TryParse(bloodInfoLatest.ProgressPct ?? "", NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { result3 = 0f; } int num = ((result3 > 0.01f) ? Mathf.RoundToInt((float)result * 100f / result3) : 0); string text = ((num > 0) ? $"Ess: {result} / {num} ({result3:0.0}%)" : $"Ess: {result} ({result3:0.0}%)"); ((TMP_Text)_legacyCounterLabel.TextMesh).text = text; if (!_legacyCounterLabel.GameObject.activeSelf) { _legacyCounterLabel.GameObject.SetActive(true); } } private static int ResolveBarHeight() { return Settings.ProgressBarHeight; } private void ApplyBarChrome() { int height = ResolveBarHeight(); MiniBar.SetHeight(_xpBar, height); MiniBar.SetHeight(_weaponBar, height); MiniBar.SetHeight(_legacyBar, height); } private static void ApplySubLine(GameObject bar, RectTransform subFill, bool wantVisible, float progress01) { if ((Object)(object)bar == (Object)null || (Object)(object)subFill == (Object)null) { return; } GameObject gameObject = ((Component)subFill).gameObject; if (!wantVisible) { if (gameObject.activeSelf) { gameObject.SetActive(false); } return; } if (!gameObject.activeSelf) { gameObject.SetActive(true); } MiniBar.SetProgress(subFill, progress01); } private void RenderLegacyStats() { if (_legacyStatsLabel == null) { return; } bool showOverlayBonusStats = Settings.ShowOverlayBonusStats; PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; bool flag = !string.IsNullOrEmpty(bloodInfoLatest.BloodType) && bloodInfoLatest.StatLines != null && bloodInfoLatest.StatLines.Count > 0; if (!showOverlayBonusStats || !flag) { if (_legacyStatsLabel.GameObject.activeSelf) { _legacyStatsLabel.GameObject.SetActive(false); } return; } ((TMP_Text)_legacyStatsLabel.TextMesh).text = string.Join("\n", bloodInfoLatest.StatLines); if (!_legacyStatsLabel.GameObject.activeSelf) { _legacyStatsLabel.GameObject.SetActive(true); } } private void BonusStatsTick() { RenderWeaponStats(); RenderLegacyStats(); if (!Settings.ShowOverlayBonusStats || !MessageService.IsInitialized) { return; } bool valueOrDefault = (Plugin.UIManager?.CombinedOverlay?.Enabled).GetValueOrDefault(); if (!base.Enabled && !valueOrDefault) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (_lastBonusStatsFetchAt > 0.0 && realtimeSinceStartupAsDouble - _lastBonusStatsFetchAt < 10.0) { return; } _lastBonusStatsFetchAt = realtimeSinceStartupAsDouble; try { if (_bonusStatsFetchToggle) { MessageService.EnqueueMessageSilent(".wep get"); } else { PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; if (PlayerStateService.IsBondableBloodType(legacy.Type)) { MessageService.EnqueueMessageSilent($".bl get {legacy.Type}"); } } _bonusStatsFetchToggle = !_bonusStatsFetchToggle; } catch (Exception ex) { LogUtils.LogWarning("ExperienceOverlay: bonus-stats refresh failed — " + ex.Message); } } private void OnPrestigeInfoChanged() { TryRenderExoFromState(); } private void TryRenderExoFromState() { PlayerStateService.PrestigeInfo prestigeInfoLatest = PlayerStateService.PrestigeInfoLatest; if (prestigeInfoLatest.TypeName != null && string.Equals(prestigeInfoLatest.TypeName, "Exo", StringComparison.OrdinalIgnoreCase)) { _exoLevel = prestigeInfoLatest.Level; _exoMaxLevel = prestigeInfoLatest.MaxLevel; if (_exoLabel != null) { ((TMP_Text)_exoLabel.TextMesh).text = $"EXO Prestige: {_exoLevel}" + ((_exoMaxLevel > 0) ? $" / {_exoMaxLevel}" : ""); } } } private void Render(PlayerStateService.ExperienceState s) { if (_levelLabel != null) { ApplyBarChrome(); string text = ((s.Prestige > 0) ? $"Level {s.Level} Prestige {s.Prestige}" : $"Level {s.Level}"); ((TMP_Text)_levelLabel.TextMesh).text = text; ((TMP_Text)_progressLabel.TextMesh).text = $"XP {s.Progress * 100f:0.#}%"; ((TMP_Text)_classLabel.TextMesh).text = $"Class: {s.Class}"; bool showProgressBarXP = Settings.ShowProgressBarXP; if ((Object)(object)_xpBar != (Object)null && _xpBar.activeSelf != showProgressBarXP) { _xpBar.SetActive(showProgressBarXP); } if (showProgressBarXP) { MiniBar.SetProgress(_xpBarFill, s.Progress); int maxPlayerLevel = PlayerStateService.Config.MaxPlayerLevel; float progress = ((maxPlayerLevel > 0) ? Mathf.Clamp01((float)s.Level / (float)maxPlayerLevel) : 0f); ApplySubLine(_xpBar, _xpBarSubFill, Settings.ShowPrestigeSubLine && maxPlayerLevel > 0, progress); } } } internal override void Reset() { if (_subscribed) { PlayerStateService.ExperienceChanged -= OnExperienceChanged; _subscribed = false; } if (_expertiseSubscribed) { PlayerStateService.ExpertiseChanged -= OnExpertiseChanged; _expertiseSubscribed = false; } if (_legacySubscribed) { PlayerStateService.LegacyChanged -= OnLegacyChanged; _legacySubscribed = false; } if (_prestigeSubscribed) { PlayerStateService.PrestigeInfoChanged -= OnPrestigeInfoChanged; _prestigeSubscribed = false; } if (_lastResponseSubscribed) { PlayerStateService.LastResponseChanged -= OnLastResponseChanged; _lastResponseSubscribed = false; } if (_bloodInfoSubscribed) { PlayerStateService.BloodInfoChanged -= OnBloodInfoChanged; _bloodInfoSubscribed = false; } if (_bonusStatsTicker != null) { CoreUpdateBehavior.Actions.Remove(_bonusStatsTicker); _bonusStatsTicker = null; } } } public class FamiliarBrowserOverlayPanel : ResizeablePanelBase { private enum ViewMode { BoxView, VBloodView } private struct VBloodOverlayRow { public string BaseName; public string DisplayName; public string Box; public int Index; public int Level; public int Prestige; public bool IsShiny; public bool IsPrimal; public string ShinySchool; } private TextMeshProUGUI _boxNameLabel; private TextMeshProUGUI _activeFamLabel; private TextMeshProUGUI _swapWarningLabel; private GameObject _famListContainer; private ButtonRef _toggleBtn; private ButtonRef _unbindBtn; private ButtonRef _sortBtn; private ButtonRef _scanBtn; private ViewMode _viewMode; private ButtonRef _viewBtn; private GameObject _boxNavRow; private TextMeshProUGUI _vbStatusLabel; private bool _vbSummonStatusSubscribed; private bool _vbCollectionSubscribed; private int _pendingSwapIndex = -1; private float _pendingSwapDeadline = -1f; private const float SWAP_CONFIRM_WINDOW_SECONDS = 5f; private bool _subscribed; private bool _autoPullDone; public override string PanelId => "FamiliarBrowserOverlay"; public override PanelType PanelType => PanelType.FamiliarBrowserOverlay; public override int MinWidth => 280; public override int MinHeight => 440; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2(base.Owner.Scaler.m_ReferenceResolution.x * 0.5f - 320f, -40f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.FamiliarBrowserTransparency); public override bool UsesCustomBackgroundColor => true; public override bool UsesCustomInnerBackgroundColor => true; public FamiliarBrowserOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { base.ConstructPanelContent(); VerticalLayoutGroup component = base.ContentRoot.GetComponent(); if ((Object)(object)component != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; } BuildHeader(); BuildFamiliarList(); BuildFooter(); if (!_subscribed) { PlayerStateService.BoxListChanged += OnAnyBoxStateChanged; PlayerStateService.BoxContentsChanged += OnAnyBoxStateChanged; PlayerStateService.ActiveBoxChanged += OnAnyBoxStateChanged; PlayerStateService.FamiliarChanged += OnAnyBoxStateChanged; _subscribed = true; } if (!_vbCollectionSubscribed) { PlayerStateService.VBloodCollectionChanged += OnAnyBoxStateChanged; VBloodScannerService.ScanStateChanged += OnScanStateChanged; _vbCollectionSubscribed = true; } ApplyViewModeVisibility(); CoreUpdateBehavior.Actions.Add(TickDeferredAutoPull); Render(); } private void TickDeferredAutoPull() { if (!_autoPullDone && MessageService.IsInitialized) { _autoPullDone = true; if (PlayerStateService.BoxList == null || PlayerStateService.BoxList.Count <= 0) { EnqueueOrWarn(".fam boxes"); } } } private void BuildHeader() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Unknown result type (might be due to invalid IL or missing references) //IL_066b: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(base.ContentRoot, "OverlayToolbar", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 260; int? preferredWidth = 280; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ButtonRef buttonRef = UIFactory.CreateButton(val, "BoxPrev", "←"); GameObject gameObject = buttonRef.GameObject; int? minWidth2 = 28; int? preferredWidth2 = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledOverlay(16); ((TMP_Text)componentInChildren).fontStyle = (FontStyles)1; } buttonRef.OnClick = delegate { CycleBox(-1); }; TooltipHover.Attach(buttonRef.GameObject, "Previous box (cycles left through your familiar boxes)."); ButtonRef buttonRef2 = UIFactory.CreateButton(val, "BoxNext", "→"); GameObject gameObject2 = buttonRef2.GameObject; int? minWidth3 = 28; preferredWidth = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren2 = ((Component)buttonRef2.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledOverlay(16); ((TMP_Text)componentInChildren2).fontStyle = (FontStyles)1; } buttonRef2.OnClick = delegate { CycleBox(1); }; TooltipHover.Attach(buttonRef2.GameObject, "Next box (cycles right through your familiar boxes)."); ButtonRef buttonRef3 = UIFactory.CreateButton(val, "BoxRefresh", "Reload"); GameObject gameObject3 = buttonRef3.GameObject; int? minWidth4 = 54; preferredWidth2 = 60; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren3 = ((Component)buttonRef3.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren3 != (Object)null) { ((TMP_Text)componentInChildren3).fontSize = Theme.ScaledOverlay(11); } buttonRef3.OnClick = delegate { EnqueueOrWarn(".fam boxes"); EnqueueOrWarn(".fam l"); }; TooltipHover.Attach(buttonRef3.GameObject, "Re-pull the box list AND the current box's familiar list from the server."); _scanBtn = UIFactory.CreateButton(val, "OverlayScanBtn", FormatScanBtnText()); GameObject gameObject4 = _scanBtn.GameObject; int? minWidth5 = 54; preferredWidth = 60; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren4 = ((Component)_scanBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren4 != (Object)null) { ((TMP_Text)componentInChildren4).fontSize = Theme.ScaledOverlay(11); } _scanBtn.OnClick = delegate { if (VBloodScannerService.Scanning) { VBloodScannerService.CancelScan(); } else { VBloodScannerService.StartScan(); } RefreshScanBtnText(); }; TooltipHover.Attach(_scanBtn.GameObject, "Run the V-Blood box-sweep scan from the overlay — same as the V-Bloods tab's Scan all button. Walks every box; ~30-60s; restores your active box at the end. Click again while running to cancel."); _viewBtn = UIFactory.CreateButton(val, "ViewModeBtn", FormatViewBtnText()); GameObject gameObject5 = _viewBtn.GameObject; int? minWidth6 = 60; preferredWidth2 = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject5, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren5 = ((Component)_viewBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren5 != (Object)null) { ((TMP_Text)componentInChildren5).fontSize = Theme.ScaledOverlay(10); } _viewBtn.OnClick = delegate { _viewMode = ((_viewMode == ViewMode.BoxView) ? ViewMode.VBloodView : ViewMode.BoxView); RefreshViewBtnText(); ApplyViewModeVisibility(); Render(); }; TooltipHover.Attach(_viewBtn.GameObject, "Switch between the box-by-box familiar browser (default) and a compact V-Blood collection view sourced from the V-Blood scanner. Click Scan above to populate V-Blood data."); _sortBtn = UIFactory.CreateButton(val, "FamSortBtn", FormatSortBtnText()); GameObject gameObject6 = _sortBtn.GameObject; int? minWidth7 = 50; preferredWidth = 56; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject6, minWidth: minWidth7, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren6 = ((Component)_sortBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren6 != (Object)null) { ((TMP_Text)componentInChildren6).fontSize = Theme.ScaledOverlay(10); } _sortBtn.OnClick = delegate { Settings.FamiliarSortOrder familiarSortOrderSetting = Settings.FamiliarSortOrderSetting; Settings.SetFamiliarSortOrder(((familiarSortOrderSetting != Settings.FamiliarSortOrder.Location) ? familiarSortOrderSetting : Settings.FamiliarSortOrder.Default) switch { Settings.FamiliarSortOrder.Default => Settings.FamiliarSortOrder.Alphabetical, Settings.FamiliarSortOrder.Alphabetical => Settings.FamiliarSortOrder.Level, Settings.FamiliarSortOrder.Level => Settings.FamiliarSortOrder.Default, _ => Settings.FamiliarSortOrder.Default, }); RefreshSortBtnText(); Render(); }; TooltipHover.Attach(_sortBtn.GameObject, "Cycle the list sort order: Box (server order) → A→Z → Level (descending)."); GameObject val2 = UIFactory.CreateHorizontalGroup(base.ContentRoot, "BoxNameRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 0, new Vector4(0f, 0f, 0f, 0f)); int? minWidth8 = 260; preferredWidth2 = 280; num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: val2, minWidth: minWidth8, flexibleHeight: 0, preferredWidth: preferredWidth2); _boxNavRow = val2; LabelRef labelRef = UIFactory.CreateLabel(val2, "BoxName", "(no box)", (TextAlignmentOptions)514, null, Theme.ScaledOverlay(13)); GameObject gameObject7 = labelRef.GameObject; int? minWidth9 = 200; preferredWidth = 260; num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject7, minWidth: minWidth9, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; _boxNameLabel = labelRef.TextMesh; LabelRef labelRef2 = UIFactory.CreateLabel(base.ContentRoot, "StatusLine", "Active: (none)", Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(12)); GameObject gameObject8 = labelRef2.GameObject; int? minWidth10 = 260; preferredWidth2 = 280; num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject8, minWidth: minWidth10, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)1; _activeFamLabel = labelRef2.TextMesh; _swapWarningLabel = labelRef2.TextMesh; } private string FormatScanBtnText() { if (!VBloodScannerService.Scanning) { return "Scan"; } return "Cancel"; } private void RefreshScanBtnText() { if (_scanBtn != null) { TextMeshProUGUI componentInChildren = ((Component)_scanBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = FormatScanBtnText(); } } } private void BuildFamiliarList() { //IL_0015: 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) UIFactory.SetLayoutElement(UIFactory.CreateScrollView(base.ContentRoot, "FamListScroll", out _famListContainer, out var _), 260, preferredWidth: 280, flexibleWidth: 1, minHeight: 80, flexibleHeight: 1); if ((Object)(object)_famListContainer != (Object)null) { VerticalLayoutGroup component = _famListContainer.GetComponent(); if ((Object)(object)component != (Object)null) { int overlayEdgePadding = Settings.OverlayEdgePadding; RectOffset padding = ((LayoutGroup)component).padding; padding.left = overlayEdgePadding; padding.right = overlayEdgePadding; ((LayoutGroup)component).padding = padding; } } } private void BuildFooter() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(base.ContentRoot, "Footer", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 260; int? preferredWidth = 280; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); _toggleBtn = UIFactory.CreateButton(val, "Toggle", "Toggle"); GameObject gameObject = _toggleBtn.GameObject; int? minWidth2 = 60; int? preferredWidth2 = 100; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); _toggleBtn.OnClick = delegate { EnqueueOrWarn(".fam t"); }; TooltipHover.Attach(_toggleBtn.GameObject, ".fam t — Toggles your active familiar between enabled (visible / in combat) and disabled (hidden / out of combat). Flying and teleporting auto-disable the familiar but auto-re-enable on landing; dominating an NPC also disables it but does NOT re-enable — use Toggle to bring it back. Doesn't change which familiar is bound. Disabled when no familiar is bound."); _unbindBtn = UIFactory.CreateButton(val, "Unbind", "Unbind active"); GameObject gameObject2 = _unbindBtn.GameObject; int? minWidth3 = 90; preferredWidth = 140; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); _unbindBtn.OnClick = delegate { EnqueueOrWarn(".fam ub"); }; TooltipHover.Attach(_unbindBtn.GameObject, ".fam ub — Removes the active binding. Familiar returns to your box and can be re-bound any time; the box record is preserved. Use Toggle if you only want to temporarily disable the familiar in combat. (Permanent box deletion is .fam r N, available on the main Familiars tab.)"); } private static void EnqueueOrWarn(string command) { if (!MessageService.IsInitialized) { LogUtils.LogWarning("FamiliarBrowserOverlay: cannot send '" + command + "' — MessageService not bound yet."); } else { MessageService.EnqueueMessage(command); } } private void CycleBox(int direction) { List boxList = PlayerStateService.BoxList; if (boxList != null && boxList.Count != 0) { int num = boxList.IndexOf(PlayerStateService.ActiveBox ?? ""); if (num < 0) { num = 0; } num = (num + direction + boxList.Count) % boxList.Count; string text = boxList[num]; ClearPendingSwap(); PlayerStateService.SetActiveBox(text); EnqueueOrWarn($".fam cb {text}"); EnqueueOrWarn(".fam l"); } } private void OnAnyBoxStateChanged() { Render(); } private void OnScanStateChanged() { RefreshScanBtnText(); if (!((Object)(object)_activeFamLabel == (Object)null) && VBloodScannerService.Scanning) { string currentBoxBeingScanned = VBloodScannerService.CurrentBoxBeingScanned; string value = (string.IsNullOrEmpty(currentBoxBeingScanned) ? "" : (" — " + currentBoxBeingScanned)); ((TMP_Text)_activeFamLabel).text = $"Scanning… box {VBloodScannerService.CompletedForCurrentScan + 1} / {VBloodScannerService.TotalForCurrentScan}{value}"; } } private void Render() { //IL_0463: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_boxNameLabel == (Object)null) { return; } if (_viewMode == ViewMode.VBloodView) { RenderVBloodView(); return; } List boxList = PlayerStateService.BoxList; string activeBox = PlayerStateService.ActiveBox; if (string.IsNullOrEmpty(activeBox)) { ((TMP_Text)_boxNameLabel).text = "(no box selected)"; } else if (boxList != null && boxList.Count > 0) { int num = boxList.IndexOf(activeBox); if (num >= 0) { ((TMP_Text)_boxNameLabel).text = $"{activeBox} ({num + 1} / {boxList.Count})"; } else { ((TMP_Text)_boxNameLabel).text = activeBox; } } else { ((TMP_Text)_boxNameLabel).text = activeBox; } PlayerStateService.FamiliarState familiar = PlayerStateService.Familiar; ((TMP_Text)_activeFamLabel).text = (string.IsNullOrEmpty(familiar.Name) ? "Active: (none bound)" : $"Active: {familiar.Name} Lv {familiar.Level}"); bool interactable = !string.IsNullOrEmpty(familiar.Name); if (_toggleBtn != null) { ((Selectable)_toggleBtn.Component).interactable = interactable; } if (_unbindBtn != null) { ((Selectable)_unbindBtn.Component).interactable = interactable; } ClearChildren(_famListContainer); if (PlayerStateService.BoxList == null || PlayerStateService.BoxList.Count == 0) { AddListLine("(no boxes loaded — click Reload above)"); return; } if (string.IsNullOrEmpty(activeBox)) { AddListLine("(use ← / → above to pick a box)"); return; } if (!PlayerStateService.BoxContents.TryGetValue(activeBox, out var value) || value.Count == 0) { AddListLine("(loading familiars for " + activeBox + "…)"); return; } List list = new List(value); switch (Settings.FamiliarSortOrderSetting) { case Settings.FamiliarSortOrder.Alphabetical: list.Sort((PlayerStateService.FamiliarBoxEntry a, PlayerStateService.FamiliarBoxEntry b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); break; case Settings.FamiliarSortOrder.Level: list.Sort((PlayerStateService.FamiliarBoxEntry a, PlayerStateService.FamiliarBoxEntry b) => (b.Level != a.Level) ? b.Level.CompareTo(a.Level) : string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); break; case Settings.FamiliarSortOrder.Location: list.Sort(delegate(PlayerStateService.FamiliarBoxEntry a, PlayerStateService.FamiliarBoxEntry b) { string name = ((a.Name != null && a.Name.StartsWith("Primal ", StringComparison.OrdinalIgnoreCase)) ? a.Name.Substring("Primal ".Length) : a.Name); string name2 = ((b.Name != null && b.Name.StartsWith("Primal ", StringComparison.OrdinalIgnoreCase)) ? b.Name.Substring("Primal ".Length) : b.Name); int num3 = VBloodRegistry.RegionOrderFor(name); int num4 = VBloodRegistry.RegionOrderFor(name2); return (num3 != num4) ? num3.CompareTo(num4) : string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); }); break; } foreach (PlayerStateService.FamiliarBoxEntry item in list) { int idx = item.Index; string text = $"{item.Index:00} — {item.Name}"; if (item.Level > 0) { text += $" Lv {item.Level}"; } if (item.Prestige > 0) { text += $" P{item.Prestige}"; } if (item.IsShiny) { text += " ★"; string shinySchool = item.ShinySchool; if (!string.IsNullOrEmpty(shinySchool)) { text = text + " " + shinySchool; } } ButtonRef buttonRef = UIFactory.CreateButton(_famListContainer, $"FamBtn_{item.Index}", text); GameObject gameObject = buttonRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num2 = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num2, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).alignment = Theme.OverlayMidlineAlignment(); ((TMP_Text)componentInChildren).fontSize = Theme.ScaledOverlay(12); ((TMP_Text)componentInChildren).enableWordWrapping = false; ((TMP_Text)componentInChildren).overflowMode = (TextOverflowModes)0; } buttonRef.OnClick = delegate { OnFamiliarClicked(idx); }; } } public void NotifySortOrderChanged() { RefreshSortBtnText(); if (base.Enabled) { Render(); } } private string FormatViewBtnText() { if (_viewMode != ViewMode.VBloodView) { return "View: Box"; } return "View: V-Bloods"; } private void RefreshViewBtnText() { if (_viewBtn != null) { TextMeshProUGUI componentInChildren = ((Component)_viewBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = FormatViewBtnText(); } } } private void ApplyViewModeVisibility() { if ((Object)(object)_boxNavRow != (Object)null) { _boxNavRow.SetActive(_viewMode == ViewMode.BoxView); } } private string FormatSortBtnText() { return Settings.FamiliarSortOrderSetting switch { Settings.FamiliarSortOrder.Alphabetical => "A→Z", Settings.FamiliarSortOrder.Level => "Lv↓", Settings.FamiliarSortOrder.Location => "Box", _ => "Box", }; } private void RefreshSortBtnText() { if (_sortBtn != null) { TextMeshProUGUI componentInChildren = ((Component)_sortBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = FormatSortBtnText(); } } } private void RenderVBloodView() { //IL_0621: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (KeyValuePair item in PlayerStateService.VBloodCollection) { PlayerStateService.VBloodCaptureStatus value = item.Value; if (value.Instances == null) { continue; } foreach (PlayerStateService.VBloodInstance instance in value.Instances) { list.Add(new VBloodOverlayRow { BaseName = value.Name, DisplayName = (instance.IsPrimal ? ("Primal " + value.Name) : value.Name), Box = instance.Box, Index = instance.Index, Level = instance.Level, Prestige = instance.Prestige, IsShiny = instance.IsShiny, IsPrimal = instance.IsPrimal, ShinySchool = instance.ShinySchool }); } } int value2 = VBloodRegistry.All.Length; int num = 0; int num2 = 0; int num3 = 0; foreach (PlayerStateService.VBloodCaptureStatus value3 in PlayerStateService.VBloodCollection.Values) { if (value3.HasBasic || value3.HasShiny || value3.HasPrimal || value3.HasPrimalShiny) { num++; } if (value3.HasPrimal || value3.HasPrimalShiny) { num2++; } if (value3.HasShiny || value3.HasPrimalShiny) { num3++; } } string text = $"V-Bloods: {num} / {value2} ({list.Count} captured)"; if (num2 > 0) { text += $" · {num2}P"; } if (num3 > 0) { text += $" · {num3}★"; } string lastStatus = VBloodSummonService.LastStatus; if (!string.IsNullOrEmpty(lastStatus)) { text = text + " | " + lastStatus; } ((TMP_Text)_activeFamLabel).text = text; if (_toggleBtn != null) { ((Selectable)_toggleBtn.Component).interactable = false; } if (_unbindBtn != null) { ((Selectable)_unbindBtn.Component).interactable = false; } ClearChildren(_famListContainer); if (list.Count == 0) { AddListLine((PlayerStateService.VBloodCollection.Count == 0) ? "(no scan yet — click Scan above)" : "(no V-Bloods captured in your boxes)"); return; } switch (Settings.FamiliarSortOrderSetting) { case Settings.FamiliarSortOrder.Alphabetical: list.Sort(delegate(VBloodOverlayRow a, VBloodOverlayRow b) { int num8 = string.Compare(a.BaseName, b.BaseName, StringComparison.OrdinalIgnoreCase); return (num8 != 0) ? num8 : VariantOrder(a).CompareTo(VariantOrder(b)); }); break; case Settings.FamiliarSortOrder.Level: list.Sort(delegate(VBloodOverlayRow a, VBloodOverlayRow b) { if (b.Level != a.Level) { return b.Level.CompareTo(a.Level); } int num9 = string.Compare(a.BaseName, b.BaseName, StringComparison.OrdinalIgnoreCase); return (num9 != 0) ? num9 : VariantOrder(a).CompareTo(VariantOrder(b)); }); break; case Settings.FamiliarSortOrder.Location: list.Sort(delegate(VBloodOverlayRow a, VBloodOverlayRow b) { int num5 = VBloodRegistry.RegionOrderFor(a.BaseName); int num6 = VBloodRegistry.RegionOrderFor(b.BaseName); if (num5 != num6) { return num5.CompareTo(num6); } int num7 = string.Compare(a.BaseName, b.BaseName, StringComparison.OrdinalIgnoreCase); return (num7 != 0) ? num7 : VariantOrder(a).CompareTo(VariantOrder(b)); }); break; default: list.Sort(delegate(VBloodOverlayRow a, VBloodOverlayRow b) { int num10 = string.Compare(a.Box ?? "", b.Box ?? "", StringComparison.OrdinalIgnoreCase); return (num10 != 0) ? num10 : a.Index.CompareTo(b.Index); }); break; } foreach (VBloodOverlayRow item2 in list) { string text2 = $"{item2.Index:00} — {item2.DisplayName}"; if (item2.Level > 0) { text2 += $" Lv {item2.Level}"; } if (item2.Prestige > 0) { text2 += $" P{item2.Prestige}"; } if (item2.IsShiny) { text2 += " ★"; if (!string.IsNullOrEmpty(item2.ShinySchool)) { text2 = text2 + " " + item2.ShinySchool; } } ButtonRef buttonRef = UIFactory.CreateButton(_famListContainer, $"VBRow_{item2.BaseName}_{item2.Box}_{item2.Index}_{(item2.IsPrimal ? 80 : 66)}{(item2.IsShiny ? 83 : 78)}", text2); GameObject gameObject = buttonRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num4 = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num4, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).alignment = Theme.OverlayMidlineAlignment(); ((TMP_Text)componentInChildren).fontSize = Theme.ScaledOverlay(12); ((TMP_Text)componentInChildren).enableWordWrapping = false; ((TMP_Text)componentInChildren).overflowMode = (TextOverflowModes)0; } string captured_name = item2.BaseName; buttonRef.OnClick = delegate { if (!_vbSummonStatusSubscribed) { VBloodSummonService.StatusChanged += OnVBSummonStatus; _vbSummonStatusSubscribed = true; } VBloodSummonService.SummonVBlood(captured_name); }; } static int VariantOrder(VBloodOverlayRow r) { return (r.IsPrimal ? 2 : 0) + (r.IsShiny ? 1 : 0); } } private void OnVBSummonStatus(string status) { if (_viewMode == ViewMode.VBloodView && base.Enabled) { Render(); } } private void AddListLine(string text) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(_famListContainer, "ListLine", text, Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; } private void OnFamiliarClicked(int index) { if (string.IsNullOrEmpty(PlayerStateService.Familiar.Name)) { ClearPendingSwap(); EnqueueOrWarn($".fam b {index}"); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (_pendingSwapIndex == index && realtimeSinceStartup <= _pendingSwapDeadline) { ClearPendingSwap(); EnqueueOrWarn(".fam ub"); EnqueueOrWarn($".fam b {index}"); return; } string value = $"#{index}"; string activeBox = PlayerStateService.ActiveBox; if (!string.IsNullOrEmpty(activeBox) && PlayerStateService.BoxContents.TryGetValue(activeBox, out var value2)) { foreach (PlayerStateService.FamiliarBoxEntry item in value2) { if (item.Index == index) { value = item.Name; break; } } } _pendingSwapIndex = index; _pendingSwapDeadline = realtimeSinceStartup + 5f; if ((Object)(object)_swapWarningLabel != (Object)null) { ((TMP_Text)_swapWarningLabel).text = $"Active: {PlayerStateService.Familiar.Name}. Click {value} again within {5}s to unbind current and bind it."; } } private void ClearPendingSwap() { _pendingSwapIndex = -1; _pendingSwapDeadline = -1f; if ((Object)(object)_swapWarningLabel != (Object)null) { ((TMP_Text)_swapWarningLabel).text = ""; } } private static void ClearChildren(GameObject parent) { if (!((Object)(object)parent == (Object)null)) { Transform transform = parent.transform; for (int num = transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)transform.GetChild(num)).gameObject); } } } internal override void Reset() { if (_subscribed) { PlayerStateService.BoxListChanged -= OnAnyBoxStateChanged; PlayerStateService.BoxContentsChanged -= OnAnyBoxStateChanged; PlayerStateService.ActiveBoxChanged -= OnAnyBoxStateChanged; PlayerStateService.FamiliarChanged -= OnAnyBoxStateChanged; _subscribed = false; } if (_vbCollectionSubscribed) { PlayerStateService.VBloodCollectionChanged -= OnAnyBoxStateChanged; VBloodScannerService.ScanStateChanged -= OnScanStateChanged; _vbCollectionSubscribed = false; } CoreUpdateBehavior.Actions.Remove(TickDeferredAutoPull); _autoPullDone = false; } } public class FamiliarOverlayPanel : ResizeablePanelBase { private LabelRef _nameLabel; private LabelRef _progressLabel; private GameObject _xpBar; private RectTransform _xpBarFill; private LabelRef _statsLabel; private bool _subscribed; public override string PanelId => "FamiliarOverlay"; public override PanelType PanelType => PanelType.FamiliarOverlay; public override int MinWidth => 240; public override int MinHeight => 70; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2((0f - base.Owner.Scaler.m_ReferenceResolution.x) * 0.5f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f - 110f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.FamiliarOverlayTransparency); public override bool UsesCustomBackgroundColor => true; public FamiliarOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) base.ConstructPanelContent(); _nameLabel = AddRow("FamOvName", "—", (FontStyles)1, Theme.ScaledOverlay(15)); _progressLabel = AddRow("FamOvProgress", "Lv —", (FontStyles)0, Theme.ScaledOverlay(13)); _xpBar = MiniBar.Create(base.ContentRoot, "FamXpBar", out _xpBarFill, new Color(1f, 0.6f, 0.2f, 0.95f)); _xpBar.SetActive(false); _statsLabel = AddRow("FamOvStats", "HP —", (FontStyles)0, Theme.ScaledOverlay(12)); Render(PlayerStateService.Familiar); if (!_subscribed) { PlayerStateService.FamiliarChanged += OnFamiliarChanged; _subscribed = true; } } private LabelRef AddRow(string name, string text, FontStyles style, int fontSize) { //IL_0008: 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) LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, name, text, Theme.OverlayMidlineAlignment(), null, fontSize); GameObject gameObject = labelRef.GameObject; int? minWidth = 220; int? preferredWidth = 240; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = style; return labelRef; } private void OnFamiliarChanged() { Render(PlayerStateService.Familiar); } private void Render(PlayerStateService.FamiliarState s) { if (_nameLabel != null) { bool hasActive = s.HasActive; ((TMP_Text)_nameLabel.TextMesh).text = (s.HasActive ? s.Name : "(no familiar bound)"); ((TMP_Text)_progressLabel.TextMesh).text = ((!hasActive) ? "Lv —" : ((s.Prestige > 0) ? $"Lv {s.Level} ({s.Progress * 100f:0.#}%) Pr {s.Prestige}" : $"Lv {s.Level} ({s.Progress * 100f:0.#}%)")); ((TMP_Text)_statsLabel.TextMesh).text = (hasActive ? $"HP {s.MaxHealth} PP {s.PhysicalPower} SP {s.SpellPower}" : "HP —"); bool flag = hasActive && Settings.ShowProgressBarFamiliar; if ((Object)(object)_xpBar != (Object)null && _xpBar.activeSelf != flag) { _xpBar.SetActive(flag); } if (flag) { MiniBar.SetProgress(_xpBarFill, s.Progress); } } } internal override void Reset() { if (_subscribed) { PlayerStateService.FamiliarChanged -= OnFamiliarChanged; _subscribed = false; } } } public class FloatingButtonPanel : ResizeablePanelBase { public override string PanelId => "FloatingButton"; public override PanelType PanelType => PanelType.Base; public override int MinWidth => 104; public override int MinHeight => 56; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2(base.Owner.Scaler.m_ReferenceResolution.x * 0.5f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.None; public override float Opacity => Settings.UITransparency; public override bool ResizeWholePanel => true; public FloatingButtonPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) base.ConstructPanelContent(); ButtonRef buttonRef = UIFactory.CreateButton(base.ContentRoot, "HubToggleButton", "BCH"); GameObject gameObject = buttonRef.GameObject; int? minWidth = 40; int? preferredWidth = 40; int? num = 0; UIFactory.SetLayoutElement(minHeight: 40, flexibleWidth: num, preferredHeight: 40, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth, ignoreLayout: true); RectTransform component = buttonRef.GameObject.GetComponent(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(40f, 40f); component.anchoredPosition = new Vector2(-24f, 0f); buttonRef.OnClick = delegate { try { Plugin.UIManager.ToggleMainPanel(); } catch (Exception value2) { LogUtils.LogError($"FloatingButton click failed: {value2}"); } }; TooltipHover.Attach(buttonRef.GameObject, "Open or close the BloodCraftHub main panel."); ButtonRef buttonRef2 = UIFactory.CreateButton(base.ContentRoot, "OverlayToggleButton", "OV"); GameObject gameObject2 = buttonRef2.GameObject; int? minWidth2 = 40; int? preferredWidth2 = 40; num = 0; UIFactory.SetLayoutElement(minHeight: 40, flexibleWidth: num, preferredHeight: 40, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2, ignoreLayout: true); RectTransform component2 = buttonRef2.GameObject.GetComponent(); component2.anchorMin = new Vector2(0.5f, 0.5f); component2.anchorMax = new Vector2(0.5f, 0.5f); component2.pivot = new Vector2(0.5f, 0.5f); component2.sizeDelta = new Vector2(40f, 40f); component2.anchoredPosition = new Vector2(24f, 0f); buttonRef2.OnClick = delegate { try { Plugin.UIManager.ToggleAllOverlaysSuppressed(); } catch (Exception value) { LogUtils.LogError($"FloatingButton OV click failed: {value}"); } }; TooltipHover.Attach(buttonRef2.GameObject, "Show/hide all currently-enabled overlays. Useful when the in-game menus conflict with overlay positioning on smaller screens. This only toggles overlays you've already enabled via the panel footer — it never makes hidden-by-config overlays visible. Session-only: overlays return to their configured visibility on game restart."); } internal override void Reset() { } } public class MainPanel : ResizeablePanelBase { private enum AllFamSort { BoxIndex, Alphabetical, LevelDesc, ShinyFirst } private struct AllFamRow { public string Box; public int Index; public string Name; public int Level; public int Prestige; public bool IsShiny; public string ShinySchool; public int RowKey => HashCode.Combine(Box ?? "", Index); } private enum VBloodFilter { All, Captured, Missing, ShinyOnly } private sealed class TabGroupDef { public string Title; public bool StartExpanded; public (PanelType Tab, string Label)[] Tabs; } private struct ClassInfo { public string DisplayName; public string Archetype; public string Tagline; public string[] WeaponSynergies; public string[] BloodSynergies; public string OnHitDebuff; public string OnHitSecondary; } private struct VBRowSpec { public string Name; public PlayerStateService.VBloodInstance Instance; public bool IsMissing; } private AllFamSort _allFamSort; private string _allFamSearch = ""; private GameObject _allFamRowContainer; private TextMeshProUGUI _allFamStatsLabel; private TextMeshProUGUI _allFamScanStatusLabel; private ButtonRef _allFamScanButton; private ButtonRef _allFamSortButton; private InputFieldRef _allFamSearchInput; private bool _allFamSubscribed; private int _allFamPendingDeleteRowKey = -1; private float _allFamPendingDeleteDeadline = -1f; private const float ALLFAM_DELETE_CONFIRM_WINDOW_SECONDS = 3f; private readonly Dictionary _tabContent = new Dictionary(); private readonly Dictionary _tabInnerContent = new Dictionary(); private readonly Dictionary _tabButtons = new Dictionary(); private Toggle _xpOverlayToggle; private Toggle _famOverlayToggle; private Toggle _famBrowserToggle; private Toggle _dqOverlayToggle; private Toggle _profOverlayToggle; private Toggle _shiftOverlayToggle; private Toggle _combinedOverlayToggle; private readonly Dictionary _overlayToggleGOs = new Dictionary(); private Toggle _combinedMasterToggle; private TextMeshProUGUI _famNameLabel; private TextMeshProUGUI _famProgressLabel; private TextMeshProUGUI _famStatsLabel; private bool _famSubscribed; private TextMeshProUGUI _famSearchResultHeader; private GameObject _famSearchResultList; private bool _famSearchSubscribed; private TextMeshProUGUI _classNameLabel; private TextMeshProUGUI _classLevelLabel; private TextMeshProUGUI _classDetailsLabel; private TextMeshProUGUI _wepClassSynergyLabel; private TextMeshProUGUI _blClassSynergyLabel; private bool _classSubscribed; private TextMeshProUGUI _wepTypeLabel; private TextMeshProUGUI _wepProgressLabel; private TextMeshProUGUI _wepBonusLabel; private TextMeshProUGUI _wepStatsValuesLabel; private bool _wepSubscribed; private bool _wepLastResponseSubscribed; private List _cachedWepGetLines; private TextMeshProUGUI _vbProgressLabel; private ButtonRef _vbScanButton; private TextMeshProUGUI _vbScanStatusLabel; private ButtonRef _vbSortButton; private GameObject _vbRowContainer; private bool _vbSubscribed; private VBloodFilter _vbFilter; private TextMeshProUGUI _blTypeLabel; private TextMeshProUGUI _blProgressLabel; private TextMeshProUGUI _blBonusLabel; private TextMeshProUGUI _blStatsValuesLabel; private bool _blSubscribed; private TextMeshProUGUI _blInfoTitleLabel; private TextMeshProUGUI _blInfoLevelLabel; private TextMeshProUGUI _blInfoStatsLabel; private bool _blInfoSubscribed; private TextMeshProUGUI _shiftSpellLabel; private TextMeshProUGUI _unarmedStatusLabel; private TextMeshProUGUI _unarmedBonusLabel; private bool _shiftSubscribed; private TextMeshProUGUI _prestigeXpLabel; private TextMeshProUGUI _prestigeLegacyLabel; private TextMeshProUGUI _prestigeExpertiseLabel; private TextMeshProUGUI _prestigeFamLabel; private bool _prestigeSubscribed; private GameObject _prestigeInfoSection; private TextMeshProUGUI _prestigeInfoTitleLabel; private TextMeshProUGUI _prestigeInfoLevelLabel; private TextMeshProUGUI _prestigeInfoEffectsLabel; private GameObject _prestigeBar; private RectTransform _prestigeBarFill; private bool _prestigeInfoSubscribed; private TextMeshProUGUI _lvlXpLabel; private TextMeshProUGUI _lvlLegacyLabel; private TextMeshProUGUI _lvlExpertiseLabel; private TextMeshProUGUI _lvlExpertiseBonusLabel; private TextMeshProUGUI _lvlFamLabel; private TextMeshProUGUI _lvlFamStatsLabel; private TextMeshProUGUI _lvlProfessions1Label; private TextMeshProUGUI _lvlProfessions2Label; private TextMeshProUGUI _lvlProfessions3Label; private TextMeshProUGUI _lvlProfessions4Label; private bool _lvlSubscribed; private int _clanListPage = 1; private TextMeshProUGUI _clanListPageLabel; private TextMeshProUGUI _boxesActiveBoxLabel; private TextMeshProUGUI _boxesContentHeading; private TextMeshProUGUI _boxesStatusLabel; private LabelRef _boxesSwapWarning; private GameObject _boxesPickerSection; private GameObject _boxesContentSection; private GameObject _boxesListContainer; private GameObject _boxesContentContainer; private bool _boxesShowingContents; private bool _boxesSubscribed; private int _pendingSwapIndex = -1; private float _pendingSwapDeadline = -1f; private const float SWAP_CONFIRM_WINDOW_SECONDS = 5f; private bool _boxesEditMode; private int _pendingDeleteIndex = -1; private float _pendingDeleteDeadline = -1f; private const float DELETE_CONFIRM_WINDOW_SECONDS = 3f; private Toggle _boxesEditModeToggle; private static readonly TabGroupDef[] TabGroups = new TabGroupDef[3] { new TabGroupDef { Title = "Bloodcraft", StartExpanded = true, Tabs = new(PanelType, string)[12] { (PanelType.FamiliarsTab, "Familiars"), (PanelType.BoxesTab, "Boxes"), (PanelType.VBloodsTab, "V-Bloods"), (PanelType.AllFamiliarsTab, "All Familiars"), (PanelType.ClassTab, "Class"), (PanelType.ExpertiseTab, "Weapon Expertise"), (PanelType.BloodLegacyTab, "Blood Legacy"), (PanelType.UnarmedShiftTab, "Unarmed + Shift"), (PanelType.PrestigeTab, "Prestige"), (PanelType.LevelsTab, "Levels"), (PanelType.DailyQuestTab, "Daily Quests"), (PanelType.AdminTab, "Admin") } }, new TabGroupDef { Title = "Kindred", StartExpanded = false, Tabs = new(PanelType, string)[6] { (PanelType.KindredLogisticsTab, "Logistics"), (PanelType.KindredLogisticsAdminTab, "Logistics: Admin"), (PanelType.KindredCommandsPlayerTab, "Commands"), (PanelType.KindredAdminPlayersTab, "Admin: Players"), (PanelType.KindredAdminServerTab, "Admin: Server"), (PanelType.KindredAdminWorldTab, "Admin: World") } }, new TabGroupDef { Title = "Settings and Help", StartExpanded = false, Tabs = new(PanelType, string)[6] { (PanelType.QuickStartTab, "Quick Start"), (PanelType.ModHelpTab, "Mod Help"), (PanelType.GameGuideTab, "Game Guide"), (PanelType.SettingsTab, "Settings"), (PanelType.VanillaAdminTab, "Vanilla Admin"), (PanelType.AboutTab, "About") } } }; private readonly Dictionary _groupExpanded = new Dictionary(); private readonly Dictionary _groupContent = new Dictionary(); private readonly Dictionary _groupHeaderText = new Dictionary(); private readonly Dictionary _groupHeaderButton = new Dictionary(); private bool _availabilitySubscribed; private GameObject _tabStripGo; private bool _isFullscreen; private Vector2 _preFullscreenSizeDelta; private Vector2 _preFullscreenAnchoredPos; private Vector2 _preFullscreenAnchorMin; private Vector2 _preFullscreenAnchorMax; private Vector2 _preFullscreenPivot; private bool _preFullscreenPinned; private ButtonRef _maximizeBtn; private bool _collapsibleSubscribed; private GameObject _lastResponseRoot; private GameObject _lastResponseBodyWrap; private TextMeshProUGUI _lastResponseHeader; private TextMeshProUGUI _lastResponseBody; private bool _lastResponseCollapsed; private bool _lastResponseSubscribed; private const float TAB_STRIP_MAX_WIDTH = 220f; private Action _deferredAvailabilityRefresh; private static readonly Dictionary ClassInfoByClass = new Dictionary { [PlayerStateService.PlayerClass.BloodKnight] = new ClassInfo { DisplayName = "Blood Knight", Archetype = "Warrior", Tagline = "Tank-leaning vampire warrior; sword + life-leech identity.", WeaponSynergies = new string[4] { "Max Health", "Primary Attack Speed", "Primary Life Leech", "Physical Power" }, BloodSynergies = new string[4] { "Damage Reduction", "Reduced Blood Drain", "Weapon Cooldown Recovery", "Ability Attack Speed" }, OnHitDebuff = "Leech", OnHitSecondary = "Lesser Bloodrage self-buff" }, [PlayerStateService.PlayerClass.VampireLord] = new ClassInfo { DisplayName = "Vampire Lord", Archetype = "Warrior", Tagline = "AOE / sustain warrior; mace + scholar blood scales spell power.", WeaponSynergies = new string[4] { "Max Health", "Spell Life Leech", "Physical Power", "Spell Power" }, BloodSynergies = new string[4] { "Damage Reduction", "Spell Resistance", "Ultimate Cooldown Recovery", "Corruption Damage Reduction" }, OnHitDebuff = "Chill", OnHitSecondary = "Lesser Frozen Weapon self-buff" }, [PlayerStateService.PlayerClass.DemonHunter] = new ClassInfo { DisplayName = "Demon Hunter", Archetype = "Rogue", Tagline = "Ranged / holy crit-driven physical damage.", WeaponSynergies = new string[4] { "Movement Speed", "Primary Attack Speed", "Physical Crit Chance", "Physical Crit Damage" }, BloodSynergies = new string[4] { "Physical Resistance", "Reduced Blood Drain", "Weapon Cooldown Recovery", "Minion Damage" }, OnHitDebuff = "Static", OnHitSecondary = "Lesser Stormshield self-buff" }, [PlayerStateService.PlayerClass.ShadowBlade] = new ClassInfo { DisplayName = "Shadow Blade", Archetype = "Rogue", Tagline = "Dagger / shadow rogue; movement + crit-chance leaning.", WeaponSynergies = new string[4] { "Movement Speed", "Primary Attack Speed", "Physical Power", "Physical Crit Damage" }, BloodSynergies = new string[4] { "Spell Resistance", "Reduced Blood Drain", "Weapon Cooldown Recovery", "Ability Attack Speed" }, OnHitDebuff = "Ignite", OnHitSecondary = "Lesser Powersurge self-buff" }, [PlayerStateService.PlayerClass.ArcaneSorcerer] = new ClassInfo { DisplayName = "Arcane Sorcerer", Archetype = "Caster", Tagline = "Pure spell-power caster; scholar blood is the natural pair.", WeaponSynergies = new string[4] { "Spell Life Leech", "Spell Power", "Spell Crit Chance", "Spell Crit Damage" }, BloodSynergies = new string[4] { "Healing Received", "Spell Cooldown Recovery", "Ultimate Cooldown Recovery", "Ability Attack Speed" }, OnHitDebuff = "Weaken", OnHitSecondary = "Lesser Aegis self-buff" }, [PlayerStateService.PlayerClass.DeathMage] = new ClassInfo { DisplayName = "Death Mage", Archetype = "Caster", Tagline = "Necromancy-themed caster; shadow / corruption synergies.", WeaponSynergies = new string[4] { "Max Health", "Spell Life Leech", "Spell Power", "Spell Crit Damage" }, BloodSynergies = new string[4] { "Physical Resistance", "Spell Resistance", "Spell Cooldown Recovery", "Minion Damage" }, OnHitDebuff = "Condemn", OnHitSecondary = "Guardian Block self-buff" } }; private const string VB_VARIANT_BASIC_HEX = "#7CDA7C"; private const string VB_VARIANT_SHINY_HEX = "#9AE0FF"; private const string VB_VARIANT_PRIMAL_HEX = "#FFC066"; private const string VB_VARIANT_PRIMAL_SHINY_HEX = "#FFA0F0"; private const string VB_MISSING_HEX = "#888888"; private const int VB_COL_TAG_W = 36; private const int VB_COL_LV_W = 70; private const int VB_COL_SHINY_W = 96; private const int VB_COL_BOX_W = 100; private const int VB_COL_SUMMON_W = 78; private const int VB_ROW_SPACING = 6; private bool _vbSummonStatusSubscribed; private TextMeshProUGUI _dqDailyTargetLabel; private TextMeshProUGUI _dqDailyProgressLabel; private TextMeshProUGUI _dqWeeklyTargetLabel; private TextMeshProUGUI _dqWeeklyProgressLabel; private bool _dqSubscribed; private ToggleRef _chatBchAutoToggle; private ToggleRef _chatBloodcraftToggle; private ToggleRef _chatKindredToggle; private const int SIZE_STEP_NORMAL = 20; private const int SIZE_STEP_LARGE = 100; private readonly List _sizePosRefreshers = new List(); private Action _sizePosReadoutTicker; private TextMeshProUGUI _panelBgCurrentLabel; private TextMeshProUGUI _innerBgCurrentLabel; private static readonly (string Label, string Hex)[] DefaultDarkPresets = new(string, string)[7] { ("Default", "#121212"), ("Black", "#000000"), ("Slate", "#1A1B25"), ("Wine", "#1F0A10"), ("Forest", "#0A1A0B"), ("Indigo", "#0E0A1F"), ("Crimson", "#3B0B0F") }; private static readonly (string Label, string Hex)[] DefaultBrightPresets = new(string, string)[7] { ("Default", "#666666"), ("Black", "#404040"), ("Slate", "#4A5070"), ("Wine", "#8B1A2E"), ("Forest", "#2A6E2E"), ("Indigo", "#3D2D80"), ("Crimson", "#A30000") }; private static readonly Color ARTICLE_HEADING_ACCENT = new Color(0.9f, 0.72f, 0.36f, 1f); private double _lastWepAutoFetchAt; private double _lastBlAutoFetchAt; private PlayerStateService.WeaponType _wepTabLastType; private PlayerStateService.BloodType _blTabLastType; private bool _wepTabTypeBaseline; private bool _blTabTypeBaseline; private Action _tabAutoRefreshTicker; private const double TAB_AUTO_REFRESH_SECONDS = 10.0; public override string PanelId => "MainPanel"; public override PanelType PanelType => PanelType.Base; public override int MinWidth => 960; public override int MinHeight => 700; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => Vector2.zero; public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.UITransparency; public override bool ResizeWholePanel => false; public override bool UsesCustomBackgroundColor => true; public override bool UsesCustomInnerBackgroundColor => true; protected override bool RespectsLockOverlays => false; public PanelType ActiveTab { get; private set; } = PanelType.FamiliarsTab; public bool IsFullscreen => _isFullscreen; private void BuildAllFamiliarsTab(GameObject page) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: 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) GameObject parent = AddCard(page, "AllFamHeaderCard"); AddSectionHeading(parent, "All Familiars"); AddBodyText(parent, "Every familiar in every box, in one list. Click a row to switch to that box and bind the familiar. Delete permanently removes the entry (" + Mono(".fam r") + " — two-click confirm). The list is populated by the same V-Blood scanner — click " + Mono("Scan all") + " to walk every box; if you already scanned for V-Bloods the rows are already here."); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "AllFamStatusCard"); GameObject val2 = UIFactory.CreateHorizontalGroup(parent2, "AllFamStatusRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val2, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); _allFamStatsLabel = AddInfoLabel(val2, "AllFamStats", "0 familiars across 0 boxes", (FontStyles)1, Theme.ScaledUI(13)); GameObject gameObject = ((Component)_allFamStatsLabel).gameObject; int? minWidth2 = 180; int? preferredWidth2 = 260; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); _allFamScanButton = UIFactory.CreateButton(val2, "AllFamScanBtn", "Scan all"); GameObject gameObject2 = _allFamScanButton.GameObject; int? minWidth3 = 90; preferredWidth = 110; num = 0; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)_allFamScanButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(_allFamScanButton.GameObject, "Sweep every familiar box (same backend as the V-Bloods tab Scan). Populates this list AND the V-Blood collection in one pass. ~30–60s."); _allFamScanButton.OnClick = delegate { if (VBloodScannerService.Scanning) { VBloodScannerService.CancelScan(); } else { VBloodScannerService.StartScan(); } RefreshAllFamScanButton(); }; _allFamScanStatusLabel = AddInfoLabel(parent2, "AllFamScanStatus", "", (FontStyles)2, Theme.ScaledUI(11)); ((Component)_allFamScanStatusLabel).gameObject.SetActive(false); AddSpacer(page, 6); GameObject val3 = UIFactory.CreateHorizontalGroup(AddCard(page, "AllFamFilterCard"), "AllFamFilterRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth4 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); _allFamSearchInput = UIFactory.CreateInputField(val3, "AllFamSearch", "Filter by name…"); GameObject gameObject3 = _allFamSearchInput.GameObject; int? minWidth5 = 180; preferredWidth = 240; num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject3, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); _allFamSearchInput.OnValueChanged += delegate(string val) { _allFamSearch = val ?? ""; RebuildAllFamRows(); }; _allFamSortButton = UIFactory.CreateButton(val3, "AllFamSortBtn", FormatAllFamSortText()); GameObject gameObject4 = _allFamSortButton.GameObject; int? minWidth6 = 110; preferredWidth2 = 140; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject4, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren2 = ((Component)_allFamSortButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(_allFamSortButton.GameObject, "Cycle sort order: Box order (server) → A→Z → Level desc → Shinies first."); _allFamSortButton.OnClick = delegate { _allFamSort = _allFamSort switch { AllFamSort.BoxIndex => AllFamSort.Alphabetical, AllFamSort.Alphabetical => AllFamSort.LevelDesc, AllFamSort.LevelDesc => AllFamSort.ShinyFirst, AllFamSort.ShinyFirst => AllFamSort.BoxIndex, _ => AllFamSort.BoxIndex, }; RefreshAllFamSortButton(); RebuildAllFamRows(); }; AddSpacer(page, 6); GameObject parent3 = AddCard(page, "AllFamRowsCard", null, 4, 2); BuildAllFamColumnHeader(parent3); _allFamRowContainer = UIFactory.CreateVerticalGroup(parent3, "AllFamRows", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(0f, 0f, 2f, 2f)); UIFactory.SetLayoutElement(_allFamRowContainer, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 60, flexibleHeight: 0); if (!_allFamSubscribed) { PlayerStateService.BoxContentsChanged += OnAllFamBoxContentsChanged; VBloodScannerService.ScanStateChanged += OnAllFamScanStateChanged; _allFamSubscribed = true; } RebuildAllFamRows(); RefreshAllFamHeader(); RefreshAllFamScanButton(); } private void BuildAllFamColumnHeader(GameObject parent) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) GameObject headerRow = UIFactory.CreateHorizontalGroup(parent, "AllFamColHeader", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); GameObject gameObject = headerRow; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCol("Box · #", 90, 110, 0); AddCol("Familiar", 160, 200, 1); AddCol("Level", 46, 56, 0); AddCol("", 60, 80, 0); AddCol("", 56, 72, 0); void AddCol(string text, int min, int pref, int flex) { LabelRef labelRef = UIFactory.CreateLabel(headerRow, "Col_" + text, ColLabel(text), (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject2 = labelRef.GameObject; int? minWidth2 = min; int? preferredWidth2 = pref; int? num2 = flex; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num2, preferredHeight: 20, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } static string ColLabel(string s) { return $"{s}"; } } private void OnAllFamBoxContentsChanged() { RebuildAllFamRows(); RefreshAllFamHeader(); } private void OnAllFamScanStateChanged() { RefreshAllFamScanButton(); RefreshAllFamHeader(); } private void RefreshAllFamScanButton() { if (_allFamScanButton != null) { TextMeshProUGUI componentInChildren = ((Component)_allFamScanButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = (VBloodScannerService.Scanning ? "Cancel" : "Scan all"); } } } private void RefreshAllFamHeader() { if ((Object)(object)_allFamStatsLabel == (Object)null) { return; } int num = 0; int num2 = 0; int num3 = 0; foreach (KeyValuePair> boxContent in PlayerStateService.BoxContents) { if (boxContent.Value == null || boxContent.Value.Count == 0) { continue; } num++; foreach (PlayerStateService.FamiliarBoxEntry item in boxContent.Value) { num2++; if (item.IsShiny) { num3++; } } } string text = $"{num2} familiar{((num2 == 1) ? "" : "s")} across {num} box{((num == 1) ? "" : "es")}"; if (num3 > 0) { text += $" · {num3} shiny"; } ((TMP_Text)_allFamStatsLabel).text = text; if (!((Object)(object)_allFamScanStatusLabel != (Object)null)) { return; } if (VBloodScannerService.Scanning) { string currentBoxBeingScanned = VBloodScannerService.CurrentBoxBeingScanned; string value = (string.IsNullOrEmpty(currentBoxBeingScanned) ? "" : (" — " + currentBoxBeingScanned)); ((TMP_Text)_allFamScanStatusLabel).text = $"Scanning… box {VBloodScannerService.CompletedForCurrentScan + 1} / {VBloodScannerService.TotalForCurrentScan}{value}"; if (!((Component)_allFamScanStatusLabel).gameObject.activeSelf) { ((Component)_allFamScanStatusLabel).gameObject.SetActive(true); } } else if (((TMP_Text)_allFamScanStatusLabel).text != null && ((TMP_Text)_allFamScanStatusLabel).text.StartsWith("Scanning")) { ((Component)_allFamScanStatusLabel).gameObject.SetActive(false); } } private string FormatAllFamSortText() { return _allFamSort switch { AllFamSort.BoxIndex => "Sort: Box · #", AllFamSort.Alphabetical => "Sort: A→Z", AllFamSort.LevelDesc => "Sort: Level ↓", AllFamSort.ShinyFirst => "Sort: Shinies ↑", _ => "Sort: Box · #", }; } private void RefreshAllFamSortButton() { if (_allFamSortButton != null) { TextMeshProUGUI componentInChildren = ((Component)_allFamSortButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = FormatAllFamSortText(); } } } private void RebuildAllFamRows() { if ((Object)(object)_allFamRowContainer == (Object)null) { return; } for (int num = _allFamRowContainer.transform.childCount - 1; num >= 0; num--) { Transform child = _allFamRowContainer.transform.GetChild(num); if (!((Object)(object)child == (Object)null)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } string value = (_allFamSearch ?? "").Trim(); bool flag = !string.IsNullOrEmpty(value); List list = new List(); foreach (KeyValuePair> boxContent in PlayerStateService.BoxContents) { string key = boxContent.Key; List value2 = boxContent.Value; if (value2 == null) { continue; } foreach (PlayerStateService.FamiliarBoxEntry item in value2) { if (!string.IsNullOrEmpty(item.Name) && (!flag || item.Name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)) { list.Add(new AllFamRow { Box = key, Index = item.Index, Name = item.Name, Level = item.Level, Prestige = item.Prestige, IsShiny = item.IsShiny, ShinySchool = item.ShinySchool }); } } } switch (_allFamSort) { case AllFamSort.Alphabetical: list.Sort(delegate(AllFamRow a, AllFamRow b) { int num4 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); if (num4 != 0) { return num4; } num4 = string.Compare(a.Box ?? "", b.Box ?? "", StringComparison.OrdinalIgnoreCase); return (num4 != 0) ? num4 : a.Index.CompareTo(b.Index); }); break; case AllFamSort.LevelDesc: list.Sort(delegate(AllFamRow a, AllFamRow b) { if (b.Level != a.Level) { return b.Level.CompareTo(a.Level); } int num5 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); return (num5 != 0) ? num5 : a.Index.CompareTo(b.Index); }); break; case AllFamSort.ShinyFirst: list.Sort(delegate(AllFamRow a, AllFamRow b) { if (a.IsShiny != b.IsShiny) { return b.IsShiny.CompareTo(a.IsShiny); } int num3 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); return (num3 != 0) ? num3 : a.Index.CompareTo(b.Index); }); break; default: list.Sort(delegate(AllFamRow a, AllFamRow b) { int num6 = string.Compare(a.Box ?? "", b.Box ?? "", StringComparison.OrdinalIgnoreCase); return (num6 != 0) ? num6 : a.Index.CompareTo(b.Index); }); break; } if (list.Count == 0) { LabelRef labelRef = UIFactory.CreateLabel(_allFamRowContainer, "AllFamEmpty", (PlayerStateService.BoxContents.Count == 0) ? "(no box data yet — click Scan all)" : (flag ? "(no familiars match this search)" : "(no familiars captured)"), (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num2 = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num2, preferredHeight: 32, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; return; } foreach (AllFamRow item2 in list) { BuildAllFamRow(_allFamRowContainer, item2); } } private void BuildAllFamRow(GameObject parent, AllFamRow r) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, $"AllFamRow_{r.Box}_{r.Index}", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(val, "Box", $"{r.Box} · {r.Index:00}", (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 90; int? preferredWidth2 = 110; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)1; string text = r.Name; if (r.Prestige > 0) { text += $" P{r.Prestige}"; } if (r.IsShiny) { text += " ★"; if (!string.IsNullOrEmpty(r.ShinySchool)) { text = text + " " + r.ShinySchool; } } LabelRef labelRef2 = UIFactory.CreateLabel(val, "Name", text, (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject2 = labelRef2.GameObject; int? minWidth3 = 160; preferredWidth = 200; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)1; GameObject gameObject3 = UIFactory.CreateLabel(val, "Level", (r.Level > 0) ? $"Lv {r.Level}" : "—", (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)).GameObject; int? minWidth4 = 46; preferredWidth2 = 56; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(val, $"AllFamBind_{r.Box}_{r.Index}", "Bind"); GameObject gameObject4 = buttonRef.GameObject; int? minWidth5 = 60; preferredWidth = 80; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(11); } TooltipHover.Attach(buttonRef.GameObject, "Switch to this familiar's box and bind it. If you already have an active familiar, this issues an unbind first."); string captureBox = r.Box; int captureIdx = r.Index; buttonRef.OnClick = delegate { OnAllFamBindClicked(captureBox, captureIdx); }; ButtonRef delBtn = UIFactory.CreateButton(val, $"AllFamDel_{r.Box}_{r.Index}", "Delete", (Color?)new Color(0.55f, 0.18f, 0.18f)); GameObject gameObject5 = delBtn.GameObject; int? minWidth6 = 56; preferredWidth2 = 72; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject5, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren2 = ((Component)delBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(11); } TooltipHover.Attach(delBtn.GameObject, $"PERMANENTLY delete this familiar ({Mono(".fam r")}). Two-click confirm — first click changes the label to 'Confirm?' and waits {3f:0}s. Box record is gone forever."); int rowKey = r.RowKey; delBtn.OnClick = delegate { OnAllFamDeleteClicked(captureBox, captureIdx, rowKey, delBtn); }; } private void OnAllFamBindClicked(string box, int index) { if (string.IsNullOrEmpty(box) || index <= 0) { return; } if (!MessageService.IsInitialized) { LogUtils.LogWarning("AllFamiliars: cannot bind — MessageService not bound yet."); return; } if (PlayerStateService.Familiar.HasActive) { MessageService.EnqueueMessage(".fam ub"); } if (string.IsNullOrEmpty(PlayerStateService.ActiveBox) || !string.Equals(box, PlayerStateService.ActiveBox, StringComparison.OrdinalIgnoreCase)) { PlayerStateService.SetActiveBox(box); MessageService.EnqueueMessage($".fam cb {box}"); } MessageService.EnqueueMessage($".fam b {index}"); } private void OnAllFamDeleteClicked(string box, int index, int rowKey, ButtonRef btn) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (_allFamPendingDeleteRowKey == rowKey && realtimeSinceStartup <= _allFamPendingDeleteDeadline) { ClearAllFamPendingDelete(); TextMeshProUGUI componentInChildren = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = "Delete"; } if (!MessageService.IsInitialized) { LogUtils.LogWarning("AllFamiliars: cannot delete — MessageService not bound yet."); return; } if (string.IsNullOrEmpty(PlayerStateService.ActiveBox) || !string.Equals(box, PlayerStateService.ActiveBox, StringComparison.OrdinalIgnoreCase)) { PlayerStateService.SetActiveBox(box); MessageService.EnqueueMessage($".fam cb {box}"); } MessageService.EnqueueMessage($".fam r {index}"); MessageService.EnqueueMessage(".fam l"); } else { _allFamPendingDeleteRowKey = rowKey; _allFamPendingDeleteDeadline = realtimeSinceStartup + 3f; TextMeshProUGUI componentInChildren2 = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).text = "Confirm?"; } } } private void ClearAllFamPendingDelete() { _allFamPendingDeleteRowKey = -1; _allFamPendingDeleteDeadline = -1f; } private static IEnumerable<(PanelType Tab, string Label)> AllTabs() { TabGroupDef[] tabGroups = TabGroups; foreach (TabGroupDef tabGroupDef in tabGroups) { (PanelType Tab, string Label)[] tabs = tabGroupDef.Tabs; for (int j = 0; j < tabs.Length; j++) { yield return tabs[j]; } } } public MainPanel(UIBase owner) : base(owner) { } protected override void OnClosePanelClicked() { SetActive(active: false); } public void ToggleFullscreen() { SetFullscreen(!_isFullscreen); } public void SetFullscreen(bool fullscreen) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_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_0086: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)base.Rect == (Object)null) && fullscreen != _isFullscreen) { if (fullscreen) { _preFullscreenSizeDelta = base.Rect.sizeDelta; _preFullscreenAnchoredPos = base.Rect.anchoredPosition; _preFullscreenAnchorMin = base.Rect.anchorMin; _preFullscreenAnchorMax = base.Rect.anchorMax; _preFullscreenPivot = base.Rect.pivot; _preFullscreenPinned = IsPinned; base.Rect.anchorMin = Vector2.zero; base.Rect.anchorMax = Vector2.one; base.Rect.pivot = new Vector2(0.5f, 0.5f); base.Rect.offsetMin = new Vector2(20f, 20f); base.Rect.offsetMax = new Vector2(-20f, -20f); IsPinned = true; _isFullscreen = true; } else { base.Rect.anchorMin = _preFullscreenAnchorMin; base.Rect.anchorMax = _preFullscreenAnchorMax; base.Rect.pivot = _preFullscreenPivot; base.Rect.sizeDelta = _preFullscreenSizeDelta; base.Rect.anchoredPosition = _preFullscreenAnchoredPos; IsPinned = _preFullscreenPinned; _isFullscreen = false; } base.Dragger?.OnEndResize(); UpdateMaximizeBtnVisuals(); } } private void UpdateMaximizeBtnVisuals() { if (_maximizeBtn != null) { ((TMP_Text)_maximizeBtn.ButtonText).text = (_isFullscreen ? "[X]" : "[ ]"); } } private void BuildMaximizeButton() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)base.CloseButton == (Object)null)) { _maximizeBtn = UIFactory.CreateButton(base.CloseButton, "MaximizeButton", _isFullscreen ? "[X]" : "[ ]"); Object.Destroy((Object)(object)((Component)_maximizeBtn.Component).gameObject.GetComponent()); UIFactory.SetLayoutElement(((Component)_maximizeBtn.Component).gameObject, minHeight: 25, minWidth: 36, flexibleWidth: 0); Button component = _maximizeBtn.Component; ColorBlock colors = default(ColorBlock); ((ColorBlock)(ref colors)).normalColor = Theme.SliderHandle; ((ColorBlock)(ref colors)).colorMultiplier = 1f; ((Selectable)component).colors = colors; ButtonRef maximizeBtn = _maximizeBtn; maximizeBtn.OnClick = (Action)Delegate.Combine(maximizeBtn.OnClick, new Action(ToggleFullscreen)); ((Component)_maximizeBtn.Component).gameObject.transform.SetSiblingIndex(0); } } protected override void ConstructPanelContent() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) VerticalLayoutGroup component = base.ContentRoot.GetComponent(); if ((Object)(object)component != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; } GameObject val = UIFactory.CreateHorizontalGroup(base.ContentRoot, "Body", forceExpandWidth: false, forceExpandHeight: true, childControlWidth: true, childControlHeight: true, 4, new Vector4(6f, 6f, 6f, 6f)); int? flexibleHeight = 1; int? flexibleWidth = 1; UIFactory.SetLayoutElement(val, null, null, flexibleWidth, flexibleHeight); BuildTabStrip(val); BuildContentArea(val); BuildLastResponsePanel(base.ContentRoot); BuildOverlayFooter(base.ContentRoot); BuildTooltipFooter(base.ContentRoot); BuildMaximizeButton(); ShowTab(ActiveTab); if (_tabAutoRefreshTicker == null) { _tabAutoRefreshTicker = TickTabAutoRefresh; CoreUpdateBehavior.Actions.Add(_tabAutoRefreshTicker); } } private void BuildTooltipFooter(GameObject parent) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "TooltipFooter", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(8f, 8f, 4f, 4f)); int? minHeight = 56; int? preferredHeight = 56; int? flexibleHeight = 0; int? flexibleWidth = 1; UIFactory.SetLayoutElement(obj, null, minHeight, flexibleWidth, flexibleHeight, null, preferredHeight); LabelRef labelRef = UIFactory.CreateLabel(obj, "TooltipText", TooltipHover.IdlePlaceholder, (TextAlignmentOptions)4097, null, Theme.ScaledUI(13)); GameObject gameObject = labelRef.GameObject; int? minWidth = 400; flexibleWidth = 600; flexibleHeight = 1; UIFactory.SetLayoutElement(minHeight: 48, flexibleWidth: flexibleHeight, preferredHeight: 52, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: flexibleWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)1; ((Graphic)labelRef.TextMesh).color = Theme.MutedBody; TooltipHover.Sink = labelRef.TextMesh; LogUtils.LogInfo($"TooltipHover sink wired ({TooltipHover.BindingCount} bindings tracked)."); if (!_collapsibleSubscribed) { CollapsibleSection.Toggled += AutoResizeIfEnabled; _collapsibleSubscribed = true; } } private void BuildLastResponsePanel(GameObject parent) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) _lastResponseRoot = UIFactory.CreateVerticalGroup(parent, "LastResponsePanel", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(8f, 8f, 4f, 4f)); GameObject lastResponseRoot = _lastResponseRoot; int? minHeight = 0; int? flexibleHeight = 0; int? flexibleWidth = 1; UIFactory.SetLayoutElement(lastResponseRoot, null, minHeight, flexibleWidth, flexibleHeight); _lastResponseRoot.SetActive(false); ButtonRef buttonRef = UIFactory.CreateButton(_lastResponseRoot, "LastResponseHeaderBtn", ""); GameObject gameObject = buttonRef.GameObject; int? minWidth = 360; flexibleWidth = 600; flexibleHeight = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: flexibleHeight, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: flexibleWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)4097; ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).fontStyle = (FontStyles)3; _lastResponseHeader = componentInChildren; } buttonRef.OnClick = delegate { _lastResponseCollapsed = !_lastResponseCollapsed; if ((Object)(object)_lastResponseBodyWrap != (Object)null) { _lastResponseBodyWrap.SetActive(!_lastResponseCollapsed); } UpdateLastResponseHeaderText(); AutoResizeIfEnabled(); }; TooltipHover.Attach(buttonRef.GameObject, "Click to collapse/expand. Updates whenever you click a read-data command in any tab (.wep get, .class l, .misc userstats, .clan list, .boss list, etc.). Replies still also appear in chat unless you've enabled Clear server messages."); _lastResponseBodyWrap = UIFactory.CreateVerticalGroup(_lastResponseRoot, "LastResponseBody", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 0, new Vector4(6f, 6f, 4f, 4f)); GameObject lastResponseBodyWrap = _lastResponseBodyWrap; minHeight = 0; flexibleHeight = 0; flexibleWidth = 1; UIFactory.SetLayoutElement(lastResponseBodyWrap, null, minHeight, flexibleWidth, flexibleHeight); LabelRef labelRef = UIFactory.CreateLabel(_lastResponseBodyWrap, "LastResponseText", "", (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); UIFactory.SetLayoutElement(labelRef.GameObject, 360, preferredWidth: 600, flexibleWidth: 1, minHeight: 0, flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).richText = true; ApplyStrongAccentOutline(labelRef.TextMesh); ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; _lastResponseBody = labelRef.TextMesh; if (!_lastResponseSubscribed) { PlayerStateService.LastResponseChanged += OnLastResponseChanged; _lastResponseSubscribed = true; } } private void OnLastResponseChanged() { if (!((Object)(object)_lastResponseRoot == (Object)null)) { PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; _lastResponseRoot.SetActive(true); UpdateLastResponseHeaderText(); if ((Object)(object)_lastResponseBody != (Object)null) { ((TMP_Text)_lastResponseBody).text = ((lastResponse.Lines != null) ? string.Join("\n", lastResponse.Lines) : ""); } _lastResponseCollapsed = false; if ((Object)(object)_lastResponseBodyWrap != (Object)null) { _lastResponseBodyWrap.SetActive(true); } AutoResizeIfEnabled(); } } private void UpdateLastResponseHeaderText() { if (!((Object)(object)_lastResponseHeader == (Object)null)) { PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; string value = (_lastResponseCollapsed ? "▶" : "▼"); string value2 = (string.IsNullOrEmpty(lastResponse.Command) ? "(no response yet)" : lastResponse.Command); int num = lastResponse.Lines?.Count ?? 0; ((TMP_Text)_lastResponseHeader).text = $"{value} Last server response — {value2} ({num} line{((num == 1) ? "" : "s")})"; } } private void BuildTabStrip(GameObject parent) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!_availabilitySubscribed) { EclipseProtocolService.AvailabilityChanged += OnBloodcraftAvailabilityChanged; _availabilitySubscribed = true; } GameObject val = UIFactory.CreateVerticalGroup(parent, "TabStrip", forceWidth: false, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 220; int? preferredWidth = 220; int? flexibleWidth = 0; int? flexibleHeight = 1; UIFactory.SetLayoutElement(val, minWidth, null, flexibleWidth, flexibleHeight, preferredWidth); _tabStripGo = val; TabGroupDef[] tabGroups = TabGroups; foreach (TabGroupDef group in tabGroups) { BuildTabGroup(val, group); } } private void BuildTabGroup(GameObject parent, TabGroupDef group) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) _groupExpanded[group.Title] = group.StartExpanded; bool flag = IsTabGroupAvailable(group.Title); bool flag2 = group.StartExpanded && flag; ButtonRef buttonRef = UIFactory.CreateButton(parent, "GroupHeader_" + group.Title, FormatGroupHeader(group.Title, flag2, flag)); GameObject gameObject = buttonRef.GameObject; int? minWidth = 140; int? preferredWidth = 144; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)4097; ((TMP_Text)componentInChildren).enableWordWrapping = false; ((TMP_Text)componentInChildren).overflowMode = (TextOverflowModes)0; ((TMP_Text)componentInChildren).fontStyle = (FontStyles)1; ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); if (!flag) { ((Graphic)componentInChildren).color = new Color(0.55f, 0.55f, 0.55f); } _groupHeaderText[group.Title] = componentInChildren; } if (!flag) { ((Selectable)buttonRef.Component).interactable = false; } _groupHeaderButton[group.Title] = buttonRef; TooltipHover.Attach(buttonRef.GameObject, flag ? ("Show / hide the " + group.Title + " tab list.") : (group.Title + " is marked unavailable on this server (no backing mod detected). Adjust via .cfg: BloodcraftAvailability / KindredAvailability = On to force-enable.")); GameObject val = UIFactory.CreateVerticalGroup(parent, "GroupContent_" + group.Title, forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(6f, 2f, 2f, 2f)); int? minWidth2 = 140; int? preferredWidth2 = 144; num = 1; UIFactory.SetLayoutElement(minHeight: 0, flexibleWidth: num, preferredHeight: Mathf.Max(28, group.Tabs.Length * 30 + 4), gameObject: val, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); _groupContent[group.Title] = val; if (group.Tabs.Length == 0) { LabelRef labelRef = UIFactory.CreateLabel(val, "Empty", "(coming soon)", (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject2 = labelRef.GameObject; int? minWidth3 = 130; preferredWidth = 140; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; } else { (PanelType, string)[] tabs = group.Tabs; for (int i = 0; i < tabs.Length; i++) { (PanelType, string) tuple = tabs[i]; PanelType item = tuple.Item1; string item2 = tuple.Item2; ButtonRef buttonRef2 = UIFactory.CreateButton(val, $"TabBtn_{item}", item2); GameObject gameObject3 = buttonRef2.GameObject; int? minWidth4 = 130; preferredWidth2 = 138; num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren2 = ((Component)buttonRef2.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).enableWordWrapping = false; ((TMP_Text)componentInChildren2).overflowMode = (TextOverflowModes)0; ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(13); } PanelType captured = item; buttonRef2.OnClick = delegate { ShowTab(captured); }; _tabButtons[item] = buttonRef2; } } val.SetActive(flag2); if (flag) { buttonRef.OnClick = delegate { ToggleGroup(group.Title); }; } } private static bool IsTabGroupAvailable(string title) { if (!(title == "Bloodcraft")) { if (title == "Kindred") { return Settings.KindredAvailability switch { Settings.ModAvailability.On => true, Settings.ModAvailability.Off => false, _ => true, }; } return true; } return Settings.BloodcraftAvailability switch { Settings.ModAvailability.On => true, Settings.ModAvailability.Off => false, _ => EclipseProtocolService.UserRegistered || !EclipseProtocolService.RegistrationGaveUp, }; } private void ToggleGroup(string title) { if (_groupExpanded.TryGetValue(title, out var value)) { bool flag = !value; _groupExpanded[title] = flag; if (_groupContent.TryGetValue(title, out var value2)) { value2.SetActive(flag); } if (_groupHeaderText.TryGetValue(title, out var value3)) { ((TMP_Text)value3).text = FormatGroupHeader(title, flag); } AutoResizeIfEnabled(); } } private static string FormatGroupHeader(string title, bool expanded, bool available = true) { string text = title.ToUpper(); if (!available) { return "– " + text + " (unavailable)"; } if (!expanded) { return "▶ " + text; } return "▼ " + text; } private void OnBloodcraftAvailabilityChanged() { if (_deferredAvailabilityRefresh == null) { _deferredAvailabilityRefresh = delegate { CoreUpdateBehavior.Actions.Remove(_deferredAvailabilityRefresh); _deferredAvailabilityRefresh = null; RefreshAllTabGroupAvailability(); }; CoreUpdateBehavior.Actions.Add(_deferredAvailabilityRefresh); } } private void RefreshAllTabGroupAvailability() { foreach (string item in new List(_groupHeaderText.Keys)) { RefreshTabGroupAvailability(item); } AutoResizeIfEnabled(); } private void RefreshTabGroupAvailability(string title) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) bool flag = IsTabGroupAvailable(title); bool value; bool flag2 = _groupExpanded.TryGetValue(title, out value) && value; if (_groupHeaderText.TryGetValue(title, out var value2)) { ((TMP_Text)value2).text = FormatGroupHeader(title, flag2 && flag, flag); ((Graphic)value2).color = (Color)(flag ? Theme.DefaultText : new Color(0.55f, 0.55f, 0.55f)); } if (_groupHeaderButton.TryGetValue(title, out var value3)) { ((Selectable)value3.Component).interactable = flag; value3.OnClick = (flag ? ((Action)delegate { ToggleGroup(title); }) : null); } if (_groupContent.TryGetValue(title, out var value4) && !flag && value4.activeSelf) { value4.SetActive(false); } } private void BuildContentArea(GameObject parent) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateVerticalGroup(parent, "TabContent", forceWidth: true, forceHeight: true, childControlWidth: true, childControlHeight: true, 4, new Vector4(6f, 6f, 6f, 6f)); int? minWidth = 380; int? preferredWidth = 420; int? num = 1; UIFactory.SetLayoutElement(minHeight: 280, flexibleWidth: num, preferredHeight: 320, gameObject: val, minWidth: minWidth, flexibleHeight: 1, preferredWidth: preferredWidth); foreach (var item3 in AllTabs()) { PanelType item = item3.Tab; string item2 = item3.Label; GameObject content; GameObject val2 = CreateTabPage(val, out content); AddTabHeading(content, item2); switch (item) { case PanelType.FamiliarsTab: BuildFamiliarsTab(content); break; case PanelType.BoxesTab: BuildBoxesTab(content); break; case PanelType.VBloodsTab: BuildVBloodsTab(content); break; case PanelType.AllFamiliarsTab: BuildAllFamiliarsTab(content); break; case PanelType.ClassTab: BuildClassTab(content); break; case PanelType.ExpertiseTab: BuildExpertiseTab(content); break; case PanelType.BloodLegacyTab: BuildBloodLegacyTab(content); break; case PanelType.UnarmedShiftTab: BuildUnarmedShiftTab(content); break; case PanelType.PrestigeTab: BuildPrestigeTab(content); break; case PanelType.LevelsTab: BuildLevelsTab(content); break; case PanelType.AdminTab: BuildAdminTab(content); break; case PanelType.KindredLogisticsTab: BuildKindredLogisticsTab(content); break; case PanelType.KindredLogisticsAdminTab: BuildKindredLogisticsAdminTab(content); break; case PanelType.DailyQuestTab: BuildDailyQuestTab(content); break; case PanelType.KindredCommandsPlayerTab: BuildKindredCommandsPlayerTab(content); break; case PanelType.KindredAdminPlayersTab: BuildKindredAdminPlayersTab(content); break; case PanelType.KindredAdminServerTab: BuildKindredAdminServerTab(content); break; case PanelType.KindredAdminWorldTab: BuildKindredAdminWorldTab(content); break; case PanelType.QuickStartTab: BuildQuickStartTab(content); break; case PanelType.ModHelpTab: BuildModHelpTab(content); break; case PanelType.GameGuideTab: BuildGameGuideTab(content); break; case PanelType.SettingsTab: BuildSettingsTab(content); break; case PanelType.AboutTab: BuildAboutTab(content); break; case PanelType.VanillaAdminTab: BuildVanillaAdminTab(content); break; default: AddComingSoonBody(content, item2); break; } val2.SetActive(false); _tabContent[item] = val2; _tabInnerContent[item] = content; } } private GameObject CreateTabPage(GameObject parent, out GameObject content) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) AutoSliderScrollbar autoScrollbar; GameObject obj = UIFactory.CreateScrollView(parent, "TabPage", out content, out autoScrollbar, new Color(0f, 0f, 0f, 0f)); UIFactory.SetLayoutElement(obj, 380, preferredWidth: 420, flexibleWidth: 1, minHeight: 280, flexibleHeight: 1); VerticalLayoutGroup component = content.GetComponent(); if ((Object)(object)component != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component).spacing = 6f; ((LayoutGroup)component).padding.left = 8; ((LayoutGroup)component).padding.right = 8; ((LayoutGroup)component).padding.top = 8; ((LayoutGroup)component).padding.bottom = 8; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((LayoutGroup)component).childAlignment = (TextAnchor)0; } return obj; } private static void AddTabHeading(GameObject page, string text) { LabelRef labelRef = UIFactory.CreateLabel(page, "TabHeading", text, (TextAlignmentOptions)257, null, Theme.ScaledUI(20)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } private static void AddComingSoonBody(GameObject page, string label) { LabelRef labelRef = UIFactory.CreateLabel(page, "Placeholder", "Coming soon — this tab will surface the matching Bloodcraft commands.", (TextAlignmentOptions)257, null, Theme.ScaledUI(14)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 40, flexibleWidth: num, preferredHeight: 80, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } private void BuildFamiliarsTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "FamActiveCard", Theme.SystemTintFamiliar); AddSectionHeading(parent, "★ Active Familiar"); _famNameLabel = AddInfoLabel(parent, "FamName", "—", (FontStyles)1, Theme.ScaledUI(18)); _famProgressLabel = AddInfoLabel(parent, "FamProgress", "Level — ", (FontStyles)0, Theme.ScaledUI(14)); _famStatsLabel = AddInfoLabel(parent, "FamStats", "HP — PP — SP —", (FontStyles)0, Theme.ScaledUI(14)); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "FamActionsCard"); AddSectionHeading(parent2, "Actions"); GameObject obj = UIFactory.CreateHorizontalGroup(parent2, "FamActionsRow1", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Recall / Dismiss", ".fam t", "Calls or dismisses your active familiar (.fam t). Recallable — does NOT destroy the familiar."); AddCommandButton(obj, "Toggle Combat", ".fam c", "Toggle combat mode for the active familiar (.fam c). Off = passive, won't engage enemies."); AddCommandButton(obj, "Toggle Emotes", ".fam e", "Enable/disable emote-action bindings (.fam e). When OFF, emoting (e.g. clap) won't open the familiar's inventory or trigger other actions."); AddCommandButton(obj, "List Emotes", ".fam actions", "List the current emote→action bindings (.fam actions). Tells you which emote does what (e.g. clap = open inventory)."); GameObject obj2 = UIFactory.CreateHorizontalGroup(parent2, "FamActionsRow2", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj2, "★ Prestige", ".fam pr", "Prestige the active familiar (.fam pr). Requires max level; resets level and grants permanent bonuses."); AddCommandButton(obj2, "Unbind", ".fam ub", "Unbind the active familiar (.fam ub). The in-world entity is released but the familiar STAYS in your box — you can re-bind it from the Boxes tab any time. Use this to free the bind slot so you can summon a different familiar. To permanently delete a familiar from your collection, use the Boxes tab → Permanently Delete form."); AddDivider(parent2); AddBodyText(parent2, "Switch to the Boxes tab to browse your familiar boxes and click-to-bind."); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "FamEmoteCard"); AddSectionHeading(parent3, "Emote Bindings (perform these in-world)"); AddBodyText(parent3, "Bloodcraft binds these emotes to familiar actions. Trigger by performing the emote in-world (e.g. /clap), NOT via this UI — there's no chat command to invoke an emote programmatically. Toggle Emotes (above) enables/disables the whole system."); LabelRef labelRef = UIFactory.CreateLabel(parent3, "FamEmoteRef", " • Wave → Recall / Dismiss\n • Salute → Toggle Combat Mode\n • Clap → Bind / Unbind active familiar\n • Beckon → Interact (opens familiar's inventory, equipment, name & settings)", (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 80, flexibleWidth: num, preferredHeight: 96, gameObject: gameObject, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; AddSpacer(page, 6); GameObject parent4 = AddCard(page, "FamMoreActionsCard"); AddSectionHeading(parent4, "More Familiar Actions"); CollapsibleSection.Build(parent4, "Search boxes by name (.fam s)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Search familiars", ".fam s \"{name}\"", new TextField("name", "Name (substring)", "Wolf", "Substring of the familiar's display name. Bloodcraft does case-insensitive matching across boxes.")); }, "Search across ALL your boxes for familiars whose name matches the text. Reply appears in chat."); CollapsibleSection.Build(parent4, "Smart bind by name (.fam sb)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Smart bind", ".fam sb \"{name}\"", new TextField("name", "Name (substring)", "Wolf", "Substring of the familiar's display name to bind.")); }, "Search + bind in one step. If multiple matches are found Bloodcraft returns the list for clarification (no destructive action). Will fail if you already have a familiar bound."); CollapsibleSection.Build(parent4, "Make active familiar shiny (.fam shiny)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Apply shiny", ".fam shiny {school}", new EnumField("school", "Spell school", PlayerStateService.FamiliarShinySchoolChoice.Storm, "The shiny element to apply. Each school has a flavour (Storm = stun, Blood = leech, etc.).")); }, "Spends vampiric dust to permanently mark your CURRENT active familiar with a shiny buff of the chosen school. Requires an active familiar bound first."); CollapsibleSection.Build(parent4, "Toggle a familiar setting (.fam option)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Toggle option", ".fam option {setting}", new TextField("setting", "Setting name", "shiny", "Name of the setting to toggle. Server replies with the new state in chat.")); }, "Flips one of Bloodcraft's per-player familiar settings. Common settings: 'shiny' (apply shiny visuals), 'vbloodemotes' (familiar plays VBlood emotes). Bloodcraft's reply tells you what's now on/off."); CollapsibleSection.Build(parent4, "Buy V-Blood echoes (.fam echoes)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Buy echoes", ".fam echoes \"{vblood}\"", new TextField("vblood", "V-Blood name", "Quincey the Bandit King", "Exact display name of the V-Blood whose echo reward you want.")); }, "Spend V-Blood essence to purchase the exo reward tied to the named V-Blood unit. Cost scales with unit tier."); CollapsibleSection.Build(parent4, "Reset all familiar entities (.fam reset) — DESTRUCTIVE", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Reset familiars", ".fam reset", new BoolField("confirm", "Yes, destroy active follower entities", defaultValue: false, "Required. Box records and unlock data are NOT touched — this only clears in-world entities + active state. Re-bind from a box to summon again.", requireTrue: true)); }, "Destroys every entity in your follower buffer and clears your familiar-actives state. Use to recover from a bugged or stuck familiar bind. Required confirm checkbox."); BuildFamSearchResultPanel(parent4); RenderFamiliar(PlayerStateService.Familiar); if (!_famSubscribed) { PlayerStateService.FamiliarChanged += OnFamiliarChanged; _famSubscribed = true; } if (!_famSearchSubscribed) { MessageService.FamSearchCompleted += OnFamSearchCompletedForFamTab; _famSearchSubscribed = true; } } private void OnFamiliarChanged() { RenderFamiliar(PlayerStateService.Familiar); } private void BuildFamSearchResultPanel(GameObject parent) { //IL_0045: 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) AddDivider(parent); AddSectionHeading(parent, "Last search result"); _famSearchResultHeader = AddInfoLabel(parent, "FamSearchResultHeader", "(submit a search above to populate)", (FontStyles)2, Theme.ScaledUI(12)); ((Graphic)_famSearchResultHeader).color = new Color(0.7f, 0.7f, 0.7f); _famSearchResultList = UIFactory.CreateVerticalGroup(parent, "FamSearchResultList", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(0f, 0f, 6f, 4f)); UIFactory.SetLayoutElement(_famSearchResultList, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 0, flexibleHeight: 0); } private void OnFamSearchCompletedForFamTab(MessageService.FamSearchResult r) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_famSearchResultHeader == (Object)null || (Object)(object)_famSearchResultList == (Object)null) { return; } if (string.IsNullOrEmpty(r.Query)) { ((TMP_Text)_famSearchResultHeader).text = "(submit a search above to populate)"; } else if (!r.HadAnyMatch || r.Boxes == null || r.Boxes.Count == 0) { ((TMP_Text)_famSearchResultHeader).text = "Search: \"" + r.Query + "\" → no matches."; } else { ((TMP_Text)_famSearchResultHeader).text = $"Search: \"{r.Query}\" → {r.Boxes.Count} box{((r.Boxes.Count == 1) ? "" : "es")}."; } ((Graphic)_famSearchResultHeader).color = new Color(0.9f, 0.9f, 0.9f); for (int num = _famSearchResultList.transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)_famSearchResultList.transform.GetChild(num)).gameObject); } if (r.HadAnyMatch && r.Boxes != null) { foreach (var box in r.Boxes) { string text = (box.HasShiny ? " ★ shiny" : ""); LabelRef labelRef = UIFactory.CreateLabel(_famSearchResultList, "ResultRow", "• " + box.Box + text, (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num2 = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num2, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } } AutoResizeIfEnabled(); } private void BuildBoxesTab(GameObject page) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_0475: Unknown result type (might be due to invalid IL or missing references) //IL_05cb: Unknown result type (might be due to invalid IL or missing references) //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_07d9: Unknown result type (might be due to invalid IL or missing references) //IL_082a: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "BoxesHeaderCard", Theme.SystemTintFamiliar); _boxesActiveBoxLabel = AddInfoLabel(parent, "ActiveBox", "Active Box: (none selected)", (FontStyles)1, Theme.ScaledUI(14)); AddBodyText(parent, "Tip: click Refresh to pull your box list, click a box to see its familiars, click a familiar to bind it. Use ← Back to return."); AddSpacer(page, 6); _boxesPickerSection = UIFactory.CreateVerticalGroup(page, "BoxPickerSection", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); UIFactory.SetLayoutElement(_boxesPickerSection, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 80, flexibleHeight: 0); GameObject obj = UIFactory.CreateHorizontalGroup(_boxesPickerSection, "PickerActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(obj, "Cmd_RefreshBoxes", "Refresh"); GameObject gameObject = buttonRef.GameObject; int? minWidth3 = 70; int? preferredWidth3 = 110; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth3); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).enableWordWrapping = false; ((TMP_Text)componentInChildren).overflowMode = (TextOverflowModes)0; ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(13); } TooltipHover.Attach(buttonRef.GameObject, "Re-fetch your familiar boxes from the server (.fam boxes). The server reply can take a few seconds."); buttonRef.OnClick = delegate { if ((Object)(object)_boxesStatusLabel != (Object)null) { ((TMP_Text)_boxesStatusLabel).text = "Loading boxes from the server…"; } EnqueueOrWarn(".fam boxes"); }; _boxesStatusLabel = AddInfoLabel(_boxesPickerSection, "BoxesStatus", "", (FontStyles)2, Theme.ScaledUI(11)); AddSectionHeading(_boxesPickerSection, "Available Boxes"); _boxesListContainer = UIFactory.CreateVerticalGroup(_boxesPickerSection, "BoxListContainer", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(2f, 2f, 6f, 2f)); UIFactory.SetLayoutElement(_boxesListContainer, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 60, flexibleHeight: 0); AddSpacer(_boxesPickerSection, 4); AddSectionHeading(_boxesPickerSection, "Manage Boxes"); Action refreshBoxes = delegate { EnqueueOrWarn(".fam boxes"); }; CollapsibleSection.Build(_boxesPickerSection, "Create new box (.fam ab)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Create box", ".fam ab {boxName}", refreshBoxes, new TextField("boxName", "Box name", "MyBox", "Name for the new box. Avoid spaces if possible — Bloodcraft can be picky.")); }, "Adds an empty box you can move familiars into."); CollapsibleSection.Build(_boxesPickerSection, "Delete empty box (.fam db)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Delete empty box", ".fam db {boxName}", refreshBoxes, new BoxNameDropdownField("boxName", "Box name", "Pick the box to delete. Server will reject if it isn't empty — move familiars out first.")); }, "Removes a box. Bloodcraft only allows deleting boxes that are already empty — move familiars out first."); CollapsibleSection.Build(_boxesPickerSection, "Rename box (.fam rb)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Rename box", ".fam rb {current} {newName}", refreshBoxes, new BoxNameDropdownField("current", "Current name", "Pick the box to rename."), new TextField("newName", "New name", "NewName", "What to rename it to.")); }, "Renames an existing box."); _boxesContentSection = UIFactory.CreateVerticalGroup(page, "BoxContentSection", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); UIFactory.SetLayoutElement(_boxesContentSection, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 80, flexibleHeight: 0); GameObject obj2 = UIFactory.CreateHorizontalGroup(_boxesContentSection, "ContentActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth6 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj2, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef2 = UIFactory.CreateButton(obj2, "BackToBoxes", "← Back"); GameObject gameObject2 = buttonRef2.GameObject; int? minWidth7 = 70; preferredWidth3 = 110; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject2, minWidth: minWidth7, flexibleHeight: 0, preferredWidth: preferredWidth3); TextMeshProUGUI componentInChildren2 = ((Component)buttonRef2.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).enableWordWrapping = false; ((TMP_Text)componentInChildren2).overflowMode = (TextOverflowModes)0; ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(13); } buttonRef2.OnClick = OnBackToBoxesClicked; TooltipHover.Attach(buttonRef2.GameObject, "Return to the box list without changing your active box."); AddCommandButton(obj2, "Reload", ".fam l", "Re-fetch the familiars in the currently-active box (sends .fam l)."); ToggleRef toggleRef = UIFactory.CreateToggle(obj2, "BoxEditMode"); GameObject gameObject3 = toggleRef.GameObject; int? minWidth8 = 110; preferredWidth2 = 120; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject3, minWidth: minWidth8, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Edit mode"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject4 = ((Component)toggleRef.Text).gameObject; int? minWidth9 = 80; preferredWidth3 = 90; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject4, minWidth: minWidth9, flexibleHeight: 0, preferredWidth: preferredWidth3); toggleRef.Toggle.isOn = _boxesEditMode; TooltipHover.Attach(toggleRef.GameObject, "When ON, each familiar row gets a Delete button (red, two-click confirm) for permanent removal. Move stays as the form below — Bloodcraft's .fam mb only acts on the bound familiar, so the Move workflow is multi-step."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { _boxesEditMode = value; ClearPendingDelete(); RenderBoxContents(); AutoResizeIfEnabled(); }); _boxesEditModeToggle = toggleRef.Toggle; _boxesContentHeading = AddInfoLabel(_boxesContentSection, "ContentHeading", "Familiars in (none)", (FontStyles)2, Theme.ScaledUI(13)); _boxesSwapWarning = UIFactory.CreateLabel(_boxesContentSection, "SwapWarning", "", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject5 = _boxesSwapWarning.GameObject; int? minWidth10 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 38, flexibleWidth: num, preferredHeight: 38, gameObject: gameObject5, minWidth: minWidth10, flexibleHeight: 0, preferredWidth: preferredWidth2); ((Graphic)_boxesSwapWarning.TextMesh).color = new Color(1f, 0.65f, 0.45f); ((TMP_Text)_boxesSwapWarning.TextMesh).enableWordWrapping = true; ((TMP_Text)_boxesSwapWarning.TextMesh).overflowMode = (TextOverflowModes)0; _boxesContentContainer = UIFactory.CreateVerticalGroup(_boxesContentSection, "BoxContentContainer", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(2f, 2f, 2f, 2f)); UIFactory.SetLayoutElement(_boxesContentContainer, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 60, flexibleHeight: 0); AddSpacer(_boxesContentSection, 6); AddSectionHeading(_boxesContentSection, "Edit Familiar (advanced)"); CollapsibleSection.Build(_boxesContentSection, "Move active familiar to box (.fam mb)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Move active familiar", ".fam mb {boxName}", delegate { EnqueueOrWarn(".fam l"); }, new BoxNameDropdownField("boxName", "Destination box", "Pick the destination box. Must already exist; create one with the box management form on the picker view.")); }, "Moves your CURRENTLY-BOUND familiar to the named box. You must bind a familiar first (click it in the list above). The active familiar stays bound after the move; use Unbind on the Familiars tab to release it."); CollapsibleSection.Build(_boxesContentSection, "Permanently delete familiar from box (.fam r)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Permanently delete familiar", ".fam r {index}", delegate { EnqueueOrWarn(".fam l"); }, new IntField("index", "Familiar index", 1, 999, "The 1-based index from the list above (the leading '01', '02', etc.)."), new BoolField("confirm", "Yes, permanently delete", defaultValue: false, "Required. Check this box to confirm the deletion is intentional.", requireTrue: true)); }, "DESTRUCTIVE — permanently removes a familiar from your collection. The level/prestige/shiny are gone forever; only re-unlockable via gameplay drop. Two-click the Submit button (and check the box) so you don't fire it by accident."); UpdateBoxesSectionVisibility(); RenderBoxList(); RenderBoxContents(); if (!_boxesSubscribed) { PlayerStateService.BoxListChanged += OnBoxListChanged; PlayerStateService.BoxContentsChanged += OnBoxContentsChanged; PlayerStateService.ActiveBoxChanged += OnActiveBoxChanged; _boxesSubscribed = true; } } private void UpdateBoxesSectionVisibility() { if ((Object)(object)_boxesPickerSection != (Object)null) { _boxesPickerSection.SetActive(!_boxesShowingContents); } if ((Object)(object)_boxesContentSection != (Object)null) { _boxesContentSection.SetActive(_boxesShowingContents); } } private void OnBackToBoxesClicked() { ClearPendingSwap(); ClearPendingDelete(); _boxesShowingContents = false; PlayerStateService.SetActiveBox(null); UpdateBoxesSectionVisibility(); } private void OnBoxListChanged() { if ((Object)(object)_boxesStatusLabel != (Object)null) { ((TMP_Text)_boxesStatusLabel).text = ""; } RenderBoxList(); AutoResizeIfEnabled(); } private void OnBoxContentsChanged() { RenderBoxContents(); AutoResizeIfEnabled(); } private void OnActiveBoxChanged() { string activeBox = PlayerStateService.ActiveBox; if ((Object)(object)_boxesActiveBoxLabel != (Object)null) { ((TMP_Text)_boxesActiveBoxLabel).text = (string.IsNullOrEmpty(activeBox) ? "Active Box: (none selected)" : ("Active Box: " + activeBox)); } if ((Object)(object)_boxesContentHeading != (Object)null) { ((TMP_Text)_boxesContentHeading).text = "Familiars in " + (string.IsNullOrEmpty(activeBox) ? "(none)" : activeBox); } RenderBoxContents(); } private void RenderBoxList() { if ((Object)(object)_boxesListContainer == (Object)null) { return; } ClearChildren(_boxesListContainer); List boxList = PlayerStateService.BoxList; if (boxList.Count == 0) { LabelRef labelRef = UIFactory.CreateLabel(_boxesListContainer, "BoxesEmpty", "(no boxes loaded yet — click Refresh Boxes)", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 340; int? preferredWidth = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; return; } foreach (string item in boxList) { string captured = item; ButtonRef buttonRef = UIFactory.CreateButton(_boxesListContainer, "BoxBtn_" + item, item); GameObject gameObject2 = buttonRef.GameObject; int? minWidth2 = 340; int? preferredWidth2 = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); buttonRef.OnClick = delegate { OnBoxClicked(captured); }; } } private void RenderBoxContents() { //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0450: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_boxesContentContainer == (Object)null) { return; } ClearChildren(_boxesContentContainer); string activeBox = PlayerStateService.ActiveBox; if (string.IsNullOrEmpty(activeBox)) { LabelRef labelRef = UIFactory.CreateLabel(_boxesContentContainer, "ContentEmpty", "(click a box above to load its familiars)", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 340; int? preferredWidth = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; return; } if (!PlayerStateService.BoxContents.TryGetValue(activeBox, out var value) || value.Count == 0) { LabelRef labelRef2 = UIFactory.CreateLabel(_boxesContentContainer, "ContentPending", "Loading familiars for " + activeBox + "…", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject2 = labelRef2.GameObject; int? minWidth2 = 340; int? preferredWidth2 = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef2.TextMesh).fontStyle = (FontStyles)2; return; } foreach (PlayerStateService.FamiliarBoxEntry item in value) { int idx = item.Index; string text = $"{item.Index:00} — {item.Name}"; if (item.Level > 0) { text += $" Lv {item.Level}"; } if (item.Prestige > 0) { text += $" P{item.Prestige}"; } if (item.IsShiny) { text += " ★"; string shinySchool = item.ShinySchool; if (!string.IsNullOrEmpty(shinySchool)) { text = text + " " + shinySchool; } } if (_boxesEditMode) { GameObject val = UIFactory.CreateHorizontalGroup(_boxesContentContainer, $"FamRow_{item.Index}", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); int? minWidth3 = 340; int? preferredWidth = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: val, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ButtonRef buttonRef = UIFactory.CreateButton(val, $"FamBtn_{item.Index}", text); GameObject gameObject3 = buttonRef.GameObject; int? minWidth4 = 240; int? preferredWidth2 = 280; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); buttonRef.OnClick = delegate { OnFamiliarClicked(idx); }; ButtonRef delBtn = UIFactory.CreateButton(val, $"FamDel_{item.Index}", "Delete", (Color?)new Color(0.55f, 0.18f, 0.18f)); GameObject gameObject4 = delBtn.GameObject; int? minWidth5 = 70; preferredWidth = 80; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)delBtn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); } TooltipHover.Attach(delBtn.GameObject, "PERMANENTLY delete this familiar (.fam r). Two-click confirm — first click changes the label to 'Confirm?' and waits 3 seconds. Box record is gone forever."); int capturedIdx = idx; delBtn.OnClick = delegate { OnDeleteClicked(capturedIdx, delBtn, "Delete"); }; } else { ButtonRef buttonRef2 = UIFactory.CreateButton(_boxesContentContainer, $"FamBtn_{item.Index}", text); GameObject gameObject5 = buttonRef2.GameObject; int? minWidth6 = 340; int? preferredWidth2 = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject5, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); buttonRef2.OnClick = delegate { OnFamiliarClicked(idx); }; } } } private void OnDeleteClicked(int index, ButtonRef btn, string originalLabel) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (_pendingDeleteIndex == index && realtimeSinceStartup <= _pendingDeleteDeadline) { ClearPendingDelete(); TextMeshProUGUI componentInChildren = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = originalLabel; } EnqueueOrWarn($".fam r {index}"); EnqueueOrWarn(".fam l"); } else { _pendingDeleteIndex = index; _pendingDeleteDeadline = realtimeSinceStartup + 3f; TextMeshProUGUI componentInChildren2 = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).text = "Confirm?"; } } } private void ClearPendingDelete() { _pendingDeleteIndex = -1; _pendingDeleteDeadline = -1f; } private void OnBoxClicked(string boxName) { ClearPendingSwap(); ClearPendingDelete(); PlayerStateService.SetActiveBox(boxName); EnqueueOrWarn($".fam cb {boxName}"); EnqueueOrWarn(".fam l"); _boxesShowingContents = true; UpdateBoxesSectionVisibility(); } private void OnFamiliarClicked(int index) { if (string.IsNullOrEmpty(PlayerStateService.Familiar.Name)) { ClearPendingSwap(); EnqueueOrWarn($".fam b {index}"); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (_pendingSwapIndex == index && realtimeSinceStartup <= _pendingSwapDeadline) { ClearPendingSwap(); EnqueueOrWarn(".fam ub"); EnqueueOrWarn($".fam b {index}"); return; } string value = $"#{index}"; string activeBox = PlayerStateService.ActiveBox; if (!string.IsNullOrEmpty(activeBox) && PlayerStateService.BoxContents.TryGetValue(activeBox, out var value2)) { foreach (PlayerStateService.FamiliarBoxEntry item in value2) { if (item.Index == index) { value = item.Name; break; } } } _pendingSwapIndex = index; _pendingSwapDeadline = realtimeSinceStartup + 5f; ShowSwapWarning($"Active: {PlayerStateService.Familiar.Name}. Click {value} again within {5}s to unbind current and bind it. The current familiar returns to its box (level/prestige preserved); use Permanently Delete below to actually remove it from your collection."); } private void ClearPendingSwap() { _pendingSwapIndex = -1; _pendingSwapDeadline = -1f; if (_boxesSwapWarning != null) { ((TMP_Text)_boxesSwapWarning.TextMesh).text = ""; } } private void ShowSwapWarning(string msg) { if (_boxesSwapWarning != null) { ((TMP_Text)_boxesSwapWarning.TextMesh).text = msg; } } private static void ClearChildren(GameObject parent) { if (!((Object)(object)parent == (Object)null)) { Transform transform = parent.transform; for (int num = transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)transform.GetChild(num)).gameObject); } } } private void BuildClassTab(GameObject page) { //IL_0006: 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) GameObject parent = AddCard(page, "ClassCurrentCard", Theme.SystemTintExpertise); AddSectionHeading(parent, "Active Class"); _classNameLabel = AddInfoLabel(parent, "ClassName", "—", (FontStyles)1, Theme.ScaledUI(18)); _classLevelLabel = AddInfoLabel(parent, "ClassLevel", "Level —", (FontStyles)0, Theme.ScaledUI(14)); AddDivider(parent); _classDetailsLabel = AddContextBodyLabel(parent, "ClassDetails", FormatClassDetailsBlock(PlayerStateService.PlayerClass.None)); AddServerDisclaimer(parent); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "ClassActionsCard"); AddSectionHeading(parent2, "Actions"); GameObject obj = UIFactory.CreateHorizontalGroup(parent2, "ClassActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "List Classes", ".class l", "List the classes available on this server (.class l). Response appears in chat."); AddCommandButton(obj, "List Spells", ".class lsp", "List the spells granted by your current class (.class lsp). Response in chat."); AddCommandButton(obj, "List Stats", ".class lst", "List the weapon/blood stat synergies for your current class (.class lst)."); AddCommandButton(obj, "Toggle Shift", ".class shift", "Toggle whether the class spell occupies your shift-slot (.class shift)."); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "ClassChangeCard"); AddSectionHeading(parent3, "Change Class"); CollapsibleSection.Build(parent3, "Select / change your class (.class s)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Select class", ".class s {class}", new EnumField("class", "Class", PlayerStateService.BloodcraftClassChoice.BloodKnight, "The class you want active. Bloodcraft's six built-in classes are listed.")); }, "Pick a class from the dropdown and Submit. Some servers may rate-limit class changes or require a cost — Bloodcraft replies in chat with success or the rejection reason."); CollapsibleSection.Build(parent3, "Choose class shift spell (.class csp)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Choose shift spell", ".class csp {index}", new IntField("index", "Spell #", 1, 32, "1-based index of the class spell. Run 'List Spells' to see what each number maps to before submitting.")); }, "Set which of your class's spells occupies your shift slot. Use 'List Spells' above to see the available spells (numbered) for your current class, then enter the spell's 1-based index here."); AddDivider(parent3); AddBodyText(parent3, $"Tip: {Mono("List Spells")} / {Mono("List Stats")} above describe what each class grants before you commit."); CollapsibleSection.Build(parent3, "Compare all classes", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "ClassCompareIntro", "Each class grants a 1.5× cap multiplier on its synergized weapon + blood stats, an on-hit debuff at 7.5% proc chance (default), and a class-specific spell school. Class change costs 750× Shattered Bone by default.", 12); AddDivider(c); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.BloodKnight); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.VampireLord); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.DemonHunter); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.ShadowBlade); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.ArcaneSorcerer); AddClassComparisonBlock(c, PlayerStateService.PlayerClass.DeathMage); AddServerDisclaimer(c); }, "Expand to see each of the six Bloodcraft classes side-by-side — their weapon and blood synergies, archetype, and on-hit debuff school. Helpful before committing to a class change."); RenderClass(PlayerStateService.Experience); if (!_classSubscribed) { PlayerStateService.ExperienceChanged += OnExperienceChangedForClass; _classSubscribed = true; } } private void OnExperienceChangedForClass() { RenderClass(PlayerStateService.Experience); } private void RenderClass(PlayerStateService.ExperienceState s) { if (!((Object)(object)_classNameLabel == (Object)null)) { ((TMP_Text)_classNameLabel).text = ((s.Class == PlayerStateService.PlayerClass.None) ? "(no class selected)" : s.Class.ToString()); ((TMP_Text)_classLevelLabel).text = ((s.Class == PlayerStateService.PlayerClass.None) ? "Use .class s in chat to choose one." : $"Player Level {s.Level} Prestige {s.Prestige}"); if ((Object)(object)_classDetailsLabel != (Object)null) { ((TMP_Text)_classDetailsLabel).text = FormatClassDetailsBlock(s.Class); } if ((Object)(object)_wepClassSynergyLabel != (Object)null) { ((TMP_Text)_wepClassSynergyLabel).text = FormatClassWeaponSynergyHint(s.Class); } if ((Object)(object)_blClassSynergyLabel != (Object)null) { ((TMP_Text)_blClassSynergyLabel).text = FormatClassBloodSynergyHint(s.Class); } } } private static string FormatSynergyList(string[] stats) { if (stats != null && stats.Length != 0) { return string.Join(" • ", stats); } return "—"; } private static string FormatClassDetailsBlock(PlayerStateService.PlayerClass cls) { if (cls == PlayerStateService.PlayerClass.None || !ClassInfoByClass.TryGetValue(cls, out var value)) { return "No class selected — pick one in the Change Class section below to see its synergies and on-hit effect."; } return $"{value.DisplayName} ({value.Archetype})\n{value.Tagline}\n Weapon synergies (1.5× cap): {FormatSynergyList(value.WeaponSynergies)}\n Blood synergies (1.5× cap): {FormatSynergyList(value.BloodSynergies)}\n On-hit debuff: {value.OnHitDebuff} (secondary: {value.OnHitSecondary})"; } private static string FormatClassWeaponSynergyHint(PlayerStateService.PlayerClass cls) { if (cls == PlayerStateService.PlayerClass.None || !ClassInfoByClass.TryGetValue(cls, out var value)) { return "No class selected — every weapon stat scales at its baseline cap until you pick a class (Class tab)."; } return $"Your class ({value.DisplayName}) amplifies these weapon stats with a 1.5× cap:\n {FormatSynergyList(value.WeaponSynergies)}\nPicking one of these for the weapon you fight with gets you the most out of every expertise level."; } private static string FormatClassBloodSynergyHint(PlayerStateService.PlayerClass cls) { if (cls == PlayerStateService.PlayerClass.None || !ClassInfoByClass.TryGetValue(cls, out var value)) { return "No class selected — every blood stat scales at its baseline cap until you pick a class (Class tab)."; } return $"Your class ({value.DisplayName}) amplifies these blood stats with a 1.5× cap:\n {FormatSynergyList(value.BloodSynergies)}\nStacking blood + weapon picks toward the same role compounds the bonuses."; } private static TextMeshProUGUI AddContextBodyLabel(GameObject parent, string name, string initialText, int fontSize = 13) { LabelRef labelRef = UIFactory.CreateLabel(parent, name, initialText, (TextAlignmentOptions)257, null, Theme.ScaledUI(fontSize)); UIFactory.SetLayoutElement(labelRef.GameObject, 340, preferredWidth: 380, flexibleWidth: 1, minHeight: 22, flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; return labelRef.TextMesh; } private static void AddServerDisclaimer(GameObject parent, string name = "ServerDisclaimer") { LabelRef labelRef = UIFactory.CreateLabel(parent, name, "Defaults shown — your server's admin may have overridden any of these values in Bloodcraft.cfg.", (TextAlignmentOptions)257, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 340; int? preferredWidth = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } private static void AddClassComparisonBlock(GameObject parent, PlayerStateService.PlayerClass cls) { AddContextBodyLabel(parent, $"ClassCompare_{cls}", FormatClassDetailsBlock(cls)); AddDivider(parent); } private void BuildVBloodsTab(GameObject page) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "VBloodsHeaderCard"); AddSectionHeading(parent, "V-Blood Collection"); AddBodyText(parent, "One row per captured V-Blood variant (basic / shiny / primal / primal shiny). " + $"Scan walks each of your familiar boxes once via {Mono(".fam boxes")} + {Mono(".fam l")} — your active box is restored when it finishes. " + "Filter shows All / Captured / Missing / Shiny only."); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "VBloodsProgressCard"); GameObject val = UIFactory.CreateHorizontalGroup(parent2, "VBloodsHeader", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); _vbProgressLabel = AddInfoLabel(val, "VBProgress", "0 / 65 captured", (FontStyles)1, Theme.ScaledUI(13)); GameObject gameObject = ((Component)_vbProgressLabel).gameObject; int? minWidth2 = 180; int? preferredWidth2 = 240; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); _vbScanButton = UIFactory.CreateButton(val, "VBScanBtn", "Scan all"); GameObject gameObject2 = _vbScanButton.GameObject; int? minWidth3 = 90; preferredWidth = 110; num = 0; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)_vbScanButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(_vbScanButton.GameObject, "Sweep every familiar box via .fam boxes + .fam cb + .fam l — populates the V-Blood collection AND per-box contents in one pass. ~30–60s. Your active box is restored when the sweep finishes. Cancel halts the sweep mid-flight."); _vbScanButton.OnClick = delegate { if (VBloodScannerService.Scanning) { VBloodScannerService.CancelScan(); } else { VBloodScannerService.StartScan(); } RefreshVBScanButton(); }; _vbScanStatusLabel = AddInfoLabel(parent2, "VBScanStatus", "", (FontStyles)2, Theme.ScaledUI(11)); ((Component)_vbScanStatusLabel).gameObject.SetActive(false); AddSpacer(page, 6); GameObject val2 = UIFactory.CreateHorizontalGroup(AddCard(page, "VBloodsFilterCard"), "VBloodsFilters", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); int? minWidth4 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: val2, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); AddVBFilterButton(val2, "All", VBloodFilter.All); AddVBFilterButton(val2, "Captured", VBloodFilter.Captured); AddVBFilterButton(val2, "Missing", VBloodFilter.Missing); AddVBFilterButton(val2, "Shiny", VBloodFilter.ShinyOnly); _vbSortButton = UIFactory.CreateButton(val2, "VBSortBtn", FormatVBSortButtonText()); GameObject gameObject3 = _vbSortButton.GameObject; int? minWidth5 = 100; preferredWidth = 130; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject3, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren2 = ((Component)_vbSortButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(_vbSortButton.GameObject, "Cycle sort order: Default (alpha by name) → Alphabetical → By level (descending across all captured instances) → By region."); _vbSortButton.OnClick = delegate { Settings.SetFamiliarSortOrder(Settings.FamiliarSortOrderSetting switch { Settings.FamiliarSortOrder.Default => Settings.FamiliarSortOrder.Alphabetical, Settings.FamiliarSortOrder.Alphabetical => Settings.FamiliarSortOrder.Level, Settings.FamiliarSortOrder.Level => Settings.FamiliarSortOrder.Location, Settings.FamiliarSortOrder.Location => Settings.FamiliarSortOrder.Default, _ => Settings.FamiliarSortOrder.Default, }); RefreshVBSortButtonText(); RebuildVBRows(); try { Plugin.UIManager?.FamiliarBrowserOverlay?.NotifySortOrderChanged(); } catch { } }; AddSpacer(page, 6); GameObject parent3 = AddCard(page, "VBloodsRowsCard", null, 4, 2); AddSectionHeading(parent3, "V-Bloods"); BuildVBColumnHeader(parent3); _vbRowContainer = UIFactory.CreateVerticalGroup(parent3, "VBloodRows", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(0f, 0f, 2f, 2f)); UIFactory.SetLayoutElement(_vbRowContainer, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 60, flexibleHeight: 0); if (!_vbSubscribed) { PlayerStateService.VBloodCollectionChanged += OnVBloodCollectionChanged; VBloodScannerService.ScanStateChanged += OnVBloodScanStateChanged; _vbSubscribed = true; } if (Settings.AutoScanVBloodsOnTabOpen && PlayerStateService.VBloodCollection.Count == 0 && !VBloodScannerService.Scanning) { Action deferStart = null; deferStart = delegate { CoreUpdateBehavior.Actions.Remove(deferStart); if (MessageService.IsInitialized && PlayerStateService.VBloodCollection.Count == 0) { VBloodScannerService.StartScan(); } RefreshVBScanButton(); }; CoreUpdateBehavior.Actions.Add(deferStart); } RebuildVBRows(); RefreshVBHeader(); RefreshVBScanButton(); } private void OnVBloodCollectionChanged() { RebuildVBRows(); RefreshVBHeader(); } private void OnVBloodScanStateChanged() { RefreshVBScanButton(); RefreshVBHeader(); } private void RefreshVBScanButton() { if (_vbScanButton != null) { TextMeshProUGUI componentInChildren = ((Component)_vbScanButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = (VBloodScannerService.Scanning ? "Cancel" : "Scan all"); } } } private void RefreshVBHeader() { if ((Object)(object)_vbProgressLabel == (Object)null) { return; } int value = VBloodRegistry.All.Length; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (PlayerStateService.VBloodCaptureStatus value3 in PlayerStateService.VBloodCollection.Values) { if (value3.Instances == null || value3.Instances.Count == 0) { continue; } num++; if (value3.HasPrimal || value3.HasPrimalShiny) { num2++; } foreach (PlayerStateService.VBloodInstance instance in value3.Instances) { num4++; if (instance.IsShiny) { num3++; } } } string text = $"{num} / {value} captured · {num4} instance{((num4 == 1) ? "" : "s")}"; if (num2 > 0) { text += $" · {num2} primal"; } if (num3 > 0) { text += $" · {num3} shiny"; } ((TMP_Text)_vbProgressLabel).text = text; if (!((Object)(object)_vbScanStatusLabel != (Object)null)) { return; } if (VBloodScannerService.Scanning) { string currentBoxBeingScanned = VBloodScannerService.CurrentBoxBeingScanned; string value2 = (string.IsNullOrEmpty(currentBoxBeingScanned) ? "" : (" — " + currentBoxBeingScanned)); ((TMP_Text)_vbScanStatusLabel).text = $"Scanning… box {VBloodScannerService.CompletedForCurrentScan + 1} / {VBloodScannerService.TotalForCurrentScan}{value2}"; if (!((Component)_vbScanStatusLabel).gameObject.activeSelf) { ((Component)_vbScanStatusLabel).gameObject.SetActive(true); } } else if (((TMP_Text)_vbScanStatusLabel).text != null && ((TMP_Text)_vbScanStatusLabel).text.StartsWith("Scanning")) { ((Component)_vbScanStatusLabel).gameObject.SetActive(false); } } private void RebuildVBRows() { if ((Object)(object)_vbRowContainer == (Object)null) { return; } for (int num = _vbRowContainer.transform.childCount - 1; num >= 0; num--) { Transform child = _vbRowContainer.transform.GetChild(num); if (!((Object)(object)child == (Object)null)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } List list = new List(); bool flag = _vbFilter == VBloodFilter.All || _vbFilter == VBloodFilter.Missing; bool num2 = _vbFilter != VBloodFilter.Missing; bool flag2 = _vbFilter == VBloodFilter.ShinyOnly; if (num2) { foreach (KeyValuePair item in PlayerStateService.VBloodCollection) { PlayerStateService.VBloodCaptureStatus value = item.Value; if (value.Instances == null) { continue; } foreach (PlayerStateService.VBloodInstance instance in value.Instances) { if ((!flag2 || instance.IsShiny) && !(_vbFilter == VBloodFilter.Captured && flag2)) { list.Add(new VBRowSpec { Name = value.Name, Instance = instance, IsMissing = false }); } } } } if (flag) { string[] all = VBloodRegistry.All; foreach (string text in all) { if (!PlayerStateService.VBloodCollection.TryGetValue(text, out var value2) || value2.Instances == null || value2.Instances.Count <= 0) { list.Add(new VBRowSpec { Name = text, IsMissing = true }); } } } SortVBRows(list); if (list.Count == 0) { LabelRef labelRef = UIFactory.CreateLabel(_vbRowContainer, "VBEmpty", (_vbFilter == VBloodFilter.Missing) ? "Nothing missing — every registered V-Blood has at least one capture. Nice work." : "No captures match the current filter. Try Scan or switch filter to All.", (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num3 = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num3, preferredHeight: 32, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; return; } foreach (VBRowSpec item2 in list) { if (item2.IsMissing) { BuildVBMissingRow(_vbRowContainer, item2.Name); } else { BuildVBVariantRow(_vbRowContainer, item2.Name, item2.Instance); } } } private static void SortVBRows(List rows) { Settings.FamiliarSortOrder mode = Settings.FamiliarSortOrderSetting; rows.Sort(delegate(VBRowSpec a, VBRowSpec b) { int num = a.IsMissing.CompareTo(b.IsMissing); if (num != 0) { return num; } switch (mode) { case Settings.FamiliarSortOrder.Alphabetical: { int num4 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); if (num4 != 0) { return num4; } return CompareVariantOrder(a, b); } case Settings.FamiliarSortOrder.Level: { if (a.IsMissing || b.IsMissing) { return string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); } int num3 = b.Instance.Level.CompareTo(a.Instance.Level); if (num3 != 0) { return num3; } num3 = b.Instance.Prestige.CompareTo(a.Instance.Prestige); if (num3 != 0) { return num3; } return string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); } case Settings.FamiliarSortOrder.Location: { int num5 = VBloodRegistry.RegionOrderFor(a.Name); int num6 = VBloodRegistry.RegionOrderFor(b.Name); if (num5 != num6) { return num5.CompareTo(num6); } int num7 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); if (num7 != 0) { return num7; } return CompareVariantOrder(a, b); } default: { int num2 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); if (num2 != 0) { return num2; } return CompareVariantOrder(a, b); } } }); } private static int CompareVariantOrder(VBRowSpec a, VBRowSpec b) { if (a.IsMissing || b.IsMissing) { return 0; } int num = VariantOrder(a.Instance); int value = VariantOrder(b.Instance); return num.CompareTo(value); } private static int VariantOrder(PlayerStateService.VBloodInstance i) { return (i.IsPrimal ? 2 : 0) + (i.IsShiny ? 1 : 0); } private static string FormatVBSortButtonText() { return Settings.FamiliarSortOrderSetting switch { Settings.FamiliarSortOrder.Alphabetical => "Sort: Alpha", Settings.FamiliarSortOrder.Level => "Sort: Level", Settings.FamiliarSortOrder.Location => "Sort: Region", _ => "Sort: Default", }; } private void RefreshVBSortButtonText() { if (_vbSortButton != null) { TextMeshProUGUI componentInChildren = ((Component)_vbSortButton.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = FormatVBSortButtonText(); } } } private void AddVBFilterButton(GameObject parent, string label, VBloodFilter mode) { ButtonRef buttonRef = UIFactory.CreateButton(parent, "VBFilter_" + label, label); GameObject gameObject = buttonRef.GameObject; int? minWidth = 70; int? preferredWidth = 90; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } buttonRef.OnClick = delegate { _vbFilter = mode; RebuildVBRows(); }; } private static string VariantTag(PlayerStateService.VBloodInstance i) { string value = ((!i.IsPrimal) ? (i.IsShiny ? "S" : "B") : (i.IsShiny ? "PS" : "P")); string value2 = ((!i.IsPrimal) ? (i.IsShiny ? "#9AE0FF" : "#7CDA7C") : (i.IsShiny ? "#FFA0F0" : "#FFC066")); return $"[{value}]"; } private static void BuildVBColumnHeader(GameObject parent) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) GameObject row = UIFactory.CreateHorizontalGroup(parent, "VBColHeader", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 1f, 2f)); GameObject gameObject = row; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); Col("Type", 36, (TextAlignmentOptions)4098); LabelRef labelRef = UIFactory.CreateLabel(row, "H_Name", "Name", (TextAlignmentOptions)4097, null, Theme.ScaledUI(10)); GameObject gameObject2 = labelRef.GameObject; int? minWidth2 = 100; int? preferredWidth2 = 140; num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)33; Col("Lv", 70, (TextAlignmentOptions)4098); Col("Shiny", 96, (TextAlignmentOptions)4098); Col("Box", 100, (TextAlignmentOptions)4097); Col("", 78, (TextAlignmentOptions)4098); void Col(string label, int w, TextAlignmentOptions align) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef2 = UIFactory.CreateLabel(row, "H_" + label, $"{label}", align, null, Theme.ScaledUI(10)); GameObject gameObject3 = labelRef2.GameObject; int? minWidth3 = w; int? preferredWidth3 = w; int? num2 = 0; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num2, preferredHeight: 20, gameObject: gameObject3, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth3); ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef2.TextMesh).fontStyle = (FontStyles)33; } } private void BuildVBVariantRow(GameObject parent, string name, PlayerStateService.VBloodInstance instance) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, $"VBRow_{name}_{(instance.IsPrimal ? 80 : 66)}{(instance.IsShiny ? 83 : 95)}_{instance.Box}_{instance.Index}", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(obj, "Tag", VariantTag(instance), (TextAlignmentOptions)4098, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 36; int? preferredWidth2 = 36; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; TextMeshProUGUI obj2 = AddInfoLabel(obj, "Name", name, (FontStyles)0, Theme.ScaledUI(12)); ((TMP_Text)obj2).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)obj2).gameObject; int? minWidth3 = 100; preferredWidth = 140; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)obj2).enableWordWrapping = false; ((TMP_Text)obj2).overflowMode = (TextOverflowModes)1; string initialText = ((instance.Prestige > 0) ? $"Lv {instance.Level} Pr {instance.Prestige}" : ((instance.Level > 0) ? $"Lv {instance.Level}" : "—")); TextMeshProUGUI obj3 = AddInfoLabel(obj, "Stats", initialText, (FontStyles)0, Theme.ScaledUI(11)); ((TMP_Text)obj3).alignment = (TextAlignmentOptions)4098; GameObject gameObject3 = ((Component)obj3).gameObject; int? minWidth4 = 70; preferredWidth2 = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)obj3).enableWordWrapping = false; ((TMP_Text)obj3).overflowMode = (TextOverflowModes)0; string defaultText = ((!instance.IsShiny) ? "—" : (string.IsNullOrEmpty(instance.ShinySchool) ? "★" : ("★ " + instance.ShinySchool))); LabelRef labelRef2 = UIFactory.CreateLabel(obj, "School", defaultText, (TextAlignmentOptions)4098, null, Theme.ScaledUI(11)); GameObject gameObject4 = labelRef2.GameObject; int? minWidth5 = 96; preferredWidth = 96; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef2.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; TextMeshProUGUI obj4 = AddInfoLabel(obj, "Box", instance.Box, (FontStyles)2, Theme.ScaledUI(11)); ((TMP_Text)obj4).alignment = (TextAlignmentOptions)4097; GameObject gameObject5 = ((Component)obj4).gameObject; int? minWidth6 = 100; preferredWidth2 = 100; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject5, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)obj4).enableWordWrapping = false; ((TMP_Text)obj4).overflowMode = (TextOverflowModes)1; ButtonRef buttonRef = UIFactory.CreateButton(obj, "Summon", "Summon"); GameObject gameObject6 = buttonRef.GameObject; int? minWidth7 = 78; preferredWidth = 78; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject6, minWidth: minWidth7, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(11); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } string capturedName = name; bool capturedShiny = instance.IsShiny; bool capturedPrimal = instance.IsPrimal; buttonRef.OnClick = delegate { OnVBSummonVariantClicked(capturedName, capturedShiny, capturedPrimal); }; } private void BuildVBMissingRow(GameObject parent, string name) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) GameObject row = UIFactory.CreateHorizontalGroup(parent, "VBMissingRow_" + name, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); GameObject gameObject = row; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject2 = UIFactory.CreateLabel(row, "Tag", "—", (TextAlignmentOptions)4098, null, Theme.ScaledUI(11)).GameObject; int? minWidth2 = 36; int? preferredWidth2 = 36; num = 0; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); LabelRef labelRef = UIFactory.CreateLabel(row, "Name", $"{name}", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject3 = labelRef.GameObject; int? minWidth3 = 100; preferredWidth = 140; num = 1; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject3, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)1; EmptyCol("LvEmpty", 70); EmptyCol("ShinyEmpty", 96); EmptyCol("BoxEmpty", 100); LabelRef labelRef2 = UIFactory.CreateLabel(row, "Status", "not captured", (TextAlignmentOptions)4098, null, Theme.ScaledUI(10)); GameObject gameObject4 = labelRef2.GameObject; int? minWidth4 = 78; preferredWidth2 = 78; num = 0; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject4, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef2.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; void EmptyCol(string label, int w) { LabelRef labelRef3 = UIFactory.CreateLabel(row, label, "—", (TextAlignmentOptions)4098, null, Theme.ScaledUI(11)); GameObject gameObject5 = labelRef3.GameObject; int? minWidth5 = w; int? preferredWidth3 = w; int? num2 = 0; UIFactory.SetLayoutElement(minHeight: 20, flexibleWidth: num2, preferredHeight: 22, gameObject: gameObject5, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth3); ((TMP_Text)labelRef3.TextMesh).fontStyle = (FontStyles)2; } } private void OnVBSummonVariantClicked(string name, bool isShiny, bool isPrimal) { if (!_vbSummonStatusSubscribed) { VBloodSummonService.StatusChanged += OnVBSummonStatusChanged; _vbSummonStatusSubscribed = true; } VBloodSummonService.SummonVariant(name, isShiny, isPrimal); } private void OnVBSummonStatusChanged(string status) { if (!((Object)(object)_vbScanStatusLabel == (Object)null)) { ((TMP_Text)_vbScanStatusLabel).text = status; if (!((Component)_vbScanStatusLabel).gameObject.activeSelf) { ((Component)_vbScanStatusLabel).gameObject.SetActive(true); } } } private void BuildExpertiseTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "WepCurrentCard", Theme.SystemTintExpertise); AddSectionHeading(parent, "Current Weapon Expertise"); _wepTypeLabel = AddInfoLabel(parent, "WepType", "—", (FontStyles)1, Theme.ScaledUI(18)); _wepProgressLabel = AddInfoLabel(parent, "WepProgress", "Level —", (FontStyles)0, Theme.ScaledUI(14)); _wepBonusLabel = AddInfoLabel(parent, "WepBonus", "Bonus Stats: —", (FontStyles)0, Theme.ScaledUI(13)); _wepStatsValuesLabel = AddInfoLabel(parent, "WepStatsValues", "", (FontStyles)2, Theme.ScaledUI(12)); ((Component)_wepStatsValuesLabel).gameObject.SetActive(false); ((TMP_Text)_wepStatsValuesLabel).enableWordWrapping = true; ((TMP_Text)_wepStatsValuesLabel).overflowMode = (TextOverflowModes)0; AddSpacer(page, 6); GameObject parent2 = AddCard(page, "WepClassHintCard"); AddSectionHeading(parent2, "Class synergies"); _wepClassSynergyLabel = AddContextBodyLabel(parent2, "WepClassSynergy", FormatClassWeaponSynergyHint(PlayerStateService.Experience.Class)); CollapsibleSection.Build(parent2, "All stat caps + per-stat details", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "WepStatCapsBody", "Bonus stat caps (baseline at expertise L100, before any prestige boost):\n • Physical Power: +20\n • Spell Power: +10\n • Max Health: +250\n • Movement Speed: +25%\n • Primary Attack Speed: +10%\n • Physical / Spell Crit Chance: +10%\n • Physical / Spell Crit Damage: +50%\n • Physical / Spell Life Leech: +10%\n • Primary Life Leech: +15%\n\nEach weapon expertise prestige tier (max 10): −10% XP rate, +10% stat-cap boost. Reset a weapon's chosen stats with .wep rst (default cost: 500× Shattered Bone).", 12); }, "Reference list of every weapon expertise stat and its baseline cap at L100. Class synergy multiplies the cap by 1.5× for the four stats listed above."); AddServerDisclaimer(parent2); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "WepActionsCard"); AddSectionHeading(parent3, "Actions"); GameObject obj = UIFactory.CreateHorizontalGroup(parent3, "WepActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Refresh", ".wep get", "Display your current weapon expertise details in chat (.wep get)."); AddCommandButton(obj, "List Weps", ".wep l", "List all weapon expertise types tracked by Bloodcraft (.wep l)."); AddCommandButton(obj, "List Stats", ".wep lst", "List the weapon-stat bonuses you can choose between (.wep lst)."); AddCommandButton(obj, "Reset Stats", ".wep rst", "Reset your chosen bonus stats for the current weapon (.wep rst)."); AddCommandButton(obj, "Lock Spells", ".wep locksp", "Lock in the next spells you equip to use as your unarmed slot spells (.wep locksp)."); AddSpacer(page, 6); GameObject parent4 = AddCard(page, "WepStatPickerCard"); AddSectionHeading(parent4, "Choose Bonus Stat"); CollapsibleSection.Build(parent4, "Set bonus stat for a weapon (.wep cst)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set bonus stat", ".wep cst {weapon} {stat}", delegate { EnqueueOrWarn(".wep get"); }, new EnumField("weapon", "Weapon", PlayerStateService.WeaponType.Sword, "Which weapon type the chosen bonus stat applies to."), new EnumIndexField("stat", "Bonus stat", PlayerStateService.WeaponBonusStat.PhysicalPower, "The stat to enhance. Bloodcraft expects a 1-12 index; the dropdown sends it for you.")); }, "Pick the weapon type AND the bonus stat you want to lock in for it. Bloodcraft applies the chosen stat scaled by your expertise level for that weapon. Each weapon tracks up to 3 chosen stats; submit again to add more (or use Reset Stats to clear)."); AddDivider(parent4); AddBodyText(parent4, "Bloodcraft only streams the EQUIPPED weapon's expertise. Switch weapons to see each one's level + chosen stats above. The " + Mono(".wep l") + " button lists every weapon type you can level."); RenderExpertise(PlayerStateService.Expertise); if (!_wepSubscribed) { PlayerStateService.ExpertiseChanged += OnExpertiseChanged; _wepSubscribed = true; } if (!_wepLastResponseSubscribed) { PlayerStateService.LastResponseChanged += OnLastResponseChangedForWep; _wepLastResponseSubscribed = true; } PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; if (lastResponse.Command == ".wep get" && lastResponse.Lines != null && lastResponse.Lines.Count > 0) { _cachedWepGetLines = new List(lastResponse.Lines); RenderWepStatsValues(); } } private void OnLastResponseChangedForWep() { PlayerStateService.LastServerResponse lastResponse = PlayerStateService.LastResponse; if (!(lastResponse.Command != ".wep get") && lastResponse.Lines != null) { _cachedWepGetLines = new List(lastResponse.Lines); RenderWepStatsValues(); AutoResizeIfEnabled(); } } private void RenderWepStatsValues() { if ((Object)(object)_wepStatsValuesLabel == (Object)null) { return; } if (_cachedWepGetLines == null || _cachedWepGetLines.Count == 0) { if (((Component)_wepStatsValuesLabel).gameObject.activeSelf) { ((Component)_wepStatsValuesLabel).gameObject.SetActive(false); } return; } ((TMP_Text)_wepStatsValuesLabel).text = "• " + string.Join("\n• ", _cachedWepGetLines); if (!((Component)_wepStatsValuesLabel).gameObject.activeSelf) { ((Component)_wepStatsValuesLabel).gameObject.SetActive(true); } } private void OnExpertiseChanged() { PlayerStateService.ExpertiseState expertise = PlayerStateService.Expertise; if (_wepTabTypeBaseline && expertise.Type != _wepTabLastType) { _lastWepAutoFetchAt = 0.0; _cachedWepGetLines = null; if ((Object)(object)_wepStatsValuesLabel != (Object)null) { RenderWepStatsValues(); } } _wepTabLastType = expertise.Type; _wepTabTypeBaseline = true; RenderExpertise(expertise); } private void BuildBloodLegacyTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "BlCurrentCard", Theme.SystemTintLegacy); AddSectionHeading(parent, "Current Blood Legacy"); _blTypeLabel = AddInfoLabel(parent, "BlType", "—", (FontStyles)1, Theme.ScaledUI(18)); ((Graphic)_blTypeLabel).color = new Color(1f, 0.4f, 0.4f); ApplyStrongAccentOutline(_blTypeLabel); _blProgressLabel = AddInfoLabel(parent, "BlProgress", "Level —", (FontStyles)0, Theme.ScaledUI(14)); _blBonusLabel = AddInfoLabel(parent, "BlBonus", "Bonus Stats: —", (FontStyles)0, Theme.ScaledUI(13)); _blStatsValuesLabel = AddInfoLabel(parent, "BlStatsValues", "", (FontStyles)2, Theme.ScaledUI(12)); ((Component)_blStatsValuesLabel).gameObject.SetActive(false); ApplyStrongAccentOutline(_blStatsValuesLabel); ((TMP_Text)_blStatsValuesLabel).enableWordWrapping = true; ((TMP_Text)_blStatsValuesLabel).overflowMode = (TextOverflowModes)0; AddSpacer(page, 6); GameObject parent2 = AddCard(page, "BlClassHintCard"); AddSectionHeading(parent2, "Class synergies"); _blClassSynergyLabel = AddContextBodyLabel(parent2, "BlClassSynergy", FormatClassBloodSynergyHint(PlayerStateService.Experience.Class)); CollapsibleSection.Build(parent2, "All stat caps + per-stat details", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "BlStatCapsBody", "Bonus stat caps (baseline at legacy L100, before any prestige boost):\n • Healing Received: +15%\n • Damage Reduction: +5%\n • Physical / Spell Resistance: +10%\n • Resource Yield: +25%\n • Reduced Blood Drain: +50%\n • Weapon / Spell Cooldown Recovery: +10%\n • Ultimate Cooldown Recovery: +20%\n • Minion Damage: +25%\n • Ability Attack Speed: +10%\n • Corruption Damage Reduction: +10%\n\nEach blood legacy prestige tier (max 10): −10% gain rate, +10% stat-cap boost. Reset a blood's chosen stats with .bl rst (default cost: 500× Shattered Bone — same item as expertise reset).", 12); }, "Reference list of every blood legacy stat and its baseline cap at L100. Class synergy multiplies the cap by 1.5× for the four stats listed above."); AddServerDisclaimer(parent2); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "BlActionsCard"); AddSectionHeading(parent3, "Actions"); GameObject obj = UIFactory.CreateHorizontalGroup(parent3, "BlActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Refresh", ".bl get", "Display your current blood legacy details in chat (.bl get)."); AddCommandButton(obj, "List Bloods", ".bl l", "List all blood legacy types tracked by Bloodcraft (.bl l)."); AddCommandButton(obj, "List Stats", ".bl lst", "List the blood-stat bonuses you can choose between (.bl lst)."); AddCommandButton(obj, "Reset Stats", ".bl rst", "Reset your chosen bonus stats for the current blood (.bl rst)."); AddSpacer(page, 6); GameObject parent4 = AddCard(page, "BlStatPickerCard"); AddSectionHeading(parent4, "Choose Bonus Stat"); CollapsibleSection.Build(parent4, "Set bonus stat for a blood type (.bl cst)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set bonus stat", ".bl cst {blood} {stat}", delegate { EnqueueOrWarn(".bl get"); }, new EnumField("blood", "Blood", PlayerStateService.BloodTypeChoice.Warrior, "Which blood type the chosen bonus stat applies to."), new EnumIndexField("stat", "Bonus stat", PlayerStateService.BloodBonusStat.PhysicalResistance, "The stat to enhance. Bloodcraft expects a 1-12 index; the dropdown sends it for you.")); }, "Pick a blood type AND the bonus stat you want to lock in for it. Bloodcraft applies the chosen stat scaled by your legacy level for that blood. Each blood tracks up to 3 chosen stats; submit again to add more (or use Reset Stats to clear the current blood)."); CollapsibleSection.Build(parent4, "Show info for a specific blood (.bl get [Blood])", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Show blood info", ".bl get {blood}", new EnumField("blood", "Blood", PlayerStateService.BloodTypeChoice.Warrior, "Which blood type to inspect.")); }, "Query any blood type's level + chosen stats — not just the one you currently have. Result is parsed and shown in the panel below; chat is also updated unless you've enabled 'Clear server messages'."); AddDivider(parent4); AddBodyText(parent4, "Unlike weapon expertise, " + Mono(".bl get") + " accepts a blood-type argument — so the 'Show info' form above can inspect ANY blood you've leveled, not just your current one."); AddSpacer(page, 6); BuildBloodInfoDisplay(page); RenderBloodLegacy(PlayerStateService.Legacy); if (!_blSubscribed) { PlayerStateService.LegacyChanged += OnLegacyChanged; _blSubscribed = true; } if (!_blInfoSubscribed) { PlayerStateService.BloodInfoChanged += OnBloodInfoChanged; _blInfoSubscribed = true; } RenderBloodInfo(); } private void BuildBloodInfoDisplay(GameObject page) { //IL_001f: 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) GameObject val = UIFactory.CreateVerticalGroup(page, "BloodInfoDisplay", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 2, new Vector4(6f, 6f, 6f, 6f)); UIFactory.SetLayoutElement(val, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 80, flexibleHeight: 0); _blInfoTitleLabel = AddInfoLabel(val, "BloodInfoTitle", "Blood Info", (FontStyles)3, Theme.ScaledUI(14)); ((Graphic)_blInfoTitleLabel).color = new Color(1f, 0.4f, 0.4f); ApplyStrongAccentOutline(_blInfoTitleLabel); _blInfoLevelLabel = AddInfoLabel(val, "BloodInfoLevel", "(submit Show info above to populate)", (FontStyles)2, Theme.ScaledUI(12)); _blInfoStatsLabel = AddInfoLabel(val, "BloodInfoStats", "", (FontStyles)0, Theme.ScaledUI(12)); ApplyStrongAccentOutline(_blInfoStatsLabel); ContentSizeFitter obj = ((Component)_blInfoStatsLabel).gameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; ((TMP_Text)_blInfoStatsLabel).enableWordWrapping = true; ((TMP_Text)_blInfoStatsLabel).overflowMode = (TextOverflowModes)0; } private void OnBloodInfoChanged() { RenderBloodInfo(); RenderBlStatsValues(); AutoResizeIfEnabled(); } private void RenderBlStatsValues() { if ((Object)(object)_blStatsValuesLabel == (Object)null) { return; } PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; bool num = !string.IsNullOrEmpty(bloodInfoLatest.BloodType) && string.Equals(bloodInfoLatest.BloodType, legacy.Type.ToString(), StringComparison.OrdinalIgnoreCase); bool flag = bloodInfoLatest.StatLines != null && bloodInfoLatest.StatLines.Count > 0; if (!num || !flag) { if (((Component)_blStatsValuesLabel).gameObject.activeSelf) { ((Component)_blStatsValuesLabel).gameObject.SetActive(false); } return; } ((TMP_Text)_blStatsValuesLabel).text = "• " + string.Join("\n• ", bloodInfoLatest.StatLines); if (!((Component)_blStatsValuesLabel).gameObject.activeSelf) { ((Component)_blStatsValuesLabel).gameObject.SetActive(true); } } private void RenderBloodInfo() { if ((Object)(object)_blInfoTitleLabel == (Object)null) { return; } PlayerStateService.BloodInfo bloodInfoLatest = PlayerStateService.BloodInfoLatest; if (string.IsNullOrEmpty(bloodInfoLatest.BloodType)) { ((TMP_Text)_blInfoTitleLabel).text = "Blood Info"; ((TMP_Text)_blInfoLevelLabel).text = "(submit Show info above to populate)"; ((TMP_Text)_blInfoStatsLabel).text = ""; return; } ((TMP_Text)_blInfoTitleLabel).text = bloodInfoLatest.BloodType + " Blood Info"; ((TMP_Text)_blInfoLevelLabel).text = ((bloodInfoLatest.Prestige > 0) ? $"Level {bloodInfoLatest.Level} Prestige {bloodInfoLatest.Prestige} Essence {bloodInfoLatest.Essence} ({bloodInfoLatest.ProgressPct}%)" : $"Level {bloodInfoLatest.Level} Essence {bloodInfoLatest.Essence} ({bloodInfoLatest.ProgressPct}%)"); if (bloodInfoLatest.StatLines != null && bloodInfoLatest.StatLines.Count > 0) { ((TMP_Text)_blInfoStatsLabel).text = "• " + string.Join("\n• ", bloodInfoLatest.StatLines); } else { ((TMP_Text)_blInfoStatsLabel).text = "(no stat lines parsed — Bloodcraft may not have sent any)"; } } private void OnLegacyChanged() { PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; if (_blTabTypeBaseline && legacy.Type != _blTabLastType) { _lastBlAutoFetchAt = 0.0; } _blTabLastType = legacy.Type; _blTabTypeBaseline = true; RenderBloodLegacy(legacy); RenderBlStatsValues(); } private void RenderBloodLegacy(PlayerStateService.LegacyState s) { if ((Object)(object)_blTypeLabel == (Object)null) { return; } ((TMP_Text)_blTypeLabel).text = s.Type.ToString(); ((TMP_Text)_blProgressLabel).text = ((s.Prestige > 0) ? $"Level {s.Level} ({s.Progress * 100f:0.#}%) Prestige {s.Prestige}" : $"Level {s.Level} ({s.Progress * 100f:0.#}%)"); List list = PlayerStateService.DecodeBloodBonusStats(s.BonusStatsRaw); List list2 = new List(); foreach (PlayerStateService.BloodStatType item in list) { if (item != 0) { list2.Add(item.ToString()); } } ((TMP_Text)_blBonusLabel).text = ((list2.Count > 0) ? ("Bonus Stats: " + string.Join(", ", list2)) : "Bonus Stats: (none yet — use the form below to choose)"); } private void BuildUnarmedShiftTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "ShiftSpellCard", Theme.SystemTintExpertise); AddSectionHeading(parent, "Shift Spell"); _shiftSpellLabel = AddInfoLabel(parent, "ShiftSpell", "Equipped: —", (FontStyles)0, Theme.ScaledUI(14)); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "UnarmedCard", Theme.SystemTintExpertise); AddSectionHeading(parent2, "Unarmed Expertise"); _unarmedStatusLabel = AddInfoLabel(parent2, "UnarmedStatus", "Equip your fists (no weapon) to inspect unarmed expertise.", (FontStyles)0, Theme.ScaledUI(14)); _unarmedBonusLabel = AddInfoLabel(parent2, "UnarmedBonus", "Bonus Stats: —", (FontStyles)0, Theme.ScaledUI(13)); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "ShiftActionsCard"); AddSectionHeading(parent3, "Actions"); GameObject obj = UIFactory.CreateHorizontalGroup(parent3, "UnarmedShiftActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Toggle Shift", ".class shift", "Toggle whether your class spell is locked into the shift slot (.class shift)."); AddCommandButton(obj, "Lock Spells", ".wep locksp", "Lock in next-equipped spells for use in your unarmed slots (.wep locksp)."); AddCommandButton(obj, "Refresh", ".wep get", "Refresh weapon expertise details (.wep get). Chat receives the response."); AddDivider(parent3); AddBodyText(parent3, "Choosing which class spell goes in the shift slot takes a number (" + Mono(".class csp <#>") + "). Use chat for now; a spell picker arrives in a later phase."); RenderUnarmedShift(PlayerStateService.Expertise, PlayerStateService.ShiftSpell); if (!_shiftSubscribed) { PlayerStateService.ExpertiseChanged += OnExpertiseChangedForUnarmed; PlayerStateService.ShiftSpellChanged += OnShiftSpellChanged; _shiftSubscribed = true; } } private void OnExpertiseChangedForUnarmed() { RenderUnarmedShift(PlayerStateService.Expertise, PlayerStateService.ShiftSpell); } private void OnShiftSpellChanged() { RenderUnarmedShift(PlayerStateService.Expertise, PlayerStateService.ShiftSpell); } private void RenderUnarmedShift(PlayerStateService.ExpertiseState exp, PlayerStateService.ShiftSpellState shift) { if ((Object)(object)_shiftSpellLabel == (Object)null) { return; } ((TMP_Text)_shiftSpellLabel).text = ((shift.SpellIndex == 0) ? "Equipped: (none)" : (PrefabNameResolver.TryGet(shift.SpellIndex, out var name) ? ("Equipped: " + name) : $"Equipped: PrefabGUID {shift.SpellIndex}")); if (exp.Type == PlayerStateService.WeaponType.Unarmed) { ((TMP_Text)_unarmedStatusLabel).text = ((exp.Prestige > 0) ? $"Level {exp.Level} ({exp.Progress * 100f:0.#}%) Prestige {exp.Prestige}" : $"Level {exp.Level} ({exp.Progress * 100f:0.#}%)"); List list = PlayerStateService.DecodeWeaponBonusStats(exp.BonusStatsRaw); List list2 = new List(); foreach (PlayerStateService.WeaponStatType item in list) { if (item != 0) { list2.Add(item.ToString()); } } ((TMP_Text)_unarmedBonusLabel).text = ((list2.Count > 0) ? ("Bonus Stats: " + string.Join(", ", list2)) : "Bonus Stats: (none yet — pick one via .wep cst)"); } else { ((TMP_Text)_unarmedStatusLabel).text = $"Currently equipped: {exp.Type}. Equip your fists (unarm) to inspect unarmed expertise."; ((TMP_Text)_unarmedBonusLabel).text = "Bonus Stats: —"; } } private void BuildPrestigeTab(GameObject page) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) AddSectionHeading(page, "Current Prestige"); GameObject parent = AddCard(page, "PrestigeXpCard", Theme.SystemTintXP, 6, 2); _prestigeXpLabel = AddInfoLabel(parent, "PrestigeXp", "Experience prestige: —", (FontStyles)0, Theme.ScaledUI(14)); GameObject parent2 = AddCard(page, "PrestigeLegacyCard", Theme.SystemTintLegacy, 6, 2); _prestigeLegacyLabel = AddInfoLabel(parent2, "PrestigeLegacy", "Blood legacy prestige: —", (FontStyles)0, Theme.ScaledUI(14)); GameObject parent3 = AddCard(page, "PrestigeExpertiseCard", Theme.SystemTintExpertise, 6, 2); _prestigeExpertiseLabel = AddInfoLabel(parent3, "PrestigeExpertise", "Weapon expertise prestige: —", (FontStyles)0, Theme.ScaledUI(14)); GameObject parent4 = AddCard(page, "PrestigeFamCard", Theme.SystemTintFamiliar, 6, 2); _prestigeFamLabel = AddInfoLabel(parent4, "PrestigeFam", "Familiar prestige: —", (FontStyles)0, Theme.ScaledUI(14)); AddSpacer(page, 6); AddSectionHeading(page, "Quick actions"); GameObject obj = UIFactory.CreateHorizontalGroup(page, "PrestigeActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "List", ".prestige l", "List the prestige systems available on this server (.prestige l). Response in chat."); AddCommandButton(obj, "Sync Buffs", ".prestige sb", "Re-apply your prestige buffs if any have dropped (.prestige sb)."); AddCommandButton(obj, "Exoform", ".prestige exoform", "Toggle taunting to enter exoform shapeshift (.prestige exoform). Requires Exo prestige."); AddCommandButton(obj, "Shroud", ".prestige shroud", "Toggle permashroud if you qualify for it (.prestige shroud)."); AddSpacer(page, 4); AddSectionHeading(page, "Prestige actions (forms)"); CollapsibleSection.Build(page, "Prestige in a system (.prestige me)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Prestige in a system", ".prestige me {type}", new EnumField("type", "Prestige type", PlayerStateService.PrestigeType.Experience, "Which leveling system to prestige in. You must be at max level in that system.")); }, "Expand to prestige in a specific system — Experience, a weapon expertise, a blood legacy, or Exo."); CollapsibleSection.Build(page, "Show prestige info (.prestige get)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Show prestige info", ".prestige get {type}", new EnumField("type", "Prestige type", PlayerStateService.PrestigeType.Experience)); }, "Expand, pick a system, Submit. The reply is parsed and rendered in-panel below — chat is also updated unless you've enabled 'Clear server messages' in settings."); AddSpacer(page, 4); BuildPrestigeInfoDisplay(page); CollapsibleSection.Build(page, "Leaderboard (.prestige lb)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Prestige leaderboard", ".prestige lb {type}", new EnumField("type", "Prestige type", PlayerStateService.PrestigeType.Experience)); }, "Expand to view the prestige leaderboard for a system."); CollapsibleSection.Build(page, "Select exoform variant (.prestige sf)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Select exoform", ".prestige sf {form}", new EnumField("form", "Exoform", PlayerStateService.ExoformVariant.EvolvedVampire, "EvolvedVampire or CorruptedSerpent.")); }, "Expand to switch between the Evolved Vampire and Corrupted Serpent exoform shapeshifts."); AddSpacer(page, 6); GameObject parent5 = AddCard(page, "PrestigeProgressionCard"); AddSectionHeading(parent5, "What each prestige tier gives you"); AddContextBodyLabel(parent5, "PrestigeProgressionIntro", "Bloodcraft has up to 10 tiers per system (Experience / each weapon / each blood) — plus 100 Exo tiers on top of leveling-prestige."); CollapsibleSection.Build(parent5, "Leveling (Experience) prestige — per tier", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "LevelingPrestigeBody", "Per tier (max 10):\n • Resets character level back to 10.\n • Permanent leveling buff (server-tunable; varies by Bloodcraft version).\n • −5% XP gain from kills (LevelingPrestigeReducer = 0.05).\n • +10% rate boost to weapon expertise + blood legacy XP (PrestigeRateMultiplier = 0.10).\n • Unlocks one additional class-spell slot (PrestigeLevelsToUnlockClassSpells = 0..5, one per tier).\n\nNet: each tier slows your raw level XP slightly but accelerates expertise / legacy gain. By tier 10 you've traded −50% XP for +100% expertise + legacy gain rate.", 12); }, "What you get every time you complete .prestige me Experience."); CollapsibleSection.Build(parent5, "Weapon Expertise / Blood Legacy prestige — per tier", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "ExpLegPrestigeBody", "Per tier (max 10 per weapon, 10 per blood):\n • Resets that specific system's level back to 1.\n • −10% XP rate for THAT weapon / blood (PrestigeRatesReducer = 0.10).\n • +10% stat-bonus cap boost for THAT weapon's / blood's chosen stats (PrestigeStatMultiplier = 0.10).\n\nBy tier 10: stat caps are ×2.0 their baseline for that weapon / blood, but levelling that weapon / blood takes about twice as long.", 12); }, "Per-system prestige using .prestige me or .prestige me ."); CollapsibleSection.Build(parent5, "Exo Prestige — endgame tier (after max Experience prestige)", startExpanded: false, delegate(GameObject c) { AddContextBodyLabel(c, "ExoPrestigeBody", "Up to 100 Exo tiers available once you've maxed leveling-prestige.\n • Each Exo prestige resets your XP to 0 (level stays at max).\n • Awards 500× Primal Stygian Shards per tier (ExoPrestigeReward = 28358550, ExoPrestigeRewardQuantity = 500).\n • Unlocks Exoforms — shapeshift between Evolved Vampire and Corrupted Serpent.\n Form duration grows from 15s at Exo 1 to roughly 180s at Exo 100 (formula: 15 + (165 ÷ 100) × exoLevel).\n • Shard rewards can buy specific V-Blood familiars via .fam echoes <Name> (cost scales with V-Blood level + tier; shard bearers cost ~25× baseline).\n\nUse the .prestige sf form (above) to pick which exoform is active; .prestige exoform triggers the transformation (taunt emote by default).", 12); }, "Unlocks at maxed Experience prestige + level 90. The highest tier of Bloodcraft progression."); AddServerDisclaimer(parent5); RenderPrestige(); if (!_prestigeSubscribed) { PlayerStateService.ExperienceChanged += OnAnyForPrestige; PlayerStateService.LegacyChanged += OnAnyForPrestige; PlayerStateService.ExpertiseChanged += OnAnyForPrestige; PlayerStateService.FamiliarChanged += OnAnyForPrestige; _prestigeSubscribed = true; } if (!_prestigeInfoSubscribed) { PlayerStateService.PrestigeInfoChanged += OnPrestigeInfoChanged; _prestigeInfoSubscribed = true; } RenderPrestigeInfo(); } private void BuildPrestigeInfoDisplay(GameObject page) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) _prestigeInfoSection = UIFactory.CreateVerticalGroup(page, "PrestigeInfoDisplay", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 8, new Vector4(12f, 12f, 10f, 10f)); UIFactory.SetLayoutElement(_prestigeInfoSection, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 120, flexibleHeight: 0); _prestigeInfoTitleLabel = AddInfoLabel(_prestigeInfoSection, "PrestigeInfoTitle", "Prestige Info", (FontStyles)3, Theme.ScaledUI(16)); ((Graphic)_prestigeInfoTitleLabel).color = new Color(0.6f, 0.95f, 0.6f); _prestigeInfoLevelLabel = AddInfoLabel(_prestigeInfoSection, "PrestigeInfoLevel", "(submit Show prestige info above to populate)", (FontStyles)2, Theme.ScaledUI(13)); _prestigeBar = MiniBar.Create(_prestigeInfoSection, "PrestigeBar", out _prestigeBarFill, new Color(0.6f, 0.95f, 0.6f, 0.95f)); _prestigeBar.SetActive(false); _prestigeInfoEffectsLabel = AddInfoLabel(_prestigeInfoSection, "PrestigeInfoEffects", "", (FontStyles)0, Theme.ScaledUI(13)); ContentSizeFitter obj = ((Component)_prestigeInfoEffectsLabel).gameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; ((TMP_Text)_prestigeInfoEffectsLabel).enableWordWrapping = true; ((TMP_Text)_prestigeInfoEffectsLabel).overflowMode = (TextOverflowModes)0; } private void OnPrestigeInfoChanged() { RenderPrestigeInfo(); AutoResizeIfEnabled(); } private void RenderPrestigeInfo() { if ((Object)(object)_prestigeInfoTitleLabel == (Object)null) { return; } PlayerStateService.PrestigeInfo prestigeInfoLatest = PlayerStateService.PrestigeInfoLatest; if (string.IsNullOrEmpty(prestigeInfoLatest.TypeName)) { ((TMP_Text)_prestigeInfoTitleLabel).text = "Prestige Info"; ((TMP_Text)_prestigeInfoLevelLabel).text = "(submit Show prestige info above to populate)"; ((TMP_Text)_prestigeInfoEffectsLabel).text = ""; if ((Object)(object)_prestigeBar != (Object)null && _prestigeBar.activeSelf) { _prestigeBar.SetActive(false); } return; } ((TMP_Text)_prestigeInfoTitleLabel).text = prestigeInfoLatest.TypeName + " Prestige Info"; ((TMP_Text)_prestigeInfoLevelLabel).text = ((prestigeInfoLatest.MaxLevel > 0) ? $"Current Prestige Level: {prestigeInfoLatest.Level} / {prestigeInfoLatest.MaxLevel}" : $"Current Prestige Level: {prestigeInfoLatest.Level}"); if (prestigeInfoLatest.EffectLines != null && prestigeInfoLatest.EffectLines.Count > 0) { ((TMP_Text)_prestigeInfoEffectsLabel).text = "• " + string.Join("\n• ", prestigeInfoLatest.EffectLines); } else { ((TMP_Text)_prestigeInfoEffectsLabel).text = "(no additional effect lines parsed)"; } bool flag = Settings.ShowProgressBars && prestigeInfoLatest.MaxLevel > 0; if ((Object)(object)_prestigeBar != (Object)null && _prestigeBar.activeSelf != flag) { _prestigeBar.SetActive(flag); } if (flag) { MiniBar.SetProgress(_prestigeBarFill, (float)prestigeInfoLatest.Level / (float)prestigeInfoLatest.MaxLevel); } } private void OnAnyForPrestige() { RenderPrestige(); } private void RenderPrestige() { if (!((Object)(object)_prestigeXpLabel == (Object)null)) { ((TMP_Text)_prestigeXpLabel).text = $"Experience prestige: {PlayerStateService.Experience.Prestige}"; ((TMP_Text)_prestigeLegacyLabel).text = $"Blood legacy prestige ({PlayerStateService.Legacy.Type}): {PlayerStateService.Legacy.Prestige}"; ((TMP_Text)_prestigeExpertiseLabel).text = $"Weapon expertise prestige ({PlayerStateService.Expertise.Type}): {PlayerStateService.Expertise.Prestige}"; ((TMP_Text)_prestigeFamLabel).text = $"Familiar prestige ({(string.IsNullOrEmpty(PlayerStateService.Familiar.Name) ? "no familiar" : PlayerStateService.Familiar.Name)}): {PlayerStateService.Familiar.Prestige}"; } } private void BuildLevelsTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "LvlXpCard", Theme.SystemTintXP); AddSectionHeading(parent, "Player Experience"); _lvlXpLabel = AddInfoLabel(parent, "LvlXp", "—", (FontStyles)0, Theme.ScaledUI(13)); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "LvlLegacyCard", Theme.SystemTintLegacy); AddSectionHeading(parent2, "Blood Legacy"); _lvlLegacyLabel = AddInfoLabel(parent2, "LvlLegacy", "—", (FontStyles)0, Theme.ScaledUI(13)); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "LvlExpertiseCard", Theme.SystemTintExpertise); AddSectionHeading(parent3, "Weapon Expertise (active weapon)"); _lvlExpertiseLabel = AddInfoLabel(parent3, "LvlExpertise", "—", (FontStyles)0, Theme.ScaledUI(13)); _lvlExpertiseBonusLabel = AddInfoLabel(parent3, "LvlExpertiseBonus", "Bonus stats: —", (FontStyles)2, Theme.ScaledUI(12)); AddBodyText(parent3, "Bloodcraft streams only the currently-equipped weapon. Swap weapons to update the row above, or use " + Mono(".wep l") + " for the full list (reply lands in chat)."); GameObject obj = UIFactory.CreateHorizontalGroup(parent3, "AllWepRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "List Weapon Types", ".wep l", "Sends .wep l. Bloodcraft replies in chat with the list of weapon types you can level."); AddSpacer(page, 6); GameObject parent4 = AddCard(page, "LvlFamCard", Theme.SystemTintFamiliar); AddSectionHeading(parent4, "★ Familiar (active)"); _lvlFamLabel = AddInfoLabel(parent4, "LvlFam", "—", (FontStyles)0, Theme.ScaledUI(13)); _lvlFamStatsLabel = AddInfoLabel(parent4, "LvlFamStats", "HP — PP — SP —", (FontStyles)2, Theme.ScaledUI(12)); AddSpacer(page, 6); GameObject parent5 = AddCard(page, "LvlProfCard", Theme.SystemTintProfession); AddSectionHeading(parent5, "Professions"); _lvlProfessions1Label = AddInfoLabel(parent5, "LvlProf1", "—", (FontStyles)0, Theme.ScaledUI(12)); _lvlProfessions2Label = AddInfoLabel(parent5, "LvlProf2", "—", (FontStyles)0, Theme.ScaledUI(12)); _lvlProfessions3Label = AddInfoLabel(parent5, "LvlProf3", "—", (FontStyles)0, Theme.ScaledUI(12)); _lvlProfessions4Label = AddInfoLabel(parent5, "LvlProf4", "—", (FontStyles)0, Theme.ScaledUI(12)); AddSpacer(page, 6); GameObject parent6 = AddCard(page, "LvlProfToolsCard"); AddSectionHeading(parent6, "Profession Tools"); GameObject obj2 = UIFactory.CreateHorizontalGroup(parent6, "ProfRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj2, "List", ".prof l", "List the professions Bloodcraft tracks (.prof l). Reply in chat."); AddCommandButton(obj2, "Toggle Log", ".prof log", "Toggle in-chat profession-progress logging (.prof log). SERVER-side toggle."); CollapsibleSection.Build(parent6, "Show profession progress (.prof get)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Show profession", ".prof get {profession}", new EnumField("profession", "Profession", PlayerStateService.BloodcraftProfession.Enchanting, "Which profession to inspect.")); }, "Displays your current level + progress for the chosen profession in chat."); AddSpacer(page, 6); GameObject parent7 = AddCard(page, "LvlPlayerToolsCard"); AddSectionHeading(parent7, "Player Tools"); GameObject obj3 = UIFactory.CreateHorizontalGroup(parent7, "PlayerToolsRow1", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj3, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj3, "User Stats", ".misc userstats", "Print a summary of player stats in chat (.misc userstats)."); AddCommandButton(obj3, "Toggle XP Log", ".lvl log", "Toggle in-chat logging of leveling-progress messages (.lvl log). SERVER-side toggle — Bloodcraft replies with the new state in chat."); AddCommandButton(obj3, "Reminders", ".misc remindme", "Toggle general feature reminders (.misc remindme). SERVER-side toggle."); AddCommandButton(obj3, "Silence", ".misc silence", "Reset stuck combat music if it won't stop (.misc silence)."); GameObject obj4 = UIFactory.CreateHorizontalGroup(parent7, "PlayerToolsRow2", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth4 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj4, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj4, "Starter Kit", ".misc kitme", "Claim the server's starter kit (.misc kitme). One-time on most servers."); AddCommandButton(obj4, "Prepare Hunt", ".misc prepare", "Auto-complete the GettingReadyForTheHunt quest if it's stuck (.misc prepare)."); CollapsibleSection.Build(parent7, "Toggle scrolling combat text (.misc sct)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Toggle SCT", ".misc sct {type}", new TextField("type", "SCT element type", "", "Element name (e.g. 'damage', 'heal'). Bloodcraft's reply tells you the new state.")); }, "Enable or disable a specific scrolling-combat-text element. Bloodcraft replies with the new state in chat."); AddDivider(parent7); AddBodyText(parent7, $"Heads up: most of these are server-side TOGGLES — Bloodcraft flips a flag and reports the new state in chat. The client can't 'remember' the new state across sessions because the server is the source of truth (same with {Mono(".fam t")} / {Mono(".lvl log")} elsewhere)."); RenderLevels(); if (!_lvlSubscribed) { PlayerStateService.ExperienceChanged += OnAnyForLevels; PlayerStateService.LegacyChanged += OnAnyForLevels; PlayerStateService.ExpertiseChanged += OnAnyForLevels; PlayerStateService.FamiliarChanged += OnAnyForLevels; PlayerStateService.ProfessionChanged += OnAnyForLevels; _lvlSubscribed = true; } } private void OnAnyForLevels() { RenderLevels(); } private void RenderLevels() { if ((Object)(object)_lvlXpLabel == (Object)null) { return; } PlayerStateService.ExperienceState experience = PlayerStateService.Experience; PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; PlayerStateService.ExpertiseState expertise = PlayerStateService.Expertise; PlayerStateService.FamiliarState familiar = PlayerStateService.Familiar; PlayerStateService.ProfessionState profession = PlayerStateService.Profession; ((TMP_Text)_lvlXpLabel).text = ((experience.Prestige > 0) ? $"Level {experience.Level} ({experience.Progress * 100f:0.#}%) Prestige {experience.Prestige} Class: {experience.Class}" : $"Level {experience.Level} ({experience.Progress * 100f:0.#}%) Class: {experience.Class}"); ((TMP_Text)_lvlLegacyLabel).text = ((legacy.Prestige > 0) ? $"{legacy.Type} Level {legacy.Level} ({legacy.Progress * 100f:0.#}%) Prestige {legacy.Prestige}" : $"{legacy.Type} Level {legacy.Level} ({legacy.Progress * 100f:0.#}%)"); ((TMP_Text)_lvlExpertiseLabel).text = ((expertise.Prestige > 0) ? $"{expertise.Type} Level {expertise.Level} ({expertise.Progress * 100f:0.#}%) Prestige {expertise.Prestige}" : $"{expertise.Type} Level {expertise.Level} ({expertise.Progress * 100f:0.#}%)"); List list = PlayerStateService.DecodeWeaponBonusStats(expertise.BonusStatsRaw); List list2 = new List(); foreach (PlayerStateService.WeaponStatType item in list) { if (item != 0) { list2.Add(item.ToString()); } } ((TMP_Text)_lvlExpertiseBonusLabel).text = ((list2.Count > 0) ? ("Bonus stats: " + string.Join(", ", list2)) : "Bonus stats: (none yet — choose via .wep cst)"); bool hasActive = familiar.HasActive; ((TMP_Text)_lvlFamLabel).text = ((!hasActive) ? "(no familiar bound)" : ((familiar.Prestige > 0) ? $"{familiar.Name} Level {familiar.Level} ({familiar.Progress * 100f:0.#}%) Prestige {familiar.Prestige}" : $"{familiar.Name} Level {familiar.Level} ({familiar.Progress * 100f:0.#}%)")); ((TMP_Text)_lvlFamStatsLabel).text = (hasActive ? $"HP {familiar.MaxHealth} PP {familiar.PhysicalPower} SP {familiar.SpellPower}" : "HP — PP — SP —"); ((TMP_Text)_lvlProfessions1Label).text = $"Enchanting Lv {profession.EnchantingLevel:00} ({profession.EnchantingProgress * 100f:0.#}%) Alchemy Lv {profession.AlchemyLevel:00} ({profession.AlchemyProgress * 100f:0.#}%)"; ((TMP_Text)_lvlProfessions2Label).text = $"Harvesting Lv {profession.HarvestingLevel:00} ({profession.HarvestingProgress * 100f:0.#}%) Blacksmithing Lv {profession.BlacksmithingLevel:00} ({profession.BlacksmithingProgress * 100f:0.#}%)"; ((TMP_Text)_lvlProfessions3Label).text = $"Tailoring Lv {profession.TailoringLevel:00} ({profession.TailoringProgress * 100f:0.#}%) Woodcutting Lv {profession.WoodcuttingLevel:00} ({profession.WoodcuttingProgress * 100f:0.#}%)"; ((TMP_Text)_lvlProfessions4Label).text = $"Mining Lv {profession.MiningLevel:00} ({profession.MiningProgress * 100f:0.#}%) Fishing Lv {profession.FishingLevel:00} ({profession.FishingProgress * 100f:0.#}%)"; } private void BuildDailyQuestTab(GameObject page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) GameObject parent = AddCard(page, "DQDailyCard", Theme.SystemTintQuest); AddSectionHeading(parent, "Daily Quest"); _dqDailyTargetLabel = AddInfoLabel(parent, "DQDailyTarget", "—", (FontStyles)1, Theme.ScaledUI(15)); ((Graphic)_dqDailyTargetLabel).color = new Color(0f, 1f, 1f); ApplyStrongAccentOutline(_dqDailyTargetLabel); _dqDailyProgressLabel = AddInfoLabel(parent, "DQDailyProgress", "—", (FontStyles)2, Theme.ScaledUI(13)); GameObject obj = UIFactory.CreateHorizontalGroup(parent, "DQDailyActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Refresh", ".quest p d", "Print the daily quest objective into chat (.quest p d). The UI updates automatically when Bloodcraft pushes new state via the Eclipse protocol; this is for an explicit poll."); AddCommandButton(obj, "Track", ".quest t d", "Print the location/direction to your daily target (.quest t d). Reply appears in chat."); AddCommandButton(obj, "Reroll", ".quest r d", "Reroll the daily quest (.quest r d). Costs the server-configured reroll item; only works once the daily is complete OR if the server allows mid-quest rerolls."); AddSpacer(page, 6); GameObject parent2 = AddCard(page, "DQWeeklyCard", Theme.SystemTintQuest); AddSectionHeading(parent2, "Weekly Quest"); _dqWeeklyTargetLabel = AddInfoLabel(parent2, "DQWeeklyTarget", "—", (FontStyles)1, Theme.ScaledUI(15)); ((Graphic)_dqWeeklyTargetLabel).color = new Color(1f, 0.85f, 0.3f); ApplyStrongAccentOutline(_dqWeeklyTargetLabel); _dqWeeklyProgressLabel = AddInfoLabel(parent2, "DQWeeklyProgress", "—", (FontStyles)2, Theme.ScaledUI(13)); GameObject obj2 = UIFactory.CreateHorizontalGroup(parent2, "DQWeeklyActions", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj2, "Refresh", ".quest p w", "Print the weekly quest objective into chat (.quest p w)."); AddCommandButton(obj2, "Track", ".quest t w", "Print the location/direction to your weekly target (.quest t w)."); AddCommandButton(obj2, "Reroll", ".quest r w", "Reroll the weekly quest (.quest r w). Costs the server-configured reroll item."); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "DQSettingsCard"); AddSectionHeading(parent3, "Settings"); GameObject obj3 = UIFactory.CreateHorizontalGroup(parent3, "DQSettings", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj3, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj3, "Toggle Quest Log", ".quest log", "Toggle in-chat progress logging (.quest log). When on, Bloodcraft prints a message each time you progress an objective."); AddDivider(parent3); AddBodyText(parent3, "Toggle the Daily Quest overlay from the panel footer to track progress in a small movable HUD."); RenderDailyQuestTab(); if (!_dqSubscribed) { PlayerStateService.QuestChanged += OnQuestChangedForTab; _dqSubscribed = true; } } private void OnQuestChangedForTab() { RenderDailyQuestTab(); } private void RenderDailyQuestTab() { if (!((Object)(object)_dqDailyTargetLabel == (Object)null)) { PlayerStateService.QuestState dailyQuest = PlayerStateService.DailyQuest; PlayerStateService.QuestState weeklyQuest = PlayerStateService.WeeklyQuest; FormatQuestRow(_dqDailyTargetLabel, _dqDailyProgressLabel, dailyQuest, "(no daily quest yet — check back after the next refresh)"); FormatQuestRow(_dqWeeklyTargetLabel, _dqWeeklyProgressLabel, weeklyQuest, "(no weekly quest yet — check back after the next refresh)"); } } private static void FormatQuestRow(TextMeshProUGUI target, TextMeshProUGUI progress, PlayerStateService.QuestState s, string emptyHint) { //IL_00e4: 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) if (string.IsNullOrEmpty(s.TargetName) && s.Goal <= 0) { ((TMP_Text)target).text = emptyHint; ((TMP_Text)progress).text = ""; return; } ((TMP_Text)target).text = (s.IsVBlood ? (s.TargetName + " (V Blood)") : (s.TargetName ?? "")); if (s.Goal > 0 && s.Progress >= s.Goal) { ((TMP_Text)progress).text = "Complete! Reroll for the next one."; ((Graphic)progress).color = new Color(0.6f, 1f, 0.6f); return; } ((TMP_Text)progress).text = $"Progress: {s.Progress} / {s.Goal}"; ((Graphic)progress).color = Color.white; } private void RenderAdminInfoNote(GameObject page, string contextLabel) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(page, "AdminInfoNote", "Admin only. " + contextLabel + " commands require server-admin permission. Non-admins can click these buttons, but the server will reject them with a permission error. Nothing here can damage your client.", (TextAlignmentOptions)257, (Color?)new Color(1f, 0.85f, 0.5f), Theme.ScaledUI(12), 0.15f, (Color?)null); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 40, flexibleWidth: num, preferredHeight: 48, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).richText = true; AddSpacer(page, 4); } private void BuildAdminTab(GameObject page) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) GameObject page2 = AddCard(page, "AdminNoteCard"); RenderAdminInfoNote(page2, "Bloodcraft admin"); AddSpacer(page, 6); GameObject parent = AddCard(page, "AdminDiagCard"); AddSectionHeading(parent, "Server Diagnostics"); GameObject obj = UIFactory.CreateHorizontalGroup(parent, "AdminDiag", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Server Health", ".misc health", "Show the Bloodcraft server's startup readiness summary in chat (.misc health). Admin only."); AddSpacer(page, 6); AddSectionHeading(page, "Admin forms"); CollapsibleSection.Build(page, "Set player level (.lvl set)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Set player level", ".lvl set {player} {level}", new PlayerNameField("player", "Player", "Target player's character name (must match exactly)."), new IntField("level", "Level", 1, 200, "Target character level. Bloodcraft default cap is 90.")); }, "Expand to set a player's character level. Admin only."); CollapsibleSection.Build(page, "Toggle shared-XP exclusion (.lvl ignore)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Toggle shared-XP ignore", ".lvl ignore {player}", new PlayerNameField("player", "Player", "Target player. The flip is reversible — call again to remove from the ignore list.")); }, "Adds (or removes) a player from the list of those NOT eligible to receive shared experience. Toggle — Bloodcraft replies with the new state in chat."); CollapsibleSection.Build(page, "Set player profession level (.prof set)", startExpanded: false, delegate(GameObject content) { FormBuilder.Build(content, "Set profession", ".prof set {player} {profession} {level}", new PlayerNameField("player", "Player"), new EnumField("profession", "Profession", PlayerStateService.BloodcraftProfession.Enchanting), new IntField("level", "Level", 0, 100, "Target level. 0 resets.")); }, "Set a player's profession level for the named profession."); CollapsibleSection.Build(page, "Set player prestige (.prestige set)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set player prestige", ".prestige set {player} {type} {level}", new PlayerNameField("player", "Player", "Target player's character name."), new EnumField("type", "Prestige type", PlayerStateService.PrestigeType.Experience, "Which prestige system to set."), new IntField("level", "Level", 0, 100, "Prestige level (0 to reset).")); }, "Expand to set a player's prestige level in a specific system."); CollapsibleSection.Build(page, "Reset player prestige (.prestige r)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Reset player prestige", ".prestige r {player} {type}", new PlayerNameField("player", "Player"), new EnumField("type", "Prestige type", PlayerStateService.PrestigeType.Experience, "Which prestige system to reset.")); }, "Expand to reset a player's prestige in a specific system."); CollapsibleSection.Build(page, "Toggle prestige-leaderboard exclusion (.prestige ignore)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Toggle leaderboard exclusion", ".prestige ignore {player}", new PlayerNameField("player", "Player", "Toggle — calling again removes them from the exclusion list.")); }, "Adds (or removes) the player from the list of those who are HIDDEN from prestige leaderboards. Intended for admin/staff accounts."); CollapsibleSection.Build(page, "GLOBAL prestige-buff purge — DESTRUCTIVE", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Global prestige-buff purge", ".prestige iacknowledgethiswillremoveallprestigebuffsfromplayersandwantthattohappen", new BoolField("confirm", "Yes, purge prestige buffs from EVERY player", defaultValue: false, "Required. Affects every player on the server — they each need to .prestige sb to re-apply.", requireTrue: true)); }, "Removes prestige buffs from EVERY player on the server, so config-changed buffs can be re-applied cleanly. Cannot be undone in one click — every player would need to re-apply via .prestige sb. Required confirm."); CollapsibleSection.Build(page, "Force-complete a player's quest (.quest c)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Force-complete quest", ".quest c {player} {type}", new PlayerNameField("player", "Player"), new EnumField("type", "Quest type", PlayerStateService.BloodcraftQuestType.Daily, "Daily or Weekly.")); }, "Marks a Daily or Weekly quest as complete for the named player without them having to fulfil the objective."); CollapsibleSection.Build(page, "Set blood legacy (.bl set)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set blood legacy", ".bl set {player} {blood} {level}", new PlayerNameField("player", "Player"), new EnumField("blood", "Blood", PlayerStateService.BloodType.Warrior, "Worker / Warrior / Scholar / Rogue / Mutant / Draculin / Immortal / Creature / Brute / Corruption."), new IntField("level", "Level", 0, 100)); }, "Expand to set a player's blood legacy level."); CollapsibleSection.Build(page, "Set weapon expertise (.wep set)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set weapon expertise", ".wep set {player} {weapon} {level}", new PlayerNameField("player", "Player"), new EnumField("weapon", "Weapon", PlayerStateService.WeaponType.Sword), new IntField("level", "Level", 0, 100)); }, "Expand to set a player's weapon expertise level for a specific weapon."); CollapsibleSection.Build(page, "Set profession (.prof set)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set profession", ".prof set {player} {profession} {level}", new PlayerNameField("player", "Player"), new EnumField("profession", "Profession", PlayerStateService.ProfessionType.Mining), new IntField("level", "Level", 0, 100)); }, "Expand to set a player's level in a specific profession."); CollapsibleSection.Build(page, "Set familiar level (.fam sl)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set familiar level", ".fam sl {player} {level}", new PlayerNameField("player", "Player", "Target player. Their currently-bound familiar is affected."), new IntField("level", "Level", 1, 100)); }, "Expand to set a player's currently-bound familiar to a specific level."); CollapsibleSection.Build(page, "Refresh quests (.quest rf)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Refresh quests", ".quest rf {player}", new PlayerNameField("player", "Player")); }, "Expand to force-refresh a player's daily and weekly quests."); CollapsibleSection.Build(page, "Complete quest (.quest c)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Complete quest", ".quest c {player} {schedule}", new PlayerNameField("player", "Player"), new EnumField("schedule", "Schedule", PlayerStateService.QuestSchedule.Daily, "Daily or Weekly.")); }, "Expand to forcibly complete a player's daily or weekly quest."); AddSpacer(page, 6); AddBodyText(page, "All admin commands now have forms. If you aren't an admin on this server, commands return a permission error."); } private void BuildKindredLogisticsTab(GameObject page) { AddBodyText(AddCard(page, "KLIntroCard"), $"Requires the KindredLogistics server mod. Personal toggles affect only your character; admin globals affect the whole server (admin only). Personal toggles use {Mono(".l ...")}; admin globals use {Mono(".lg ...")}."); AddSpacer(page, 6); GameObject parent = AddCard(page, "KLPersonalCard"); AddSectionHeading(parent, "Personal Toggles (.l)"); GameObject parent2 = AddKLRow(parent, "KLPersonal1"); AddCommandButton(parent2, "Sort Stash", ".l ss", "Toggle auto-stash on double-click of the sort button (.l ss)."); AddCommandButton(parent2, "Craft Pull", ".l cr", "Toggle right-click on a recipe pulling missing ingredients (.l cr)."); AddCommandButton(parent2, "Don't Pull Last", ".l dpl", "Toggle never pulling the last item from a container (.l dpl)."); AddCommandButton(parent2, "Servant Stash", ".l asm", "Toggle auto-stash of servant mission rewards (.l asm)."); GameObject parent3 = AddKLRow(parent, "KLPersonal2"); AddCommandButton(parent3, "Conveyor", ".l co", "Toggle named sender/receiver chests routing items between them (.l co)."); AddCommandButton(parent3, "Salvage", ".l sal", "Toggle chests named 'salvage' auto-salvaging their contents (.l sal)."); AddCommandButton(parent3, "Unit Spawner", ".l us", "Toggle chests named 'spawner' auto-filling unit stations (.l us)."); AddCommandButton(parent3, "Brazier", ".l bz", "Toggle chests named 'brazier' auto-fueling braziers (.l bz)."); GameObject parent4 = AddKLRow(parent, "KLPersonal3"); AddCommandButton(parent4, "Silent Pull", ".l sp", "Toggle suppressing chat messages when pulling items (.l sp)."); AddCommandButton(parent4, "Silent Stash", ".l ssh", "Toggle suppressing chat messages when stashing items (.l ssh)."); AddCommandButton(parent4, "Show Settings", ".l s", "Print your current personal Logistics settings into chat (.l s)."); AddSpacer(page, 6); GameObject parent5 = AddCard(page, "KLUtilityCard"); AddSectionHeading(parent5, "Utility"); AddCommandButton(AddKLRow(parent5, "KLUtility"), "Stash All", ".stash", "Stash all items in your inventory into nearby chests (.stash)."); CollapsibleSection.Build(parent5, "Pull item from containers (.pull)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Pull item", ".pull {item} {quantity}", new TextField("item", "Item name", "", "Item to pull. Exact match against the item's prefab name (e.g. 'Iron Ingot')."), new IntField("quantity", "Quantity", 1, 9999, "How many to pull. KindredLogistics caps at what's available across all reachable chests.")); }, "Pulls a specific item (and quantity) from nearby chests into your inventory."); CollapsibleSection.Build(parent5, "Find item (.fi)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Find item", ".fi {item}", new TextField("item", "Item name", "", "Item to search for. Exact match against the item's prefab name.")); }, "Locates the specified item in nearby chests and prints which chest holds it."); CollapsibleSection.Build(parent5, "Find chest by name (.fc)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Find chest", ".fc {name}", new TextField("name", "Chest name", "", "The custom name written on the chest's sign (e.g. 'salvage', 'spawner', 'brazier').")); }, "Locates chests with the specified custom name."); } private void BuildKindredLogisticsAdminTab(GameObject page) { GameObject val = AddCard(page, "KLAdminNoteCard"); RenderAdminInfoNote(val, "Kindred Logistics admin"); AddBodyText(val, "Server-wide toggles for the KindredLogistics features. These affect every player on the server. Requires admin permission server-side."); AddSpacer(page, 6); GameObject parent = AddCard(page, "KLAdminGlobalsCard"); AddSectionHeading(parent, "Admin Globals (.lg)"); GameObject parent2 = AddKLRow(parent, "KLAdmin1"); AddCommandButton(parent2, "Sort Stash", ".lg ss", "Server-wide: enable auto-stash on sort double-click (.lg ss)."); AddCommandButton(parent2, "Pull", ".lg p", "Server-wide: enable the .pull command for all players (.lg p)."); AddCommandButton(parent2, "Craft Pull", ".lg cr", "Server-wide: enable right-click-recipe ingredient pulling (.lg cr)."); AddCommandButton(parent2, "Servant Stash", ".lg asm", "Server-wide: enable auto-stash for servant mission rewards (.lg asm)."); GameObject parent3 = AddKLRow(parent, "KLAdmin2"); AddCommandButton(parent3, "Conveyor", ".lg co", "Server-wide: enable sender/receiver conveyor chests (.lg co)."); AddCommandButton(parent3, "Salvage", ".lg sal", "Server-wide: enable 'salvage' chests (.lg sal)."); AddCommandButton(parent3, "Unit Spawner", ".lg us", "Server-wide: enable 'spawner' chests filling unit stations (.lg us)."); AddCommandButton(parent3, "Brazier", ".lg bz", "Server-wide: enable 'brazier' chests auto-fueling braziers (.lg bz)."); GameObject parent4 = AddKLRow(parent, "KLAdmin3"); AddCommandButton(parent4, "Named Brazier", ".lg nam", "Server-wide: enable night/proximity-controlled named braziers (.lg nam)."); AddCommandButton(parent4, "Trash", ".lg trash", "Server-wide: allow 'trash' chests to delete their contents (.lg trash)."); AddCommandButton(parent4, "Show Settings", ".lg s", "Print the current server-wide Logistics settings into chat (.lg s)."); AddCommandButton(parent4, "Empty Trash", ".emptytrash", "Empty all trash containers in your current territory (.emptytrash)."); AddSpacer(page, 6); GameObject parent5 = AddCard(page, "KLAdminSpawnCard"); AddSectionHeading(parent5, "Admin Item Spawn"); CollapsibleSection.Build(parent5, "Spawn item to territory stash (.adminstash)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Admin stash spawn", ".adminstash {item} {quantity}", new TextField("item", "Item name", "", "Item prefab name to spawn (e.g. 'Iron Ingot', 'Blood Essence')."), new IntField("quantity", "Quantity", 1, 9999, "How many to spawn.")); }, "Spawns a quantity of an item directly into the current territory's stash containers."); } private static GameObject AddKLRow(GameObject parent, string id) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, id, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); return obj; } private void BuildKindredCommandsPlayerTab(GameObject page) { AddBodyText(AddCard(page, "KCPlayerIntroCard"), "Requires the KindredCommands server mod. Player-facing commands only — admin commands land in their own tab."); AddSpacer(page, 6); GameObject parent = AddCard(page, "KCSelfCard"); AddSectionHeading(parent, "Self"); GameObject parent2 = AddKLRow(parent, "KCSelf"); AddCommandButton(parent2, "AFK", ".afk", "Toggle AFK animation - locks WASD movement until you run .afk again (.afk)."); AddCommandButton(parent2, "Ping", ".ping", "Show your latency in chat (.ping)."); AddCommandButton(parent2, "Pace", ".pace", "Pace at the closest NPC near you - a cosmetic walk loop (.pace)."); AddSpacer(page, 6); GameObject parent3 = AddCard(page, "KCInfoCard"); AddSectionHeading(parent3, "Server info"); GameObject parent4 = AddKLRow(parent3, "KCInfo1"); AddCommandButton(parent4, "Server Time", ".time", "Print the current server time into chat (.time)."); AddCommandButton(parent4, "Online Staff", ".staff", "List staff members currently online (.staff)."); AddCommandButton(parent4, "Open Plots", ".openplots", "Report territories with open or decaying castle plots (.openplots — alias .op). Reply appears in chat."); AddCommandButton(parent4, "Soulshards", ".gear soulshardstatus", "Print the status of soulshards on the server (.gear soulshardstatus)."); GameObject parent5 = AddKLRow(parent3, "KCInfo2"); AddCommandButton(parent5, "Boss List", ".boss list", "List all locked bosses on the server (.boss list)."); AddCommandButton(parent5, "Region List", ".region list", "List all locked and gated regions on the server (.region list)."); BuildClanListPager(parent5); AddSpacer(page, 6); GameObject parent6 = AddCard(page, "KCLookupsCard"); AddSectionHeading(parent6, "Lookups"); CollapsibleSection.Build(parent6, "Check player level (.checklevel)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Check player level", ".checklevel {player}", new PlayerNameField("player", "Player", "Player whose level you want to look up. Exact character-name match.")); }, "Print a player's current level into chat."); CollapsibleSection.Build(parent6, "List clan members (.clan members)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "List clan members", ".clan members {clan}", new TextField("clan", "Clan name", "", "Exact clan name. Use the Clan List pager above to find it.")); }, "List the members of a specific clan."); } private void BuildClanListPager(GameObject parent) { ButtonRef buttonRef = UIFactory.CreateButton(parent, "ClanListPrev", "<"); GameObject gameObject = buttonRef.GameObject; int? minWidth = 32; int? preferredWidth = 36; int? num = 0; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TooltipHover.Attach(buttonRef.GameObject, "Previous page of clans (.clan list )."); buttonRef.OnClick = delegate { if (_clanListPage > 1) { _clanListPage--; } RefreshClanListPage(send: true); }; _clanListPageLabel = UIFactory.CreateLabel(parent, "ClanListPage", $"Clan List p{_clanListPage}", (TextAlignmentOptions)514, null, Theme.ScaledUI(12)).TextMesh; GameObject gameObject2 = ((Component)_clanListPageLabel).gameObject; int? minWidth2 = 90; int? preferredWidth2 = 100; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)_clanListPageLabel).enableWordWrapping = false; ((TMP_Text)_clanListPageLabel).overflowMode = (TextOverflowModes)0; ButtonRef buttonRef2 = UIFactory.CreateButton(parent, "ClanListNext", ">"); GameObject gameObject3 = buttonRef2.GameObject; int? minWidth3 = 32; preferredWidth = 36; num = 0; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: gameObject3, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TooltipHover.Attach(buttonRef2.GameObject, "Next page of clans (.clan list )."); buttonRef2.OnClick = delegate { _clanListPage++; RefreshClanListPage(send: true); }; } private void RefreshClanListPage(bool send) { if ((Object)(object)_clanListPageLabel != (Object)null) { ((TMP_Text)_clanListPageLabel).text = $"Clan List p{_clanListPage}"; } if (send) { MessageService.EnqueueMessage($"{".clan list"} {_clanListPage}"); } } private void BuildVanillaAdminTab(GameObject page) { AddGuideSection(page, "Why this is reference, not action", "V Rising's vanilla admin commands (the ones the game ships with — no mods needed) are CONSOLE commands, typed into the in-game console (default key F1) — NOT chat commands. They use a different input pipeline than mod commands like .fam or .lvl. BloodCraftHub is a CLIENT mod that sends CHAT messages, so it can't trigger console commands directly.\n\nThe good news: KindredCommands re-implements most of the common admin actions (kick / ban / give / spawn / teleport / etc.) as CHAT commands — and BCH wires every one of those into the KINDRED → Admin: Players / Server / World tabs. So the actions you want to take are already covered there. This tab is a reference for the underlying vanilla console commands in case you need to type them yourself (open the console with F1)."); AddCommandTable(page, "Authentication", ("adminauth", "Grant yourself admin powers (needed before any other vanilla admin command)."), ("adminderegister", "Drop admin powers for the current session.")); AddCommandTable(page, "Player management", ("Kick ", "Kick a player by name."), ("BanUser ", "Ban a player by SteamID."), ("Banhammer ", "Ban + delete the player's characters."), ("Unban ", "Unban (use BanList to find the index)."), ("BanList", "List current bans."), ("Mute ", "Silence a player."), ("PlayerInfo ", "Print info about a specific player."), ("UserList", "List all users registered on the server."), ("WhoIsOnline", "List currently-connected players."), ("Connectinfo", "Print connection info for diagnostics."), ("ForceConnectInfo", "Force-refresh connection info display.")); AddGuideSection(page, "", "Chat-command equivalents already in BCH (KINDRED → Admin: Players): .kick, .ban (via Kindred or vanilla), .unban, etc."); AddCommandTable(page, "Character actions", ("Suicide", "Kill your own character (no penalty)."), ("KillPlayer ", "Kill a player."), ("RevivePlayer ", "Revive a downed player."), ("HealPlayer ", "Fully restore a player's HP."), ("DamagePlayer ", "Apply damage to a player."), ("ResetCharacter ", "Fully reset a player's character (DESTRUCTIVE)."), ("KillUnit", "Kill the unit your reticle is targeting."), ("HealUnit", "Heal the targeted unit to full."), ("DamageUnit ", "Apply damage to the targeted unit."), ("Despawn", "Despawn the targeted unit.")); AddCommandTable(page, "Item / character spawning", ("give ", "Give yourself an item by prefab name."), ("giveset", "Open the giveset menu (sets of armor/weapons)."), ("SpawnUnit ", "Spawn an NPC at your position."), ("SpawnCastle ", "Spawn a castle structure."), ("FillStorage", "Fill the targeted storage container."), ("ClearAllInventories", "Wipe every inventory on the server (DESTRUCTIVE)."), ("DespawnAll", "Despawn all units in the world (DESTRUCTIVE).")); AddGuideSection(page, "", "Chat-command equivalents already in BCH (KINDRED → Admin: World): .give {item} {qty}, .spawnnpc / .customspawn / .customspawnat. Use the Lookups section on the same tab to find prefab names."); AddCommandTable(page, "Teleportation", ("teleporttowaypoint ", "Teleport to a named waypoint."), ("TeleportToPlayer ", "Teleport to a specific player."), ("TeleportToBoss ", "Teleport to a boss's spawn location."), ("TeleportToHorse", "Teleport to your horse (if any)."), ("TeleportToOwner", "Teleport to the targeted creature's owner."), ("TeleportToWorld ", "Teleport to absolute world coordinates."), ("UnlockAllPlayerWaypoints", "Unlock every waypoint for a player."), ("MapMarker ", "Add / manage map markers.")); AddGuideSection(page, "", "Chat-command equivalents: .teleport {x} {y} {z} {player}, .tpb {boss} (Kindred teleport-to-boss)."); AddCommandTable(page, "Time, world & difficulty", ("Time", "Print current server time."), ("ChangeMapTime ", "Set the in-world time of day."), ("SetTimeOfDay ", "Alias of ChangeMapTime on some versions."), ("weather ", "Change weather (clear / rain / mist / storm)."), ("GameDifficulty ", "Adjust the server's difficulty."), ("Lockdown", "Toggle PvP / siege lockdown."), ("alllockdown", "Server-wide lockdown.")); AddCommandTable(page, "Server administration", ("Save", "Force a server save."), ("AutoSave", "Enable auto-save."), ("StopAutoSave", "Disable auto-save."), ("ReloadServerSettings", "Re-apply ServerHostSettings.json without restart."), ("Restart", "Restart the server."), ("Disconnect", "Disconnect yourself from the server."), ("Quit", "Close the V Rising client."), ("List", "List every available console command (live reference)."), ("Help ", "Detailed help for a specific command."), ("ShowVersion", "Display the V Rising client/server version."), ("ShowAdminCommands", "List admin-only commands (live filtered List).")); AddCommandTable(page, "Debugging / display", ("DebugHud", "Toggle the debug heads-up display."), ("ShowDebugUI", "Toggle extended debug UI."), ("ShowFPS", "Show frame-rate counter."), ("ShowInputBindings", "List current input bindings."), ("BlockUserInput", "Block all input (anti-stuck recovery)."), ("Console.SetCheats", "Enable cheat-level commands (requires extra setup).")); AddGuideSection(page, "Authoritative list", "V Rising occasionally adds/removes commands between patches. The in-game console's `List` command always shows the live set the current build accepts — use it as the authoritative reference if any command listed here is rejected. `Help ` prints usage details for a specific entry."); AddGuideSection(page, "How to use the in-game console", " 1. Press F1 while in-game (or whatever your bound console key is)\n 2. Type the command exactly as written (case can matter for some)\n 3. Press Enter\n\nIf F1 doesn't open the console, check your keybindings in the V Rising settings or run with `-console` on the command line.\n\nIf a command says 'access denied' you haven't run adminauth yet, OR your SteamID isn't in the server's adminlist (server-side config)."); } private void BuildAboutTab(GameObject page) { AddGuideSection(page, "BloodCraftHub v0.14.0", "A unified CLIENT UI for the Bloodcraft suite of V Rising server mods. Surfaces every Bloodcraft, KindredCommands, and KindredLogistics chat command as buttons and forms — no more typing in chat to manage your familiars, run a class change, or fire an admin command. Live progress overlays for XP, weapon expertise, blood legacy, familiars, professions, and daily quests stream in at ~1 Hz over the signed [ECLIPSE] protocol Bloodcraft already speaks, so the cost on the server is the same as if you had Eclipse installed."); AddSpacer(page, 12); AddSectionHeading(page, "Mods this UI is built on"); AddGuideSection(page, "", "BloodCraftHub is purely a client-side overlay — it doesn't modify the server or add new gameplay systems. Every feature you see is wrapping the chat-command surface of these server-side mods by other developers:"); AddSpacer(page, 4); AddGuideSection(page, "Bloodcraft — by zfolmt", "Leveling, weapon expertise, blood legacies, professions, familiars, classes, quests, and prestige. The bulk of what BloodCraftHub surfaces (every tab in the BLOODCRAFT group) would not exist without zfolmt's mod."); AddLinkRow(page, "Bloodcraft on Thunderstore", "https://thunderstore.io/c/v-rising/p/zfolmt/Bloodcraft/"); AddSpacer(page, 6); AddGuideSection(page, "KindredCommands — by odjit", "Commands to expand server administration and add quality-of-life affordances for players. The KINDRED admin tabs (Players, Server, World) and the entire Logistics section call into odjit's mods."); AddLinkRow(page, "KindredCommands on Thunderstore", "https://thunderstore.io/c/v-rising/p/odjit/KindredCommands/"); AddSpacer(page, 12); AddSectionHeading(page, "About the author"); AddGuideSection(page, "", "Maintained by kdpen (in-game: Chaos). I play on The Shadow Realm — a Brutal, PvE community server — and built this mod to give that community a click-driven alternative to typing every Bloodcraft command. Feedback, bug reports, and pull requests are very welcome through any of the links below."); AddSpacer(page, 4); AddLinkRow(page, "Server Discord (The Shadow Realm)", "https://discord.gg/usC9QgBrXK"); AddLinkRow(page, "DM me on Discord (PerpetualChaos)", "https://discord.com/users/PerpetualChaos"); AddLinkRow(page, "Support development (PayPal)", "https://www.paypal.com/paypalme/KrisPenland"); AddLinkRow(page, "SkillEra.IO (other projects)", "https://SkillEra.IO"); AddSpacer(page, 12); AddSectionHeading(page, "Project"); AddGuideSection(page, "", "BloodCraftHub is open source under the MIT license. The repository, the release feed, and every prior version's CHANGELOG entry are public:"); AddSpacer(page, 4); AddLinkRow(page, "GitHub repository", "https://github.com/KDavidP1987/BloodCraftHub"); AddLinkRow(page, "Thunderstore listing", "https://thunderstore.io/c/v-rising/p/kdpen/BloodCraftHub/"); AddSpacer(page, 6); AddGuideSection(page, "", "Bloodcraft compatibility: v1.13.x • License: MIT • Plugin GUID: kdpen.BloodCraftHub"); } private void BuildSettingsTab(GameObject page) { BuildDisplaySettingsSection(page); } private void BuildDisplaySettingsSection(GameObject page) { AddGuideSection(page, "Display settings", "Adjust text size and overlay transparency. Text-size changes apply when the panel is closed and reopened (or when an overlay is toggled off and back on). Transparency changes apply immediately. 0% transparency = solid background; 100% transparency = invisible background (capped internally at 95% so the drag handle stays visible)."); AddTextScaleRow(page, "UI text size", () => Settings.UITextScale, delegate(float v) { Settings.SetUITextScale(v); Theme.UIFontMultiplier = v; Plugin.UIManager.RequestRebuildMainPanel(); }); AddTextScaleRow(page, "Overlay text size", () => Settings.OverlayTextScale, delegate(float v) { Settings.SetOverlayTextScale(v); Theme.OverlayFontMultiplier = v; Plugin.UIManager.RequestRebuildAllOverlays(); }); AddSpacer(page, 4); AddSectionHeading(page, "Overlay transparency"); AddTransparencyRow(page, "XP overlay", () => Settings.XPOverlayTransparency, delegate(float v) { Settings.SetXPOverlayTransparency(v); }); AddTransparencyRow(page, "Familiar overlay", () => Settings.FamiliarOverlayTransparency, delegate(float v) { Settings.SetFamiliarOverlayTransparency(v); }); AddTransparencyRow(page, "Familiar Browser", () => Settings.FamiliarBrowserTransparency, delegate(float v) { Settings.SetFamiliarBrowserTransparency(v); }); AddTransparencyRow(page, "Daily quest", () => Settings.DailyQuestTransparency, delegate(float v) { Settings.SetDailyQuestTransparency(v); }); AddTransparencyRow(page, "Professions", () => Settings.ProfessionOverlayTransparency, delegate(float v) { Settings.SetProfessionOverlayTransparency(v); }); AddTransparencyRow(page, "Combined overlay", () => Settings.CombinedOverlayTransparency, delegate(float v) { Settings.SetCombinedOverlayTransparency(v); }); AddSpacer(page, 8); BuildCombinedOverlaySection(page); AddSpacer(page, 8); BuildPanelBackgroundColorSection(page); AddSpacer(page, 8); AddSectionHeading(page, "HUD extras"); AddShowProgressBarsToggle(page); AddShowOverlayBonusStatsToggle(page); AddShowOverlayXpCounterToggle(page); AddProgressBarHeightControls(page); AddOverlayEdgePaddingControls(page); AddShowPrestigeSubLineToggle(page); AddOverlayAlignmentToggle(page); AddAutoScanVBloodsToggle(page); AddSpacer(page, 6); BuildProfessionTrackedSection(page); AddSpacer(page, 8); AddSectionHeading(page, "Chat noise"); AddSuppressActionChatterToggle(page); AddSpacer(page, 8); BuildSizePositioningSection(page); AddSpacer(page, 8); BuildChatLoggingSection(page); } private void BuildChatLoggingSection(GameObject page) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) AddSectionHeading(page, "Chat Logging"); LabelRef labelRef = UIFactory.CreateLabel(page, "ChatLoggingHelp", "Diagnostic toggles. Each controls whether chat shows the SERVER REPLIES to commands BCH already mirrors to its UI. Action confirmations and commands without a BCH UI display stay visible regardless. Suppression is purely cosmetic — data collection (familiars, V-Bloods, expertise, etc.) always works.", (TextAlignmentOptions)257, null, Theme.ScaledUI(13)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 48, flexibleWidth: num, preferredHeight: 68, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; GameObject obj = UIFactory.CreateHorizontalGroup(page, "ChatLogMasterRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(obj, "ChatShowAllBtn", "Show all mod chat"); GameObject gameObject2 = buttonRef.GameObject; int? minWidth3 = 140; preferredWidth = 180; num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(buttonRef.GameObject, "Enable visibility for all three Chat Logging categories — useful when diagnosing why a BCH feature isn't picking up server data. Does NOT touch the global ClearServerMessages admin setting."); buttonRef.OnClick = delegate { Settings.ShowAllChat(); RefreshChatLoggingTogglesUI(); }; ButtonRef buttonRef2 = UIFactory.CreateButton(obj, "ChatHideAllBtn", "Hide all mod chat"); GameObject gameObject3 = buttonRef2.GameObject; int? minWidth4 = 140; preferredWidth2 = 180; num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); TextMeshProUGUI componentInChildren2 = ((Component)buttonRef2.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(buttonRef2.GameObject, "Disable visibility for all three Chat Logging categories — maximum chat quiet. Action confirmations and any command BCH doesn't parse will still appear in chat (those have no alternative UI display)."); buttonRef2.OnClick = delegate { Settings.HideAllChat(); RefreshChatLoggingTogglesUI(); }; AddSpacer(page, 4); _chatBchAutoToggle = AddChatLoggingToggle(page, "BCH internal auto-fires", "Replies to BCH's own automatic background commands — V-Blood scanner searches, the XP overlay bonus-stats ticker, the Wep/Blood-Legacy tab auto-refresh. Off by default to avoid spam from background polling. Turn ON if you want to see what BCH is sending and verify the server is replying.", () => Settings.ShowChatBchAuto, delegate(bool v) { Settings.SetShowChatBchAuto(v); }); _chatBloodcraftToggle = AddChatLoggingToggle(page, "Bloodcraft command replies", "Replies to user-initiated Bloodcraft commands BCH structurally parses — .fam boxes / .fam l / .fam s / .bl get / .wep get / .prestige get. On by default. Off = the BCH UI is the only place this data shows (less chat noise). Action confirmations (.fam b, .fam ub, etc.) and commands BCH doesn't parse stay visible regardless.", () => Settings.ShowChatBloodcraft, delegate(bool v) { Settings.SetShowChatBloodcraft(v); }); _chatKindredToggle = AddChatLoggingToggle(page, "Kindred command replies", "Same as the Bloodcraft toggle, applied to KindredCommands / KindredLogistics commands. BCH doesn't structurally parse any Kindred replies in this version, so the toggle is currently a no-op — reserved for future Kindred structured parsing.", () => Settings.ShowChatKindred, delegate(bool v) { Settings.SetShowChatKindred(v); }); } private ToggleRef AddChatLoggingToggle(GameObject parent, string label, string tooltip, Func get, Action set) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ChatLogRow_" + label, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "ChatLogTog_" + label); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = get(); TooltipHover.Attach(toggleRef.GameObject, tooltip); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { set(v); }); return toggleRef; } private void RefreshChatLoggingTogglesUI() { if (_chatBchAutoToggle != null) { _chatBchAutoToggle.Toggle.isOn = Settings.ShowChatBchAuto; } if (_chatBloodcraftToggle != null) { _chatBloodcraftToggle.Toggle.isOn = Settings.ShowChatBloodcraft; } if (_chatKindredToggle != null) { _chatKindredToggle.Toggle.isOn = Settings.ShowChatKindred; } } private void BuildSizePositioningSection(GameObject page) { AddSectionHeading(page, "Size & Positioning"); if (_sizePosReadoutTicker == null) { _sizePosReadoutTicker = TickSizePosReadouts; CoreUpdateBehavior.Actions.Add(_sizePosReadoutTicker); } LabelRef labelRef = UIFactory.CreateLabel(page, "SizePosHelp", "Click +/- to adjust the width/height of the main panel or any overlay (hold Shift while clicking for 100 px steps). Default returns it to its factory size and position. Drag-to-resize from any edge still works.", (TextAlignmentOptions)257, null, Theme.ScaledUI(13)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 36, flexibleWidth: num, preferredHeight: 52, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; AddSpacer(page, 4); BuildPrimaryUISizeControls(page); AddSpacer(page, 4); BuildOverlaySizeControls(page, "XP overlay", () => Plugin.UIManager?.ExperienceOverlay); BuildOverlaySizeControls(page, "Familiar overlay", () => Plugin.UIManager?.FamiliarOverlay); BuildOverlaySizeControls(page, "Familiar Browser", () => Plugin.UIManager?.FamiliarBrowserOverlay); BuildOverlaySizeControls(page, "Daily Quest", () => Plugin.UIManager?.DailyQuestOverlay); BuildOverlaySizeControls(page, "Professions", () => Plugin.UIManager?.ProfessionOverlay); BuildOverlaySizeControls(page, "Combined overlay", () => Plugin.UIManager?.CombinedOverlay); } private void BuildPrimaryUISizeControls(GameObject page) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) AddSizePosSubHeading(page, "Primary UI"); GameObject val = UIFactory.CreateHorizontalGroup(page, "PrimaryUISizeBtns", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddSizePosButton(val, "Auto-size", "Auto-resize the main panel vertically to fit the active tab's content. Mirrors the footer toggle.", delegate { Settings.SetIsPanelAutoResizeEnabled(!Settings.IsPanelAutoResizeEnabled); AutoResizeIfEnabled(); }); AddSizePosButton(val, "Fullscreen", "Toggle the main panel between its current size+position and a fullscreen stretch (with a small inset so the edges stay grabbable). Mirrors the maximize button on the title bar.", ToggleFullscreen); AddSizePosButton(val, "Default", "Reset the main panel to its default size (does NOT move it — drag to re-center if you want).", delegate { SetFullscreen(fullscreen: false); SetDefaultSize(); }); AddSizePosStepRow(page, "Width", () => ((Object)(object)base.Rect != (Object)null) ? ((int)base.Rect.sizeDelta.x) : 0, delegate(int d) { SetFullscreen(fullscreen: false); AdjustSize(d, 0); }); AddSizePosStepRow(page, "Height", () => ((Object)(object)base.Rect != (Object)null) ? ((int)base.Rect.sizeDelta.y) : 0, delegate(int d) { SetFullscreen(fullscreen: false); AdjustSize(0, d); }); } private void BuildOverlaySizeControls(GameObject page, string label, Func getter) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) AddSizePosSubHeading(page, label); GameObject val = UIFactory.CreateHorizontalGroup(page, label + "_SizeBtns", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddSizePosButton(val, "Default", "Reset the " + label + "'s size to its default. Does NOT move it — drag the overlay if you also want to reset position. Only affects this overlay if it's currently open.", delegate { getter()?.SetDefaultSize(); }); AddSizePosStepRow(page, "Width", delegate { //IL_0028: Unknown result type (might be due to invalid IL or missing references) ResizeablePanelBase resizeablePanelBase2 = getter(); return ((Object)(object)resizeablePanelBase2?.Rect != (Object)null) ? ((int)resizeablePanelBase2.Rect.sizeDelta.x) : 0; }, delegate(int d) { getter()?.AdjustSize(d, 0); }); AddSizePosStepRow(page, "Height", delegate { //IL_0028: Unknown result type (might be due to invalid IL or missing references) ResizeablePanelBase resizeablePanelBase = getter(); return ((Object)(object)resizeablePanelBase?.Rect != (Object)null) ? ((int)resizeablePanelBase.Rect.sizeDelta.y) : 0; }, delegate(int d) { getter()?.AdjustSize(0, d); }); AddSpacer(page, 2); } private void AddSizePosSubHeading(GameObject parent, string text) { LabelRef labelRef = UIFactory.CreateLabel(parent, "SizePosSub_" + text, text, (TextAlignmentOptions)4097, null, Theme.ScaledUI(13)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; } private void AddSizePosButton(GameObject parent, string label, string tooltip, Action onClick) { ButtonRef buttonRef = UIFactory.CreateButton(parent, "SizePosBtn_" + label, label); GameObject gameObject = buttonRef.GameObject; int? minWidth = 90; int? preferredWidth = 110; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(buttonRef.GameObject, tooltip); buttonRef.OnClick = onClick; } private void AddSizePosStepRow(GameObject parent, string label, Func getCurrent, Action applyDelta) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "SizePosRow_" + label, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "SizePosRowLabel_" + label, label + ":", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)).GameObject; int? minWidth2 = 60; int? preferredWidth2 = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(val, "SizePosMinus_" + label, "−"); GameObject gameObject2 = buttonRef.GameObject; int? minWidth3 = 32; preferredWidth = 32; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(14); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } LabelRef valLabel = UIFactory.CreateLabel(val, "SizePosVal_" + label, $"{getCurrent()} px", (TextAlignmentOptions)514, null, Theme.ScaledUI(12)); GameObject gameObject3 = valLabel.GameObject; int? minWidth4 = 60; preferredWidth2 = 80; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef2 = UIFactory.CreateButton(val, "SizePosPlus_" + label, "+"); GameObject gameObject4 = buttonRef2.GameObject; int? minWidth5 = 32; preferredWidth = 32; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren2 = ((Component)buttonRef2.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).fontSize = Theme.ScaledUI(14); ((TMP_Text)componentInChildren2).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(buttonRef.GameObject, $"Shrink {label.ToLowerInvariant()} by {20} px (Shift+click: {100} px)."); TooltipHover.Attach(buttonRef2.GameObject, $"Grow {label.ToLowerInvariant()} by {20} px (Shift+click: {100} px)."); Action refresh = delegate { if (!((Object)(object)valLabel?.TextMesh == (Object)null)) { string text = $"{getCurrent()} px"; if (((TMP_Text)valLabel.TextMesh).text != text) { ((TMP_Text)valLabel.TextMesh).text = text; } } }; buttonRef.OnClick = delegate { applyDelta(-CurrentStep()); refresh(); }; buttonRef2.OnClick = delegate { applyDelta(CurrentStep()); refresh(); }; _sizePosRefreshers.Add(refresh); } private void TickSizePosReadouts() { if (ActiveTab != PanelType.SettingsTab || !base.Enabled) { return; } for (int i = 0; i < _sizePosRefreshers.Count; i++) { try { _sizePosRefreshers[i]?.Invoke(); } catch (Exception ex) { LogUtils.LogWarning($"Size-pos readout refresher #{i} threw: {ex.Message}"); } } } private static int CurrentStep() { if (!Input.GetKey((KeyCode)304) && !Input.GetKey((KeyCode)303)) { return 20; } return 100; } private void AddShowProgressBarsToggle(GameObject parent) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) AddPanelColorSubHeading(parent, "ShowBarsHeader", "Show progress bars for"); GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ShowProgressBarsRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddProgressBarSystemToggle(obj, "XP", () => Settings.ShowProgressBarXP, Settings.SetShowProgressBarXP); AddProgressBarSystemToggle(obj, "Familiar", () => Settings.ShowProgressBarFamiliar, Settings.SetShowProgressBarFamiliar); AddProgressBarSystemToggle(obj, "Weapon", () => Settings.ShowProgressBarExpertise, Settings.SetShowProgressBarExpertise); AddProgressBarSystemToggle(obj, "Blood", () => Settings.ShowProgressBarLegacy, Settings.SetShowProgressBarLegacy); AddProgressBarSystemToggle(obj, "Professions", () => Settings.ShowProgressBarProfessions, Settings.SetShowProgressBarProfessions); } private static void AddProgressBarSystemToggle(GameObject row, string label, Func get, Action set) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ToggleRef toggleRef = UIFactory.CreateToggle(row, "BarSys_" + label); GameObject gameObject = toggleRef.GameObject; int? minWidth = 70; int? preferredWidth = 80; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(11); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 50; int? preferredWidth2 = 65; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = get(); TooltipHover.Attach(toggleRef.GameObject, "Show the progress bar for " + label + " in BOTH the standalone overlay and the combined overlay. Applies wherever the system renders."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { set(v); Plugin.UIManager?.RefreshCombinedOverlaySections(); }); } private void AddShowOverlayBonusStatsToggle(GameObject parent) { //IL_001f: 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_0096: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ShowOverlayBonusStatsRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "ShowOverlayBonusStatsToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Show weapon expertise & blood legacy bonus stats on the XP overlay"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = Settings.ShowOverlayBonusStats; TooltipHover.Attach(toggleRef.GameObject, "When on, the XP overlay shows the chosen bonus-stat names AND their current numeric values under the Weapon and Legacy rows (e.g. '+12.5% PhysicalPower'). Auto-fetches .wep get and .bl get every 10s while the overlay is visible. Off by default for a minimal HUD."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { Settings.SetShowOverlayBonusStats(value); Plugin.UIManager?.RefreshCombinedOverlaySections(); }); } private void AddShowOverlayXpCounterToggle(GameObject parent) { //IL_001f: 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_0096: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ShowOverlayXpCounterRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "ShowOverlayXpCounterToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Show numerical Exp / Ess counter on the XP overlay"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = Settings.ShowOverlayXpCounter; TooltipHover.Attach(toggleRef.GameObject, "Adds a sub-row under Weapon and Legacy showing 'Exp: 123 / 4500 (2.7%)' — current expertise / essence and the threshold to the next level. Derives the threshold from the percentage the server prints, so it's accurate to within ±1 of the true value."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { Settings.SetShowOverlayXpCounter(value); Plugin.UIManager?.RefreshCombinedOverlaySections(); }); } private void AddProgressBarHeightControls(GameObject parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "ProgressBarHeightRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "ProgressBarHeightLabel", "Progress bar height:", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)).GameObject; int? minWidth2 = 160; int? preferredWidth2 = 180; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(val, "ProgressBarHeightMinus", "−"); GameObject gameObject2 = ((Component)buttonRef.Component).gameObject; int? minWidth3 = 30; preferredWidth = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef valueLbl = UIFactory.CreateLabel(val, "ProgressBarHeightValue", $"{Settings.ProgressBarHeight} px", (TextAlignmentOptions)514, null, Theme.ScaledUI(12)); GameObject gameObject3 = valueLbl.GameObject; int? minWidth4 = 60; preferredWidth2 = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef2 = UIFactory.CreateButton(val, "ProgressBarHeightPlus", "+"); GameObject gameObject4 = ((Component)buttonRef2.Component).gameObject; int? minWidth5 = 30; preferredWidth = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); buttonRef.OnClick = delegate { Settings.SetProgressBarHeight(Settings.ProgressBarHeight - 1); ((TMP_Text)valueLbl.TextMesh).text = $"{Settings.ProgressBarHeight} px"; }; buttonRef2.OnClick = delegate { Settings.SetProgressBarHeight(Settings.ProgressBarHeight + 1); ((TMP_Text)valueLbl.TextMesh).text = $"{Settings.ProgressBarHeight} px"; }; TooltipHover.Attach(val, $"Absolute pixel height for the XP/Weapon/Legacy progress bars when 'Scale bar with overlay' is off. Clamped {4}..{24}. Default 8."); GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ProgressBarRelativeRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth6 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "ProgressBarRelativeToggle"); GameObject gameObject5 = toggleRef.GameObject; int? minWidth7 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject5, minWidth: minWidth7, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = "Scale bar height with overlay (pre-0.10.7 behavior)"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject6 = ((Component)toggleRef.Text).gameObject; int? minWidth8 = 320; preferredWidth2 = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject6, minWidth: minWidth8, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = Settings.ProgressBarHeightRelative; TooltipHover.Attach(toggleRef.GameObject, "When on, the bars stretch vertically as you grow the overlay. When off (default), the bars stay at the fixed pixel height above regardless of overlay size."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { Settings.SetProgressBarHeightRelative(v); }); } private void AddOverlayEdgePaddingControls(GameObject parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "OverlayEdgePadRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "OverlayEdgePadLabel", "Overlay edge padding:", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)).GameObject; int? minWidth2 = 160; int? preferredWidth2 = 180; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef = UIFactory.CreateButton(val, "OverlayEdgePadMinus", "−"); GameObject gameObject2 = ((Component)buttonRef.Component).gameObject; int? minWidth3 = 30; preferredWidth = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef valueLbl = UIFactory.CreateLabel(val, "OverlayEdgePadValue", $"{Settings.OverlayEdgePadding} px", (TextAlignmentOptions)514, null, Theme.ScaledUI(12)); GameObject gameObject3 = valueLbl.GameObject; int? minWidth4 = 60; preferredWidth2 = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef buttonRef2 = UIFactory.CreateButton(val, "OverlayEdgePadPlus", "+"); GameObject gameObject4 = ((Component)buttonRef2.Component).gameObject; int? minWidth5 = 30; preferredWidth = 30; num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject4, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); buttonRef.OnClick = delegate { Settings.SetOverlayEdgePadding(Settings.OverlayEdgePadding - 1); ((TMP_Text)valueLbl.TextMesh).text = $"{Settings.OverlayEdgePadding} px"; Plugin.UIManager.RequestRebuildAllOverlays(); }; buttonRef2.OnClick = delegate { Settings.SetOverlayEdgePadding(Settings.OverlayEdgePadding + 1); ((TMP_Text)valueLbl.TextMesh).text = $"{Settings.OverlayEdgePadding} px"; Plugin.UIManager.RequestRebuildAllOverlays(); }; TooltipHover.Attach(val, $"Inner left/right padding applied to every overlay (XP, Familiar, Familiar Browser, Daily Quest, Professions). Higher = more breathing room between text and panel edge / scrollbar. Clamped {0}..{32}. Default 6."); } private void AddAutoScanVBloodsToggle(GameObject parent) { //IL_001f: 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_0096: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "AutoScanVBloodsRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "AutoScanVBloodsToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Auto-scan V-Bloods when the V-Bloods tab opens"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = Settings.AutoScanVBloodsOnTabOpen; TooltipHover.Attach(toggleRef.GameObject, "When on, opening the V-Bloods tab with an empty collection automatically triggers a box-sweep scan (.fam boxes + .fam cb + .fam l for every box). Off by default — the scanner switches your active box ~10-15 times, so most users prefer the explicit Scan all button."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { Settings.SetAutoScanVBloodsOnTabOpen(v); }); } private void BuildCombinedOverlaySection(GameObject page) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) AddSectionHeading(page, "Combined overlay"); LabelRef labelRef = UIFactory.CreateLabel(page, "CombinedOverlayHelp", "Single draggable overlay containing XP / Familiar / Weapon / Blood / Professions / Quests sections — replaces the four standalone info overlays when on. Per-section checkboxes below pick which slices show inside the combined panel; the per-profession checkboxes from 'Professions tracked' still filter the profession rows.", (TextAlignmentOptions)257, null, Theme.ScaledUI(14)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 56, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; GameObject obj = UIFactory.CreateHorizontalGroup(page, "CombinedMasterRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "CombinedMasterToggle"); GameObject gameObject2 = toggleRef.GameObject; int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = "Use combined overlay (hides individual info overlays)"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(13); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject3 = ((Component)toggleRef.Text).gameObject; int? minWidth4 = 320; preferredWidth2 = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject3, minWidth: minWidth4, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = Settings.ShowCombinedOverlay; _combinedMasterToggle = toggleRef.Toggle; TooltipHover.Attach(toggleRef.GameObject, "When on, BCH replaces the standalone XP / Familiar / Daily Quest / Professions overlays with a single combined panel. Familiar Browser and Shift Spell overlays stay independent."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { Settings.SetShowCombinedOverlay(v); Plugin.UIManager?.ApplyCombinedOverlayMutualExclusion(); ApplyCombinedFooterVisibility(); Plugin.UIManager?.RefreshCombinedOverlaySections(); RefreshAllOverlayToggleStates(); }); AddSpacer(page, 4); GameObject val = UIFactory.CreateHorizontalGroup(page, "CombinedSectionsRow1", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth5 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth5, flexibleHeight: 0, preferredWidth: preferredWidth); AddCombinedSectionToggleUnified(val, "XP", () => Settings.ShowExperienceOverlay, Settings.SetShowExperienceOverlay); AddCombinedSectionToggleUnified(val, "Familiar", () => Settings.ShowFamiliarOverlay, Settings.SetShowFamiliarOverlay); AddCombinedSectionToggle(val, "Weapon", () => Settings.CombinedOverlayShowExpertise, Settings.SetCombinedOverlayShowExpertise); GameObject val2 = UIFactory.CreateHorizontalGroup(page, "CombinedSectionsRow2", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth6 = 360; preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val2, minWidth: minWidth6, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCombinedSectionToggle(val2, "Blood", () => Settings.CombinedOverlayShowLegacy, Settings.SetCombinedOverlayShowLegacy); AddCombinedSectionToggleUnified(val2, "Professions", () => Settings.ShowProfessionOverlay, Settings.SetShowProfessionOverlay); AddCombinedSectionToggleUnified(val2, "Quests", () => Settings.ShowDailyQuestOverlay, Settings.SetShowDailyQuestOverlay); } private void AddCombinedSectionToggleUnified(GameObject row, string label, Func get, Action set) { //IL_0023: 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) ToggleRef toggleRef = UIFactory.CreateToggle(row, "CombinedSec_" + label); GameObject gameObject = toggleRef.GameObject; int? minWidth = 110; int? preferredWidth = 130; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 80; int? preferredWidth2 = 100; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = get(); TooltipHover.Attach(toggleRef.GameObject, $"Show the {label} system — affects BOTH the standalone {label} overlay AND the corresponding section in the combined overlay. One flag controls both views."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { set(v); Plugin.UIManager?.RefreshCombinedOverlaySections(); Plugin.UIManager?.ApplyCombinedOverlayMutualExclusion(); RefreshAllOverlayToggleStates(); }); } public void RefreshAllOverlayToggleStates() { if ((Object)(object)_xpOverlayToggle != (Object)null) { _xpOverlayToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.ExperienceOverlay) ?? false); } if ((Object)(object)_famOverlayToggle != (Object)null) { _famOverlayToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.FamiliarOverlay) ?? false); } if ((Object)(object)_famBrowserToggle != (Object)null) { _famBrowserToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.FamiliarBrowserOverlay) ?? false); } if ((Object)(object)_dqOverlayToggle != (Object)null) { _dqOverlayToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.DailyQuestOverlay) ?? false); } if ((Object)(object)_profOverlayToggle != (Object)null) { _profOverlayToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.ProfessionOverlay) ?? false); } if ((Object)(object)_shiftOverlayToggle != (Object)null) { _shiftOverlayToggle.SetIsOnWithoutNotify(Plugin.UIManager?.IsOverlayOpen(PanelType.ShiftSpellOverlay) ?? false); } if ((Object)(object)_combinedOverlayToggle != (Object)null) { _combinedOverlayToggle.SetIsOnWithoutNotify(Settings.ShowCombinedOverlay); } if ((Object)(object)_combinedMasterToggle != (Object)null) { _combinedMasterToggle.SetIsOnWithoutNotify(Settings.ShowCombinedOverlay); } } private static void AddCombinedSectionToggle(GameObject row, string label, Func get, Action set) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ToggleRef toggleRef = UIFactory.CreateToggle(row, "CombinedSec_" + label); GameObject gameObject = toggleRef.GameObject; int? minWidth = 110; int? preferredWidth = 130; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 80; int? preferredWidth2 = 100; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = get(); TooltipHover.Attach(toggleRef.GameObject, "Show the " + label + " section inside the combined overlay. No effect when combined-mode is off."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { set(v); Plugin.UIManager?.RefreshCombinedOverlaySections(); }); } private void BuildProfessionTrackedSection(GameObject page) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) AddSectionHeading(page, "Professions tracked"); LabelRef labelRef = UIFactory.CreateLabel(page, "ProfTrackedHelp", "Choose which of the eight Bloodcraft professions appear on the Professions overlay. Defaults show all eight (preserves pre-0.13.0 behavior). Each row hides immediately when its checkbox is cleared.", (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 32, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; GameObject obj = UIFactory.CreateHorizontalGroup(page, "ProfTogglesRow1", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddProfessionTrackedToggle(obj, "Enchanting", () => Settings.ShowProfessionEnchanting, Settings.SetShowProfessionEnchanting); AddProfessionTrackedToggle(obj, "Alchemy", () => Settings.ShowProfessionAlchemy, Settings.SetShowProfessionAlchemy); AddProfessionTrackedToggle(obj, "Harvesting", () => Settings.ShowProfessionHarvesting, Settings.SetShowProfessionHarvesting); AddProfessionTrackedToggle(obj, "Blacksmithing", () => Settings.ShowProfessionBlacksmithing, Settings.SetShowProfessionBlacksmithing); GameObject obj2 = UIFactory.CreateHorizontalGroup(page, "ProfTogglesRow2", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(2f, 2f, 2f, 2f)); int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); AddProfessionTrackedToggle(obj2, "Tailoring", () => Settings.ShowProfessionTailoring, Settings.SetShowProfessionTailoring); AddProfessionTrackedToggle(obj2, "Woodcutting", () => Settings.ShowProfessionWoodcutting, Settings.SetShowProfessionWoodcutting); AddProfessionTrackedToggle(obj2, "Mining", () => Settings.ShowProfessionMining, Settings.SetShowProfessionMining); AddProfessionTrackedToggle(obj2, "Fishing", () => Settings.ShowProfessionFishing, Settings.SetShowProfessionFishing); } private static void AddProfessionTrackedToggle(GameObject row, string label, Func get, Action set) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ToggleRef toggleRef = UIFactory.CreateToggle(row, "ProfTog_" + label); GameObject gameObject = toggleRef.GameObject; int? minWidth = 80; int? preferredWidth = 95; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(11); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 60; int? preferredWidth2 = 75; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = get(); TooltipHover.Attach(toggleRef.GameObject, "Show the " + label + " row on the Professions overlay. Default: on."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { set(value); Plugin.UIManager?.RefreshProfessionOverlay(); }); } private void AddShowPrestigeSubLineToggle(GameObject parent) { //IL_001f: 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_0096: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "ShowPrestigeSubLineRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "ShowPrestigeSubLineToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Show prestige-progress sub-line in progress bars (Eclipse-style)"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = Settings.ShowPrestigeSubLine; TooltipHover.Attach(toggleRef.GameObject, "Adds a slim inset fill at the bottom of each progress bar reflecting how close you are to the next prestige tier (Level / MaxLevel for that system). Mirrors Eclipse's overlay style. Requires Show Progress Bars to be on."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool v) { Settings.SetShowPrestigeSubLine(v); }); } private void AddOverlayAlignmentToggle(GameObject parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "OverlayAlignRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "OverlayAlignLabel", "Overlay text alignment:", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)).GameObject; int? minWidth2 = 200; int? preferredWidth2 = 240; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ButtonRef btn = UIFactory.CreateButton(val, "OverlayAlignBtn", FormatOverlayAlignText()); GameObject gameObject2 = btn.GameObject; int? minWidth3 = 80; preferredWidth = 100; num = 0; UIFactory.SetLayoutElement(minHeight: 26, flexibleWidth: num, preferredHeight: 28, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; } TooltipHover.Attach(btn.GameObject, "Cycle between Left and Right text alignment for ALL overlays (XP, Familiar, Familiar Browser, Daily Quest, Professions). Right is handy when you've pinned an overlay to the right edge of the screen and want the values closer to the panel border."); btn.OnClick = delegate { Settings.SetOverlayTextAlignment((Settings.OverlayTextAlignmentSetting == Settings.OverlayAlignment.Left) ? Settings.OverlayAlignment.Right : Settings.OverlayAlignment.Left); TextMeshProUGUI componentInChildren2 = ((Component)btn.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).text = FormatOverlayAlignText(); } Plugin.UIManager?.RequestRebuildAllOverlays(); }; } private static string FormatOverlayAlignText() { if (Settings.OverlayTextAlignmentSetting != Settings.OverlayAlignment.Right) { return "Left"; } return "Right"; } private void AddSuppressActionChatterToggle(GameObject parent) { //IL_001f: 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_0096: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "SuppressChatterRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ToggleRef toggleRef = UIFactory.CreateToggle(obj, "SuppressActionChatterToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)toggleRef.Text).text = "Hide familiar-action chat (bind/unbind/switch box/...)"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(12); ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth3 = 320; preferredWidth = 360; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); toggleRef.Toggle.isOn = Settings.SuppressFamiliarActionChatter; TooltipHover.Attach(toggleRef.GameObject, "When on, Bloodcraft's confirmation chat lines for .fam b / .fam ub / .fam t / .fam cb / .fam mb / .fam sb / .fam r get eaten so they don't clutter your chat box. The UI continues to work normally — box list, contents, and overlays read from separate data feeds, not these confirmation lines. Off by default."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { Settings.SetSuppressFamiliarActionChatter(value); }); } private void AddTextScaleRow(GameObject parent, string label, Func currentScaleSetting, Action applyScale) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "DisplayRow_" + label, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 30, flexibleWidth: num, preferredHeight: 32, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "Lbl_" + label, label + ":", (TextAlignmentOptions)4097, null, 13).GameObject; int? minWidth2 = 140; int? preferredWidth2 = 160; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); LabelRef hint = UIFactory.CreateLabel(val, "Hint_" + label, FormatScaleHint(currentScaleSetting()), (TextAlignmentOptions)4097, null, 11); GameObject gameObject2 = hint.GameObject; int? minWidth3 = 90; preferredWidth = 100; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)hint.TextMesh).fontStyle = (FontStyles)2; AddScaleButton(val, "Small", delegate { Pick(0.85f); }); AddScaleButton(val, "Standard", delegate { Pick(1f); }); AddScaleButton(val, "Large", delegate { Pick(1.2f); }); AddScaleButton(val, "X-Large", delegate { Pick(1.5f); }); void Pick(float v) { applyScale(v); ((TMP_Text)hint.TextMesh).text = FormatScaleHint(v); } } private static string FormatScaleHint(float v) { if (v <= 0.9f) { return "(current: Small)"; } if (v >= 1.35f) { return "(current: X-Large)"; } if (v >= 1.1f) { return "(current: Large)"; } return "(current: Standard)"; } private static void AddScaleButton(GameObject row, string text, Action onClick) { ButtonRef buttonRef = UIFactory.CreateButton(row, "Btn_" + text, text); GameObject gameObject = buttonRef.GameObject; int? minWidth = 64; int? preferredWidth = 72; int? num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = 11f; } buttonRef.OnClick = delegate { onClick(); }; } private void AddTransparencyRow(GameObject parent, string overlayLabel, Func currentValue, Action applyValue) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateHorizontalGroup(parent, "OpacityRow_" + overlayLabel, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); GameObject gameObject = UIFactory.CreateLabel(val, "Lbl_" + overlayLabel, overlayLabel + ":", (TextAlignmentOptions)4097, null, 12).GameObject; int? minWidth2 = 130; int? preferredWidth2 = 150; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); LabelRef hint = UIFactory.CreateLabel(val, "Hint_" + overlayLabel, FormatTransparencyHint(currentValue()), (TextAlignmentOptions)4097, null, 10); GameObject gameObject2 = hint.GameObject; int? minWidth3 = 60; preferredWidth = 70; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)hint.TextMesh).fontStyle = (FontStyles)2; AddOpacityButton(val, "0%", delegate { Pick(0f); }); AddOpacityButton(val, "25%", delegate { Pick(0.25f); }); AddOpacityButton(val, "50%", delegate { Pick(0.5f); }); AddOpacityButton(val, "75%", delegate { Pick(0.75f); }); AddOpacityButton(val, "100%", delegate { Pick(1f); }); void Pick(float v) { applyValue(v); ((TMP_Text)hint.TextMesh).text = FormatTransparencyHint(v); Plugin.UIManager.RefreshAllOpacities(); } } private static string FormatTransparencyHint(float v) { if (v < 0.13f) { return "(0%)"; } if (v < 0.38f) { return "(25%)"; } if (v < 0.63f) { return "(50%)"; } if (v < 0.88f) { return "(75%)"; } return "(100%)"; } private static void AddOpacityButton(GameObject row, string text, Action onClick) { ButtonRef buttonRef = UIFactory.CreateButton(row, "Op_" + text, text); GameObject gameObject = buttonRef.GameObject; int? minWidth = 36; int? preferredWidth = 42; int? num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = 10f; } buttonRef.OnClick = delegate { onClick(); }; } private void BuildPanelBackgroundColorSection(GameObject page) { AddSectionHeading(page, "Panel background color"); AddPanelColorHelp(page, "PanelBgHelp", "Sets the OUTER background color of every panel BCH builds — the main panel, the Familiar Browser, and all five info overlays (XP, Familiar, Daily Quest, Profession, Shift Spell). Light colors may reduce text legibility — labels assume a dark background. Transparency per panel is configured by the sliders above; this picker controls hue only."); AddPanelColorPresetRow(page, "PanelBgPresetRow", ApplyOuterPanelBgHex); _panelBgCurrentLabel = AddPanelColorInfoRow(page, "PanelBgInfoRow", FormatOuterBgCurrentText, "#121212", "Restore the default panel background color (#121212 — near-black, the pre-0.12.0 look).", ApplyOuterPanelBgHex); AddSpacer(page, 6); AddSectionHeading(page, "Interior background color"); AddPanelColorHelp(page, "InnerBgHelp", "Sets the INTERIOR background — the scroll-view area where tab content shows in the main panel and where familiar rows show in the Familiar Browser. Pre-0.12.0 this was bright red by framework default (UIFactory.CreateScrollView used Theme.Level1). Two palettes are offered — the dark row keeps the muted modern look, the bright row restores the saturation similar to that original red (Crimson Bright = #A30000 = the pre-0.12.0 default exactly). Independent of the outer color above so you can build a two-tone theme. Smaller info overlays don't host scroll views so this picker doesn't affect them."); AddPanelColorSubHeading(page, "InnerBgDarkLabel", "Dark variants"); AddPanelColorPresetRow(page, "InnerBgPresetDarkRow", ApplyInnerPanelBgHex, DefaultDarkPresets); AddPanelColorSubHeading(page, "InnerBgBrightLabel", "Bright variants"); AddPanelColorPresetRow(page, "InnerBgPresetBrightRow", ApplyInnerPanelBgHex, DefaultBrightPresets); _innerBgCurrentLabel = AddPanelColorInfoRow(page, "InnerBgInfoRow", FormatInnerBgCurrentText, "#121212", "Restore the default interior background (#121212 near-black — masks the framework-default red.)", ApplyInnerPanelBgHex); } private static void AddPanelColorSubHeading(GameObject page, string name, string text) { LabelRef labelRef = UIFactory.CreateLabel(page, name, text, (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; } private static void AddPanelColorHelp(GameObject page, string name, string body) { LabelRef labelRef = UIFactory.CreateLabel(page, name, body, (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 36, flexibleWidth: num, preferredHeight: 52, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; } private void AddPanelColorPresetRow(GameObject page, string name, Action applyAction, (string Label, string Hex)[] presets = null) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (presets == null) { presets = DefaultDarkPresets; } GameObject val = UIFactory.CreateHorizontalGroup(page, name, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 28, flexibleWidth: num, preferredHeight: 30, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); (string, string)[] array = presets; for (int i = 0; i < array.Length; i++) { (string, string) tuple = array[i]; AddPanelBgPresetButton(val, tuple.Item1, tuple.Item2, applyAction); } } private TextMeshProUGUI AddPanelColorInfoRow(GameObject page, string name, Func currentText, string resetHex, string resetTooltip, Action applyAction) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(page, name, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(obj, name + "_Current", currentText(), (TextAlignmentOptions)4097, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 200; int? preferredWidth2 = 260; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; ButtonRef buttonRef = UIFactory.CreateButton(obj, name + "_Reset", "Reset"); GameObject gameObject2 = buttonRef.GameObject; int? minWidth3 = 54; preferredWidth = 60; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(11); } buttonRef.OnClick = delegate { applyAction(resetHex); }; TooltipHover.Attach(buttonRef.GameObject, resetTooltip); return labelRef.TextMesh; } private void AddPanelBgPresetButton(GameObject row, string label, string hex, Action applyAction) { ButtonRef buttonRef = UIFactory.CreateButton(row, "PanelBg_" + ((Object)row).name + "_" + label, label); GameObject gameObject = buttonRef.GameObject; int? minWidth = 50; int? preferredWidth = 60; int? num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(11); } buttonRef.OnClick = delegate { applyAction(hex); }; TooltipHover.Attach(buttonRef.GameObject, $"Apply preset {label} ({hex})."); } private void ApplyOuterPanelBgHex(string hex) { Settings.SetPanelBackgroundColorHex(hex); Plugin.UIManager?.RefreshAllPanelBackgrounds(); if ((Object)(object)_panelBgCurrentLabel != (Object)null) { ((TMP_Text)_panelBgCurrentLabel).text = FormatOuterBgCurrentText(); } } private void ApplyInnerPanelBgHex(string hex) { Settings.SetInnerPanelBackgroundColorHex(hex); Plugin.UIManager?.RefreshScopedInnerBackgrounds(); if ((Object)(object)_innerBgCurrentLabel != (Object)null) { ((TMP_Text)_innerBgCurrentLabel).text = FormatInnerBgCurrentText(); } } private static string FormatOuterBgCurrentText() { return "Current: " + Settings.PanelBackgroundColorHex; } private static string FormatInnerBgCurrentText() { return "Current: " + Settings.InnerPanelBackgroundColorHex; } private static void AddLinkRow(GameObject parent, string label, string url) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "LinkRow_" + label, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(2f, 2f, 1f, 1f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 26, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(obj, "LinkLbl", label + ": " + url, (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 240; int? preferredWidth2 = 320; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)1; ButtonRef buttonRef = UIFactory.CreateButton(obj, "OpenBtn", "Open"); GameObject gameObject2 = buttonRef.GameObject; int? minWidth3 = 56; preferredWidth = 64; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI componentInChildren = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = Theme.ScaledUI(12); } buttonRef.OnClick = delegate { try { Application.OpenURL(url); } catch (Exception ex) { LogUtils.LogWarning("OpenURL('" + url + "') threw: " + ex.Message); } }; TooltipHover.Attach(buttonRef.GameObject, "Open " + url + " in your default browser."); } private void BuildGameGuideTab(GameObject page) { AddGuideSection(page, "V Rising — quick reference", "BloodCraftHub is a UI mod for the Bloodcraft / Kindred V Rising server mods. This tab links to resources for V Rising itself — the official homepage, the community wiki, fan-maintained guides, and the official Discord. Click 'Open' on any row to launch the URL in your default browser."); AddSectionHeading(page, "Official"); AddLinkRow(page, "Game homepage (Stunlock Studios)", "https://playvrising.com"); AddSpacer(page, 6); AddSectionHeading(page, "Community resources"); AddLinkRow(page, "V Rising Wiki (Fandom)", "https://vrising.fandom.com/wiki/V_Rising_Wiki"); AddLinkRow(page, "CaDrift — community guides + tools", "https://www.cadrift.net/v-rising/"); AddSpacer(page, 6); AddSectionHeading(page, "Discord"); AddLinkRow(page, "V Rising official Discord", "https://discord.com/invite/vrising"); AddSpacer(page, 8); AddGuideSection(page, "Suggest a resource", "Have another V Rising guide / map / Discord worth surfacing here? Open an issue on the BloodCraftHub GitHub (link on the About tab) and a future version can include it. Resources listed here are user-suggested — they are not maintained by the mod author and their content / availability may change."); } private void BuildModHelpTab(GameObject page) { AddGuideSection(page, "Bloodcraft mechanics — overview", "Vanilla V Rising has no character-level progression — your power comes from the gear you craft. Bloodcraft is a server-side mod that layers a full RPG progression system over the base game: experience leveling, weapon expertise, blood legacies, classes, prestige, familiars, professions, and daily/weekly quests. BloodCraftHub is the client-side UI for those systems — every command this mod exposes via chat is reachable from this panel. This tab explains each system; the other tabs let you USE them.\n\nNumbers below are the Bloodcraft v1.13.x DEFAULTS. Your server's admin can override any of them in BepInEx/config/Bloodcraft.cfg, so treat the values as a baseline — your actual rates may differ."); AddGuideSection(page, "Experience leveling", "Killing enemies grants XP based on enemy level. Rested XP accumulates while you're logged out inside a coffin — stone coffins give the full rested rate, wooden coffins half. Toggle gain notifications in chat with .lvl log; check current progress with .lvl get. The XP overlay (toggle in the footer) shows level + progress live. At the level cap you can prestige to reset level for permanent bonuses (see below)."); AddCollapsibleHelpDetail(page, "Details — XP", "• Level cap: 90. New players start at level 10.\n• XP multipliers per kill: regular units ×7.5, V-Bloods ×15, docile units ×0.15, war events ×0.2. Unit spawners give no XP by default.\n• Group XP sharing: party / clan members within 25 units of the killer share full XP, as long as their level is within 10 of yours (prestiged players are exempt from the level-difference cap).\n• Rested XP: max stored = 5 levels' worth, accrues at 5% of max per 120-minute tick. Stone coffin = 100% rate; wooden = 50%. Fully recharged after roughly 20 hours of offline coffin time.\n• Per-prestige XP penalty: −5% XP earned per leveling-prestige tier you've completed (server can set this to 0)."); AddCollapsibleHelpDetail(page, "Default settings — XP", "Server-config defaults (BepInEx/config/Bloodcraft.cfg):\n• MaxLevel = 90 • StartingLevel = 10\n• UnitLevelingMultiplier = 7.5\n• VBloodLevelingMultiplier = 15\n• DocileUnitMultiplier = 0.15\n• WarEventMultiplier = 0.2\n• UnitSpawnerMultiplier = 0\n• GroupLevelingMultiplier = 1.0\n• LevelScalingMultiplier = 0.05\n• RestedXPRate = 0.05 RestedXPMax = 5\n• RestedXPTickRate = 120 min\n• ExpShareDistance = 25 ExpShareLevelRange = 10"); AddGuideSection(page, "Weapon Expertise", "Each weapon type tracks its own expertise level — swing it to level it up. Higher expertise = larger bonuses from the stat you've chosen for that weapon. Pick a stat with .wep cst after .wep lst lists what's available; .wep get shows your current weapon's progress and chosen stat. Classes have weapon-stat SYNERGIES that boost specific stat effectiveness — picking stats your class synergizes with is usually the optimal play."); AddCollapsibleHelpDetail(page, "Details — Weapon Expertise", "• Cap per weapon: level 100, up to 10 prestige tiers on top.\n• XP rates: regular units ×2, V-Bloods ×5.\n• You pick 3 stats per weapon. Each scales linearly from 0 at L1 to its full cap at L100; class synergy multiplies the effective cap by 1.5×.\n• Stat caps (baseline, before class synergy): Physical Power +20, Spell Power +10, Max Health +250, Movement Speed +25%, Primary Attack Speed +10%, Physical / Spell Crit Chance +10%, Crit Damage +50%, Life Leech variants +10–15%.\n• Each prestige tier (max 10): −10% XP rate, +10% stat-cap boost. Net: harder to level, bigger payoff at the top.\n• To reset a weapon's stat pick: .wep rst (costs 500× Shattered Bone by default)."); AddCollapsibleHelpDetail(page, "Default settings — Weapon Expertise", "• MaxExpertiseLevel = 100 MaxExpertisePrestiges = 10\n• UnitExpertiseMultiplier = 2\n• VBloodExpertiseMultiplier = 5\n• ExpertiseStatChoices = 3\n• ResetExpertiseItem = 576389135 (Shattered Bone), qty 500\n\nPer-stat caps (Settings → Bloodcraft.cfg):\n• PhysicalPower 20 SpellPower 10 MaxHealth 250\n• MovementSpeed 0.25 PrimaryAttackSpeed 0.10\n• PhysicalCritChance 0.10 PhysicalCritDamage 0.50\n• SpellCritChance 0.10 SpellCritDamage 0.50\n• PhysicalLifeLeech 0.10 SpellLifeLeech 0.10 PrimaryLifeLeech 0.15"); AddGuideSection(page, "Blood Legacies", "Drinking from enemies grants legacy XP for that blood type. Higher legacy = larger bonuses from the stat you've picked for that blood. .bl lst lists available stats per blood; .bl cst picks one; .bl get shows your current blood's progress + chosen stat. Like expertise, classes have blood-stat synergies that amplify specific picks. Worth coordinating blood + weapon + class picks to stack the same stat (e.g. all SpellPower-leaning)."); AddCollapsibleHelpDetail(page, "Details — Blood Legacies", "• Cap per blood type: level 100, up to 10 prestige tiers.\n• XP rates: regular units ×1, V-Bloods ×5.\n• 3 stat picks per blood. Same prestige mechanic as expertise: −10% rate per tier, +10% stat-cap boost per tier.\n• Stat-cap baselines: Healing Received +15%, Damage Reduction +5%, Physical / Spell Resistance +10%, Resource Yield +25%, Reduced Blood Drain +50%, Weapon / Spell Cooldown Recovery +10%, Ultimate Cooldown Recovery +20%, Minion Damage +25%, Ability Attack Speed +10%, Corruption Damage Reduction +10%.\n• Reset a blood's stat pick with .bl rst (500× Shattered Bone by default — same item as expertise reset)."); AddCollapsibleHelpDetail(page, "Default settings — Blood Legacies", "• MaxBloodLevel = 100 MaxLegacyPrestiges = 10\n• UnitLegacyMultiplier = 1\n• VBloodLegacyMultiplier = 5\n• LegacyStatChoices = 3\n• ResetLegacyItem = 576389135 (Shattered Bone), qty 500\n\nPer-stat caps (baseline):\n• HealingReceived 0.15 DamageReduction 0.05\n• PhysicalResistance 0.10 SpellResistance 0.10\n• ResourceYield 0.25 ReducedBloodDrain 0.50\n• WeaponCooldownRecoveryRate 0.10 SpellCooldownRecoveryRate 0.10\n• UltimateCooldownRecoveryRate 0.20 MinionDamage 0.25\n• AbilityAttackSpeed 0.10 CorruptionDamageReduction 0.10"); AddGuideSection(page, "Classes — the six options", "A class is a free pick at character start and a paid change after (via .class change). It grants three things:\n\n• Weapon + Blood synergies — specific stats become more effective (synergized stats get a 1.5× cap multiplier).\n• On-hit debuff effects — chance to apply ignite / weaken / chill / etc. when you damage an enemy. The default proc chance is 7.5%. If the primary debuff is already on the target, the secondary tier-2 self-buff applies instead.\n• Extra spells from the class's spell school — one slot unlocks per leveling-prestige tier, usable on Shift.\n\nClass change cost: 750× Shattered Bone by default."); AddCollapsibleHelpDetail(page, "Details — per-class synergies + debuffs", "Each class's weapon + blood synergies (stats that get 1.5× effective cap) and on-hit debuff school:\n\nBlood Knight (warrior)\n weapon: MaxHealth, PrimaryAttackSpeed, PrimaryLifeLeech, PhysicalPower\n blood: DamageReduction, ReducedBloodDrain, WeaponCooldownRecovery, AbilityAttackSpeed\n on-hit: Leech debuff (secondary: lesser bloodrage self-buff)\n\nVampire Lord (warrior)\n weapon: MaxHealth, SpellLifeLeech, PhysicalPower, SpellPower\n blood: DamageReduction, SpellResistance, UltimateCooldownRecovery, CorruptionDamageReduction\n on-hit: Chill debuff (secondary: lesser frozen weapon)\n\nDemon Hunter (rogue)\n weapon: MovementSpeed, PrimaryAttackSpeed, PhysicalCritChance, PhysicalCritDamage\n blood: PhysicalResistance, ReducedBloodDrain, WeaponCooldownRecovery, MinionDamage\n on-hit: Static debuff (secondary: lesser stormshield)\n\nShadow Blade (rogue)\n weapon: MovementSpeed, PrimaryAttackSpeed, PhysicalPower, PhysicalCritDamage\n blood: SpellResistance, ReducedBloodDrain, WeaponCooldownRecovery, AbilityAttackSpeed\n on-hit: Ignite debuff (secondary: lesser powersurge)\n\nArcane Sorcerer (caster)\n weapon: SpellLifeLeech, SpellPower, SpellCritChance, SpellCritDamage\n blood: HealingReceived, SpellCooldownRecovery, UltimateCooldownRecovery, AbilityAttackSpeed\n on-hit: Weaken debuff (secondary: lesser aegis)\n\nDeath Mage (caster)\n weapon: MaxHealth, SpellLifeLeech, SpellPower, SpellCritDamage\n blood: PhysicalResistance, SpellResistance, SpellCooldownRecovery, MinionDamage\n on-hit: Condemn debuff (secondary: guardian block self-buff)\n\nTip: stack your weapon expertise + blood legacy + class so all three pull toward the same role (e.g. Arcane Sorcerer with SpellPower expertise stat + SpellCooldownRecovery legacy stat)."); AddCollapsibleHelpDetail(page, "Default settings — Classes", "• ClassSystem = false (server must enable)\n• ClassOnHitEffects = true\n• OnHitProcChance = 0.075 (7.5%)\n• SynergyMultiplier = 1.5\n• ChangeClassItem = 576389135 (Shattered Bone), qty 750\n• DefaultClassSpell = −433204738 (Veil of Shadow)\n• PrestigeLevelsToUnlockClassSpells = 0,1,2,3,4,5 (one per tier)"); AddGuideSection(page, "Prestige", "At max level in any progression system (Experience, a specific weapon, a specific blood) you can prestige that system. Prestiging resets the system's level and grants permanent buffs / scaling bonuses — XP-prestige slows future XP gain but boosts your expertise + legacy gain rate, raising your long-term ceiling.\n\nCommands you'll use:\n• .prestige l — list available prestige types.\n• .prestige me — prestige yourself.\n• .prestige get — view current prestige + buffs.\n• .prestige sb — re-sync prestige buffs if they got removed.\n\nThe Prestige tab in BCH dispatches all of these from forms."); AddCollapsibleHelpDetail(page, "Details — Prestige", "• Up to 10 prestige tiers per system (Experience, each weapon type, each blood type).\n• Each XP prestige tier: resets level to 10, applies a permanent buff, reduces XP from kills by 5% per tier, AND boosts your expertise + legacy gain rates by 10% per tier.\n• Each weapon / blood prestige tier: resets that system's level to 1, −10% gain rate, +10% stat-bonus cap per tier.\n• A leaderboard tracks prestige ranks (enabled by default; opt out via admin command).\n• .prestige sb (sync buffs) is the one to remember — if you die or get debuffed and your prestige buffs drop, this reapplies them."); AddCollapsibleHelpDetail(page, "Default settings — Prestige", "• PrestigeSystem = false (server must enable)\n• MaxLevelingPrestiges = 10\n• MaxExpertisePrestiges = 10\n• MaxLegacyPrestiges = 10\n• LevelingPrestigeReducer = 0.05 (5% XP reduction per tier)\n• PrestigeRatesReducer = 0.10 (10% rate reduction per tier for weapons/blood)\n• PrestigeStatMultiplier = 0.10 (10% stat-cap boost per tier)\n• PrestigeRateMultiplier = 0.10 (10% rate bonus to expertise/legacy from leveling prestige)\n• Leaderboard = true"); AddGuideSection(page, "Exo Prestige & Exoforms (endgame)", "Past the regular experience-prestige cap, Bloodcraft has an Exo prestige tier that grants the most powerful endgame buffs and unlocks shapeshift forms (Exoforms).\n\nTwo Exoforms are available — Evolved Vampire and Corrupted Serpent. Pick which one is active with .prestige sf ; trigger the transformation with .prestige exoform (defaults to a taunt emote trigger). Form duration scales with your Exo tier.\n\nExo prestige also yields reward shards usable for advanced familiar unlocks — .fam echoes spends shards to unlock V-Blood familiars whose costs scale by tier."); AddCollapsibleHelpDetail(page, "Details — Exo & Exoforms", "• Requires maxed Experience prestige + level 90 to start Exo tiering. Up to 100 Exo tiers available.\n• Each Exo prestige resets your XP to 0 again but awards 500× Primal Stygian Shards (PrefabGUID 28358550) per tier.\n• Exoform duration: 15s at Exo 1, growing to roughly 180s at Exo 100 (formula: 15 + (165 ÷ 100) × exoLevel).\n• Forms recharge passively; base full-recharge time scales down as your Exo tier rises. A 5-second countdown warning fires just before the form ends.\n• Optional TrueImmortal server toggle: while in exoform your blood swaps to Immortal, restoring to your original on exit.\n• .fam echoes cost formula: base × scaledFactor × EchoesFactor. Higher-level + higher-tier V-Bloods cost more — shard bearers (top tier) cost 25× the base."); AddCollapsibleHelpDetail(page, "Default settings — Exo", "• ExoPrestiging = false (server must enable)\n• ExoPrestigeReward = 28358550 (Primal Stygian Shard)\n• ExoPrestigeRewardQuantity = 500\n• PrimalEchoes = false (enables .fam echoes V-Blood unlocks)\n• EchoesFactor = 1 (cost multiplier, clamped 1–4)\n• TrueImmortal = false"); AddGuideSection(page, "Familiars", "Enemies you defeat have a configured chance to drop as familiars — summonable combat companions. Stored in named boxes (you can create + organize multiple). The Boxes tab lists boxes; the Familiars tab handles your active familiar (bind / unbind / toggle / prestige); the Familiar Browser overlay (footer toggle) is a compact draggable subset for in-combat swaps.\n\nVariants you'll see:\n• Basic — standard capture.\n• Shiny — rare visual + stat variant with a glowing effect and an extra debuff proc when dealing damage.\n• Primal — primal echoes-unlocked V-Blood variant.\n\nFamiliars level via combat; .fam pr prestiges at max level for permanent stat bonuses."); AddCollapsibleHelpDetail(page, "Details — Familiars", "• Unlock chance: 5% on regular units, 1% on V-Bloods, only on the final-blow kill.\n• Cap: level 90, scaling 7.5× units / 15× V-Bloods. Up to 10 prestige tiers, each adding +10% stat bonus (no rate penalty for familiars).\n• Shinies: 20% chance on first unlock of a species, 100% on any repeat unlock. Shiny familiars proc their assigned spell-school debuff on attacks at the same proc rate as class on-hit (7.5%).\n• Shiny cost: 100× Vampiric Dust to apply; 25% of that to change school later.\n• Prestige cost: 1000× Schematics, grants levels equal to the familiar's max-level cap.\n• Share unlocks (off by default): clan / party within experience share distance can co-receive captures.\n• .fam echoes uses Exo shards to unlock specific V-Bloods directly — see the Exo section.\n• Server controls: AllowVBloods, AllowMinions, BannedUnits, BannedTypes filter what's eligible to capture."); AddCollapsibleHelpDetail(page, "Default settings — Familiars", "• FamiliarSystem = false (server must enable)\n• MaxFamiliarLevel = 90 MaxFamiliarPrestiges = 10\n• FamiliarPrestigeStatMultiplier = 0.10\n• FamiliarCombat = true FamiliarPvP = true\n• UnitFamiliarMultiplier = 7.5 VBloodFamiliarMultiplier = 15\n• UnitUnlockChance = 0.05 VBloodUnlockChance = 0.01\n• AllowVBloods = false AllowMinions = false\n• EquipmentOnly = false\n• ShinyChance = 0.20\n• ShinyCostItemQuantity = 100 (range 50–200)\n• PrestigeCostItemQuantity = 1000 (range 500–2000)\n• ShareUnlocks = false"); AddGuideSection(page, "Professions", "Eight gathering / crafting skills level independently. Bonuses differ by profession:\n\n• Mining / Woodcutting / Harvesting — bonus resources per broken node, scaling with profession level. Profession-specific bonus drops (gold ore from mining, saplings from woodcutting, seeds from harvesting).\n• Fishing — extra catch every 20 levels.\n• Alchemy — potions you craft become more effective + last longer (up to ×2 duration at max; holy potions get duration only).\n• Blacksmithing / Tailoring / Enchanting — gear you craft gets +10% base stats and ×2 durability at max profession level.\n\n.prof get shows current level. The Professions overlay (footer toggle) tracks them live; Settings → Display has per-profession checkboxes if you only care about a few."); AddCollapsibleHelpDetail(page, "Details — Professions", "• Profession multiplier (server-tunable): 1.0× by default — all professions level at the same base rate.\n• Stat / durability bonuses scale linearly from L1 to max.\n• Server admins can disable individual professions by listing them in the DisabledProfessions config (comma-separated).\n• Gathering bonus drops: gold ore from mining nodes contributes salvageable jewelry; random saplings + seeds give planters something to work with."); AddCollapsibleHelpDetail(page, "Default settings — Professions", "• ProfessionSystem = false (server must enable)\n• ProfessionFactor = 1.0\n• DisabledProfessions = \"\" (comma-separated profession names)"); AddGuideSection(page, "Daily & Weekly Quests", "Bloodcraft assigns a daily quest (resets daily) and a weekly quest (resets weekly). Each targets specific enemies — kill the target type the required number of times for XP + reward items. Use .quest d / .quest w to view them, .quest t to set a tracker waypoint to the nearest target, .quest r to reroll for a configured cost.\n\nThe Daily Quest overlay (footer toggle) keeps both progress lines visible while you play."); AddCollapsibleHelpDetail(page, "Details — Quests", "• Daily reward multiplier is the base rate; weekly rewards are roughly the daily reward.\n• Each completed daily has a 10% chance to also drop a random perfect gem (useful for Primal Jewel crafting — the gem school influences the resulting jewel).\n• Reroll cost (daily): 50 of the configured reroll item (default PrefabGUID −949672483 — Stygian Coin). Weekly reroll is the same item type, also 50 by default.\n• Optional InfiniteDailies server toggle: when on, daily quests can be repeated as fast as you complete them (off by default → one daily per server day)."); AddCollapsibleHelpDetail(page, "Default settings — Quests", "• QuestSystem = false (server must enable)\n• InfiniteDailies = false\n• DailyPerfectChance = 0.10\n• QuestRewards = 28358550, 576389135, -257494203\n (Primal Stygian Shard, Shattered Bone, ...)\n• QuestRewardAmounts = 50, 250, 50\n• RerollDailyAmount = 50 RerollWeeklyAmount = 50"); AddGuideSection(page, "Where to go next", "Open any of the BLOODCRAFT-group tabs in the left rail to actually use the systems described here. Most commands have UI forms with tooltips on every field. The Quick Start tab covers BCH's UI conventions; the Game Guide tab links external V Rising resources (wiki, Discord, etc.) if you want broader context.\n\nBloodcraft Thunderstore: https://thunderstore.io/c/v-rising/p/zfolmt/Bloodcraft/\nBloodcraft GitHub: https://github.com/mfoltz/Bloodcraft"); } private static void AddCollapsibleHelpDetail(GameObject parent, string title, string body) { CollapsibleSection.Build(parent, title, startExpanded: false, delegate(GameObject content) { LabelRef labelRef = UIFactory.CreateLabel(content, "HelpDetail_" + title, body, (TextAlignmentOptions)257, null, Theme.ScaledUI(14)); UIFactory.SetLayoutElement(labelRef.GameObject, 340, preferredWidth: 380, flexibleWidth: 1, minHeight: 20, flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; }); } private void BuildQuickStartTab(GameObject page) { AddGuideSection(page, "Welcome to BloodCraftHub", "BloodCraftHub is the unified UI for the Bloodcraft V Rising mod. Every chat command from Bloodcraft can be issued from this UI - just open the matching tab and use the buttons or forms. The left rail groups tabs into BLOODCRAFT / KINDRED / HELP - click a group header to collapse or expand it. The footer toggles the secondary overlays (XP, Familiar), auto-resize, and the input block while typing.\n\nTip: this main panel and every overlay are BOTH draggable AND resizable. Drag from anywhere inside to move; drag the bottom-right (or any edge) to resize. The maximize button in the top-right of this main panel toggles fullscreen, and the Settings tab has size controls if you prefer click-to-resize."); AddGuideSection(page, "Leveling (passive)", "Your character earns XP automatically as you defeat enemies. Current level, progress, and class show on the Levels tab and in the XP overlay (toggle on the panel footer). Nothing to click to gain XP - just play. When you hit the level cap, you can prestige (see Prestige below) to reset level and earn permanent bonuses."); AddGuideSection(page, "Familiars", "Familiars are summoned combat companions collected from random drops when you defeat eligible mobs and V-Bloods. The drop table and rate are server-configured.\n\nBoxes hold your familiar collection (named buckets you can switch between). The Boxes tab lists them: click a box to load its contents, then click any familiar to bind it as your active companion. Use the Familiars tab to unbind, toggle combat mode, or prestige the active familiar.\n\nShiny familiars are rare visual+stat variants of normal familiars - the server admin configures the rate. A shiny drop is the same creature but with a glowing effect and (usually) noticeably better stats."); AddGuideSection(page, "Classes", "A class specializes your character with weapon synergies, stat bonuses, and a unique spell. Use Class -> List Classes to see what's available on your server. To pick a class, type `.class s ` in chat - a UI selector lands in a later phase. Most classes grant a spell that can occupy your shift slot (see Unarmed + Shift below)."); AddGuideSection(page, "Weapon Expertise", "Each weapon type (Sword, Axe, Mace, ...) tracks its own expertise level. Switch to a weapon and use it - expertise rises. The Weapon Expertise tab shows level, progress, prestige, and the bonus stats you've chosen for the currently-equipped weapon. Choose a stat with `.wep cst ` (stat picker coming to UI in a later phase). Common choices: PhysicalPower / SpellPower / one of the crit chances."); AddGuideSection(page, "Unarmed + Shift slot", "Two ways to gain extra ability slots:\n\nUnarmed: when you have no weapon equipped, you use unarmed expertise. Bloodcraft tracks it like any weapon. Unarmed expertise unlocks extra spell slots so you can cast while weaponless. Use `.wep locksp` after equipping the spells you want to keep.\n\nShift slot: by default your shift ability is your travel spell (wolf, bat, etc.). Bloodcraft can replace it with a class spell instead. Toggle the override from Class -> Toggle Shift. Pick which class spell goes in the slot with `.class csp <#>`."); AddGuideSection(page, "Prestige", "At max level in any system (Experience, a weapon expertise, a blood legacy, etc.) you can prestige. Prestiging resets the level to 1 but grants permanent stat multipliers proportional to your prestige count.\n\nUse the Prestige tab: expand 'Prestige in a system', pick the type from the dropdown, Submit. Quick actions: List shows what exists; Sync Buffs re-applies your prestige buffs; Exoform toggles the high-prestige shapeshift; Shroud is the permanent stealth toggle if you qualify."); AddGuideSection(page, "Tips", "- Hover any control to see what it does (footer at panel bottom).\n- Auto-resize ON: the panel grows to fit the active tab. Turn off if you prefer manual sizing.\n- The secondary overlays (XP, Familiar, Familiar Browser, Daily Quest) are independent draggable panels - toggle them in the footer."); } private static void AddGuideSection(GameObject parent, string title, string body) { AddSectionHeading(parent, title); LabelRef labelRef = UIFactory.CreateLabel(parent, "Guide_" + title, body, (TextAlignmentOptions)257, null, Theme.ScaledUI(14)); UIFactory.SetLayoutElement(labelRef.GameObject, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 20, flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; } private static void AddAdminRefLine(GameObject parent, string command, string summary) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "AdminRef_" + command, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 8, new Vector4(2f, 2f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(obj, "Cmd", command, (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 200; int? preferredWidth2 = 220; num = 0; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; LabelRef labelRef2 = UIFactory.CreateLabel(obj, "Desc", summary, (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject2 = labelRef2.GameObject; int? minWidth3 = 160; preferredWidth = 180; num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; } private static void AddCommandTableRow(GameObject parent, string command, string description) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateHorizontalGroup(parent, "CmdRow_" + command, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 10, new Vector4(2f, 2f, 2f, 2f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: -1, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ContentSizeFitter obj2 = obj.AddComponent(); obj2.horizontalFit = (FitMode)0; obj2.verticalFit = (FitMode)2; LabelRef labelRef = UIFactory.CreateLabel(obj, "Cmd", command, (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 220; int? preferredWidth2 = 220; num = 0; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 22, gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; LabelRef labelRef2 = UIFactory.CreateLabel(obj, "Desc", description, (TextAlignmentOptions)257, null, Theme.ScaledUI(12)); GameObject gameObject2 = labelRef2.GameObject; int? minWidth3 = 160; preferredWidth = 200; num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: -1, gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; ContentSizeFitter obj3 = labelRef2.GameObject.AddComponent(); obj3.horizontalFit = (FitMode)0; obj3.verticalFit = (FitMode)2; } private static void AddCommandTable(GameObject parent, string title, params (string command, string description)[] entries) { AddSectionHeading(parent, title); for (int i = 0; i < entries.Length; i++) { var (command, description) = entries[i]; AddCommandTableRow(parent, command, description); } } private void RenderExpertise(PlayerStateService.ExpertiseState s) { if ((Object)(object)_wepTypeLabel == (Object)null) { return; } ((TMP_Text)_wepTypeLabel).text = s.Type.ToString(); ((TMP_Text)_wepProgressLabel).text = ((s.Prestige > 0) ? $"Level {s.Level} ({s.Progress * 100f:0.#}%) Prestige {s.Prestige}" : $"Level {s.Level} ({s.Progress * 100f:0.#}%)"); List list = PlayerStateService.DecodeWeaponBonusStats(s.BonusStatsRaw); if (list.Count == 0) { ((TMP_Text)_wepBonusLabel).text = "Bonus Stats: (none yet — pick one via .wep cst)"; return; } List list2 = new List(); foreach (PlayerStateService.WeaponStatType item in list) { if (item != 0) { list2.Add(item.ToString()); } } ((TMP_Text)_wepBonusLabel).text = ((list2.Count > 0) ? ("Bonus Stats: " + string.Join(", ", list2)) : "Bonus Stats: (none yet)"); } private void RenderFamiliar(PlayerStateService.FamiliarState s) { if (!((Object)(object)_famNameLabel == (Object)null)) { ((TMP_Text)_famNameLabel).text = (s.HasActive ? s.Name : "(no familiar bound)"); bool hasActive = s.HasActive; ((TMP_Text)_famProgressLabel).text = ((!hasActive) ? "—" : ((s.Prestige > 0) ? $"Level {s.Level} ({s.Progress * 100f:0.#}%) Prestige {s.Prestige}" : $"Level {s.Level} ({s.Progress * 100f:0.#}%)")); ((TMP_Text)_famStatsLabel).text = (hasActive ? $"HP {s.MaxHealth} PP {s.PhysicalPower} SP {s.SpellPower}" : "HP — PP — SP —"); } } private static void ApplyStrongAccentOutline(TextMeshProUGUI t) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)t == (Object)null) { return; } try { ((TMP_Text)t).outlineColor = Color32.op_Implicit(Color.black); ((TMP_Text)t).outlineWidth = 0.25f; } catch { } } private static TextMeshProUGUI AddInfoLabel(GameObject parent, string name, string initialText, FontStyles style, int fontSize) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(parent, name, initialText, (TextAlignmentOptions)4097, null, fontSize); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(24), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(26), gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = style; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; return labelRef.TextMesh; } private static void AddSectionHeading(GameObject parent, string text) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(text)) { GameObject obj = UIFactory.CreateUIObject("SectionDivider_" + text, parent); ((Graphic)obj.AddComponent()).color = ARTICLE_HEADING_ACCENT; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 2, flexibleWidth: num, preferredHeight: 2, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(parent, "Section_" + text, text, (TextAlignmentOptions)4097, null, Theme.ScaledUI(16)); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(30), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(34), gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)3; ((Graphic)labelRef.TextMesh).color = ARTICLE_HEADING_ACCENT; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } } private static void AddSpacer(GameObject parent, int height) { //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) GameObject gameObject = UIFactory.CreateUIObject("Spacer", parent); int? minHeight = height; int? preferredHeight = height; int? flexibleHeight = 0; int? flexibleWidth = 1; UIFactory.SetLayoutElement(gameObject, null, minHeight, flexibleWidth, flexibleHeight, null, preferredHeight); } private static GameObject AddCard(GameObject parent, string name, Color? tint = null, int padding = 10, int innerSpacing = 4) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) GameObject val = UIFactory.CreateVerticalGroup(parent, name, forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, innerSpacing, new Vector4((float)padding, (float)padding, (float)padding, (float)padding), Theme.CardBackground); UIFactory.SetLayoutElement(val, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: Theme.ScaledHeight(28), flexibleHeight: 0); if (tint.HasValue && tint.Value.a > 0.0001f) { GameObject obj = UIFactory.CreateUIObject("Tint", val); RectTransform component = obj.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; Image obj2 = obj.AddComponent(); ((Graphic)obj2).color = tint.Value; ((Graphic)obj2).raycastTarget = false; obj.transform.SetSiblingIndex(0); bool? ignoreLayout = true; UIFactory.SetLayoutElement(obj, null, null, null, null, null, null, ignoreLayout); } return val; } private static (TextMeshProUGUI labelTmp, TextMeshProUGUI valueTmp) AddStatRow(GameObject parent, string label, string value, int fontSize = -1, FontStyles valueStyle = 1) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) int fontSize2 = ((fontSize > 0) ? fontSize : Theme.ScaledUI(12)); GameObject val = UIFactory.CreateHorizontalGroup(parent, "StatRow_" + label, forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 8, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 320; int? preferredWidth = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(20), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(22), gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); LabelRef labelRef = UIFactory.CreateLabel(val, "Label", $"{label}", (TextAlignmentOptions)4097, null, fontSize2); GameObject gameObject = labelRef.GameObject; int? minWidth2 = 140; int? preferredWidth2 = 170; num = 0; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(20), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(22), gameObject: gameObject, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; LabelRef labelRef2 = UIFactory.CreateLabel(val, "Value", value, (TextAlignmentOptions)4100, null, fontSize2); GameObject gameObject2 = labelRef2.GameObject; int? minWidth3 = 120; preferredWidth = 200; num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(20), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(22), gameObject: gameObject2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef2.TextMesh).fontStyle = valueStyle; ((TMP_Text)labelRef2.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef2.TextMesh).overflowMode = (TextOverflowModes)0; return (labelRef.TextMesh, labelRef2.TextMesh); } private static void AddDivider(GameObject parent, int verticalGap = 6) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) int height = Mathf.Max(2, verticalGap / 2); AddSpacer(parent, height); GameObject obj = UIFactory.CreateUIObject("DividerLine", parent); int? minWidth = 100; int? preferredWidth = 320; int? num = 1; UIFactory.SetLayoutElement(minHeight: 1, flexibleWidth: num, preferredHeight: 1, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); Image obj2 = obj.AddComponent(); ((Graphic)obj2).color = Theme.DividerLine; ((Graphic)obj2).raycastTarget = false; AddSpacer(parent, height); } private static TextMeshProUGUI AddBodyText(GameObject parent, string text, int fontSize = -1) { int fontSize2 = ((fontSize > 0) ? fontSize : Theme.ScaledUI(13)); LabelRef labelRef = UIFactory.CreateLabel(parent, "BodyText", $"{text}", (TextAlignmentOptions)257, null, fontSize2); UIFactory.SetLayoutElement(labelRef.GameObject, 320, preferredWidth: 380, flexibleWidth: 1, minHeight: Theme.ScaledHeight(22), flexibleHeight: 0); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ContentSizeFitter obj = labelRef.GameObject.AddComponent(); obj.horizontalFit = (FitMode)0; obj.verticalFit = (FitMode)2; return labelRef.TextMesh; } private static string Mono(string s) { if (!string.IsNullOrEmpty(s)) { return $"{s}"; } return s; } private static void AddCommandButton(GameObject parent, string label, string command, string tooltip = null, Color? color = null, bool confirm = false) { ButtonRef buttonRef = UIFactory.CreateButton(parent, "Cmd_" + label, label, color); GameObject gameObject = buttonRef.GameObject; int? minWidth = 70; int? preferredWidth = 110; int? num = 1; UIFactory.SetLayoutElement(minHeight: Theme.ScaledHeight(28), flexibleWidth: num, preferredHeight: Theme.ScaledHeight(30), gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); TextMeshProUGUI t = ((Component)buttonRef.Component).GetComponentInChildren(); if ((Object)(object)t != (Object)null) { ((TMP_Text)t).enableWordWrapping = false; ((TMP_Text)t).overflowMode = (TextOverflowModes)0; ((TMP_Text)t).alignment = (TextAlignmentOptions)514; ((TMP_Text)t).fontSize = Theme.ScaledUI(13); } if (confirm && (Object)(object)t != (Object)null) { float armUntil = -1f; string originalLabel = label; buttonRef.OnClick = delegate { float realtimeSinceStartup = Time.realtimeSinceStartup; if (armUntil > 0f && realtimeSinceStartup <= armUntil) { armUntil = -1f; ((TMP_Text)t).text = originalLabel; EnqueueOrWarn(command); } else { armUntil = realtimeSinceStartup + 3f; ((TMP_Text)t).text = "Confirm?"; } }; } else { buttonRef.OnClick = delegate { EnqueueOrWarn(command); }; } TooltipHover.Attach(buttonRef.GameObject, tooltip ?? ("Sends '" + command + "' to the server.")); } private static void EnqueueOrWarn(string command) { if (!MessageService.IsInitialized) { LogUtils.LogWarning("Cannot send '" + command + "' — MessageService not yet bound to character/user."); return; } MessageService.EnqueueMessage(command); LogUtils.LogInfo("Enqueued outbound: " + command); } private void BuildOverlayFooter(GameObject parent) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateVerticalGroup(parent, "OverlayFooterWrap", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(10f, 10f, 6f, 6f), Theme.CardBackground); int? minHeight = 62; int? flexibleHeight = 0; int? flexibleWidth = 1; UIFactory.SetLayoutElement(obj, null, minHeight, flexibleWidth, flexibleHeight); GameObject val = UIFactory.CreateHorizontalGroup(obj, "OverlayFooterRow1", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: false, childControlHeight: false, 10, new Vector4(0f, 0f, 0f, 0f)); flexibleWidth = 26; flexibleHeight = 0; minHeight = 1; UIFactory.SetLayoutElement(val, null, flexibleWidth, minHeight, flexibleHeight); LabelRef labelRef = UIFactory.CreateLabel(val, "OverlayVisLabel", "Show overlays:", (TextAlignmentOptions)4097, null, Theme.ScaledUI(12)); GameObject gameObject = labelRef.GameObject; int? minWidth = 110; minHeight = 120; flexibleHeight = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: flexibleHeight, preferredHeight: 26, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: minHeight); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)1; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; _combinedOverlayToggle = AddOverlayToggle(val, "Combined", PanelType.CombinedOverlay); if (_overlayToggleGOs.TryGetValue(PanelType.CombinedOverlay, out var value)) { TooltipHover.Attach(value, "Toggle the combined info overlay — one panel with XP / Familiar / Weapon / Blood / Professions / Quests sections in a single container. When on, the standalone XP / Familiar / Daily Quest / Professions overlays auto-hide; their per-section visibility is controlled in Settings → Display → Combined overlay."); } _xpOverlayToggle = AddOverlayToggle(val, "XP", PanelType.ExperienceOverlay); _famOverlayToggle = AddOverlayToggle(val, "Familiar", PanelType.FamiliarOverlay); _famBrowserToggle = AddOverlayToggle(val, "Familiar Browser", PanelType.FamiliarBrowserOverlay); _dqOverlayToggle = AddOverlayToggle(val, "Daily quest", PanelType.DailyQuestOverlay); _profOverlayToggle = AddOverlayToggle(val, "Professions", PanelType.ProfessionOverlay); _shiftOverlayToggle = AddOverlayToggle(val, "Shift spell", PanelType.ShiftSpellOverlay); ApplyCombinedFooterVisibility(); GameObject val2 = UIFactory.CreateHorizontalGroup(obj, "OverlayFooterRow2", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: false, childControlHeight: false, 12, new Vector4(0f, 0f, 0f, 0f)); flexibleWidth = 26; flexibleHeight = 0; minHeight = 1; UIFactory.SetLayoutElement(val2, null, flexibleWidth, minHeight, flexibleHeight); AddAutoResizeToggle(val2); AddLockOverlaysToggle(val2); } private void AddLockOverlaysToggle(GameObject parent) { //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) ToggleRef toggleRef = UIFactory.CreateToggle(parent, "LockOverlaysToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth = 160; int? preferredWidth = 180; int? num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = "Lock overlays"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(13); ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 130; int? preferredWidth2 = 150; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = Settings.LockOverlays; TooltipHover.Attach(toggleRef.GameObject, "Lock the position and size of every overlay so they can't be dragged or resized by accident during play. Settings-driven resize (e.g. enabling progress bars on the XP overlay, or a V-Blood scan growing the list) still works."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { Settings.SetLockOverlays(value); Plugin.UIManager?.ApplyOverlayLockState(); }); } private void AddAutoResizeToggle(GameObject parent) { //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) ToggleRef toggleRef = UIFactory.CreateToggle(parent, "AutoResizeToggle"); GameObject gameObject = toggleRef.GameObject; int? minWidth = 160; int? preferredWidth = 180; int? num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = "Auto-resize panel"; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(13); ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 130; int? preferredWidth2 = 150; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = Settings.IsPanelAutoResizeEnabled; TooltipHover.Attach(toggleRef.GameObject, "When on, the main panel grows to fit the active tab's content (capped at 90% of screen height)."); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate(bool value) { ((BasePlugin)Plugin.Instance).Config.Bind("UISettings", "IsPanelAutoResizeEnabled", true, "").Value = value; AutoResizeIfEnabled(); }); } private void AutoResizeIfEnabled() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if (!Settings.IsPanelAutoResizeEnabled || _isFullscreen || !_tabContent.TryGetValue(ActiveTab, out var value) || (Object)(object)value == (Object)null) { return; } _tabInnerContent.TryGetValue(ActiveTab, out var value2); GameObject parent = (((Object)(object)value2 != (Object)null) ? value2 : value); try { RectTransform component = value.GetComponent(); if (!((Object)(object)component == (Object)null)) { LayoutRebuilder.ForceRebuildLayoutImmediate(component); float val = ComputeChildrenSumHeight(parent); float val2 = (((Object)(object)_tabStripGo != (Object)null) ? ComputeChildrenSumHeight(_tabStripGo) : 0f); float num = Math.Max(val, val2); float num2 = 110f; float val3 = num + num2; float num3 = Math.Min(val2: (float)Screen.height * 0.9f, val1: Math.Max(val3, MinHeight)); Vector2 sizeDelta = base.Rect.sizeDelta; if (Math.Abs(sizeDelta.y - num3) > 1f) { base.Rect.sizeDelta = new Vector2(sizeDelta.x, num3); EnsureValidPosition(); base.Dragger?.OnEndResize(); } } } catch (Exception value3) { LogUtils.LogError($"AutoResizeIfEnabled failed: {value3}"); } } private static float ComputeChildrenSumHeight(GameObject parent) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) RectTransform component = parent.GetComponent(); if ((Object)(object)component == (Object)null) { return 0f; } VerticalLayoutGroup component2 = parent.GetComponent(); float num = (((Object)(object)component2 != (Object)null) ? ((HorizontalOrVerticalLayoutGroup)component2).spacing : 0f); float num2 = (((Object)(object)component2 != (Object)null) ? ((float)((LayoutGroup)component2).padding.top) : 0f); float num3 = (((Object)(object)component2 != (Object)null) ? ((float)((LayoutGroup)component2).padding.bottom) : 0f); float num4 = num2 + num3; int num5 = 0; for (int i = 0; i < ((Transform)component).childCount; i++) { Transform child = ((Transform)component).GetChild(i); if (!((Component)child).gameObject.activeInHierarchy) { continue; } num5++; RectTransform component3 = ((Component)child).GetComponent(); if (!((Object)(object)component3 == (Object)null)) { float num6 = LayoutUtility.GetPreferredHeight(component3); if (num6 < 0f) { Rect rect = component3.rect; num6 = ((Rect)(ref rect)).height; } num4 += num6; } } if (num5 > 1) { num4 += num * (float)(num5 - 1); } return num4; } private Toggle AddOverlayToggle(GameObject parent, string label, PanelType overlay) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) ToggleRef toggleRef = UIFactory.CreateToggle(parent, $"OverlayToggle_{overlay}"); GameObject gameObject = toggleRef.GameObject; int? minWidth = 130; int? preferredWidth = 150; int? num = 0; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)toggleRef.Text).text = label; ((TMP_Text)toggleRef.Text).fontSize = Theme.ScaledUI(13); ((TMP_Text)toggleRef.Text).enableWordWrapping = false; ((TMP_Text)toggleRef.Text).overflowMode = (TextOverflowModes)0; ((TMP_Text)toggleRef.Text).alignment = (TextAlignmentOptions)4097; GameObject gameObject2 = ((Component)toggleRef.Text).gameObject; int? minWidth2 = 100; int? preferredWidth2 = 120; num = 1; UIFactory.SetLayoutElement(minHeight: 24, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); toggleRef.Toggle.isOn = Plugin.UIManager.IsOverlayOpen(overlay); toggleRef.OnValueChanged = (Action)Delegate.Combine(toggleRef.OnValueChanged, (Action)delegate { Plugin.UIManager.ToggleOverlay(overlay); if (overlay == PanelType.CombinedOverlay) { ApplyCombinedFooterVisibility(); Plugin.UIManager?.RefreshCombinedOverlaySections(); } RefreshAllOverlayToggleStates(); }); _overlayToggleGOs[overlay] = toggleRef.GameObject; return toggleRef.Toggle; } public void ApplyCombinedFooterVisibility() { bool showCombinedOverlay = Settings.ShowCombinedOverlay; SetToggleVisible(PanelType.ExperienceOverlay, !showCombinedOverlay); SetToggleVisible(PanelType.FamiliarOverlay, !showCombinedOverlay); SetToggleVisible(PanelType.DailyQuestOverlay, !showCombinedOverlay); SetToggleVisible(PanelType.ProfessionOverlay, !showCombinedOverlay); } private void SetToggleVisible(PanelType overlay, bool visible) { if (_overlayToggleGOs.TryGetValue(overlay, out var value) && (Object)(object)value != (Object)null && value.activeSelf != visible) { value.SetActive(visible); } } public void ShowTab(PanelType tab) { if (!_tabContent.ContainsKey(tab)) { return; } foreach (KeyValuePair item in _tabContent) { item.Value.SetActive(item.Key == tab); } ActiveTab = tab; if (tab == PanelType.BoxesTab && PlayerStateService.BoxList != null && PlayerStateService.BoxList.Count == 0 && MessageService.IsInitialized) { if ((Object)(object)_boxesStatusLabel != (Object)null) { ((TMP_Text)_boxesStatusLabel).text = "Loading boxes from the server…"; } EnqueueOrWarn(".fam boxes"); } if (MessageService.IsInitialized) { switch (tab) { case PanelType.ExpertiseTab: FireWepInfoFetch(); break; case PanelType.BloodLegacyTab: FireBlInfoFetch(); break; } } AutoResizeIfEnabled(); } private void TickTabAutoRefresh() { if (MessageService.IsInitialized && base.Enabled) { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (ActiveTab == PanelType.ExpertiseTab && realtimeSinceStartupAsDouble - _lastWepAutoFetchAt >= 10.0) { FireWepInfoFetch(); } else if (ActiveTab == PanelType.BloodLegacyTab && realtimeSinceStartupAsDouble - _lastBlAutoFetchAt >= 10.0) { FireBlInfoFetch(); } } } private void FireWepInfoFetch() { _lastWepAutoFetchAt = Time.realtimeSinceStartupAsDouble; if (MessageService.IsInitialized) { MessageService.EnqueueMessageSilent(".wep get"); } else { EnqueueOrWarn(".wep get"); } } private void FireBlInfoFetch() { _lastBlAutoFetchAt = Time.realtimeSinceStartupAsDouble; PlayerStateService.LegacyState legacy = PlayerStateService.Legacy; if (PlayerStateService.IsBondableBloodType(legacy.Type)) { if (MessageService.IsInitialized) { MessageService.EnqueueMessageSilent($".bl get {legacy.Type}"); } else { EnqueueOrWarn($".bl get {legacy.Type}"); } } } internal override void Reset() { if (_availabilitySubscribed) { EclipseProtocolService.AvailabilityChanged -= OnBloodcraftAvailabilityChanged; _availabilitySubscribed = false; } if (_deferredAvailabilityRefresh != null) { CoreUpdateBehavior.Actions.Remove(_deferredAvailabilityRefresh); _deferredAvailabilityRefresh = null; } if (_tabAutoRefreshTicker != null) { CoreUpdateBehavior.Actions.Remove(_tabAutoRefreshTicker); _tabAutoRefreshTicker = null; } if (_sizePosReadoutTicker != null) { CoreUpdateBehavior.Actions.Remove(_sizePosReadoutTicker); _sizePosReadoutTicker = null; } _sizePosRefreshers.Clear(); if (_wepLastResponseSubscribed) { PlayerStateService.LastResponseChanged -= OnLastResponseChangedForWep; _wepLastResponseSubscribed = false; } if (_vbSubscribed) { PlayerStateService.VBloodCollectionChanged -= OnVBloodCollectionChanged; VBloodScannerService.ScanStateChanged -= OnVBloodScanStateChanged; _vbSubscribed = false; } if (_vbSummonStatusSubscribed) { VBloodSummonService.StatusChanged -= OnVBSummonStatusChanged; _vbSummonStatusSubscribed = false; } if (_famSubscribed) { PlayerStateService.FamiliarChanged -= OnFamiliarChanged; _famSubscribed = false; } if (_famSearchSubscribed) { MessageService.FamSearchCompleted -= OnFamSearchCompletedForFamTab; _famSearchSubscribed = false; } if (_classSubscribed) { PlayerStateService.ExperienceChanged -= OnExperienceChangedForClass; _classSubscribed = false; } if (_wepSubscribed) { PlayerStateService.ExpertiseChanged -= OnExpertiseChanged; _wepSubscribed = false; } if (_shiftSubscribed) { PlayerStateService.ExpertiseChanged -= OnExpertiseChangedForUnarmed; PlayerStateService.ShiftSpellChanged -= OnShiftSpellChanged; _shiftSubscribed = false; } if (_boxesSubscribed) { PlayerStateService.BoxListChanged -= OnBoxListChanged; PlayerStateService.BoxContentsChanged -= OnBoxContentsChanged; PlayerStateService.ActiveBoxChanged -= OnActiveBoxChanged; _boxesSubscribed = false; } if (_prestigeSubscribed) { PlayerStateService.ExperienceChanged -= OnAnyForPrestige; PlayerStateService.LegacyChanged -= OnAnyForPrestige; PlayerStateService.ExpertiseChanged -= OnAnyForPrestige; PlayerStateService.FamiliarChanged -= OnAnyForPrestige; _prestigeSubscribed = false; } if (_lvlSubscribed) { PlayerStateService.ExperienceChanged -= OnAnyForLevels; PlayerStateService.LegacyChanged -= OnAnyForLevels; PlayerStateService.ExpertiseChanged -= OnAnyForLevels; PlayerStateService.FamiliarChanged -= OnAnyForLevels; PlayerStateService.ProfessionChanged -= OnAnyForLevels; _lvlSubscribed = false; } if (_dqSubscribed) { PlayerStateService.QuestChanged -= OnQuestChangedForTab; _dqSubscribed = false; } if (_blSubscribed) { PlayerStateService.LegacyChanged -= OnLegacyChanged; _blSubscribed = false; } if (_blInfoSubscribed) { PlayerStateService.BloodInfoChanged -= OnBloodInfoChanged; _blInfoSubscribed = false; } if (_prestigeInfoSubscribed) { PlayerStateService.PrestigeInfoChanged -= OnPrestigeInfoChanged; _prestigeInfoSubscribed = false; } if (_collapsibleSubscribed) { CollapsibleSection.Toggled -= AutoResizeIfEnabled; _collapsibleSubscribed = false; } if (_lastResponseSubscribed) { PlayerStateService.LastResponseChanged -= OnLastResponseChanged; _lastResponseSubscribed = false; } } private void BuildKindredAdminPlayersTab(GameObject page) { //IL_06c1: Unknown result type (might be due to invalid IL or missing references) RenderAdminInfoNote(page, "Kindred admin (Players)"); AddAdminWarningIntro(page, "Player-targeting admin commands. Most accept a player name; leave the player field blank to target yourself. Requires KindredCommands + server admin permission."); AddSpacer(page, 4); AddSectionHeading(page, "General"); AddCommandButton(AddKLRow(page, "KCAGenRow1"), "Revive Target", ".revivetarget", "Revive the dead player you are currently targeting (.revivetarget)."); AddPlayerCollapse(page, "Revive (.revive)", "Revive a player from death (defaults to self).", ".revive {player}", optional: true); AddPlayerCollapse(page, "Kill player (.killplayer)", "Instantly kill the specified player.", ".killplayer {player}", optional: false); AddPlayerCollapse(page, "Stay down (.staydown)", "Down the target player until revived.", ".staydown {player}", optional: false); CollapsibleSection.Build(page, "Set heart count (.playerheartcount)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set heart count", ".playerheartcount {amount} {player}", new IntField("amount", "Heart count", 0, 99, "Number of soul-shard hearts."), new PlayerNameField("player", "Player", "Player to apply to. Required for this command.")); }, "Set how many soul-shard 'hearts' a player counts as on the server."); CollapsibleSection.Build(page, "Teleport player to coords (.teleport)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Teleport to coords", ".teleport {x} {y} {z} {player}", new TextField("x", "X", "", "World X coordinate."), new TextField("y", "Y", "", "World Y coordinate."), new TextField("z", "Z", "", "World Z coordinate."), new TextField("player", "Player (blank for self)", "", "Target player. Leave blank to teleport yourself.")); }, "Teleport a player to specific X/Y/Z world coordinates (defaults to self)."); AddPlayerCollapse(page, "Reveal map (.revealmap)", "Reveal the entire map for a player.", ".revealmap {player}", optional: true); AddPlayerCollapse(page, "Unlock skills/journal (.unlock)", "Unlock all skills, journal entries, and abilities for a player.", ".unlock {player}", optional: true); AddPlayerCollapse(page, "God mode on (.god)", "Enable full god mode for the target.", ".god {player}", optional: true); AddPlayerCollapse(page, "Mortal (god mode off) (.mortal)", "Remove god mode and admin boosts from the target.", ".mortal {player}", optional: true); AddPlayerCollapse(page, "Reset cooldowns (.resetcooldown)", "Instantly reset all ability cooldowns for the target.", ".resetcooldown {player}", optional: true); AddPlayerCollapse(page, "List active buffs (.listbuffs)", "List the buffs currently applied to the target.", ".listbuffs {player}", optional: true); CollapsibleSection.Build(page, "Spectate (.spectate)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Spectate", ".spectate {player} {returnToStart}", new TextField("player", "Player (blank for self)", "", "Player to spectate. Blank reverts to self."), new BoolField("returnToStart", "Return to start when done", defaultValue: true, "If true, snaps you back to your starting position when spectate ends.")); }, "Toggle spectate mode following a player."); AddSpacer(page, 6); AddSectionHeading(page, "Identity"); CollapsibleSection.Build(page, "Rename self (.rename)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Rename self", ".rename {newName}", new TextField("newName", "New name", "", "Your new character name.")); }, "Rename your own character."); CollapsibleSection.Build(page, "Rename player (.rename)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Rename player", ".rename {player} {newName}", new PlayerNameField("player", "Player"), new TextField("newName", "New name")); }, "Rename another player's character."); CollapsibleSection.Build(page, "Unbind player save (.unbindplayer)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Unbind player", ".unbindplayer {player}", new PlayerNameField("player", "Player")); }, "Unbinds a SteamID from a player save so the slot can be re-used."); CollapsibleSection.Build(page, "Swap two players' SteamIDs (.swapplayers)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Swap players", ".swapplayers {player1} {player2}", new PlayerNameField("player1", "Player 1"), new PlayerNameField("player2", "Player 2")); }, "Switches the SteamIDs of two players. Useful for recovering lost characters."); AddSpacer(page, 6); AddSectionHeading(page, "Fly"); AddPlayerCollapse(page, "Toggle fly (.fly)", "Toggle fly mode for a player (defaults to self).", ".fly {player}", optional: true); AddPlayerCollapse(page, "Fly up one floor (.flyup)", "Increase fly height by one floor.", ".flyup {player}", optional: true); AddPlayerCollapse(page, "Fly down one floor (.flydown)", "Decrease fly height by one floor.", ".flydown {player}", optional: true); CollapsibleSection.Build(page, "Fly level (.flylevel)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Fly to level", ".flylevel {floor} {player}", new IntField("floor", "Floor", 0, 99, "Floor number to fly to."), new TextField("player", "Player (blank for self)")); }, "Set fly height to a specific floor level."); CollapsibleSection.Build(page, "Set fly height (.flyheight)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set fly height", ".flyheight {height}", new IntField("height", "Height", 1, 500, "Fly height in world units. Default 30.")); }, "Set the absolute fly height for yourself (default 30)."); CollapsibleSection.Build(page, "Set fly obstacle height (.flyobstacleheight)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set obstacle height", ".flyobstacleheight {height}", new IntField("height", "Height", 0, 100, "Auto-rise height when an obstacle is below. Default 7.")); }, "Set the height to fly above any obstacles (default 7)."); AddSpacer(page, 6); AddSectionHeading(page, "Buffs & items"); CollapsibleSection.Build(page, "Apply buff (.buff)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Apply buff", ".buff {buff} {player} {duration} {immortal}", new TextField("buff", "Buff prefab", "", "Buff prefab name or ID. Use .search npc on the server for hints."), new TextField("player", "Player (blank for self)"), new IntField("duration", "Duration (seconds, 0=infinite)", 0, 86400), new BoolField("immortal", "Immortal flag", defaultValue: false, "If true the buff cannot be cleansed.")); }, "Apply a named buff to a player. Buff names match the server's prefab lookup."); CollapsibleSection.Build(page, "Remove buff (.debuff)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Remove buff", ".debuff {buff} {player}", new TextField("buff", "Buff prefab"), new TextField("player", "Player (blank for self)")); }, "Remove a specific buff from a player."); CollapsibleSection.Build(page, "Give item (.give)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Give item", ".give {item} {quantity}", new TextField("item", "Item", "", "Item prefab/name. Use .search item to look up names."), new IntField("quantity", "Quantity", 1, 9999)); }, "Spawn an item directly into your inventory."); CollapsibleSection.Build(page, "Create blood potion (.bloodpotion)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Create blood potion", ".bloodpotion {type} {quality} {quantity}", new EnumField("type", "Blood type", PlayerStateService.BloodType.Warrior), new IntField("quality", "Quality (1-100)", 1, 100), new IntField("quantity", "Quantity", 1, 99)); }, "Spawn a blood potion of the given blood type and quality."); CollapsibleSection.Build(page, "Create mixed blood potion (.bloodpotionmix)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Create mixed potion", ".bloodpotionmix {primaryType} {primaryQuality} {secondaryType} {secondaryQuality} {secondaryTrait} {quantity}", new EnumField("primaryType", "Primary type", PlayerStateService.BloodType.Warrior), new IntField("primaryQuality", "Primary quality", 1, 100), new EnumField("secondaryType", "Secondary type", PlayerStateService.BloodType.Scholar), new IntField("secondaryQuality", "Secondary quality", 1, 100), new IntField("secondaryTrait", "Secondary trait #", 1, 4), new IntField("quantity", "Quantity", 1, 99)); }, "Spawn a mixed blood potion blending two blood types."); AddSpacer(page, 6); CollapsibleSection.Build(page, "Boost (.bst) - 22 commands", startExpanded: false, BuildBoostGroup, "Per-player stat boosts and admin toggles. Each accepts an optional player (blank for self)."); AddSpacer(page, 6); AddSectionHeading(page, "Gear"); AddPlayerCollapse(page, "Repair all gear (.gear repair)", "Repair every piece of gear the target is wearing.", ".gear repair {player}", optional: true); AddPlayerCollapse(page, "Break all gear (.gear break)", "Drop the target's gear durability to zero.", ".gear break {player}", optional: true); CollapsibleSection.Build(page, "Set soulshard durability (.gear soulsharddurability)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set soulshard durability", ".gear soulsharddurability {durability} {player}", new IntField("durability", "Durability (0-1)", 0, 1, "Use a fractional value via the chat command for non-integer durability. UI uses int."), new TextField("player", "Player (blank for self)")); }, "Set the soulshard durability for a player."); AddSpacer(page, 6); AddSectionHeading(page, "Clan"); CollapsibleSection.Build(page, "Add player to clan (.clan add)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Add to clan", ".clan add {player} {clanName}", new PlayerNameField("player", "Player"), new TextField("clanName", "Clan name")); }, "Force-add a player to a clan."); CollapsibleSection.Build(page, "Kick player from clan (.clan kick)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Kick from clan", ".clan kick {player}", new PlayerNameField("player", "Player")); }, "Remove a player from their current clan."); CollapsibleSection.Build(page, "Change clan role (.clan changerole)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Change clan role", ".clan changerole {player} {role}", new PlayerNameField("player", "Player"), new TextField("role", "Role", "", "Role name (e.g. Member, Officer, Leader). Match what KindredCommands expects.")); }, "Change a player's role within their clan (Member / Officer / Leader)."); AddSpacer(page, 6); AddSectionHeading(page, "Lookup"); CollapsibleSection.Build(page, "Player info (.playerinfo)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Player info", ".playerinfo {player}", new PlayerNameField("player", "Player")); }, "Display detailed info about a specific player. Reply appears in chat."); CollapsibleSection.Build(page, "Search player by SteamID (.idcheck)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "ID check", ".idcheck {steamId}", new TextField("steamId", "SteamID", "76561198…")); }, "Look up which character is associated with a given SteamID."); CollapsibleSection.Build(page, "Assign SteamID to player (.assignsteamID)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Assign SteamID", ".assignsteamID {player} {steamId}", new PlayerNameField("player", "Player"), new TextField("steamId", "SteamID", "76561198…")); }, "Link a player record to a different SteamID. Recovery use — most servers will never need this."); AddSpacer(page, 6); AddSectionHeading(page, "Cosmetic / Convenience"); CollapsibleSection.Build(page, "Toggle hair visibility (.showhair)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Show hair", ".showhair {player}", new PlayerNameField("player", "Player (blank for self)")); }, "Toggle hair rendering on a player (or yourself if blank)."); AddSpacer(page, 6); AddSectionHeading(page, "Prisoner config (read)"); GameObject obj = UIFactory.CreateHorizontalGroup(page, "PrisonerDiag", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj, "Gruel Settings", ".gruelsettings", "Print current gruel-conversion config (.gruelsettings)."); AddCommandButton(obj, "Feed Settings", ".feedsettings", "Print current feed-prisoner config (.feedsettings)."); AddSpacer(page, 6); AddSectionHeading(page, "Mass action (DESTRUCTIVE)"); CollapsibleSection.Build(page, "Unbind every player (.unbindall) — DESTRUCTIVE", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Unbind ALL players", ".unbindall", new BoolField("confirm", "Yes, rename + unbind everyone", defaultValue: false, "Required. Even with this checked, double-check before clicking Submit.", requireTrue: true)); }, "Renames EVERY player to OLD##### and unbinds them from their SteamIDs. There is no undo. Used for fresh-start prep. Requires the confirm checkbox."); } private static void AddPlayerCollapse(GameObject page, string title, string tooltip, string template, bool optional) { CollapsibleSection.Build(page, title, startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, title, template, optional ? new TextField("player", "Player (blank for self)", "", "Leave blank to target yourself.") : new PlayerNameField("player", "Player")); }, tooltip); } private void BuildBoostGroup(GameObject c) { AddCommandButton(AddKLRow(c, "BstZeroArg"), "List Boosted", ".bst players", "List all currently boosted players (.bst players)."); AddBstValueForm(c, "Attack speed (.bst attackspeed)", ".bst attackspeed {value} {player}", "Attack speed multiplier (default 10).", 10); AddBstTogglePlayer(c, "Remove attack speed (.bst removeattackspeed)", ".bst removeattackspeed {player}"); AddBstValueForm(c, "Damage (.bst damage)", ".bst damage {value} {player}", "Bonus damage value (default 10000).", 10000); AddBstTogglePlayer(c, "Remove damage (.bst removedamage)", ".bst removedamage {player}"); AddBstValueForm(c, "Health (.bst health)", ".bst health {value} {player}", "Bonus health value (default 100000).", 100000); AddBstTogglePlayer(c, "Remove health (.bst removehealth)", ".bst removehealth {player}"); AddBstValueForm(c, "Movement speed (.bst speed)", ".bst speed {value} {player}", "Movement speed multiplier (default 15).", 15); AddBstTogglePlayer(c, "Remove speed (.bst removespeed)", ".bst removespeed {player}"); AddBstValueForm(c, "Yield (.bst yield)", ".bst yield {value} {player}", "Yield multiplier on harvest/loot (default 10).", 10); AddBstTogglePlayer(c, "Remove yield (.bst removeyield)", ".bst removeyield {player}"); AddBstTogglePlayer(c, "Bat vision (.bst batvision)", ".bst batvision {player}"); AddBstTogglePlayer(c, "Fly (.bst fly)", ".bst fly {player}"); AddBstTogglePlayer(c, "No aggro (.bst noaggro)", ".bst noaggro {player}"); AddBstTogglePlayer(c, "No blood drain (.bst noblooddrain)", ".bst noblooddrain {player}"); AddBstTogglePlayer(c, "No cooldown (.bst nocooldown)", ".bst nocooldown {player}"); AddBstTogglePlayer(c, "No durability loss (.bst nodurability)", ".bst nodurability {player}"); AddBstTogglePlayer(c, "Immaterial (.bst immaterial)", ".bst immaterial {player}"); AddBstTogglePlayer(c, "Invincible (.bst invincible)", ".bst invincible {player}"); AddBstTogglePlayer(c, "Shrouded (.bst shrouded)", ".bst shrouded {player}"); AddBstTogglePlayer(c, "Sun invulnerable (.bst suninvulnerable)", ".bst suninvulnerable {player}"); AddBstTogglePlayer(c, "Show state (.bst state)", ".bst state {player}"); } private static void AddBstValueForm(GameObject c, string title, string template, string valueTooltip, int defaultValue) { CollapsibleSection.Build(c, title, startExpanded: false, delegate(GameObject cc) { FormBuilder.Build(cc, title, template, new IntField("value", "Value", 0, 1000000, valueTooltip + $" Try {defaultValue} for parity with the chat default."), new TextField("player", "Player (blank for self)")); }, valueTooltip); } private static void AddBstTogglePlayer(GameObject c, string title, string template) { CollapsibleSection.Build(c, title, startExpanded: false, delegate(GameObject cc) { FormBuilder.Build(cc, title, template, new TextField("player", "Player (blank for self)")); }, "Toggle on the target (blank for self)."); } private void BuildKindredAdminServerTab(GameObject page) { //IL_0986: Unknown result type (might be due to invalid IL or missing references) RenderAdminInfoNote(page, "Kindred admin (Server)"); AddAdminWarningIntro(page, "Server-wide admin commands. These affect global config, all players, or persistent server state. Requires KindredCommands + server admin permission."); AddSpacer(page, 4); AddSectionHeading(page, "Global"); GameObject parent = AddKLRow(page, "KCAServerGen1"); AddCommandButton(parent, "Reveal map (all)", ".revealmapforallplayers", "Reveal the map for every player on the server (.revealmapforallplayers)."); AddCommandButton(parent, "Clean lost shards", ".cleancontainerlessshards", "Destroy all items that aren't currently in containers (.cleancontainerlessshards)."); AddCommandButton(parent, "Daywalker (all)", ".everyonedaywalker", "Toggle daywalker (sun immunity) for every player (.everyonedaywalker)."); AddCommandButton(parent, "Bat vision (all)", ".globalbatvision", "Toggle bat vision for every player (.globalbatvision)."); CollapsibleSection.Build(page, "Set time of day (.settime)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set time", ".settime {day} {hour}", new IntField("day", "Day", 0, 9999), new IntField("hour", "Hour", 0, 23)); }, "Set the game world's time to a specific day and hour."); CollapsibleSection.Build(page, "Force respawn (.forcerespawn)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Force respawn", ".forcerespawn {range}", new IntField("range", "Range", 1, 200, "Radius in world units. Default 10.")); }, "Force spawn chains (NPCs, resources) to respawn in a radius around you."); AddSpacer(page, 6); AddSectionHeading(page, "Announcements"); AddCommandButton(AddKLRow(page, "KCAAnnounce"), "List", ".announce list", "List scheduled announcements (.announce list)."); CollapsibleSection.Build(page, "Add announcement (.announce add)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Add announcement", ".announce add {name} {message} {time} {oneTime}", new TextField("name", "Name", "", "Unique announcement key."), new TextField("message", "Message", "", "Message text to post."), new TextField("time", "Time/interval", "", "Schedule expression - see KindredCommands docs."), new BoolField("oneTime", "One time only")); }, "Schedule a new chat announcement."); CollapsibleSection.Build(page, "Change announcement (.announce change)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Change announcement", ".announce change {name} {message} {time} {oneTime}", new TextField("name", "Name"), new TextField("message", "Message"), new TextField("time", "Time/interval"), new BoolField("oneTime", "One time only")); }, "Update an existing announcement by name."); CollapsibleSection.Build(page, "Remove announcement (.announce remove)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Remove announcement", ".announce remove {name}", new TextField("name", "Name")); }, "Remove an announcement by name."); AddSpacer(page, 6); AddSectionHeading(page, "Dropped items"); GameObject parent2 = AddKLRow(page, "KCADrops"); AddCommandButton(parent2, "Clear All", ".dropitems clearall", "Clear every dropped item in the world (.dropitems clearall)."); AddCommandButton(parent2, "Clear All Shards", ".dropitems clearallshards", "Clear every dropped soulshard in the world (.dropitems clearallshards)."); AddCommandButton(parent2, "Remove Lifetime", ".dropitems removelifetime", "Remove the lifetime cap on dropped items (.dropitems removelifetime)."); CollapsibleSection.Build(page, "Set drop lifetime (.dropitems lifetime)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set lifetime", ".dropitems lifetime {seconds}", new IntField("seconds", "Seconds", 0, 86400)); }, "Set how long dropped items persist (in seconds)."); CollapsibleSection.Build(page, "Set drop lifetime when disabled (.dropitems lifetimewhendisabled)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set fallback lifetime", ".dropitems lifetimewhendisabled {seconds}", new IntField("seconds", "Seconds", 0, 86400, "Default 300.")); }, "Lifetime applied while the dropped-item system is disabled."); CollapsibleSection.Build(page, "Set shard lifetime (.dropitems shardlifetime)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set shard lifetime", ".dropitems shardlifetime {seconds}", new IntField("seconds", "Seconds", 0, 86400)); }, "Set how long dropped soulshards persist (default 3600)."); CollapsibleSection.Build(page, "Clear drops in radius (.dropitems clear)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Clear radius", ".dropitems clear {radius}", new IntField("radius", "Radius", 1, 500)); }, "Clear dropped items within a radius around you."); CollapsibleSection.Build(page, "Clear shards in radius (.dropitems clearshards)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Clear shards radius", ".dropitems clearshards {radius}", new IntField("radius", "Radius", 1, 500)); }, "Clear dropped soulshards within a radius around you."); AddSpacer(page, 6); AddSectionHeading(page, "Regions"); AddCommandButton(AddKLRow(page, "KCARegion"), "List Players", ".region listplayers", "List all players currently allowed to enter gated regions (.region listplayers)."); AddSimpleTextCollapse(page, "Lock region (.region lock)", ".region lock {region}", "region", "Region name"); AddSimpleTextCollapse(page, "Unlock region (.region unlock)", ".region unlock {region}", "region", "Region name"); CollapsibleSection.Build(page, "Gate region (.region gate)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Gate region", ".region gate {region} {level}", new TextField("region", "Region"), new IntField("level", "Level", 0, 100)); }, "Gate a region behind a player level requirement."); AddSimpleTextCollapse(page, "Ungate region (.region ungate)", ".region ungate {region}", "region", "Region name"); AddSimpleTextCollapse(page, "Allow player (.region allow)", ".region allow {player}", "player", "Player name"); CollapsibleSection.Build(page, "Ban player from region (.region ban)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Ban from region", ".region ban {player} {region}", new PlayerNameField("player", "Player"), new TextField("region", "Region")); }, "Ban a player from a specific region."); CollapsibleSection.Build(page, "Unban player from region (.region unban)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Unban from region", ".region unban {player} {region}", new PlayerNameField("player", "Player"), new TextField("region", "Region")); }, "Lift a region ban on a player."); AddSimpleTextCollapse(page, "List region bans (.region listbans)", ".region listbans {region}", "region", "Region name"); AddSimpleTextCollapse(page, "Remove from allowlist (.region remove)", ".region remove {player}", "player", "Player name"); AddSpacer(page, 6); AddSectionHeading(page, "Bosses (lock)"); AddSimpleTextCollapse(page, "Lock boss (.boss lock)", ".boss lock {boss}", "boss", "V Blood boss name"); AddSimpleTextCollapse(page, "Unlock boss (.boss unlock)", ".boss unlock {boss}", "boss", "V Blood boss name"); AddSimpleTextCollapse(page, "Lock primal (.boss lockprimal)", ".boss lockprimal {boss}", "boss", "Primal boss name"); AddSimpleTextCollapse(page, "Unlock primal (.boss unlockprimal)", ".boss unlockprimal {boss}", "boss", "Primal boss name"); AddSpacer(page, 6); AddSectionHeading(page, "Gear (server)"); GameObject parent3 = AddKLRow(page, "KCAGearServer1"); AddCommandButton(parent3, "Headgear loss", ".gear headgear", "Toggle whether players lose headgear on death (.gear headgear)."); AddCommandButton(parent3, "Shard flight", ".gear soulshardflight", "Toggle soulshard flight restrictions (.gear soulshardflight)."); AddCommandButton(parent3, "Shard drop mgmt", ".gear togglesoulsharddropmanagement", "Toggle KC-managed soulshard drops (.gear togglesoulsharddropmanagement)."); AddCommandButton(parent3, "Destroy shards", ".gear destroyallshards", "Destroy every soulshard currently in the world (.gear destroyallshards)."); CollapsibleSection.Build(page, "Shard drop limit (.gear soulshardlimit)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set shard limit", ".gear soulshardlimit {limit} {shardType}", new IntField("limit", "Limit", 0, 99), new TextField("shardType", "Shard type", "", "Type filter - blank or 'None' for all types.")); }, "Set how many soulshards of a given type can exist before drops stop."); CollapsibleSection.Build(page, "Shard durability time (.gear soulsharddurabilitytime)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set shard durability time", ".gear soulsharddurabilitytime {seconds}", new IntField("seconds", "Seconds", 1, 86400)); }, "Set how long a soulshard lasts before its durability ticks down."); AddSpacer(page, 6); AddSectionHeading(page, "Clans"); CollapsibleSection.Build(page, "Rename clan (.clan rename)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Rename clan", ".clan rename {oldClanName} {newClanName} {leaderName}", new TextField("oldClanName", "Old clan name"), new TextField("newClanName", "New clan name"), new TextField("leaderName", "Leader name (optional)", "", "Leave blank if not changing leadership.")); }, "Admin-rename a clan, optionally specifying the leader."); AddSpacer(page, 6); AddSectionHeading(page, "Prisoners"); CollapsibleSection.Build(page, "Set gruel transform chance (.prisoner gruel)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set gruel", ".prisoner gruel {chance} {min} {max}", new TextField("chance", "Chance (0-1)"), new TextField("min", "Min value"), new TextField("max", "Max value")); }, "Adjust the chance and rate window of gruel transformations."); CollapsibleSection.Build(page, "Set gruel transform prefab (.prisoner grueltransform)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set gruel transform", ".prisoner grueltransform {prefab}", new TextField("prefab", "Unit prefab")); }, "Set the unit prefab a gruel-transformed prisoner becomes."); CollapsibleSection.Build(page, "Set prisoner feed settings (.prisoner feed)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set feed", ".prisoner feed {feed} {healthMin} {healthMax} {miseryMin} {miseryMax} {qualityMin} {qualityMax}", new TextField("feed", "Feed name"), new TextField("healthMin", "Health min"), new TextField("healthMax", "Health max"), new TextField("miseryMin", "Misery min"), new TextField("miseryMax", "Misery max"), new TextField("qualityMin", "Quality min"), new TextField("qualityMax", "Quality max")); }, "Adjust the per-feed ranges of health / misery / blood-quality changes."); CollapsibleSection.Build(page, "Restore feed defaults (.prisoner feeddefault)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Restore feed defaults", ".prisoner feeddefault {feed}", new TextField("feed", "Feed name")); }, "Reset a feed to its default config."); AddSpacer(page, 6); AddSectionHeading(page, "Staff"); GameObject parent4 = AddKLRow(page, "KCAStaff"); AddCommandButton(parent4, "Reload staff", ".staff reloadstaff", "Reload the staff config from disk (.staff reloadstaff)."); AddCommandButton(parent4, "Reload admin list", ".staff reloadadmin", "Reload the admin list (.staff reloadadmin)."); AddCommandButton(parent4, "Auto admin auth", ".staff autoadminauth", "Toggle whether you are on the auto-admin-auth list (.staff autoadminauth)."); CollapsibleSection.Build(page, "Set staff rank (.staff setstaff)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Set staff", ".staff setstaff {player} {rank}", new PlayerNameField("player", "Player"), new TextField("rank", "Rank")); }, "Assign a staff rank to a player."); CollapsibleSection.Build(page, "Remove staff rank (.staff removestaff)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Remove staff", ".staff removestaff {player}", new PlayerNameField("player", "Player")); }, "Remove a player's staff rank."); CollapsibleSection.Build(page, "Toggle admin (.staff toggleadmin)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Toggle admin", ".staff toggleadmin {player}", new PlayerNameField("player", "Player")); }, "Add or remove a player from the admin list."); AddSpacer(page, 6); AddSectionHeading(page, "Server wipe (DESTRUCTIVE)"); LabelRef labelRef = UIFactory.CreateLabel(page, "WipeNote", "Wipe is a 3-step flow: (1) .wipe sets up the exclusion list (territories that survive), (2) .commencewipe ACTUALLY wipes, (3) .cancelwipe aborts before commence. There is no undo once you commence.", (TextAlignmentOptions)257, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 36, flexibleWidth: num, preferredHeight: 48, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; CollapsibleSection.Build(page, "Step 1: prepare wipe (.wipe)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Prepare wipe", ".wipe {territories}", new TextField("territories", "Excluded territory IDs", "12,34,56", "Comma-separated territory IDs that should NOT be wiped.")); }, "Queue the wipe and tell the server which territory IDs to EXCLUDE (those keep their owners). Run .commencewipe afterwards to actually fire."); CollapsibleSection.Build(page, "Step 2: commence wipe (.commencewipe) — DESTRUCTIVE", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Commence wipe", ".commencewipe", new BoolField("confirm", "Yes, perform the wipe NOW", defaultValue: false, "Required. The confirm checkbox + Submit click is the only safety between you and a wiped server.", requireTrue: true)); }, "Actually performs the wipe queued by .wipe. There is no undo. Requires the confirm checkbox."); GameObject obj = UIFactory.CreateHorizontalGroup(page, "WipeCancelRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj, "Cancel queued wipe", ".cancelwipe", "Aborts a wipe queued via .wipe before .commencewipe is called."); } private void BuildKindredAdminWorldTab(GameObject page) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_07a4: Unknown result type (might be due to invalid IL or missing references) RenderAdminInfoNote(page, "Kindred admin (World)"); AddAdminWarningIntro(page, "World manipulation commands - spawn units, teleport, search prefabs, manage servants and castles. Acts on the world around you (or supplied coordinates). Requires KindredCommands + server admin permission."); AddSpacer(page, 4); AddSectionHeading(page, "Position"); AddCommandButton(AddKLRow(page, "KCAWorldPos"), "Where am I", ".whereami", "Print your current position and territory (.whereami)."); CollapsibleSection.Build(page, "Teleport horses to me (.teleporthorse)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Teleport horses", ".teleporthorse {radius}", new IntField("radius", "Radius", 1, 500, "Default 5.")); }, "Teleport all horses within radius to your position."); AddSpacer(page, 6); AddSectionHeading(page, "Lookups (find IDs for spawn / give)"); LabelRef labelRef = UIFactory.CreateLabel(page, "LookupsHint", "Use these to find the prefab name / ID for an item, NPC, or boss before using the Spawn or Give forms below. Replies appear in chat — V Rising's prefab registry isn't available client-side, so the search runs on the server and KindredCommands lists matching results.", (TextAlignmentOptions)257, null, Theme.ScaledUI(11)); GameObject gameObject = labelRef.GameObject; int? minWidth = 360; int? preferredWidth = 400; int? num = 1; UIFactory.SetLayoutElement(minHeight: 40, flexibleWidth: num, preferredHeight: 56, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)2; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; GameObject obj = UIFactory.CreateHorizontalGroup(page, "LookupsRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth2 = 360; int? preferredWidth2 = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); AddCommandButton(obj, "List All Bosses", ".boss list", "List every boss prefab the server knows about (.boss list). Reply in chat. Use one of the names with the boss-modify / lock / teleportto forms or the spawn forms below."); CollapsibleSection.Build(page, "Search items (.search item)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Search items", ".search item {query} {page}", new TextField("query", "Search text", "iron", "Substring of the item name. Case-insensitive."), new IntField("page", "Page", 1, 99, "Pagination — start at 1, increment if results say there's more.")); }, "Search items by name fragment. Reply lists every match with its prefab name — copy the name (e.g. 'Item_Ingredient_Iron') and paste into the .give form on the Players admin tab to spawn it. Page defaults to 1; KindredCommands paginates if there are many matches."); CollapsibleSection.Build(page, "Search NPCs / creatures (.search npc)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Search NPCs", ".search npc {query} {page}", new TextField("query", "Search text", "wolf", "Substring of the unit name. Case-insensitive."), new IntField("page", "Page", 1, 99)); }, "Search NPC + creature prefabs by name fragment. Reply lists every match — copy a 'CHAR_'-prefixed name and use it with .spawnnpc / .customspawn / .customspawnat / .servant change / .despawnnpc."); AddSpacer(page, 6); AddSectionHeading(page, "Spawn"); CollapsibleSection.Build(page, "Spawn NPC (.spawnnpc)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Spawn NPC", ".spawnnpc {unit} {count} {level}", new TextField("unit", "Unit prefab", "", "CHAR_-prefixed prefab name. Use .search npc to find one."), new IntField("count", "Count", 1, 50), new IntField("level", "Level (-1 default)", -1, 200)); }, "Spawn a number of NPCs at your position."); CollapsibleSection.Build(page, "Custom spawn at me (.customspawn)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Custom spawn", ".customspawn {unit} {type} {quality} {consumable} {duration} {level}", new TextField("unit", "Unit prefab"), new EnumField("type", "Blood type", PlayerStateService.BloodType.Warrior), new IntField("quality", "Blood quality", 0, 100), new BoolField("consumable", "Consumable", defaultValue: true), new IntField("duration", "Duration (-1 infinite)", -1, 86400), new IntField("level", "Level (0 default)", 0, 200)); }, "Spawn an NPC at your position with full customization."); CollapsibleSection.Build(page, "Custom spawn at coords (.customspawnat)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Custom spawn at coords", ".customspawnat {unit} {x} {y} {z} {type} {quality} {consumable} {duration} {level}", new TextField("unit", "Unit prefab"), new TextField("x", "X"), new TextField("y", "Y"), new TextField("z", "Z"), new EnumField("type", "Blood type", PlayerStateService.BloodType.Warrior), new IntField("quality", "Blood quality", 0, 100), new BoolField("consumable", "Consumable", defaultValue: true), new IntField("duration", "Duration (-1 infinite)", -1, 86400), new IntField("level", "Level", 0, 200)); }, "Spawn an NPC at specific world coordinates with full customization."); CollapsibleSection.Build(page, "Despawn NPCs (.despawnnpc)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Despawn NPC", ".despawnnpc {unit} {radius}", new TextField("unit", "Unit prefab"), new IntField("radius", "Radius", 1, 200, "Default 25.")); }, "Despawn NPCs of a given prefab within a radius."); CollapsibleSection.Build(page, "Spawn horse (.spawnhorse)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Spawn horse", ".spawnhorse {speed} {acceleration} {rotation} {num}", new TextField("speed", "Speed"), new TextField("acceleration", "Acceleration"), new TextField("rotation", "Rotation speed"), new IntField("num", "Count", 1, 20)); }, "Spawn one or more horses with custom stats."); CollapsibleSection.Build(page, "Ban unit from spawning (.spawnban)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Spawn ban", ".spawnban {unit} {reason}", new TextField("unit", "Unit prefab"), new TextField("reason", "Reason")); }, "Prevent a unit prefab from spawning naturally, with a reason logged."); AddSpacer(page, 6); AddSectionHeading(page, "Bosses (world)"); CollapsibleSection.Build(page, "Modify boss level (.boss modify)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Modify boss", ".boss modify {boss} {level}", new TextField("boss", "Boss name"), new IntField("level", "Level", 1, 200)); }, "Set the level of the nearest V Blood boss with the given name."); CollapsibleSection.Build(page, "Modify primal boss level (.boss modifyprimal)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Modify primal boss", ".boss modifyprimal {boss} {level}", new TextField("boss", "Boss name"), new IntField("level", "Level", 1, 200)); }, "Set the level of the nearest primal boss with the given name."); CollapsibleSection.Build(page, "Teleport to boss (.boss teleportto)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Teleport to boss", ".boss teleportto {boss} {whichOne}", new TextField("boss", "Boss name"), new IntField("whichOne", "Index (0 default)", 0, 50)); }, "Teleport yourself to a named boss."); AddSpacer(page, 6); AddSectionHeading(page, "Castles"); GameObject parent = AddKLRow(page, "KCACastle"); AddCommandButton(parent, "Relocate Reset", ".relocatereset", "Clear the relocation cooldown on the current castle (.relocatereset)."); AddCommandButton(parent, "Incoming Decay", ".castle incomingdecay", "List territories with the least castle time remaining (.castle incomingdecay)."); AddCommandButton(parent, "Freeze Heart", ".castle freezeheart", "Freeze the castle heart you are targeting (stops decay) (.castle freezeheart)."); AddCommandButton(parent, "Thaw Heart", ".castle thawheart", "Resume decay on the castle heart you are targeting (.castle thawheart)."); CollapsibleSection.Build(page, "Claim castle (.claim)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Claim castle", ".claim {player}", new TextField("player", "Player (blank for self)")); }, "Claim the castle heart you are targeting for the specified player (self if blank)."); CollapsibleSection.Build(page, "List plots owned (.castle plotsowned)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Plots owned", ".castle plotsowned {page}", new IntField("page", "Page", 1, 99)); }, "List castle plots owned by each player."); CollapsibleSection.Build(page, "Frozen hearts (.castle frozenhearts)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Frozen hearts", ".castle frozenhearts {page}", new IntField("page", "Page", 1, 99)); }, "List frozen (non-decaying) castle hearts."); CollapsibleSection.Build(page, "Clan plots owned (.castle clanplotsowned)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Clan plots owned", ".castle clanplotsowned {page}", new IntField("page", "Page", 1, 99)); }, "List castle plots owned by each clan."); CollapsibleSection.Build(page, "Teleport to territory plot (.castle teleporttoplot)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Teleport to plot", ".castle teleporttoplot {territoryIndex}", new IntField("territoryIndex", "Territory index", 0, 9999)); }, "Teleport to the castle heart of a specific territory."); CollapsibleSection.Build(page, "Plot info (.castle plotinfo)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Plot info", ".castle plotinfo {territoryIndex}", new IntField("territoryIndex", "Territory index", 0, 9999)); }, "Print detailed info about a territory's plot."); AddSpacer(page, 6); AddSectionHeading(page, "Servants"); GameObject parent2 = AddKLRow(page, "KCAServant1"); AddCommandButton(parent2, "Convert", ".servant convert", "Instantly convert the servant in the targeted coffin (.servant convert)."); AddCommandButton(parent2, "Perfect", ".servant perfect", "Make the targeted servant perfect (max expertise) (.servant perfect)."); AddCommandButton(parent2, "Heal", ".servant heal", "Cure the targeted servant of injuries (.servant heal)."); AddCommandButton(parent2, "Revive", ".servant revive", "Revive the servant in the targeted coffin (.servant revive)."); AddCommandButton(AddKLRow(page, "KCAServant2"), "Complete Mission", ".servant completemission", "Complete the active mission on the targeted servant (.servant completemission)."); CollapsibleSection.Build(page, "Change servant in coffin (.servant change)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Change servant", ".servant change {character}", new TextField("character", "Unit prefab")); }, "Replace the servant in the targeted coffin with a different unit prefab."); CollapsibleSection.Build(page, "Add servant to coffin (.servant add)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Add servant", ".servant add {character}", new TextField("character", "Unit prefab")); }, "Add a servant to the targeted empty coffin."); AddSpacer(page, 6); AddSectionHeading(page, "Gear (radius)"); CollapsibleSection.Build(page, "Repair all gear in radius (.gear repairall)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Repair all in radius", ".gear repairall {range}", new IntField("range", "Range", 1, 200, "Default 10.")); }, "Repair every piece of gear worn by any player in the radius."); CollapsibleSection.Build(page, "Break all gear in radius (.gear breakall)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Break all in radius", ".gear breakall {range}", new IntField("range", "Range", 1, 200, "Default 10.")); }, "Drop the gear durability of all players in the radius to zero."); AddSpacer(page, 6); AddSectionHeading(page, "Castle & Clan"); GameObject obj2 = UIFactory.CreateHorizontalGroup(page, "CastleClanRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: true, childControlHeight: true, 6, new Vector4(0f, 0f, 0f, 0f)); int? minWidth3 = 360; preferredWidth = 400; num = 1; UIFactory.SetLayoutElement(minHeight: 32, flexibleWidth: num, preferredHeight: 32, gameObject: obj2, minWidth: minWidth3, flexibleHeight: 0, preferredWidth: preferredWidth); AddCommandButton(obj2, "Longest offline", ".longestofflinecastles", "List castles ranked by owner-last-online (.longestofflinecastles). Helps clean up dead bases."); AddCommandButton(obj2, "Repair clan data", ".clan fix", "Repair clan-data inconsistencies (.clan fix). Use if a player shows in a clan they're not actually in."); CollapsibleSection.Build(page, "List castles owned by a clan (.clan castles)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Clan castles", ".clan castles {clan}", new TextField("clan", "Clan name")); }, "List the territories a specific clan owns. Reply appears in chat."); AddSpacer(page, 6); AddSectionHeading(page, "Bloodbound items"); CollapsibleSection.Build(page, "Add Bloodbound attribute (.bloodbound add)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Add bloodbound", ".bloodbound add {item}", new TextField("item", "Item prefab/name")); }, "Marks the named/prefab item as Bloodbound (drops only to its owner, etc.)."); CollapsibleSection.Build(page, "Remove Bloodbound attribute (.bloodbound remove)", startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, "Remove bloodbound", ".bloodbound remove {item}", new TextField("item", "Item prefab/name")); }, "Removes the Bloodbound flag from the item."); } private static void AddAdminWarningIntro(GameObject page, string body) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) GameObject obj = UIFactory.CreateVerticalGroup(page, "AdminIntroCard", forceWidth: true, forceHeight: false, childControlWidth: true, childControlHeight: true, 4, new Vector4(8f, 8f, 10f, 10f), Theme.CardBackground); UIFactory.SetLayoutElement(obj, 360, preferredWidth: 400, flexibleWidth: 1, minHeight: 28, flexibleHeight: 0); LabelRef labelRef = UIFactory.CreateLabel(obj, "AdminIntro", $"{body}", (TextAlignmentOptions)257, null, Theme.ScaledUI(13)); GameObject gameObject2 = labelRef.GameObject; int? minWidth2 = 320; int? preferredWidth2 = 380; int? num = 1; UIFactory.SetLayoutElement(minHeight: 36, flexibleWidth: num, preferredHeight: 44, gameObject: gameObject2, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = true; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)0; } private static void AddSimpleTextCollapse(GameObject page, string title, string template, string fieldName, string fieldLabel) { CollapsibleSection.Build(page, title, startExpanded: false, delegate(GameObject c) { FormBuilder.Build(c, title, template, new TextField(fieldName, fieldLabel)); }, title); } } public class ProfessionOverlayPanel : ResizeablePanelBase { private LabelRef _enchantingLabel; private LabelRef _alchemyLabel; private LabelRef _harvestingLabel; private LabelRef _blacksmithingLabel; private LabelRef _tailoringLabel; private LabelRef _woodcuttingLabel; private LabelRef _miningLabel; private LabelRef _fishingLabel; private GameObject _enchantingBar; private GameObject _alchemyBar; private GameObject _harvestingBar; private GameObject _blacksmithingBar; private GameObject _tailoringBar; private GameObject _woodcuttingBar; private GameObject _miningBar; private GameObject _fishingBar; private RectTransform _enchantingFill; private RectTransform _alchemyFill; private RectTransform _harvestingFill; private RectTransform _blacksmithingFill; private RectTransform _tailoringFill; private RectTransform _woodcuttingFill; private RectTransform _miningFill; private RectTransform _fishingFill; private bool _subscribed; public override string PanelId => "ProfessionOverlay"; public override PanelType PanelType => PanelType.ProfessionOverlay; public override int MinWidth => 260; public override int MinHeight => 220; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2((0f - base.Owner.Scaler.m_ReferenceResolution.x) * 0.5f, base.Owner.Scaler.m_ReferenceResolution.y * 0.5f - 240f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.ProfessionOverlayTransparency); public override bool UsesCustomBackgroundColor => true; public ProfessionOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { base.ConstructPanelContent(); AddHeader(); _enchantingLabel = AddRow("ProfEnchanting", "Enchanting —"); _enchantingBar = AddBar("ProfEnchantingBar", out _enchantingFill); _alchemyLabel = AddRow("ProfAlchemy", "Alchemy —"); _alchemyBar = AddBar("ProfAlchemyBar", out _alchemyFill); _harvestingLabel = AddRow("ProfHarvesting", "Harvesting —"); _harvestingBar = AddBar("ProfHarvestingBar", out _harvestingFill); _blacksmithingLabel = AddRow("ProfBlacksmithing", "Blacksmithing —"); _blacksmithingBar = AddBar("ProfBlacksmithingBar", out _blacksmithingFill); _tailoringLabel = AddRow("ProfTailoring", "Tailoring —"); _tailoringBar = AddBar("ProfTailoringBar", out _tailoringFill); _woodcuttingLabel = AddRow("ProfWoodcutting", "Woodcutting —"); _woodcuttingBar = AddBar("ProfWoodcuttingBar", out _woodcuttingFill); _miningLabel = AddRow("ProfMining", "Mining —"); _miningBar = AddBar("ProfMiningBar", out _miningFill); _fishingLabel = AddRow("ProfFishing", "Fishing —"); _fishingBar = AddBar("ProfFishingBar", out _fishingFill); Render(PlayerStateService.Profession); if (!_subscribed) { PlayerStateService.ProfessionChanged += OnProfessionChanged; _subscribed = true; } } private void AddHeader() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, "ProfHeader", "Professions", Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(15)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: 22, flexibleWidth: num, preferredHeight: 24, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = (FontStyles)3; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; } private LabelRef AddRow(string name, string text) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, name, text, Theme.OverlayMidlineAlignment(), null, Theme.ScaledOverlay(13)); GameObject gameObject = labelRef.GameObject; int? minWidth = 240; int? preferredWidth = 260; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; return labelRef; } private GameObject AddBar(string name, out RectTransform fillRect) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) GameObject obj = MiniBar.Create(base.ContentRoot, name, out fillRect, new Color(0.95f, 0.75f, 0.35f, 0.95f), 10); obj.SetActive(false); return obj; } private void OnProfessionChanged() { Render(PlayerStateService.Profession); } public void Refresh() { Render(PlayerStateService.Profession); } private void Render(PlayerStateService.ProfessionState s) { if (_enchantingLabel != null) { bool showProfessionEnchanting = Settings.ShowProfessionEnchanting; bool showProfessionAlchemy = Settings.ShowProfessionAlchemy; bool showProfessionHarvesting = Settings.ShowProfessionHarvesting; bool showProfessionBlacksmithing = Settings.ShowProfessionBlacksmithing; bool showProfessionTailoring = Settings.ShowProfessionTailoring; bool showProfessionWoodcutting = Settings.ShowProfessionWoodcutting; bool showProfessionMining = Settings.ShowProfessionMining; bool showProfessionFishing = Settings.ShowProfessionFishing; SetRowActive(_enchantingLabel, showProfessionEnchanting); SetRowActive(_alchemyLabel, showProfessionAlchemy); SetRowActive(_harvestingLabel, showProfessionHarvesting); SetRowActive(_blacksmithingLabel, showProfessionBlacksmithing); SetRowActive(_tailoringLabel, showProfessionTailoring); SetRowActive(_woodcuttingLabel, showProfessionWoodcutting); SetRowActive(_miningLabel, showProfessionMining); SetRowActive(_fishingLabel, showProfessionFishing); if (showProfessionEnchanting) { ((TMP_Text)_enchantingLabel.TextMesh).text = FormatRow("Enchanting", s.EnchantingLevel, s.EnchantingProgress); } if (showProfessionAlchemy) { ((TMP_Text)_alchemyLabel.TextMesh).text = FormatRow("Alchemy", s.AlchemyLevel, s.AlchemyProgress); } if (showProfessionHarvesting) { ((TMP_Text)_harvestingLabel.TextMesh).text = FormatRow("Harvesting", s.HarvestingLevel, s.HarvestingProgress); } if (showProfessionBlacksmithing) { ((TMP_Text)_blacksmithingLabel.TextMesh).text = FormatRow("Blacksmithing", s.BlacksmithingLevel, s.BlacksmithingProgress); } if (showProfessionTailoring) { ((TMP_Text)_tailoringLabel.TextMesh).text = FormatRow("Tailoring", s.TailoringLevel, s.TailoringProgress); } if (showProfessionWoodcutting) { ((TMP_Text)_woodcuttingLabel.TextMesh).text = FormatRow("Woodcutting", s.WoodcuttingLevel, s.WoodcuttingProgress); } if (showProfessionMining) { ((TMP_Text)_miningLabel.TextMesh).text = FormatRow("Mining", s.MiningLevel, s.MiningProgress); } if (showProfessionFishing) { ((TMP_Text)_fishingLabel.TextMesh).text = FormatRow("Fishing", s.FishingLevel, s.FishingProgress); } bool showProgressBarProfessions = Settings.ShowProgressBarProfessions; SyncBar(_enchantingBar, _enchantingFill, s.EnchantingProgress, showProgressBarProfessions && showProfessionEnchanting); SyncBar(_alchemyBar, _alchemyFill, s.AlchemyProgress, showProgressBarProfessions && showProfessionAlchemy); SyncBar(_harvestingBar, _harvestingFill, s.HarvestingProgress, showProgressBarProfessions && showProfessionHarvesting); SyncBar(_blacksmithingBar, _blacksmithingFill, s.BlacksmithingProgress, showProgressBarProfessions && showProfessionBlacksmithing); SyncBar(_tailoringBar, _tailoringFill, s.TailoringProgress, showProgressBarProfessions && showProfessionTailoring); SyncBar(_woodcuttingBar, _woodcuttingFill, s.WoodcuttingProgress, showProgressBarProfessions && showProfessionWoodcutting); SyncBar(_miningBar, _miningFill, s.MiningProgress, showProgressBarProfessions && showProfessionMining); SyncBar(_fishingBar, _fishingFill, s.FishingProgress, showProgressBarProfessions && showProfessionFishing); } } private static void SetRowActive(LabelRef row, bool active) { if (row != null && !((Object)(object)row.GameObject == (Object)null) && row.GameObject.activeSelf != active) { row.GameObject.SetActive(active); } } private static void SyncBar(GameObject bar, RectTransform fill, float progress, bool show) { if (!((Object)(object)bar == (Object)null)) { if (bar.activeSelf != show) { bar.SetActive(show); } if (show) { MiniBar.SetProgress(fill, progress); } } } private static string FormatRow(string name, int level, float progress) { return $"{name}: Lv {level} ({progress * 100f:0.#}%)"; } internal override void Reset() { if (_subscribed) { PlayerStateService.ProfessionChanged -= OnProfessionChanged; _subscribed = false; } } } public class ShiftSpellOverlayPanel : ResizeablePanelBase { private GameObject _tileGo; private Image _tileBg; private Image _tileRadial; private TextMeshProUGUI _tileText; private TextMeshProUGUI _labelText; private TextMeshProUGUI _diagText; private Action _ticker; private static Sprite _whiteSprite; private static readonly Color TILE_BASE_READY = new Color(0.32f, 0.55f, 0.85f, 0.95f); private static readonly Color TILE_BASE_BUSY = new Color(0.18f, 0.24f, 0.36f, 0.95f); private static readonly Color RADIAL_OVERLAY = new Color(0.04f, 0.04f, 0.06f, 0.78f); public override string PanelId => "ShiftSpellOverlay"; public override PanelType PanelType => PanelType.ShiftSpellOverlay; public override int MinWidth => 140; public override int MinHeight => 120; public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f); public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPivot => new Vector2(0.5f, 0.5f); public override Vector2 DefaultPosition => new Vector2(base.Owner.Scaler.m_ReferenceResolution.x * 0.5f - 220f, (0f - base.Owner.Scaler.m_ReferenceResolution.y) * 0.5f + 220f); public override bool CanDrag => true; public override PanelDragger.ResizeTypes CanResize => PanelDragger.ResizeTypes.All; public override float Opacity => Settings.TransparencyToAlpha(Settings.ShiftSpellOverlayTransparency); public override bool UsesCustomBackgroundColor => true; private static Sprite GetWhiteSprite() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_whiteSprite != (Object)null) { return _whiteSprite; } try { _whiteSprite = Sprite.Create(Texture2D.whiteTexture, new Rect(0f, 0f, (float)((Texture)Texture2D.whiteTexture).width, (float)((Texture)Texture2D.whiteTexture).height), new Vector2(0.5f, 0.5f)); } catch { _whiteSprite = null; } return _whiteSprite; } public ShiftSpellOverlayPanel(UIBase owner) : base(owner) { } protected override void ConstructPanelContent() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) base.ConstructPanelContent(); GameObject contentRoot = base.ContentRoot; Vector4 padding = new Vector4(0f, 0f, 0f, 0f); TextAnchor? childAlignment = (TextAnchor)4; GameObject val = UIFactory.CreateHorizontalGroup(contentRoot, "ShiftTileRow", forceExpandWidth: true, forceExpandHeight: false, childControlWidth: false, childControlHeight: false, 0, padding, null, childAlignment); int? minWidth = MinWidth - 12; int? preferredWidth = MinWidth - 12; int? num = 1; UIFactory.SetLayoutElement(minHeight: 84, flexibleWidth: num, preferredHeight: 84, gameObject: val, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); _tileGo = UIFactory.CreateUIObject("ShiftTile", val); _tileGo.GetComponent().sizeDelta = new Vector2(80f, 80f); GameObject tileGo = _tileGo; int? minWidth2 = 80; int? preferredWidth2 = 80; num = 0; UIFactory.SetLayoutElement(minHeight: 80, flexibleWidth: num, preferredHeight: 80, gameObject: tileGo, minWidth: minWidth2, flexibleHeight: 0, preferredWidth: preferredWidth2); _tileBg = _tileGo.AddComponent(); ((Graphic)_tileBg).color = TILE_BASE_READY; Outline obj = _tileGo.AddComponent(); ((Shadow)obj).effectColor = new Color(0.85f, 0.85f, 0.85f, 0.9f); ((Shadow)obj).effectDistance = new Vector2(1.2f, -1.2f); GameObject val2 = UIFactory.CreateUIObject("ShiftRadial", _tileGo); RectTransform component = val2.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; _tileRadial = val2.AddComponent(); ((Graphic)_tileRadial).color = RADIAL_OVERLAY; ((Graphic)_tileRadial).raycastTarget = false; Sprite whiteSprite = GetWhiteSprite(); if ((Object)(object)whiteSprite != (Object)null) { _tileRadial.sprite = whiteSprite; _tileRadial.type = (Type)3; _tileRadial.fillMethod = (FillMethod)4; _tileRadial.fillOrigin = 2; _tileRadial.fillClockwise = true; _tileRadial.fillAmount = 0f; } else { Color color = ((Graphic)_tileRadial).color; color.a = 0f; ((Graphic)_tileRadial).color = color; } GameObject val3 = UIFactory.CreateUIObject("ShiftCountdown", _tileGo); RectTransform component2 = val3.GetComponent(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; _tileText = val3.AddComponent(); ((TMP_Text)_tileText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_tileText).fontSize = Theme.ScaledOverlay(26); ((TMP_Text)_tileText).fontStyle = (FontStyles)1; ((Graphic)_tileText).color = Color.white; ((TMP_Text)_tileText).enableWordWrapping = false; ((TMP_Text)_tileText).overflowMode = (TextOverflowModes)0; ((Graphic)_tileText).raycastTarget = false; ((TMP_Text)_tileText).outlineWidth = 0.25f; ((TMP_Text)_tileText).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220); ((TMP_Text)_tileText).text = "—"; _labelText = AddCenteredLabel("ShiftLabel", "SHIFT", (FontStyles)1, Theme.ScaledOverlay(14)); ((TMP_Text)_labelText).outlineWidth = 0.18f; ((TMP_Text)_labelText).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200); if (Settings.ShiftSpellOverlayShowDiagnostics) { _diagText = AddCenteredLabel("ShiftDiag", "", (FontStyles)2, Theme.ScaledOverlay(9)); LayoutElement component3 = ((Component)_diagText).gameObject.GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.minHeight = 14f; component3.preferredHeight = 14f; } ((Graphic)_diagText).color = new Color(0.7f, 0.7f, 0.7f, 0.95f); } if (_ticker == null) { _ticker = OnTick; CoreUpdateBehavior.Actions.Add(_ticker); } } private TextMeshProUGUI AddCenteredLabel(string name, string text, FontStyles style, int fontSize) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) LabelRef labelRef = UIFactory.CreateLabel(base.ContentRoot, name, text, (TextAlignmentOptions)514, null, fontSize); GameObject gameObject = labelRef.GameObject; int? minWidth = 90; int? preferredWidth = 100; int? num = 1; UIFactory.SetLayoutElement(minHeight: 18, flexibleWidth: num, preferredHeight: 20, gameObject: gameObject, minWidth: minWidth, flexibleHeight: 0, preferredWidth: preferredWidth); ((TMP_Text)labelRef.TextMesh).fontStyle = style; ((TMP_Text)labelRef.TextMesh).enableWordWrapping = false; ((TMP_Text)labelRef.TextMesh).overflowMode = (TextOverflowModes)0; return labelRef.TextMesh; } private void OnTick() { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tileText == (Object)null) { return; } ShiftCooldownService.Tick(); if ((Object)(object)_diagText != (Object)null) { ((TMP_Text)_diagText).text = $"pf {ShiftCooldownService.DiagShiftPrefabHash} cg {ShiftCooldownService.DiagCastGroupPrefabHash} si {ShiftCooldownService.DiagCastGroupSlotIndex} end {ShiftCooldownService.DiagLatchedEnd:F1} srv {ShiftCooldownService.DiagServerNow:F1} {ShiftCooldownService.DiagLastReadSource}"; } if (!ShiftCooldownService.HasShiftSpell) { ((TMP_Text)_labelText).text = "SHIFT"; ((TMP_Text)_tileText).text = (string.IsNullOrEmpty(ShiftCooldownService.LastError) ? "—" : "n/a"); ((Graphic)_tileBg).color = TILE_BASE_BUSY; if ((Object)(object)_tileRadial != (Object)null) { _tileRadial.fillAmount = 0f; } return; } ((TMP_Text)_labelText).text = ((ShiftCooldownService.MaxCharges > 1) ? $"SHIFT {ShiftCooldownService.CurrentCharges}/{ShiftCooldownService.MaxCharges}" : "SHIFT"); float cooldownRemaining = ShiftCooldownService.CooldownRemaining; float cooldownTotal = ShiftCooldownService.CooldownTotal; bool flag = cooldownRemaining <= 0.05f; float fillAmount = ((cooldownTotal > 0.001f) ? Mathf.Clamp01(cooldownRemaining / cooldownTotal) : 0f); if ((Object)(object)_tileRadial != (Object)null) { _tileRadial.fillAmount = fillAmount; } ((Graphic)_tileBg).color = (flag ? TILE_BASE_READY : TILE_BASE_BUSY); if (flag) { ((TMP_Text)_tileText).text = "Ready"; } else if (cooldownRemaining < 10f) { ((TMP_Text)_tileText).text = $"{cooldownRemaining:0.0}s"; } else { ((TMP_Text)_tileText).text = $"{(int)cooldownRemaining}s"; } } internal override void Reset() { if (_ticker != null) { CoreUpdateBehavior.Actions.Remove(_ticker); _ticker = null; } } } } namespace BloodCraftHub.UI.ModContent.Data { public enum PanelType { Base, FamiliarsTab, BoxesTab, VBloodsTab, AllFamiliarsTab, ClassTab, ExpertiseTab, BloodLegacyTab, UnarmedShiftTab, PrestigeTab, LevelsTab, AdminTab, KindredLogisticsTab, KindredLogisticsAdminTab, KindredCommandsPlayerTab, KindredAdminPlayersTab, KindredAdminServerTab, KindredAdminWorldTab, DailyQuestTab, DailyQuestOverlay, QuickStartTab, ModHelpTab, GameGuideTab, VanillaAdminTab, AboutTab, SettingsTab, ExperienceOverlay, FamiliarOverlay, FamiliarBrowserOverlay, ProfessionOverlay, ShiftSpellOverlay, CombinedOverlay, BoxList, BoxContent, FamStats, TestPanel } } namespace BloodCraftHub.UI.Framework { public class FrameTimer { private bool _enabled; private bool _isRunning; private bool _runOnce; private DateTime _executeAfter = DateTime.MinValue; private DateTime _lastExecution = DateTime.MinValue; private TimeSpan _delay; private Action _action; private Func _delayGenerator; public TimeSpan TimeSinceLastRun => DateTime.Now - _lastExecution; public bool Enabled => _enabled; public FrameTimer Initialise(Action action, TimeSpan delay, bool runOnce = true) { _delayGenerator = null; _delay = delay; _executeAfter = DateTime.Now + delay; _action = action; _runOnce = runOnce; return this; } public FrameTimer Initialise(Action action, Func delayGenerator, bool runOnce = true) { _delayGenerator = delayGenerator; _delay = _delayGenerator(); _executeAfter = DateTime.Now + _delay; _action = action; _runOnce = runOnce; return this; } public void Start() { Refresh(); if (!_enabled) { _lastExecution = DateTime.MinValue; CoreUpdateBehavior.Actions.Add(GameFrame_OnUpdate); _enabled = true; } } public void Stop() { if (_enabled) { CoreUpdateBehavior.Actions.Remove(GameFrame_OnUpdate); _enabled = false; } } private void Refresh() { if (_delayGenerator != null) { _delay = _delayGenerator(); } _executeAfter = DateTime.Now + _delay; } private void GameFrame_OnUpdate() { Update(); } private void Update() { if (!_enabled || _isRunning || _executeAfter >= DateTime.Now) { return; } _isRunning = true; try { _action(); _lastExecution = DateTime.Now; } catch (Exception ex) { LogUtils.LogError("Timer failed " + ex.Message + "\n" + ex.StackTrace); _runOnce = true; Stop(); } finally { if (_runOnce) { Stop(); } else { Refresh(); } _isRunning = false; } } } } namespace BloodCraftHub.UI.Framework.UniverseLib.UI { public static class CoroutineUtility { private static ConcurrentBag _nextFrameRoutines = new ConcurrentBag(); private static ConcurrentBag _thisFrameRoutines = new ConcurrentBag(); public static void StartCoroutine(IEnumerator coroutine) { _nextFrameRoutines.Add(coroutine); } public static void TickRoutines() { ConcurrentBag nextFrameRoutines = _nextFrameRoutines; ConcurrentBag thisFrameRoutines = _thisFrameRoutines; _thisFrameRoutines = nextFrameRoutines; _nextFrameRoutines = thisFrameRoutines; while (!_thisFrameRoutines.IsEmpty) { if (_thisFrameRoutines.TryTake(out var result) && result.MoveNext()) { _nextFrameRoutines.Add(result); } } } } public static class Il2CppExtensions { public static void AddListener(this UnityEvent action, Action listener) { action.AddListener(UnityAction.op_Implicit(listener)); } public static void AddListener(this UnityEvent action, Action listener) { action.AddListener(UnityAction.op_Implicit(listener)); } public static void RemoveListener(this UnityEvent action, Action listener) { action.RemoveListener(UnityAction.op_Implicit(listener)); } public static void RemoveListener(this UnityEvent action, Action listener) { action.RemoveListener(UnityAction.op_Implicit(listener)); } public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) { group.childControlHeight = value; } public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) { group.childControlWidth = value; } } public class UIBase { internal static readonly int TOP_SORTORDER = 30000; public string ID { get; } public GameObject RootObject { get; } public RectTransform RootRect { get; } public Canvas Canvas { get; } public CanvasScaler Scaler { get; } public Action UpdateMethod { get; } public PanelManager Panels { get; } public bool Enabled { get { if (Object.op_Implicit((Object)(object)RootObject)) { return RootObject.activeSelf; } return false; } set { UniversalUI.SetUIActive(ID, value); } } public UIBase(string id, Action updateMethod) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(id)) { throw new ArgumentException("Cannot register a UI with a null or empty id!"); } if (UniversalUI.registeredUIs.ContainsKey(id)) { throw new ArgumentException("A UI with the id '" + id + "' is already registered!"); } ID = id; UpdateMethod = updateMethod; RootObject = UIFactory.CreateUIObject(id + "_Root", UniversalUI.CanvasRoot); RootObject.SetActive(false); RootRect = RootObject.GetComponent(); Canvas = RootObject.AddComponent(); Canvas.renderMode = (RenderMode)0; Canvas.referencePixelsPerUnit = 100f; Canvas.sortingOrder = TOP_SORTORDER; Canvas.overrideSorting = true; Scaler = RootObject.AddComponent(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor((float)Screen.width, (float)Screen.height); LogUtils.LogInfo($"Scaler ref: {val.x}x{val.y}"); Scaler.referenceResolution = val; Scaler.uiScaleMode = (ScaleMode)1; Scaler.screenMatchMode = (ScreenMatchMode)0; RootObject.AddComponent(); RectTransform component = RootObject.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.pivot = new Vector2(0.5f, 0.5f); Panels = CreatePanelManager(); RootObject.SetActive(true); UniversalUI.registeredUIs.Add(id, this); UniversalUI.uiBases.Add(this); } protected virtual PanelManager CreatePanelManager() { return new PanelManager(this); } public void SetOnTop() { RootObject.transform.SetAsLastSibling(); foreach (UIBase uiBasis in UniversalUI.uiBases) { int num = UniversalUI.CanvasRoot.transform.childCount - ((Transform)uiBasis.RootRect).GetSiblingIndex(); uiBasis.Canvas.sortingOrder = TOP_SORTORDER - num; } UniversalUI.uiBases.Sort((UIBase a, UIBase b) => b.RootObject.transform.GetSiblingIndex().CompareTo(a.RootObject.transform.GetSiblingIndex())); } internal void Update() { try { Panels.Update(); UpdateMethod?.Invoke(); } catch (Exception value) { LogUtils.LogWarning($"Exception invoking update method for {ID}: {value}"); } } } public static class UIFactory { internal static Vector2 largeElementSize = new Vector2(100f, 30f); internal static Vector2 smallElementSize = new Vector2(25f, 25f); internal static Vector2 outlineDistance = new Vector2(2f, 2f); public static GameObject PlayerHUDCanvas { get; set; } public static TMP_FontAsset Font { get; set; } public static Material FontMaterial { get; set; } public static GameObject CreateUIObject(string name, GameObject parent, Vector2 sizeDelta = default(Vector2)) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name) { layer = 5, hideFlags = (HideFlags)61 }; if (Object.op_Implicit((Object)(object)parent)) { val.transform.SetParent(parent.transform, false); } val.AddComponent().sizeDelta = sizeDelta; return val; } internal static void SetDefaultTextValues(TextMeshProUGUI text) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ((Graphic)text).color = Theme.DefaultText; ((TMP_Text)text).font = Font; ((TMP_Text)text).fontSize = 14f; } internal static void SetDefaultSelectableValues(Selectable selectable) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) Navigation navigation = selectable.navigation; navigation.mode = (Mode)4; selectable.navigation = navigation; ColorBlock val = default(ColorBlock); ((ColorBlock)(ref val)).normalColor = Theme.SelectableNormal; ((ColorBlock)(ref val)).highlightedColor = Theme.SelectableHighlighted; ((ColorBlock)(ref val)).pressedColor = Theme.SelectablePressed; ((ColorBlock)(ref val)).colorMultiplier = 1f; ColorBlock colors = val; selectable.colors = colors; } public static LayoutElement SetLayoutElement(GameObject gameObject, int? minWidth = null, int? minHeight = null, int? flexibleWidth = null, int? flexibleHeight = null, int? preferredWidth = null, int? preferredHeight = null, bool? ignoreLayout = null) { LayoutElement val = gameObject.GetComponent(); if (!Object.op_Implicit((Object)(object)val)) { val = gameObject.AddComponent(); } if (minWidth.HasValue) { val.minWidth = minWidth.Value; } if (minHeight.HasValue) { val.minHeight = minHeight.Value; } if (flexibleWidth.HasValue) { val.flexibleWidth = flexibleWidth.Value; } if (flexibleHeight.HasValue) { val.flexibleHeight = flexibleHeight.Value; } if (preferredWidth.HasValue) { val.preferredWidth = preferredWidth.Value; } if (preferredHeight.HasValue) { val.preferredHeight = preferredHeight.Value; } if (ignoreLayout.HasValue) { val.ignoreLayout = ignoreLayout.Value; } return val; } public static T SetLayoutGroup(GameObject gameObject, bool? forceWidth = null, bool? forceHeight = null, bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null, int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null) where T : HorizontalOrVerticalLayoutGroup { T val = gameObject.GetComponent(); if (!Object.op_Implicit((Object)(object)val)) { val = gameObject.AddComponent(); } return SetLayoutGroup(val, forceWidth, forceHeight, childControlWidth, childControlHeight, spacing, padTop, padBottom, padLeft, padRight, childAlignment); } public static T SetLayoutGroup(T group, bool? forceWidth = null, bool? forceHeight = null, bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null, int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null) where T : HorizontalOrVerticalLayoutGroup { //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (forceWidth.HasValue) { ((HorizontalOrVerticalLayoutGroup)group).childForceExpandWidth = forceWidth.Value; } if (forceHeight.HasValue) { ((HorizontalOrVerticalLayoutGroup)group).childForceExpandHeight = forceHeight.Value; } if (childControlWidth.HasValue) { ((HorizontalOrVerticalLayoutGroup)group).childControlWidth = childControlWidth.Value; } if (childControlHeight.HasValue) { ((HorizontalOrVerticalLayoutGroup)group).childControlHeight = childControlHeight.Value; } if (spacing.HasValue) { ((HorizontalOrVerticalLayoutGroup)group).spacing = spacing.Value; } if (padTop.HasValue) { ((LayoutGroup)(object)group).padding.top = padTop.Value; } if (padBottom.HasValue) { ((LayoutGroup)(object)group).padding.bottom = padBottom.Value; } if (padLeft.HasValue) { ((LayoutGroup)(object)group).padding.left = padLeft.Value; } if (padRight.HasValue) { ((LayoutGroup)(object)group).padding.right = padRight.Value; } if (childAlignment.HasValue) { ((LayoutGroup)(object)group).childAlignment = childAlignment.Value; } return group; } public static GameObject CreatePanel(string name, GameObject parent, out GameObject contentHolder, Color? bgColor = null, float opacity = 1f) { //IL_0004: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateUIObject(name, parent); UIFactory.SetLayoutGroup(val, (bool?)true, (bool?)true, (bool?)true, (bool?)true, (int?)null, (int?)null, (int?)null, (int?)null, (int?)null, (TextAnchor?)null); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.anchoredPosition = Vector2.zero; component.sizeDelta = Vector2.zero; contentHolder = CreateUIObject("Content", val); UIFactory.SetLayoutGroup(contentHolder, (bool?)true, (bool?)true, (bool?)true, (bool?)true, (int?)null, (int?)null, (int?)null, (int?)null, (int?)null, (TextAnchor?)null); Image obj = contentHolder.AddComponent(); obj.type = (Type)3; Color color = (Color)(((??)bgColor) ?? Theme.DarkBackground); color.a = opacity; ((Graphic)obj).color = color; Outline obj2 = contentHolder.AddComponent(); ((Shadow)obj2).effectColor = Theme.DarkBackground; ((Shadow)obj2).effectDistance = outlineDistance; return val; } public static void ApplyOpacityToPanel(GameObject panelObj, float opacity) { //IL_0037: 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_0046: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panelObj == (Object)null) { return; } Transform val = panelObj.transform.Find("Content"); if (!((Object)(object)val == (Object)null)) { Image component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null)) { Color color = ((Graphic)component).color; color.a = opacity; ((Graphic)component).color = color; } } } public static void ApplyBackgroundColorRgbToPanel(GameObject panelObj, Color rgb) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panelObj == (Object)null) { return; } foreach (Image componentsInChild in panelObj.GetComponentsInChildren(true)) { if (!((Object)(object)componentsInChild == (Object)null) && !((Object)(object)((Component)componentsInChild).gameObject.GetComponent() == (Object)null)) { Color color = ((Graphic)componentsInChild).color; if (!(color.r > 0.1f) || !(color.r < 0.4f) || !(Math.Abs(color.r - color.g) < 0.02f) || !(Math.Abs(color.g - color.b) < 0.02f) || !(Math.Abs(color.r - color.b) < 0.02f)) { ((Graphic)componentsInChild).color = new Color(rgb.r, rgb.g, rgb.b, color.a); } } } } public static void ApplyInnerBackgroundColorToPanel(GameObject panelObj, Color rgb) { //IL_0061: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panelObj == (Object)null) { return; } foreach (Image componentsInChild in panelObj.GetComponentsInChildren(true)) { if ((Object)(object)componentsInChild == (Object)null) { continue; } GameObject gameObject = ((Component)componentsInChild).gameObject; if (!((Object)(object)gameObject.GetComponent() != (Object)null)) { bool flag = (Object)(object)gameObject.GetComponent() != (Object)null; bool flag2 = (Object)(object)gameObject.GetComponent() != (Object)null; if (flag || flag2) { Color color = ((Graphic)componentsInChild).color; ((Graphic)componentsInChild).color = new Color(rgb.r, rgb.g, rgb.b, color.a); } } } } public static GameObject CreateVerticalGroup(GameObject parent, string name, bool forceWidth, bool forceHeight, bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default(Vector4), Color? bgColor = null, TextAnchor? childAlignment = null, float opacity = 1f) { //IL_0004: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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) GameObject obj = CreateUIObject(name, parent); UIFactory.SetLayoutGroup(obj, (bool?)forceWidth, (bool?)forceHeight, (bool?)childControlWidth, (bool?)childControlHeight, (int?)spacing, (int?)(int)padding.x, (int?)(int)padding.y, (int?)(int)padding.z, (int?)(int)padding.w, childAlignment); ((Graphic)obj.AddComponent()).color = (Color)(((??)bgColor) ?? Theme.PanelBackground); return obj; } public static GameObject CreateHorizontalGroup(GameObject parent, string name, bool forceExpandWidth, bool forceExpandHeight, bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default(Vector4), Color? bgColor = null, TextAnchor? childAlignment = null, float opacity = 1f) { //IL_0004: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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) GameObject obj = CreateUIObject(name, parent); UIFactory.SetLayoutGroup(obj, (bool?)forceExpandWidth, (bool?)forceExpandHeight, (bool?)childControlWidth, (bool?)childControlHeight, (int?)spacing, (int?)(int)padding.x, (int?)(int)padding.y, (int?)(int)padding.z, (int?)(int)padding.w, childAlignment); ((Graphic)obj.AddComponent()).color = (Color)(((??)bgColor) ?? Theme.PanelBackground); return obj; } public static GameObject CreateGridGroup(GameObject parent, string name, Vector2 cellSize, Vector2 spacing, Color? bgColor = null) { //IL_0004: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) GameObject obj = CreateUIObject(name, parent); GridLayoutGroup obj2 = obj.AddComponent(); ((LayoutGroup)obj2).childAlignment = (TextAnchor)0; obj2.cellSize = cellSize; obj2.spacing = spacing; ((Graphic)obj.AddComponent()).color = (Color)(((??)bgColor) ?? Theme.PanelBackground); return obj; } public static LabelRef CreateLabel(GameObject parent, string name, string defaultText, TextAlignmentOptions alignment = 514, Color? color = null, int fontSize = 14, float outlineWidth = 0.15f, Color? outlineColor = null) { //IL_0004: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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) GameObject val = CreateUIObject(name, parent); TextMeshProUGUI val2 = val.AddComponent(); ((Graphic)val2).color = (Color)(((??)color) ?? Theme.DefaultText); ((TMP_Text)val2).font = Font; ((TMP_Text)val2).text = defaultText; ((TMP_Text)val2).alignment = alignment; ((TMP_Text)val2).fontSize = fontSize; try { ((TMP_Text)val2).outlineWidth = outlineWidth; ((TMP_Text)val2).outlineColor = Color32.op_Implicit((Color)(((??)outlineColor) ?? Color.black)); } catch (Exception) { } return new LabelRef { GameObject = val, TextMesh = val2 }; } public static ButtonRef CreateButton(GameObject parent, string name, string text, Color? normalColor = null) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) Color val = (Color)(((??)normalColor) ?? Theme.SliderFill); ColorBlock val2 = default(ColorBlock); ((ColorBlock)(ref val2)).normalColor = val; ((ColorBlock)(ref val2)).highlightedColor = val * 1.2f; ((ColorBlock)(ref val2)).selectedColor = val * 1.1f; ((ColorBlock)(ref val2)).pressedColor = val * 0.7f; ((ColorBlock)(ref val2)).disabledColor = val * 0.4f; ((ColorBlock)(ref val2)).colorMultiplier = 1f; ColorBlock colors = val2; val2 = default(ColorBlock); ButtonRef buttonRef = CreateButton(parent, name, text, val2); ((Selectable)buttonRef.Component).colors = colors; return buttonRef; } public static ButtonRef CreateButton(GameObject parent, string name, string text, ColorBlock colors) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateUIObject(name, parent, smallElementSize); GameObject val2 = CreateUIObject("Text", val); Image obj = val.AddComponent(); obj.type = (Type)1; ((Graphic)obj).color = Theme.White; Outline obj2 = val.AddComponent(); ((Shadow)obj2).effectColor = Theme.DarkBackground; ((Shadow)obj2).effectDistance = outlineDistance; Button obj3 = val.AddComponent