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 ShopCurator v1.1.0
ShopCurator.dll
Decompiled 2 weeks agousing System; 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.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("hvg-solutions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f922c64c9d228f7d56872f53b8e4331b41a6c3dd")] [assembly: AssemblyProduct("ShopCurator")] [assembly: AssemblyTitle("ShopCurator")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 ShopCurator { [HarmonyPatch(typeof(PunManager), "SpawnShopItem")] internal static class ForcePlacementPatch { [HarmonyPrefix] private static bool Prefix(ItemVolume itemVolume, List<Item> itemList, ref int spawnCount, bool isSecret, ref bool __result) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.IgnoreItemSizes.Value) { return true; } ShopManager instance = ShopManager.instance; if ((Object)(object)instance == (Object)null || itemList != instance.potentialItems) { return true; } if (ShopSpawn.IsDedicatedShelf(itemVolume.itemVolume) || itemList.Count == 0) { __result = false; return false; } int num = -1; for (int num2 = itemList.Count - 1; num2 >= 0; num2--) { if (itemList[num2].itemVolume == itemVolume.itemVolume) { num = num2; break; } } if (num < 0) { num = itemList.Count - 1; } Item item = itemList[num]; ShopSpawn.Place(instance, itemVolume, item); itemList.RemoveAt(num); if (!isSecret) { spawnCount++; } __result = true; return false; } } [HarmonyPatch(typeof(PunManager), "ShopPopulateItemVolumes")] internal static class GuaranteedSpawnPatch { [HarmonyPrefix] private static void Prefix() { //IL_0049: 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_005d: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || SemiFunc.IsNotMasterClient() || !ModConfig.AnyGuarantees()) { return; } ShopManager instance = ShopManager.instance; if ((Object)(object)instance == (Object)null) { return; } int num = 0; bool flag = false; foreach (itemType type in ModConfig.GuaranteedTypes()) { if (flag) { break; } int minCount = ModConfig.GetMinCount(type); List<Item> list = PoolForType(instance, type); List<Item> list2 = list.Where((Item it) => (Object)(object)it != (Object)null && it.itemType == type).ToList(); if (list2.Count == 0) { ShopCurator.Logger.LogWarning((object)$"Guaranteed: no '{type}' items available in the shop this run — skipped."); continue; } int num2 = 0; for (int num3 = 0; num3 < minCount; num3++) { Item val = list2[num3 % list2.Count]; ItemVolume val2 = TakeShelf(instance.itemVolumes, val); if ((Object)(object)val2 == (Object)null) { if (!ShopSpawn.IsDedicatedShelf(val.itemVolume)) { flag = true; } break; } ShopSpawn.Place(instance, val2, val); Object.Destroy((Object)(object)((Component)val2).gameObject); list.Remove(val); num2++; num++; } ShopCurator.Logger.LogInfo((object)$"Guaranteed: {type} x{num2} requested {minCount} (found {list2.Count} in pool)."); } if (flag) { ShopCurator.Logger.LogWarning((object)"Guaranteed: ran out of shelves before all minimums were met."); } ShopCurator.Logger.LogInfo((object)$"Guaranteed spawns placed: {num} total."); } private static List<Item> PoolForType(ShopManager sm, itemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)type != 3) { if ((int)type != 5) { if ((int)type == 8) { return sm.potentialItemHealthPacks; } return sm.potentialItems; } return sm.potentialItemConsumables; } return sm.potentialItemUpgrades; } private static ItemVolume? TakeShelf(List<ItemVolume> shelves, Item item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) itemVolume itemVolume = item.itemVolume; for (int i = 0; i < shelves.Count; i++) { ItemVolume val = shelves[i]; if ((Object)(object)val != (Object)null && val.itemVolume == itemVolume) { shelves.RemoveAt(i); return val; } } if (!ShopSpawn.IsDedicatedShelf(itemVolume)) { for (int j = 0; j < shelves.Count; j++) { ItemVolume val2 = shelves[j]; if ((Object)(object)val2 != (Object)null && !ShopSpawn.IsDedicatedShelf(val2.itemVolume)) { shelves.RemoveAt(j); return val2; } } } return null; } } internal static class ModConfig { internal static ConfigEntry<bool> Enabled = null; internal static ConfigEntry<bool> IgnoreItemSizes = null; internal static ConfigEntry<int> MaxShopItems = null; internal static ConfigEntry<bool> ProtectSupportItems = null; internal static ConfigEntry<bool> EnableDebugKeys = null; internal static ConfigEntry<KeyCode> PreviewKey = null; private static readonly Dictionary<itemType, ConfigEntry<int>> TypeWeights = new Dictionary<itemType, ConfigEntry<int>>(); private static readonly Dictionary<itemType, ConfigEntry<int>> TypeMinCounts = new Dictionary<itemType, ConfigEntry<int>>(); internal static void Init(ConfigFile cfg) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown Enabled = cfg.Bind<bool>("General", "Enabled", true, "Enable the mod. If false, the shop stays vanilla."); IgnoreItemSizes = cfg.Bind<bool>("General", "Ignore Item Sizes", false, "Let weapons spawn on any shelf, ignoring item size. Weights then act as a true spawn rate, so one type can fill the whole shop. Dedicated shelves (upgrades/health/crystals) stay untouched."); MaxShopItems = cfg.Bind<int>("General", "Max Shop Items", 0, new ConfigDescription("Max number of weapons in the shop. 0 = game default (~8).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 40), Array.Empty<object>())); ProtectSupportItems = cfg.Bind<bool>("General", "Protect Support Items", true, "Keep upgrades, health packs and power crystals at their normal rate: their type weights are ignored so a weapon-heavy setup can't accidentally shrink or remove them. Turn off to weight them like everything else."); EnableDebugKeys = cfg.Bind<bool>("Debug", "Enable Debug Keys", false, "Enable the shop preview key below. Off by default."); PreviewKey = cfg.Bind<KeyCode>("Debug", "Preview Key", (KeyCode)289, "Log the weighted shop contents to the console, without visiting the shop."); foreach (itemType value in Enum.GetValues(typeof(itemType))) { TypeWeights[value] = cfg.Bind<int>("Item Types", "Weight " + ((object)value/*cast due to .constrained prefix*/).ToString(), 1, new ConfigDescription("0 = never, 1 = normal, 2+ = more frequent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); } foreach (itemType value2 in Enum.GetValues(typeof(itemType))) { TypeMinCounts[value2] = cfg.Bind<int>("Guaranteed Minimum", "Min " + ((object)value2/*cast due to .constrained prefix*/).ToString(), 0, new ConfigDescription("Always spawn at least this many of this type per shop, even if shelf sizes would normally keep it rare. Drawn from the general shop pool, so this type's weight must be 1+. 0 = no guarantee.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); } } internal static int GetWeight(itemType type) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!TypeWeights.TryGetValue(type, out ConfigEntry<int> value)) { return 1; } return Mathf.Max(0, value.Value); } internal static bool IsTypeAllowed(itemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return GetWeight(type) > 0; } internal static int GetMinCount(itemType type) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!TypeMinCounts.TryGetValue(type, out ConfigEntry<int> value)) { return 0; } return Mathf.Max(0, value.Value); } internal static bool AnyGuarantees() { return TypeMinCounts.Any<KeyValuePair<itemType, ConfigEntry<int>>>((KeyValuePair<itemType, ConfigEntry<int>> kv) => kv.Value.Value > 0); } internal static IEnumerable<itemType> GuaranteedTypes() { return from kv in TypeMinCounts where kv.Value.Value > 0 select kv.Key; } internal static string WeightsSummary() { return string.Join(", ", from kv in TypeWeights where kv.Value.Value != 1 orderby ((object)kv.Key/*cast due to .constrained prefix*/).ToString() select $"{kv.Key}x{kv.Value.Value}"); } } [BepInPlugin("hvg-solutions.ShopCurator", "ShopCurator", "1.0")] public class ShopCurator : BaseUnityPlugin { internal static ShopCurator Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; ModConfig.Init(((BaseUnityPlugin)this).Config); Patch(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded."); } internal void Patch() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.EnableDebugKeys.Value && Input.GetKeyDown(ModConfig.PreviewKey.Value)) { ShopPreview.LogPreview(); } } } [HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")] internal static class ShopFilterPatch { [HarmonyPostfix] private static void Postfix(ShopManager __instance) { if (!ModConfig.Enabled.Value || SemiFunc.IsNotMasterClient()) { return; } ApplyWeights(__instance.potentialItems); foreach (List<Item> value in __instance.potentialSecretItems.Values) { ApplyWeights(value); } if (!ModConfig.ProtectSupportItems.Value) { ApplyWeights(__instance.potentialItemConsumables); ApplyWeights(__instance.potentialItemUpgrades); ApplyWeights(__instance.potentialItemHealthPacks); } if (ModConfig.MaxShopItems.Value > 0) { __instance.itemSpawnTargetAmount = ModConfig.MaxShopItems.Value; } string text = ModConfig.WeightsSummary(); ShopCurator.Logger.LogInfo((object)("Shop weighted. Non-default weights: " + ((text.Length == 0) ? "none" : text))); } private static void ApplyWeights(List<Item> items) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) List<Item> list = new List<Item>(items.Count); foreach (Item item in items) { if (!((Object)(object)item == (Object)null)) { for (int i = 0; i < ModConfig.GetWeight(item.itemType); i++) { list.Add(item); } } } items.Clear(); items.AddRange(list); for (int num = items.Count - 1; num > 0; num--) { int num2 = Random.Range(0, num + 1); int index = num; int index2 = num2; Item value = items[num2]; Item value2 = items[num]; items[index] = value; items[index2] = value2; } } } internal static class ShopPreview { internal static void LogPreview() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null || instance.itemDictionary.Count == 0) { ShopCurator.Logger.LogWarning((object)"[Preview] StatsManager not ready - start a run first."); return; } ShopCurator.Logger.LogInfo((object)"===== Shop preview (weight per type) ====="); IOrderedEnumerable<IGrouping<itemType, Item>> orderedEnumerable = from i in instance.itemDictionary.Values where (Object)(object)i != (Object)null group i by i.itemType into g orderby ((object)g.Key/*cast due to .constrained prefix*/).ToString() select g; foreach (IGrouping<itemType, Item> item in orderedEnumerable) { int weight = ModConfig.GetWeight(item.Key); string arg = weight switch { 1 => "normal", 0 => "blocked", _ => $"x{weight}", }; string arg2 = string.Join(", ", item.Select((Item i) => $"{i.itemName} <{i.itemVolume}>")); ShopCurator.Logger.LogInfo((object)$" {item.Key,-14} ({arg}) : {arg2}"); } } } internal static class ShopSpawn { internal static void Place(ShopManager sm, ItemVolume shelf, Item item) { //IL_0022: 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) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_0070: Unknown result type (might be due to invalid IL or missing references) ((Component)sm.itemRotateHelper).transform.parent = ((Component)shelf).transform; ((Component)sm.itemRotateHelper).transform.localRotation = item.spawnRotationOffset; Quaternion rotation = ((Component)sm.itemRotateHelper).transform.rotation; ((Component)sm.itemRotateHelper).transform.parent = ((Component)sm).transform; if (SemiFunc.IsMultiplayer()) { PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)item.prefab).ResourcePath, ((Component)shelf).transform.position, rotation, (byte)0, (object[])null); } else { Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)item.prefab).Prefab, ((Component)shelf).transform.position, rotation); } } internal static bool IsDedicatedShelf(itemVolume v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)v != 6 && (int)v != 7) { return (int)v == 4; } return true; } } }