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 WiresHelmetHider v0.2.4
WiresHelmetHider.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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("WiresHelmetHider")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WiresHelmetHider")] [assembly: AssemblyTitle("WiresHelmetHider")] [assembly: AssemblyVersion("1.0.0.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 WiresHoodToggle { internal static class HelmetToggleAnimation { private static Player _player; private static ZSyncAnimation _sync; private static float _remaining; private static bool _pending; private static bool _desiredHidden; internal static bool Pending => _pending; internal static void Start(Player player, bool desiredHidden) { Cancel(commit: true); _player = player; _sync = (((Object)(object)player != (Object)null) ? ((Component)player).GetComponent<ZSyncAnimation>() : null); if ((Object)(object)player == (Object)null || (Object)(object)_sync == (Object)null || !Plugin.PlayEquipAnimation.Value || player.GetActionQueueCount() != 0 || ((Character)player).InAttack() || ((Character)player).IsDead() || ((Character)player).IsStaggering() || !((Character)player).IsOnGround()) { HoodVisuals.CommitToggle(player, desiredHidden); return; } _pending = true; _desiredHidden = desiredHidden; _remaining = Plugin.AnimationDuration.Value; _sync.SetBool("equipping", true); } internal static void Update(Player player) { if (!_pending) { return; } if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)_player || ((Character)player).IsDead() || ((Character)player).InAttack() || ((Character)player).IsStaggering() || !((Character)player).IsOnGround() || player.GetActionQueueCount() > 0 || !Plugin.PlayEquipAnimation.Value) { Cancel(commit: true); return; } _remaining -= Time.deltaTime; if (_remaining <= 0f) { Cancel(commit: true); } } internal static void Cancel(bool commit) { if (_pending) { Player player = _player; bool desiredHidden = _desiredHidden; ZSyncAnimation sync = _sync; _pending = false; _player = null; _sync = null; _remaining = 0f; if ((Object)(object)sync != (Object)null && ((Object)(object)player == (Object)null || player.GetActionQueueCount() == 0)) { sync.SetBool("equipping", false); } if (commit && (Object)(object)player != (Object)null) { HoodVisuals.CommitToggle(player, desiredHidden); } } } } internal static class HoodInput { private const string EmoteButton = "OpenEmote"; private static float _lastTapAt = -10f; private static bool _consumeCurrentPress; internal static bool UsingEmoteButton { get { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (Plugin.CustomToggleKey != null) { KeyboardShortcut value = Plugin.CustomToggleKey.Value; return (int)((KeyboardShortcut)(ref value)).MainKey == 0; } return true; } } internal static void Update() { //IL_0038: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Reset(); return; } bool flag = CanTakeInput(localPlayer); if (!UsingEmoteButton) { _lastTapAt = -10f; _consumeCurrentPress = false; KeyboardShortcut value = Plugin.CustomToggleKey.Value; if (flag && ((KeyboardShortcut)(ref value)).IsDown()) { HoodVisuals.ToggleLocal(localPlayer); } return; } bool buttonDown = ZInput.GetButtonDown("OpenEmote"); bool buttonUp = ZInput.GetButtonUp("OpenEmote"); float unscaledTime = Time.unscaledTime; if (_lastTapAt > -1f && unscaledTime - _lastTapAt > Plugin.DoubleTapWindow.Value) { _lastTapAt = -10f; } if (buttonDown && flag) { if (_lastTapAt > -1f && unscaledTime - _lastTapAt <= Plugin.DoubleTapWindow.Value) { _lastTapAt = -10f; _consumeCurrentPress = true; HoodVisuals.ToggleLocal(localPlayer); } else { _lastTapAt = unscaledTime; } } if (buttonUp) { _consumeCurrentPress = false; } } internal static bool ShouldOpenEmoteRadial() { if (!UsingEmoteButton || _consumeCurrentPress || Plugin.EmoteHoldDuration == null) { return false; } if (ZInput.GetButton("OpenEmote")) { return ZInput.GetButtonPressedTimer("OpenEmote") >= Plugin.EmoteHoldDuration.Value; } return false; } internal static void Reset() { _lastTapAt = -10f; _consumeCurrentPress = false; } private static bool CanTakeInput(Player player) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).InCutscene() || ((Character)player).IsTeleporting() || InventoryGui.IsVisible() || Menu.IsVisible() || GameCamera.InFreeFly() || Minimap.IsOpen() || Hud.IsPieceSelectionVisible() || StoreGui.IsVisible() || Console.IsVisible() || TextInput.IsVisible() || PlayerCustomizaton.IsBarberGuiVisible() || Hud.InRadial()) { return false; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } return true; } } internal static class HoodVisuals { private sealed class VisualState { internal GameObject Helmet; internal int HelmetId; internal int HelmetHash; internal bool Hidden; } private static readonly int HiddenKey = StringExtensionMethods.GetStableHashCode("wire.hoodtoggle.lowered"); private static readonly FieldInfo HelmetInstanceField = AccessTools.Field(typeof(VisEquipment), "m_helmetItemInstance"); private static readonly FieldInfo CurrentHelmetHashField = AccessTools.Field(typeof(VisEquipment), "m_currentHelmetItemHash"); private static readonly FieldInfo NViewField = AccessTools.Field(typeof(VisEquipment), "m_nview"); private static readonly FieldInfo EquippedHelmetField = AccessTools.Field(typeof(Humanoid), "m_helmetItem"); private static readonly MethodInfo UpdateEquipmentVisualsMethod = AccessTools.Method(typeof(VisEquipment), "UpdateEquipmentVisuals", (Type[])null, (Type[])null); private static readonly MethodInfo TriggerEquipEffectMethod = AccessTools.Method(typeof(Humanoid), "TriggerEquipEffect", (Type[])null, (Type[])null); private static readonly Dictionary<int, VisualState> States = new Dictionary<int, VisualState>(); private static Player _trackedPlayer; private static ItemData _trackedHelmet; private static bool _restoredForPlayer; private static VisEquipment _menuPreviewVis; private static long _menuPreviewPlayerId; internal static void UpdateLocal() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { HelmetToggleAnimation.Cancel(commit: false); _trackedPlayer = null; _trackedHelmet = null; _restoredForPlayer = false; return; } object? obj = EquippedHelmetField?.GetValue(localPlayer); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if ((Object)(object)localPlayer != (Object)(object)_trackedPlayer) { HelmetToggleAnimation.Cancel(commit: false); _trackedPlayer = localPlayer; _trackedHelmet = val; _restoredForPlayer = false; } if (!_restoredForPlayer) { RestoreLocalSavedState(localPlayer); } if ((Object)(object)localPlayer == (Object)(object)_trackedPlayer && _restoredForPlayer && _trackedHelmet != val) { _trackedHelmet = val; HelmetToggleAnimation.Cancel(commit: false); if (Plugin.ResetOnHeadgearChange.Value) { ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { SavedHelmetStates.SetHidden(localPlayer.GetPlayerID(), hidden: false); ZDO zDO = component.GetZDO(); if (zDO != null && zDO.GetBool(HiddenKey, false)) { zDO.Set(HiddenKey, false); VisEquipment component2 = ((Component)localPlayer).GetComponent<VisEquipment>(); if ((Object)(object)component2 != (Object)null) { RefreshHairAndBeard(component2); Apply(component2, force: true); } } } } } HelmetToggleAnimation.Update(localPlayer); } internal static void ToggleLocal(Player player) { if ((Object)(object)player == (Object)null) { return; } VisEquipment component = ((Component)player).GetComponent<VisEquipment>(); ZNetView component2 = ((Component)player).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid() || !component2.IsOwner()) { return; } ZDO zDO = component2.GetZDO(); if (zDO != null) { bool desiredHidden = !(HelmetToggleAnimation.Pending ? (!zDO.GetBool(HiddenKey, false)) : zDO.GetBool(HiddenKey, false)); if (EquippedHelmetField?.GetValue(player) is ItemData) { HelmetToggleAnimation.Start(player, desiredHidden); } } } internal static void CommitToggle(Player player, bool hidden) { if ((Object)(object)player == (Object)null) { return; } VisEquipment component = ((Component)player).GetComponent<VisEquipment>(); ZNetView component2 = ((Component)player).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid() || !component2.IsOwner()) { return; } ZDO zDO = component2.GetZDO(); if (zDO != null) { object? obj = EquippedHelmetField?.GetValue(player); ItemData val = (ItemData)((obj is ItemData) ? obj : null); zDO.Set(HiddenKey, hidden); SavedHelmetStates.SetHidden(player.GetPlayerID(), hidden); RefreshHairAndBeard(component); Apply(component, force: true); PlayToggleEffects(player, component, val, hidden); if (val != null) { ShowNotification(hidden); } } } internal static bool IsHidden(VisEquipment vis) { if ((Object)(object)vis == (Object)null || !vis.m_isPlayer || vis.m_isArmorStand) { return false; } if ((Object)(object)vis == (Object)(object)_menuPreviewVis) { return SavedHelmetStates.IsHidden(_menuPreviewPlayerId); } ZNetView view = GetView(vis); ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null); if (val != null) { return val.GetBool(HiddenKey, false); } return false; } internal static void Apply(VisEquipment vis, bool force = false) { if ((Object)(object)vis == (Object)null || !vis.m_isPlayer || vis.m_isArmorStand || HelmetInstanceField == null || CurrentHelmetHashField == null) { return; } int instanceID = ((Object)vis).GetInstanceID(); if (!States.TryGetValue(instanceID, out var value)) { value = new VisualState(); States[instanceID] = value; } object? value2 = HelmetInstanceField.GetValue(vis); GameObject val = (GameObject)((value2 is GameObject) ? value2 : null); int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0); int num2 = (int)CurrentHelmetHashField.GetValue(vis); bool flag = IsHidden(vis); if (force || value.HelmetId != num || value.HelmetHash != num2 || value.Hidden != flag) { Restore(value); value.Helmet = val; value.HelmetId = num; value.HelmetHash = num2; value.Hidden = flag; if (flag && (Object)(object)val != (Object)null && num2 != 0) { val.SetActive(false); } } } internal static void Clear() { foreach (VisualState value in States.Values) { Restore(value); } States.Clear(); _trackedPlayer = null; _trackedHelmet = null; _restoredForPlayer = false; _menuPreviewVis = null; _menuPreviewPlayerId = 0L; } internal static void RestoreLocalSavedState(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } if ((Object)(object)player != (Object)(object)_trackedPlayer) { _trackedPlayer = player; object? obj = EquippedHelmetField?.GetValue(player); _trackedHelmet = (ItemData)((obj is ItemData) ? obj : null); _restoredForPlayer = false; } if (_restoredForPlayer) { return; } long playerID = player.GetPlayerID(); ZNetView component = ((Component)player).GetComponent<ZNetView>(); if (playerID == 0L || (Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } _restoredForPlayer = true; if (!SavedHelmetStates.IsHidden(playerID) || _trackedHelmet == null) { return; } ZDO zDO = component.GetZDO(); if (zDO != null) { zDO.Set(HiddenKey, true); VisEquipment component2 = ((Component)player).GetComponent<VisEquipment>(); if ((Object)(object)component2 != (Object)null) { RefreshHairAndBeard(component2); Apply(component2, force: true); } } } internal static void SetMenuPreview(Player preview, long playerId) { _menuPreviewVis = (((Object)(object)preview != (Object)null) ? ((Component)preview).GetComponent<VisEquipment>() : null); _menuPreviewPlayerId = playerId; if ((Object)(object)_menuPreviewVis != (Object)null) { RefreshHairAndBeard(_menuPreviewVis); Apply(_menuPreviewVis, force: true); } } private static void Restore(VisualState state) { if ((Object)(object)state?.Helmet != (Object)null) { state.Helmet.SetActive(true); } } private static ZNetView GetView(VisEquipment vis) { object? obj = NViewField?.GetValue(vis); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)vis.m_nViewOverride != (Object)null) { return vis.m_nViewOverride; } return ((Component)vis).GetComponent<ZNetView>(); } private static void RefreshHairAndBeard(VisEquipment vis) { if (!((Object)(object)vis == (Object)null) && !(UpdateEquipmentVisualsMethod == null)) { UpdateEquipmentVisualsMethod.Invoke(vis, null); } } private static void PlayToggleEffects(Player player, VisEquipment vis, ItemData helmetItem, bool hidden) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && helmetItem != null) { ZDOID zDOID = ((Character)player).GetZDOID(); if (hidden) { helmetItem.m_shared.m_unequipEffect.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, zDOID); } else { Transform val = (((Object)(object)vis != (Object)null) ? vis.m_helmet : null); Vector3 val2 = (((Object)(object)val != (Object)null) ? val.position : ((Component)player).transform.position); Quaternion val3 = (((Object)(object)val != (Object)null) ? val.rotation : ((Component)player).transform.rotation); helmetItem.m_shared.m_equipEffect.Create(val2, val3, (Transform)null, 1f, -1, zDOID); } TriggerEquipEffectMethod?.Invoke(player, new object[1] { helmetItem }); } } private static void ShowNotification(bool hidden) { if (!((Object)(object)MessageHud.instance == (Object)null)) { MessageHud.instance.ShowMessage((MessageType)1, hidden ? "Headpiece hidden" : "Headpiece visible", 0, (Sprite)null, false, true); } } } [HarmonyPatch(typeof(MaterialMan), "RegisterRenderers")] internal static class EarlyMaterialBlockPatch { private static readonly FieldInfo SharedBlock = AccessTools.Field(typeof(MaterialMan), "m_propertyBlock"); private static bool _reportedMissingField; [HarmonyPrefix] private static void Prefix(MaterialMan __instance) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null) { return; } if (SharedBlock == null) { if (!_reportedMissingField) { _reportedMissingField = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"MaterialMan private property-block field could not be found. Startup-order guard was not applied."); } } } else if (SharedBlock.GetValue(__instance) == null) { SharedBlock.SetValue(__instance, (object?)new MaterialPropertyBlock()); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"MaterialMan property block initialized before early renderer registration."); } } } } [BepInPlugin("wire.valheim.hoodtoggle", "Wire's Helmet Hider", "0.2.4")] [BepInProcess("valheim.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "wire.valheim.hoodtoggle"; public const string Name = "Wire's Helmet Hider"; public const string Version = "0.2.4"; internal static ConfigEntry<KeyboardShortcut> CustomToggleKey; internal static ConfigEntry<float> DoubleTapWindow; internal static ConfigEntry<float> EmoteHoldDuration; internal static ConfigEntry<bool> PlayEquipAnimation; internal static ConfigEntry<float> AnimationDuration; internal static ConfigEntry<bool> ResetOnHeadgearChange; private Harmony _harmony; internal static ManualLogSource Log; private void Awake() { //IL_0016: 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_0062: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown CustomToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Input", "Custom Toggle Key", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), "Optional direct toggle key. Leave None to use the default emote-button double tap."); DoubleTapWindow = ((BaseUnityPlugin)this).Config.Bind<float>("Input", "Double Tap Window", 0.3f, new ConfigDescription("Maximum time between two taps when using the emote button.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 0.6f), Array.Empty<object>())); EmoteHoldDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Input", "Emote Hold Duration", 0.33f, new ConfigDescription("How long the emote button must be held before the emote radial opens when using the default emote-button control.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 1f), Array.Empty<object>())); PlayEquipAnimation = ((BaseUnityPlugin)this).Config.Bind<bool>("Animation", "Play equip animation", true, "Play a short, synced visual-only equipping animation when toggling helmet visibility. No equipment action, movement restriction or delay beyond the visual."); AnimationDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Animation", "Animation duration", 0.22f, new ConfigDescription("Maximum time to play the cosmetic equip animation before applying the visual change. Cancelling the animation applies the change immediately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.08f, 0.4f), Array.Empty<object>())); ResetOnHeadgearChange = ((BaseUnityPlugin)this).Config.Bind<bool>("Visibility", "Reset hidden state on headgear change", true, "Show the headpiece again when actually equipping, unequipping or swapping headgear. Disable to keep the hidden state until manually toggled."); Log = ((BaseUnityPlugin)this).Logger; SavedHelmetStates.Load(); _harmony = new Harmony("wire.valheim.hoodtoggle"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Helmet Hider 0.2.4 loaded."); } private void Update() { HoodVisuals.UpdateLocal(); HoodInput.Update(); } private void OnDestroy() { HoodInput.Reset(); HelmetToggleAnimation.Cancel(commit: false); HoodVisuals.Clear(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } [HarmonyPatch(typeof(Player), "CheckKeyboardRadialPressed")] internal static class EmoteButtonHoldPatch { [HarmonyPrefix] private static bool Prefix(ref bool __result) { if (!HoodInput.UsingEmoteButton) { return true; } if (ZInput.GetButtonDown("OpenRadial")) { __result = true; return false; } __result = HoodInput.ShouldOpenEmoteRadial(); return false; } } [HarmonyPatch(typeof(VisEquipment), "CustomUpdate")] internal static class VisEquipmentUpdatePatch { [HarmonyPostfix] private static void Postfix(VisEquipment __instance) { HoodVisuals.Apply(__instance); } } [HarmonyPatch(typeof(VisEquipment), "GetHairItem")] internal static class HairVisibilityPatch { [HarmonyPostfix] private static void Postfix(VisEquipment __instance, int itemHash, ref int __result) { if (HoodVisuals.IsHidden(__instance)) { __result = itemHash; } } } internal static class SavedHelmetStates { private const string FileName = "wire.valheim.helmethider.hidden.txt"; private static readonly HashSet<long> HiddenIds = new HashSet<long>(); private static string _path; private static bool _loaded; private static bool _dirty; internal static string PathToFile => _path; internal static void Load() { if (_loaded) { return; } _loaded = true; _path = Path.Combine(Paths.ConfigPath, "wire.valheim.helmethider.hidden.txt"); HiddenIds.Clear(); try { if (!File.Exists(_path)) { File.WriteAllText(_path, string.Empty); return; } string[] array = File.ReadAllLines(_path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && !text.StartsWith("#") && long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result != 0L) { HiddenIds.Add(result); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not read helmet-hidden state file: " + ex.Message)); } } } internal static bool IsHidden(long playerId) { Load(); if (playerId != 0L) { return HiddenIds.Contains(playerId); } return false; } internal static void SetHidden(long playerId, bool hidden) { if (playerId != 0L) { Load(); if (hidden ? HiddenIds.Add(playerId) : HiddenIds.Remove(playerId)) { _dirty = true; } } } internal static void AfterCharacterSaved(PlayerProfile profile, bool saveSucceeded) { if (!saveSucceeded || !_dirty || profile == null || (Object)(object)Game.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || Game.instance.GetPlayerProfile() != profile || Player.m_localPlayer.GetPlayerID() != profile.GetPlayerID()) { return; } try { string[] array = new string[HiddenIds.Count]; List<long> list = new List<long>(HiddenIds); list.Sort(); for (int i = 0; i < list.Count; i++) { array[i] = list[i].ToString(CultureInfo.InvariantCulture); } string text = _path + ".tmp"; File.WriteAllLines(text, array); if (File.Exists(_path)) { File.Replace(text, _path, null); } else { File.Move(text, _path); } _dirty = false; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not save helmet-hidden state file: " + ex.Message)); } } } } [HarmonyPatch(typeof(PlayerProfile), "Save")] internal static class HelmetStatePlayerProfileSavePatch { [HarmonyPostfix] private static void Postfix(PlayerProfile __instance, bool __result) { SavedHelmetStates.AfterCharacterSaved(__instance, __result); } } [HarmonyPatch(typeof(Game), "SpawnPlayer")] internal static class HelmetStateSpawnPatch { [HarmonyPostfix] private static void Postfix(Player __result) { HoodVisuals.RestoreLocalSavedState(__result); } } [HarmonyPatch(typeof(FejdStartup), "SetupCharacterPreview")] internal static class HelmetStatePreviewPatch { [HarmonyPostfix] private static void Postfix(FejdStartup __instance, PlayerProfile profile) { HoodVisuals.SetMenuPreview(((Object)(object)__instance != (Object)null) ? __instance.GetPreviewPlayer() : null, (profile != null) ? profile.GetPlayerID() : 0); } } }