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 Odins Latch v0.1.1
plugins/OdinsLatch.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; 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: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("OdinsLatch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+3f51590bcc71bad50962201797350c250e270175")] [assembly: AssemblyProduct("OdinsLatch")] [assembly: AssemblyTitle("OdinsLatch")] [assembly: AssemblyVersion("0.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OdinsLatch { internal static class HudText { private sealed class Scale : MonoBehaviour { private const float ReferenceWidth = 1920f; private const float ReferenceHeight = 1080f; private static readonly FieldRef<float> GameGuiScale = TryFindGuiScale(); internal CanvasScaler Scaler; private float _logged = -1f; private void Update() { if (!((Object)(object)Scaler == (Object)null)) { float num = ((GameGuiScale != null) ? GameGuiScale.Invoke() : 1f); if (num <= 0f) { num = 1f; } float num2 = Mathf.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f) * num * Plugin.TextScale.Value; if (Mathf.Abs(Scaler.scaleFactor - num2) > 0.0001f) { Scaler.scaleFactor = num2; } if (Plugin.DebugLog.Value && Mathf.Abs(_logged - num2) > 0.0001f) { _logged = num2; Plugin.Log.LogInfo((object)$"Text scale {num2:0.###}: screen {Screen.width}x{Screen.height}, game GUI scale {num:0.##}, TextScale {Plugin.TextScale.Value:0.##}."); } } } private static FieldRef<float> TryFindGuiScale() { FieldInfo fieldInfo = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale"); if (fieldInfo == null || fieldInfo.FieldType != typeof(float) || !fieldInfo.IsStatic) { Plugin.Log.LogWarning((object)"Couldn't find the game's GUI scale; text follows the screen size only."); return null; } return AccessTools.StaticFieldRefAccess<float>(fieldInfo); } } private static Canvas _canvas; public static TMP_Text Create(string name, Transform owner) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = (((Object)(object)MessageHud.instance != (Object)null) ? MessageHud.instance.m_messageCenterText : null); if ((Object)(object)val == (Object)null || (Object)(object)val.font == (Object)null || !EnsureCanvas(owner)) { return null; } GameObject val2 = new GameObject(name, new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(((Component)_canvas).transform, false); TextMeshProUGUI obj = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).font = val.font; ((TMP_Text)obj).fontSharedMaterial = val.fontSharedMaterial; ((TMP_Text)obj).alignment = (TextAlignmentOptions)514; ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)1; ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).outlineWidth = 0.15f; ((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200); RectTransform val3 = (RectTransform)val2.transform; val3.pivot = new Vector2(0.5f, 0.5f); val3.anchoredPosition = Vector2.zero; return (TMP_Text)(object)obj; } private static bool EnsureCanvas(Transform owner) { //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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_canvas != (Object)null) { return true; } GameObject val = new GameObject("OdinsLatch_Canvas"); val.transform.SetParent(owner, false); _canvas = val.AddComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 5000; CanvasScaler val2 = val.AddComponent<CanvasScaler>(); val2.uiScaleMode = (ScaleMode)0; val.AddComponent<Scale>().Scaler = val2; Plugin.Log.LogInfo((object)"On-screen text layer created."); return true; } } internal sealed class Latch : MonoBehaviour { private enum Reason { None, Enemy, Raid, Night, Horn, Dusk, Always } private sealed class Tracked { public Door Door; public ZNetView View; public string Name; public float UsedAt; public float DueSince = -1f; public float AskedAt = -1f; public string HeldBy; } private const float ScanInterval = 0.25f; private const float OpenGrace = 3f; private const float RetryAfter = 4f; private const double HornWindow = 0.45; private const float NightStartsAt = 0.85f; private const float NightEndsAt = 0.15f; internal const int Unknown = int.MinValue; private static readonly Dictionary<Reason, string[]> Lines = new Dictionary<Reason, string[]> { [Reason.Enemy] = new string[6] { "Greydwarfs don't knock.", "Trolls don't knock either. Door shut.", "{0} nearby. The door is shut. It can knock.", "Shut. The {0} will have to try the wall.", "Visitors. The pointy kind. Door shut.", "Odin shut the door. The {0} was looking at it." }, [Reason.Raid] = new string[3] { "They've come for your base. They can start with the door.", "Raid on. Door shut. The rest is your job.", "A raid is no time for draughts." }, [Reason.Night] = new string[4] { "Night's here. So is the latch.", "Dark out. Door shut. Odin's rule, not yours.", "The sun has gone home. So has the door.", "Night. The dark can stay outside." }, [Reason.Horn] = new string[2] { "The horn has sounded. The door listened, at least.", "Horn's gone. Door's shut. One of you was paying attention." }, [Reason.Dusk] = new string[2] { "Dusk. Odin shut the door before the night could try the handle.", "Sun's going. Door's shut. One of you was paying attention." }, [Reason.Always] = new string[2] { "You left it open. Odin didn't.", "Door shut. Odin hung nine nights on a tree. He can manage a latch." } }; private static readonly string[] Shared = new string[2] { "Born in a barn, were we?", "Shut. You're welcome." }; private readonly List<Tracked> _doors = new List<Tracked>(); private readonly List<Character> _near = new List<Character>(); private OdinLine _line; private float _nextScan; private float _nextRemarkAt; private string _lastRemark; internal static Latch Instance { get; private set; } internal bool Closing { get; private set; } private void Awake() { Instance = this; _line = ((Component)this).gameObject.AddComponent<OdinLine>(); } private void Start() { DuskHornLink.TryHook(Plugin.Patcher); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal static int StateOf(Door door) { ZNetView val = (((Object)(object)door != (Object)null) ? ((Component)door).GetComponent<ZNetView>() : null); if (!((Object)(object)val != (Object)null) || !val.IsValid()) { return int.MinValue; } return val.GetZDO().GetInt(ZDOVars.s_state, 0); } internal void OnUsed(Door door, int stateBefore) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value || stateBefore == int.MinValue) { return; } int num = _doors.FindIndex((Tracked t) => (Object)(object)t.Door == (Object)(object)door); if (stateBefore != 0) { if (num >= 0) { Note("You shut " + _doors[num].Name + " yourself. Odin stops watching it."); _doors.RemoveAt(num); } return; } if ((Object)(object)door.m_keyItem != (Object)null || door.m_canNotBeClosed) { Note("Opened " + Name(door) + ": it needs a key or can't be shut. Odin leaves it alone."); return; } Tracked tracked = ((num >= 0) ? _doors[num] : new Tracked { Door = door, View = ((Component)door).GetComponent<ZNetView>(), Name = Name(door) }); tracked.UsedAt = Time.time; tracked.DueSince = -1f; tracked.AskedAt = -1f; tracked.HeldBy = null; if (num < 0) { _doors.Add(tracked); } Note($"Opened {tracked.Name} at {Where(((Component)door).transform.position)}. Odin is watching it ({_doors.Count} in all)."); } private void Update() { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { _doors.Clear(); } else { if (!Plugin.Enabled.Value || Time.time < _nextScan) { return; } _nextScan = Time.time + 0.25f; if (((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || _doors.Count == 0) { return; } Reason everywhere = ClockReason(); Reason reason = Reason.None; string creature = null; int num = 0; for (int num2 = _doors.Count - 1; num2 >= 0; num2--) { Tracked tracked = _doors[num2]; if ((Object)(object)tracked.Door == (Object)null || (Object)(object)tracked.View == (Object)null || !tracked.View.IsValid()) { _doors.RemoveAt(num2); } else if (tracked.View.GetZDO().GetInt(ZDOVars.s_state, 0) == 0) { if (Time.time - tracked.UsedAt > 3f) { if (tracked.AskedAt < 0f) { Note(tracked.Name + " is shut (not by Odin). Odin stops watching it."); } _doors.RemoveAt(num2); } } else { Vector3 position = ((Component)tracked.Door).transform.position; Character enemy; Reason reason2 = WhyShut(localPlayer, position, everywhere, out enemy); if (reason2 == Reason.None) { tracked.DueSince = -1f; } else { if (tracked.DueSince < 0f) { tracked.DueSince = Time.time; } if (!(Time.time - tracked.DueSince < Plugin.CloseDelay.Value) && (!(tracked.AskedAt >= 0f) || !(Time.time - tracked.AskedAt < 4f))) { string text = KeptOpen(localPlayer, tracked.Door, position); if (text != null) { if (text != tracked.HeldBy) { Note($"{tracked.Name}: {reason2}, but {text}. Odin waits."); } tracked.HeldBy = text; } else { tracked.HeldBy = null; if (Shut(localPlayer, tracked)) { Note("Shut " + tracked.Name + " at " + Where(position) + ": " + Describe(reason2, enemy, position, localPlayer) + "."); if (num == 0 || reason2 < reason) { reason = reason2; creature = (((Object)(object)enemy != (Object)null) ? ShownName(enemy) : null); } num++; } } } } } } if (num > 0) { Remark(reason, creature); } } } private static Reason ClockReason() { if ((Object)(object)EnvMan.instance != (Object)null && (Object)(object)ZNet.instance != (Object)null) { if (Plugin.CloseAtNight.Value && EnvMan.IsNight()) { return Reason.Night; } if (Plugin.FollowDuskHorn.Value && DuskHornLink.Hooked && !EnvMan.IsNight() && ZNet.instance.GetTimeSeconds() - DuskHornLink.LastHornAt < 0.45 * (double)EnvMan.instance.m_dayLengthSec) { return Reason.Horn; } if (Plugin.CloseAtNight.Value && Plugin.DuskMinutes.Value > 0f && SecondsToNight() <= (double)(Plugin.DuskMinutes.Value * 60f)) { return Reason.Dusk; } } if (!Plugin.AlwaysClose.Value) { return Reason.None; } return Reason.Always; } private Reason WhyShut(Player player, Vector3 pos, Reason everywhere, out Character enemy) { //IL_0015: 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_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) enemy = null; Reason reason = everywhere; if (Plugin.CloseNearEnemies.Value && (Object)(object)(enemy = NearestEnemy(player, pos, Plugin.EnemyRadius.Value)) != (Object)null) { reason = Reason.Enemy; } else if (Plugin.CloseInRaids.Value && RaidCovers(pos)) { reason = Reason.Raid; } if (reason == Reason.None) { return Reason.None; } if (Plugin.WalkAway.Value && Vector3.Distance(((Component)player).transform.position, pos) <= Plugin.WalkAwayDistance.Value) { return Reason.None; } return reason; } private static double SecondsToNight() { double num = EnvMan.instance.m_dayLengthSec; if (num <= 0.0) { return double.MaxValue; } double timeSeconds = ZNet.instance.GetTimeSeconds(); double num2 = timeSeconds / num - Math.Floor(timeSeconds / num); if (num2 >= 0.8500000238418579 || num2 <= 0.15000000596046448) { return 0.0; } return (0.8500000238418579 - num2) * num; } private static bool RaidCovers(Vector3 pos) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) RandomEvent val = (((Object)(object)RandEventSystem.instance != (Object)null) ? RandEventSystem.instance.GetCurrentRandomEvent() : null); if (val != null) { return Utils.DistanceXZ(pos, val.m_pos) < val.m_eventRange; } return false; } private static Character NearestEnemy(Player player, Vector3 pos, float radius) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) Character result = null; float num = float.MaxValue; foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player) && !allCharacter.IsPlayer() && !allCharacter.IsDead() && !allCharacter.IsTamed()) { float num2 = Mathf.Max(0f, Vector3.Distance(((Component)allCharacter).transform.position, pos) - allCharacter.GetRadius()); if (!(num2 > radius) && !(num2 >= num) && IsThreat(player, allCharacter)) { result = allCharacter; num = num2; } } } return result; } private static bool IsThreat(Player player, Character other) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 if (!BaseAI.IsEnemy((Character)(object)player, other)) { return false; } BaseAI baseAI = other.GetBaseAI(); if ((Object)(object)baseAI == (Object)null) { return false; } Faction faction = other.GetFaction(); if ((int)faction != 1) { if ((int)faction == 12) { return false; } if (!baseAI.IsAlerted() && !baseAI.HaveTarget()) { return baseAI.HuntPlayer(); } return true; } if (baseAI is MonsterAI) { return baseAI.HaveTarget(); } return false; } private string KeptOpen(Player player, Door door, Vector3 pos) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)door.m_keyItem != (Object)null || door.m_canNotBeClosed) { return "it needs a key or can't be shut"; } if (Character.InInterior(pos)) { return "it's in a dungeon"; } if (Location.IsInsideLocation(pos, 0f) && !BuiltByPlayer(door)) { return "it's a location's own door"; } if (Excluded(door)) { return "it's in Exclude"; } if (door.m_checkGuardStone && !PrivateArea.CheckAccess(pos, 0f, false, false)) { return "a ward you can't use covers it"; } if (!View(door).HasOwner()) { return "nobody owns it yet"; } float value = Plugin.DoorwayRadius.Value; if (value <= 0f) { return null; } if (Vector3.Distance(((Component)player).transform.position, pos) < value) { return "you're in the doorway"; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && (Object)(object)allPlayer != (Object)(object)player && !((Character)allPlayer).IsDead() && Vector3.Distance(((Component)allPlayer).transform.position, pos) < value) { return "another player is in the doorway"; } } _near.Clear(); Character.GetCharactersInRange(pos, value, _near); foreach (Character item in _near) { if ((Object)(object)item != (Object)null && !item.IsDead() && item.IsTamed()) { return "a tamed " + ShownName(item) + " is in the doorway"; } } return null; } private static bool BuiltByPlayer(Door door) { Piece component = ((Component)door).GetComponent<Piece>(); if ((Object)(object)component != (Object)null) { return component.IsPlacedByPlayer(); } return false; } private static bool Excluded(Door door) { string[] array = (Plugin.Exclude.Value ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && (string.Equals(Utils.GetPrefabName(((Component)door).gameObject), text, StringComparison.OrdinalIgnoreCase) || string.Equals(Localize(door.m_name), text, StringComparison.OrdinalIgnoreCase) || string.Equals((door.m_name ?? "").TrimStart(new char[1] { '$' }), text.TrimStart(new char[1] { '$' }), StringComparison.OrdinalIgnoreCase))) { return true; } } return false; } private bool Shut(Player player, Tracked t) { if (t.View.GetZDO().GetInt(ZDOVars.s_state, 0) == 0) { return false; } Closing = true; bool flag; try { flag = t.Door.Interact((Humanoid)(object)player, false, false); } finally { Closing = false; } if (flag) { t.AskedAt = Time.time; } else { Note(t.Name + " is still swinging. Odin tries again shortly."); } return flag; } private void Remark(Reason why, string creature) { if (!Plugin.ShowRemarks.Value || Time.time < _nextRemarkAt || !Lines.TryGetValue(why, out var value)) { return; } string text = null; for (int i = 0; i < 8; i++) { if (text != null && !(text == _lastRemark)) { break; } int num = Random.Range(0, value.Length + Shared.Length); string text2 = ((num < value.Length) ? value[num] : Shared[num - value.Length]); if (!text2.Contains("{0}") || creature != null) { text = string.Format(text2, creature); } } if (text != null) { _lastRemark = text; _nextRemarkAt = Time.time + Plugin.RemarkCooldown.Value; _line.Show(text); } } private static ZNetView View(Door door) { return ((Component)door).GetComponent<ZNetView>(); } private static string Name(Door door) { return Utils.GetPrefabName(((Component)door).gameObject); } private static string ShownName(Character c) { return Localize(c.m_name); } private static string Localize(string token) { if (Localization.instance == null) { return (token ?? "").TrimStart(new char[1] { '$' }); } return Localization.instance.Localize(token ?? ""); } private static string Where(Vector3 pos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) return $"({pos.x:0}, {pos.z:0})"; } private static string Describe(Reason why, Character enemy, Vector3 pos, Player player) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) string arg; switch (why) { case Reason.Enemy: arg = $"{enemy.m_name} {Vector3.Distance(((Component)enemy).transform.position, pos):0} m from it"; break; case Reason.Raid: { RandomEvent val = (((Object)(object)RandEventSystem.instance != (Object)null) ? RandEventSystem.instance.GetCurrentRandomEvent() : null); arg = "raid " + ((val != null) ? val.m_name : "?"); break; } case Reason.Dusk: arg = $"dusk, night in {SecondsToNight():0} s"; break; default: arg = why.ToString().ToLowerInvariant(); break; } return $"{arg}; you're {Vector3.Distance(((Component)player).transform.position, pos):0} m away"; } private static void Note(string message) { if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)message); } } } internal class OdinLine : MonoBehaviour { private const float ScreenY = 0.72f; private const float FadeIn = 0.2f; private const float Hold = 2.5f; private const float FadeOut = 0.8f; private const float TextSize = 21f; private static readonly Color Tint = new Color(1f, 0.86f, 0.62f); private TMP_Text _text; private float _elapsed = -1f; public void Show(string line) { if (EnsureText()) { _text.text = line; _elapsed = 0f; SetAlpha(0f); ((Component)_text).gameObject.SetActive(true); } } private void Update() { if (_elapsed < 0f || (Object)(object)_text == (Object)null) { return; } _elapsed += Time.deltaTime; float num = 3.5f; if (_elapsed >= num) { _elapsed = -1f; ((Component)_text).gameObject.SetActive(false); return; } float num2 = Mathf.Min(Mathf.Clamp01(_elapsed / 0.2f), Mathf.Clamp01((num - _elapsed) / 0.8f)); if (!HudVisible()) { num2 = 0f; } SetAlpha(Mathf.SmoothStep(0f, 1f, num2)); } private void SetAlpha(float alpha) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Color tint = Tint; tint.a = alpha; ((Graphic)_text).color = tint; } private bool EnsureText() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_text != (Object)null) { return true; } _text = HudText.Create("OdinsLatch_Line", ((Component)this).transform); if ((Object)(object)_text == (Object)null) { return false; } RectTransform val = (RectTransform)_text.transform; val.anchorMin = new Vector2(0.5f, 0.72f); val.anchorMax = new Vector2(0.5f, 0.72f); val.sizeDelta = new Vector2(1400f, 160f); _text.fontSize = 21f; return true; } private static bool HudVisible() { if ((Object)(object)Hud.instance != (Object)null && !Hud.IsUserHidden()) { return Hud.instance.IsVisible(); } return false; } private void OnDestroy() { if ((Object)(object)_text != (Object)null) { Object.Destroy((Object)(object)((Component)_text).gameObject); } } } [HarmonyPatch(typeof(Door), "Interact")] internal static class DoorInteractPatch { private static void Prefix(Door __instance, out int __state) { __state = Latch.StateOf(__instance); } private static void Postfix(Door __instance, Humanoid character, bool hold, bool __result, int __state) { if (!(!__result || hold) && !((Object)(object)character == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && !((Object)(object)Latch.Instance == (Object)null) && !Latch.Instance.Closing) { Latch.Instance.OnUsed(__instance, __state); } } } internal static class DuskHornLink { internal const string DuskHornGuid = "hevyh2o.valheim.odinsduskhorn"; private const string HornMethod = "OdinsDuskHorn.Horn:Play"; internal static double LastHornAt = double.MinValue; internal static bool Hooked { get; private set; } internal static void TryHook(Harmony harmony) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if (Hooked || harmony == null || !Chainloader.PluginInfos.ContainsKey("hevyh2o.valheim.odinsduskhorn")) { return; } try { MethodInfo methodInfo = AccessTools.Method("OdinsDuskHorn.Horn:Play", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogInfo((object)"Odin's Dusk Horn is here, but its horn has moved. Doors follow the clock instead."); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(DuskHornLink), "OnHorn", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Hooked = true; Plugin.Log.LogInfo((object)"Odin's Dusk Horn found. Doors shut when the horn sounds."); } catch (Exception ex) { Plugin.Log.LogInfo((object)("Odin's Dusk Horn found, but its horn couldn't be followed (" + ex.GetType().Name + "). Doors follow the clock instead.")); } } private static void OnHorn() { if ((Object)(object)ZNet.instance != (Object)null) { LastHornAt = ZNet.instance.GetTimeSeconds(); } } } [BepInPlugin("hevyh2o.valheim.odinslatch", "Odin's Latch", "0.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "hevyh2o.valheim.odinslatch"; public const string Name = "Odin's Latch"; public const string Version = "0.1.1"; internal static ManualLogSource Log; internal static Harmony Patcher; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> CloseDelay; internal static ConfigEntry<float> DoorwayRadius; internal static ConfigEntry<string> Exclude; internal static ConfigEntry<bool> CloseNearEnemies; internal static ConfigEntry<float> EnemyRadius; internal static ConfigEntry<bool> CloseAtNight; internal static ConfigEntry<float> DuskMinutes; internal static ConfigEntry<bool> FollowDuskHorn; internal static ConfigEntry<bool> CloseInRaids; internal static ConfigEntry<bool> WalkAway; internal static ConfigEntry<float> WalkAwayDistance; internal static ConfigEntry<bool> AlwaysClose; internal static ConfigEntry<bool> ShowRemarks; internal static ConfigEntry<float> RemarkCooldown; internal static ConfigEntry<float> TextScale; internal static ConfigEntry<bool> DebugLog; private Latch _latch; private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Expected O, but got Unknown //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch."); CloseDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CloseDelay", 2f, new ConfigDescription("Seconds Odin waits once a reason to close a door turns up, 0 to 30. 0 = straight away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); DoorwayRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DoorwayRadius", 2.5f, new ConfigDescription("Odin never shuts a door while you, another player or a tamed creature is this close to it, in metres, 0 to 6. 0 = he doesn't look.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 6f), Array.Empty<object>())); Exclude = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Exclude", "", "Doors Odin leaves alone, by prefab name (wood_door, iron_grate) or name as the game shows it (Wood door), separated by commas. Empty = every door you can close."); CloseNearEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseNearEnemies", true, "Shut the door when an enemy is within EnemyRadius of it and awake: alerted, hunting or after something. Animals only count while they're attacking. Tamed creatures never do."); EnemyRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "EnemyRadius", 30f, new ConfigDescription("How close to the door, in metres, an enemy has to be, 5 to 80.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>())); CloseAtNight = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseAtNight", true, "Shut the door at night, and for the last DuskMinutes before it."); DuskMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "DuskMinutes", 2f, new ConfigDescription("Only with CloseAtNight: how many minutes before nightfall count as night already, 0 to 6. Minutes of the game's clock as you play it (a whole Valheim day is 20). 0 = night only.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 6f), Array.Empty<object>())); FollowDuskHorn = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "FollowDuskHorn", true, "If Odin's Dusk Horn is installed, shut the door when its horn sounds, until night takes over. Does nothing without Dusk Horn."); CloseInRaids = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "CloseInRaids", true, "Shut the door while a raid or other event is on and the door is inside its area."); WalkAway = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "WalkAway", false, "Wait until you're more than WalkAwayDistance from the door before shutting it. Odin still only shuts it for one of the reasons above."); WalkAwayDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Triggers", "WalkAwayDistance", 10f, new ConfigDescription("Only with WalkAway: metres you have to be from the door, 3 to 50.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 50f), Array.Empty<object>())); AlwaysClose = ((BaseUnityPlugin)this).Config.Bind<bool>("Triggers", "AlwaysClose", false, "Shut the doors you open in daylight too, with no danger about: a plain door closer. The doorway check still applies."); ShowRemarks = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "ShowRemarks", true, "Odin says a word when he shuts a door for you."); RemarkCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "RemarkCooldown", 30f, new ConfigDescription("Seconds between remarks, 0 to 300. Doors shut in between are shut quietly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>())); TextScale = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "TextScale", 1f, new ConfigDescription("Size of Odin's remarks, 0.5 to 2. 1 = sized to match the game's own HUD, which already follows your screen height and the game's GUI scale setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Write each door you open, each door Odin shuts and why, and what kept a door open, to the BepInEx log."); Patcher = new Harmony("hevyh2o.valheim.odinslatch"); Patcher.PatchAll(typeof(Plugin).Assembly); _latch = ((Component)this).gameObject.AddComponent<Latch>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Latch 0.1.1 loaded. Odin is minding the door."); } private void OnDestroy() { Harmony patcher = Patcher; if (patcher != null) { patcher.UnpatchSelf(); } if ((Object)(object)_latch != (Object)null) { Object.Destroy((Object)(object)_latch); } } } internal static class BuildInfo { public const string Version = "0.1.1"; } }