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 ItemShelvesPlatforms v1.0.2
ItemRackMod.dll
Decompiled 12 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [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 ItemRackMod { internal static class DefaultItemNameResolver { private static readonly string[] PreferredMemberNames = new string[8] { "itemName", "displayName", "itemDisplayName", "display_name", "name", "Name", "item_name", "display_name_text" }; private static readonly Dictionary<Type, MemberInfo[]> CachedMembers = new Dictionary<Type, MemberInfo[]>(); private static readonly Dictionary<string, string> CanonicalItemAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "explosive mine", "ItemMineExplosive" }, { "sledge hammer", "ItemMelee" }, { "hammer", "ItemMelee" }, { "inflatable hammer", "ItemMeleeInflatableHammer" }, { "shockwave mine", "ItemMineStun" }, { "duct taped granade", "ItemGrenadeDuctTaped" }, { "trapzap", "ItemMineTrigger" }, { "human granade", "ItemGrenadeHuman" }, { "granade", "ItemGrenadeExplosive" }, { "stun granade", "ItemGrenadeStun" }, { "shockwave granade", "ItemGrenadeShockwave" }, { "recharge drone", "ItemDroneBattery" }, { "feather drone", "ItemDroneFeather" }, { "roll drone", "ItemDroneTorque" }, { "zero gravity drone", "ItemDroneZeroGravity" }, { "indestructible drone", "ItemDroneIndestructible" }, { "zero gravity orb", "ItemOrbZeroGravity" } }; public static bool IsCanonicalPlatformName(string configuredName) { return !string.IsNullOrWhiteSpace(configuredName) && CanonicalItemAliases.ContainsKey(Normalize(configuredName)); } public static bool MatchesConfiguredName(string configuredName, PhysGrabObject pgo) { if ((Object)(object)pgo == (Object)null || string.IsNullOrWhiteSpace(configuredName)) { return false; } string text = Normalize(configuredName); if (string.IsNullOrEmpty(text)) { return false; } if (CanonicalItemAliases.TryGetValue(text, out var value)) { Component[] components = ((Component)pgo).GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && string.Equals(((object)val).GetType().Name, value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } foreach (Component relevantComponent in GetRelevantComponents(pgo)) { if (!((Object)(object)relevantComponent == (Object)null)) { if (string.Equals(Normalize(((object)relevantComponent).GetType().Name), text, StringComparison.OrdinalIgnoreCase)) { return true; } if (TryReadNamedString(relevantComponent, out var value2) && string.Equals(Normalize(value2), text, StringComparison.OrdinalIgnoreCase)) { return true; } } } return (Object)(object)((Component)pgo).gameObject != (Object)null && string.Equals(Normalize(((Object)((Component)pgo).gameObject).name), text, StringComparison.OrdinalIgnoreCase); } public static bool TryGetItemName(PhysGrabObject pgo, out string name) { name = null; if ((Object)(object)pgo == (Object)null) { return false; } foreach (Component relevantComponent in GetRelevantComponents(pgo)) { if (TryReadNamedString(relevantComponent, out name) && !string.IsNullOrWhiteSpace(name)) { return true; } } name = (((Object)(object)((Component)pgo).gameObject != (Object)null) ? ((Object)((Component)pgo).gameObject).name : null); return !string.IsNullOrWhiteSpace(name); } public static IEnumerable<string> GetItemAliases(PhysGrabObject pgo) { if ((Object)(object)pgo == (Object)null) { yield break; } HashSet<string> yielded = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (Component component in GetRelevantComponents(pgo)) { if (!((Object)(object)component == (Object)null)) { string typeName = ((object)component).GetType().Name; if (!string.IsNullOrWhiteSpace(typeName) && yielded.Add(typeName)) { yield return typeName; } if (TryReadNamedString(component, out var value) && !string.IsNullOrWhiteSpace(value) && yielded.Add(value)) { yield return value; } value = null; } } if ((Object)(object)((Component)pgo).gameObject != (Object)null && yielded.Add(((Object)((Component)pgo).gameObject).name)) { yield return ((Object)((Component)pgo).gameObject).name; } } public static string Normalize(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } string text = value.Trim(); int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } text = text.Replace("_", " ").Replace("-", " "); while (text.Contains(" ")) { text = text.Replace(" ", " "); } return text.Trim().ToLowerInvariant(); } private static IEnumerable<Component> GetRelevantComponents(PhysGrabObject pgo) { if ((Object)(object)pgo == (Object)null) { yield break; } Component[] componentsInChildren = ((Component)pgo).GetComponentsInChildren<Component>(true); foreach (Component component in componentsInChildren) { if (!((Object)(object)component == (Object)null)) { string typeName = ((object)component).GetType().Name; if (typeName.Equals("Item", StringComparison.OrdinalIgnoreCase) || typeName.Equals("ItemAttributes", StringComparison.OrdinalIgnoreCase) || typeName.Equals("ItemMelee", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemMelee", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemDrone", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemGrenade", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemMine", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ItemOrb", StringComparison.OrdinalIgnoreCase)) { yield return component; } } } Transform current = ((Component)pgo).transform.parent; while ((Object)(object)current != (Object)null) { Component[] components = ((Component)current).GetComponents<Component>(); foreach (Component component2 in components) { if (!((Object)(object)component2 == (Object)null)) { string typeName2 = ((object)component2).GetType().Name; if (typeName2.Equals("Item", StringComparison.OrdinalIgnoreCase) || typeName2.Equals("ItemAttributes", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemDrone", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemGrenade", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemMine", StringComparison.OrdinalIgnoreCase) || typeName2.StartsWith("ItemOrb", StringComparison.OrdinalIgnoreCase)) { yield return component2; } } } current = current.parent; } } private static bool TryReadNamedString(object target, out string value) { value = null; if (target == null) { return false; } Type type = target.GetType(); MemberInfo[] members = GetMembers(type); string[] preferredMemberNames = PreferredMemberNames; foreach (string value2 in preferredMemberNames) { MemberInfo[] array = members; foreach (MemberInfo memberInfo in array) { if (memberInfo.Name.Equals(value2, StringComparison.OrdinalIgnoreCase) && TryGetString(target, memberInfo, out value) && !string.IsNullOrWhiteSpace(value)) { return true; } } } MemberInfo[] array2 = members; foreach (MemberInfo memberInfo2 in array2) { if ((memberInfo2.Name.IndexOf("name", StringComparison.OrdinalIgnoreCase) >= 0 || memberInfo2.Name.IndexOf("display", StringComparison.OrdinalIgnoreCase) >= 0) && TryGetString(target, memberInfo2, out value) && !string.IsNullOrWhiteSpace(value)) { return true; } } return false; } private static MemberInfo[] GetMembers(Type type) { if (CachedMembers.TryGetValue(type, out var value)) { return value; } List<MemberInfo> list = new List<MemberInfo>(); list.AddRange(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); list.AddRange(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); value = list.ToArray(); CachedMembers[type] = value; return value; } private static bool TryGetString(object target, MemberInfo member, out string value) { value = null; try { if (member is FieldInfo fieldInfo && fieldInfo.FieldType == typeof(string)) { value = fieldInfo.GetValue(target) as string; return true; } if (member is PropertyInfo propertyInfo && propertyInfo.PropertyType == typeof(string) && propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0) { value = propertyInfo.GetValue(target, null) as string; return true; } } catch { } return false; } } public class DefaultItemPlacement : MonoBehaviour { private ItemRackController _controller; private readonly List<DefaultAssignment> _assignments = new List<DefaultAssignment>(); private readonly HashSet<int> _loggedMissingSlots = new HashSet<int>(); private readonly HashSet<PhysGrabObject> _placedDefaultObjects = new HashSet<PhysGrabObject>(); private readonly HashSet<int> _inventoryObjectIds = new HashSet<int>(); private float _scanTimer; private bool _loggedInitialScan; private bool _placementComplete; private float _startupUntil; private const float FastScanInterval = 0.25f; private const float FastScanSeconds = 5f; private const float StartupPlacementSeconds = 5f; public void Init(ItemRackController controller) { _controller = controller; _assignments.Clear(); _loggedMissingSlots.Clear(); _placedDefaultObjects.Clear(); _inventoryObjectIds.Clear(); _scanTimer = 0f; _loggedInitialScan = false; _placementComplete = false; _startupUntil = Time.time + 5f; foreach (DefaultAssignment item in ModConfig.ParseSlotAssignments()) { _assignments.Add(item); } Plugin.Log.LogInfo((object)$"Default item placement initialized with {_assignments.Count} preferred item slot(s)."); ScanNow(); } private void Update() { if (!_placementComplete && !(Time.time >= _startupUntil)) { _scanTimer -= Time.deltaTime; if (!(_scanTimer > 0f)) { _scanTimer = 0.25f; ScanNow(); } } } private void ScanNow() { if ((Object)(object)_controller == (Object)null || _assignments.Count == 0 || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } CaptureInventoryItems(); _placedDefaultObjects.RemoveWhere((PhysGrabObject pgo) => (Object)(object)pgo == (Object)null || pgo.dead); PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>(); if (array == null || array.Length == 0) { return; } List<PhysGrabObject> list = new List<PhysGrabObject>(); PhysGrabObject[] array2 = array; foreach (PhysGrabObject val in array2) { if (!((Object)(object)val == (Object)null) && !val.dead && !val.grabbed && !_placedDefaultObjects.Contains(val) && !_inventoryObjectIds.Contains(((Object)val).GetInstanceID()) && !((Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null)) { list.Add(val); } } ProcessPlatformGroup(list, "MinePlatform"); ProcessPlatformGroup(list, "DronePlatform"); ProcessPlatformGroup(list, "GrenadePlatform"); foreach (DefaultAssignment assignment in _assignments) { if (!IsPlatformAssignment(assignment.SlotKey)) { TryPlaceAssignment(assignment, list); } } if (AreAllAssignmentsSatisfied()) { _placementComplete = true; Plugin.Log.LogInfo((object)"Automatic rack/platform placement completed; scene-wide startup scans stopped."); } if (!_loggedInitialScan) { _loggedInitialScan = true; Plugin.Log.LogInfo((object)"Automatic rack/platform placement is active for the startup window. Platform priority: MinePlatform -> DronePlatform -> GrenadePlatform; exact item matching is enforced and fuzzy matching is disabled."); } } private void ProcessPlatformGroup(List<PhysGrabObject> candidates, string platformName) { foreach (DefaultAssignment assignment in _assignments) { if (IsAssignmentOnPlatform(assignment, platformName)) { TryPlaceAssignment(assignment, candidates); } } } private static bool IsAssignmentOnPlatform(DefaultAssignment assignment, string platformName) { if (assignment == null || string.IsNullOrEmpty(assignment.SlotKey)) { return false; } return assignment.SlotKey.StartsWith(platformName + ":", StringComparison.OrdinalIgnoreCase); } private void TryPlaceAssignment(DefaultAssignment assignment, List<PhysGrabObject> candidates) { if (!_controller.TryGetSlot(assignment.SlotKey, out var slot)) { int hashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(assignment.SlotKey); if (_loggedMissingSlots.Add(hashCode)) { Plugin.Log.LogWarning((object)("Default item assignment points to missing slot '" + assignment.SlotKey + "'. Check the platform dimensions and SlotAssignments.")); } return; } if ((Object)(object)slot.Occupant != (Object)null) { if (DefaultItemNameResolver.MatchesConfiguredName(assignment.ItemName, slot.Occupant)) { _placedDefaultObjects.Add(slot.Occupant); return; } slot.DetachForAutomaticMove(); } PhysGrabObject val = FindExactCandidate(assignment.ItemName, candidates); if (!((Object)(object)val == (Object)null)) { ItemRackSlot itemRackSlot = _controller.FindSlotContaining(val); if ((Object)(object)itemRackSlot != (Object)null && (Object)(object)itemRackSlot != (Object)(object)slot) { itemRackSlot.DetachForAutomaticMove(); } if (slot.TrySnapItem(val, allowReplace: false, applyConfiguredOrientation: true, bypassStartupFilter: true)) { _placedDefaultObjects.Add(val); Plugin.Log.LogInfo((object)("Default item '" + assignment.ItemName + "' placed in " + assignment.SlotKey + ".")); } } } private bool AreAllAssignmentsSatisfied() { if (_assignments.Count == 0) { return true; } foreach (DefaultAssignment assignment in _assignments) { if (assignment != null) { if (!_controller.TryGetSlot(assignment.SlotKey, out var slot)) { return false; } if ((Object)(object)slot.Occupant == (Object)null || !DefaultItemNameResolver.MatchesConfiguredName(assignment.ItemName, slot.Occupant)) { return false; } } } return true; } private void CaptureInventoryItems() { try { InventorySpot[] array = Resources.FindObjectsOfTypeAll<InventorySpot>(); if (array == null) { return; } InventorySpot[] array2 = array; foreach (InventorySpot val in array2) { if ((Object)(object)val == (Object)null) { continue; } PhysGrabObject[] componentsInChildren = ((Component)val).GetComponentsInChildren<PhysGrabObject>(true); PhysGrabObject[] array3 = componentsInChildren; foreach (PhysGrabObject val2 in array3) { if ((Object)(object)val2 != (Object)null) { _inventoryObjectIds.Add(((Object)val2).GetInstanceID()); } } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Could not inspect inventory items: " + ex.Message)); } } private static bool IsPlatformAssignment(string slotKey) { return slotKey.StartsWith("DronePlatform:", StringComparison.OrdinalIgnoreCase) || slotKey.StartsWith("GrenadePlatform:", StringComparison.OrdinalIgnoreCase) || slotKey.StartsWith("MinePlatform:", StringComparison.OrdinalIgnoreCase); } private PhysGrabObject FindExactCandidate(string configuredName, List<PhysGrabObject> candidates) { foreach (PhysGrabObject candidate in candidates) { if ((Object)(object)candidate == (Object)null || candidate.dead || candidate.grabbed || _placedDefaultObjects.Contains(candidate) || !DefaultItemNameResolver.MatchesConfiguredName(configuredName, candidate)) { continue; } return candidate; } return null; } public void ResetPlacementBookkeeping() { _placedDefaultObjects.Clear(); _inventoryObjectIds.Clear(); _loggedMissingSlots.Clear(); _scanTimer = 0f; _loggedInitialScan = false; _placementComplete = false; _startupUntil = Time.time + 5f; } } internal sealed class DefaultAssignment { public string SlotKey; public string ItemName; } public class ItemDisplayPlatform : MonoBehaviour { public int Rows { get; private set; } public int Columns { get; private set; } public void Setup(int rows, int columns) { Rows = Mathf.Max(1, rows); Columns = Mathf.Max(1, columns); } } internal static class ItemOrientation { private static readonly Dictionary<string, Vector3> _rotations = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase); private static bool _loaded; public static void Apply(PhysGrabObject pgo, Transform slot) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)pgo == (Object)null) && !((Object)(object)slot == (Object)null)) { EnsureLoaded(); if (DefaultItemNameResolver.TryGetItemName(pgo, out var name) && _rotations.TryGetValue(DefaultItemNameResolver.Normalize(name), out var value)) { ((Component)pgo).transform.localRotation = Quaternion.Euler(value); } } } private static void EnsureLoaded() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (_loaded) { return; } _loaded = true; foreach (KeyValuePair<string, Vector3> item in ModConfig.ParseItemOrientations()) { _rotations[DefaultItemNameResolver.Normalize(item.Key)] = item.Value; } } } public class ItemRackController : MonoBehaviour { private readonly List<ItemRackSlot> _slots = new List<ItemRackSlot>(); private readonly Dictionary<string, ItemRackSlot> _slotByKey = new Dictionary<string, ItemRackSlot>(StringComparer.OrdinalIgnoreCase); private DefaultItemPlacement _defaultPlacement; private bool _initialized; public IReadOnlyList<ItemRackSlot> Slots => _slots; public void Init(Transform anchor) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _initialized = true; Plugin.Log.LogInfo((object)"ItemRackController initialization started."); if (ModConfig.EnableLeftWall.Value) { BuildWall("Left", anchor, new Vector3(ModConfig.LeftOffsetX.Value, ModConfig.LeftOffsetY.Value, ModConfig.LeftOffsetZ.Value), ModConfig.LeftRotationY.Value); } if (ModConfig.EnableRightWall.Value) { BuildWall("Right", anchor, new Vector3(ModConfig.RightOffsetX.Value, ModConfig.RightOffsetY.Value, ModConfig.RightOffsetZ.Value), ModConfig.RightRotationY.Value); } if (ModConfig.EnableGrenadePlatform.Value) { BuildItemPlatform(anchor, "GrenadePlatform", 3, 4, new Vector3(ModConfig.GrenadePlatformOffsetX.Value, ModConfig.GrenadePlatformOffsetY.Value, ModConfig.GrenadePlatformOffsetZ.Value), ModConfig.GrenadePlatformRotationY.Value, ModConfig.GrenadePlatformSpacingX.Value, ModConfig.GrenadePlatformSpacingZ.Value, new Vector3(ModConfig.GrenadePlatformSizeX.Value, ModConfig.GrenadePlatformSizeY.Value, ModConfig.GrenadePlatformSizeZ.Value)); } if (ModConfig.EnableDronePlatform.Value) { BuildItemPlatform(anchor, "DronePlatform", 2, 3, new Vector3(ModConfig.DronePlatformOffsetX.Value, ModConfig.DronePlatformOffsetY.Value, ModConfig.DronePlatformOffsetZ.Value), ModConfig.DronePlatformRotationY.Value, ModConfig.DronePlatformSpacingX.Value, ModConfig.DronePlatformSpacingZ.Value, new Vector3(ModConfig.DronePlatformSizeX.Value, ModConfig.DronePlatformSizeY.Value, ModConfig.DronePlatformSizeZ.Value)); } if (ModConfig.EnableMinePlatform.Value) { BuildItemPlatform(anchor, "MinePlatform", 2, 6, new Vector3(ModConfig.MinePlatformOffsetX.Value, ModConfig.MinePlatformOffsetY.Value, ModConfig.MinePlatformOffsetZ.Value), ModConfig.MinePlatformRotationY.Value, ModConfig.MinePlatformSpacingX.Value, ModConfig.MinePlatformSpacingZ.Value, new Vector3(ModConfig.MinePlatformSizeX.Value, ModConfig.MinePlatformSizeY.Value, ModConfig.MinePlatformSizeZ.Value)); } _defaultPlacement = ((Component)this).gameObject.GetComponent<DefaultItemPlacement>(); if ((Object)(object)_defaultPlacement == (Object)null) { _defaultPlacement = ((Component)this).gameObject.AddComponent<DefaultItemPlacement>(); } _defaultPlacement.Init(this); Plugin.Log.LogInfo((object)$"Built {_slots.Count} rack/platform slot(s) total inside the truck."); Plugin.Log.LogInfo((object)"Horizontal item platforms initialized. Platform cells are non-solid and use the same snap/hold system as rack shelves."); } } private void BuildWall(string wallName, Transform anchor, Vector3 localOffset, float extraRotationY) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ItemRackWall_" + wallName); val.transform.SetParent(anchor, false); val.transform.localPosition = localOffset; val.transform.localRotation = Quaternion.Euler(0f, extraRotationY, 0f); int num = Mathf.Max(1, ModConfig.Rows.Value); int num2 = Mathf.Max(1, ModConfig.Columns.Value); float value = ModConfig.SpacingHorizontal.Value; float value2 = ModConfig.SpacingVertical.Value; float num3 = (float)(num2 - 1) * value; float num4 = (float)(num - 1) * value2; Vector3 localPosition = default(Vector3); for (int i = 0; i < num; i++) { for (int j = 0; j < num2; j++) { int index = i * num2 + j + 1; ((Vector3)(ref localPosition))..ctor((0f - num3) / 2f + (float)j * value, (0f - num4) / 2f + (float)i * value2, 0f); BuildSlot(val.transform, wallName, i + 1, j + 1, index, localPosition, platform: false, Vector3.zero); } } Plugin.Log.LogInfo((object)$"Built {wallName} wall: {num} row(s) x {num2} column(s)."); } private void BuildSlot(Transform parent, string wallName, int row, int column, int index, Vector3 localPosition, bool platform, Vector3 cellSize) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) string text = ModConfig.MakeSlotKey(wallName, row, column); GameObject val = new GameObject($"ItemRackSlot_{wallName}_{index}"); val.transform.SetParent(parent, false); val.transform.localPosition = localPosition; val.transform.localRotation = Quaternion.identity; SphereCollider val2 = val.AddComponent<SphereCollider>(); ((Collider)val2).isTrigger = true; val2.radius = (platform ? Mathf.Max(ModConfig.SlotRadius.Value, Mathf.Max(cellSize.x, Mathf.Max(cellSize.y, cellSize.z)) * 0.5f) : ModConfig.SlotRadius.Value); ItemRackSlot itemRackSlot = val.AddComponent<ItemRackSlot>(); itemRackSlot.Setup(this, wallName, row, column, text); string configuredItemName = GetConfiguredItemName(text); if (!string.IsNullOrWhiteSpace(configuredItemName)) { itemRackSlot.SetInitialItemBinding(configuredItemName); } _slots.Add(itemRackSlot); _slotByKey[text] = itemRackSlot; if (ModConfig.ShowVisualMarkers.Value) { if (platform) { CreatePlatformMarker(val.transform, cellSize); } else { CreateVisualMarker(val.transform); } } } private void BuildItemPlatform(Transform anchor, string platformName, int rows, int columns, Vector3 localOffset, float rotationY, float spacingX, float spacingZ, Vector3 cellSize) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0026: 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_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ItemRack_" + platformName); val.transform.SetParent(anchor, false); val.transform.localPosition = localOffset; val.transform.localRotation = Quaternion.Euler(0f, rotationY, 0f); float num = (float)(columns - 1) * spacingX; float num2 = (float)(rows - 1) * spacingZ; Vector3 localPosition = default(Vector3); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { ((Vector3)(ref localPosition))..ctor((0f - num) / 2f + (float)j * spacingX, 0f, (0f - num2) / 2f + (float)i * spacingZ); string text = ModConfig.MakePlatformSlotKey(platformName, i + 1, j + 1); GameObject val2 = new GameObject($"{platformName}_Slot_{i + 1}_{j + 1}"); val2.transform.SetParent(val.transform, false); val2.transform.localPosition = localPosition; val2.transform.localRotation = Quaternion.identity; SphereCollider val3 = val2.AddComponent<SphereCollider>(); ((Collider)val3).isTrigger = true; val3.radius = Mathf.Max(ModConfig.SlotRadius.Value, Mathf.Max(cellSize.x, Mathf.Max(cellSize.y, cellSize.z)) * 0.5f); ItemRackSlot itemRackSlot = val2.AddComponent<ItemRackSlot>(); itemRackSlot.Setup(this, platformName, i + 1, j + 1, text); string configuredItemName = GetConfiguredItemName(text); if (!string.IsNullOrWhiteSpace(configuredItemName)) { itemRackSlot.SetInitialItemBinding(configuredItemName); } _slots.Add(itemRackSlot); _slotByKey[text] = itemRackSlot; if (ModConfig.ShowVisualMarkers.Value) { CreatePlatformMarker(val2.transform, cellSize); } } } Plugin.Log.LogInfo((object)($"SUCCESS: Built {platformName} at local position {localOffset}, " + $"rotation Y={rotationY}, {rows} row(s) x {columns} column(s).")); } public bool TryGetSlot(string key, out ItemRackSlot slot) { return _slotByKey.TryGetValue(key, out slot); } public ItemRackSlot FindSlotContaining(PhysGrabObject pgo) { if ((Object)(object)pgo == (Object)null) { return null; } foreach (ItemRackSlot slot in _slots) { if ((Object)(object)slot != (Object)null && (Object)(object)slot.Occupant == (Object)(object)pgo) { return slot; } } return null; } private static string GetConfiguredItemName(string slotKey) { foreach (DefaultAssignment item in ModConfig.ParseSlotAssignments()) { if (string.Equals(item.SlotKey, slotKey, StringComparison.OrdinalIgnoreCase)) { return item.ItemName; } } return null; } private void CreateVisualMarker(Transform parent) { //IL_0042: 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) try { GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val).name = "SlotMarker"; Collider component = val.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } val.transform.SetParent(parent, false); val.transform.localPosition = Vector3.zero; float num = ModConfig.SlotRadius.Value * 1.6f; val.transform.localScale = new Vector3(num, num, 0.015f); ApplyMarkerMaterial(val); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not create slot visual marker: " + ex.Message)); } } private void CreatePlatformMarker(Transform parent, Vector3 cellSize) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val).name = "PlatformSlotMarker"; Collider component = val.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } val.transform.SetParent(parent, false); val.transform.localPosition = Vector3.zero; val.transform.localScale = cellSize; ApplyMarkerMaterial(val); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not create platform visual marker: " + ex.Message)); } } private static void ApplyMarkerMaterial(GameObject marker) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) Renderer component = marker.GetComponent<Renderer>(); if (!((Object)(object)component == (Object)null)) { component.shadowCastingMode = (ShadowCastingMode)0; component.receiveShadows = false; if ((Object)(object)component.material != (Object)null) { component.material.color = new Color(0.25f, 0.9f, 1f, 0.55f); } } } public void ResetForNewScene() { foreach (ItemRackSlot slot in _slots) { if ((Object)(object)slot != (Object)null) { slot.ResetForNewScene(); } } if ((Object)(object)_defaultPlacement != (Object)null) { _defaultPlacement.ResetPlacementBookkeeping(); } Plugin.Log.LogInfo((object)"ItemRackMod reset startup platform bindings for a new scene."); } public void OnSlotFreed(ItemRackSlot slot) { } } public class ItemRackSlot : MonoBehaviour { private ItemRackController _controller; private string _configuredInitialItemName; private float _initialBindingReleaseAt = -1f; private const float InitialBindingSeconds = 5f; private float _kinematicRefreshTimer; private const float KinematicRefreshInterval = 0.25f; private const float KinematicHoldSeconds = 0.75f; public string WallName { get; private set; } public int Row { get; private set; } public int Column { get; private set; } public string SlotKey { get; private set; } public PhysGrabObject Occupant { get; private set; } public string InitialItemName { get; private set; } public bool IsPlatformSlot { get; private set; } public bool InitialBindingActive { get; private set; } public void Setup(ItemRackController controller, string wallName, int row, int column, string slotKey) { _controller = controller; WallName = wallName; Row = row; Column = column; SlotKey = slotKey; IsPlatformSlot = IsPlatformName(wallName); } public void SetInitialItemBinding(string itemName) { if (!string.IsNullOrWhiteSpace(itemName)) { _configuredInitialItemName = itemName.Trim(); InitialItemName = _configuredInitialItemName; InitialBindingActive = true; _initialBindingReleaseAt = Time.time + 5f; Plugin.Log.LogInfo((object)$"Startup item filter armed for {SlotKey}: '{InitialItemName}' for {5f:0.#} seconds."); } } public void ResetForNewScene() { if ((Object)(object)Occupant != (Object)null && (Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform) { ((Component)Occupant).transform.SetParent((Transform)null, true); } Occupant = null; _kinematicRefreshTimer = 0f; if (!string.IsNullOrWhiteSpace(_configuredInitialItemName)) { InitialItemName = _configuredInitialItemName; InitialBindingActive = true; _initialBindingReleaseAt = Time.time + 5f; } } public bool AcceptsItemForInitialBinding(PhysGrabObject pgo) { if (!InitialBindingActive) { return true; } return !string.IsNullOrWhiteSpace(InitialItemName) && DefaultItemNameResolver.MatchesConfiguredName(InitialItemName, pgo); } private static bool IsPlatformName(string name) { return string.Equals(name, "DronePlatform", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "GrenadePlatform", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "MinePlatform", StringComparison.OrdinalIgnoreCase); } private void Update() { if (InitialBindingActive && _initialBindingReleaseAt > 0f && Time.time >= _initialBindingReleaseAt) { InitialBindingActive = false; InitialItemName = null; _initialBindingReleaseAt = -1f; Plugin.Log.LogInfo((object)("Initial item filter expired for " + SlotKey + "; slot is now universal.")); } if ((Object)(object)Occupant == (Object)null) { return; } if (Occupant.dead || Occupant.grabbed) { ReleaseOccupant(); } else if (SemiFunc.IsMasterClientOrSingleplayer()) { _kinematicRefreshTimer -= Time.deltaTime; if (!(_kinematicRefreshTimer > 0f)) { _kinematicRefreshTimer = 0.25f; MaintainOccupant(); } } } private void OnTriggerStay(Collider other) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Occupant != (Object)null || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } PhysGrabObject val = FindPhysGrabObject(other); if ((Object)(object)val == (Object)null || val.dead || val.grabbed || (Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null || (IsPlatformSlot && InitialBindingActive) || !AcceptsItemForInitialBinding(val)) { return; } if ((Object)(object)val.rb != (Object)null) { float value = ModConfig.MaxSnapSpeed.Value; Vector3 velocity = val.rb.velocity; if (((Vector3)(ref velocity)).sqrMagnitude > value * value) { return; } } TrySnapItem(val); } private static PhysGrabObject FindPhysGrabObject(Collider other) { if ((Object)(object)other == (Object)null) { return null; } PhysGrabObject component = ((Component)other).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)other).GetComponentInParent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { return component; } if ((Object)(object)other.attachedRigidbody != (Object)null) { component = ((Component)other.attachedRigidbody).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)other.attachedRigidbody).GetComponentInParent<PhysGrabObject>(); } return component; } public bool TrySnapItem(PhysGrabObject pgo, bool allowReplace = false, bool applyConfiguredOrientation = true, bool bypassStartupFilter = false) { if ((Object)(object)pgo == (Object)null || pgo.dead || pgo.grabbed) { return false; } if ((Object)(object)((Component)pgo).GetComponent<PhysGrabCart>() != (Object)null) { return false; } if (!bypassStartupFilter && !AcceptsItemForInitialBinding(pgo)) { return false; } if ((Object)(object)Occupant != (Object)null) { if (!allowReplace) { return false; } ReleaseOccupant(); if ((Object)(object)Occupant != (Object)null) { return false; } } PhotonView component = ((Component)pgo).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && !component.IsMine) { component.RequestOwnership(); } Occupant = pgo; _kinematicRefreshTimer = 0f; ((Component)pgo).transform.SetParent(((Component)this).transform, false); MaintainOccupant(applyConfiguredOrientation); return (Object)(object)Occupant == (Object)(object)pgo; } public void DetachForAutomaticMove() { if (!((Object)(object)Occupant == (Object)null)) { if ((Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform) { ((Component)Occupant).transform.SetParent((Transform)null, true); } Occupant = null; _kinematicRefreshTimer = 0f; _controller?.OnSlotFreed(this); } } private void MaintainOccupant(bool applyConfiguredOrientation = true) { //IL_003f: 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_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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Occupant == (Object)null) { return; } if (Occupant.dead || Occupant.grabbed) { ReleaseOccupant(); return; } Quaternion val = Quaternion.identity; if (applyConfiguredOrientation) { val = GetDesiredLocalRotation(); } Quaternion val2 = ((Component)this).transform.rotation * val; Occupant.OverrideKinematic(0.75f); ((Component)Occupant).transform.localPosition = Vector3.zero; ((Component)Occupant).transform.localRotation = val; Occupant.Teleport(((Component)this).transform.position, val2); } private Quaternion GetDesiredLocalRotation() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0056: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_004b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Occupant == (Object)null) { return Quaternion.identity; } if (!DefaultItemNameResolver.TryGetItemName(Occupant, out var name)) { return Quaternion.identity; } if (ModConfig.TryGetItemOrientation(name, out var euler)) { return Quaternion.Euler(euler); } return Quaternion.identity; } private void ReleaseOccupant() { if ((Object)(object)Occupant != (Object)null && (Object)(object)((Component)Occupant).transform.parent == (Object)(object)((Component)this).transform) { ((Component)Occupant).transform.SetParent((Transform)null, true); } Occupant = null; _kinematicRefreshTimer = 0f; _controller?.OnSlotFreed(this); } } internal static class ModConfig { public static ConfigEntry<int> Rows; public static ConfigEntry<int> Columns; public static ConfigEntry<float> SpacingHorizontal; public static ConfigEntry<float> SpacingVertical; public static ConfigEntry<bool> EnableLeftWall; public static ConfigEntry<bool> EnableRightWall; public static ConfigEntry<float> SlotRadius; public static ConfigEntry<float> MaxSnapSpeed; public static ConfigEntry<bool> ShowVisualMarkers; public static ConfigEntry<string> SlotAssignments; public static ConfigEntry<string> ItemOrientations; private static readonly Dictionary<string, Vector3> CachedItemOrientations = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<float> LeftOffsetX; public static ConfigEntry<float> LeftOffsetY; public static ConfigEntry<float> LeftOffsetZ; public static ConfigEntry<float> LeftRotationY; public static ConfigEntry<float> RightOffsetX; public static ConfigEntry<float> RightOffsetY; public static ConfigEntry<float> RightOffsetZ; public static ConfigEntry<float> RightRotationY; public static ConfigEntry<bool> EnableGrenadePlatform; public static ConfigEntry<float> GrenadePlatformOffsetX; public static ConfigEntry<float> GrenadePlatformOffsetY; public static ConfigEntry<float> GrenadePlatformOffsetZ; public static ConfigEntry<float> GrenadePlatformRotationY; public static ConfigEntry<float> GrenadePlatformSpacingX; public static ConfigEntry<float> GrenadePlatformSpacingZ; public static ConfigEntry<float> GrenadePlatformSizeX; public static ConfigEntry<float> GrenadePlatformSizeY; public static ConfigEntry<float> GrenadePlatformSizeZ; public static ConfigEntry<bool> EnableDronePlatform; public static ConfigEntry<float> DronePlatformOffsetX; public static ConfigEntry<float> DronePlatformOffsetY; public static ConfigEntry<float> DronePlatformOffsetZ; public static ConfigEntry<float> DronePlatformRotationY; public static ConfigEntry<float> DronePlatformSpacingX; public static ConfigEntry<float> DronePlatformSpacingZ; public static ConfigEntry<float> DronePlatformSizeX; public static ConfigEntry<float> DronePlatformSizeY; public static ConfigEntry<float> DronePlatformSizeZ; public static ConfigEntry<bool> EnableMinePlatform; public static ConfigEntry<float> MinePlatformOffsetX; public static ConfigEntry<float> MinePlatformOffsetY; public static ConfigEntry<float> MinePlatformOffsetZ; public static ConfigEntry<float> MinePlatformRotationY; public static ConfigEntry<float> MinePlatformSpacingX; public static ConfigEntry<float> MinePlatformSpacingZ; public static ConfigEntry<float> MinePlatformSizeX; public static ConfigEntry<float> MinePlatformSizeY; public static ConfigEntry<float> MinePlatformSizeZ; public static void Init(ConfigFile config) { Rows = config.Bind<int>("Rack", "Rows", 2, "Rows per wall (vertical, floor-to-ceiling)."); Columns = config.Bind<int>("Rack", "Columns", 7, "Columns per wall (along the wall)."); SpacingHorizontal = config.Bind<float>("Rack", "SpacingHorizontal", 0.8000002f, "Distance between columns, in meters."); SpacingVertical = config.Bind<float>("Rack", "SpacingVertical", 1.099999f, "Distance between rows, in meters."); EnableLeftWall = config.Bind<bool>("Rack", "EnableLeftWall", true, "Build a rack on the left wall."); EnableRightWall = config.Bind<bool>("Rack", "EnableRightWall", true, "Build a rack on the right wall."); SlotRadius = config.Bind<float>("Rack", "SlotRadius", 0.02000006f, "Radius of each slot's detection trigger, in meters."); MaxSnapSpeed = config.Bind<float>("Rack", "MaxSnapSpeed", 3.5f, "Items moving faster than this (m/s) will not snap into a slot."); ShowVisualMarkers = config.Bind<bool>("Rack", "ShowVisualMarkers", true, "Draw the same thin cyan marker at each empty rack/platform slot."); SlotAssignments = config.Bind<string>("Rack.ItemAssignments", "SlotAssignments", "Right:1:1=Inflatable Hammer,Right:1:2=Baseball Bat,Right:1:3=Zero Gravity Staff,Right:1:4=Roll Staff,Right:1:5=Void Staff,Right:1:6=Rubber Duck,Right:1:7=semibot walkie,Right:2:1=Sword,Right:2:2=Frying Pan,Right:2:3=ProdZap,Right:2:4=Sledge Hammer,Right:2:7=semibot walkie,Left:1:1=C.A.R.T. Cannon,Left:1:2=Defibro,Left:1:3=Extraction Tracker,Left:1:4=Tranq Gun,Left:1:5=Shotgun,Left:1:6=Gun,Left:1:7=Pulse Pistol,Left:2:1=C.A.R.T. Laser,Left:2:2=Leaf Blower,Left:2:3=Duck Bucket,Left:2:4=Valuable Tracker,Left:2:5=Photon Blaster,Left:2:6=Boltzap,Left:2:7=Phase Bridge,DronePlatform:1:1=zero gravity drone,DronePlatform:1:2=zero gravity orb,DronePlatform:1:3=recharge drone,DronePlatform:2:1=indestructible drone,DronePlatform:2:2=roll drone,DronePlatform:2:3=feather drone,GrenadePlatform:1:1=grenade,GrenadePlatform:1:2=human grenade,GrenadePlatform:1:3=stun grenade,GrenadePlatform:1:4=shockwave mine,GrenadePlatform:2:1=grenade,GrenadePlatform:2:2=human grenade,GrenadePlatform:2:3=stun grenade,GrenadePlatform:2:4=shockwave mine,GrenadePlatform:3:1=grenade,GrenadePlatform:3:2=human grenade,GrenadePlatform:3:3=stun grenade,GrenadePlatform:3:4=shockwave mine,MinePlatform:1:1=explosive mine,MinePlatform:1:2=explosive mine,MinePlatform:1:3=explosive mine,MinePlatform:1:4=trapzap,MinePlatform:1:5=trapzap,MinePlatform:1:6=trapzap,MinePlatform:2:1=shockwave grenade,MinePlatform:2:2=shockwave grenade,MinePlatform:2:3=shockwave grenade,MinePlatform:2:4=duct taped grenade,MinePlatform:2:5=duct taped grenade,MinePlatform:2:6=duct taped grenade", "Default locations for wall racks and the three horizontal item platforms. Platform assignments use stable game component aliases."); EnsurePlatformAssignments(); ItemOrientations = config.Bind<string>("Rack.ItemAssignments", "ItemOrientations", "Boltzap=90,0,0;Photon Blaster=90,0,0;Valuable Tracker=90,0,0;Leaf Blower=90,0,0;Pulse Pistol=90,0,0;Gun=90,0,0;Shotgun=90,0,0;Tranq Gun=90,0,0;Extraction Tracker=90,0,0", "Extra local Euler rotation for automatically placed rack items."); RebuildItemOrientationCache(); LeftOffsetX = config.Bind<float>("Rack.LeftWall", "OffsetX", 1.399298f, "Local X offset of the left rack from TruckSafetySpawnPoint."); LeftOffsetY = config.Bind<float>("Rack.LeftWall", "OffsetY", 0.8999991f, "Local Y offset of the left rack."); LeftOffsetZ = config.Bind<float>("Rack.LeftWall", "OffsetZ", -2f, "Local Z offset of the left rack."); LeftRotationY = config.Bind<float>("Rack.LeftWall", "RotationY", -3.650784E-06f, "Extra Y rotation for the left rack."); RightOffsetX = config.Bind<float>("Rack.RightWall", "OffsetX", -1.000082f, "Local X offset of the right rack."); RightOffsetY = config.Bind<float>("Rack.RightWall", "OffsetY", 0.8999996f, "Local Y offset of the right rack."); RightOffsetZ = config.Bind<float>("Rack.RightWall", "OffsetZ", 1.999999f, "Local Z offset of the right rack."); RightRotationY = config.Bind<float>("Rack.RightWall", "RotationY", -3.650784E-06f, "Extra Y rotation for the right rack."); EnableGrenadePlatform = config.Bind<bool>("Rack.GrenadePlatform", "Enable", true, "Build the grenade platform."); GrenadePlatformOffsetX = config.Bind<float>("Rack.GrenadePlatform", "OffsetX", 2.149997f, "Local X offset of the grenade platform."); GrenadePlatformOffsetY = config.Bind<float>("Rack.GrenadePlatform", "OffsetY", -0.6999996f, "Local Y offset of the grenade platform."); GrenadePlatformOffsetZ = config.Bind<float>("Rack.GrenadePlatform", "OffsetZ", 1.499974f, "Local Z offset of the grenade platform."); GrenadePlatformRotationY = config.Bind<float>("Rack.GrenadePlatform", "RotationY", 0f, "Extra Y rotation of the grenade platform."); GrenadePlatformSpacingX = config.Bind<float>("Rack.GrenadePlatform", "SpacingX", 0.4f, "Horizontal distance between grenade platform cells."); GrenadePlatformSpacingZ = config.Bind<float>("Rack.GrenadePlatform", "SpacingZ", 0.4000001f, "Depth distance between grenade platform cells."); GrenadePlatformSizeX = config.Bind<float>("Rack.GrenadePlatform", "CellSizeX", 0.04999996f, "Width of each grenade platform marker."); GrenadePlatformSizeY = config.Bind<float>("Rack.GrenadePlatform", "CellSizeY", 0.05f, "Height of each grenade platform marker."); GrenadePlatformSizeZ = config.Bind<float>("Rack.GrenadePlatform", "CellSizeZ", 0.04999998f, "Depth of each grenade platform marker."); EnableDronePlatform = config.Bind<bool>("Rack.DronePlatform", "Enable", true, "Build the drone platform."); DronePlatformOffsetX = config.Bind<float>("Rack.DronePlatform", "OffsetX", 2.000009f, "Local X offset of the drone platform."); DronePlatformOffsetY = config.Bind<float>("Rack.DronePlatform", "OffsetY", -0.7000024f, "Local Y offset of the drone platform."); DronePlatformOffsetZ = config.Bind<float>("Rack.DronePlatform", "OffsetZ", -1.400007f, "Local Z offset of the drone platform."); DronePlatformRotationY = config.Bind<float>("Rack.DronePlatform", "RotationY", 0f, "Extra Y rotation of the drone platform."); DronePlatformSpacingX = config.Bind<float>("Rack.DronePlatform", "SpacingX", 0.5000001f, "Horizontal distance between drone platform cells."); DronePlatformSpacingZ = config.Bind<float>("Rack.DronePlatform", "SpacingZ", 0.5000006f, "Depth distance between drone platform cells."); DronePlatformSizeX = config.Bind<float>("Rack.DronePlatform", "CellSizeX", 0.04999997f, "Width of each drone platform marker."); DronePlatformSizeY = config.Bind<float>("Rack.DronePlatform", "CellSizeY", 0.04999999f, "Height of each drone platform marker."); DronePlatformSizeZ = config.Bind<float>("Rack.DronePlatform", "CellSizeZ", 0.04999991f, "Depth of each drone platform marker."); EnableMinePlatform = config.Bind<bool>("Rack.MinePlatform", "Enable", true, "Build the mine/explosive-grenade platform."); MinePlatformOffsetX = config.Bind<float>("Rack.MinePlatform", "OffsetX", 5.000046f, "Local X offset of the mine platform."); MinePlatformOffsetY = config.Bind<float>("Rack.MinePlatform", "OffsetY", -1.589999f, "Local Y offset of the mine platform."); MinePlatformOffsetZ = config.Bind<float>("Rack.MinePlatform", "OffsetZ", -1.600002f, "Local Z offset of the mine platform."); MinePlatformRotationY = config.Bind<float>("Rack.MinePlatform", "RotationY", 0f, "Extra Y rotation of the mine platform."); MinePlatformSpacingX = config.Bind<float>("Rack.MinePlatform", "SpacingX", 0.6499999f, "Horizontal distance between mine platform cells."); MinePlatformSpacingZ = config.Bind<float>("Rack.MinePlatform", "SpacingZ", 0.6499999f, "Depth distance between mine platform cells."); MinePlatformSizeX = config.Bind<float>("Rack.MinePlatform", "CellSizeX", 0.04999996f, "Width of each mine platform marker."); MinePlatformSizeY = config.Bind<float>("Rack.MinePlatform", "CellSizeY", 0.05f, "Height of each mine platform marker."); MinePlatformSizeZ = config.Bind<float>("Rack.MinePlatform", "CellSizeZ", 0.04999996f, "Depth of each mine platform marker."); try { config.Save(); Plugin.Log.LogInfo((object)"ItemRackMod configuration initialized and saved."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not save ItemRackMod configuration: " + ex.Message)); } } private static void EnsurePlatformAssignments() { if (SlotAssignments == null) { return; } string text = SlotAssignments.Value ?? string.Empty; string[] collection = new string[30] { "DronePlatform:1:1=zero gravity drone", "DronePlatform:1:2=zero gravity orb", "DronePlatform:1:3=recharge drone", "DronePlatform:2:1=indestructible drone", "DronePlatform:2:2=roll drone", "DronePlatform:2:3=feather drone", "GrenadePlatform:1:1=granade", "GrenadePlatform:1:2=human granade", "GrenadePlatform:1:3=stun granade", "GrenadePlatform:1:4=shockwave mine", "GrenadePlatform:2:1=granade", "GrenadePlatform:2:2=human granade", "GrenadePlatform:2:3=stun granade", "GrenadePlatform:2:4=shockwave mine", "GrenadePlatform:3:1=granade", "GrenadePlatform:3:2=human granade", "GrenadePlatform:3:3=stun granade", "GrenadePlatform:3:4=shockwave mine", "MinePlatform:1:1=explosive mine", "MinePlatform:1:2=explosive mine", "MinePlatform:1:3=explosive mine", "MinePlatform:1:4=shockwave granade", "MinePlatform:1:5=shockwave granade", "MinePlatform:1:6=shockwave granade", "MinePlatform:2:1=trapzap", "MinePlatform:2:2=trapzap", "MinePlatform:2:3=trapzap", "MinePlatform:2:4=duct taped granade", "MinePlatform:2:5=duct taped granade", "MinePlatform:2:6=duct taped granade" }; List<string> list = new List<string>(); string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = text2.Trim(); int num = text3.IndexOf('='); if (num > 0) { string text4 = text3.Substring(0, num).Trim(); if (!text4.StartsWith("DronePlatform:", StringComparison.OrdinalIgnoreCase) && !text4.StartsWith("GrenadePlatform:", StringComparison.OrdinalIgnoreCase) && !text4.StartsWith("MinePlatform:", StringComparison.OrdinalIgnoreCase)) { list.Add(text3); } } } list.AddRange(collection); SlotAssignments.Value = string.Join(",", list.ToArray()); Plugin.Log.LogInfo((object)"Platform item assignments migrated to the exact item names supplied by the user."); } public static string MakeSlotKey(string wall, int row, int column) { return $"{wall}:{row}:{column}"; } public static string MakePlatformSlotKey(string platformName, int row, int column) { return $"{platformName}:{row}:{column}"; } public static List<DefaultAssignment> ParseSlotAssignments() { List<DefaultAssignment> list = new List<DefaultAssignment>(); string text = SlotAssignments?.Value ?? string.Empty; if (string.IsNullOrWhiteSpace(text)) { return list; } string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); int num = text3.IndexOf('='); if (num <= 0 || num >= text3.Length - 1) { continue; } string text4 = text3.Substring(0, num).Trim(); string text5 = text3.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text4) && !string.IsNullOrWhiteSpace(text5)) { string[] array3 = text4.Split(new char[1] { ':' }); if (array3.Length == 3 && int.TryParse(array3[1], out var result) && int.TryParse(array3[2], out var result2) && result >= 1 && result2 >= 1 && !string.IsNullOrWhiteSpace(array3[0])) { list.Add(new DefaultAssignment { SlotKey = MakeSlotKey(array3[0].Trim(), result, result2), ItemName = text5 }); } } } return list; } public static bool TryGetItemOrientation(string itemName, out Vector3 euler) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) euler = Vector3.zero; if (string.IsNullOrWhiteSpace(itemName)) { return false; } return CachedItemOrientations.TryGetValue(DefaultItemNameResolver.Normalize(itemName), out euler); } private static void RebuildItemOrientationCache() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) CachedItemOrientations.Clear(); foreach (KeyValuePair<string, Vector3> item in ParseItemOrientations()) { string text = DefaultItemNameResolver.Normalize(item.Key); if (!string.IsNullOrEmpty(text)) { CachedItemOrientations[text] = item.Value; } } } public static List<KeyValuePair<string, Vector3>> ParseItemOrientations() { //IL_0152: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<string, Vector3>> list = new List<KeyValuePair<string, Vector3>>(); string text = ItemOrientations?.Value ?? string.Empty; if (string.IsNullOrWhiteSpace(text)) { return list; } string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); int num = text3.IndexOf('='); if (num > 0 && num < text3.Length - 1) { string key = text3.Substring(0, num).Trim(); string[] array3 = text3.Substring(num + 1).Trim().Split(new char[1] { ',' }); if (array3.Length == 3 && float.TryParse(array3[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array3[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array3[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { list.Add(new KeyValuePair<string, Vector3>(key, new Vector3(result, result2, result3))); } } } return list; } } [HarmonyPatch(typeof(TruckSafetySpawnPoint), "Awake")] internal static class TruckSafetySpawnPoint_Awake_Patch { private static void Postfix(TruckSafetySpawnPoint __instance) { if (!((Object)(object)__instance == (Object)null)) { ItemRackController component = ((Component)__instance).GetComponent<ItemRackController>(); if (!((Object)(object)component != (Object)null)) { component = ((Component)__instance).gameObject.AddComponent<ItemRackController>(); component.Init(((Component)__instance).transform); Plugin.Log.LogInfo((object)"Item racks attached inside the truck (anchored to TruckSafetySpawnPoint)."); } } } } [BepInPlugin("Item.Shelves.Platforms", "Item Shelves & Platforms", "1.4.3")] public class Plugin : BaseUnityPlugin { public const string Guid = "Item.Shelves.Platforms"; public const string Name = "Item Shelves & Platforms"; public const string Version = "1.4.3"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Item.Shelves.Platforms"); _harmony.PatchAll(); SceneManager.sceneLoaded += OnSceneLoaded; Log.LogInfo((object)"Item Shelves & Platforms v1.4.3 loaded, patches applied."); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ItemRackController[] array = Object.FindObjectsOfType<ItemRackController>(); if (array == null) { return; } ItemRackController[] array2 = array; foreach (ItemRackController itemRackController in array2) { if ((Object)(object)itemRackController != (Object)null) { itemRackController.ResetForNewScene(); } } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } }