Decompiled source of All Shop Items In Level v1.2.0

plugins/REPO_All_Shop_Items_in_Level.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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 HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[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 = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace REPO_All_Shop_Items_in_Level
{
	public class UsedVolumeTracker : MonoBehaviour
	{
	}
	public class SpawnedItemTracker : MonoBehaviour
	{
	}
	[BepInPlugin("REPO_All_Shop_Items_in_Level", "ALL Shop Items spawn in Level", "1.7.23")]
	[BepInProcess("REPO.exe")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(EnemyParent), "Despawn")]
		private class DespawnPatch
		{
			private static int GetSpawnValuableCurrent(EnemyParent ep)
			{
				object? obj = AccessTools.Field(typeof(EnemyParent), "Enemy")?.GetValue(ep);
				Enemy val = (Enemy)((obj is Enemy) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					return 0;
				}
				object? obj2 = AccessTools.Field(typeof(Enemy), "Health")?.GetValue(val);
				EnemyHealth val2 = (EnemyHealth)((obj2 is EnemyHealth) ? obj2 : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return 0;
				}
				FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyHealth), "spawnValuableCurrent");
				return (!(fieldInfo == null)) ? ((int)fieldInfo.GetValue(val2)) : 0;
			}

			private static void Prefix(EnemyParent __instance, out int __state)
			{
				__state = GetSpawnValuableCurrent(__instance);
			}

			private static void Postfix(EnemyParent __instance, int __state)
			{
				//IL_00bc: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
				if (SemiFunc.IsMasterClientOrSingleplayer() && SpawnHealthPacksFromEnemies.Value && GetSpawnValuableCurrent(__instance) > __state && !(Random.Range(0f, 100f) > HealthPackDropChance.Value) && GetRandomItemOfType((itemType)8, out var item))
				{
					object? obj = AccessTools.Field(typeof(EnemyParent), "Enemy")?.GetValue(__instance);
					Enemy val = (Enemy)((obj is Enemy) ? obj : null);
					Transform val2 = (Object.op_Implicit((Object)(object)val.CustomValuableSpawnTransform) ? val.CustomValuableSpawnTransform : val.CenterTransform);
					SpawnItem(item, val2.position + Vector3.up, Quaternion.identity);
				}
			}
		}

		[HarmonyPatch]
		private class ValuableDirector_Spawn_Patch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(ValuableDirector), "Spawn", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(ValuableDirector), "SpawnValuable", (Type[])null, (Type[])null);
			}

			[HarmonyPrefix]
			private static void Prefix(object[] __args)
			{
				ValuableVolume val = __args.OfType<ValuableVolume>().FirstOrDefault();
				if ((Object)(object)val != (Object)null)
				{
					((Component)val).gameObject.AddComponent<UsedVolumeTracker>();
				}
			}
		}

		[CompilerGenerated]
		private sealed class <PopulateAllowListWhenReady>d__28 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public Plugin <>4__this;

			private Dictionary<string, Item>.ValueCollection.Enumerator <>s__1;

			private Item <value>5__2;

			private int <t>5__3;

			private string <section>5__4;

			private string <key>5__5;

			private ConfigEntry<bool> <entry>5__6;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PopulateAllowListWhenReady>d__28(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = default(Dictionary<string, Item>.ValueCollection.Enumerator);
				<value>5__2 = null;
				<section>5__4 = null;
				<key>5__5 = null;
				<entry>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d6: Expected I4, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null || StatsManager.instance.itemDictionary.Count == 0)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if (ItemAllowList != null)
				{
					return false;
				}
				Logger.LogInfo((object)"Initializing per-item allow list");
				ItemAllowList = new Dictionary<string, ConfigEntry<bool>>();
				<>s__1 = StatsManager.instance.itemDictionary.Values.GetEnumerator();
				try
				{
					while (<>s__1.MoveNext())
					{
						<value>5__2 = <>s__1.Current;
						<t>5__3 = (int)<value>5__2.itemType;
						<section>5__4 = ((<t>5__3 == 0) ? "6_AllowedItems_Drones" : ((<t>5__3 == 3) ? "6_AllowedItems_Upgrades" : ((<t>5__3 == 8) ? null : "6_AllowedItems_OtherShop")));
						if (<section>5__4 != null)
						{
							<key>5__5 = ((Object)<value>5__2).name;
							<entry>5__6 = ((BaseUnityPlugin)Instance).Config.Bind<bool>(<section>5__4, <key>5__5, true, "Allow '" + <key>5__5 + "' to spawn");
							ItemAllowList[<key>5__5] = <entry>5__6;
							DebugLog("Registered: " + <key>5__5);
							<section>5__4 = null;
							<key>5__5 = null;
							<entry>5__6 = null;
							<value>5__2 = null;
						}
					}
				}
				finally
				{
					((IDisposable)<>s__1).Dispose();
				}
				<>s__1 = default(Dictionary<string, Item>.ValueCollection.Enumerator);
				Logger.LogInfo((object)$"Registered {ItemAllowList.Count} items");
				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();
			}
		}

		internal static ManualLogSource Logger;

		internal static ConfigEntry<bool> DebugMode;

		internal static ConfigEntry<bool> SaveAllOnLevelComplete;

		internal static ConfigEntry<bool> SpawnUpgradeItems;

		internal static ConfigEntry<float> UpgradeItemSpawnChance;

		internal static ConfigEntry<bool> MapHideUpgradeItems;

		internal static ConfigEntry<bool> UseShopPriceForUpgradeItems;

		internal static ConfigEntry<bool> SpawnDroneItems;

		internal static ConfigEntry<float> DroneItemSpawnChance;

		internal static ConfigEntry<bool> MapHideDroneItems;

		internal static ConfigEntry<bool> UseShopPriceForDroneItems;

		internal static ConfigEntry<bool> SpawnOtherShopItems;

		internal static ConfigEntry<float> OtherShopItemSpawnChance;

		internal static ConfigEntry<bool> MapHideOtherShopItems;

		internal static ConfigEntry<bool> UseShopPriceForOtherItems;

		internal static ConfigEntry<bool> SpawnHealthPacksFromEnemies;

		internal static ConfigEntry<float> HealthPackDropChance;

		internal static ConfigEntry<float> MinItemValue;

		internal static ConfigEntry<float> MaxItemValue;

		internal static ConfigEntry<int> MaxItemsPerLevel;

		internal static Dictionary<string, ConfigEntry<bool>> ItemAllowList;

		private static readonly HashSet<int> KnownSpecialTypes = new HashSet<int> { 0, 3, 8 };

		public static Plugin Instance { get; private set; }

		internal static void DebugLog(string message)
		{
			if (DebugMode != null && DebugMode.Value)
			{
				Logger.LogInfo((object)("[DEBUG] " + message));
			}
		}

		private void Awake()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Expected O, but got Unknown
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Expected O, but got Unknown
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Expected O, but got Unknown
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Expected O, but got Unknown
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin REPO_All_Shop_Items_in_Level is loaded!");
			Harmony val = new Harmony("REPO_All_Shop_Items_in_Level");
			val.PatchAll(typeof(Plugin));
			val.PatchAll(typeof(DespawnPatch));
			val.PatchAll(typeof(ValuableDirector_Spawn_Patch));
			Logger.LogInfo((object)"Harmony patches applied!");
			DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("0_Debug", "DebugMode", false, "Enable verbose debug logging.");
			SaveAllOnLevelComplete = ((BaseUnityPlugin)this).Config.Bind<bool>("0_Debug", "SaveAllOnLevelComplete", true, "If true, all spawned items still in the level are saved when you complete a level (even if not in the truck). This ensures no item is lost.");
			SpawnUpgradeItems = ((BaseUnityPlugin)this).Config.Bind<bool>("1_UpgradeItems", "SpawnUpgradeItems", true, "");
			MapHideUpgradeItems = ((BaseUnityPlugin)this).Config.Bind<bool>("1_UpgradeItems", "MapHideUpgradeItems", false, "");
			UpgradeItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind<float>("1_UpgradeItems", "UpgradeItemSpawnChance", 2.5f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			UseShopPriceForUpgradeItems = ((BaseUnityPlugin)this).Config.Bind<bool>("1_UpgradeItems", "UseShopPriceForItemSelection", true, "");
			SpawnDroneItems = ((BaseUnityPlugin)this).Config.Bind<bool>("2_DroneItems", "SpawnDroneItems", true, "");
			MapHideDroneItems = ((BaseUnityPlugin)this).Config.Bind<bool>("2_DroneItems", "MapHideDroneItems", false, "");
			DroneItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind<float>("2_DroneItems", "DroneItemsSpawnChance", 0.95f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			UseShopPriceForDroneItems = ((BaseUnityPlugin)this).Config.Bind<bool>("2_DroneItems", "UseShopPriceForItemSelection", true, "");
			SpawnOtherShopItems = ((BaseUnityPlugin)this).Config.Bind<bool>("3_OtherShopItems", "SpawnOtherShopItems", true, "");
			MapHideOtherShopItems = ((BaseUnityPlugin)this).Config.Bind<bool>("3_OtherShopItems", "MapHideOtherShopItems", false, "");
			OtherShopItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind<float>("3_OtherShopItems", "OtherShopItemSpawnChance", 1.5f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			UseShopPriceForOtherItems = ((BaseUnityPlugin)this).Config.Bind<bool>("3_OtherShopItems", "UseShopPriceForItemSelection", true, "");
			SpawnHealthPacksFromEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("4_HealthPacks", "SpawnHealthPacksFromEnemies", true, "");
			HealthPackDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("4_HealthPacks", "HealthPackDropChance", 100f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MinItemValue = ((BaseUnityPlugin)this).Config.Bind<float>("5_Filters", "MinItemValue", 0f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 999999f), Array.Empty<object>()));
			MaxItemValue = ((BaseUnityPlugin)this).Config.Bind<float>("5_Filters", "MaxItemValue", 0f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 999999f), Array.Empty<object>()));
			MaxItemsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("5_Filters", "MaxItemsPerLevel", 0, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>()));
			((MonoBehaviour)this).StartCoroutine(PopulateAllowListWhenReady());
		}

		[IteratorStateMachine(typeof(<PopulateAllowListWhenReady>d__28))]
		private IEnumerator PopulateAllowListWhenReady()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PopulateAllowListWhenReady>d__28(0)
			{
				<>4__this = this
			};
		}

		private static bool PassesValueFilter(Item item)
		{
			float num = (((Object)(object)item.value != (Object)null) ? item.value.valueMin : 0f);
			if (MinItemValue.Value > 0f && num < MinItemValue.Value)
			{
				return false;
			}
			if (MaxItemValue.Value > 0f && num > MaxItemValue.Value)
			{
				return false;
			}
			return true;
		}

		private static bool IsItemAllowed(Item item)
		{
			if (ItemAllowList == null)
			{
				return true;
			}
			string name = ((Object)item).name;
			ConfigEntry<bool> value;
			return !ItemAllowList.TryGetValue(name, out value) || value.Value;
		}

		private static List<Item> GetCandidates(Func<Item, bool> typeFilter)
		{
			if (StatsManager.instance?.itemDictionary == null)
			{
				return new List<Item>();
			}
			return (from i in StatsManager.instance.itemDictionary.Values.Where(typeFilter)
				where (Object)(object)i.value != (Object)null && i.value.valueMin > 0f
				select i).Where(PassesValueFilter).Where(IsItemAllowed).ToList();
		}

		private static Item WeightedOrRandom(List<Item> list, bool usePrice)
		{
			if (!usePrice)
			{
				return list[Random.Range(0, list.Count)];
			}
			float num = list.Sum((Item i) => 1f / i.value.valueMin);
			if (num <= 0f)
			{
				return null;
			}
			float num2 = Random.Range(0f, num);
			foreach (Item item in list)
			{
				num2 -= 1f / item.value.valueMin;
				if (num2 <= 0f)
				{
					return item;
				}
			}
			return null;
		}

		private static bool GetRandomItemOfType(itemType type, out Item item)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			item = null;
			List<Item> candidates = GetCandidates((Item i) => i.itemType == type);
			if (candidates.Count == 0)
			{
				return false;
			}
			item = candidates[Random.Range(0, candidates.Count)];
			return true;
		}

		private static bool GetRandomOtherShopItem(out Item item)
		{
			item = null;
			List<Item> candidates = GetCandidates((Item i) => !KnownSpecialTypes.Contains((int)i.itemType));
			if (candidates.Count == 0)
			{
				return false;
			}
			item = WeightedOrRandom(candidates, UseShopPriceForOtherItems.Value);
			return (Object)(object)item != (Object)null;
		}

		private static GameObject SpawnItem(Item item, Vector3 position, Quaternion rotation)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate("Items/" + ((Object)item).name, position, rotation, (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)item.prefab).Prefab, position, rotation));
			val.AddComponent<SpawnedItemTracker>();
			return val;
		}

		private static bool HasValuablePropSwitch(ValuableVolume volume)
		{
			return (Object)(object)((Component)volume).GetComponentInParent<ValuablePropSwitch>() != (Object)null;
		}

		private static bool ShouldSpawnItem(ValuableVolume volume, out string category)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected I4, but got Unknown
			category = null;
			if (HasValuablePropSwitch(volume))
			{
				return false;
			}
			switch ((int)volume.VolumeType)
			{
			case 0:
				if (!SpawnUpgradeItems.Value)
				{
					return false;
				}
				category = "upgrade";
				return Random.Range(0f, 100f) <= UpgradeItemSpawnChance.Value;
			case 1:
				if (!SpawnDroneItems.Value)
				{
					return false;
				}
				category = "drone";
				return Random.Range(0f, 100f) <= DroneItemSpawnChance.Value;
			default:
				if (!SpawnOtherShopItems.Value)
				{
					return false;
				}
				category = "other";
				return Random.Range(0f, 100f) <= OtherShopItemSpawnChance.Value;
			}
		}

		private static bool RandomItemSpawn(ValuableVolume volume)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldSpawnItem(volume, out var category))
			{
				return false;
			}
			if (!((category == "upgrade") ? GetRandomItemOfType((itemType)3, out var item) : ((!(category == "drone")) ? GetRandomOtherShopItem(out item) : GetRandomItemOfType((itemType)0, out item))))
			{
				return false;
			}
			SpawnItem(item, ((Component)volume).transform.position, ((Component)volume).transform.rotation);
			return true;
		}

		[HarmonyPatch(typeof(ValuableDirector), "VolumesAndSwitchSetup")]
		[HarmonyPostfix]
		public static void ValuableDirector_VolumesAndSwitchSetup_Postfix()
		{
			if (!SemiFunc.RunIsLevel())
			{
				return;
			}
			List<ValuableVolume> list = (from v in Object.FindObjectsOfType<ValuableVolume>(false)
				where (Object)(object)((Component)v).gameObject.GetComponent<UsedVolumeTracker>() == (Object)null
				where !HasValuablePropSwitch(v)
				select v).ToList();
			Logger.LogInfo((object)$"Found {list.Count} volumes (upgrade:{UpgradeItemSpawnChance.Value}%, drone:{DroneItemSpawnChance.Value}%, other:{OtherShopItemSpawnChance.Value}%)");
			int value = MaxItemsPerLevel.Value;
			int num = 0;
			foreach (ValuableVolume item in list)
			{
				if (value > 0 && num >= value)
				{
					break;
				}
				if (RandomItemSpawn(item))
				{
					num++;
				}
			}
			Logger.LogInfo((object)$"Spawned {num} items total");
		}

		[HarmonyPatch(typeof(Map), "AddCustom")]
		[HarmonyPostfix]
		public static void Map_AddCustom_Postfix(MapCustom mapCustom)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected I4, but got Unknown
			ItemAttributes val = default(ItemAttributes);
			if (SemiFunc.RunIsLevel() && ((Component)mapCustom).gameObject.TryGetComponent<ItemAttributes>(ref val))
			{
				int num = (int)val.item.itemType;
				if ((num == 3 && MapHideUpgradeItems.Value) || (num == 0 && MapHideDroneItems.Value) || (!KnownSpecialTypes.Contains(num) && MapHideOtherShopItems.Value))
				{
					((Component)mapCustom).gameObject.SetActive(false);
				}
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInHaulList")]
		[HarmonyPostfix]
		public static void ExtractionPoint_DestroyAllPhysObjectsInHaulList_Postfix(ExtractionPoint __instance)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			SpawnedItemTracker[] array = Object.FindObjectsOfType<SpawnedItemTracker>(false);
			int num = 0;
			SpawnedItemTracker[] array2 = array;
			foreach (SpawnedItemTracker spawnedItemTracker in array2)
			{
				GameObject gameObject = ((Component)spawnedItemTracker).gameObject;
				bool flag = false;
				RoomVolumeCheck component = gameObject.GetComponent<RoomVolumeCheck>();
				if ((Object)(object)component != (Object)null && component.CurrentRooms.Any((RoomVolume r) => r.Extraction))
				{
					flag = true;
					DebugLog("Item " + ((Object)gameObject).name + " is inside extraction zone");
				}
				PhysGrabObject component2 = gameObject.GetComponent<PhysGrabObject>();
				if ((Object)(object)component2 != (Object)null && component2.grabbed)
				{
					flag = true;
					DebugLog("Item " + ((Object)gameObject).name + " is grabbed by a player");
				}
				if (!flag)
				{
					continue;
				}
				ItemAttributes component3 = gameObject.GetComponent<ItemAttributes>();
				if ((Object)(object)component3 == (Object)null || (Object)(object)component3.item == (Object)null)
				{
					DebugLog("Item " + ((Object)gameObject).name + " has no ItemAttributes, skipping");
					continue;
				}
				string name = ((Object)component3.item).name;
				Logger.LogInfo((object)("Saving extracted item: " + name));
				StatsManager.instance.ItemPurchase(name);
				PhysGrabObject component4 = gameObject.GetComponent<PhysGrabObject>();
				if (component4 != null)
				{
					component4.DestroyPhysGrabObject();
				}
				num++;
			}
			Logger.LogInfo((object)$"Saved {num} items from extraction");
		}

		[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
		[HarmonyPrefix]
		public static void RunManager_ChangeLevel_Prefix(RunManager __instance, bool _completedLevel, bool _levelFailed, int _changeLevelType)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || !_completedLevel || _levelFailed || !SaveAllOnLevelComplete.Value || _changeLevelType == 4)
			{
				return;
			}
			SpawnedItemTracker[] array = Object.FindObjectsOfType<SpawnedItemTracker>(false);
			if (array.Length == 0)
			{
				return;
			}
			Logger.LogInfo((object)$"Level completed. Saving {array.Length} remaining spawned items as purchased (fallback)");
			int num = 0;
			SpawnedItemTracker[] array2 = array;
			foreach (SpawnedItemTracker spawnedItemTracker in array2)
			{
				GameObject gameObject = ((Component)spawnedItemTracker).gameObject;
				ItemAttributes component = gameObject.GetComponent<ItemAttributes>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.item == (Object)null))
				{
					string name = ((Object)component.item).name;
					DebugLog("Fallback save: " + name);
					StatsManager.instance.ItemPurchase(name);
					num++;
				}
			}
			Logger.LogInfo((object)$"Fallback saved {num} items");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "REPO_All_Shop_Items_in_Level";

		public const string PLUGIN_NAME = "ALL Shop Items spawn in Level";

		public const string PLUGIN_VERSION = "1.7.23";
	}
}