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] [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 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.8.0")] [BepInProcess("REPO.exe")] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(EnemyParent), "Despawn")] private class DespawnPatch { private static int GetSpawnValuableCurrent(EnemyParent ep) { try { object? obj = AccessTools.Field(typeof(EnemyParent), "Enemy")?.GetValue(ep); Enemy obj2 = (Enemy)((obj is Enemy) ? obj : null); object? obj3 = AccessTools.Field(typeof(Enemy), "Health")?.GetValue(obj2); EnemyHealth obj4 = (EnemyHealth)((obj3 is EnemyHealth) ? obj3 : null); FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyHealth), "spawnValuableCurrent"); return (!(fieldInfo == null)) ? ((int)fieldInfo.GetValue(obj4)) : 0; } catch (Exception ex) { DebugLog("Error reading spawnValuableCurrent: " + ex.Message); return 0; } } private static itemType GetLootTypeForEnemy(EnemyParent ep) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (!EnableEnemyLootTables.Value) { return (itemType)8; } string text = ((Object)ep).name?.ToLower() ?? ""; if (text.Contains("gnome") || text.Contains("small")) { return (itemType)3; } if (text.Contains("large") || text.Contains("boss")) { return (itemType)0; } return (itemType)8; } private static void Prefix(EnemyParent __instance, out int __state) { __state = GetSpawnValuableCurrent(__instance); } private static void Postfix(EnemyParent __instance, int __state) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0177: 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) { return; } itemType lootTypeForEnemy = GetLootTypeForEnemy(__instance); if ((!GetRandomItemOfType(lootTypeForEnemy, out var item) && !GetRandomItemOfType((itemType)8, out item)) || (Object)(object)item == (Object)null) { return; } try { object? obj = AccessTools.Field(typeof(EnemyParent), "Enemy")?.GetValue(__instance); Enemy val = (Enemy)((obj is Enemy) ? obj : null); if ((Object)(object)val == (Object)null) { return; } Transform val2 = (Object.op_Implicit((Object)(object)val.CustomValuableSpawnTransform) ? val.CustomValuableSpawnTransform : val.CenterTransform); if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = val2.position + Vector3.up; if (!IsPositionValid(val3)) { DebugLog("Enemy " + ((Object)__instance).name + " has invalid spawn position, skipping drop"); return; } SpawnItem(item, val3, Quaternion.identity); DebugLog($"Dropped {((Object)item).name} from {((Object)__instance).name} ({lootTypeForEnemy})"); } } catch (Exception ex) { DebugLog("Error spawning enemy loot: " + ex.Message); } } } [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().FirstOrDefault(); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.AddComponent(); } } } internal static ManualLogSource Logger; internal static ConfigEntry DebugMode; internal static ConfigEntry SaveAllOnLevelComplete; internal static ConfigEntry SpawnUpgradeItems; internal static ConfigEntry UpgradeItemSpawnChance; internal static ConfigEntry MapHideUpgradeItems; internal static ConfigEntry UseShopPriceForUpgradeItems; internal static ConfigEntry SpawnDroneItems; internal static ConfigEntry DroneItemSpawnChance; internal static ConfigEntry MapHideDroneItems; internal static ConfigEntry UseShopPriceForDroneItems; internal static ConfigEntry SpawnOtherShopItems; internal static ConfigEntry OtherShopItemSpawnChance; internal static ConfigEntry MapHideOtherShopItems; internal static ConfigEntry UseShopPriceForOtherItems; internal static ConfigEntry SpawnHealthPacksFromEnemies; internal static ConfigEntry HealthPackDropChance; internal static ConfigEntry MinItemValue; internal static ConfigEntry MaxItemValue; internal static ConfigEntry MaxItemsPerLevel; internal static ConfigEntry MinItemsPerLevel; internal static ConfigEntry EnableLevelScaling; internal static ConfigEntry LevelScalingMultiplier; internal static ConfigEntry AnnounceRareItems; internal static ConfigEntry AnnounceValueThreshold; internal static ConfigEntry EnableEnemyLootTables; internal static List> DisallowedUpgradeItems; internal static List> DisallowedDroneItems; internal static List> DisallowedOtherItems; private static readonly HashSet KnownSpecialTypes = new HashSet { 0, 3, 8 }; private static Dictionary _cachedItemDictionary; private static List _cachedUpgrades; private static List _cachedDrones; private static List _cachedOtherItems; private static float _lastItemRefreshTime; private static bool _isSpawning = false; 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 //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Expected O, but got Unknown //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Expected O, but got Unknown //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_047e: 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("0_Debug", "DebugMode", false, "Enable verbose debug logging."); SaveAllOnLevelComplete = ((BaseUnityPlugin)this).Config.Bind("0_Debug", "SaveAllOnLevelComplete", true, "If true, all spawned items still in the level are saved when you complete a level."); SpawnUpgradeItems = ((BaseUnityPlugin)this).Config.Bind("1_UpgradeItems", "SpawnUpgradeItems", true, "Whether upgrade items can spawn in levels"); MapHideUpgradeItems = ((BaseUnityPlugin)this).Config.Bind("1_UpgradeItems", "MapHideShopUpgradeItems", false, "(Client) Whether upgrade items are hidden on the map"); UpgradeItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind("1_UpgradeItems", "UpgradeItemSpawnChance", 2.5f, new ConfigDescription("% chance for an upgrade item to spawn", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); UseShopPriceForUpgradeItems = ((BaseUnityPlugin)this).Config.Bind("1_UpgradeItems", "UseShopPriceForItemSelection", true, "If ON: Cheaper upgrade items appear more often."); SpawnDroneItems = ((BaseUnityPlugin)this).Config.Bind("2_DroneItems", "SpawnDroneItems", true, "Whether drone items can spawn in levels"); MapHideDroneItems = ((BaseUnityPlugin)this).Config.Bind("2_DroneItems", "MapHideDroneItems", false, "(Client) Whether drone items are hidden on the map"); DroneItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind("2_DroneItems", "DroneItemsSpawnChance", 0.95f, new ConfigDescription("% chance for a drone item to spawn", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); UseShopPriceForDroneItems = ((BaseUnityPlugin)this).Config.Bind("2_DroneItems", "UseShopPriceForItemSelection", true, "If ON: Cheaper drone items appear more often."); SpawnOtherShopItems = ((BaseUnityPlugin)this).Config.Bind("3_OtherShopItems", "SpawnOtherShopItems", true, "Whether other shop items can spawn in levels"); MapHideOtherShopItems = ((BaseUnityPlugin)this).Config.Bind("3_OtherShopItems", "MapHideOtherShopItems", false, "(Client) Whether other shop items are hidden on the map"); OtherShopItemSpawnChance = ((BaseUnityPlugin)this).Config.Bind("3_OtherShopItems", "OtherShopItemSpawnChance", 1.5f, new ConfigDescription("% chance for other shop items to spawn", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); UseShopPriceForOtherItems = ((BaseUnityPlugin)this).Config.Bind("3_OtherShopItems", "UseShopPriceForItemSelection", true, "If ON: Cheaper items appear more often."); SpawnHealthPacksFromEnemies = ((BaseUnityPlugin)this).Config.Bind("4_HealthPacks", "SpawnHealthPacksFromEnemies", true, "Whether health packs can spawn when enemies die"); HealthPackDropChance = ((BaseUnityPlugin)this).Config.Bind("4_HealthPacks", "HealthPackDropChance", 100f, new ConfigDescription("% chance for a health pack to drop when an enemy dies", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); MinItemValue = ((BaseUnityPlugin)this).Config.Bind("5_Filters", "MinItemValue", 0f, new ConfigDescription("Minimum item value for spawning (0 = disabled)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999999f), Array.Empty())); MaxItemValue = ((BaseUnityPlugin)this).Config.Bind("5_Filters", "MaxItemValue", 0f, new ConfigDescription("Maximum item value for spawning (0 = disabled)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999999f), Array.Empty())); MaxItemsPerLevel = ((BaseUnityPlugin)this).Config.Bind("5_Filters", "MaxItemsPerLevel", 0, new ConfigDescription("Maximum items to spawn per level (0 = unlimited)", (AcceptableValueBase)(object)new AcceptableValueRange(0, 999), Array.Empty())); MinItemsPerLevel = ((BaseUnityPlugin)this).Config.Bind("5_Filters", "MinItemsPerLevel", 0, new ConfigDescription("Minimum items guaranteed to spawn per level (0 = disabled)", (AcceptableValueBase)(object)new AcceptableValueRange(0, 999), Array.Empty())); EnableLevelScaling = ((BaseUnityPlugin)this).Config.Bind("6_LevelScaling", "EnableLevelScaling", true, "Spawn chances scale up as you progress through floors"); LevelScalingMultiplier = ((BaseUnityPlugin)this).Config.Bind("6_LevelScaling", "LevelScalingMultiplier", 0.1f, new ConfigDescription("Bonus multiplier per floor completed (0.1 = +10% per floor)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); AnnounceRareItems = ((BaseUnityPlugin)this).Config.Bind("7_Announce", "AnnounceRareItems", true, "Log to console when an expensive item spawns"); AnnounceValueThreshold = ((BaseUnityPlugin)this).Config.Bind("7_Announce", "AnnounceValueThreshold", 5000f, new ConfigDescription("Min item value to trigger announce", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999999f), Array.Empty())); EnableEnemyLootTables = ((BaseUnityPlugin)this).Config.Bind("8_EnemyLoot", "EnableEnemyLootTables", true, "Specific enemy types drop specific item types instead of always health packs"); DisallowedUpgradeItems = new List>(); DisallowedDroneItems = new List>(); DisallowedOtherItems = new List>(); ((MonoBehaviour)this).StartCoroutine(InitializeItemListsWhenReady()); } private IEnumerator InitializeItemListsWhenReady() { float startTime = Time.time; float timeout = 30f; while ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null || StatsManager.instance.itemDictionary.Count == 0) { if (Time.time - startTime > timeout) { Logger.LogError((object)"StatsManager failed to initialize within timeout - item filtering disabled"); yield break; } yield return null; } Logger.LogInfo((object)"Initializing per-item disable lists"); _cachedItemDictionary = StatsManager.instance.itemDictionary; foreach (Item item in _cachedItemDictionary.Values) { string itemName = ((Object)item).name; itemType itemType = item.itemType; itemType val = itemType; itemType val2 = val; if ((int)val2 != 0) { if ((int)val2 == 3) { ConfigEntry upgradeEntry = ((BaseUnityPlugin)this).Config.Bind("9_DisallowedItems_Upgrades", itemName, false, new ConfigDescription("Disable '" + itemName + "' from spawning in levels", (AcceptableValueBase)null, Array.Empty())); if (upgradeEntry.Value) { DisallowedUpgradeItems.Add(upgradeEntry); } } else if (!KnownSpecialTypes.Contains((int)item.itemType) && (Object)(object)item.value != (Object)null && item.value.valueMin > 0f) { ConfigEntry otherEntry = ((BaseUnityPlugin)this).Config.Bind("9_DisallowedItems_OtherShop", itemName, false, new ConfigDescription("Disable '" + itemName + "' from spawning in levels", (AcceptableValueBase)null, Array.Empty())); if (otherEntry.Value) { DisallowedOtherItems.Add(otherEntry); } } } else { ConfigEntry droneEntry = ((BaseUnityPlugin)this).Config.Bind("9_DisallowedItems_Drones", itemName, false, new ConfigDescription("Disable '" + itemName + "' from spawning in levels", (AcceptableValueBase)null, Array.Empty())); if (droneEntry.Value) { DisallowedDroneItems.Add(droneEntry); } } } Logger.LogInfo((object)$"Initialized disallows: Upgrades={DisallowedUpgradeItems.Count} Drones={DisallowedDroneItems.Count} Other={DisallowedOtherItems.Count}"); RefreshCachedItemLists(); } private static void RefreshCachedItemLists() { if (StatsManager.instance?.itemDictionary != null) { _cachedItemDictionary = StatsManager.instance.itemDictionary; _cachedUpgrades = GetCandidates((Item i) => (int)i.itemType == 3); _cachedDrones = GetCandidates((Item i) => (int)i.itemType == 0); _cachedOtherItems = GetCandidates((Item i) => !KnownSpecialTypes.Contains((int)i.itemType)); _lastItemRefreshTime = Time.time; DebugLog($"Cache refreshed: {_cachedUpgrades.Count} upgrades, {_cachedDrones.Count} drones, {_cachedOtherItems.Count} other"); } } 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, List> disallowedList) { string key = ((Object)item).name; return !disallowedList.Any((ConfigEntry entry) => ((ConfigEntryBase)entry).Definition.Key == key && entry.Value); } private static List GetCandidates(Func typeFilter) { if (StatsManager.instance?.itemDictionary == null) { return new List(); } 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).ToList(); } private static Item WeightedOrRandom(List list, bool usePrice) { if (list == null || list.Count == 0) { return null; } if (!usePrice) { return list[Random.Range(0, list.Count)]; } float num = list.Sum((Item i) => 1f / Mathf.Max(0.01f, i.value.valueMin)); if (num <= 0f || float.IsNaN(num) || float.IsInfinity(num)) { return list[Random.Range(0, list.Count)]; } int num2 = 0; while (num2++ < 50) { float num3 = Random.Range(0f, num); foreach (Item item in list) { num3 -= 1f / Mathf.Max(0.01f, item.value.valueMin); if (num3 <= 0f) { return item; } } } return list[0]; } private static float GetScaledChance(float baseChance) { if (!EnableLevelScaling.Value) { return baseChance; } if ((Object)(object)RunManager.instance == (Object)null) { return baseChance; } int levelsCompleted = RunManager.instance.levelsCompleted; return Mathf.Min(baseChance * (1f + (float)levelsCompleted * LevelScalingMultiplier.Value), 100f); } private static bool IsPositionValid(Vector3 pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (float.IsNaN(pos.x) || float.IsNaN(pos.y) || float.IsNaN(pos.z)) { return false; } if (pos == Vector3.zero) { return false; } return true; } private static bool IsVolumeStillValid(ValuableVolume volume) { if ((Object)(object)volume == (Object)null) { return false; } if ((Object)(object)volume == (Object)null || (Object)(object)((Component)volume).gameObject == (Object)null) { return false; } if (!((Component)volume).gameObject.activeInHierarchy) { return false; } return true; } private static void AnnounceItem(Item item, Vector3 position) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (AnnounceRareItems.Value && !((Object)(object)item?.value == (Object)null) && !(item.value.valueMin < AnnounceValueThreshold.Value)) { Logger.LogInfo((object)$"[RARE SPAWN] {((Object)item).name} (value ~{item.value.valueMin}) at {position}"); } } private static bool GetRandomItemOfType(itemType type, out Item item, bool usePriceWeighting = true) { //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) //IL_0015: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 item = null; List list = null; List> disallowedList = null; if ((int)type != 0) { if ((int)type != 3) { if ((int)type == 8) { list = GetCandidates((Item i) => (int)i.itemType == 8); disallowedList = null; usePriceWeighting = true; } else { list = _cachedOtherItems; disallowedList = DisallowedOtherItems; usePriceWeighting = UseShopPriceForOtherItems.Value; } } else { list = _cachedUpgrades; disallowedList = DisallowedUpgradeItems; usePriceWeighting = UseShopPriceForUpgradeItems.Value; } } else { list = _cachedDrones; disallowedList = DisallowedDroneItems; usePriceWeighting = UseShopPriceForDroneItems.Value; } if (list == null || list.Count == 0) { return false; } if (disallowedList != null && disallowedList.Count > 0) { list = list.Where((Item i) => IsItemAllowed(i, disallowedList)).ToList(); if (list.Count == 0) { return false; } } if (Time.time - _lastItemRefreshTime > 60f) { RefreshCachedItemLists(); } item = WeightedOrRandom(list, usePriceWeighting); return (Object)(object)item != (Object)null; } private static GameObject SpawnItem(Item item, Vector3 position, Quaternion rotation) { //IL_002d: 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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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) if ((Object)(object)item == (Object)null || item.prefab == null) { DebugLog("Spawn failed: null item or prefab"); return null; } if (!IsPositionValid(position)) { DebugLog($"Spawn of {((Object)item).name} rejected: bad position {position}"); return null; } if (_isSpawning) { DebugLog("Spawn of " + ((Object)item).name + " blocked: already inside a spawn call"); return null; } _isSpawning = true; try { string text = "Items/" + ((Object)item).name; GameObject val; if (SemiFunc.IsMultiplayer()) { if (!PhotonNetwork.IsConnected || !PhotonNetwork.InRoom) { DebugLog("Spawn of " + ((Object)item).name + " blocked: not in a Photon room"); return null; } val = PhotonNetwork.InstantiateRoomObject(text, position, rotation, (byte)0, (object[])null); } else { val = Object.Instantiate(((PrefabRef)(object)item.prefab).Prefab, position, rotation); } if ((Object)(object)val != (Object)null) { val.AddComponent(); AnnounceItem(item, position); DebugLog($"Spawned {((Object)item).name} at {position}"); } return val; } catch (Exception ex) { DebugLog("Spawn of " + ((Object)item).name + " threw: " + ex.Message); return null; } finally { _isSpawning = false; } } private static bool HasValuablePropSwitch(ValuableVolume volume) { return (Object)(object)((Component)volume).GetComponentInParent() != (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) <= GetScaledChance(UpgradeItemSpawnChance.Value); case 1: if (!SpawnDroneItems.Value) { return false; } category = "drone"; return Random.Range(0f, 100f) <= GetScaledChance(DroneItemSpawnChance.Value); default: if (!SpawnOtherShopItems.Value) { return false; } category = "other"; return Random.Range(0f, 100f) <= GetScaledChance(OtherShopItemSpawnChance.Value); } } private static bool RandomItemSpawn(ValuableVolume volume) { //IL_009a: 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) if (!IsVolumeStillValid(volume)) { return false; } if (!ShouldSpawnItem(volume, out var category)) { return false; } Item item; bool flag = ((category == "upgrade") ? GetRandomItemOfType((itemType)3, out item) : ((!(category == "drone")) ? GetRandomItemOfType((itemType)999, out item) : GetRandomItemOfType((itemType)0, out item))); if (!flag || (Object)(object)item == (Object)null) { return false; } return (Object)(object)SpawnItem(item, ((Component)volume).transform.position, ((Component)volume).transform.rotation) != (Object)null; } [HarmonyPatch(typeof(ValuableDirector), "VolumesAndSwitchSetup")] [HarmonyPostfix] public static void ValuableDirector_VolumesAndSwitchSetup_Postfix() { //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.RunIsLevel() || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } RefreshCachedItemLists(); List list = (from v in Object.FindObjectsOfType(false) where (Object)(object)((Component)v).gameObject.GetComponent() == (Object)null where !HasValuablePropSwitch(v) select v).ToList(); Logger.LogInfo((object)$"Found {list.Count} volumes | upgrade:{GetScaledChance(UpgradeItemSpawnChance.Value):F1}% drone:{GetScaledChance(DroneItemSpawnChance.Value):F1}% other:{GetScaledChance(OtherShopItemSpawnChance.Value):F1}%"); Logger.LogInfo((object)$"Item pool: {_cachedUpgrades?.Count ?? 0} upgrades | {_cachedDrones?.Count ?? 0} drones | {_cachedOtherItems?.Count ?? 0} other"); int value = MaxItemsPerLevel.Value; int value2 = MinItemsPerLevel.Value; int num = 0; foreach (ValuableVolume item2 in list) { if (value > 0 && num >= value) { break; } if (RandomItemSpawn(item2)) { num++; } } if (value2 > 0 && num < value2) { List list2 = list.Where((ValuableVolume v) => (Object)(object)((Component)v).gameObject.GetComponent() == (Object)null).ToList(); for (int num2 = list2.Count - 1; num2 > 0; num2--) { int num3 = Random.Range(0, num2 + 1); List list3 = list2; int index = num2; int index2 = num3; ValuableVolume value3 = list2[num3]; ValuableVolume value4 = list2[num2]; list3[index] = value3; list2[index2] = value4; } foreach (ValuableVolume item3 in list2) { if (num >= value2 || (value > 0 && num >= value)) { break; } if (IsVolumeStillValid(item3) && (GetRandomItemOfType((itemType)3, out var item) || GetRandomItemOfType((itemType)0, out item) || GetRandomItemOfType((itemType)999, out item)) && !((Object)(object)item == (Object)null)) { Component val = (Component)(object)item3; if ((Object)(object)SpawnItem(item, val.transform.position, val.transform.rotation) != (Object)null) { num++; } } } } Logger.LogInfo((object)$"Spawned {num} items (min={value2} cap={value})"); } [HarmonyPatch(typeof(Map), "AddCustom")] [HarmonyPostfix] public static void Map_AddCustom_Postfix(MapCustom mapCustom) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected I4, but got Unknown ItemAttributes val = default(ItemAttributes); if (SemiFunc.RunIsLevel() && !((Object)(object)mapCustom == (Object)null) && !((Object)(object)((Component)mapCustom).gameObject == (Object)null) && ((Component)mapCustom).gameObject.TryGetComponent(ref val) && !((Object)(object)val?.item == (Object)null)) { 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); DebugLog("Hidden " + ((Object)val.item).name + " from map"); } } } [HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInHaulList")] [HarmonyPostfix] public static void ExtractionPoint_DestroyAllPhysObjectsInHaulList_Postfix(ExtractionPoint __instance) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } SpawnedItemTracker[] array = Object.FindObjectsOfType(false); if (array.Length == 0) { return; } HashSet hashSet = new HashSet(); int num = 0; SpawnedItemTracker[] array2 = array; foreach (SpawnedItemTracker spawnedItemTracker in array2) { GameObject gameObject = ((Component)spawnedItemTracker).gameObject; if ((Object)(object)gameObject == (Object)null) { continue; } bool flag = false; RoomVolumeCheck component = gameObject.GetComponent(); if (component?.CurrentRooms != null && component.CurrentRooms.Any((RoomVolume r) => (Object)(object)r != (Object)null && r.Extraction)) { flag = true; } PhysGrabObject component2 = gameObject.GetComponent(); if ((Object)(object)component2 != (Object)null && component2.grabbed) { flag = true; } if (!flag) { continue; } ItemAttributes component3 = gameObject.GetComponent(); if ((Object)(object)component3?.item == (Object)null) { continue; } string name = ((Object)component3.item).name; if (hashSet.Contains(name)) { continue; } if ((Object)(object)StatsManager.instance == (Object)null) { DebugLog("StatsManager is null during extraction, skipping " + name); continue; } Logger.LogInfo((object)("Saving extracted item: " + name)); StatsManager.instance.ItemPurchase(name); hashSet.Add(name); if ((Object)(object)component2 != (Object)null) { component2.DestroyPhysGrabObject(); } num++; } Logger.LogInfo((object)$"Saved {num} unique 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(false); if (array.Length == 0) { return; } Logger.LogInfo((object)$"Level complete. Fallback-saving {array.Length} spawned items..."); HashSet hashSet = new HashSet(); int num = 0; SpawnedItemTracker[] array2 = array; foreach (SpawnedItemTracker spawnedItemTracker in array2) { GameObject gameObject = ((Component)spawnedItemTracker).gameObject; if ((Object)(object)gameObject == (Object)null) { continue; } ItemAttributes component = gameObject.GetComponent(); if ((Object)(object)component?.item == (Object)null) { continue; } string name = ((Object)component.item).name; if (!hashSet.Contains(name)) { PhysGrabObject component2 = gameObject.GetComponent(); if ((Object)(object)component2 != (Object)null && component2.grabbed) { DebugLog("Saving carried item during level transition: " + name); } if ((Object)(object)StatsManager.instance == (Object)null) { DebugLog("StatsManager null during ChangeLevel save, skipping " + name); continue; } StatsManager.instance.ItemPurchase(name); hashSet.Add(name); num++; } } Logger.LogInfo((object)$"Fallback saved {num} unique 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.8.0"; } }