using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("RustyLaserGaming")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Fantasy rune Viking character customization for Valheim.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("RustyHeathensRuneborn")] [assembly: AssemblyTitle("RustyHeathensRuneborn")] [assembly: AssemblyVersion("0.1.0.0")] namespace RustyHeathensRuneborn; [BepInPlugin("com.rustylasergaming.runeborn", "Rusty Heathens Runeborn", "0.4.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class RunebornPlugin : BaseUnityPlugin { public const string PluginGuid = "com.rustylasergaming.runeborn"; public const string PluginName = "Rusty Heathens Runeborn"; public const string PluginVersion = "0.4.2"; internal static ConfigEntry MenuKey; internal static ConfigEntry AdminMenuKey; internal static ConfigEntry ServerReminderEnabledEntry; internal static ConfigEntry RaidWarningsEnabledEntry; private Harmony _harmony; private bool _menuOpen; private bool _adminMenuOpen; private Rect _windowRect = new Rect(40f, 80f, 560f, 700f); private Rect _adminWindowRect = new Rect(620f, 80f, 520f, 640f); private Vector2 _scrollPosition = Vector2.zero; private Vector2 _adminScrollPosition = Vector2.zero; private GUISkin _runebornSkin; private GUIStyle _sectionHeaderStyle; private GUIStyle _subHeaderStyle; private GUIStyle _mutedLabelStyle; private GUIStyle _goodLabelStyle; private GUIStyle _warningLabelStyle; private GUIStyle _accentLabelStyle; private GUIStyle _selectedButtonStyle; private GUIStyle _lockedButtonStyle; private GUIStyle _disabledButtonStyle; private GUIStyle _closeButtonStyle; private GUIStyle _hudTitleStyle; private GUIStyle _hudLabelStyle; private readonly List _guiTextures = new List(); private bool _showAdminDiagnostics; private int _playerMenuTab; private int _adminMenuTab; private CursorLockMode _previousCursorLock; private bool _previousCursorVisible; internal static RunebornPlugin Instance { get; private set; } private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; BindConfig(); RunebornBuffStatusEffects.Register(); _harmony = new Harmony("com.rustylasergaming.runeborn"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Rusty Heathens Runeborn 0.4.2 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_runebornSkin != (Object)null) { Object.Destroy((Object)(object)_runebornSkin); _runebornSkin = null; } foreach (Texture2D guiTexture in _guiTextures) { if ((Object)(object)guiTexture != (Object)null) { Object.Destroy((Object)(object)guiTexture); } } _guiTextures.Clear(); } private void Update() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) RunebornAdminEventMessages.EnsureRegistered(); RunebornBuffSlotSyncFoundation.EnsureRegistered(); RunebornBuffSlotSyncFoundation.UpdateLateJoinRequest(); RunebornBuffAvailabilitySync.EnsureRegistered(); RunebornBuffAvailabilitySync.UpdateLateJoinRequest(); RunebornBuffStatusEffects.UpdateLocalPlayerEffects(); KeyboardShortcut value = MenuKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { ToggleMenu(); } value = AdminMenuKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (RunebornAdminTools.IsLocalAdmin()) { ToggleAdminMenu(); } else { ShowLocalCenterMessage("Admin menu is locked."); } } } private void BindConfig() { //IL_001a: 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) MenuKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "OpenPlayerHelpMenu", new KeyboardShortcut((KeyCode)289, Array.Empty()), "Open or close the Rusty Heathens player help menu."); AdminMenuKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "OpenAdminMenu", new KeyboardShortcut((KeyCode)288, Array.Empty()), "Open or close the Rusty Heathens admin menu. Admin SteamID only."); ServerReminderEnabledEntry = ((BaseUnityPlugin)this).Config.Bind("Admin Settings", "ServerReminderEnabled", true, "Admin-controlled toggle for the Rusty Heathens server reminder message."); RaidWarningsEnabledEntry = ((BaseUnityPlugin)this).Config.Bind("Admin Settings", "RaidWarningsEnabled", true, "Admin-controlled toggle for Rusty Heathens raid warning messages."); ApplyAdminSettingsToRuntime(); } internal static void ApplyAdminSettingsToRuntime() { if (ServerReminderEnabledEntry != null) { RunebornServerReminder.ServerReminderEnabled = ServerReminderEnabledEntry.Value; } if (RaidWarningsEnabledEntry != null) { RunebornRaidWarnings.RaidWarningsEnabled = RaidWarningsEnabledEntry.Value; } } private static void ShowLocalCenterMessage(string text) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } try { ((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } catch { } } private void ToggleMenu() { _menuOpen = !_menuOpen; ApplyCursorStateForOpenMenus(); } private void ToggleAdminMenu() { _adminMenuOpen = !_adminMenuOpen; ApplyCursorStateForOpenMenus(); } private void ApplyCursorStateForOpenMenus() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (_menuOpen || _adminMenuOpen) { _previousCursorLock = Cursor.lockState; _previousCursorVisible = Cursor.visible; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } else { Cursor.lockState = _previousCursorLock; Cursor.visible = _previousCursorVisible; } } private void OnGUI() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) GUISkin skin = GUI.skin; EnsureRunebornGuiSkin(); if ((Object)(object)_runebornSkin != (Object)null) { GUI.skin = _runebornSkin; } try { if (_menuOpen) { _windowRect = GUI.Window(928417, _windowRect, new WindowFunction(DrawWindow), "Rusty Heathens", GUI.skin.window); } if (_adminMenuOpen) { if (RunebornAdminTools.IsLocalAdmin()) { _adminWindowRect = GUI.Window(928418, _adminWindowRect, new WindowFunction(DrawAdminWindow), "Rusty Heathens Admin", GUI.skin.window); return; } _adminMenuOpen = false; ApplyCursorStateForOpenMenus(); } } finally { GUI.skin = skin; } } private void DrawActiveBuffHud() { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } List list = new List(); if (RunebornServerComfort.IsRustyRestedActiveForLocalPlayer()) { list.Add("[R] Rusty Buff"); } if (RunebornServerComfort.IsSeppyBuffActiveForLocalPlayer()) { list.Add("[S] Seppy Buff"); } if (RunebornServerComfort.IsXpertBuffActiveForLocalPlayer()) { list.Add("[X] Xpert Buff"); } if (RunebornServerComfort.IsChaosBringerBuffActiveForLocalPlayer()) { list.Add("[C] Chaos Bringer"); } if (RunebornServerComfort.IsWitchyBuffActiveForLocalPlayer()) { list.Add("[W] Witchy Buff"); } if (list.Count != 0) { float num = 42f + (float)list.Count * 22f; float num2 = (float)Screen.width - 230f - 18f; float num3 = Mathf.Max(18f, (float)Screen.height - num - 18f); float num4 = Mathf.Clamp(220f, 18f, num3); GUI.Box(new Rect(num2, num4, 230f, num), GUIContent.none, GUI.skin.box); GUI.Label(new Rect(num2 + 8f, num4 + 6f, 214f, 24f), "ACTIVE BUFFS", _hudTitleStyle ?? GUI.skin.label); for (int i = 0; i < list.Count; i++) { GUI.Label(new Rect(num2 + 12f, num4 + 34f + (float)i * 22f, 206f, 22f), list[i], _hudLabelStyle ?? GUI.skin.label); } } } private void DrawWindow(int windowId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 34f, 6f, 26f, 22f), "X", _closeButtonStyle ?? GUI.skin.button)) { ToggleMenu(); return; } GUILayout.Space(8f); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, false, true, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(((Rect)(ref _windowRect)).width - 28f), GUILayout.Height(((Rect)(ref _windowRect)).height - 52f) }); DrawServerComfortMenuContent(); GUILayout.EndScrollView(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 42f, 30f)); } private void DrawAdminWindow(int windowId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (GUI.Button(new Rect(((Rect)(ref _adminWindowRect)).width - 34f, 6f, 26f, 22f), "X", _closeButtonStyle ?? GUI.skin.button)) { ToggleAdminMenu(); return; } GUILayout.Space(8f); _adminScrollPosition = GUILayout.BeginScrollView(_adminScrollPosition, false, true, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(((Rect)(ref _adminWindowRect)).width - 28f), GUILayout.Height(((Rect)(ref _adminWindowRect)).height - 52f) }); DrawAdminMenuContent(); GUILayout.EndScrollView(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _adminWindowRect)).width - 42f, 30f)); } private void EnsureRunebornGuiSkin() { //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: 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_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0264: 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) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: 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) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Expected O, but got Unknown //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Expected O, but got Unknown //IL_0443: 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) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0484: Expected O, but got Unknown //IL_0488: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Expected O, but got Unknown //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_04ce: Expected O, but got Unknown //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_0528: Unknown result type (might be due to invalid IL or missing references) //IL_0536: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Unknown result type (might be due to invalid IL or missing references) //IL_0562: Unknown result type (might be due to invalid IL or missing references) //IL_056c: Expected O, but got Unknown //IL_0572: Unknown result type (might be due to invalid IL or missing references) //IL_057c: Expected O, but got Unknown //IL_058e: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Expected O, but got Unknown //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05ba: Unknown result type (might be due to invalid IL or missing references) //IL_05c8: Unknown result type (might be due to invalid IL or missing references) //IL_05f4: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Expected O, but got Unknown //IL_061b: Unknown result type (might be due to invalid IL or missing references) //IL_0625: Expected O, but got Unknown //IL_0629: Unknown result type (might be due to invalid IL or missing references) //IL_0633: Expected O, but got Unknown //IL_06bc: Unknown result type (might be due to invalid IL or missing references) //IL_06c6: Expected O, but got Unknown //IL_06e3: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_0723: Expected O, but got Unknown //IL_072d: Unknown result type (might be due to invalid IL or missing references) //IL_0737: Expected O, but got Unknown //IL_0751: Unknown result type (might be due to invalid IL or missing references) //IL_075b: Expected O, but got Unknown //IL_075e: Unknown result type (might be due to invalid IL or missing references) //IL_0768: Expected O, but got Unknown //IL_0773: Unknown result type (might be due to invalid IL or missing references) //IL_079d: Unknown result type (might be due to invalid IL or missing references) //IL_07a7: Expected O, but got Unknown //IL_07aa: Unknown result type (might be due to invalid IL or missing references) //IL_07b4: Expected O, but got Unknown //IL_07bf: Unknown result type (might be due to invalid IL or missing references) //IL_07d6: Unknown result type (might be due to invalid IL or missing references) //IL_07e0: Expected O, but got Unknown //IL_07eb: Unknown result type (might be due to invalid IL or missing references) //IL_0801: Unknown result type (might be due to invalid IL or missing references) //IL_080b: Expected O, but got Unknown //IL_0816: Unknown result type (might be due to invalid IL or missing references) //IL_082c: Unknown result type (might be due to invalid IL or missing references) //IL_0836: Expected O, but got Unknown //IL_0841: Unknown result type (might be due to invalid IL or missing references) //IL_0857: Unknown result type (might be due to invalid IL or missing references) //IL_0861: Expected O, but got Unknown //IL_0890: Unknown result type (might be due to invalid IL or missing references) //IL_08a2: Unknown result type (might be due to invalid IL or missing references) //IL_08b4: Unknown result type (might be due to invalid IL or missing references) //IL_08be: Unknown result type (might be due to invalid IL or missing references) //IL_08c8: Expected O, but got Unknown //IL_08f7: Unknown result type (might be due to invalid IL or missing references) //IL_0909: Unknown result type (might be due to invalid IL or missing references) //IL_091b: Unknown result type (might be due to invalid IL or missing references) //IL_0925: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Expected O, but got Unknown //IL_095e: Unknown result type (might be due to invalid IL or missing references) //IL_0970: Unknown result type (might be due to invalid IL or missing references) //IL_097a: Unknown result type (might be due to invalid IL or missing references) //IL_0984: Expected O, but got Unknown //IL_09c7: Unknown result type (might be due to invalid IL or missing references) //IL_09d1: Expected O, but got Unknown //IL_09d4: Unknown result type (might be due to invalid IL or missing references) //IL_09de: Expected O, but got Unknown //IL_09e9: Unknown result type (might be due to invalid IL or missing references) //IL_0a18: Unknown result type (might be due to invalid IL or missing references) //IL_0a22: Expected O, but got Unknown //IL_0a2d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_runebornSkin != (Object)null) && !((Object)(object)GUI.skin == (Object)null)) { _runebornSkin = Object.Instantiate(GUI.skin); ((Object)_runebornSkin).name = "RustyHeathensValheimSkin"; Color fillColor = default(Color); ((Color)(ref fillColor))..ctor(0.105f, 0.065f, 0.035f, 0.97f); Color fillColor2 = default(Color); ((Color)(ref fillColor2))..ctor(0.175f, 0.105f, 0.052f, 0.97f); Color fillColor3 = default(Color); ((Color)(ref fillColor3))..ctor(0.235f, 0.145f, 0.07f, 1f); Color fillColor4 = default(Color); ((Color)(ref fillColor4))..ctor(0.315f, 0.205f, 0.105f, 1f); Color fillColor5 = default(Color); ((Color)(ref fillColor5))..ctor(0.125f, 0.075f, 0.035f, 1f); Color borderColor = default(Color); ((Color)(ref borderColor))..ctor(0.34f, 0.29f, 0.22f, 1f); Color borderColor2 = default(Color); ((Color)(ref borderColor2))..ctor(0.63f, 0.43f, 0.19f, 1f); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.89f, 0.84f, 0.72f, 1f); Color textColor2 = default(Color); ((Color)(ref textColor2))..ctor(0.68f, 0.62f, 0.53f, 1f); Color textColor3 = default(Color); ((Color)(ref textColor3))..ctor(0.92f, 0.72f, 0.38f, 1f); Color textColor4 = default(Color); ((Color)(ref textColor4))..ctor(0.58f, 0.8f, 0.48f, 1f); Color textColor5 = default(Color); ((Color)(ref textColor5))..ctor(0.92f, 0.48f, 0.32f, 1f); Texture2D background = CreateFramedTexture(fillColor, borderColor2); Texture2D background2 = CreateFramedTexture(fillColor2, borderColor); Texture2D background3 = CreateFramedTexture(fillColor3, borderColor); Texture2D background4 = CreateFramedTexture(fillColor4, borderColor2); Texture2D background5 = CreateFramedTexture(fillColor5, borderColor2); Texture2D background6 = CreateFramedTexture(new Color(0.16f, 0.28f, 0.12f, 1f), new Color(0.53f, 0.7f, 0.31f, 1f)); Texture2D background7 = CreateFramedTexture(new Color(0.13f, 0.13f, 0.13f, 1f), borderColor); Texture2D background8 = CreateFramedTexture(new Color(0.26f, 0.09f, 0.07f, 1f), new Color(0.62f, 0.22f, 0.16f, 1f)); Texture2D background9 = CreateFramedTexture(new Color(0.22f, 0.13f, 0.055f, 1f), borderColor2); Texture2D background10 = CreateFramedTexture(new Color(0.07f, 0.045f, 0.025f, 1f), borderColor); Texture2D background11 = CreateFramedTexture(new Color(0.31f, 0.2f, 0.1f, 1f), borderColor2); GUIStyle window = _runebornSkin.window; window.normal.background = background; window.hover.background = background; window.active.background = background; window.focused.background = background; window.onNormal.background = background; window.onHover.background = background; window.onActive.background = background; window.onFocused.background = background; window.normal.textColor = textColor3; window.hover.textColor = textColor3; window.active.textColor = textColor3; window.focused.textColor = textColor3; window.onNormal.textColor = textColor3; window.onHover.textColor = textColor3; window.onActive.textColor = textColor3; window.onFocused.textColor = textColor3; window.fontSize = 16; window.fontStyle = (FontStyle)1; window.alignment = (TextAnchor)1; window.padding = new RectOffset(14, 14, 34, 14); window.border = new RectOffset(1, 1, 1, 1); GUIStyle box = _runebornSkin.box; box.normal.background = background2; box.hover.background = background2; box.active.background = background2; box.focused.background = background2; box.onNormal.background = background2; box.onHover.background = background2; box.onActive.background = background2; box.onFocused.background = background2; box.normal.textColor = textColor; box.hover.textColor = textColor; box.active.textColor = textColor; box.focused.textColor = textColor; box.padding = new RectOffset(10, 10, 10, 10); box.border = new RectOffset(1, 1, 1, 1); GUIStyle label = _runebornSkin.label; label.normal.textColor = textColor; label.fontSize = 13; label.wordWrap = true; label.padding = new RectOffset(2, 2, 2, 2); GUIStyle button = _runebornSkin.button; button.normal.background = background3; button.hover.background = background4; button.active.background = background5; button.focused.background = background3; button.normal.textColor = textColor; button.hover.textColor = textColor3; button.active.textColor = textColor3; button.focused.textColor = textColor; button.fontSize = 13; button.alignment = (TextAnchor)4; button.padding = new RectOffset(8, 8, 5, 5); button.margin = new RectOffset(2, 2, 2, 2); button.fixedHeight = 28f; button.border = new RectOffset(1, 1, 1, 1); GUIStyle toggle = _runebornSkin.toggle; toggle.normal.textColor = textColor; toggle.hover.textColor = textColor3; toggle.active.textColor = textColor3; toggle.fontSize = 13; toggle.padding = new RectOffset(toggle.padding.left, toggle.padding.right, 4, 4); GUIStyle scrollView = _runebornSkin.scrollView; scrollView.normal.background = background2; scrollView.padding = new RectOffset(8, 8, 8, 8); scrollView.border = new RectOffset(1, 1, 1, 1); _runebornSkin.verticalScrollbar.normal.background = background10; _runebornSkin.verticalScrollbar.fixedWidth = 14f; _runebornSkin.verticalScrollbarThumb.normal.background = background11; _runebornSkin.verticalScrollbarThumb.hover.background = background4; _runebornSkin.verticalScrollbarThumb.active.background = background5; _runebornSkin.verticalScrollbarThumb.fixedWidth = 12f; _sectionHeaderStyle = new GUIStyle(label); _sectionHeaderStyle.normal.background = background9; _sectionHeaderStyle.normal.textColor = textColor3; _sectionHeaderStyle.fontSize = 15; _sectionHeaderStyle.fontStyle = (FontStyle)1; _sectionHeaderStyle.alignment = (TextAnchor)3; _sectionHeaderStyle.padding = new RectOffset(8, 8, 6, 6); _sectionHeaderStyle.margin = new RectOffset(0, 0, 8, 4); _sectionHeaderStyle.fixedHeight = 30f; _sectionHeaderStyle.border = new RectOffset(1, 1, 1, 1); _subHeaderStyle = new GUIStyle(label); _subHeaderStyle.normal.textColor = textColor3; _subHeaderStyle.fontSize = 14; _subHeaderStyle.fontStyle = (FontStyle)1; _subHeaderStyle.margin = new RectOffset(2, 2, 7, 2); _mutedLabelStyle = new GUIStyle(label); _mutedLabelStyle.normal.textColor = textColor2; _mutedLabelStyle.fontSize = 12; _goodLabelStyle = new GUIStyle(label); _goodLabelStyle.normal.textColor = textColor4; _goodLabelStyle.fontStyle = (FontStyle)1; _warningLabelStyle = new GUIStyle(label); _warningLabelStyle.normal.textColor = textColor5; _warningLabelStyle.fontStyle = (FontStyle)1; _accentLabelStyle = new GUIStyle(label); _accentLabelStyle.normal.textColor = textColor3; _accentLabelStyle.fontStyle = (FontStyle)1; _selectedButtonStyle = new GUIStyle(button); _selectedButtonStyle.normal.background = background6; _selectedButtonStyle.hover.background = background6; _selectedButtonStyle.normal.textColor = textColor; _selectedButtonStyle.hover.textColor = textColor3; _selectedButtonStyle.active.textColor = textColor3; _lockedButtonStyle = new GUIStyle(button); _lockedButtonStyle.normal.background = background7; _lockedButtonStyle.hover.background = background7; _lockedButtonStyle.normal.textColor = textColor; _lockedButtonStyle.hover.textColor = textColor3; _lockedButtonStyle.active.textColor = textColor3; _disabledButtonStyle = new GUIStyle(button); _disabledButtonStyle.normal.background = background8; _disabledButtonStyle.hover.background = background8; _disabledButtonStyle.normal.textColor = textColor5; _disabledButtonStyle.hover.textColor = textColor; _closeButtonStyle = new GUIStyle(button); _closeButtonStyle.fixedWidth = 26f; _closeButtonStyle.fixedHeight = 22f; _closeButtonStyle.fontSize = 12; _closeButtonStyle.fontStyle = (FontStyle)1; _closeButtonStyle.padding = new RectOffset(0, 0, 0, 0); _hudTitleStyle = new GUIStyle(label); _hudTitleStyle.normal.textColor = textColor3; _hudTitleStyle.fontSize = 13; _hudTitleStyle.fontStyle = (FontStyle)1; _hudTitleStyle.alignment = (TextAnchor)4; _hudLabelStyle = new GUIStyle(label); _hudLabelStyle.normal.textColor = textColor; _hudLabelStyle.fontSize = 13; _hudLabelStyle.richText = true; _hudLabelStyle.alignment = (TextAnchor)3; } } private Texture2D CreateFramedTexture(Color fillColor, Color borderColor) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(3, 3, (TextureFormat)4, false); ((Object)val).name = "RustyHeathensGuiTexture"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { bool flag = j == 0 || j == 2 || i == 0 || i == 2; val.SetPixel(j, i, flag ? borderColor : fillColor); } } val.Apply(); _guiTextures.Add(val); return val; } private void DrawSectionHeader(string title) { GUILayout.Label(title, _sectionHeaderStyle ?? GUI.skin.label, Array.Empty()); } private void DrawSubHeader(string title) { GUILayout.Label(title, _subHeaderStyle ?? GUI.skin.label, Array.Empty()); } private void DrawMutedLabel(string text) { GUILayout.Label(text, _mutedLabelStyle ?? GUI.skin.label, Array.Empty()); } private bool DrawRunebornButton(string text) { GUIStyle val = GUI.skin.button; if (text.StartsWith("Selected:") || text.StartsWith("Enabled:")) { val = _selectedButtonStyle ?? GUI.skin.button; } else if (text.StartsWith("Locked:")) { val = _lockedButtonStyle ?? GUI.skin.button; } else if (text.StartsWith("Disabled:")) { val = _disabledButtonStyle ?? GUI.skin.button; } return GUILayout.Button(text, val, Array.Empty()); } private void DrawSlotLimitButton(int slotCount) { bool num = RunebornServerComfort.GetMaxSelectedBuffs() == slotCount; string text = ((slotCount == 1) ? "1 Slot" : (slotCount + " Slots")); GUIStyle val = (num ? (_selectedButtonStyle ?? GUI.skin.button) : GUI.skin.button); if (GUILayout.Button(text, val, Array.Empty())) { RunebornServerComfort.SetMaxSelectedBuffs(slotCount); } } private void DrawServerComfortMenuContent() { DrawSectionHeader("PLAYER MENU"); DrawMutedLabel("Rusty Heathens server information and personal settings"); DrawMenuTabs(ref _playerMenuTab, "BUFFS", "SERVER", "HELP"); switch (_playerMenuTab) { case 1: DrawPlayerServerTab(); break; case 2: DrawPlayerHelpTab(); break; default: DrawPlayerBuffsTab(); break; } } private void DrawMenuTabs(ref int selectedTab, params string[] tabLabels) { //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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < tabLabels.Length; i++) { GUIStyle val = ((i == selectedTab) ? (_selectedButtonStyle ?? GUI.skin.button) : GUI.skin.button); if (GUILayout.Button(tabLabels[i], val, Array.Empty())) { selectedTab = i; _scrollPosition = Vector2.zero; _adminScrollPosition = Vector2.zero; } } GUILayout.EndHorizontal(); GUILayout.Space(6f); } private void DrawPlayerBuffsTab() { DrawSectionHeader("CHOOSE YOUR BUFFS"); int selectedBuffCount = RunebornServerComfort.GetSelectedBuffCount(); int maxSelectedBuffs = RunebornServerComfort.GetMaxSelectedBuffs(); GUILayout.BeginHorizontal(Array.Empty()); GUIStyle val = ((selectedBuffCount > maxSelectedBuffs) ? (_warningLabelStyle ?? GUI.skin.label) : ((selectedBuffCount == maxSelectedBuffs) ? (_accentLabelStyle ?? GUI.skin.label) : (_goodLabelStyle ?? GUI.skin.label))); GUILayout.Label("Selected " + selectedBuffCount + " of " + maxSelectedBuffs, val, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Rested status controls activation", _mutedLabelStyle ?? GUI.skin.label, Array.Empty()); GUILayout.EndHorizontal(); DrawPlayerBuffChoice("rusty", "Rusty Buff"); DrawPlayerBuffChoice("seppy", "Seppy Buff"); DrawPlayerBuffChoice("xpert", "Xpert Buff"); DrawPlayerBuffChoice("chaos", "Chaos Bringer Buff"); DrawPlayerBuffChoice("witchy", "Witchy Buff"); DrawSectionHeader("SELECTED BUFF DETAILS"); DrawSelectedBuffDetails(); DrawSectionHeader("UTILITY EFFECTS"); DrawUtilityStatusCards(); } private void DrawPlayerBuffChoice(string buffId, string displayName) { if (RunebornServerComfort.IsBuffEnabled(buffId)) { string buffButtonText = RunebornServerComfort.GetBuffButtonText(buffId, displayName); if (DrawRunebornButton(buffButtonText)) { ShowLocalCenterMessage(RunebornServerComfort.ToggleBuffSelection(buffId, displayName)); } } } private bool IsBuffChosenForGui(string buffId, string displayName) { return RunebornServerComfort.GetBuffButtonText(buffId, displayName).StartsWith("Selected:", StringComparison.Ordinal); } private void DrawSelectedBuffDetails() { bool flag = false; if (IsBuffChosenForGui("rusty", "Rusty Buff")) { flag = true; DrawBuffStatusCard("Rusty Buff", RunebornServerComfort.IsRustyRestedActiveForLocalPlayer(), "Requires Rested", "10% less stamina used"); } if (IsBuffChosenForGui("seppy", "Seppy Buff")) { flag = true; DrawBuffStatusCard("Seppy Buff", RunebornServerComfort.IsSeppyBuffActiveForLocalPlayer(), "Requires Rested", "10% less incoming damage", "15% less stamina used while blocking"); } if (IsBuffChosenForGui("xpert", "Xpert Buff")) { flag = true; DrawBuffStatusCard("Xpert Buff", RunebornServerComfort.IsXpertBuffActiveForLocalPlayer(), "Requires Rested and an axe or melee weapon", "10% more melee damage", "10% less weapon stamina used"); } if (IsBuffChosenForGui("chaos", "Chaos Bringer Buff")) { flag = true; DrawBuffStatusCard("Chaos Bringer", RunebornServerComfort.IsChaosBringerBuffActiveForLocalPlayer(), "Requires Rested", "10% faster movement", "20% faster sneak", "12% stronger jump", "15% less dodge and swim stamina"); } if (IsBuffChosenForGui("witchy", "Witchy Buff")) { flag = true; DrawBuffStatusCard("Witchy Buff", RunebornServerComfort.IsWitchyBuffActiveForLocalPlayer(), "Requires Rested", "10% less staff and magic stamina"); } if (!flag) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); DrawMutedLabel("Choose a buff above to see its active effects."); GUILayout.EndVertical(); } } private void DrawBuffStatusCard(string title, bool active, string inactiveRequirement, params string[] effectLines) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(title, _subHeaderStyle ?? GUI.skin.label, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(active ? "ACTIVE" : "INACTIVE", active ? (_goodLabelStyle ?? GUI.skin.label) : (_warningLabelStyle ?? GUI.skin.label), Array.Empty()); GUILayout.EndHorizontal(); if (!active) { DrawMutedLabel(inactiveRequirement); } foreach (string text in effectLines) { GUILayout.Label("• " + text, Array.Empty()); } GUILayout.EndVertical(); } private void DrawUtilityStatusCards() { bool flag = RunebornServerComfort.IsBuildComfortActiveForLocalPlayer(); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawSubHeader("Build Mode Comfort"); GUILayout.FlexibleSpace(); GUILayout.Label(flag ? "ACTIVE" : "READY", flag ? (_goodLabelStyle ?? GUI.skin.label) : (_mutedLabelStyle ?? GUI.skin.label), Array.Empty()); GUILayout.EndHorizontal(); DrawMutedLabel(flag ? "50% less stamina used while building" : "Equip a Hammer, Hoe, or Cultivator"); GUILayout.EndVertical(); float bodyRecoverySecondsLeft = RunebornServerComfort.GetBodyRecoverySecondsLeft(); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawSubHeader("Body Recovery"); GUILayout.FlexibleSpace(); GUILayout.Label((bodyRecoverySecondsLeft > 0f) ? "ACTIVE" : "INACTIVE", (bodyRecoverySecondsLeft > 0f) ? (_goodLabelStyle ?? GUI.skin.label) : (_mutedLabelStyle ?? GUI.skin.label), Array.Empty()); GUILayout.EndHorizontal(); if (bodyRecoverySecondsLeft > 0f) { DrawMutedLabel(Mathf.CeilToInt(bodyRecoverySecondsLeft / 60f) + " minutes remaining"); GUILayout.Label("• +150 carry weight\n• 35% less stamina used", Array.Empty()); } else { DrawMutedLabel("Activates for 10 minutes after death"); } GUILayout.EndVertical(); } private void DrawPlayerServerTab() { DrawSectionHeader("SERVER STATUS"); DrawServerStatusCard("Welcome Message", enabled: true, "Shown once after loading into the world."); DrawServerStatusCard("Server Reminder", RunebornServerReminder.ServerReminderEnabled, "Shown once after the welcome message."); DrawServerStatusCard("Raid Warnings", RunebornRaidWarnings.RaidWarningsEnabled, "Warns when raids start, repeat, and clear."); DrawServerStatusCard("Boss Kill Messages", enabled: true, "Shows a Rusty Heathens victory message."); DrawSectionHeader("DEATH COUNTER"); int currentDeathCount = PlayerPrefs.GetInt("RustyHeathensRuneborn.DeathCount", 0); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.Label("Deaths: " + currentDeathCount, _subHeaderStyle ?? GUI.skin.label, Array.Empty()); DrawMutedLabel("Next message: " + RunebornServerComfort.PreviewNextFunnyDeathMessage(currentDeathCount)); GUILayout.BeginHorizontal(Array.Empty()); if (DrawRunebornButton("Show Count")) { ShowLocalCenterMessage("Rusty Heathens Death Count: " + currentDeathCount); } if (DrawRunebornButton("Reset Count")) { PlayerPrefs.SetInt("RustyHeathensRuneborn.DeathCount", 0); PlayerPrefs.Save(); ShowLocalCenterMessage("Rusty Heathens Death Count reset"); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawServerStatusCard(string title, bool enabled, string description) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawSubHeader(title); GUILayout.FlexibleSpace(); GUILayout.Label(enabled ? "ON" : "OFF", enabled ? (_goodLabelStyle ?? GUI.skin.label) : (_warningLabelStyle ?? GUI.skin.label), Array.Empty()); GUILayout.EndHorizontal(); DrawMutedLabel(description); GUILayout.EndVertical(); } private void DrawPlayerHelpTab() { DrawSectionHeader("SERVER RULES & HELP"); DrawMutedLabel("Choose a topic to display it in the center of the screen."); if (DrawRunebornButton("Basic Server Rules")) { ShowLocalCenterMessage("RUSTY HEATHENS RULES\nRespect the crew.\nNo stealing.\nNo boss fights without the group.\nHelp with body recovery when you can.\nCheck Discord for full rules."); } if (DrawRunebornButton("Boss Fight Rules")) { ShowLocalCenterMessage("BOSS FIGHT RULES\nDo not summon bosses alone.\nWait for Rusty and the group.\nRepair gear, bring food, and be ready.\nIf you die, call for recovery."); } if (DrawRunebornButton("Body Recovery Rules")) { ShowLocalCenterMessage("BODY RECOVERY RULES\nDo not run in alone if the area is hot.\nCall out where your body is.\nStay grouped and protect each other.\nRecovery before revenge."); } if (DrawRunebornButton("Discord Reminder")) { ShowLocalCenterMessage("DISCORD REMINDER\nUse Discord for rules, event plans, updates, and stream info.\nMessage RustyLaser if you need help."); } if (DrawRunebornButton("Server Etiquette")) { ShowLocalCenterMessage("SERVER ETIQUETTE\nAsk before taking shared items.\nLabel storage when possible.\nDo not spoil new areas or bosses.\nBuild smart, clean up, and help the crew."); } DrawSectionHeader("SERVER EVENTS"); DrawMutedLabel(RunebornAdminTools.IsLocalAdmin() ? "Event broadcasts are controlled from F7." : "Server-event broadcasts appear automatically."); } private void ShowBuffAvailabilityMessage(string message) { if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } } private void DrawAdminMenuContent() { DrawSectionHeader("ADMIN MENU"); DrawMutedLabel("Server-owner controls - F7"); DrawMenuTabs(ref _adminMenuTab, "SETTINGS", "EVENTS", "SYSTEM"); switch (_adminMenuTab) { case 1: DrawAdminEventsTab(); break; case 2: DrawAdminSystemTab(); break; default: DrawAdminSettingsTab(); break; } } private void DrawAdminSettingsTab() { DrawSectionHeader("SERVER SETTINGS"); DrawSubHeader("Maximum Buff Slots"); DrawMutedLabel("Choose how many character buffs each player may equip."); GUILayout.BeginHorizontal(Array.Empty()); DrawSlotLimitButton(1); DrawSlotLimitButton(2); DrawSlotLimitButton(3); GUILayout.EndHorizontal(); GUILayout.Label("Admin authority active", _goodLabelStyle ?? GUI.skin.label, Array.Empty()); DrawSectionHeader("BUFF AVAILABILITY"); DrawAdminBuffAvailabilityButton("rusty", "Rusty Buff"); DrawAdminBuffAvailabilityButton("seppy", "Seppy Buff"); DrawAdminBuffAvailabilityButton("xpert", "Xpert Buff"); DrawAdminBuffAvailabilityButton("chaos", "Chaos Bringer Buff"); DrawAdminBuffAvailabilityButton("witchy", "Witchy Buff"); DrawMutedLabel("Enabled: " + RunebornServerComfort.GetEnabledBuffSummary()); GUILayout.Label("Multiplayer availability sync active", _goodLabelStyle ?? GUI.skin.label, Array.Empty()); DrawSectionHeader("ADMIN TOGGLES"); DrawAdminSettingToggle("Server Reminder", ServerReminderEnabledEntry, "Server Reminder"); DrawAdminSettingToggle("Raid Warnings", RaidWarningsEnabledEntry, "Raid Warnings"); } private void DrawAdminSettingToggle(string displayName, ConfigEntry configEntry, string messageName) { bool value = configEntry.Value; string text = (value ? "Enabled: " : "Disabled: ") + displayName + " - click to " + (value ? "disable" : "enable"); if (DrawRunebornButton(text)) { configEntry.Value = !value; ApplyAdminSettingsToRuntime(); ((BaseUnityPlugin)this).Config.Save(); ShowLocalCenterMessage(messageName + " " + (configEntry.Value ? "Enabled" : "Disabled")); } } private void DrawAdminBuffAvailabilityButton(string buffId, string displayName) { string buffEnabledButtonText = RunebornServerComfort.GetBuffEnabledButtonText(buffId, displayName); if (DrawRunebornButton(buffEnabledButtonText)) { ShowBuffAvailabilityMessage(RunebornServerComfort.ToggleBuffEnabledByAdmin(buffId, displayName)); } } private void DrawAdminEventsTab() { DrawSectionHeader("SERVER EVENTS"); DrawMutedLabel("Broadcast a large center-screen message to every player."); if (DrawRunebornButton("Boss Fight Starting Soon")) { RunebornAdminEventMessages.SendAdminBroadcast("BOSS FIGHT STARTING SOON", "Return to base, repair your gear, grab food, and wait for Rusty."); } if (DrawRunebornButton("Return To Base")) { RunebornAdminEventMessages.SendAdminBroadcast("RETURN TO BASE", "Everyone head back to base. Regroup before the chaos gets worse."); } if (DrawRunebornButton("Body Recovery Run")) { RunebornAdminEventMessages.SendAdminBroadcast("BODY RECOVERY RUN", "A Heathen is down. Gear up, stay together, and help recover the body."); } if (DrawRunebornButton("Discord Reminder")) { RunebornAdminEventMessages.SendAdminBroadcast("DISCORD REMINDER", "Check Discord for server rules, event plans, and stream updates."); } if (DrawRunebornButton("Stream Starting Soon")) { RunebornAdminEventMessages.SendAdminBroadcast("STREAM STARTING SOON", "Get ready, Heathens. The stream is about to begin."); } } private void DrawAdminSystemTab() { DrawSectionHeader("SYSTEM STATUS"); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.Label("Runeborn v0.4.2", _subHeaderStyle ?? GUI.skin.label, Array.Empty()); GUILayout.Label("Buff-slot synchronization: " + (RunebornBuffSlotSyncFoundation.WasLastSenderTrusted() ? "TRUSTED" : "ADMIN AUTHORITY"), _goodLabelStyle ?? GUI.skin.label, Array.Empty()); GUILayout.Label("Availability synchronization: " + (RunebornBuffAvailabilitySync.WasLastSenderTrusted() ? "TRUSTED" : "ADMIN AUTHORITY"), _goodLabelStyle ?? GUI.skin.label, Array.Empty()); DrawMutedLabel("Late-join request and response synchronization is active."); GUILayout.EndVertical(); DrawSectionHeader("BUFF-SLOT DIAGNOSTICS"); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); DrawMutedLabel("Current slot limit: " + RunebornServerComfort.GetMaxSelectedBuffs()); DrawMutedLabel("Last received value: " + RunebornBuffSlotSyncFoundation.GetLastReceivedMaxBuffSlots()); DrawMutedLabel("Last RPC sender: " + RunebornBuffSlotSyncFoundation.GetLastRpcSender()); DrawMutedLabel("Server peer ID: " + RunebornBuffSlotSyncFoundation.GetLastServerPeerId()); DrawMutedLabel("Local peer ID: " + RunebornBuffSlotSyncFoundation.GetLastLocalPeerId()); DrawMutedLabel("Last request sender: " + RunebornBuffSlotSyncFoundation.GetLastRequestSender()); DrawMutedLabel("Live ID equality: " + (RunebornBuffSlotSyncFoundation.DoLastRpcIdsMatch() ? "YES" : "NO")); DrawMutedLabel("Sender hex: " + RunebornBuffSlotSyncFoundation.GetLastRpcSenderHex()); DrawMutedLabel("Server hex: " + RunebornBuffSlotSyncFoundation.GetLastServerPeerIdHex()); GUILayout.EndVertical(); DrawSectionHeader("AVAILABILITY DIAGNOSTICS"); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); DrawMutedLabel("Enabled mask: " + RunebornServerComfort.GetEnabledBuffMask()); DrawMutedLabel("Last received mask: " + RunebornBuffAvailabilitySync.GetLastReceivedMask()); DrawMutedLabel("Last RPC sender: " + RunebornBuffAvailabilitySync.GetLastRpcSender()); DrawMutedLabel("Last request sender: " + RunebornBuffAvailabilitySync.GetLastRequestSender()); GUILayout.EndVertical(); DrawSectionHeader("ADMIN ID"); GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); DrawMutedLabel(RunebornAdminTools.GetLocalAdminDebugText()); GUILayout.EndVertical(); } } public sealed class RunebornRustyStatusEffect : StatusEffect { public override string GetTooltipString() { return "10% less stamina used while Rested."; } } public sealed class RunebornSeppyStatusEffect : StatusEffect { public override string GetTooltipString() { return "10% less incoming damage.\n15% less stamina used while blocking."; } } public sealed class RunebornXpertStatusEffect : StatusEffect { public override string GetTooltipString() { return "10% more melee damage.\n10% less weapon stamina used while Rested with a melee weapon."; } } public sealed class RunebornChaosStatusEffect : StatusEffect { public override string GetTooltipString() { return "10% faster movement.\n20% faster sneak.\n12% stronger jump.\n15% less dodge and swim stamina."; } } public sealed class RunebornWitchyStatusEffect : StatusEffect { public override string GetTooltipString() { return "10% less staff and magic stamina while Rested."; } } public static class RunebornBuffStatusEffects { private const string RustyEffectName = "SE_RustyHeathens_RustyBuff"; private const string SeppyEffectName = "SE_RustyHeathens_SeppyBuff"; private const string XpertEffectName = "SE_RustyHeathens_XpertBuff"; private const string ChaosEffectName = "SE_RustyHeathens_ChaosBringerBuff"; private const string WitchyEffectName = "SE_RustyHeathens_WitchyBuff"; private static readonly int RustyEffectHash = StringExtensionMethods.GetStableHashCode("SE_RustyHeathens_RustyBuff"); private static readonly int SeppyEffectHash = StringExtensionMethods.GetStableHashCode("SE_RustyHeathens_SeppyBuff"); private static readonly int XpertEffectHash = StringExtensionMethods.GetStableHashCode("SE_RustyHeathens_XpertBuff"); private static readonly int ChaosEffectHash = StringExtensionMethods.GetStableHashCode("SE_RustyHeathens_ChaosBringerBuff"); private static readonly int WitchyEffectHash = StringExtensionMethods.GetStableHashCode("SE_RustyHeathens_WitchyBuff"); private static bool _registered; private static CustomStatusEffect _rustyStatusEffect; private static CustomStatusEffect _seppyStatusEffect; private static CustomStatusEffect _xpertStatusEffect; private static CustomStatusEffect _chaosStatusEffect; private static CustomStatusEffect _witchyStatusEffect; public static void Register() { if (!_registered) { Sprite val = LoadIconSprite("RustyBuffIcon_128.png"); Sprite val2 = LoadIconSprite("SeppyBuffIcon_128.png"); Sprite val3 = LoadIconSprite("XpertBuffIcon_128.png"); Sprite val4 = LoadIconSprite("ChaosBringerBuffIcon_128.png"); Sprite val5 = LoadIconSprite("WitchyBuffIcon_128.png"); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null) && !((Object)(object)val4 == (Object)null) && !((Object)(object)val5 == (Object)null)) { _rustyStatusEffect = RegisterStatusEffect("SE_RustyHeathens_RustyBuff", "Rusty Buff", val, "10% less stamina used while Rested."); _seppyStatusEffect = RegisterStatusEffect("SE_RustyHeathens_SeppyBuff", "Seppy Buff", val2, "10% less incoming damage and 15% less blocking stamina."); _xpertStatusEffect = RegisterStatusEffect("SE_RustyHeathens_XpertBuff", "Xpert Buff", val3, "10% more melee damage and 10% less weapon stamina."); _chaosStatusEffect = RegisterStatusEffect("SE_RustyHeathens_ChaosBringerBuff", "Chaos Bringer", val4, "Movement, sneak, jump, dodge, and swim bonuses."); _witchyStatusEffect = RegisterStatusEffect("SE_RustyHeathens_WitchyBuff", "Witchy Buff", val5, "10% less staff and magic stamina while Rested."); _registered = _rustyStatusEffect != null && _seppyStatusEffect != null && _xpertStatusEffect != null && _chaosStatusEffect != null && _witchyStatusEffect != null; } } } public static void UpdateLocalPlayerEffects() { if (!_registered) { Register(); } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { SEMan sEMan = ((Character)localPlayer).GetSEMan(); if (sEMan != null) { SyncStatusEffect(sEMan, RustyEffectHash, RunebornServerComfort.IsRustyRestedActiveForLocalPlayer()); SyncStatusEffect(sEMan, SeppyEffectHash, RunebornServerComfort.IsSeppyBuffActiveForLocalPlayer()); SyncStatusEffect(sEMan, XpertEffectHash, RunebornServerComfort.IsXpertBuffActiveForLocalPlayer()); SyncStatusEffect(sEMan, ChaosEffectHash, RunebornServerComfort.IsChaosBringerBuffActiveForLocalPlayer()); SyncStatusEffect(sEMan, WitchyEffectHash, RunebornServerComfort.IsWitchyBuffActiveForLocalPlayer()); } } } private static CustomStatusEffect RegisterStatusEffect(string effectName, string displayName, Sprite icon, string fallbackTooltip) where T : StatusEffect { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown try { T val = ScriptableObject.CreateInstance(); ((Object)(object)val).name = effectName; ((StatusEffect)val).m_name = displayName; ((StatusEffect)val).m_tooltip = fallbackTooltip; ((StatusEffect)val).m_icon = icon; ((StatusEffect)val).m_ttl = 0f; CustomStatusEffect val2 = new CustomStatusEffect((StatusEffect)(object)val, false); ItemManager.Instance.AddStatusEffect(val2); return val2; } catch { return null; } } private static void SyncStatusEffect(SEMan statusManager, int effectHash, bool shouldBeActive) { bool flag = statusManager.HaveStatusEffect(effectHash); if (shouldBeActive && !flag) { statusManager.AddStatusEffect(effectHash, true, 0, 0f); } else if (!shouldBeActive && flag) { statusManager.RemoveStatusEffect(effectHash, false); } } private static Sprite LoadIconSprite(string fileName) { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { return null; } string text = Path.Combine(directoryName, "Assets", fileName); if (!File.Exists(text)) { return null; } return AssetUtils.LoadSpriteFromFile(text); } catch { return null; } } } public static class RunebornBuffSlotSyncFoundation { private const string RpcName = "RustyHeathensRuneborn.BuffSlotSyncFoundation"; private const string RequestRpcName = "RustyHeathensRuneborn.BuffSlotSyncRequest"; private static bool _registered; private static bool _lateJoinRequestSent; private static float _nextLateJoinRequestTime; private static long _requestedServerPeerId = long.MinValue; private static long _lastRequestSender; private static int _lastReceivedMaxBuffSlots; private static long _lastRpcSender; private static long _lastServerPeerId = long.MinValue; private static long _lastLocalPeerId = long.MinValue; private static bool _lastSenderTrusted; public static void EnsureRegistered() { if (_registered || ZRoutedRpc.instance == null) { return; } try { ZRoutedRpc.instance.Register("RustyHeathensRuneborn.BuffSlotSyncFoundation", (Action)RPC_BuffSlotSyncFoundation); ZRoutedRpc.instance.Register("RustyHeathensRuneborn.BuffSlotSyncRequest", (Action)RPC_BuffSlotSyncRequest); _registered = true; } catch { _registered = true; } } public static long GetLastRequestSender() { return _lastRequestSender; } public static void UpdateLateJoinRequest() { EnsureRegistered(); if (!_registered || ZRoutedRpc.instance == null || RunebornAdminTools.IsLocalAdmin() || (Object)(object)Player.m_localPlayer == (Object)null) { return; } long num = TryGetRoutedRpcId("GetServerPeerID"); if (num == long.MinValue) { return; } if (_requestedServerPeerId != num) { _requestedServerPeerId = num; _lateJoinRequestSent = false; _nextLateJoinRequestTime = 0f; } if (_lateJoinRequestSent || Time.unscaledTime < _nextLateJoinRequestTime) { return; } try { ZRoutedRpc.instance.InvokeRoutedRPC(num, "RustyHeathensRuneborn.BuffSlotSyncRequest", new object[1] { 1 }); _lateJoinRequestSent = true; } catch { _nextLateJoinRequestTime = Time.unscaledTime + 2f; } } public static int GetLastReceivedMaxBuffSlots() { return _lastReceivedMaxBuffSlots; } public static long GetLastRpcSender() { return _lastRpcSender; } public static long GetLastServerPeerId() { return _lastServerPeerId; } public static long GetLastLocalPeerId() { return _lastLocalPeerId; } public static bool WasLastSenderTrusted() { return _lastSenderTrusted; } public static bool DoLastRpcIdsMatch() { if (_lastServerPeerId != long.MinValue) { return _lastRpcSender == _lastServerPeerId; } return false; } public static string GetLastRpcSenderHex() { ulong lastRpcSender = (ulong)_lastRpcSender; return lastRpcSender.ToString("X16"); } public static string GetLastServerPeerIdHex() { ulong lastServerPeerId = (ulong)_lastServerPeerId; return lastServerPeerId.ToString("X16"); } private static long TryGetRoutedRpcId(string methodName) { if (ZRoutedRpc.instance == null) { return long.MinValue; } try { MethodInfo method = typeof(ZRoutedRpc).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return long.MinValue; } object obj = method.Invoke(ZRoutedRpc.instance, null); if (obj == null) { return long.MinValue; } return Convert.ToInt64(obj); } catch { return long.MinValue; } } private static bool IsTrustedServerSender(long sender) { _lastServerPeerId = TryGetRoutedRpcId("GetServerPeerID"); _lastLocalPeerId = TryGetRoutedRpcId("GetUID"); if (_lastServerPeerId == long.MinValue) { return false; } return sender.ToString() == _lastServerPeerId.ToString(); } public static void BroadcastMaxBuffSlots(int maxBuffs) { if (!RunebornAdminTools.IsLocalAdmin()) { return; } EnsureRegistered(); if (ZRoutedRpc.instance == null) { return; } int num = Mathf.Clamp(maxBuffs, 1, 3); try { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RustyHeathensRuneborn.BuffSlotSyncFoundation", new object[1] { num }); } catch { } } private static void RPC_BuffSlotSyncRequest(long sender, int value) { _lastRequestSender = sender; if (!RunebornAdminTools.IsLocalAdmin() || ZRoutedRpc.instance == null) { return; } int num = Mathf.Clamp(RunebornServerComfort.GetMaxSelectedBuffs(), 1, 3); try { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "RustyHeathensRuneborn.BuffSlotSyncFoundation", new object[1] { num }); } catch { } } private static void RPC_BuffSlotSyncFoundation(long sender, int value) { _lastRpcSender = sender; _lastSenderTrusted = IsTrustedServerSender(sender); if (_lastSenderTrusted) { _lastReceivedMaxBuffSlots = Mathf.Clamp(value, 1, 3); RunebornServerComfort.ApplySyncedMaxSelectedBuffs(_lastReceivedMaxBuffSlots); } } } public static class RunebornBuffAvailabilitySync { private const string ValueRpcName = "RustyHeathensRuneborn.BuffAvailabilitySyncValue"; private const string RequestRpcName = "RustyHeathensRuneborn.BuffAvailabilitySyncRequest"; private const int AllBuffsMask = 31; private static bool _registered; private static bool _lateJoinRequestSent; private static float _nextLateJoinRequestTime; private static long _requestedServerPeerId = long.MinValue; private static int _lastReceivedMask = 31; private static long _lastRpcSender; private static long _lastServerPeerId = long.MinValue; private static long _lastRequestSender; private static bool _lastSenderTrusted; public static void EnsureRegistered() { if (_registered || ZRoutedRpc.instance == null) { return; } try { ZRoutedRpc.instance.Register("RustyHeathensRuneborn.BuffAvailabilitySyncValue", (Action)RPC_BuffAvailabilityValue); ZRoutedRpc.instance.Register("RustyHeathensRuneborn.BuffAvailabilitySyncRequest", (Action)RPC_BuffAvailabilityRequest); _registered = true; } catch { _registered = true; } } public static void UpdateLateJoinRequest() { EnsureRegistered(); if (!_registered || ZRoutedRpc.instance == null || RunebornAdminTools.IsLocalAdmin() || (Object)(object)Player.m_localPlayer == (Object)null) { return; } long num = TryGetRoutedRpcId("GetServerPeerID"); if (num == long.MinValue) { return; } if (_requestedServerPeerId != num) { _requestedServerPeerId = num; _lateJoinRequestSent = false; _nextLateJoinRequestTime = 0f; } if (_lateJoinRequestSent || Time.unscaledTime < _nextLateJoinRequestTime) { return; } try { ZRoutedRpc.instance.InvokeRoutedRPC(num, "RustyHeathensRuneborn.BuffAvailabilitySyncRequest", new object[1] { 1 }); _lateJoinRequestSent = true; } catch { _nextLateJoinRequestTime = Time.unscaledTime + 2f; } } public static void BroadcastEnabledBuffMask(int enabledMask) { if (!RunebornAdminTools.IsLocalAdmin()) { return; } EnsureRegistered(); if (ZRoutedRpc.instance == null) { return; } int num = enabledMask & 0x1F; try { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RustyHeathensRuneborn.BuffAvailabilitySyncValue", new object[1] { num }); } catch { } } public static int GetLastReceivedMask() { return _lastReceivedMask; } public static long GetLastRpcSender() { return _lastRpcSender; } public static long GetLastRequestSender() { return _lastRequestSender; } public static bool WasLastSenderTrusted() { return _lastSenderTrusted; } private static void RPC_BuffAvailabilityRequest(long sender, int value) { _lastRequestSender = sender; if (!RunebornAdminTools.IsLocalAdmin() || ZRoutedRpc.instance == null) { return; } int num = RunebornServerComfort.GetEnabledBuffMask() & 0x1F; try { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "RustyHeathensRuneborn.BuffAvailabilitySyncValue", new object[1] { num }); } catch { } } private static void RPC_BuffAvailabilityValue(long sender, int value) { _lastRpcSender = sender; _lastSenderTrusted = IsTrustedServerSender(sender); if (_lastSenderTrusted) { _lastReceivedMask = value & 0x1F; RunebornServerComfort.ApplySyncedEnabledBuffMask(_lastReceivedMask); } } private static bool IsTrustedServerSender(long sender) { _lastServerPeerId = TryGetRoutedRpcId("GetServerPeerID"); if (_lastServerPeerId != long.MinValue) { return sender == _lastServerPeerId; } return false; } private static long TryGetRoutedRpcId(string methodName) { if (ZRoutedRpc.instance == null) { return long.MinValue; } try { MethodInfo method = typeof(ZRoutedRpc).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return long.MinValue; } object obj = method.Invoke(ZRoutedRpc.instance, null); return (obj != null) ? Convert.ToInt64(obj) : long.MinValue; } catch { return long.MinValue; } } } public static class RunebornAdminEventMessages { private const string RpcName = "RustyHeathensRuneborn.AdminEventMessage"; private static bool _registered; public static void EnsureRegistered() { if (_registered || ZRoutedRpc.instance == null) { return; } try { ZRoutedRpc.instance.Register("RustyHeathensRuneborn.AdminEventMessage", (Action)RPC_AdminEventMessage); _registered = true; } catch { _registered = true; } } public static void SendAdminBroadcast(string title, string body) { if (!RunebornAdminTools.IsLocalAdmin()) { ShowLocal("Admin event buttons are locked."); return; } EnsureRegistered(); string text = "RUSTY HEATHENS EVENT\n" + title + "\n" + body; if (ZRoutedRpc.instance == null) { ShowLocal(text); return; } try { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RustyHeathensRuneborn.AdminEventMessage", new object[1] { text }); } catch { ShowLocal(text); } } private static void RPC_AdminEventMessage(long sender, string message) { ShowLocal(message); } private static void ShowLocal(string message) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } try { ((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } catch { } } } public static class RunebornAdminTools { private const string RustySteamId = "76561198921017348"; public static bool IsLocalAdmin() { try { return GetLocalAdminDebugText().Contains("76561198921017348"); } catch { return false; } } public static string GetLocalAdminDebugText() { try { List localSteamIdCandidates = GetLocalSteamIdCandidates(); if (localSteamIdCandidates.Count == 0) { return "No SteamID detected"; } return string.Join(", ", localSteamIdCandidates.ToArray()); } catch { return "SteamID debug failed"; } } private static List GetLocalSteamIdCandidates() { List list = new List(); try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { AddCandidate(list, TryCallNoArgs(localPlayer, "GetPlayerID")); AddCandidate(list, TryCallNoArgs(localPlayer, "GetNetworkUserId")); AddCandidate(list, TryCallNoArgs(localPlayer, "GetSteamID")); AddCandidate(list, GetFieldValue(localPlayer, "m_playerID")); AddCandidate(list, GetFieldValue(localPlayer, "m_steamID")); } object zNetInstance = GetZNetInstance(); if (zNetInstance != null) { AddCandidate(list, TryCallNoArgs(zNetInstance, "GetUID")); AddCandidate(list, TryCallNoArgs(zNetInstance, "GetSteamID")); AddCandidate(list, GetFieldValue(zNetInstance, "m_steamID")); AddCandidate(list, GetFieldValue(zNetInstance, "m_mySteamID")); AddCandidate(list, GetFieldValue(zNetInstance, "m_steamId")); AddCandidate(list, GetFieldValue(zNetInstance, "m_userID")); } AddSteamworksCandidate(list); AddKnownTypeCandidates(list); } catch { } return list; } private static void AddCandidate(List candidates, object value) { if (value == null) { return; } AddCandidateString(candidates, value.ToString()); Type type = value.GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text = fieldInfo.Name.ToLowerInvariant(); if (!text.Contains("id") && !text.Contains("steam")) { continue; } try { object value2 = fieldInfo.GetValue(value); if (value2 != null) { AddCandidateString(candidates, value2.ToString()); } } catch { } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { string text2 = propertyInfo.Name.ToLowerInvariant(); if ((!text2.Contains("id") && !text2.Contains("steam")) || propertyInfo.GetIndexParameters().Length != 0) { continue; } try { object value3 = propertyInfo.GetValue(value, null); if (value3 != null) { AddCandidateString(candidates, value3.ToString()); } } catch { } } } private static void AddCandidateString(List candidates, string value) { string text = NormalizeSteamId(value); if (!string.IsNullOrEmpty(text) && !candidates.Contains(text)) { candidates.Add(text); } } private static string NormalizeSteamId(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } string text = string.Empty; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (char.IsDigit(c)) { text += c; continue; } if (text.Length >= 15) { return text; } text = string.Empty; } if (text.Length >= 15) { return text; } return string.Empty; } private static object GetZNetInstance() { try { Type typeFromHandle = typeof(ZNet); FieldInfo field = typeFromHandle.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value = field.GetValue(null); if (value != null) { return value; } } PropertyInfo property = typeFromHandle.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value2 = property.GetValue(null, null); if (value2 != null) { return value2; } } return Object.FindObjectOfType(typeFromHandle); } catch { return null; } } private static object GetFieldValue(object target, string fieldName) { if (target == null) { return null; } try { FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); return (field != null) ? field.GetValue(target) : null; } catch { return null; } } private static object TryCallNoArgs(object target, string methodName) { if (target == null) { return null; } try { MethodInfo method = target.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); return (method != null) ? method.Invoke(target, null) : null; } catch { return null; } } private static void AddSteamworksCandidate(List candidates) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("Steamworks.SteamUser"); if (!(type == null)) { MethodInfo method = type.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (!(method == null)) { object value = method.Invoke(null, null); AddCandidate(candidates, value); } } } } catch { } } private static void AddKnownTypeCandidates(List candidates) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type[] array = SafeGetTypes(assemblies[i]); foreach (Type type in array) { string text = type.Name.ToLowerInvariant(); if (text.Contains("platform") || text.Contains("steam") || text.Contains("playfab")) { object staticInstance = GetStaticInstance(type); if (staticInstance != null) { AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetLocalUserId")); AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetLocalUserID")); AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetLocalUser")); AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetUserID")); AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetUID")); AddCandidate(candidates, TryCallNoArgs(staticInstance, "GetSteamID")); AddCandidate(candidates, GetFieldValue(staticInstance, "m_steamID")); AddCandidate(candidates, GetFieldValue(staticInstance, "m_userID")); } } } } } catch { } } private static Type[] SafeGetTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch { return new Type[0]; } } private static object GetStaticInstance(Type type) { try { FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value = field.GetValue(null); if (value != null) { return value; } } PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value2 = property.GetValue(null, null); if (value2 != null) { return value2; } } } catch { } return null; } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class RunebornBossKillMessagesPatch { private static string _lastBossKey = string.Empty; private static float _lastBossMessageTime; private static void Postfix(Character __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } string bossDisplayName = GetBossDisplayName(((Object)((Component)__instance).gameObject).name); if (string.IsNullOrEmpty(bossDisplayName)) { return; } string text = bossDisplayName.ToLowerInvariant(); if (_lastBossKey == text && Time.time - _lastBossMessageTime < 8f) { return; } _lastBossKey = text; _lastBossMessageTime = Time.time; ((Character)Player.m_localPlayer).Message((MessageType)2, "The Rusty Heathens defeated " + bossDisplayName + "!\nValhalla heard the victory roar.", 0, (Sprite)null); try { ZLog.Log((object)("[RustyHeathensRuneborn] Boss defeated: " + bossDisplayName)); } catch { } } private static string GetBossDisplayName(string rawName) { if (string.IsNullOrEmpty(rawName)) { return string.Empty; } string text = rawName.ToLowerInvariant(); if (text.Contains("eikthyr")) { return "Eikthyr"; } if (text.Contains("gd_king") || text.Contains("theelder")) { return "The Elder"; } if (text.Contains("bonemass")) { return "Bonemass"; } if (text.Contains("dragon") || text.Contains("moder")) { return "Moder"; } if (text.Contains("goblinking") || text.Contains("yagluth")) { return "Yagluth"; } if (text.Contains("queen") || text.Contains("seekerqueen")) { return "The Queen"; } if (text.Contains("fader")) { return "Fader"; } return string.Empty; } } [HarmonyPatch(typeof(Player), "Awake")] public static class RunebornWelcomeMessageOncePlayerAwakePatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } public sealed class RunebornWelcomeMessageOnce : MonoBehaviour { private static bool _welcomeShownThisGameSession; private Player _player; private bool _shown; private float _showAt; private const string WelcomeText = "Welcome to the Rusty Heathens\nSurvive the chaos. Earn Valhalla."; private void Awake() { _player = ((Component)this).GetComponent(); _showAt = Time.time + 5f; if (_welcomeShownThisGameSession) { _shown = true; } } private void Update() { if (_shown || (Object)(object)_player == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)_player || Time.time < _showAt) { return; } _shown = true; _welcomeShownThisGameSession = true; try { ((Character)_player).Message((MessageType)2, "Welcome to the Rusty Heathens\nSurvive the chaos. Earn Valhalla.", 0, (Sprite)null); } catch { } } } [HarmonyPatch(typeof(Player), "Awake")] public static class RunebornServerReminderPlayerAwakePatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } public sealed class RunebornServerReminder : MonoBehaviour { private static bool _serverReminderShownThisGameSession; private Player _player; private int _messagesShown; private float _nextMessageAt; public static bool ServerReminderEnabled = true; private const int ReminderRepeatCount = 1; private const float ReminderFirstDelay = 16f; private const float ReminderRepeatDelay = 6f; private const string ReminderText = "RUSTY HEATHENS SERVER\n\nRespect the crew.\nNo stealing.\nNo boss fights without the group.\nHelp with body recovery when you can.\nJoin Discord for rules, events, and updates.\n\nSkal, Heathens!"; private void Awake() { _player = ((Component)this).GetComponent(); _nextMessageAt = Time.time + 16f; if (_serverReminderShownThisGameSession) { _messagesShown = 1; } } private void Update() { if (_messagesShown < 1 && ServerReminderEnabled && !((Object)(object)_player == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)_player) && !(Time.time < _nextMessageAt)) { try { ((Character)_player).Message((MessageType)2, "RUSTY HEATHENS SERVER\n\nRespect the crew.\nNo stealing.\nNo boss fights without the group.\nHelp with body recovery when you can.\nJoin Discord for rules, events, and updates.\n\nSkal, Heathens!", 0, (Sprite)null); } catch { } _messagesShown++; _serverReminderShownThisGameSession = true; _nextMessageAt = Time.time + 6f; } } } [HarmonyPatch(typeof(Player), "Awake")] public static class RunebornRaidWarningsPlayerAwakePatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } public sealed class RunebornRaidWarnings : MonoBehaviour { private Player _player; private string _activeRaid = string.Empty; private float _nextCheck; private float _nextRepeatMessage; public static bool RaidWarningsEnabled = true; private void Awake() { _player = ((Component)this).GetComponent(); } private void Update() { if (!((Object)(object)_player == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)_player) && RaidWarningsEnabled && !(Time.time < _nextCheck)) { _nextCheck = Time.time + 1f; string currentRaidDisplayName = GetCurrentRaidDisplayName(); if (!string.IsNullOrEmpty(currentRaidDisplayName) && string.IsNullOrEmpty(_activeRaid)) { _activeRaid = currentRaidDisplayName; _nextRepeatMessage = Time.time + 45f; ShowCenter("RAID WARNING\n" + currentRaidDisplayName + "\nThe Rusty Heathens are under attack!"); } else if (!string.IsNullOrEmpty(currentRaidDisplayName) && currentRaidDisplayName == _activeRaid && Time.time >= _nextRepeatMessage) { _nextRepeatMessage = Time.time + 45f; ShowCenter("RAID STILL ACTIVE\n" + currentRaidDisplayName + "\nHold the line, Heathens!"); } else if (string.IsNullOrEmpty(currentRaidDisplayName) && !string.IsNullOrEmpty(_activeRaid)) { ShowCenter("RAID CLEARED\nThe Heathens still stand."); _activeRaid = string.Empty; _nextRepeatMessage = 0f; } } } public static string GetCurrentRaidDisplayName() { object currentRandomEventObject = GetCurrentRandomEventObject(); if (currentRandomEventObject == null) { return string.Empty; } string text = GetStringField(currentRandomEventObject, "m_name"); if (string.IsNullOrEmpty(text)) { text = currentRandomEventObject.ToString(); } return MakeRaidNameFriendly(text); } private static object GetCurrentRandomEventObject() { try { Type type = Type.GetType("RandEventSystem, Assembly-CSharp"); if (type == null) { return null; } object obj = GetStaticField(type, "instance") ?? GetStaticField(type, "m_instance"); if (obj == null) { Object val = Object.FindObjectOfType(type); if (val != (Object)null) { obj = val; } } if (obj == null) { return null; } return GetInstanceField(obj, "m_randomEvent") ?? GetInstanceField(obj, "m_currentRandomEvent") ?? GetInstanceField(obj, "m_event"); } catch { return null; } } private static object GetStaticField(Type type, string fieldName) { FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { return null; } return field.GetValue(null); } private static object GetInstanceField(object target, string fieldName) { if (target == null) { return null; } FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { return null; } return field.GetValue(target); } private static string GetStringField(object target, string fieldName) { object instanceField = GetInstanceField(target, fieldName); if (instanceField == null) { return string.Empty; } return instanceField.ToString(); } private static string MakeRaidNameFriendly(string rawName) { if (string.IsNullOrEmpty(rawName)) { return "Unknown Raid"; } string text = rawName.ToLowerInvariant(); if (text.Contains("army_eikthyr")) { return "Eikthyr's Herd"; } if (text.Contains("army_theelder") || text.Contains("foresttrolls")) { return "The Forest Moves"; } if (text.Contains("army_bonemass") || text.Contains("skeletons")) { return "Bonemass' Dead"; } if (text.Contains("army_moder") || text.Contains("wolves")) { return "The Mountain Hunt"; } if (text.Contains("army_goblin") || text.Contains("goblin")) { return "The Plains Warband"; } if (text.Contains("army_gjall") || text.Contains("gjall")) { return "Mistlands Terror"; } if (text.Contains("army_charred") || text.Contains("charred")) { return "Ashlands Siege"; } return CleanName(rawName); } private static string CleanName(string rawName) { string text = rawName.Replace("$event_", string.Empty).Replace("event_", string.Empty).Replace("army_", string.Empty) .Replace("_", " ") .Trim(); if (string.IsNullOrEmpty(text)) { return "Unknown Raid"; } StringBuilder stringBuilder = new StringBuilder(text.Length); bool flag = true; string text2 = text; foreach (char c in text2) { if (char.IsWhiteSpace(c)) { stringBuilder.Append(c); flag = true; } else if (flag) { stringBuilder.Append(char.ToUpperInvariant(c)); flag = false; } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private void ShowCenter(string text) { if ((Object)(object)_player == (Object)null) { return; } try { ((Character)_player).Message((MessageType)2, text, 0, (Sprite)null); } catch { } } } [HarmonyPatch(typeof(Player), "Awake")] public static class RunebornServerComfortPlayerAwakePatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] public static class RunebornBodyRecoveryCarryWeightPatch { private static void Postfix(Player __instance, ref float __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)__instance) && RunebornServerComfort.IsBodyRecoveryActiveForLocalPlayer()) { __result += 150f; } } } [HarmonyPatch(typeof(Character), "UseStamina")] public static class RunebornBodyRecoveryStaminaUsePatch { private static void Prefix(Character __instance, ref float stamina) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && (object)__instance == Player.m_localPlayer) { if (RunebornServerComfort.IsBodyRecoveryActiveForLocalPlayer()) { stamina *= 0.65f; } if (RunebornServerComfort.IsRustyRestedActiveForLocalPlayer()) { stamina *= 0.9f; } if (RunebornServerComfort.IsSeppyBuffActiveForLocalPlayer() && RunebornServerComfort.IsLocalPlayerBlocking()) { stamina *= 0.85f; } if (RunebornServerComfort.IsXpertBuffActiveForLocalPlayer()) { stamina *= 0.9f; } if (RunebornServerComfort.IsLocalPlayerInChaosMovementStaminaState()) { stamina *= 0.85f; } if (RunebornServerComfort.IsWitchyMagicStaminaActiveForLocalPlayer()) { stamina *= 0.9f; } if (RunebornServerComfort.IsBuildComfortActiveForLocalPlayer()) { stamina *= 0.5f; } } } } [HarmonyPatch(typeof(Character), "Damage")] public static class RunebornSeppyBuffDamageReductionPatch { private static void Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null) && hit != null && !((Object)(object)Player.m_localPlayer == (Object)null) && (object)__instance == Player.m_localPlayer && RunebornServerComfort.IsSeppyBuffActiveForLocalPlayer()) { RunebornServerComfort.ScaleHitDamage(hit, 0.9f); } } } [HarmonyPatch(typeof(Character), "Damage")] public static class RunebornXpertBuffDamageBoostPatch { private static void Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null) && hit != null && !((Object)(object)Player.m_localPlayer == (Object)null)) { Character val = RunebornServerComfort.TryGetHitAttacker(hit); if (!((Object)(object)val == (Object)null) && (object)val == Player.m_localPlayer && (object)__instance != Player.m_localPlayer && RunebornServerComfort.IsXpertBuffActiveForLocalPlayer()) { RunebornServerComfort.ScaleHitDamage(hit, 1.1f); } } } } [HarmonyPatch(typeof(Player), "Update")] public static class RunebornChaosBringerMovementOnlyPatch { private static void Postfix(Player __instance) { try { RunebornServerComfort.ApplyChaosBringerMovementOnly(__instance); } catch { } } } public sealed class RunebornServerComfort : MonoBehaviour { private const string DeathCountKey = "RustyHeathensRuneborn.DeathCount"; private const string BodyRecoveryExpiresKey = "RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks"; private const float BodyRecoverySeconds = 600f; private Player _player; private bool _wasDead; private bool _bodyRecoveryNoticeShown; private float _nextCheck; private string _deathMessageRepeatText = string.Empty; private int _deathMessageRepeatsLeft; private float _nextDeathMessageRepeatAt; private const int DefaultMaxSelectedBuffs = 2; private const string MaxSelectedBuffsKey = "RustyHeathensRuneborn.MaxSelectedBuffs"; private const string SelectedBuffsKey = "RustyHeathensRuneborn.SelectedBuffs"; private const string EnabledBuffMaskKey = "RustyHeathensRuneborn.EnabledBuffMask"; private const int RustyBuffBit = 1; private const int SeppyBuffBit = 2; private const int XpertBuffBit = 4; private const int ChaosBuffBit = 8; private const int WitchyBuffBit = 16; private const int AllBuffsMask = 31; private static readonly Dictionary> ChaosMovementBaseValues = new Dictionary>(); private static int MaxSelectedBuffs => Mathf.Clamp(PlayerPrefs.GetInt("RustyHeathensRuneborn.MaxSelectedBuffs", 2), 1, 3); private void Awake() { _player = ((Component)this).GetComponent(); } private void Update() { if (!((Object)(object)_player == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)_player) && !(Time.time < _nextCheck)) { _nextCheck = Time.time + 0.25f; bool flag = IsPlayerDead(_player); if (flag && !_wasDead) { RegisterLocalDeath(); } if (!flag && _wasDead) { _bodyRecoveryNoticeShown = false; } _wasDead = flag; TryShowBodyRecoveryReminder(flag); TryRepeatDeathMessage(); } } private void RegisterLocalDeath() { int num = PlayerPrefs.GetInt("RustyHeathensRuneborn.DeathCount", 0) + 1; PlayerPrefs.SetInt("RustyHeathensRuneborn.DeathCount", num); PlayerPrefs.SetString("RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks", DateTime.UtcNow.AddSeconds(600.0).Ticks.ToString()); PlayerPrefs.Save(); string funnyDeathMessage = GetFunnyDeathMessage(num); string text = "Death #" + num + "\n" + funnyDeathMessage + "\nBody Recovery ready for 10 minutes.\n+150 carry weight, less stamina used."; ShowCenter(text); _deathMessageRepeatText = text; _deathMessageRepeatsLeft = 2; _nextDeathMessageRepeatAt = Time.time + 3.5f; try { ZLog.Log((object)("[RustyHeathensRuneborn] Local death count: " + num)); } catch { } } public static string PreviewNextFunnyDeathMessage(int currentDeathCount) { return GetFunnyDeathMessage(currentDeathCount + 1); } private static string GetFunnyDeathMessage(int deathCount) { if (deathCount <= 1) { return "First blood. Valhalla is watching."; } switch (deathCount) { case 10: return "Ten deaths. The Heathens are officially warming up."; case 25: return "Twenty-five deaths. Odin filed a report."; case 50: return "Fifty deaths. The death counter needs a bigger boat."; case 100: return "One hundred deaths. Seppy would be proud."; default: { string[] array = new string[20] { "Another shortcut to Valhalla.", "Odin saw that. So did chat.", "Body recovery team, saddle up.", "The ground won that fight.", "Your gear is now on a side quest.", "The Rusty Heathens death counter demands tribute.", "Not a death, just aggressive fast travel.", "That was tactical. Probably.", "The trolls are taking notes.", "Somebody clip that.", "Valheim said no.", "The corpse run begins.", "Rusty Heathens rule one: do not panic. Too late.", "The swamp sends its regards.", "The mountain claimed another kneecap.", "Ashlands heard you were feeling confident.", "The Plains mosquito has entered the chat.", "Mistlands visibility: zero. Survival odds: also zero.", "A brave death. A questionable plan.", "The gear is fine. The owner, less so." }; int num = Mathf.Abs(deathCount - 2) % array.Length; return array[num]; } } } private void TryRepeatDeathMessage() { if (_deathMessageRepeatsLeft > 0) { if (string.IsNullOrEmpty(_deathMessageRepeatText)) { _deathMessageRepeatsLeft = 0; } else if (!(Time.time < _nextDeathMessageRepeatAt)) { ShowCenter(_deathMessageRepeatText); _deathMessageRepeatsLeft--; _nextDeathMessageRepeatAt = Time.time + 3.5f; } } } private void TryShowBodyRecoveryReminder(bool isDead) { if (!isDead && !_bodyRecoveryNoticeShown) { float bodyRecoverySecondsLeft = GetBodyRecoverySecondsLeft(); if (!(bodyRecoverySecondsLeft <= 0f)) { _bodyRecoveryNoticeShown = true; ShowCenter("Body Recovery Active\n" + Mathf.CeilToInt(bodyRecoverySecondsLeft / 60f) + " minutes to get your gear."); } } } public static bool IsSeppyBuffActiveForLocalPlayer() { if (IsBuffSelected("seppy")) { return IsLocalPlayerRested(); } return false; } public static bool IsLocalPlayerBlocking() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { MethodInfo method = typeof(Humanoid).GetMethod("IsBlocking", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object obj = method.Invoke(localPlayer, null); if (obj is bool) { return (bool)obj; } } } catch { } return IsShieldEquipped(localPlayer); } private static bool IsShieldEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } try { MethodInfo method = typeof(Humanoid).GetMethod("GetLeftItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return false; } object obj = method.Invoke(player, null); if (obj == null) { return false; } string text = GetItemSharedName(obj).ToLowerInvariant(); return text.Contains("shield") || text.Contains("buckler") || text.Contains("tower"); } catch { return false; } } public static void ScaleHitDamage(HitData hit, float scale) { if (hit == null) { return; } try { object fieldValue = GetFieldValue(hit, "m_damage"); if (fieldValue != null) { ScaleDamageTypesObject(fieldValue, scale); } } catch { } } private static object GetFieldValue(object target, string fieldName) { if (target == null) { return null; } FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { return null; } return field.GetValue(target); } private static void ScaleDamageTypesObject(object damageTypes, float scale) { if (damageTypes == null) { return; } FieldInfo[] fields = damageTypes.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.FieldType != typeof(float))) { string text = fieldInfo.Name.ToLowerInvariant(); if (text.Contains("damage") || text.Contains("blunt") || text.Contains("slash") || text.Contains("pierce") || text.Contains("chop") || text.Contains("pickaxe") || text.Contains("fire") || text.Contains("frost") || text.Contains("lightning") || text.Contains("poison") || text.Contains("spirit")) { float num = (float)fieldInfo.GetValue(damageTypes); fieldInfo.SetValue(damageTypes, num * scale); } } } } public static bool IsXpertBuffActiveForLocalPlayer() { if (IsBuffSelected("xpert") && IsLocalPlayerRested()) { return IsLocalPlayerUsingBerserkerWeapon(); } return false; } public static bool IsLocalPlayerUsingBerserkerWeapon() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { MethodInfo method = typeof(Humanoid).GetMethod("GetRightItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return false; } object obj = method.Invoke(localPlayer, null); if (obj == null) { return false; } string text = GetItemSharedName(obj).ToLowerInvariant(); return text.Contains("axe") || text.Contains("battleaxe") || text.Contains("sword") || text.Contains("mace") || text.Contains("club") || text.Contains("knife") || text.Contains("atgeir") || text.Contains("spear"); } catch { return false; } } public static Character TryGetHitAttacker(HitData hit) { if (hit == null) { return null; } try { MethodInfo method = ((object)hit).GetType().GetMethod("GetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null) { return null; } object? obj = method.Invoke(hit, null); return (Character)((obj is Character) ? obj : null); } catch { return null; } } public static bool IsChaosBringerBuffActiveForLocalPlayer() { if (IsBuffSelected("chaos")) { return IsLocalPlayerRested(); } return false; } public static void ApplyChaosBringerMovementOnly(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || player != Player.m_localPlayer) { return; } int instanceID = ((Object)player).GetInstanceID(); bool flag = IsChaosBringerBuffActiveForLocalPlayer(); string[] array = new string[6] { "m_walkSpeed", "m_runSpeed", "m_crouchSpeed", "m_swimSpeed", "m_jumpForce", "m_jumpForceForward" }; if (flag) { if (!ChaosMovementBaseValues.ContainsKey(instanceID)) { ChaosMovementBaseValues[instanceID] = new Dictionary(); } Dictionary dictionary = ChaosMovementBaseValues[instanceID]; string[] array2 = array; foreach (string text in array2) { FieldInfo fieldInfo = FindChaosFloatField(player, text); if (!(fieldInfo == null)) { if (!dictionary.ContainsKey(text)) { dictionary[text] = (float)fieldInfo.GetValue(player); } float num = 1.1f; if (text == "m_crouchSpeed") { num = 1.2f; } if (text == "m_jumpForce" || text == "m_jumpForceForward") { num = 1.12f; } fieldInfo.SetValue(player, dictionary[text] * num); } } } else { RestoreChaosBringerMovementOnly(player, instanceID); } } private static void RestoreChaosBringerMovementOnly(Player player, int playerKey) { if ((Object)(object)player == (Object)null || !ChaosMovementBaseValues.ContainsKey(playerKey)) { return; } foreach (KeyValuePair item in ChaosMovementBaseValues[playerKey]) { FieldInfo fieldInfo = FindChaosFloatField(player, item.Key); if (fieldInfo != null) { fieldInfo.SetValue(player, item.Value); } } ChaosMovementBaseValues.Remove(playerKey); } private static FieldInfo FindChaosFloatField(object target, string fieldName) { if (target == null || string.IsNullOrEmpty(fieldName)) { return null; } Type type = target.GetType(); while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(float)) { return field; } type = type.BaseType; } return null; } public static bool IsLocalPlayerInChaosMovementStaminaState() { if (!IsChaosBringerBuffActiveForLocalPlayer()) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if (!TryCallChaosBool(localPlayer, "IsSwimming") && !TryCallChaosBool(localPlayer, "InDodge") && !TryCallChaosBool(localPlayer, "IsDodgeInvincible") && !TryGetChaosBoolField(localPlayer, "m_dodgeInvincible")) { return TryGetChaosBoolField(localPlayer, "m_dodging"); } return true; } private static bool TryCallChaosBool(object target, string methodName) { if (target == null || string.IsNullOrEmpty(methodName)) { return false; } Type type = target.GetType(); while (type != null) { MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.ReturnType == typeof(bool)) { return (bool)method.Invoke(target, null); } type = type.BaseType; } return false; } private static bool TryGetChaosBoolField(object target, string fieldName) { if (target == null || string.IsNullOrEmpty(fieldName)) { return false; } Type type = target.GetType(); while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { return (bool)field.GetValue(target); } type = type.BaseType; } return false; } public static bool IsWitchyBuffActiveForLocalPlayer() { if (IsBuffSelected("witchy")) { return IsLocalPlayerRested(); } return false; } public static bool IsWitchyMagicStaminaActiveForLocalPlayer() { if (IsWitchyBuffActiveForLocalPlayer()) { return IsLocalPlayerUsingWitchyWeapon(); } return false; } public static bool IsLocalPlayerUsingWitchyWeapon() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { MethodInfo method = typeof(Humanoid).GetMethod("GetRightItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return false; } object obj = method.Invoke(localPlayer, null); if (obj == null) { return false; } string text = GetWitchyItemName(obj).ToLowerInvariant(); return text.Contains("staff") || text.Contains("magic") || text.Contains("eitr") || text.Contains("wand") || text.Contains("scepter") || text.Contains("trollstav") || text.Contains("deadraiser") || text.Contains("protection") || text.Contains("embers") || text.Contains("frost"); } catch { return false; } } private static string GetWitchyItemName(object item) { if (item == null) { return string.Empty; } try { FieldInfo field = item.GetType().GetField("m_shared", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return item.ToString(); } object value = field.GetValue(item); if (value == null) { return item.ToString(); } FieldInfo field2 = value.GetType().GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 == null) { return value.ToString(); } object value2 = field2.GetValue(value); if (value2 == null) { return string.Empty; } return value2.ToString(); } catch { return string.Empty; } } public static bool IsBuildComfortActiveForLocalPlayer() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { MethodInfo method = typeof(Humanoid).GetMethod("GetRightItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return false; } object obj = method.Invoke(localPlayer, null); if (obj == null) { return false; } string text = GetItemSharedName(obj).ToLowerInvariant(); return text.Contains("hammer") || text.Contains("hoe") || text.Contains("cultivator"); } catch { return false; } } private static string GetItemSharedName(object item) { if (item == null) { return string.Empty; } FieldInfo field = item.GetType().GetField("m_shared", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(item) : null); if (obj == null) { return string.Empty; } FieldInfo field2 = obj.GetType().GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = ((field2 != null) ? field2.GetValue(obj) : null); if (obj2 == null) { return string.Empty; } return obj2.ToString(); } public static int GetMaxSelectedBuffs() { return MaxSelectedBuffs; } public static void SetMaxSelectedBuffs(int maxBuffs) { int maxBuffs2 = Mathf.Clamp(maxBuffs, 1, 3); ApplySyncedMaxSelectedBuffs(maxBuffs2); if (RunebornAdminTools.IsLocalAdmin()) { RunebornBuffSlotSyncFoundation.BroadcastMaxBuffSlots(maxBuffs2); } } public static void ApplySyncedMaxSelectedBuffs(int maxBuffs) { int num = Mathf.Clamp(maxBuffs, 1, 3); PlayerPrefs.SetInt("RustyHeathensRuneborn.MaxSelectedBuffs", num); PlayerPrefs.Save(); SaveSelectedBuffIds(GetSelectedBuffIds()); } public static int GetEnabledBuffMask() { return PlayerPrefs.GetInt("RustyHeathensRuneborn.EnabledBuffMask", 31) & 0x1F; } public static void ApplySyncedEnabledBuffMask(int enabledMask) { int num = enabledMask & 0x1F; PlayerPrefs.SetInt("RustyHeathensRuneborn.EnabledBuffMask", num); PlayerPrefs.Save(); SaveSelectedBuffIds(GetSelectedBuffIds()); } public static bool IsBuffEnabled(string buffId) { int buffBit = GetBuffBit(buffId); if (buffBit != 0) { return (GetEnabledBuffMask() & buffBit) != 0; } return false; } public static string GetBuffEnabledButtonText(string buffId, string displayName) { if (!IsBuffEnabled(buffId)) { return "Disabled: " + displayName + " - click to enable"; } return "Enabled: " + displayName + " - click to disable"; } public static string ToggleBuffEnabledByAdmin(string buffId, string displayName) { if (!RunebornAdminTools.IsLocalAdmin()) { return "Admin controls are locked."; } int buffBit = GetBuffBit(buffId); if (buffBit == 0) { return "Invalid buff."; } int enabledBuffMask = GetEnabledBuffMask(); if ((enabledBuffMask & buffBit) != 0) { enabledBuffMask &= ~buffBit; ApplySyncedEnabledBuffMask(enabledBuffMask); RunebornBuffAvailabilitySync.BroadcastEnabledBuffMask(enabledBuffMask); return displayName + " disabled. Existing selections were removed."; } enabledBuffMask |= buffBit; ApplySyncedEnabledBuffMask(enabledBuffMask); RunebornBuffAvailabilitySync.BroadcastEnabledBuffMask(enabledBuffMask); return displayName + " enabled."; } public static string GetEnabledBuffSummary() { List list = new List(); if (IsBuffEnabled("rusty")) { list.Add("Rusty"); } if (IsBuffEnabled("seppy")) { list.Add("Seppy"); } if (IsBuffEnabled("xpert")) { list.Add("Xpert"); } if (IsBuffEnabled("chaos")) { list.Add("Chaos Bringer"); } if (IsBuffEnabled("witchy")) { list.Add("Witchy"); } if (list.Count <= 0) { return "None"; } return string.Join(", ", list.ToArray()); } private static int GetBuffBit(string buffId) { return NormalizeBuffId(buffId) switch { "rusty" => 1, "seppy" => 2, "xpert" => 4, "chaos" => 8, "witchy" => 16, _ => 0, }; } public static int GetSelectedBuffCount() { return GetSelectedBuffIds().Count; } public static string GetBuffButtonText(string buffId, string displayName) { if (!IsBuffEnabled(buffId)) { return "Disabled by server: " + displayName; } if (IsBuffSelected(buffId)) { return "Selected: " + displayName + " - click to remove"; } if (GetSelectedBuffCount() >= MaxSelectedBuffs) { return "Locked: " + displayName + " - " + GetSelectedBuffCount() + "/" + MaxSelectedBuffs + " chosen"; } return "Choose: " + displayName; } public static string ToggleBuffSelection(string buffId, string displayName) { buffId = NormalizeBuffId(buffId); if (string.IsNullOrEmpty(buffId)) { return "Invalid buff."; } if (!IsBuffEnabled(buffId)) { return displayName + " is disabled by the server."; } List selectedBuffIds = GetSelectedBuffIds(); if (selectedBuffIds.Contains(buffId)) { selectedBuffIds.Remove(buffId); SaveSelectedBuffIds(selectedBuffIds); return displayName + " removed. Buff slots: " + selectedBuffIds.Count + "/" + MaxSelectedBuffs; } if (selectedBuffIds.Count >= MaxSelectedBuffs) { return "You can only choose " + MaxSelectedBuffs + " buff(s). Remove one first."; } selectedBuffIds.Add(buffId); SaveSelectedBuffIds(selectedBuffIds); return displayName + " chosen. Buff slots: " + selectedBuffIds.Count + "/" + MaxSelectedBuffs; } public static bool IsBuffSelected(string buffId) { buffId = NormalizeBuffId(buffId); if (string.IsNullOrEmpty(buffId) || !IsBuffEnabled(buffId)) { return false; } return GetSelectedBuffIds().Contains(buffId); } private static List GetSelectedBuffIds() { string text = PlayerPrefs.GetString("RustyHeathensRuneborn.SelectedBuffs", string.Empty); List list = new List(); if (string.IsNullOrEmpty(text)) { return list; } string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = NormalizeBuffId(array[i]); if (!string.IsNullOrEmpty(text2) && IsBuffEnabled(text2) && !list.Contains(text2)) { list.Add(text2); } if (list.Count >= MaxSelectedBuffs) { break; } } return list; } private static void SaveSelectedBuffIds(List selected) { if (selected == null) { selected = new List(); } List list = new List(); foreach (string item in selected) { string text = NormalizeBuffId(item); if (!string.IsNullOrEmpty(text) && IsBuffEnabled(text) && !list.Contains(text)) { list.Add(text); } if (list.Count >= MaxSelectedBuffs) { break; } } PlayerPrefs.SetString("RustyHeathensRuneborn.SelectedBuffs", string.Join(",", list.ToArray())); PlayerPrefs.Save(); } private static string NormalizeBuffId(string buffId) { if (buffId == null) { return string.Empty; } return buffId.Trim().ToLowerInvariant(); } public static bool IsLocalPlayerRested() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { SEMan sEMan = ((Character)localPlayer).GetSEMan(); if (sEMan == null) { return false; } return sEMan.HaveStatusEffect(StringExtensionMethods.GetStableHashCode("Rested")); } catch { return false; } } public static bool IsRustyRestedActiveForLocalPlayer() { if (IsBuffSelected("rusty")) { return IsLocalPlayerRested(); } return false; } public static bool IsBodyRecoveryActiveForLocalPlayer() { return GetBodyRecoverySecondsLeft() > 0f; } public static float GetBodyRecoverySecondsLeft() { string text = PlayerPrefs.GetString("RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks", string.Empty); if (string.IsNullOrEmpty(text)) { return 0f; } if (!long.TryParse(text, out var result)) { PlayerPrefs.DeleteKey("RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks"); PlayerPrefs.Save(); return 0f; } double totalSeconds = (new DateTime(result, DateTimeKind.Utc) - DateTime.UtcNow).TotalSeconds; if (totalSeconds <= 0.0) { PlayerPrefs.DeleteKey("RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks"); PlayerPrefs.Save(); return 0f; } return Mathf.Max(0f, (float)totalSeconds); } public static void ActivateBodyRecoveryForMinutes(float minutes) { float num = Mathf.Max(0f, minutes * 60f); PlayerPrefs.SetString("RustyHeathensRuneborn.BodyRecoveryExpiresUtcTicks", DateTime.UtcNow.AddSeconds(num).Ticks.ToString()); PlayerPrefs.Save(); } private static bool IsPlayerDead(Player player) { if ((Object)(object)player == (Object)null) { return false; } try { MethodInfo method = typeof(Character).GetMethod("IsDead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.Invoke(player, null) is bool result) { return result; } } catch { } try { FieldInfo field = typeof(Character).GetField("m_dead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.GetValue(player) is bool result2) { return result2; } } catch { } return false; } private void ShowCenter(string text) { if ((Object)(object)_player == (Object)null) { return; } try { ((Character)_player).Message((MessageType)2, text, 0, (Sprite)null); } catch { } } }