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 BagBeltMod v1.1.0
BepInEx/plugins/BagBeltMod/BroMode.BagBeltMod.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BroMode.BagBeltMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("BagBeltMod")] [assembly: AssemblyTitle("BroMode.BagBeltMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 BagBeltMod { [BepInPlugin("BroMode.BagBeltMod", "BagBeltMod", "1.1.0")] public class BagBeltMod : BaseUnityPlugin { private const int RequiredPrice = 80; private static readonly string[] RequiredUtilityItems = new string[12] { "Flashlight", "Pro-flashlight", "Stun grenade", "Walkie-talkie", "TZP-Inhalant", "Lockpicker", "Zap gun", "Key", "Shotgun shells", "Shovel", "Yield sign", "Stop sign" }; public static BagBeltMod Instance { get; private set; } = null; internal static ManualLogSource Logger { get; private set; } = null; internal static Harmony? Harmony { get; set; } internal static ConfigEntry<bool> Enabled { get; private set; } = null; internal static ConfigEntry<int> Price { get; private set; } = null; internal static ConfigEntry<int> ExtraSlotsGranted { get; private set; } = null; internal static ConfigEntry<string> UtilityWhitelist { get; private set; } = null; internal static ConfigEntry<bool> LogDebug { get; private set; } = null; private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable the Bag Belt item."); Price = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Price", 80, "Price of Bag Belt in the store."); ExtraSlotsGranted = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ExtraSlotsGranted", 2, "Extra hotbar slots granted when Bag Belt is equipped."); UtilityWhitelist = ((BaseUnityPlugin)this).Config.Bind<string>("General", "UtilityWhitelist", "Flashlight,Pro-flashlight,Stun grenade,Walkie-talkie,TZP-Inhalant,Lockpicker,Zap gun,Key,Shotgun shells,Shovel,Yield sign,Stop sign", "Comma-separated item names allowed in the utility-only slot."); LogDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogDebug", false, "Enable debug logging for Bag Belt behavior."); EnsureUtilityWhitelistHasRequiredEntries(); if (Price.Value != 80) { Logger.LogInfo((object)$"Bag Belt price reset to ${80} (was ${Price.Value})."); Price.Value = 80; ((BaseUnityPlugin)this).Config.Save(); } WarnIfDuplicateBagBeltDllsLoaded(); Patch(); Logger.LogInfo((object)"BroMode.BagBeltMod v1.1.0 has loaded!"); Logger.LogInfo((object)$"Bag Belt enabled: {Enabled.Value}"); Logger.LogInfo((object)$"Bag Belt price: ${Price.Value}"); } internal static void Patch() { //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_0017: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony("BroMode.BagBeltMod"); } Logger.LogDebug((object)"Patching..."); Harmony.PatchAll(); Logger.LogDebug((object)"Finished patching!"); } private static void WarnIfDuplicateBagBeltDllsLoaded() { try { if (Directory.Exists(Paths.PluginPath)) { string[] files = Directory.GetFiles(Paths.PluginPath, "*BagBeltMod.dll", SearchOption.AllDirectories); if (files.Length > 1) { string text = string.Join(", ", files.Select(Path.GetFileName)); Logger.LogWarning((object)("Multiple BagBelt DLLs detected: " + text + ". Keep only one to avoid patch conflicts.")); } } } catch (Exception ex) { Logger.LogDebug((object)("Bag Belt duplicate DLL check skipped: " + ex.Message)); } } private void EnsureUtilityWhitelistHasRequiredEntries() { string text = UtilityWhitelist.Value ?? string.Empty; List<string> list = (from x in text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim() into x where x.Length > 0 select x).ToList(); HashSet<string> hashSet = new HashSet<string>(list, StringComparer.OrdinalIgnoreCase); bool flag = false; for (int i = 0; i < RequiredUtilityItems.Length; i++) { string item = RequiredUtilityItems[i]; if (!hashSet.Contains(item)) { list.Add(item); hashSet.Add(item); flag = true; } } if (flag) { UtilityWhitelist.Value = string.Join(",", list); ((BaseUnityPlugin)this).Config.Save(); Logger.LogInfo((object)"Bag Belt utility whitelist updated with required default items."); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "BroMode.BagBeltMod"; public const string PLUGIN_NAME = "BagBeltMod"; public const string PLUGIN_VERSION = "1.1.0"; } } namespace BagBeltMod.Patches { [HarmonyPatch(typeof(ItemDropship))] internal static class BagBeltDropshipSpawnPatch { private sealed class DeliverySnapshot { internal readonly HashSet<ulong> ExistingObjectIds = new HashSet<ulong>(); internal readonly List<bool> BagFlags = new List<bool>(); internal Transform? PropsContainer; internal Transform[] SpawnPositions = Array.Empty<Transform>(); } [HarmonyPrefix] [HarmonyPatch("OpenShipDoorsOnServer")] private static void OpenShipDoorsOnServerPrefix(ItemDropship __instance, out DeliverySnapshot? __state) { __state = null; if (!BagBeltMod.Enabled.Value) { return; } Terminal value = Traverse.Create((object)__instance).Field("terminalScript").GetValue<Terminal>(); StartOfRound value2 = Traverse.Create((object)__instance).Field("playersManager").GetValue<StartOfRound>(); List<int> value3 = Traverse.Create((object)__instance).Field("itemsToDeliver").GetValue<List<int>>(); if ((Object)(object)value == (Object)null || (Object)(object)value2 == (Object)null || (Object)(object)value2.propsContainer == (Object)null || value3 == null) { return; } DeliverySnapshot deliverySnapshot = new DeliverySnapshot { PropsContainer = value2.propsContainer, SpawnPositions = (__instance.itemSpawnPositions ?? Array.Empty<Transform>()) }; NetworkObject[] componentsInChildren = ((Component)value2.propsContainer).GetComponentsInChildren<NetworkObject>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { deliverySnapshot.ExistingObjectIds.Add(componentsInChildren[i].NetworkObjectId); } } Item[] array = value.buyableItemsList ?? Array.Empty<Item>(); for (int j = 0; j < value3.Count; j++) { int num = value3[j]; if (num >= 0 && num < array.Length) { Item val = array[num]; bool item = (Object)(object)val == (Object)(object)BagBeltRegistry.BagBeltItem || num == BagBeltRegistry.BagBeltBuyIndex; deliverySnapshot.BagFlags.Add(item); } } __state = deliverySnapshot; } [HarmonyPostfix] [HarmonyPatch("OpenShipDoorsOnServer")] private static void OpenShipDoorsOnServerPostfix(ItemDropship __instance, DeliverySnapshot? __state) { if (__state == null || (Object)(object)__state.PropsContainer == (Object)null || __state.BagFlags.Count == 0) { return; } List<GrabbableObject> list = new List<GrabbableObject>(); GrabbableObject[] componentsInChildren = ((Component)__state.PropsContainer).GetComponentsInChildren<GrabbableObject>(true); foreach (GrabbableObject val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { NetworkObject component = ((Component)val).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null) && !__state.ExistingObjectIds.Contains(component.NetworkObjectId)) { list.Add(val); } } } if (list.Count == 0) { return; } HashSet<int> usedIndices = new HashSet<int>(); int num = 0; for (int j = 0; j < __state.BagFlags.Count; j++) { GrabbableObject val2 = FindNearestUnassignedItem(list, usedIndices, __state.SpawnPositions, num); if ((Object)(object)val2 != (Object)null && __state.BagFlags[j]) { if ((Object)(object)BagBeltRegistry.BagBeltItem != (Object)null) { val2.itemProperties = BagBeltRegistry.BagBeltItem; } BagBeltNetworkSync.MarkBagObjectOnHostAndBroadcast(val2); if (BagBeltMod.LogDebug.Value) { NetworkObject component2 = ((Component)val2).GetComponent<NetworkObject>(); BagBeltMod.Logger.LogDebug((object)$"Bag Belt tagged after vanilla dropship spawn. netId={((component2 != null) ? new ulong?(component2.NetworkObjectId) : null)}"); } } if (__state.SpawnPositions.Length != 0) { num = ((num < __state.SpawnPositions.Length - 1) ? (num + 1) : 0); } } } private static GrabbableObject? FindNearestUnassignedItem(List<GrabbableObject> items, HashSet<int> usedIndices, Transform[] spawnPositions, int spawnIndex) { //IL_0008: 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_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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) int num = -1; float num2 = float.MaxValue; Vector3 val = Vector3.zero; bool flag = spawnPositions.Length != 0 && spawnIndex >= 0 && spawnIndex < spawnPositions.Length && (Object)(object)spawnPositions[spawnIndex] != (Object)null; if (flag) { val = spawnPositions[spawnIndex].position; } for (int i = 0; i < items.Count; i++) { if (!usedIndices.Contains(i) && !((Object)(object)items[i] == (Object)null)) { float num3; if (!flag) { num3 = i; } else { Vector3 val2 = ((Component)items[i]).transform.position - val; num3 = ((Vector3)(ref val2)).sqrMagnitude; } float num4 = num3; if (num4 < num2) { num2 = num4; num = i; } } } if (num < 0) { return null; } usedIndices.Add(num); return items[num]; } } internal static class BagBeltDropSuppression { private const int UtilitySlotIndex = 4; private const int BagSlotIndex = 5; internal static bool TeleporterContext; internal static void DropAllHeldItemsPreserveBagBelt(PlayerControllerB player) { if (!BagBeltMod.Enabled.Value) { player.DropAllHeldItems(true, false); return; } GrabbableObject bag; int bagSlot; bool flag = TryFindBagBelt(player, out bag, out bagSlot); for (int i = 0; i < player.ItemSlots.Length; i++) { GrabbableObject val = player.ItemSlots[i]; if (!((Object)(object)val == (Object)null) && !BagBeltInventoryPatch.IsBagBelt(val) && (!flag || i != 4)) { BagBeltInventoryPatch.ForceDropItem(player, val, i); } } if ((Object)(object)bag != (Object)null) { player.currentItemSlot = bagSlot; player.currentlyHeldObjectServer = bag; player.isHoldingObject = true; player.twoHanded = false; player.twoHandedAnimation = false; } } private static bool TryFindBagBelt(PlayerControllerB player, out GrabbableObject? bag, out int bagSlot) { bag = null; bagSlot = -1; for (int i = 0; i < player.ItemSlots.Length; i++) { GrabbableObject val = player.ItemSlots[i]; if (BagBeltInventoryPatch.IsBagBelt(val)) { bag = val; bagSlot = i; return true; } } if (5 < player.ItemSlots.Length) { GrabbableObject val2 = player.ItemSlots[5]; if (val2 is BeltBagItem) { bag = val2; bagSlot = 5; return true; } } return false; } } [HarmonyPatch(typeof(ShipTeleporter))] internal static class BagBeltDropSuppressionPatch { [CompilerGenerated] private sealed class <WrapTeleporterContext>d__4 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator inner; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WrapTeleporterContext>d__4(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; BagBeltDropSuppression.TeleporterContext = true; break; case 1: <>1__state = -1; break; } if (inner.MoveNext()) { <>2__current = inner.Current; <>1__state = 1; return true; } BagBeltDropSuppression.TeleporterContext = false; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPrefix] [HarmonyPatch("TeleportPlayerOutWithInverseTeleporter")] private static void InverseTeleporterPrefix() { BagBeltDropSuppression.TeleporterContext = true; } [HarmonyPostfix] [HarmonyPatch("TeleportPlayerOutWithInverseTeleporter")] private static void InverseTeleporterPostfix() { BagBeltDropSuppression.TeleporterContext = false; } [HarmonyPostfix] [HarmonyPatch("beamUpPlayer")] private static void BeamUpPlayerPostfix(ref IEnumerator __result) { __result = WrapTeleporterContext(__result); } [HarmonyPostfix] [HarmonyPatch("teleportBodyOut")] private static void TeleportBodyOutPostfix(ref IEnumerator __result) { __result = WrapTeleporterContext(__result); } [IteratorStateMachine(typeof(<WrapTeleporterContext>d__4))] private static IEnumerator WrapTeleporterContext(IEnumerator inner) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WrapTeleporterContext>d__4(0) { inner = inner }; } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class BagBeltDropAllHeldItemsPatch { [HarmonyPrefix] [HarmonyPatch("DropAllHeldItems")] private static bool DropAllHeldItemsPrefix(PlayerControllerB __instance) { if (BagBeltDropSuppression.TeleporterContext && BagBeltMod.Enabled.Value) { BagBeltDropSuppression.DropAllHeldItemsPreserveBagBelt(__instance); return false; } return true; } } [HarmonyPatch] internal static class BagBeltInventoryPatch { private const int BaseSlots = 4; private static readonly Dictionary<int, bool> BagEquippedByPlayer = new Dictionary<int, bool>(4); private static string? _cachedWhitelist; private static HashSet<string>? _whitelistSet; private static bool IsEnabled => BagBeltMod.Enabled.Value; private static int ExtraSlots => Math.Max(0, BagBeltMod.ExtraSlotsGranted.Value); private static int UtilitySlotIndex => 4; private static int BagSlotIndex => 5; internal static bool IsBagBelt(GrabbableObject? item) { return BagBeltRegistry.IsBagBelt(item); } private static bool IsUtilityAllowed(GrabbableObject item) { if (_whitelistSet == null || _cachedWhitelist != BagBeltMod.UtilityWhitelist.Value) { _cachedWhitelist = BagBeltMod.UtilityWhitelist.Value ?? string.Empty; _whitelistSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = _cachedWhitelist.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { _whitelistSet.Add(NormalizeItemName(text)); } } } if ((Object)(object)item.itemProperties != (Object)null) { return _whitelistSet.Contains(NormalizeItemName(item.itemProperties.itemName)); } return false; } private static string NormalizeItemName(string? raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } char[] array = raw.ToCharArray(); char[] array2 = new char[array.Length]; int num = 0; foreach (char c in array) { if (char.IsLetterOrDigit(c)) { array2[num++] = char.ToLowerInvariant(c); } } if (num != 0) { return new string(array2, 0, num); } return string.Empty; } private static bool HasBagBeltEquipped(PlayerControllerB player) { int instanceID = ((Object)player).GetInstanceID(); bool value; return BagEquippedByPlayer.TryGetValue(instanceID, out value) && value; } private static bool HasBagBeltInSlots(PlayerControllerB player) { if (player.ItemSlots == null) { return false; } for (int i = 0; i < player.ItemSlots.Length; i++) { if (IsBagBelt(player.ItemSlots[i])) { return true; } } return false; } private static void SetBagBeltEquipped(PlayerControllerB player, bool equipped) { BagEquippedByPlayer[((Object)player).GetInstanceID()] = equipped; } private static void EnsureExpandedSlots(PlayerControllerB player) { int num = 4 + ExtraSlots; if (num > 4 && player.ItemSlots != null) { if (player.ItemSlots.Length < num) { GrabbableObject[] array = (GrabbableObject[])(object)new GrabbableObject[num]; Array.Copy(player.ItemSlots, array, player.ItemSlots.Length); player.ItemSlots = array; } if (((NetworkBehaviour)player).IsOwner) { EnsureHudSlots(num); } } } private static void EnsureHudSlots(int target) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null || instance.itemSlotIcons == null || instance.itemSlotIconFrames == null || (instance.itemSlotIcons.Length >= target && instance.itemSlotIconFrames.Length >= target)) { return; } List<Image> list = new List<Image>(instance.itemSlotIcons); List<Image> list2 = new List<Image>(instance.itemSlotIconFrames); Vector2 val = Vector2.right * 55f; if (list.Count >= 2) { RectTransform rectTransform = ((Graphic)list[list.Count - 2]).rectTransform; RectTransform rectTransform2 = ((Graphic)list[list.Count - 1]).rectTransform; val = rectTransform2.anchoredPosition - rectTransform.anchoredPosition; if (val == Vector2.zero) { val = Vector2.right * 55f; } } while (list.Count < target) { Image val2 = list[list.Count - 1]; Image val3 = list2[list2.Count - 1]; Vector3 val4 = ((Transform)((Graphic)val2).rectTransform).position - ((Transform)((Graphic)val3).rectTransform).position; Image val5 = Object.Instantiate<Image>(val2, ((Component)val2).transform.parent); Image val6 = Object.Instantiate<Image>(val3, ((Component)val3).transform.parent); ((Graphic)val5).rectTransform.anchoredPosition = ((Graphic)val2).rectTransform.anchoredPosition + val; RectTransform rectTransform3 = ((Graphic)val6).rectTransform; rectTransform3.anchoredPosition += val; ((Transform)((Graphic)val5).rectTransform).position = ((Transform)((Graphic)val6).rectTransform).position + val4; ((Behaviour)val5).enabled = false; ((Behaviour)val6).enabled = true; list.Add(val5); list2.Add(val6); } instance.itemSlotIcons = list.ToArray(); instance.itemSlotIconFrames = list2.ToArray(); } private static int FindSlot(PlayerControllerB player, GrabbableObject item) { for (int i = 0; i < player.ItemSlots.Length; i++) { if ((Object)(object)player.ItemSlots[i] == (Object)(object)item) { return i; } } return -1; } private static int FindFirstEmptyBaseSlot(PlayerControllerB player) { int num = Math.Min(4, player.ItemSlots.Length); for (int i = 0; i < num; i++) { if ((Object)(object)player.ItemSlots[i] == (Object)null) { return i; } } return -1; } private static void UpdateSlotIcon(PlayerControllerB player, int slot) { if (((NetworkBehaviour)player).IsOwner && !((Object)(object)HUDManager.Instance == (Object)null) && slot >= 0 && slot < HUDManager.Instance.itemSlotIcons.Length) { GrabbableObject val = player.ItemSlots[slot]; if ((Object)(object)val == (Object)null || (Object)(object)val.itemProperties == (Object)null || (Object)(object)val.itemProperties.itemIcon == (Object)null) { ((Behaviour)HUDManager.Instance.itemSlotIcons[slot]).enabled = false; HUDManager.Instance.itemSlotIcons[slot].sprite = null; } else { HUDManager.Instance.itemSlotIcons[slot].sprite = val.itemProperties.itemIcon; ((Behaviour)HUDManager.Instance.itemSlotIcons[slot]).enabled = true; } } } private static void MoveItemToSlot(PlayerControllerB player, GrabbableObject item, int fromSlot, int toSlot) { if (fromSlot != toSlot) { player.ItemSlots[toSlot] = item; player.ItemSlots[fromSlot] = null; if ((Object)(object)player.currentlyHeldObjectServer == (Object)(object)item) { player.currentItemSlot = toSlot; } UpdateSlotIcon(player, fromSlot); UpdateSlotIcon(player, toSlot); } } private static void SwapItemsBetweenSlots(PlayerControllerB player, int firstSlot, int secondSlot) { if (firstSlot != secondSlot) { GrabbableObject val = player.ItemSlots[firstSlot]; GrabbableObject val2 = player.ItemSlots[secondSlot]; player.ItemSlots[firstSlot] = val2; player.ItemSlots[secondSlot] = val; if ((Object)(object)player.currentlyHeldObjectServer == (Object)(object)val) { player.currentItemSlot = secondSlot; } else if ((Object)(object)player.currentlyHeldObjectServer == (Object)(object)val2) { player.currentItemSlot = firstSlot; } UpdateSlotIcon(player, firstSlot); UpdateSlotIcon(player, secondSlot); } } internal static void ForceDropItem(PlayerControllerB player, GrabbableObject item, int slot) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)player).IsOwner && (Object)(object)player.currentlyHeldObjectServer == (Object)(object)item) { player.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true); } else { item.parentObject = null; item.heldByPlayerOnServer = false; if (player.isInElevator) { ((Component)item).transform.SetParent(player.playersManager.elevatorTransform, true); } else { ((Component)item).transform.SetParent(player.playersManager.propsContainer, true); } player.SetItemInElevator(player.isInHangarShipRoom, player.isInElevator, item); item.EnablePhysics(true); item.EnableItemMeshes(true); ((Component)item).transform.localScale = item.originalScale; item.isHeld = false; item.isPocketed = false; item.startFallingPosition = ((Component)item).transform.parent.InverseTransformPoint(((Component)item).transform.position); item.FallToGround(true, false, default(Vector3)); item.fallTime = Random.Range(-0.3f, 0.05f); if (((NetworkBehaviour)player).IsOwner) { item.DiscardItemOnClient(); } else if (!item.itemProperties.syncDiscardFunction) { item.playerHeldBy = null; } } player.ItemSlots[slot] = null; UpdateSlotIcon(player, slot); } private static void ShrinkSlotsIfNeeded(PlayerControllerB player) { if (player.ItemSlots == null || player.ItemSlots.Length <= 4 || HasBagBeltInSlots(player)) { return; } if (UtilitySlotIndex < player.ItemSlots.Length && (Object)(object)player.ItemSlots[UtilitySlotIndex] != (Object)null) { GrabbableObject item = player.ItemSlots[UtilitySlotIndex]; int num = FindFirstEmptyBaseSlot(player); if (num != -1) { MoveItemToSlot(player, item, UtilitySlotIndex, num); } else { ForceDropItem(player, item, UtilitySlotIndex); } } if (BagSlotIndex < player.ItemSlots.Length && (Object)(object)player.ItemSlots[BagSlotIndex] != (Object)null) { GrabbableObject item2 = player.ItemSlots[BagSlotIndex]; int num2 = FindFirstEmptyBaseSlot(player); if (num2 != -1) { MoveItemToSlot(player, item2, BagSlotIndex, num2); } else { ForceDropItem(player, item2, BagSlotIndex); } } GrabbableObject[] array = (GrabbableObject[])(object)new GrabbableObject[4]; Array.Copy(player.ItemSlots, array, 4); player.ItemSlots = array; if (player.currentItemSlot >= 4) { player.currentItemSlot = 0; } } private static void SyncHudExtraSlotVisibility(PlayerControllerB player, bool showExtra) { if (!((NetworkBehaviour)player).IsOwner) { return; } HUDManager instance = HUDManager.Instance; if ((Object)(object)instance == (Object)null || instance.itemSlotIcons == null || instance.itemSlotIconFrames == null) { return; } int val = 4 + ExtraSlots; GrabbableObject[] itemSlots = player.ItemSlots; EnsureHudSlots(Math.Max(val, (itemSlots != null) ? itemSlots.Length : 4)); GrabbableObject[] itemSlots2 = player.ItemSlots; int num = ((itemSlots2 != null) ? itemSlots2.Length : 4); int val2 = instance.itemSlotIcons.Length; int val3 = instance.itemSlotIconFrames.Length; int num2 = Math.Min(val2, val3); for (int i = 0; i < num2; i++) { bool flag = i >= 4; bool flag2 = !flag || showExtra; bool flag3 = i < num; Image val4 = instance.itemSlotIconFrames[i]; if ((Object)(object)val4 != (Object)null) { ((Behaviour)val4).enabled = flag2; } Image val5 = instance.itemSlotIcons[i]; if ((Object)(object)val5 == (Object)null) { continue; } if (!flag2 || !flag3) { ((Behaviour)val5).enabled = false; if (flag) { val5.sprite = null; } continue; } GrabbableObject val6 = player.ItemSlots[i]; if ((Object)(object)val6 == (Object)null || (Object)(object)val6.itemProperties == (Object)null || (Object)(object)val6.itemProperties.itemIcon == (Object)null) { ((Behaviour)val5).enabled = false; if (flag) { val5.sprite = null; } } else { val5.sprite = val6.itemProperties.itemIcon; ((Behaviour)val5).enabled = true; } } } private static void EnsureBagBeltInDedicatedSlot(PlayerControllerB player) { if (player.ItemSlots == null || BagSlotIndex >= player.ItemSlots.Length) { return; } int num = -1; for (int i = 0; i < player.ItemSlots.Length; i++) { if (IsBagBelt(player.ItemSlots[i])) { num = i; break; } } if (num == -1 || num == BagSlotIndex || (Object)(object)player.ItemSlots[num] == (Object)(object)player.currentlyHeldObjectServer) { return; } if ((Object)(object)player.ItemSlots[BagSlotIndex] == (Object)null) { MoveItemToSlot(player, player.ItemSlots[num], num, BagSlotIndex); return; } int num2 = FindFirstEmptyBaseSlot(player); if (num2 != -1) { MoveItemToSlot(player, player.ItemSlots[BagSlotIndex], BagSlotIndex, num2); MoveItemToSlot(player, player.ItemSlots[num], num, BagSlotIndex); } } private static void ReconcileUtilitySlot(PlayerControllerB player) { if (player.ItemSlots == null || UtilitySlotIndex >= player.ItemSlots.Length) { return; } GrabbableObject val = player.ItemSlots[UtilitySlotIndex]; if ((Object)(object)val != (Object)null && !IsUtilityAllowed(val)) { int num = FindFirstEmptyBaseSlot(player); if (num != -1) { MoveItemToSlot(player, val, UtilitySlotIndex, num); } else { ForceDropItem(player, val, UtilitySlotIndex); } val = null; } if ((Object)(object)val != (Object)null) { return; } for (int i = 0; i < player.ItemSlots.Length; i++) { if (i == UtilitySlotIndex || i == BagSlotIndex) { continue; } GrabbableObject val2 = player.ItemSlots[i]; if (!((Object)(object)val2 == (Object)null) && !IsBagBelt(val2) && IsUtilityAllowed(val2)) { MoveItemToSlot(player, val2, i, UtilitySlotIndex); if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)string.Format("Utility auto-slot: moved '{0}' to slot {1}.", val2.itemProperties?.itemName ?? "unknown", UtilitySlotIndex + 1)); } break; } } } [HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")] [HarmonyPostfix] private static void GrabObjectClientRpcPostfix(PlayerControllerB __instance, bool grabValidated, NetworkObjectReference grabbedObject) { NetworkObject val = default(NetworkObject); if (!grabValidated || !IsEnabled || !((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null)) { return; } GrabbableObject componentInChildren = ((Component)val).gameObject.GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren == (Object)null) { return; } if (IsBagBelt(componentInChildren)) { SetBagBeltEquipped(__instance, equipped: true); EnsureExpandedSlots(__instance); int num = FindSlot(__instance, componentInChildren); if (num >= 0 && BagSlotIndex < __instance.ItemSlots.Length && num != BagSlotIndex) { if ((Object)(object)__instance.ItemSlots[BagSlotIndex] == (Object)null) { MoveItemToSlot(__instance, componentInChildren, num, BagSlotIndex); } else { SwapItemsBetweenSlots(__instance, num, BagSlotIndex); } } ReconcileUtilitySlot(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: true); } else { if (!HasBagBeltEquipped(__instance) && !HasBagBeltInSlots(__instance)) { return; } SetBagBeltEquipped(__instance, equipped: true); EnsureExpandedSlots(__instance); int num2 = FindSlot(__instance, componentInChildren); if (num2 == UtilitySlotIndex && !IsUtilityAllowed(componentInChildren)) { int num3 = FindFirstEmptyBaseSlot(__instance); if (num3 != -1) { MoveItemToSlot(__instance, componentInChildren, num2, num3); } else { if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)("Utility slot reject: " + componentInChildren.itemProperties.itemName)); } ForceDropItem(__instance, componentInChildren, num2); } } else if (num2 >= 0 && num2 != UtilitySlotIndex && IsUtilityAllowed(componentInChildren) && UtilitySlotIndex < __instance.ItemSlots.Length) { if ((Object)(object)__instance.ItemSlots[UtilitySlotIndex] == (Object)null) { MoveItemToSlot(__instance, componentInChildren, num2, UtilitySlotIndex); } else { SwapItemsBetweenSlots(__instance, num2, UtilitySlotIndex); } } EnsureBagBeltInDedicatedSlot(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: true); } } [HarmonyPatch(typeof(BeltBagItem), "EquipItem")] [HarmonyPostfix] private static void BeltBagEquipPostfix(BeltBagItem __instance) { if (IsEnabled && !((Object)(object)((GrabbableObject)__instance).playerHeldBy == (Object)null) && IsBagBelt((GrabbableObject?)(object)__instance)) { SetBagBeltEquipped(((GrabbableObject)__instance).playerHeldBy, equipped: true); EnsureExpandedSlots(((GrabbableObject)__instance).playerHeldBy); SyncHudExtraSlotVisibility(((GrabbableObject)__instance).playerHeldBy, showExtra: true); if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)"Bag Belt equipped."); } } } [HarmonyPatch(typeof(BeltBagItem), "DiscardItem")] [HarmonyPrefix] private static void BeltBagDiscardPrefix(BeltBagItem __instance, out PlayerControllerB? __state) { __state = ((GrabbableObject)__instance).playerHeldBy; } [HarmonyPatch(typeof(BeltBagItem), "DiscardItem")] [HarmonyPostfix] private static void BeltBagDiscardPostfix(BeltBagItem __instance, PlayerControllerB? __state) { if (!IsEnabled || (Object)(object)__state == (Object)null || !IsBagBelt((GrabbableObject?)(object)__instance)) { return; } SetBagBeltEquipped(__state, equipped: false); if (UtilitySlotIndex < __state.ItemSlots.Length && (Object)(object)__state.ItemSlots[UtilitySlotIndex] != (Object)null) { GrabbableObject item = __state.ItemSlots[UtilitySlotIndex]; int num = FindFirstEmptyBaseSlot(__state); if (num != -1) { MoveItemToSlot(__state, item, UtilitySlotIndex, num); } else { ForceDropItem(__state, item, UtilitySlotIndex); } } if (__state.ItemSlots.Length > 4) { GrabbableObject[] array = (GrabbableObject[])(object)new GrabbableObject[4]; Array.Copy(__state.ItemSlots, array, 4); __state.ItemSlots = array; if (__state.currentItemSlot >= 4) { __state.currentItemSlot = 0; } } if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)"Bag Belt unequipped."); } SyncHudExtraSlotVisibility(__state, showExtra: false); } [HarmonyPatch(typeof(BeltBagItem), "ItemActivate")] [HarmonyPrefix] private static bool BeltBagActivatePrefix(BeltBagItem __instance) { if (IsEnabled && IsBagBelt((GrabbableObject?)(object)__instance)) { return false; } return true; } [HarmonyPatch(typeof(BeltBagItem), "ItemInteractLeftRight")] [HarmonyPrefix] private static bool BeltBagInteractPrefix(BeltBagItem __instance) { if (IsEnabled && IsBagBelt((GrabbableObject?)(object)__instance)) { return false; } return true; } [HarmonyPatch(typeof(BeltBagItem), "TryCheckBagContents")] [HarmonyPrefix] private static bool BeltBagTryCheckPrefix(BeltBagItem __instance) { if (IsEnabled && IsBagBelt((GrabbableObject?)(object)__instance)) { return false; } return true; } [HarmonyPatch(typeof(BeltBagItem), "TryCheckingBagServerRpc")] [HarmonyPrefix] private static bool BeltBagTryCheckServerPrefix(BeltBagItem __instance) { if (IsEnabled && IsBagBelt((GrabbableObject?)(object)__instance)) { return false; } return true; } [HarmonyPatch(typeof(BeltBagItem), "CheckBagLocalClient")] [HarmonyPrefix] private static bool BeltBagCheckLocalPrefix(BeltBagItem __instance) { if (IsEnabled && IsBagBelt((GrabbableObject?)(object)__instance)) { return false; } return true; } [HarmonyPatch(typeof(GrabbableObject), "DestroyObjectInHand")] [HarmonyPrefix] private static bool DestroyObjectInHandPrefix(GrabbableObject __instance) { return true; } [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] private static void PlayerUpdatePostfix(PlayerControllerB __instance) { if (IsEnabled && __instance.ItemSlots != null) { bool flag = HasBagBeltInSlots(__instance); bool flag2 = HasBagBeltEquipped(__instance); if (flag && !flag2) { SetBagBeltEquipped(__instance, equipped: true); EnsureExpandedSlots(__instance); EnsureBagBeltInDedicatedSlot(__instance); ReconcileUtilitySlot(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: true); } else if (!flag && flag2) { SetBagBeltEquipped(__instance, equipped: false); ShrinkSlotsIfNeeded(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: false); } else if (!flag) { ShrinkSlotsIfNeeded(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: false); } else { EnsureBagBeltInDedicatedSlot(__instance); ReconcileUtilitySlot(__instance); SyncHudExtraSlotVisibility(__instance, showExtra: true); } } } } [HarmonyPatch(typeof(GameNetworkManager))] internal static class BagBeltNetworkSyncLifecyclePatch { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix() { BagBeltNetworkSync.Initialize(); } [HarmonyPostfix] [HarmonyPatch("StartHost")] private static void StartHostPostfix() { BagBeltNetworkSync.Initialize(); } [HarmonyPostfix] [HarmonyPatch("StartClient")] private static void StartClientPostfix() { BagBeltNetworkSync.Initialize(); } [HarmonyPrefix] [HarmonyPatch("Disconnect")] private static void DisconnectPrefix() { BagBeltNetworkSync.ResetForDisconnect(); } } [HarmonyPatch(typeof(HUDManager))] internal static class BagBeltNetworkSyncHeartbeatPatch { [HarmonyPostfix] [HarmonyPatch("Update")] private static void HudUpdatePostfix() { BagBeltNetworkSync.Tick(); } } [HarmonyPatch(typeof(GrabbableObject))] internal static class BagBeltKnownObjectPatch { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(GrabbableObject __instance) { BagBeltNetworkSync.TryApplyKnownBagIdentity(__instance); } } internal static class BagBeltNetworkSync { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnBuyIndexMessageReceived; public static HandleNamedMessageDelegate <1>__OnBagObjectSpawnMessageReceived; public static Action<ulong> <2>__OnClientConnected; } private const string BuyIndexMessage = "BroMode.BagBelt/BuyIndex"; private const string BagObjectSpawnMessage = "BroMode.BagBelt/ObjectSpawn"; private static readonly HashSet<ulong> KnownBagObjectIds = new HashSet<ulong>(); private static bool _initialized; private static int _hostBuyIndex = -1; private static float _nextHeartbeatTime; private static float _nextInitWarningTime; internal static int EffectiveBuyIndex(int localBuyIndex) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && !singleton.IsServer && _hostBuyIndex >= 0) { return _hostBuyIndex; } return localBuyIndex; } internal static bool IsKnownBagObject(GrabbableObject? item) { if ((Object)(object)item == (Object)null) { return false; } NetworkObject component = ((Component)item).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { return KnownBagObjectIds.Contains(component.NetworkObjectId); } return false; } internal static void PublishHostBuyIndex(int buyIndex) { _hostBuyIndex = buyIndex; NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && _initialized) { SendBuyIndexToAll(buyIndex); } } internal static void MarkBagObjectOnHostAndBroadcast(GrabbableObject item) { NetworkObject component = ((Component)item).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null)) { ulong networkObjectId = component.NetworkObjectId; KnownBagObjectIds.Add(networkObjectId); TryApplyKnownBagIdentity(item); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsServer && _initialized) { SendObjectSpawnToAll(networkObjectId); } } } internal static void TryApplyKnownBagIdentity(GrabbableObject item) { NetworkObject component = ((Component)item).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null) && KnownBagObjectIds.Contains(component.NetworkObjectId) && !((Object)(object)BagBeltRegistry.BagBeltItem == (Object)null) && (Object)(object)item.itemProperties != (Object)(object)BagBeltRegistry.BagBeltItem) { item.itemProperties = BagBeltRegistry.BagBeltItem; if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)$"Bag Belt sync: applied identity to netId={component.NetworkObjectId}"); } } } internal static void OnRegistryReady() { if (_hostBuyIndex >= 0) { BagBeltRegistry.ApplySynchronizedBuyIndex(_hostBuyIndex); } TryApplyKnownBagIdentitiesToSpawnedObjects(); VerifyBuyIndexAgainstTerminal("registry-ready"); } internal static void Initialize() { //IL_003b: 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_0046: Expected O, but got Unknown //IL_0066: 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_0071: Expected O, but got Unknown if (_initialized) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnBuyIndexMessageReceived; if (obj == null) { HandleNamedMessageDelegate val = OnBuyIndexMessageReceived; <>O.<0>__OnBuyIndexMessageReceived = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("BroMode.BagBelt/BuyIndex", (HandleNamedMessageDelegate)obj); CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager; object obj2 = <>O.<1>__OnBagObjectSpawnMessageReceived; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnBagObjectSpawnMessageReceived; <>O.<1>__OnBagObjectSpawnMessageReceived = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("BroMode.BagBelt/ObjectSpawn", (HandleNamedMessageDelegate)obj2); singleton.OnClientConnectedCallback += OnClientConnected; _initialized = true; if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)"Bag Belt sync initialized."); } } } internal static void Tick() { if (!_initialized) { Initialize(); if (!_initialized && Time.realtimeSinceStartup >= _nextInitWarningTime) { _nextInitWarningTime = Time.realtimeSinceStartup + 5f; if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogWarning((object)"Bag Belt sync not initialized yet; will retry."); } } } else if (!(Time.realtimeSinceStartup < _nextHeartbeatTime)) { _nextHeartbeatTime = Time.realtimeSinceStartup + 2f; if (KnownBagObjectIds.Count > 0) { TryApplyKnownBagIdentitiesToSpawnedObjects(); } if (_hostBuyIndex >= 0) { VerifyBuyIndexAgainstTerminal("heartbeat"); } } } internal static void ResetForDisconnect() { NetworkManager singleton = NetworkManager.Singleton; if (_initialized && (Object)(object)singleton != (Object)null) { if (singleton.CustomMessagingManager != null) { singleton.CustomMessagingManager.UnregisterNamedMessageHandler("BroMode.BagBelt/BuyIndex"); singleton.CustomMessagingManager.UnregisterNamedMessageHandler("BroMode.BagBelt/ObjectSpawn"); } singleton.OnClientConnectedCallback -= OnClientConnected; } _initialized = false; _hostBuyIndex = -1; KnownBagObjectIds.Clear(); _nextHeartbeatTime = 0f; _nextInitWarningTime = 0f; } private static void OnClientConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || clientId == singleton.LocalClientId) { return; } if (_hostBuyIndex >= 0) { SendBuyIndexToClient(clientId, _hostBuyIndex); } foreach (ulong knownBagObjectId in KnownBagObjectIds) { SendObjectSpawnToClient(clientId, knownBagObjectId); } if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogInfo((object)$"Bag Belt sync: sent snapshot to client {clientId}. buyIndex={_hostBuyIndex}, knownObjects={KnownBagObjectIds.Count}"); } } private static void OnBuyIndexMessageReceived(ulong senderClientId, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); _hostBuyIndex = num; BagBeltRegistry.ApplySynchronizedBuyIndex(num); VerifyBuyIndexAgainstTerminal("buy-index-message"); if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)$"Bag Belt sync: received host buy index {num}"); } } private static void OnBagObjectSpawnMessageReceived(ulong senderClientId, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); KnownBagObjectIds.Add(num); TryApplyKnownBagIdentityToObjectId(num); if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)$"Bag Belt sync: received bag object id {num}"); } } private static void SendBuyIndexToAll(int buyIndex) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref buyIndex, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("BroMode.BagBelt/BuyIndex", val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void SendBuyIndexToClient(ulong clientId, int buyIndex) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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) NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref buyIndex, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("BroMode.BagBelt/BuyIndex", clientId, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void SendObjectSpawnToAll(ulong networkObjectId) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("BroMode.BagBelt/ObjectSpawn", val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void SendObjectSpawnToClient(ulong clientId, ulong networkObjectId) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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) NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("BroMode.BagBelt/ObjectSpawn", clientId, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void TryApplyKnownBagIdentityToObjectId(ulong networkObjectId) { NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.SpawnManager : null) != null && singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) && !((Object)(object)value == (Object)null)) { GrabbableObject componentInChildren = ((Component)value).GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren != (Object)null) { TryApplyKnownBagIdentity(componentInChildren); } } } private static void TryApplyKnownBagIdentitiesToSpawnedObjects() { NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.SpawnManager : null) == null) { return; } foreach (KeyValuePair<ulong, NetworkObject> spawnedObject in singleton.SpawnManager.SpawnedObjects) { if (KnownBagObjectIds.Contains(spawnedObject.Key) && !((Object)(object)spawnedObject.Value == (Object)null)) { GrabbableObject componentInChildren = ((Component)spawnedObject.Value).GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren != (Object)null) { TryApplyKnownBagIdentity(componentInChildren); } } } } private static void VerifyBuyIndexAgainstTerminal(string context) { if (_hostBuyIndex < 0) { return; } Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null || val.buyableItemsList == null || val.buyableItemsList.Length == 0) { return; } if (_hostBuyIndex >= val.buyableItemsList.Length) { BagBeltMod.Logger.LogWarning((object)$"Bag Belt sync: host buy index {_hostBuyIndex} out of range ({val.buyableItemsList.Length}) at {context}."); return; } Item val2 = val.buyableItemsList[_hostBuyIndex]; if ((Object)(object)val2 == (Object)null) { BagBeltMod.Logger.LogWarning((object)$"Bag Belt sync: null item at host buy index {_hostBuyIndex} at {context}."); } else if (val2.itemId != 195801089 && !string.Equals(val2.itemName, "Bag Belt", StringComparison.OrdinalIgnoreCase)) { BagBeltMod.Logger.LogWarning((object)$"Bag Belt sync: host buy index {_hostBuyIndex} maps to '{val2.itemName}' (id={val2.itemId}) at {context}. This can cause wrong purchase routing."); } else if (BagBeltMod.LogDebug.Value) { BagBeltMod.Logger.LogDebug((object)$"Bag Belt sync: verified host buy index {_hostBuyIndex} -> {val2.itemName} at {context}."); } } } [HarmonyPatch(typeof(Terminal))] internal static class BagBeltRegistryPatch { [HarmonyPrefix] [HarmonyPatch("Start")] private static void TerminalStartPrefix(Terminal __instance) { if (BagBeltMod.Enabled.Value) { BagBeltRegistry.EnsureRegistered(__instance); } } [HarmonyPrefix] [HarmonyPatch("LoadNewNode")] private static void LoadNewNodePrefix(Terminal __instance, TerminalNode node) { if (!BagBeltMod.Enabled.Value || (Object)(object)node == (Object)null) { return; } BagBeltRegistry.UpdateDynamicNodeText(__instance, node); if (node.displayText != null && node.displayText.Contains("[buyableItemsList]")) { BagBeltRegistry.EnsureRegistered(__instance); if (BagBeltMod.LogDebug.Value) { BagBeltRegistry.LogBuyableItems(__instance); } } } } internal static class BagBeltRegistry { internal const int BagBeltItemId = 195801089; internal const string BagBeltName = "Bag Belt"; internal static Item? VanillaBeltBagItem; internal static Item? BagBeltItem; internal static TerminalNode? BagBeltConfirmNode; internal static TerminalNode? BagBeltOrderedNode; internal static int BagBeltBuyIndex = -1; internal static bool Registered; internal static bool IsBagBelt(GrabbableObject? item) { if ((Object)(object)item == (Object)null || (Object)(object)item.itemProperties == (Object)null) { return false; } if ((Object)(object)BagBeltItem != (Object)null && (Object)(object)item.itemProperties == (Object)(object)BagBeltItem) { return true; } if (item.itemProperties.itemId == 195801089) { return true; } return BagBeltNetworkSync.IsKnownBagObject(item); } internal static bool IsVanillaBeltBag(GrabbableObject? item) { if ((Object)(object)item != (Object)null && (Object)(object)VanillaBeltBagItem != (Object)null) { return (Object)(object)item.itemProperties == (Object)(object)VanillaBeltBagItem; } return false; } internal static void EnsureRegistered(Terminal terminal) { if (Registered) { EnsureSalesArray(terminal); return; } if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.allItemsList == (Object)null) { BagBeltMod.Logger.LogWarning((object)"Bag Belt: StartOfRound or allItemsList not ready."); return; } VanillaBeltBagItem = FindVanillaBeltBagItem(StartOfRound.Instance.allItemsList.itemsList); if ((Object)(object)VanillaBeltBagItem == (Object)null || (Object)(object)VanillaBeltBagItem.spawnPrefab == (Object)null) { BagBeltMod.Logger.LogError((object)"Bag Belt: failed to locate vanilla belt bag item/prefab."); return; } VanillaBeltBagItem.weight = 1f; BagBeltItem = Object.Instantiate<Item>(VanillaBeltBagItem); BagBeltItem.itemName = "Bag Belt"; BagBeltItem.creditsWorth = BagBeltMod.Price.Value; BagBeltItem.weight = 1f; BagBeltItem.itemId = 195801089; AddToBuyableItems(terminal, BagBeltItem); EnsureTerminalKeyword(terminal, BagBeltItem); EnsureSalesArray(terminal); Registered = true; BagBeltNetworkSync.OnRegistryReady(); BagBeltMod.Logger.LogInfo((object)"Bag Belt registered in terminal store."); } internal static void UpdateDynamicNodeText(Terminal terminal, TerminalNode node) { if (!((Object)(object)BagBeltItem == (Object)null) && !((Object)(object)node != (Object)(object)BagBeltConfirmNode)) { int num = Math.Max(0, BagBeltItem.creditsWorth); int num2 = Math.Max(0, terminal.groupCredits - num); node.displayText = "You have requested to order " + BagBeltItem.itemName + ".\n" + $"Your new balance would be ${num2}.\n\n" + "Please CONFIRM or DENY."; } } internal static void ApplySynchronizedBuyIndex(int synchronizedBuyIndex) { if (synchronizedBuyIndex >= 0) { BagBeltBuyIndex = synchronizedBuyIndex; if ((Object)(object)BagBeltConfirmNode != (Object)null) { BagBeltConfirmNode.buyItemIndex = synchronizedBuyIndex; } if ((Object)(object)BagBeltOrderedNode != (Object)null) { BagBeltOrderedNode.buyItemIndex = synchronizedBuyIndex; } } } internal static void LogBuyableItems(Terminal terminal) { Item[] array = terminal.buyableItemsList ?? Array.Empty<Item>(); BagBeltMod.Logger.LogDebug((object)$"Buyable items count: {array.Length}"); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null) { BagBeltMod.Logger.LogDebug((object)$"[{i}] {array[i].itemName} (${array[i].creditsWorth})"); } } } private static Item? FindVanillaBeltBagItem(List<Item> items) { for (int i = 0; i < items.Count; i++) { Item val = items[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.spawnPrefab == (Object)null)) { if (val.itemName != null && val.itemName.IndexOf("belt bag", StringComparison.OrdinalIgnoreCase) >= 0) { return val; } if ((Object)(object)val.spawnPrefab.GetComponentInChildren<BeltBagItem>() != (Object)null) { return val; } } } return null; } private static void AddToBuyableItems(Terminal terminal, Item newItem) { Item[] array = terminal.buyableItemsList ?? Array.Empty<Item>(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] == (Object)(object)newItem || ((Object)(object)array[i] != (Object)null && array[i].itemName == newItem.itemName)) { return; } } Item[] array2 = (Item[])(object)new Item[array.Length + 1]; Array.Copy(array, array2, array.Length); array2[^1] = newItem; terminal.buyableItemsList = array2; } private static void EnsureSalesArray(Terminal terminal) { Item[] buyableItemsList = terminal.buyableItemsList; int num = ((buyableItemsList != null) ? buyableItemsList.Length : 0); BuyableVehicle[] buyableVehicles = terminal.buyableVehicles; int num2 = num + ((buyableVehicles != null) ? buyableVehicles.Length : 0); if (num2 > 0 && (terminal.itemSalesPercentages == null || terminal.itemSalesPercentages.Length != num2)) { int[] array = new int[num2]; for (int i = 0; i < array.Length; i++) { array[i] = 100; } terminal.itemSalesPercentages = array; } } private static void EnsureTerminalKeyword(Terminal terminal, Item newItem) { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown TerminalNodesList terminalNodes = terminal.terminalNodes; if ((Object)(object)terminalNodes == (Object)null || terminalNodes.allKeywords == null) { BagBeltMod.Logger.LogWarning((object)"Bag Belt: terminalNodes not available."); return; } TerminalKeyword val = FindKeyword(terminalNodes, "buy", mustBeVerb: true); if ((Object)(object)val == (Object)null) { BagBeltMod.Logger.LogWarning((object)"Bag Belt: could not find 'buy' terminal keyword."); return; } int num = FindBuyIndex(terminal, newItem); if (num < 0) { BagBeltMod.Logger.LogWarning((object)"Bag Belt: could not find buy index."); return; } BagBeltBuyIndex = num; if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { BagBeltNetworkSync.PublishHostBuyIndex(num); } int num2 = BagBeltNetworkSync.EffectiveBuyIndex(num); TerminalKeyword val2 = FindKeyword(terminalNodes, "confirm", mustBeVerb: false); TerminalKeyword val3 = FindKeyword(terminalNodes, "deny", mustBeVerb: false); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { BagBeltMod.Logger.LogWarning((object)"Bag Belt: missing confirm/deny keywords."); return; } TerminalNode val4 = (BagBeltOrderedNode = CreateTerminalNode("Ordered " + newItem.itemName + ".", num2, newItem.creditsWorth, isConfirmation: false)); TerminalNode result = CreateTerminalNode("Cancelled order.", -1, 0, isConfirmation: false); TerminalNode val5 = CreateTerminalNode("You have requested to order " + newItem.itemName + ".\nYour new balance would be [playerCredits].\n\nPlease CONFIRM or DENY.", num2, newItem.creditsWorth, isConfirmation: true); val5.overrideOptions = true; val5.terminalOptions = (CompatibleNoun[])(object)new CompatibleNoun[2] { new CompatibleNoun { noun = val2, result = val4 }, new CompatibleNoun { noun = val3, result = result } }; BagBeltConfirmNode = val5; CopyVanillaBuyAudioCue(val, val4); BagBeltMod.Logger.LogInfo((object)$"Bag Belt terminal node set. buyItemIndex={num2}, price={newItem.creditsWorth}"); AddOrOverrideKeyword(terminalNodes, val, "bagbelt", val5); AddOrOverrideKeyword(terminalNodes, val, "beltbag", val5); AddOrOverrideKeyword(terminalNodes, val, "bag", val5); } private static TerminalKeyword? FindKeyword(TerminalNodesList nodes, string word, bool mustBeVerb) { TerminalKeyword[] array = nodes.allKeywords ?? Array.Empty<TerminalKeyword>(); for (int i = 0; i < array.Length; i++) { if (!((Object)(object)array[i] == (Object)null) && string.Equals(array[i].word, word, StringComparison.OrdinalIgnoreCase) && (!mustBeVerb || array[i].isVerb)) { return array[i]; } } return null; } private static TerminalNode CreateTerminalNode(string text, int buyIndex, int itemCost, bool isConfirmation) { //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_000c: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown return new TerminalNode { displayText = text, buyItemIndex = buyIndex, itemCost = itemCost, isConfirmationNode = isConfirmation, buyVehicleIndex = -1, buyRerouteToMoon = -1, shipUnlockableID = -1, buyUnlockable = false, returnFromStorage = false, acceptAnything = false, overrideOptions = false, terminalOptions = Array.Empty<CompatibleNoun>() }; } private static void CopyVanillaBuyAudioCue(TerminalKeyword buyKeyword, TerminalNode orderedNode) { if (buyKeyword.compatibleNouns == null) { return; } for (int i = 0; i < buyKeyword.compatibleNouns.Length; i++) { TerminalNode val = buyKeyword.compatibleNouns[i]?.result; if (!((Object)(object)val == (Object)null)) { TerminalNode val2 = FindBuySuccessNode(val, new HashSet<TerminalNode>()); if (!((Object)(object)val2 == (Object)null)) { orderedNode.playSyncedClip = val2.playSyncedClip; orderedNode.playClip = val2.playClip; break; } } } } private static TerminalNode? FindBuySuccessNode(TerminalNode node, HashSet<TerminalNode> visited) { if (!visited.Add(node)) { return null; } if (node.buyItemIndex >= 0 && !node.isConfirmationNode && !string.IsNullOrEmpty(node.displayText) && node.displayText.IndexOf("ordered", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)(object)node.playClip != (Object)null || node.playSyncedClip >= 0)) { return node; } CompatibleNoun[] array = node.terminalOptions ?? Array.Empty<CompatibleNoun>(); for (int i = 0; i < array.Length; i++) { if (!((Object)(object)array[i]?.result == (Object)null)) { TerminalNode val = FindBuySuccessNode(array[i].result, visited); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static int FindBuyIndex(Terminal terminal, Item newItem) { Item[] array = terminal.buyableItemsList ?? Array.Empty<Item>(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] == (Object)(object)newItem) { return i; } } return -1; } private static void AddOrOverrideKeyword(TerminalNodesList nodes, TerminalKeyword buyKeyword, string word, TerminalNode resultNode) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown for (int i = 0; i < nodes.allKeywords.Length; i++) { if (!((Object)(object)nodes.allKeywords[i] != (Object)null) || !string.Equals(nodes.allKeywords[i].word, word, StringComparison.OrdinalIgnoreCase)) { continue; } TerminalKeyword val = nodes.allKeywords[i]; CompatibleNoun val2 = new CompatibleNoun { noun = val, result = resultNode }; CompatibleNoun[] array = buyKeyword.compatibleNouns ?? Array.Empty<CompatibleNoun>(); for (int j = 0; j < array.Length; j++) { if (array[j] != null && (Object)(object)array[j].noun == (Object)(object)val) { array[j].result = resultNode; return; } } CompatibleNoun[] array2 = (CompatibleNoun[])(object)new CompatibleNoun[array.Length + 1]; Array.Copy(array, array2, array.Length); array2[^1] = val2; buyKeyword.compatibleNouns = array2; return; } TerminalKeyword val3 = ScriptableObject.CreateInstance<TerminalKeyword>(); val3.word = word; val3.isVerb = false; val3.compatibleNouns = Array.Empty<CompatibleNoun>(); val3.defaultVerb = buyKeyword; val3.accessTerminalObjects = false; CompatibleNoun val4 = new CompatibleNoun { noun = val3, result = resultNode }; CompatibleNoun[] array3 = buyKeyword.compatibleNouns ?? Array.Empty<CompatibleNoun>(); CompatibleNoun[] array4 = (CompatibleNoun[])(object)new CompatibleNoun[array3.Length + 1]; Array.Copy(array3, array4, array3.Length); array4[^1] = val4; buyKeyword.compatibleNouns = array4; TerminalKeyword[] array5 = nodes.allKeywords ?? Array.Empty<TerminalKeyword>(); TerminalKeyword[] array6 = (TerminalKeyword[])(object)new TerminalKeyword[array5.Length + 1]; Array.Copy(array5, array6, array5.Length); array6[^1] = val3; nodes.allKeywords = array6; } } }