Decompiled source of SpaceRefill v2.3.0

BepInEx/plugins/SpaceRefill/SpaceRefillMod.dll

Decompiled 15 hours ago
using System;
using System.Diagnostics;
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.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("coddingcat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Spam Space while restocking shelves in Shift At Midnight.")]
[assembly: AssemblyFileVersion("2.3.0.0")]
[assembly: AssemblyInformationalVersion("2.3.0")]
[assembly: AssemblyProduct("SpaceRefillMod")]
[assembly: AssemblyTitle("SpaceRefillMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.3.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 SpaceRefillMod
{
	internal static class PluginInfo
	{
		internal const string Guid = "com.coddingcat.shiftatm.spacerefill";

		internal const string Name = "Space Refill";

		internal const string Version = "2.3.0";
	}
	[BepInPlugin("com.coddingcat.shiftatm.spacerefill", "Space Refill", "2.3.0")]
	public sealed class SpaceRefillPlugin : BasePlugin
	{
		private Harmony _harmony;

		private static ConfigEntry<float> _boostPerPress;

		private static ConfigEntry<float> _drainPerSecond;

		internal static ManualLogSource LogSource { get; private set; }

		internal static float BoostPerPress => Mathf.Clamp(_boostPerPress?.Value ?? 0.12f, 0.01f, 1f);

		internal static float DrainPerSecond => Mathf.Max(0f, _drainPerSecond?.Value ?? 0.5f);

		public override void Load()
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			LogSource = ((BasePlugin)this).Log;
			_boostPerPress = ((BasePlugin)this).Config.Bind<float>("Meter", "BoostPerPress", 0.12f, "Amount added to the meter by each Space press.");
			_drainPerSecond = ((BasePlugin)this).Config.Bind<float>("Meter", "DrainPerSecond", 0.5f, "Meter power lost per second. Lower values make each press last longer.");
			bool flag = default(bool);
			try
			{
				if (!ShelfGameAccess.Validate(out var error))
				{
					throw new InvalidOperationException(error);
				}
				SpamMeterUi.Create();
				_harmony = new Harmony("com.coddingcat.shiftatm.spacerefill");
				_harmony.PatchAll(typeof(RestockShelfPatches));
				ManualLogSource log = ((BasePlugin)this).Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Space Refill");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("2.3.0");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded.");
				}
				log.LogInfo(val);
				((BasePlugin)this).Log.LogInfo((object)"Interact with a shelf and spam Space. A full meter places one item.");
			}
			catch (Exception ex)
			{
				SpamMeterUi.Destroy();
				ManualLogSource log2 = ((BasePlugin)this).Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(23, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to initialise ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("Space Refill");
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				log2.LogError(val2);
				throw;
			}
		}

		public override bool Unload()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			RefillController.Reset();
			SpamMeterUi.Destroy();
			LogSource = null;
			return true;
		}
	}
	internal static class RefillController
	{
		private const float PlacementRetryDelay = 0.5f;

		private const float FailureLogCooldown = 5f;

		private static RestockShelf _activeShelf;

		private static float _meterPower;

		private static float _retryAt;

		private static float _nextFailureLogAt;

		internal static void Start(RestockShelf shelf)
		{
			if (ShelfGameAccess.IsInteracting(shelf))
			{
				_activeShelf = shelf;
				_meterPower = 0f;
				_retryAt = 0f;
				SpamMeterUi.SetLevel(0f);
				SpamMeterUi.SetVisible(visible: true);
			}
		}

		internal static void Stop(RestockShelf shelf)
		{
			if (IsActiveShelf(shelf))
			{
				Reset();
			}
		}

		internal static void Tick(RestockShelf shelf)
		{
			if (!IsActiveShelf(shelf))
			{
				return;
			}
			if (!ShelfGameAccess.IsInteracting(shelf))
			{
				Reset();
				return;
			}
			if (Input.GetKeyDown((KeyCode)32))
			{
				HandleSpacePress(shelf);
			}
			float num = Mathf.Clamp(Time.unscaledDeltaTime, 0f, 0.25f);
			_meterPower = Mathf.Max(0f, _meterPower - SpaceRefillPlugin.DrainPerSecond * num);
			SpamMeterUi.SetLevel(_meterPower);
		}

		internal static void Reset()
		{
			_activeShelf = null;
			_meterPower = 0f;
			_retryAt = 0f;
			SpamMeterUi.SetLevel(0f);
			SpamMeterUi.SetVisible(visible: false);
		}

		private static void HandleSpacePress(RestockShelf shelf)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			_meterPower = Mathf.Clamp01(_meterPower + SpaceRefillPlugin.BoostPerPress);
			if (_meterPower < 1f || Time.unscaledTime < _retryAt)
			{
				return;
			}
			string error;
			PlacementResult placementResult = ShelfGameAccess.TryPlaceNextItem(shelf, out error);
			if (placementResult == PlacementResult.Success || placementResult == PlacementResult.ShelfEmpty)
			{
				_meterPower = 0f;
				SpamMeterUi.SetLevel(0f);
				return;
			}
			_retryAt = Time.unscaledTime + 0.5f;
			if (Time.unscaledTime < _nextFailureLogAt)
			{
				return;
			}
			ManualLogSource logSource = SpaceRefillPlugin.LogSource;
			if (logSource != null)
			{
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(37, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not place the next shelf item: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error);
				}
				logSource.LogWarning(val);
			}
			_nextFailureLogAt = Time.unscaledTime + 5f;
		}

		private static bool IsActiveShelf(RestockShelf shelf)
		{
			if ((Object)(object)shelf != (Object)null && (Object)(object)_activeShelf != (Object)null)
			{
				return ((Il2CppObjectBase)_activeShelf).Pointer == ((Il2CppObjectBase)shelf).Pointer;
			}
			return false;
		}
	}
	internal enum PlacementResult
	{
		Success,
		ShelfEmpty,
		Failed
	}
	internal static class ShelfGameAccess
	{
		private const BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly PropertyInfo InteractingProperty = typeof(RestockShelf).GetProperty("interacting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly PropertyInfo ProductsQueueProperty = typeof(ShelfManager).GetProperty("productsQueue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly PropertyInfo LastShelfItemProperty = typeof(ShelfManager).GetProperty("lastShelfItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CompleteItemMethod = typeof(ShelfManager).GetMethod("CompleteItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static Type _cachedQueueType;

		private static PropertyInfo _queueCountProperty;

		private static MethodInfo _queuePeekMethod;

		internal static bool Validate(out string error)
		{
			if (InteractingProperty == null)
			{
				error = "RestockShelf.interacting was not found. The game may have been updated.";
				return false;
			}
			if (ProductsQueueProperty == null || LastShelfItemProperty == null || CompleteItemMethod == null)
			{
				error = "Required ShelfManager members were not found. The game may have been updated.";
				return false;
			}
			error = null;
			return true;
		}

		internal static bool IsInteracting(RestockShelf shelf)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			if ((Object)(object)shelf == (Object)null)
			{
				return false;
			}
			try
			{
				object value = InteractingProperty.GetValue(shelf);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				ManualLogSource logSource = SpaceRefillPlugin.LogSource;
				if (logSource != null)
				{
					bool flag2 = default(bool);
					BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(41, 1, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not read RestockShelf.interacting: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logSource.LogDebug(val);
				}
				return false;
			}
		}

		internal static PlacementResult TryPlaceNextItem(RestockShelf shelf, out string error)
		{
			error = null;
			try
			{
				ShelfManager val = ((shelf != null) ? shelf.shelfMan : null);
				if ((Object)(object)val == (Object)null)
				{
					return Fail("the shelf manager is missing", out error);
				}
				object value = ProductsQueueProperty.GetValue(val);
				if (value == null)
				{
					return Fail("the product queue is not ready", out error);
				}
				if (!TryPrepareQueueAccess(value.GetType(), out error))
				{
					return PlacementResult.Failed;
				}
				if (Convert.ToInt32(_queueCountProperty.GetValue(value)) <= 0)
				{
					return PlacementResult.ShelfEmpty;
				}
				object? obj = _queuePeekMethod.Invoke(value, null);
				object? obj2 = ((obj is GameObject) ? obj : null);
				DragItem val2 = ((obj2 != null) ? ((GameObject)obj2).GetComponent<DragItem>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return Fail("the next product has no DragItem component", out error);
				}
				ShelfItemManager val3 = FindMatchingSlot(val, val2.itemIndex);
				if ((Object)(object)val3 == (Object)null)
				{
					return Fail($"no shelf slot accepts item index {val2.itemIndex}", out error);
				}
				LastShelfItemProperty.SetValue(val, val3);
				CompleteItemMethod.Invoke(val, null);
				return PlacementResult.Success;
			}
			catch (TargetInvocationException ex)
			{
				return Fail(ex.InnerException?.Message ?? ex.Message, out error);
			}
			catch (Exception ex2)
			{
				return Fail(ex2.Message, out error);
			}
		}

		private static bool TryPrepareQueueAccess(Type queueType, out string error)
		{
			if (_cachedQueueType == queueType && _queueCountProperty != null && _queuePeekMethod != null)
			{
				error = null;
				return true;
			}
			_cachedQueueType = queueType;
			_queueCountProperty = queueType.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_queuePeekMethod = queueType.GetMethod("Peek", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (_queueCountProperty != null && _queuePeekMethod != null)
			{
				error = null;
				return true;
			}
			error = "the product queue API was not found";
			return false;
		}

		private static ShelfItemManager FindMatchingSlot(ShelfManager manager, int itemIndex)
		{
			ShelfItemManager[] array = Il2CppArrayBase<ShelfItemManager>.op_Implicit((Il2CppArrayBase<ShelfItemManager>)(object)manager.shelfItemManagers);
			if (array == null)
			{
				return null;
			}
			foreach (ShelfItemManager val in array)
			{
				if ((Object)(object)val != (Object)null && val.itemIndex == itemIndex)
				{
					return val;
				}
			}
			return null;
		}

		private static PlacementResult Fail(string message, out string error)
		{
			error = (string.IsNullOrWhiteSpace(message) ? "unknown game error" : message);
			return PlacementResult.Failed;
		}
	}
	internal static class SpamMeterUi
	{
		private static GameObject _canvasObject;

		private static RectTransform _fillRect;

		internal static void Create()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_00ce: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: 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_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_canvasObject != (Object)null))
			{
				_canvasObject = CreateUiObject<Canvas, CanvasScaler>("SpaceRefill_Canvas");
				Object.DontDestroyOnLoad((Object)(object)_canvasObject);
				Canvas component = _canvasObject.GetComponent<Canvas>();
				component.renderMode = (RenderMode)0;
				component.sortingOrder = 999;
				CanvasScaler component2 = _canvasObject.GetComponent<CanvasScaler>();
				component2.uiScaleMode = (ScaleMode)1;
				component2.referenceResolution = new Vector2(1920f, 1080f);
				component2.matchWidthOrHeight = 0.5f;
				GameObject val = CreateUiObject<Image>("SpaceRefill_Background");
				val.transform.SetParent(_canvasObject.transform, false);
				RectTransform component3 = val.GetComponent<RectTransform>();
				component3.anchorMin = new Vector2(0.92f, 0.5f);
				component3.anchorMax = new Vector2(0.92f, 0.5f);
				component3.pivot = new Vector2(0.5f, 0.5f);
				component3.anchoredPosition = Vector2.zero;
				component3.sizeDelta = new Vector2(32f, 260f);
				Image component4 = val.GetComponent<Image>();
				((Graphic)component4).color = new Color(0.02f, 0.02f, 0.02f, 0.82f);
				((Graphic)component4).raycastTarget = false;
				GameObject obj = CreateUiObject<Image>("SpaceRefill_Fill");
				obj.transform.SetParent(val.transform, false);
				_fillRect = obj.GetComponent<RectTransform>();
				_fillRect.anchorMin = Vector2.zero;
				_fillRect.anchorMax = new Vector2(1f, 0f);
				_fillRect.pivot = new Vector2(0.5f, 0f);
				_fillRect.offsetMin = Vector2.zero;
				_fillRect.offsetMax = Vector2.zero;
				Image component5 = obj.GetComponent<Image>();
				((Graphic)component5).color = Color.white;
				((Graphic)component5).raycastTarget = false;
				SetVisible(visible: false);
			}
		}

		internal static void Destroy()
		{
			if ((Object)(object)_canvasObject != (Object)null)
			{
				Object.Destroy((Object)(object)_canvasObject);
			}
			_canvasObject = null;
			_fillRect = null;
		}

		internal static void SetVisible(bool visible)
		{
			if ((Object)(object)_canvasObject != (Object)null && _canvasObject.activeSelf != visible)
			{
				_canvasObject.SetActive(visible);
			}
		}

		internal static void SetLevel(float amount)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_fillRect == (Object)null))
			{
				Vector2 anchorMax = _fillRect.anchorMax;
				anchorMax.x = 1f;
				anchorMax.y = Mathf.Clamp01(amount);
				_fillRect.anchorMax = anchorMax;
			}
		}

		private static GameObject CreateUiObject<TComponent>(string name) where TComponent : Component
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new GameObject(name, (Type[])(object)new Type[2]
			{
				Il2CppType.Of<RectTransform>(),
				Il2CppType.Of<TComponent>()
			});
		}

		private static GameObject CreateUiObject<TFirst, TSecond>(string name) where TFirst : Component where TSecond : Component
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			return new GameObject(name, (Type[])(object)new Type[3]
			{
				Il2CppType.Of<RectTransform>(),
				Il2CppType.Of<TFirst>(),
				Il2CppType.Of<TSecond>()
			});
		}
	}
	[HarmonyPatch]
	internal static class RestockShelfPatches
	{
		[HarmonyPatch(typeof(RestockShelf), "Interact")]
		[HarmonyPostfix]
		private static void AfterInteract(RestockShelf __instance)
		{
			RefillController.Start(__instance);
		}

		[HarmonyPatch(typeof(RestockShelf), "StopInteract")]
		[HarmonyPostfix]
		private static void AfterStopInteract(RestockShelf __instance)
		{
			RefillController.Stop(__instance);
		}

		[HarmonyPatch(typeof(RestockShelf), "Update")]
		[HarmonyPostfix]
		private static void AfterUpdate(RestockShelf __instance)
		{
			RefillController.Tick(__instance);
		}
	}
}