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 WiresWeaponMaster v0.2.1
WiresWeaponMaster.dll
Decompiled 15 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; 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("WiresWeaponMaster")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WiresWeaponMaster")] [assembly: AssemblyTitle("WiresWeaponMaster")] [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 WiresWeaponMaster { internal static class HoldRController { private const float HoldSeconds = 0.6f; private static readonly MethodInfo ShowHandItemsMethod = AccessTools.Method(typeof(Humanoid), "ShowHandItems", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private static bool _tracking; private static bool _fired; private static bool _gamepadHold; private static float _downTime; private static bool _hadHandsBeforePress; private static bool _pressHolsteredItems; private static bool _pressUnholsteredItems; internal static void BeforePlayerUpdate(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } if (!WeaponMasterConfig.IsEnabled) { ResetTracking(); } else if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !StoreGui.IsVisible() && !Minimap.IsOpen() && !Hud.InRadial()) { bool buttonDown = ZInput.GetButtonDown("Hide"); bool flag = ZInput.IsGamepadActive() && ZInput.GetButtonDown("JoyHide") && !ZInput.GetButton("JoyAltKeys") && !((Character)player).InPlaceMode(); if (buttonDown || flag) { _tracking = true; _fired = false; _gamepadHold = flag && !buttonDown; _downTime = Time.realtimeSinceStartup; _hadHandsBeforePress = WeaponMasterController.HasHandItems((Humanoid)(object)player); _pressHolsteredItems = false; _pressUnholsteredItems = false; } } } internal static void AfterPlayerUpdate(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } if (!WeaponMasterConfig.IsEnabled) { ResetTracking(); } else { if (!_tracking) { return; } if (!_gamepadHold && ZInput.GetButtonDown("Hide")) { CaptureVanillaToggleResult(player); } string text = (_gamepadHold ? "JoyHide" : "Hide"); if (!_fired && ZInput.GetButton(text) && Time.realtimeSinceStartup - _downTime >= 0.6f) { if (_gamepadHold) { PerformGamepadHoldToggle(player); } Fire(player); } if (ZInput.GetButtonUp(text) || (!ZInput.GetButton(text) && !ZInput.GetButtonDown(text))) { ResetTracking(); } } } private static void CaptureVanillaToggleResult(Player player) { bool flag = WeaponMasterController.HasHandItems((Humanoid)(object)player); bool flag2 = WeaponMasterController.HasHiddenHandItems((Humanoid)(object)player); _pressHolsteredItems = _hadHandsBeforePress && !flag && flag2; _pressUnholsteredItems = !_hadHandsBeforePress && flag; } private static void PerformGamepadHoldToggle(Player player) { if (_hadHandsBeforePress) { if (!((Character)player).InAttack() && !((Character)player).InDodge()) { ((Humanoid)player).HideHandItems(false, true); } } else if ((!((Character)player).IsSwimming() || ((Character)player).IsOnGround()) && !((Character)player).InDodge() && ShowHandItemsMethod != null) { ShowHandItemsMethod.Invoke(player, new object[2] { false, true }); } CaptureVanillaToggleResult(player); } private static void Fire(Player player) { _fired = true; WeaponMasterController.ClearHistoryPreserveCurrentHands(player); if (_pressHolsteredItems && !_pressUnholsteredItems) { WeaponMasterController.ClearHiddenHandItems(player); } WeaponMasterController.PlayClearFeedback(player); WeaponVisuals.RefreshLocal(); } internal static void ResetTracking() { _tracking = false; _fired = false; _gamepadHold = false; _downTime = 0f; _hadHandsBeforePress = false; _pressHolsteredItems = false; _pressUnholsteredItems = false; } } internal enum HolsterLane { None, Shield, Tool, Melee, TwoHandedMelee, Bow, Atgeir } internal static class ItemClassifier { internal static HolsterLane ResolveLane(ItemData item, bool rightHand) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected I4, but got Unknown //IL_0055: 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_0082: Expected I4, but got Unknown if (item == null || item.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null) { return HolsterLane.None; } ItemType val = (((int)item.m_shared.m_attachOverride != 0) ? item.m_shared.m_attachOverride : item.m_shared.m_itemType); switch (val - 3) { default: switch (val - 14) { case 5: return HolsterLane.Tool; case 0: case 8: return HolsterLane.TwoHandedMelee; case 6: return HolsterLane.Atgeir; case 1: if (!rightHand) { return HolsterLane.Tool; } return HolsterLane.Melee; default: return HolsterLane.None; } case 2: return HolsterLane.Shield; case 0: return HolsterLane.Melee; case 1: return HolsterLane.Bow; } } internal static bool HasHolsterVisual(ItemData item) { if (item == null || (Object)(object)item.m_dropPrefab == (Object)null) { return false; } Transform transform = item.m_dropPrefab.transform; for (int i = 0; i < transform.childCount; i++) { string name = ((Object)transform.GetChild(i)).name; if (name == "attach_back" || name == "attach") { return true; } } return false; } internal static bool UsesRightHandForBackAttach(HolsterLane lane) { return lane == HolsterLane.Melee; } } internal readonly struct HolsterSelection { internal readonly int Hash; internal readonly int Variant; internal readonly int Quality; internal int PackedMeta => (Quality << 16) | (Variant & 0xFFFF); internal HolsterSelection(int hash, int variant, int quality) { Hash = hash; Variant = variant; Quality = quality; } internal static HolsterSelection FromPacked(int hash, int meta) { if (hash == 0) { return default(HolsterSelection); } int variant = meta & 0xFFFF; int quality = (meta >> 16) & 0xFFFF; return new HolsterSelection(hash, variant, quality); } } internal static class MultiplayerSync { private const int LaneCount = 7; private static readonly FieldInfo NViewField = AccessTools.Field(typeof(VisEquipment), "m_nview"); private static readonly int[] HashKeys = BuildKeys("hash"); private static readonly int[] MetaKeys = BuildKeys("meta"); private static int[] BuildKeys(string suffix) { int[] array = new int[7]; for (int i = 1; i < 7; i++) { array[i] = StringExtensionMethods.GetStableHashCode("wire.weaponmaster.v2.lane" + i + "." + suffix); } return array; } internal static ZNetView GetNetView(VisEquipment vis) { if (!((Object)(object)vis == (Object)null) && !(NViewField == null)) { object? value = NViewField.GetValue(vis); return (ZNetView)((value is ZNetView) ? value : null); } return null; } internal static ZDO GetZdo(VisEquipment vis) { ZNetView netView = GetNetView(vis); if (!((Object)(object)netView != (Object)null) || !netView.IsValid()) { return null; } return netView.GetZDO(); } internal static void Publish(VisEquipment vis, HolsterSelection[] selections) { if ((Object)(object)vis == (Object)null || selections == null) { return; } ZNetView netView = GetNetView(vis); if ((Object)(object)netView == (Object)null || !netView.IsValid() || !netView.IsOwner()) { return; } ZDO zDO = netView.GetZDO(); if (zDO == null) { return; } for (int i = 1; i < 7; i++) { HolsterSelection holsterSelection = selections[i]; int hash = holsterSelection.Hash; int num = ((hash != 0) ? holsterSelection.PackedMeta : 0); if (zDO.GetInt(HashKeys[i], 0) != hash) { zDO.Set(HashKeys[i], hash, false); } if (zDO.GetInt(MetaKeys[i], 0) != num) { zDO.Set(MetaKeys[i], num, false); } } } internal static HolsterSelection Read(VisEquipment vis, HolsterLane lane) { ZDO zdo = GetZdo(vis); if (zdo == null || lane == HolsterLane.None) { return default(HolsterSelection); } int hash = zdo.GetInt(HashKeys[(int)lane], 0); int meta = zdo.GetInt(MetaKeys[(int)lane], 0); return HolsterSelection.FromPacked(hash, meta); } internal static void ClearLocal() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { VisEquipment visEquipment = WeaponVisuals.GetVisEquipment((Humanoid)(object)localPlayer); if (!((Object)(object)visEquipment == (Object)null)) { Publish(visEquipment, new HolsterSelection[7]); } } } } internal static class Patches { [HarmonyPatch(typeof(Player), "Start")] private static class PlayerStartPatch { [HarmonyPostfix] private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { WeaponMasterController.Reset(); WeaponMasterController.SeedCurrentState(__instance); } } } [HarmonyPatch(typeof(Player), "OnDestroy")] private static class PlayerDestroyPatch { [HarmonyPrefix] private static void Prefix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { MultiplayerSync.ClearLocal(); HoldRController.ResetTracking(); WeaponVisuals.Cleanup(); } } } [HarmonyPatch(typeof(Player), "Update")] private static class PlayerUpdatePatch { [HarmonyPrefix] private static void Prefix(Player __instance) { HoldRController.BeforePlayerUpdate(__instance); } [HarmonyPostfix] private static void Postfix(Player __instance) { HoldRController.AfterPlayerUpdate(__instance); } } [HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[] { typeof(ItemData), typeof(bool) })] private static class EquipItemPatch { [HarmonyPostfix] private static void Postfix(Humanoid __instance, ItemData item, bool __result) { if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { WeaponMasterController.NoteUsed(__instance, item); WeaponVisuals.RefreshLocal(); } } } [HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[] { typeof(bool), typeof(bool) })] private static class HideHandItemsPatch { [HarmonyPostfix] private static void Postfix(Humanoid __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { WeaponVisuals.RefreshLocal(); } } } [HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[] { typeof(bool), typeof(bool) })] private static class ShowHandItemsPatch { [HarmonyPostfix] private static void Postfix(Humanoid __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { WeaponVisuals.RefreshLocal(); } } } [HarmonyPatch(typeof(Inventory), "Changed", new Type[] { typeof(bool), typeof(bool) })] private static class InventoryChangedPatch { [HarmonyPostfix] private static void Postfix(Inventory __instance) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && ((Humanoid)localPlayer).GetInventory() == __instance) { WeaponVisuals.RefreshLocal(); } } } [HarmonyPatch(typeof(VisEquipment), "UpdateEquipmentVisuals")] private static class VisEquipmentUpdatePatch { [HarmonyPostfix] private static void Postfix(VisEquipment __instance) { WeaponVisuals.SyncNetwork(__instance); } } [HarmonyPatch(typeof(VisEquipment), "OnDisable")] private static class VisEquipmentDisablePatch { [HarmonyPrefix] private static void Prefix(VisEquipment __instance) { WeaponVisuals.Unregister(__instance); } } } [BepInPlugin("wire.valheim.weaponmaster", "Wire's Weapon Master", "0.2.1")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "wire.valheim.weaponmaster"; public const string Name = "Wire's Weapon Master"; public const string Version = "0.2.1"; internal static Plugin Instance; private Harmony _harmony; private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; WeaponMasterConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("wire.valheim.weaponmaster"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Weapon Master 0.2.1 loaded."); } internal static void LogInfo(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } } internal static void LogWarning(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)message); } } private void OnDestroy() { MultiplayerSync.ClearLocal(); WeaponMasterConfigSync.Reset(); WeaponVisuals.Cleanup(); if (_harmony != null) { _harmony.UnpatchSelf(); } Instance = null; } } internal static class VanillaAttachment { private static readonly MethodInfo AttachBackItemMethod = AccessTools.Method(typeof(VisEquipment), "AttachBackItem", (Type[])null, (Type[])null); internal static GameObject AttachBackItem(VisEquipment vis, int hash, int variant, bool rightHand, int quality) { if ((Object)(object)vis == (Object)null || hash == 0 || AttachBackItemMethod == null) { return null; } try { object? obj = AttachBackItemMethod.Invoke(vis, new object[4] { hash, variant, rightHand, quality }); return (GameObject)((obj is GameObject) ? obj : null); } catch (Exception ex) { Plugin.LogWarning("Vanilla holster attach failed for item " + hash + ": " + ex.GetType().Name); return null; } } } internal static class WeaponMasterConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> BackShield; internal static ConfigEntry<bool> BackTool; internal static ConfigEntry<bool> BackMelee; internal static ConfigEntry<bool> BackTwohandedMelee; internal static ConfigEntry<bool> BackAtgeir; internal static ConfigEntry<bool> BackBow; internal static bool IsEnabled => WeaponMasterConfigSync.GetEnabled(Enabled == null || Enabled.Value); internal static void Bind(ConfigFile file) { Enabled = file.Bind<bool>("General", "Enabled", true, "Enable Wire's Weapon Master."); BackShield = BindCategory(file, "backShield"); BackTool = BindCategory(file, "backTool"); BackMelee = BindCategory(file, "backMelee"); BackTwohandedMelee = BindCategory(file, "backTwohandedMelee"); BackAtgeir = BindCategory(file, "backAtgeir"); BackBow = BindCategory(file, "backBow"); Enabled.SettingChanged += delegate { OnChanged(); }; } private static ConfigEntry<bool> BindCategory(ConfigFile file, string name) { ConfigEntry<bool> obj = file.Bind<bool>("Permanent display categories", name, true, "Keep this vanilla holster category permanently visible when Weapon Master has a recent item for it. Disabling this does not affect normal Valheim stowing; actually stowed items can still appear."); obj.SettingChanged += delegate { OnChanged(); }; return obj; } private static void OnChanged() { WeaponMasterConfigSync.NotifyLocalChanged(); if (!IsEnabled) { HoldRController.ResetTracking(); } WeaponVisuals.RefreshConfig(); } internal static bool IsLaneEnabled(HolsterLane lane) { if (!IsEnabled) { return false; } bool local; switch (lane) { case HolsterLane.Shield: local = BackShield == null || BackShield.Value; break; case HolsterLane.Tool: local = BackTool == null || BackTool.Value; break; case HolsterLane.Melee: local = BackMelee == null || BackMelee.Value; break; case HolsterLane.TwoHandedMelee: local = BackTwohandedMelee == null || BackTwohandedMelee.Value; break; case HolsterLane.Atgeir: local = BackAtgeir == null || BackAtgeir.Value; break; case HolsterLane.Bow: local = BackBow == null || BackBow.Value; break; default: return false; } return WeaponMasterConfigSync.GetLaneEnabled(lane, local); } internal static bool GetLocalLane(HolsterLane lane) { switch (lane) { case HolsterLane.Shield: if (BackShield != null) { return BackShield.Value; } return true; case HolsterLane.Tool: if (BackTool != null) { return BackTool.Value; } return true; case HolsterLane.Melee: if (BackMelee != null) { return BackMelee.Value; } return true; case HolsterLane.TwoHandedMelee: if (BackTwohandedMelee != null) { return BackTwohandedMelee.Value; } return true; case HolsterLane.Atgeir: if (BackAtgeir != null) { return BackAtgeir.Value; } return true; case HolsterLane.Bow: if (BackBow != null) { return BackBow.Value; } return true; default: return false; } } } internal static class WeaponMasterConfigSync { private const string RequestRpc = "WWM_ConfigRequest"; private const string ConfigRpc = "WWM_Config"; private static bool _registered; private static bool _serverEnforced; private static bool _enabled = true; private static readonly bool[] Lanes = new bool[7] { false, true, true, true, true, true, true }; internal static bool GetEnabled(bool local) { if (!_serverEnforced) { return local; } return _enabled; } internal static bool GetLaneEnabled(HolsterLane lane, bool local) { if (!_serverEnforced || lane <= HolsterLane.None || (int)lane >= Lanes.Length) { return local; } return Lanes[(int)lane]; } internal static void Register() { if (!_registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("WWM_ConfigRequest", (Action<long>)OnRequested); ZRoutedRpc.instance.Register<ZPackage>("WWM_Config", (Action<long, ZPackage>)OnReceived); _registered = true; } } internal static void RequestFromServer() { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("WWM_ConfigRequest", Array.Empty<object>()); } } internal static void NotifyLocalChanged() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "WWM_Config", new object[1] { BuildPackage() }); } } private static void OnRequested(long sender) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "WWM_Config", new object[1] { BuildPackage() }); } } private static ZPackage BuildPackage() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(WeaponMasterConfig.Enabled == null || WeaponMasterConfig.Enabled.Value); for (int i = 1; i < 7; i++) { val.Write(WeaponMasterConfig.GetLocalLane((HolsterLane)i)); } return val; } private static void OnReceived(long sender, ZPackage package) { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && package != null) { _enabled = package.ReadBool(); for (int i = 1; i < 7; i++) { Lanes[i] = package.ReadBool(); } _serverEnforced = true; if (!_enabled) { HoldRController.ResetTracking(); } WeaponVisuals.RefreshConfig(); Plugin.LogInfo("Using Weapon Master settings from the server."); } } internal static void Reset() { _registered = false; _serverEnforced = false; _enabled = true; for (int i = 1; i < 7; i++) { Lanes[i] = true; } } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class WeaponMasterConfigSyncAwakePatch { [HarmonyPostfix] private static void Postfix() { WeaponMasterConfigSync.Reset(); WeaponMasterConfigSync.Register(); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class WeaponMasterConfigSyncPeerPatch { [HarmonyPostfix] private static void Postfix() { WeaponMasterConfigSync.Register(); WeaponMasterConfigSync.RequestFromServer(); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class WeaponMasterConfigSyncShutdownPatch { [HarmonyPostfix] private static void Postfix() { WeaponMasterConfigSync.Reset(); } } internal static class WeaponMasterController { private const int LaneCount = 7; private static readonly List<ItemData>[] History = new List<ItemData>[7] { null, new List<ItemData>(8), new List<ItemData>(8), new List<ItemData>(8), new List<ItemData>(8), new List<ItemData>(8), new List<ItemData>(8) }; private static readonly FieldInfo LeftItemField = AccessTools.Field(typeof(Humanoid), "m_leftItem"); private static readonly FieldInfo RightItemField = AccessTools.Field(typeof(Humanoid), "m_rightItem"); private static readonly FieldInfo HiddenLeftItemField = AccessTools.Field(typeof(Humanoid), "m_hiddenLeftItem"); private static readonly FieldInfo HiddenRightItemField = AccessTools.Field(typeof(Humanoid), "m_hiddenRightItem"); private static readonly MethodInfo TriggerEquipEffectMethod = AccessTools.Method(typeof(Humanoid), "TriggerEquipEffect", (Type[])null, (Type[])null); internal static void Reset() { for (int i = 1; i < 7; i++) { History[i].Clear(); } WeaponVisuals.RefreshLocal(); } internal static void SeedCurrentState(Player player) { if (!((Object)(object)player == (Object)null)) { NoteUsed((Humanoid)(object)player, GetLeft((Humanoid)(object)player)); NoteUsed((Humanoid)(object)player, GetRight((Humanoid)(object)player)); ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)player); ItemData hiddenRight = GetHiddenRight((Humanoid)(object)player); Promote(hiddenLeft, ItemClassifier.ResolveLane(hiddenLeft, rightHand: false)); Promote(hiddenRight, ItemClassifier.ResolveLane(hiddenRight, rightHand: true)); WeaponVisuals.RefreshLocal(); } } internal static void NoteUsed(Humanoid humanoid, ItemData item) { if (!((Object)(object)humanoid == (Object)null) && !((Object)(object)humanoid != (Object)(object)Player.m_localPlayer) && item != null) { bool rightHand = GetRight(humanoid) == item; HolsterLane lane = ItemClassifier.ResolveLane(item, rightHand); Promote(item, lane); } } private static void Promote(ItemData item, HolsterLane lane) { if (item == null || lane == HolsterLane.None) { return; } for (int i = 1; i < 7; i++) { List<ItemData> list = History[i]; for (int num = list.Count - 1; num >= 0; num--) { if (list[num] == item) { list.RemoveAt(num); } } } History[(int)lane].Insert(0, item); } internal static ItemData GetVisibleForLane(HolsterLane lane) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || lane == HolsterLane.None) { return null; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return null; } PruneMissing(inventory); ItemData left = GetLeft((Humanoid)(object)localPlayer); ItemData right = GetRight((Humanoid)(object)localPlayer); ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)localPlayer); ItemData hiddenRight = GetHiddenRight((Humanoid)(object)localPlayer); if (OccupiesLane(hiddenLeft, rightHand: false, lane) || OccupiesLane(hiddenRight, rightHand: true, lane)) { return null; } List<ItemData> list = History[(int)lane]; for (int i = 0; i < list.Count; i++) { ItemData val = list[i]; if (val != left && val != right && val != hiddenLeft && val != hiddenRight && ItemClassifier.HasHolsterVisual(val)) { return val; } } return null; } private static bool OccupiesLane(ItemData item, bool rightHand, HolsterLane lane) { if (item != null) { return ItemClassifier.ResolveLane(item, rightHand) == lane; } return false; } private static void PruneMissing(Inventory inventory) { for (int i = 1; i < 7; i++) { List<ItemData> list = History[i]; for (int num = list.Count - 1; num >= 0; num--) { ItemData val = list[num]; if (val == null || !inventory.ContainsItem(val)) { list.RemoveAt(num); } } } } internal static void ClearHistoryPreserveCurrentHands(Player player) { if (!((Object)(object)player == (Object)null)) { ItemData left = GetLeft((Humanoid)(object)player); ItemData right = GetRight((Humanoid)(object)player); for (int i = 1; i < 7; i++) { History[i].Clear(); } NoteUsed((Humanoid)(object)player, left); NoteUsed((Humanoid)(object)player, right); WeaponVisuals.RefreshLocal(); } } internal static void ClearHiddenHandItems(Player player) { if (!((Object)(object)player == (Object)null)) { ItemData hiddenLeft = GetHiddenLeft((Humanoid)(object)player); ItemData hiddenRight = GetHiddenRight((Humanoid)(object)player); if (hiddenLeft != null) { ((Humanoid)player).UnequipItem(hiddenLeft, false); } if (hiddenRight != null && hiddenRight != hiddenLeft) { ((Humanoid)player).UnequipItem(hiddenRight, false); } } } internal static bool HasHandItems(Humanoid humanoid) { if ((Object)(object)humanoid != (Object)null) { if (GetLeft(humanoid) == null) { return GetRight(humanoid) != null; } return true; } return false; } internal static bool HasHiddenHandItems(Humanoid humanoid) { if ((Object)(object)humanoid != (Object)null) { if (GetHiddenLeft(humanoid) == null) { return GetHiddenRight(humanoid) != null; } return true; } return false; } internal static void PlayClearFeedback(Player player) { if ((Object)(object)player == (Object)null || TriggerEquipEffectMethod == null) { return; } try { TriggerEquipEffectMethod.Invoke(player, new object[1]); } catch (Exception ex) { Plugin.LogWarning("Clear feedback failed: " + ex.GetType().Name); } } internal static ItemData GetLeft(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { object? value = LeftItemField.GetValue(humanoid); return (ItemData)((value is ItemData) ? value : null); } return null; } internal static ItemData GetRight(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { object? value = RightItemField.GetValue(humanoid); return (ItemData)((value is ItemData) ? value : null); } return null; } internal static ItemData GetHiddenLeft(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { object? value = HiddenLeftItemField.GetValue(humanoid); return (ItemData)((value is ItemData) ? value : null); } return null; } internal static ItemData GetHiddenRight(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { object? value = HiddenRightItemField.GetValue(humanoid); return (ItemData)((value is ItemData) ? value : null); } return null; } } internal static class WeaponVisuals { private sealed class VisualState { internal readonly GameObject[] Instances = (GameObject[])(object)new GameObject[7]; internal readonly int[] Hashes = new int[7]; internal readonly int[] Metas = new int[7]; } private const int LaneCount = 7; private static readonly FieldInfo VisEquipmentField = AccessTools.Field(typeof(Humanoid), "m_visEquipment"); private static readonly Dictionary<VisEquipment, VisualState> States = new Dictionary<VisEquipment, VisualState>(); internal static VisEquipment GetVisEquipment(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null) && !(VisEquipmentField == null)) { object? value = VisEquipmentField.GetValue(humanoid); return (VisEquipment)((value is VisEquipment) ? value : null); } return null; } internal static void RefreshLocal() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { VisEquipment visEquipment = GetVisEquipment((Humanoid)(object)localPlayer); if (!((Object)(object)visEquipment == (Object)null)) { HolsterSelection[] selections = BuildLocalSelections(); MultiplayerSync.Publish(visEquipment, selections); ApplySelections(visEquipment, selections); } } } internal static void RefreshConfig() { RefreshLocal(); List<VisEquipment> list = new List<VisEquipment>(States.Keys); for (int i = 0; i < list.Count; i++) { VisEquipment val = list[i]; if ((Object)(object)val != (Object)null) { SyncNetwork(val); } } } private static HolsterSelection[] BuildLocalSelections() { HolsterSelection[] array = new HolsterSelection[7]; for (int i = 1; i < 7; i++) { HolsterLane lane = (HolsterLane)i; if (WeaponMasterConfig.IsLaneEnabled(lane)) { ItemData visibleForLane = WeaponMasterController.GetVisibleForLane(lane); if (visibleForLane != null && !((Object)(object)visibleForLane.m_dropPrefab == (Object)null)) { array[i] = new HolsterSelection(StringExtensionMethods.GetStableHashCode(((Object)visibleForLane.m_dropPrefab).name), visibleForLane.m_variant, visibleForLane.m_quality); } } } return array; } internal static void SyncNetwork(VisEquipment vis) { if (!((Object)(object)vis == (Object)null) && vis.m_isPlayer && !vis.m_isArmorStand) { VisualState orCreateState = GetOrCreateState(vis); ZDO zdo = MultiplayerSync.GetZdo(vis); int vanillaLeftBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_leftBackItem, 0) : 0); int vanillaRightBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_rightBackItem, 0) : 0); for (int i = 1; i < 7; i++) { HolsterLane lane = (HolsterLane)i; ApplySelection(vis, orCreateState, lane, MultiplayerSync.Read(vis, lane), vanillaLeftBack, vanillaRightBack); } } } private static void ApplySelections(VisEquipment vis, HolsterSelection[] selections) { if (!((Object)(object)vis == (Object)null) && selections != null) { VisualState orCreateState = GetOrCreateState(vis); ZDO zdo = MultiplayerSync.GetZdo(vis); int vanillaLeftBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_leftBackItem, 0) : 0); int vanillaRightBack = ((zdo != null) ? zdo.GetInt(ZDOVars.s_rightBackItem, 0) : 0); for (int i = 1; i < 7; i++) { ApplySelection(vis, orCreateState, (HolsterLane)i, selections[i], vanillaLeftBack, vanillaRightBack); } } } private static VisualState GetOrCreateState(VisEquipment vis) { if (!States.TryGetValue(vis, out var value)) { value = new VisualState(); States.Add(vis, value); } return value; } private static void ApplySelection(VisEquipment vis, VisualState state, HolsterLane lane, HolsterSelection selection, int vanillaLeftBack, int vanillaRightBack) { if (!WeaponMasterConfig.IsLaneEnabled(lane)) { selection = default(HolsterSelection); } int num = selection.Hash; int num2 = ((num != 0) ? selection.PackedMeta : 0); if (num != 0 && (num == vanillaLeftBack || num == vanillaRightBack)) { num = 0; num2 = 0; selection = default(HolsterSelection); } if (state.Hashes[(int)lane] != num || state.Metas[(int)lane] != num2) { Replace(vis, state, lane, selection, num, num2); } } private static void Replace(VisEquipment vis, VisualState state, HolsterLane lane, HolsterSelection selection, int hash, int meta) { DestroyInstance(state, (int)lane); state.Hashes[(int)lane] = hash; state.Metas[(int)lane] = meta; if (hash != 0) { bool rightHand = ItemClassifier.UsesRightHandForBackAttach(lane); state.Instances[(int)lane] = VanillaAttachment.AttachBackItem(vis, hash, selection.Variant, rightHand, selection.Quality); } } private static void DestroyInstance(VisualState state, int index) { if ((Object)(object)state.Instances[index] != (Object)null) { Object.Destroy((Object)(object)state.Instances[index]); } state.Instances[index] = null; state.Hashes[index] = 0; state.Metas[index] = 0; } internal static void Unregister(VisEquipment vis) { if (!((Object)(object)vis == (Object)null) && States.TryGetValue(vis, out var value)) { for (int i = 1; i < 7; i++) { DestroyInstance(value, i); } States.Remove(vis); } } internal static void Cleanup() { foreach (KeyValuePair<VisEquipment, VisualState> state in States) { VisualState value = state.Value; for (int i = 1; i < 7; i++) { DestroyInstance(value, i); } } States.Clear(); } } }