Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AFK v0.1.0
plugins/AFK.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AFK")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AFK")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace AFK { [BepInPlugin("orax.AFK", "AFK", "0.1.0")] internal class AFK : BaseUnityPlugin { public const string PluginGUID = "orax.AFK"; public const string PluginName = "AFK"; public const string PluginVersion = "0.1.0"; internal static Harmony? PatchHarmony; private static AFK? s_instance; internal static void LogDebug(string message) { AFK? aFK = s_instance; if (aFK != null) { ((BaseUnityPlugin)aFK).Logger.LogDebug((object)message); } } internal static void LogInfo(string message) { AFK? aFK = s_instance; if (aFK != null) { ((BaseUnityPlugin)aFK).Logger.LogInfo((object)message); } } internal static void LogWarning(string message) { AFK? aFK = s_instance; if (aFK != null) { ((BaseUnityPlugin)aFK).Logger.LogWarning((object)message); } } internal static void LogError(string message) { AFK? aFK = s_instance; if (aFK != null) { ((BaseUnityPlugin)aFK).Logger.LogError((object)message); } } private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) s_instance = this; AfkConfig.Bind(((BaseUnityPlugin)this).Config); PatchHarmony = new Harmony("orax.AFK"); PatchHarmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("{0} {1} loaded - toggle: {2}, ", "AFK", "0.1.0", AfkConfig.ToggleKey.Value) + $"sit trigger: {AfkConfig.SitTriggers.Value}, " + $"invisible: {AfkConfig.InvisibleToEnemies.Value}, " + $"no damage: {AfkConfig.IgnoreEnemyDamage.Value}")); } private void OnDestroy() { AfkState.Shutdown(); AfkHud.Reset(); Harmony? patchHarmony = PatchHarmony; if (patchHarmony != null) { patchHarmony.UnpatchSelf(); } PatchHarmony = null; s_instance = null; } private void Update() { AfkState.Tick(); AfkHud.Sync(); } } internal static class AfkConfig { internal const string AnchorTopLeft = "TopLeft"; internal const string AnchorTopCenter = "TopCenter"; internal const string AnchorTopRight = "TopRight"; internal const string AnchorLeft = "Left"; internal const string AnchorCenter = "Center"; internal const string AnchorRight = "Right"; internal const string AnchorBottomLeft = "BottomLeft"; internal const string AnchorBottomCenter = "BottomCenter"; internal const string AnchorBottomRight = "BottomRight"; internal const string UnitPercent = "Percent"; internal const string UnitPixels = "Pixels"; internal const int MaxScreenCoordinate = 10000; internal const string DefaultIndicatorFont = "Valheim-AveriaSansLibre Bold"; private static ConfigFile? s_config; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> Notify; internal static ConfigEntry<KeyboardShortcut> ToggleKey; internal static ConfigEntry<bool> SitTriggers; internal static ConfigEntry<float> SecondsSinceDamageDealt; internal static ConfigEntry<float> SecondsSinceAggro; internal static ConfigEntry<bool> InvisibleToEnemies; internal static ConfigEntry<bool> IgnoreEnemyDamage; internal static ConfigEntry<bool> IndicatorEnabled; internal static ConfigEntry<bool> IndicatorShowWaiting; internal static ConfigEntry<string> IndicatorText; internal static ConfigEntry<int> IndicatorFontSize; internal static ConfigEntry<string>? IndicatorFont; internal static ConfigEntry<string> IndicatorColor; internal static ConfigEntry<string> IndicatorAnchor; internal static ConfigEntry<int> IndicatorPositionX; internal static ConfigEntry<int> IndicatorPositionY; internal static ConfigEntry<string> IndicatorPositionUnit; internal static void Bind(ConfigFile config) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Expected O, but got Unknown //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Expected O, but got Unknown //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Expected O, but got Unknown s_config = config; Enabled = config.Bind<bool>("1 - General", "Enabled", true, "Master switch. While false the AFK status can never be requested or granted."); Notify = config.Bind<bool>("1 - General", "Notify", true, "Show a status message (top left) whenever the AFK status changes: \"AFK: active\", \"AFK: waiting - ...\", \"AFK: paused (mod disabled)\", \"AFK: off\"."); ToggleKey = config.Bind<KeyboardShortcut>("2 - Trigger", "ToggleKey", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), "Key that toggles the AFK request (modifiers such as Ctrl/Shift are supported). Polled through ZInput like the game polls its own bindings, and ignored while typing in chat, the console, a menu or the inventory. Defaults to Not set: sitting down (the Sit trigger) is then the only way to request the status."); SitTriggers = config.Bind<bool>("2 - Trigger", "SitTriggers", true, "Pressing the Sit key (X by default, gamepad supported) also toggles the AFK request. The first press sits down and requests AFK, the second one stands up and releases it. Movement or action input still releases the status as usual, and so does the keybind."); SecondsSinceDamageDealt = config.Bind<float>("3 - Conditions", "SecondsSinceDamageDealt", 30f, new ConfigDescription("You only obtain the AFK status once you have NOT damaged any character for this many seconds (melee, arrows, bombs... - every hit you land counts). The timers start when you join a world. Allowed range: 5 to 60 seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>())); SecondsSinceAggro = config.Bind<float>("3 - Conditions", "SecondsSinceAggro", 30f, new ConfigDescription("You only obtain the AFK status once no enemy has had aggro on you for this many seconds. Aggro uses the game's own combat signal (the same one that starts combat music): an ALERTED hostile holding you as its target, re-sent about every 0.5 s while it lasts, and being hit by a character. Creatures that merely notice or flee from you (deer, boar...) never count. Allowed range: 5 to 60 seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>())); InvisibleToEnemies = config.Bind<bool>("4 - Effects", "InvisibleToEnemies", true, "While the AFK status is active, creatures cannot see, hear or target you: the same checks the vanilla ghost command passes, but synchronized through the afk_ghost flag on your player ZDO so they also apply to AI owned by the dedicated server or another client. This machine's setting is a local switch on top of that flag: install the DLL on the server and on every client that can own creature AI near you, and keep this value identical there (the server effectively has the final say for its own creatures)."); IgnoreEnemyDamage = config.Bind<bool>("4 - Effects", "IgnoreEnemyDamage", true, "While the AFK status is active, hits whose attacker is a creature (greydwarf, troll, boss...) deal no damage at all, so you cannot be killed while AFK. Damage from other players and from the environment (falling, fire) is NOT affected."); IndicatorEnabled = config.Bind<bool>("5 - Display", "IndicatorEnabled", true, "Show a persistent message in the top center of the screen for as long as the AFK status is active. It disappears the moment the status is released (this is independent from the one-shot Notify messages)."); IndicatorShowWaiting = config.Bind<bool>("5 - Display", "IndicatorShowWaiting", true, "While the request is waiting for the quiet timers, also show the pending condition message with a live countdown, in the same spot as IndicatorText and with its font size and color: \"AFK: waiting - no damage dealt, no enemy aggro (12s)\" - the seconds tick down and the line is replaced by IndicatorText as soon as the status activates."); IndicatorText = config.Bind<string>("5 - Display", "IndicatorText", "AFK", "The text of that persistent message. Leave empty to disable it."); IndicatorFontSize = config.Bind<int>("5 - Display", "IndicatorFontSize", 40, "Font size of the persistent message (clamped to 8-200)."); IndicatorColor = config.Bind<string>("5 - Display", "IndicatorColor", "#FFFFFFFF", "Color of the persistent message as HTML hex: #RRGGBB or #RRGGBBAA (the last two digits set the opacity). Invalid values fall back to white."); IndicatorAnchor = config.Bind<string>("5 - Display", "IndicatorAnchor", "TopCenter", new ConfigDescription("Point of the message placed at the position: TopLeft puts its top left corner there, Center its centre, and so on. Changing it barely moves the message (by its own size at most): use IndicatorPositionX and IndicatorPositionY to move it on screen.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[9] { "TopLeft", "TopCenter", "TopRight", "Left", "Center", "Right", "BottomLeft", "BottomCenter", "BottomRight" }), Array.Empty<object>())); IndicatorPositionX = config.Bind<int>("5 - Display", "IndicatorPositionX", 50, new ConfigDescription("Horizontal position of the anchor: percent of the screen width (0 = left edge, 100 = right edge), or pixels from the left edge when the unit is Pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>())); IndicatorPositionY = config.Bind<int>("5 - Display", "IndicatorPositionY", 15, new ConfigDescription("Vertical position of the anchor: percent of the screen height (0 = top edge, 100 = bottom edge), or pixels from the top edge when the unit is Pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>())); IndicatorPositionUnit = config.Bind<string>("5 - Display", "IndicatorPositionUnit", "Percent", new ConfigDescription("Whether IndicatorPositionX and IndicatorPositionY are percents of the screen or pixels.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Percent", "Pixels" }), Array.Empty<object>())); } internal static void BindIndicatorFont(string[] fontNames) { //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown if (IndicatorFont != null || s_config == null || fontNames.Length == 0) { return; } List<string> choices = new List<string>(); HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Add(""); foreach (string text in fontNames) { bool flag = text.EndsWith("Bold", StringComparison.OrdinalIgnoreCase); bool flag2 = text.EndsWith("Italic", StringComparison.OrdinalIgnoreCase); Add(text); if (!flag) { Add(text + " Bold"); } if (!flag2) { Add(text + " Italic"); } if (!flag && !flag2) { Add(text + " Bold Italic"); } } Add("Valheim-AveriaSansLibre Bold"); IndicatorFont = s_config.Bind<string>("5 - Display", "IndicatorFont", "Valheim-AveriaSansLibre Bold", new ConfigDescription("Font of the persistent message. Leave empty to use the game's own UI font, in bold. Otherwise pick one of the fonts the game has loaded - each font is offered as regular, Bold, Italic and Bold Italic. The choices are refreshed in this file each time the game enters a world.", (AcceptableValueBase)(object)new AcceptableValueList<string>(choices.ToArray()), Array.Empty<object>())); AFK.LogInfo("IndicatorFont choices: " + string.Join(", ", choices)); void Add(string value) { if (seen.Add(value)) { choices.Add(value); } } } } internal static class AfkHud { private const int SortingOrder = -1000; private static GameObject? s_root; private static TextMeshProUGUI? s_label; private static string? s_shownText; private static int s_shownFontSize = -1; private static float s_boxWidth; private static Color s_shownColor = new Color(-1f, -1f, -1f, -1f); private static bool s_fontWarningLogged; private static bool s_fontChoicesDone; private static string? s_fontChoice; private static TMP_FontAsset? s_font; private static FontStyles s_fontStyle; internal static void Sync() { OfferFontChoicesOnce(); string text = null; if (AfkConfig.IndicatorEnabled.Value) { if (AfkState.Active) { text = AfkConfig.IndicatorText.Value; } else if (AfkConfig.IndicatorShowWaiting.Value && AfkState.Requested && AfkConfig.Enabled.Value) { text = AfkState.WaitingMessage(); } } Show(string.IsNullOrEmpty(text) ? null : text); } internal static void Reset() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_root != (Object)null) { Object.Destroy((Object)(object)s_root); } s_root = null; s_label = null; s_shownText = null; s_shownFontSize = -1; s_boxWidth = 0f; s_shownColor = new Color(-1f, -1f, -1f, -1f); s_fontWarningLogged = false; s_fontChoicesDone = false; s_fontChoice = null; s_font = null; s_fontStyle = (FontStyles)0; } private static void Show(string? text) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) if (text == null) { if ((Object)(object)s_root != (Object)null && s_root.activeSelf) { s_root.SetActive(false); } return; } ApplyFontChoice(); if (!((Object)(object)s_label == (Object)null) || Create()) { int num = Mathf.Clamp(AfkConfig.IndicatorFontSize.Value, 8, 200); Color val = ParseColor(AfkConfig.IndicatorColor.Value); bool num2 = s_shownText != text; if (num2) { ((TMP_Text)s_label).text = text; s_shownText = text; } bool flag = s_shownFontSize != num; if (flag) { ((TMP_Text)s_label).fontSize = num; s_shownFontSize = num; } if (s_shownColor != val) { ((Graphic)s_label).color = val; s_shownColor = val; } if (num2 || flag) { s_boxWidth = ((TMP_Text)s_label).GetPreferredValues(text).x; } RectTransform rectTransform = ((TMP_Text)s_label).rectTransform; rectTransform.sizeDelta = new Vector2(s_boxWidth, (float)num * 1.5f); AnchorFactor(AfkConfig.IndicatorAnchor.Value, out var anchorX, out var anchorY); float num3 = Screen.width; float num4 = Screen.height; float num5; float num6; if (string.Equals(AfkConfig.IndicatorPositionUnit.Value, "Pixels", StringComparison.OrdinalIgnoreCase)) { num5 = AfkConfig.IndicatorPositionX.Value; num6 = AfkConfig.IndicatorPositionY.Value; } else { num5 = num3 * (float)AfkConfig.IndicatorPositionX.Value / 100f; num6 = num4 * (float)AfkConfig.IndicatorPositionY.Value / 100f; } Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor((num3 > 0f) ? (num5 / num3) : 0.5f, (num4 > 0f) ? (1f - num6 / num4) : 0.85f); rectTransform.anchorMax = val2; rectTransform.anchorMin = val2; rectTransform.pivot = new Vector2(anchorX, 1f - anchorY); rectTransform.anchoredPosition = Vector2.zero; if (!s_root.activeSelf) { s_root.SetActive(true); } } } private static bool Create() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_font == (Object)null) { if (!s_fontWarningLogged) { s_fontWarningLogged = true; AFK.LogWarning("No TMP font available - the indicator message stays hidden."); } return false; } s_root = new GameObject("AFK Indicator"); Object.DontDestroyOnLoad((Object)(object)s_root); Canvas obj = s_root.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = -1000; GameObject val = new GameObject("Text"); val.transform.SetParent(s_root.transform, false); s_label = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)s_label).font = s_font; ((TMP_Text)s_label).alignment = (TextAlignmentOptions)514; ((TMP_Text)s_label).fontStyle = s_fontStyle; ((TMP_Text)s_label).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)s_label).overflowMode = (TextOverflowModes)0; ((Graphic)s_label).raycastTarget = false; return true; } private static void OfferFontChoicesOnce() { if (s_fontChoicesDone || (Object)(object)Hud.instance == (Object)null) { return; } List<string> list = new List<string>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.Length > 0 && hashSet.Add(((Object)val).name)) { list.Add(((Object)val).name); } } if (list.Count != 0) { list.Sort(StringComparer.OrdinalIgnoreCase); s_fontChoicesDone = true; AfkConfig.BindIndicatorFont(list.ToArray()); } } private static void ApplyFontChoice() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) string text = (AfkConfig.IndicatorFont?.Value ?? "").Trim(); if (text == s_fontChoice) { return; } s_fontChoice = text; TMP_FontAsset val; FontStyles fontStyle; if (text.Length == 0) { val = DefaultFont(); fontStyle = (FontStyles)1; } else { val = FindLoadedFont(text); fontStyle = (FontStyles)0; if ((Object)(object)val == (Object)null) { ParseStyle(text, out string fontName, out FontStyles style); val = FindLoadedFont(fontName); fontStyle = style; } if ((Object)(object)val == (Object)null) { AFK.LogWarning("Font \"" + text + "\" is not loaded by the game - using the game's own font."); val = DefaultFont(); fontStyle = (FontStyles)1; } } s_font = val; s_fontStyle = fontStyle; if ((Object)(object)s_label != (Object)null && (Object)(object)val != (Object)null) { ((TMP_Text)s_label).font = val; ((TMP_Text)s_label).fontStyle = fontStyle; } } private static void ParseStyle(string choice, out string fontName, out FontStyles style) { if (choice.EndsWith(" Bold Italic", StringComparison.OrdinalIgnoreCase)) { fontName = choice.Substring(0, choice.Length - " Bold Italic".Length); style = (FontStyles)3; } else if (choice.EndsWith(" Bold", StringComparison.OrdinalIgnoreCase)) { fontName = choice.Substring(0, choice.Length - " Bold".Length); style = (FontStyles)1; } else if (choice.EndsWith(" Italic", StringComparison.OrdinalIgnoreCase)) { fontName = choice.Substring(0, choice.Length - " Italic".Length); style = (FontStyles)2; } else { fontName = choice; style = (FontStyles)0; } } private static TMP_FontAsset? FindLoadedFont(string name) { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.Equals(name, StringComparison.OrdinalIgnoreCase)) { return val; } } return null; } private static TMP_FontAsset? DefaultFont() { if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_healthText != (Object)null) { TMP_FontAsset font = Hud.instance.m_healthText.font; if ((Object)(object)font != (Object)null) { return font; } } return TMP_Settings.defaultFontAsset; } private static void AnchorFactor(string? name, out float anchorX, out float anchorY) { switch (name) { case "TopLeft": anchorX = 0f; anchorY = 0f; break; case "TopCenter": anchorX = 0.5f; anchorY = 0f; break; case "TopRight": anchorX = 1f; anchorY = 0f; break; case "Left": anchorX = 0f; anchorY = 0.5f; break; case "Center": anchorX = 0.5f; anchorY = 0.5f; break; case "Right": anchorX = 1f; anchorY = 0.5f; break; case "BottomLeft": anchorX = 0f; anchorY = 1f; break; case "BottomCenter": anchorX = 0.5f; anchorY = 1f; break; case "BottomRight": anchorX = 1f; anchorY = 1f; break; default: anchorX = 0.5f; anchorY = 0f; break; } } private static Color ParseColor(string value) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (!ColorUtility.TryParseHtmlString(value, ref result)) { return Color.white; } return result; } } internal static class AfkState { internal const string GhostKey = "afk_ghost"; private static readonly string[] ActionButtons = new string[20] { "Forward", "Backward", "Left", "Right", "Run", "JoyRun", "AutoRun", "JoyAutoRun", "Jump", "JoyJump", "Attack", "JoyAttack", "SecondaryAttack", "JoySecondaryAttack", "Block", "JoyBlock", "AltDodge", "JoyDodge", "Use", "JoyUse" }; private static float s_sinceDamageDealt; private static float s_sinceAggro; private static bool s_loggedNotOwner; internal static bool Requested { get; private set; } internal static bool Active { get; private set; } internal static void Tick() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { ResetLocal(); return; } float deltaTime = Time.deltaTime; s_sinceDamageDealt += deltaTime; s_sinceAggro += deltaTime; bool requested = Requested; bool active = Active; bool notifyRequestChange = false; string text = null; bool flag = IsInputBlocked(); if (AfkConfig.Enabled.Value && !((Character)localPlayer).IsDead() && !flag) { if (PressedShortcut(AfkConfig.ToggleKey.Value)) { SetRequested(!Requested); notifyRequestChange = true; } else if (AfkConfig.SitTriggers.Value && PressedSit(localPlayer)) { SetRequested(!Requested); } else if (Requested && ActionInputPressed()) { SetRequested(value: false); text = "AFK: cancelled (action input)"; } } if (Requested && ((Character)localPlayer).IsDead()) { SetRequested(value: false); } Active = AfkConfig.Enabled.Value && Requested && !((Character)localPlayer).IsDead() && (active || AreConditionsMet()); if (text == null) { text = StatusMessage(requested, active, notifyRequestChange); } if (text != null) { Notify(localPlayer, text); } PublishGhostFlag(localPlayer); } internal static void OnLocalPlayerDealtDamage() { s_sinceDamageDealt = 0f; } internal static void OnLocalPlayerTargetedByHostile() { s_sinceAggro = 0f; } internal static void OnLocalPlayerTookDamage() { s_sinceAggro = 0f; } internal static bool IsGhost(Character? character) { Player val = (Player)(object)((character is Player) ? character : null); if (val == null) { return false; } if (!AfkConfig.InvisibleToEnemies.Value) { return false; } ZDO playerZDO = GetPlayerZDO(val); if (playerZDO != null && playerZDO.IsValid()) { return playerZDO.GetBool("afk_ghost", false); } return false; } internal static void Shutdown() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ZDO playerZDO = GetPlayerZDO(localPlayer); if (playerZDO != null && playerZDO.IsValid() && playerZDO.IsOwner() && playerZDO.GetBool("afk_ghost", false)) { playerZDO.Set("afk_ghost", false); } } ResetLocal(); } private static void ResetLocal() { Requested = false; Active = false; s_sinceDamageDealt = 0f; s_sinceAggro = 0f; s_loggedNotOwner = false; } private static void SetRequested(bool value) { Requested = value; } private static bool AreConditionsMet() { if (s_sinceDamageDealt >= AfkConfig.SecondsSinceDamageDealt.Value) { return s_sinceAggro >= AfkConfig.SecondsSinceAggro.Value; } return false; } internal static string WaitingMessage() { return "AFK: waiting - " + PendingReason(); } private static string? StatusMessage(bool wasRequested, bool wasActive, bool notifyRequestChange) { if (Active != wasActive) { if (Active) { return "AFK: active"; } if (!Requested) { return "AFK: off"; } return "AFK: paused (mod disabled)"; } if (Requested != wasRequested && notifyRequestChange) { if (!Requested) { return "AFK: off"; } return "AFK: waiting - " + PendingReason(); } return null; } private static string PendingReason() { float num = AfkConfig.SecondsSinceDamageDealt.Value - s_sinceDamageDealt; float num2 = AfkConfig.SecondsSinceAggro.Value - s_sinceAggro; float num3 = Mathf.Max(num, num2); string text = ""; if (num > 0f) { text = "no damage dealt"; } if (num2 > 0f) { text = text + ((text.Length > 0) ? ", " : "") + "no enemy aggro"; } return $"{text} ({Mathf.Max(1, Mathf.CeilToInt(num3))}s)"; } private static void Notify(Player player, string message) { AFK.LogDebug(message); if (AfkConfig.Notify.Value) { ((Character)player).Message((MessageType)1, message, 0, (Sprite)null, false); } } private static void PublishGhostFlag(Player player) { ZDO playerZDO = GetPlayerZDO(player); if (playerZDO == null || !playerZDO.IsValid()) { return; } bool flag = Active && AfkConfig.InvisibleToEnemies.Value; if (playerZDO.GetBool("afk_ghost", false) == flag) { return; } if (!playerZDO.IsOwner()) { if (!s_loggedNotOwner) { s_loggedNotOwner = true; AFK.LogWarning($"Your player ZDO is owned by peer {playerZDO.GetOwner()}: the AFK flag cannot be published from here."); } } else { playerZDO.Set("afk_ghost", flag); } } private static ZDO? GetPlayerZDO(Player player) { ZNetView component = ((Component)player).GetComponent<ZNetView>(); if (component == null) { return null; } return component.GetZDO(); } private static bool PressedShortcut(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return false; } if (!ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!ZInput.GetKey(modifier, false)) { return false; } } return true; } private static bool PressedSit(Player player) { if (!ZInput.GetButtonDown("Sit")) { if (!((Character)player).InPlaceMode() && !ZInput.GetButton("JoyAltKeys")) { return ZInput.GetButtonUp("JoySit"); } return false; } return true; } private static bool ActionInputPressed() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) string[] actionButtons = ActionButtons; for (int i = 0; i < actionButtons.Length; i++) { if (ZInput.GetButton(actionButtons[i])) { return true; } } Vector2 joyLeftStick = ZInput.GetJoyLeftStick(); return ((Vector2)(ref joyLeftStick)).sqrMagnitude > 0.09f; } private static bool IsInputBlocked() { if (Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible()) { return true; } if (InventoryGui.IsVisible() || Hud.InRadial()) { return true; } if ((Object)(object)Chat.instance != (Object)null) { return Chat.instance.HasFocus(); } return false; } } [HarmonyPatch(typeof(BaseAI), "CanHearTarget", new Type[] { typeof(Transform), typeof(float), typeof(Character) })] internal static class Patch_CanHearTarget { private static bool Prefix(Character target, ref bool __result) { try { if (AfkState.IsGhost(target)) { __result = false; return false; } return true; } catch (Exception arg) { AFK.LogError($"Patch BaseAI.CanHearTarget: {arg}"); return true; } } } [HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Transform), typeof(Vector3), typeof(float), typeof(float), typeof(bool), typeof(bool), typeof(Character) })] internal static class Patch_CanSeeTarget { private static bool Prefix(Character target, ref bool __result) { try { if (AfkState.IsGhost(target)) { __result = false; return false; } return true; } catch (Exception arg) { AFK.LogError($"Patch BaseAI.CanSeeTarget: {arg}"); return true; } } } [HarmonyPatch(typeof(Character), "Damage")] internal static class Patch_Damage { private static void Prefix(Character __instance, HitData hit) { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && hit != null && hit.HaveAttacker() && !((Object)(object)__instance == (Object)(object)localPlayer) && !((Object)(object)hit.GetAttacker() != (Object)(object)localPlayer) && !__instance.IsDead()) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((val == null || ((Character)val).IsPVPEnabled() || hit.m_ignorePVP) && (__instance is Player || BaseAI.IsEnemy((Character)(object)localPlayer, __instance))) { AfkState.OnLocalPlayerDealtDamage(); } } } catch (Exception arg) { AFK.LogError($"Patch Character.Damage: {arg}"); } } } [HarmonyPatch(typeof(BaseAI), "FindEnemy")] internal static class Patch_FindEnemy { private static void Postfix(ref Character? __result) { try { Character? obj = __result; Player val = (Player)(object)((obj is Player) ? obj : null); if (val != null && AfkState.IsGhost((Character?)(object)val)) { __result = null; } } catch (Exception arg) { AFK.LogError($"Patch BaseAI.FindEnemy: {arg}"); } } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class Patch_RPCDamage { private static bool Prefix(Character __instance, HitData hit) { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance != (Object)(object)localPlayer || hit == null) { return true; } Character val = (((ZDOID)(ref hit.m_attacker)).IsNone() ? null : hit.GetAttacker()); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)localPlayer) { AfkState.OnLocalPlayerTookDamage(); } if (!AfkState.Active || !AfkConfig.IgnoreEnemyDamage.Value) { return true; } return (Object)(object)val == (Object)null || val.IsPlayer(); } catch (Exception arg) { AFK.LogError($"Patch Character.RPC_Damage: {arg}"); return true; } } } [HarmonyPatch(typeof(Player), "RPC_OnTargeted")] internal static class Patch_RPCOnTargeted { private static void Postfix(Player __instance, bool alerted) { try { if (alerted && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { AfkState.OnLocalPlayerTargetedByHostile(); } } catch (Exception arg) { AFK.LogError($"Patch Player.RPC_OnTargeted: {arg}"); } } } }