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 ExtractionPointReward v1.0.0
ExtractionPointReward.dll
Decompiled 17 hours agousing 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 System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExtractionPointReward.Providers; using ExtractionPointReward.Tracking; using ExtractionPointReward.Visuals; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using RepoAPI.Game; using RepoAPI.Items; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Osmar Briones")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0f9a9f4307da8ac7dc1dad914a21f142d0514b43")] [assembly: AssemblyProduct("ExtractionPointReward")] [assembly: AssemblyTitle("ExtractionPointReward")] [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; } } } public static class GameKeyEnumExtensions { public static string GetGameKey<T>(this T value) where T : Enum { return typeof(T).GetMember(value.ToString()).First().GetCustomAttribute<GameKeyAttribute>()?.Key ?? value.ToString(); } public static bool TryFromGameKey<T>(string key, out T result) where T : Enum { foreach (T item in Enum.GetValues(typeof(T)).Cast<T>()) { if (item.GetGameKey().Equals(key, StringComparison.OrdinalIgnoreCase)) { result = item; return true; } } result = default(T); return false; } public static string[] GetAllGameKeys<T>() where T : Enum { return (from T v in Enum.GetValues(typeof(T)) select v.GetGameKey()).ToArray(); } public static T FromGameKey<T>(string key) where T : Enum { if (TryFromGameKey<T>(key, out var result)) { return result; } throw new ArgumentException("Invalid game key '" + key + "' for enum " + typeof(T).Name); } } namespace RepoAPI.Game { [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public sealed class GameKeyAttribute(string key) : Attribute() { public string Key { get; } = key; } } namespace RepoAPI.Items { public static class ItemProvider { private static readonly Random s_rng = new Random(); public static bool TrySpawnRandomItem(Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f) { //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) spawned = null; string randomKey = ItemKeysProvider.GetRandomKey(); if (string.IsNullOrEmpty(randomKey)) { return false; } return TrySpawnByKey(randomKey, position, rotation, out spawned, upwardOffset); } public static bool TrySpawn(ItemName itemName, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return TrySpawnByKey(itemName.GetGameKey(), position, rotation, out spawned, upwardOffset); } public static bool TrySpawnByKey(string key, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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) spawned = null; if (!SemiFunc.IsMasterClientOrSingleplayer()) { return false; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return false; } Dictionary<string, Item> itemDictionary = instance.itemDictionary; if (itemDictionary == null || !itemDictionary.TryGetValue(key, out var value) || (Object)(object)value == (Object)null || value.prefab == null) { return false; } Vector3 val = position + Vector3.up * upwardOffset; try { if (SemiFunc.IsMultiplayer()) { spawned = PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)value.prefab).ResourcePath, val, rotation, (byte)0, (object[])null); } else { spawned = Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)value.prefab).Prefab, val, rotation); } } catch (Exception) { return false; } ApplySpawnImpulse(spawned); return true; } public static bool TrySpawnWeightedItem(IReadOnlyList<WeightedKey> weights, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f, float dropChance = 1f) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) spawned = null; if (!EvaluateDropChance(dropChance)) { return false; } string text = ItemKeysProvider.PickWeightedKey(weights, s_rng); if (string.IsNullOrEmpty(text)) { return false; } return TrySpawnByKey(text, position, rotation, out spawned, upwardOffset); } public static bool TrySpawnWeightedItem(IEnumerable<WeightedKey> weights, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f, float dropChance = 1f) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (weights is IReadOnlyList<WeightedKey> weights2) { return TrySpawnWeightedItem(weights2, position, rotation, out spawned, upwardOffset, dropChance); } return TrySpawnWeightedItem(weights.ToList(), position, rotation, out spawned, upwardOffset, dropChance); } public static bool TrySpawnWeightedItem(ItemKeysProvider provider, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f, float dropChance = 1f) { //IL_001a: 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) spawned = null; if (provider == null) { return false; } if (!EvaluateDropChance(dropChance)) { return false; } try { return TrySpawn(provider.GetWeightedRandomKey(), position, rotation, out spawned, upwardOffset); } catch (InvalidOperationException) { return false; } } public static bool TrySpawnWeightedItem(Dictionary<ItemName, float> weights, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f, float dropChance = 1f) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return TrySpawnWeightedItem(new ItemKeysProvider(weights), position, rotation, out spawned, upwardOffset, dropChance); } public static bool TrySpawnWeightedItem(Dictionary<string, float> weights, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f, float dropChance = 1f) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) spawned = null; if (weights == null || weights.Count == 0) { return false; } if (!EvaluateDropChance(dropChance)) { return false; } string text = ItemKeysProvider.PickWeightedKey(weights, s_rng); if (string.IsNullOrEmpty(text)) { return false; } return TrySpawnByKey(text, position, rotation, out spawned, upwardOffset); } private static bool EvaluateDropChance(float dropChance) { if (dropChance <= 0f) { return false; } if (dropChance >= 1f) { return true; } double num; lock (s_rng) { num = s_rng.NextDouble(); } return num <= (double)dropChance; } private static void ApplySpawnImpulse(GameObject? go) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = default(Rigidbody); if (go != null && Object.op_Implicit((Object)(object)go) && go.TryGetComponent<Rigidbody>(ref val)) { val.AddForce(Random.insideUnitSphere * 1.25f + Vector3.up * 2f, (ForceMode)1); } } } public class ItemKeysProvider { private class WeightedKeySelector { private readonly List<(ItemName key, float threshold)> cumulativeWeights; private readonly float totalWeight; public Dictionary<ItemName, float> Weights { get; } public WeightedKeySelector(Dictionary<ItemName, float>? weights) { Weights = weights ?? new Dictionary<ItemName, float>(); cumulativeWeights = new List<(ItemName, float)>(Weights.Count); float num = 0f; foreach (KeyValuePair<ItemName, float> item in Weights.OrderBy((KeyValuePair<ItemName, float> x) => x.Key)) { if (!(item.Value <= 0f)) { num += item.Value; cumulativeWeights.Add((item.Key, num)); } } totalWeight = num; } public ItemName GetRandomKey(Random rng) { if (cumulativeWeights.Count == 0) { throw new InvalidOperationException("ItemKeysProvider weights have not been set."); } if (cumulativeWeights.Count == 1) { return cumulativeWeights[0].key; } float value = (float)(rng.NextDouble() * (double)totalWeight); int index = BinarySearchThreshold(value); return cumulativeWeights[index].key; } private int BinarySearchThreshold(float value) { int num = 0; int num2 = cumulativeWeights.Count - 1; while (num < num2) { int num3 = num + (num2 - num) / 2; if (value < cumulativeWeights[num3].threshold) { num2 = num3; } else { num = num3 + 1; } } return num; } } private static readonly Random random = new Random(); public static string[] AllKeys = GameKeyEnumExtensions.GetAllGameKeys<ItemName>(); private WeightedKeySelector selector = new WeightedKeySelector(weights); public ItemKeysProvider(Dictionary<ItemName, float>? weights) { } public ItemKeysProvider(IEnumerable<WeightedKey>? weightedKeys) : this(FromWeightedKeys(weightedKeys)) { } private static Dictionary<ItemName, float>? FromWeightedKeys(IEnumerable<WeightedKey>? weightedKeys) { if (weightedKeys == null) { return null; } Dictionary<ItemName, float> dictionary = new Dictionary<ItemName, float>(); foreach (WeightedKey weightedKey in weightedKeys) { if (weightedKey != null && GameKeyEnumExtensions.TryFromGameKey<ItemName>(weightedKey.Key, out var result)) { dictionary[result] = weightedKey.Weight; } } return dictionary; } public Dictionary<ItemName, float> GetWeights() { return new Dictionary<ItemName, float>(selector.Weights); } public void SetWeights(Dictionary<ItemName, float>? weights) { selector = new WeightedKeySelector(weights); } public void SetWeight(ItemName itemName, float weight) { Dictionary<ItemName, float> dictionary = new Dictionary<ItemName, float>(selector.Weights); dictionary[itemName] = weight; selector = new WeightedKeySelector(dictionary); } public bool RemoveWeight(ItemName itemName) { Dictionary<ItemName, float> dictionary = new Dictionary<ItemName, float>(selector.Weights); if (!dictionary.Remove(itemName)) { return false; } selector = new WeightedKeySelector(dictionary); return true; } public static string? GetRandomKey() { if (AllKeys.Length == 0) { return null; } int num; lock (random) { num = random.Next(AllKeys.Length); } return AllKeys[num]; } public ItemName GetWeightedRandomKey() { lock (random) { return selector.GetRandomKey(random); } } public string GetWeightedRandomGameKey() { return GetWeightedRandomKey().GetGameKey(); } public static string? PickWeightedKey(IReadOnlyList<WeightedKey>? weights, Random? rng = null) { if (weights == null || weights.Count == 0) { return null; } float num = 0f; for (int i = 0; i < weights.Count; i++) { WeightedKey weightedKey = weights[i]; if (weightedKey != null && weightedKey.Weight > 0f) { num += weightedKey.Weight; } } if (num <= 0f) { return null; } double num2; if (rng != null) { num2 = rng.NextDouble() * (double)num; } else { lock (random) { num2 = random.NextDouble() * (double)num; } } float num3 = 0f; for (int j = 0; j < weights.Count; j++) { WeightedKey weightedKey2 = weights[j]; if (weightedKey2 != null && !(weightedKey2.Weight <= 0f)) { num3 += weightedKey2.Weight; if (num2 <= (double)num3) { return weightedKey2.Key; } } } for (int num4 = weights.Count - 1; num4 >= 0; num4--) { WeightedKey weightedKey3 = weights[num4]; if (weightedKey3 != null && weightedKey3.Weight > 0f) { return weightedKey3.Key; } } return null; } public static string? PickWeightedKey(IEnumerable<WeightedKey>? weights, Random? rng = null) { if (weights == null) { return null; } if (weights is IReadOnlyList<WeightedKey> weights2) { return PickWeightedKey(weights2, rng); } return PickWeightedKey(weights.ToList(), rng); } public static string? PickWeightedKey(IDictionary<string, float>? weights, Random? rng = null) { if (weights == null || weights.Count == 0) { return null; } List<WeightedKey> list = new List<WeightedKey>(weights.Count); foreach (KeyValuePair<string, float> weight in weights) { list.Add(new WeightedKey(weight.Key, weight.Value)); } return PickWeightedKey(list, rng); } } public enum ItemName { [GameKey("Item Cart Cannon")] CartCannon, [GameKey("Item Cart Laser")] CartLaser, [GameKey("Item Cart Medium")] CartMedium, [GameKey("Item Cart Small")] CartSmall, [GameKey("Item Drone Battery")] DroneBattery, [GameKey("Item Drone Feather")] DroneFeather, [GameKey("Item Drone Indestructible")] DroneIndestructible, [GameKey("Item Drone Torque")] DroneTorque, [GameKey("Item Drone Zero Gravity")] DroneZeroGravity, [GameKey("Item Duck Bucket")] DuckBucket, [GameKey("Item Extraction Tracker")] ExtractionTracker, [GameKey("Item Grenade Duct Taped")] GrenadeDuctTaped, [GameKey("Item Grenade Explosive")] GrenadeExplosive, [GameKey("Item Grenade Human")] GrenadeHuman, [GameKey("Item Grenade Shockwave")] GrenadeShockwave, [GameKey("Item Grenade Stun")] GrenadeStun, [GameKey("Item Gun Handgun")] GunHandgun, [GameKey("Item Gun Laser")] GunLaser, [GameKey("Item Gun Shockwave")] GunShockwave, [GameKey("Item Gun Shotgun")] GunShotgun, [GameKey("Item Gun Stun")] GunStun, [GameKey("Item Gun Tranq")] GunTranq, [GameKey("Item Health Pack Large")] HealthPackLarge, [GameKey("Item Health Pack Medium")] HealthPackMedium, [GameKey("Item Health Pack Small")] HealthPackSmall, [GameKey("Item Melee Baseball Bat")] MeleeBaseballBat, [GameKey("Item Melee Frying Pan")] MeleeFryingPan, [GameKey("Item Melee Inflatable Hammer")] MeleeInflatableHammer, [GameKey("Item Melee Sledge Hammer")] MeleeSledgeHammer, [GameKey("Item Melee Stun Baton")] MeleeStunBaton, [GameKey("Item Melee Sword")] MeleeSword, [GameKey("Item Mine Explosive")] MineExplosive, [GameKey("Item Mine Shockwave")] MineShockwave, [GameKey("Item Mine Stun")] MineStun, [GameKey("Item Orb Zero Gravity")] OrbZeroGravity, [GameKey("Item Phase Bridge")] PhaseBridge, [GameKey("Item Leaf Blower")] LeafBlower, [GameKey("Item Power Crystal")] PowerCrystal, [GameKey("Item ReviveItem")] ReviveItem, [GameKey("Item Rubber Duck")] RubberDuck, [GameKey("Item Staff Torque")] StaffTorque, [GameKey("Item Staff Void")] StaffVoid, [GameKey("Item Staff Zero Gravity")] StaffZeroGravity, [GameKey("Item Vehicle Semiscooter")] VehicleSemiscooter, [GameKey("Item Vehicle Semiscooter Small")] VehicleSemiscooterSmall, [GameKey("Item WalkieTalkieBox")] WalkieTalkieBox, [GameKey("Item Upgrade Death Head Battery")] UpgradeDeathHeadBattery, [GameKey("Item Upgrade Map Player Count")] UpgradeMapPlayerCount, [GameKey("Item Upgrade Player Crouch Rest")] UpgradePlayerCrouchRest, [GameKey("Item Upgrade Player Energy")] UpgradePlayerEnergy, [GameKey("Item Upgrade Player Extra Jump")] UpgradePlayerExtraJump, [GameKey("Item Upgrade Player Grab Range")] UpgradePlayerGrabRange, [GameKey("Item Upgrade Player Grab Strength")] UpgradePlayerGrabStrength, [GameKey("Item Upgrade Player Health")] UpgradePlayerHealth, [GameKey("Item Upgrade Player Sprint Speed")] UpgradePlayerSprintSpeed, [GameKey("Item Upgrade Player Tumble Climb")] UpgradePlayerTumbleClimb, [GameKey("Item Upgrade Player Tumble Launch")] UpgradePlayerTumbleLaunch, [GameKey("Item Upgrade Player Tumble Wings")] UpgradePlayerTumbleWings, [GameKey("Item Valuable Tracker")] ValuableTracker } public sealed class WeightedKey { public string Key { get; } public float Weight { get; } public WeightedKey(string key, float weight) { Key = key ?? throw new ArgumentNullException("key"); Weight = weight; } public WeightedKey(ItemName itemName, float weight) : this(itemName.GetGameKey(), weight) { } public void Deconstruct(out string key, out float weight) { key = Key; weight = Weight; } } public static class ItemPreservation { public static bool IsItemSecured(ItemAttributes? itemAttr) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemAttr == (Object)null) { return false; } RoomVolumeCheck component = ((Component)itemAttr).GetComponent<RoomVolumeCheck>(); if ((Object)(object)component != (Object)null && component.inTruck) { return true; } ItemEquippable component2 = ((Component)itemAttr).GetComponent<ItemEquippable>(); if ((Object)(object)component2 != (Object)null && (component2.IsEquipped() || component2.isEquipped)) { return true; } PhysGrabObject component3 = ((Component)itemAttr).GetComponent<PhysGrabObject>(); if ((Object)(object)component3 != (Object)null && (component3.grabbed || component3.grabbedLocal || (component3.playerGrabbing != null && component3.playerGrabbing.Count > 0))) { return true; } if (!string.IsNullOrEmpty(itemAttr.instanceName)) { int hashCode = itemAttr.instanceName.GetHashCode(); StatsManager instance = StatsManager.instance; if ((Object)(object)instance != (Object)null && (instance.playerInventorySpot1.ContainsValue(hashCode) || instance.playerInventorySpot2.ContainsValue(hashCode) || instance.playerInventorySpot3.ContainsValue(hashCode))) { return true; } } if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null && Vector3.Distance(((Component)itemAttr).transform.position, ((Component)TruckSafetySpawnPoint.instance).transform.position) <= 8f) { return true; } return false; } public static bool TryPreserveSecuredItem(ItemAttributes? itemAttr, out string baseItemName) { baseItemName = string.Empty; if ((Object)(object)itemAttr == (Object)null) { return false; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null || instance.itemsPurchased == null || instance.itemsPurchasedTotal == null) { return false; } string instanceName = itemAttr.instanceName; baseItemName = (((Object)(object)itemAttr.item != (Object)null) ? ((Object)itemAttr.item).name : (instanceName.Contains("/") ? instanceName.Split(new char[1] { '/' })[0] : instanceName)); if (string.IsNullOrEmpty(baseItemName) || !instance.itemDictionary.ContainsKey(baseItemName)) { return false; } int value; int num = (instance.itemsPurchased.TryGetValue(baseItemName, out value) ? value : 0); instance.itemsPurchased[baseItemName] = num + 1; int value2; int num2 = (instance.itemsPurchasedTotal.TryGetValue(baseItemName, out value2) ? value2 : 0); instance.itemsPurchasedTotal[baseItemName] = num2 + 1; return true; } public static void CleanupUnpreservedInstance(string instanceName) { if (string.IsNullOrEmpty(instanceName)) { return; } StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null)) { if (instance.item != null && instance.item.ContainsKey(instanceName)) { instance.item.Remove(instanceName); } if (instance.itemStatBattery != null && instance.itemStatBattery.ContainsKey(instanceName)) { instance.itemStatBattery.Remove(instanceName); } } } public static Dictionary<string, int>? GetOrCreateStatsDictionary(string dictionaryKey) { if (string.IsNullOrEmpty(dictionaryKey)) { throw new ArgumentNullException("dictionaryKey"); } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return null; } if (!instance.dictionaryOfDictionaries.TryGetValue(dictionaryKey, out var value)) { value = new Dictionary<string, int>(); instance.dictionaryOfDictionaries[dictionaryKey] = value; } return value; } public static int GetTotalCount(string dictionaryKey) { Dictionary<string, int> orCreateStatsDictionary = GetOrCreateStatsDictionary(dictionaryKey); if (orCreateStatsDictionary == null) { return 0; } int num = 0; foreach (KeyValuePair<string, int> item in orCreateStatsDictionary) { if (item.Value > 0) { num += item.Value; } } return num; } public static void RecordCount(string dictionaryKey, string baseItemName) { if (!string.IsNullOrEmpty(baseItemName)) { Dictionary<string, int> orCreateStatsDictionary = GetOrCreateStatsDictionary(dictionaryKey); if (orCreateStatsDictionary != null) { orCreateStatsDictionary[baseItemName] = ((!orCreateStatsDictionary.TryGetValue(baseItemName, out var value)) ? 1 : (value + 1)); } } } public static bool DecrementCount(string dictionaryKey, string instanceName, out string baseItemName, out int remaining) { baseItemName = string.Empty; remaining = 0; if (string.IsNullOrEmpty(instanceName)) { return false; } baseItemName = (instanceName.Contains("/") ? instanceName.Split(new char[1] { '/' })[0] : instanceName); Dictionary<string, int> orCreateStatsDictionary = GetOrCreateStatsDictionary(dictionaryKey); if (orCreateStatsDictionary == null) { return false; } if (orCreateStatsDictionary.TryGetValue(baseItemName, out var value) && value > 0) { remaining = value - 1; orCreateStatsDictionary[baseItemName] = remaining; return true; } return false; } public static void ResetCounts(string dictionaryKey) { Dictionary<string, int> orCreateStatsDictionary = GetOrCreateStatsDictionary(dictionaryKey); if (orCreateStatsDictionary != null && orCreateStatsDictionary.Count > 0) { orCreateStatsDictionary.Clear(); } } } } namespace ExtractionPointReward { internal static class ConfigurationController { public sealed class SlotWeightConfigMatrix { public const int WeightMin = 0; public const int WeightMax = 12; public const int TotalSlots = 5; private readonly ConfigEntry<bool>[] slotEnabled = new ConfigEntry<bool>[5]; private readonly Dictionary<string, ConfigEntry<int>>[] slotEntries = new Dictionary<string, ConfigEntry<int>>[5]; private readonly IReadOnlyList<WeightedKey>[] slotWeights = new IReadOnlyList<WeightedKey>[5]; public SlotWeightConfigMatrix(ConfigFile config, IReadOnlyList<WeightedKey>[] defaultsPerSlot, bool[] defaultEnabledPerSlot) { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } if (defaultsPerSlot == null) { throw new ArgumentNullException("defaultsPerSlot"); } if (defaultEnabledPerSlot == null) { throw new ArgumentNullException("defaultEnabledPerSlot"); } if (defaultsPerSlot.Length != 5) { throw new ArgumentException($"defaultsPerSlot must have {5} entries", "defaultsPerSlot"); } if (defaultEnabledPerSlot.Length != 5) { throw new ArgumentException($"defaultEnabledPerSlot must have {5} entries", "defaultEnabledPerSlot"); } AcceptableValueRange<int> val = new AcceptableValueRange<int>(0, 12); string[] allKeys = ItemKeysProvider.AllKeys; for (int i = 0; i < 5; i++) { int num = i + 1; string text = $"Slot {num}"; slotEnabled[i] = config.Bind<bool>(text, "Enabled", defaultEnabledPerSlot[i], $"Enable or disable reward Slot {num}."); slotEntries[i] = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); Dictionary<string, float> dictionary = ToMap(defaultsPerSlot[i]); foreach (string text2 in allKeys) { float value; int num2 = Clamp((int)Math.Round(dictionary.TryGetValue(text2, out value) ? value : 0f), 0, 12); slotEntries[i][text2] = config.Bind<int>(text, text2, num2, new ConfigDescription($"Weight for \"{text2}\" in slot {num}. (range {0}..{12})", (AcceptableValueBase)(object)val, Array.Empty<object>())); } slotWeights[i] = BuildWeights(slotEntries[i]); } } public bool IsSlotEnabled(int slot) { if (slot < 1 || slot > 5) { return false; } return slotEnabled[slot - 1].Value; } public IReadOnlyList<WeightedKey> GetForSlot(int slot) { if (slot < 1 || slot > 5) { return slotWeights[0]; } return slotWeights[slot - 1]; } private static Dictionary<string, float> ToMap(IReadOnlyList<WeightedKey> list) { Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < list.Count; i++) { dictionary[list[i].Key] = list[i].Weight; } return dictionary; } private static IReadOnlyList<WeightedKey> BuildWeights(Dictionary<string, ConfigEntry<int>> byKey) { string[] allKeys = ItemKeysProvider.AllKeys; WeightedKey[] array = new WeightedKey[allKeys.Length]; for (int i = 0; i < allKeys.Length; i++) { string key = allKeys[i]; int num = Clamp(byKey[key].Value, 0, 12); array[i] = new WeightedKey(key, num); } return array; } private static int Clamp(int v, int min, int max) { if (v >= min) { if (v <= max) { return v; } return max; } return min; } } private static ConfigFile? configFile; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> PreserveItemsBetweenLevels { get; private set; } public static ConfigEntry<int> MaxPreservedItems { get; private set; } public static SlotWeightConfigMatrix SlotWeights { get; private set; } public static ConfigEntry<bool> EnableColorModification { get; private set; } public static ConfigEntry<float> ColorMultiplierR { get; private set; } public static ConfigEntry<float> ColorMultiplierG { get; private set; } public static ConfigEntry<float> ColorMultiplierB { get; private set; } public static ConfigEntry<float> ColorAdd { get; private set; } public static ConfigEntry<bool> EnableRewardAura { get; private set; } public static ConfigEntry<float> AuraColorR { get; private set; } public static ConfigEntry<float> AuraColorG { get; private set; } public static ConfigEntry<float> AuraColorB { get; private set; } public static ConfigEntry<float> AuraIntensity { get; private set; } public static ConfigEntry<float> AuraRange { get; private set; } internal static void Initialize(ConfigFile config) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Expected O, but got Unknown //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Expected O, but got Unknown //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Expected O, but got Unknown //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Expected O, but got Unknown configFile = config ?? throw new ArgumentNullException("config"); Enabled = configFile.Bind<bool>("General", "Enabled", true, "Enable or disable this mod."); PreserveItemsBetweenLevels = configFile.Bind<bool>("General", "PreserveItemsBetweenLevels", false, "Allow items obtained from extraction point rewards and secured in the truck or inventory to persist across levels."); MaxPreservedItems = configFile.Bind<int>("General", "MaxPreservedItems", 10, new ConfigDescription("Maximum number of extraction-rewarded items that can be preserved simultaneously across levels.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); EnableColorModification = configFile.Bind<bool>("Visuals", "EnableColorModification", false, "Whether items spawned as extraction rewards have their materials modified with a color multiplier and additive offset."); ColorMultiplierR = configFile.Bind<float>("Visuals", "ColorMultiplierR", 0.65f, new ConfigDescription("Red channel multiplier applied to material color (preserves texture details).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); ColorMultiplierG = configFile.Bind<float>("Visuals", "ColorMultiplierG", 0.9f, new ConfigDescription("Green channel multiplier applied to material color (preserves texture details).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); ColorMultiplierB = configFile.Bind<float>("Visuals", "ColorMultiplierB", 1f, new ConfigDescription("Blue channel multiplier applied to material color (preserves texture details).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); ColorAdd = configFile.Bind<float>("Visuals", "ColorAdd", 0.05f, new ConfigDescription("Additive brightness offset applied to material color.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>())); EnableRewardAura = configFile.Bind<bool>("Visuals", "EnableRewardAura", true, "Whether items spawned as extraction rewards emit a luminous light aura centered on the item."); AuraColorR = configFile.Bind<float>("Visuals", "AuraColorR", 0.15f, new ConfigDescription("Red channel of the reward aura light color.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); AuraColorG = configFile.Bind<float>("Visuals", "AuraColorG", 0.85f, new ConfigDescription("Green channel of the reward aura light color.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); AuraColorB = configFile.Bind<float>("Visuals", "AuraColorB", 1f, new ConfigDescription("Blue channel of the reward aura light color.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); AuraIntensity = configFile.Bind<float>("Visuals", "AuraIntensity", 0.8f, new ConfigDescription("Intensity of the light aura emitted by extraction rewards.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>())); AuraRange = configFile.Bind<float>("Visuals", "AuraRange", 3.5f, new ConfigDescription("Range in meters of the light aura emitted by extraction rewards.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); IReadOnlyList<WeightedKey>[] defaultsPerSlot = BuildDefaultWeightsPerSlot(); bool[] defaultEnabledPerSlot = BuildDefaultEnabledPerSlot(); SlotWeights = new SlotWeightConfigMatrix(configFile, defaultsPerSlot, defaultEnabledPerSlot); configFile.Save(); } internal static void Reload() { if (configFile != null) { configFile.Reload(); IReadOnlyList<WeightedKey>[] defaultsPerSlot = BuildDefaultWeightsPerSlot(); bool[] defaultEnabledPerSlot = BuildDefaultEnabledPerSlot(); SlotWeights = new SlotWeightConfigMatrix(configFile, defaultsPerSlot, defaultEnabledPerSlot); configFile.Save(); } } private static IReadOnlyList<WeightedKey>[] BuildDefaultWeightsPerSlot() { IReadOnlyList<WeightedKey>[] array = new IReadOnlyList<WeightedKey>[5] { CreateHealthPackOnlyDefaults(), null, null, null, null }; for (int i = 1; i < 5; i++) { array[i] = CreateBalancedDefaults(); } return array; } private static bool[] BuildDefaultEnabledPerSlot() { bool[] array = new bool[5] { true, true, false, false, false }; for (int i = 2; i < 5; i++) { array[i] = false; } return array; } private static IReadOnlyList<WeightedKey> CreateHealthPackOnlyDefaults() { string[] allKeys = ItemKeysProvider.AllKeys; WeightedKey[] array = new WeightedKey[allKeys.Length]; for (int i = 0; i < allKeys.Length; i++) { string text = allKeys[i]; array[i] = new WeightedKey(text, text switch { "Item Health Pack Small" => 10f, "Item Health Pack Medium" => 6f, "Item Health Pack Large" => 3f, _ => 0f, }); } return array; } private static IReadOnlyList<WeightedKey> CreateBalancedDefaults() { string[] allKeys = ItemKeysProvider.AllKeys; WeightedKey[] array = new WeightedKey[allKeys.Length]; for (int i = 0; i < allKeys.Length; i++) { string key = allKeys[i]; array[i] = new WeightedKey(key, GetDefaultWeightForKey(key)); } return array; } private static float GetDefaultWeightForKey(string key) { if (key.StartsWith("Item Upgrade", StringComparison.OrdinalIgnoreCase)) { return 1f; } switch (key) { case "Item Power Crystal": return 0f; case "Item Grenade Human": case "Item Grenade Duct Taped": case "Item Grenade Shockwave": case "Item Grenade Explosive": case "Item Health Pack Small": case "Item Grenade Stun": case "Item Mine Explosive": case "Item Mine Shockwave": case "Item Mine Stun": return 10f; case "Item Health Pack Medium": case "Item Extraction Tracker": case "Item Valuable Tracker": case "Item Duck Bucket": return 6f; case "Item Gun Shockwave": case "Item Drone Battery": case "Item Drone Feather": case "Item Staff Zero Gravity": case "Item Health Pack Large": case "Item Phase Bridge": case "Item Drone Torque": case "Item Gun Tranq": case "Item Melee Frying Pan": case "Item Melee Sword": case "Item Gun Handgun": case "Item Leaf Blower": case "Item Rubber Duck": case "Item Cart Small": case "Item Vehicle Semiscooter": case "Item Melee Inflatable Hammer": case "Item Gun Stun": case "Item Vehicle Semiscooter Small": return 3f; case "Item Melee Baseball Bat": case "Item Drone Zero Gravity": case "Item Staff Torque": case "Item Gun Laser": case "Item Orb Zero Gravity": case "Item Melee Stun Baton": case "Item Gun Shotgun": case "Item Cart Cannon": case "Item Cart Medium": case "Item ReviveItem": case "Item Staff Void": case "Item Cart Laser": case "Item Melee Sledge Hammer": case "Item Drone Indestructible": case "Item WalkieTalkieBox": return 2f; default: return 1f; } } } [BepInPlugin("com.osmar.ExtractionPointReward", "ExtractionPointReward", "1.0.0")] public class ExtractionPointRewardPlugin : BaseUnityPlugin { public const string PluginGuid = "com.osmar.ExtractionPointReward"; public const string PluginName = "ExtractionPointReward"; public const string PluginVersion = "1.0.0"; internal Harmony? Harmony { get; private set; } internal static ManualLogSource Logger { get; private set; } private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; ((Component)this).transform.parent = null; ((Object)this).hideFlags = (HideFlags)61; ConfigurationController.Initialize(((BaseUnityPlugin)this).Config); Harmony = new Harmony("com.osmar.ExtractionPointReward"); Harmony.PatchAll(); Logger.LogInfo((object)"ExtractionPointReward 1.0.0 loaded!"); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace ExtractionPointReward.Visuals { internal sealed class RewardItemVisuals : MonoBehaviour { private GameObject? auraLightObject; private Light? auraLight; private PhysGrabObject? physGrabObject; private ItemEquippable? itemEquippable; private bool visualsApplied; private IEnumerator Start() { physGrabObject = ((Component)this).GetComponent<PhysGrabObject>() ?? ((Component)this).GetComponentInParent<PhysGrabObject>(); itemEquippable = ((Component)this).GetComponent<ItemEquippable>() ?? ((Component)this).GetComponentInParent<ItemEquippable>(); ApplyVisuals(); yield return null; ApplyVisuals(); } internal void ApplyVisuals() { if (ConfigurationController.EnableColorModification.Value) { ApplyMaterialColorModification(); } if (ConfigurationController.EnableRewardAura.Value) { SetupAuraLight(); } else if ((Object)(object)auraLight != (Object)null && ((Behaviour)auraLight).enabled) { ((Behaviour)auraLight).enabled = false; } visualsApplied = true; } private void ApplyMaterialColorModification() { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) float value = ConfigurationController.ColorMultiplierR.Value; float value2 = ConfigurationController.ColorMultiplierG.Value; float value3 = ConfigurationController.ColorMultiplierB.Value; float value4 = ConfigurationController.ColorAdd.Value; Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true); int num = 0; Color val4 = default(Color); Color val5 = default(Color); foreach (Renderer val in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val) || (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer))) { continue; } Material[] materials = val.materials; bool flag = false; foreach (Material val2 in materials) { if (Object.op_Implicit((Object)(object)val2)) { Color val3 = (val2.HasProperty("_Color") ? val2.GetColor("_Color") : val2.color); ((Color)(ref val4))..ctor(Mathf.Clamp01(val3.r * value + value4), Mathf.Clamp01(val3.g * value2 + value4), Mathf.Clamp01(val3.b * value3 + value4), val3.a); val2.color = val4; if (val2.HasProperty("_Color")) { val2.SetColor("_Color", val4); } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", val4); } if (val2.HasProperty("_EmissionColor")) { Color color = val2.GetColor("_EmissionColor"); ((Color)(ref val5))..ctor(Mathf.Clamp01(color.r * value), Mathf.Clamp01(color.g * value2), Mathf.Clamp01(color.b * value3), color.a); val2.SetColor("_EmissionColor", val5); } flag = true; } } if (flag) { val.materials = materials; num++; } } if (!visualsApplied) { ExtractionPointRewardPlugin.Logger.LogInfo((object)$"RewardItemVisuals: Applied material color modification (mult=[{value:F2},{value2:F2},{value3:F2}], add={value4:F2}) to {num} renderer(s) on '{((Object)((Component)this).gameObject).name}'."); } } private void SetupAuraLight() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)auraLight != (Object)null) { return; } ItemLight componentInChildren = ((Component)this).GetComponentInChildren<ItemLight>(); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.itemLight != (Object)null) { ((Behaviour)componentInChildren.itemLight).enabled = false; } auraLightObject = new GameObject("RewardItem_AuraLight"); auraLightObject.transform.SetParent(((Component)this).transform, false); Vector3 val = Vector3.zero; Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true); bool flag = false; Bounds val2 = default(Bounds); foreach (Renderer val3 in componentsInChildren) { if (val3 is MeshRenderer || val3 is SkinnedMeshRenderer) { if (!flag) { val2 = val3.bounds; flag = true; } else { ((Bounds)(ref val2)).Encapsulate(val3.bounds); } } } if (flag) { val = ((Component)this).transform.InverseTransformPoint(((Bounds)(ref val2)).center); } auraLightObject.transform.localPosition = val; Color auraBaseColor = GetAuraBaseColor(); auraLight = auraLightObject.AddComponent<Light>(); auraLight.type = (LightType)2; auraLight.color = auraBaseColor; auraLight.range = ConfigurationController.AuraRange.Value; auraLight.intensity = ConfigurationController.AuraIntensity.Value; auraLight.shadows = (LightShadows)0; auraLight.renderMode = (LightRenderMode)1; auraLight.cullingMask = -1; ((Behaviour)auraLight).enabled = true; ExtractionPointRewardPlugin.Logger.LogInfo((object)$"RewardItemVisuals: Attached centered aura light at local offset {val} on '{((Object)((Component)this).gameObject).name}'."); } private void Update() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)auraLight == (Object)null) && ConfigurationController.EnableRewardAura.Value) { bool num = (Object)(object)physGrabObject != (Object)null && (physGrabObject.grabbed || physGrabObject.grabbedLocal); bool flag = (Object)(object)itemEquippable != (Object)null && itemEquippable.IsEquipped(); bool flag2 = !num && !flag; if (((Behaviour)auraLight).enabled != flag2) { ((Behaviour)auraLight).enabled = flag2; } if (flag2) { float num2 = (Mathf.Sin(Time.time * 2.5f) + 1f) * 0.5f; Color auraBaseColor = GetAuraBaseColor(); Color val = default(Color); ((Color)(ref val))..ctor(auraBaseColor.r * 0.75f, auraBaseColor.g * 0.75f, auraBaseColor.b * 0.75f); auraLight.color = Color.Lerp(val, auraBaseColor, num2); auraLight.intensity = ConfigurationController.AuraIntensity.Value * (0.85f + 0.3f * num2); auraLight.range = ConfigurationController.AuraRange.Value; } } } private static Color GetAuraBaseColor() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) return new Color(Mathf.Clamp01(ConfigurationController.AuraColorR.Value), Mathf.Clamp01(ConfigurationController.AuraColorG.Value), Mathf.Clamp01(ConfigurationController.AuraColorB.Value)); } private void OnDestroy() { if ((Object)(object)auraLightObject != (Object)null) { Object.Destroy((Object)(object)auraLightObject); } } } } namespace ExtractionPointReward.Tracking { internal static class RewardInstanceTracker { private const string PreservedStatsKey = "extractionRewardPreserved"; private static readonly HashSet<string> rewardInstances = new HashSet<string>(); internal static void MarkReward(GameObject go) { if (Object.op_Implicit((Object)(object)go)) { if (!Object.op_Implicit((Object)(object)go.GetComponent<RewardItemTag>())) { go.AddComponent<RewardItemTag>(); } if (!Object.op_Implicit((Object)(object)go.GetComponent<RewardItemVisuals>())) { go.AddComponent<RewardItemVisuals>(); } ItemAttributes component = go.GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.instanceName)) { RegisterInstance(component.instanceName); } } } internal static void RegisterInstance(string instanceName) { if (!string.IsNullOrEmpty(instanceName) && rewardInstances.Add(instanceName)) { ExtractionPointRewardPlugin.Logger.LogDebug((object)("ExtractionPointReward: Registered reward instance '" + instanceName + "'.")); } } internal static int GetTotalPreservedCount() { return ItemPreservation.GetTotalCount("extractionRewardPreserved"); } internal static void ProcessSceneSwitch(bool gameOver, bool leaveGame) { if (gameOver || leaveGame) { if (gameOver) { ItemPreservation.ResetCounts("extractionRewardPreserved"); } ClearForNewLevel(); return; } if (!SemiFunc.IsMasterClientOrSingleplayer()) { ClearForNewLevel(); return; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.item == null || instance.itemStatBattery == null || instance.itemsPurchased == null) { ExtractionPointRewardPlugin.Logger.LogWarning((object)"ExtractionPointReward: StatsManager tables are null; cannot process scene switch."); ClearForNewLevel(); return; } bool value = ConfigurationController.PreserveItemsBetweenLevels.Value; int value2 = ConfigurationController.MaxPreservedItems.Value; int num = GetTotalPreservedCount(); RewardItemTag[] array = Object.FindObjectsOfType<RewardItemTag>(); HashSet<string> hashSet = new HashSet<string>(); if (value && array != null) { foreach (RewardItemTag rewardItemTag in array) { if (!Object.op_Implicit((Object)(object)rewardItemTag)) { continue; } ItemAttributes component = ((Component)rewardItemTag).GetComponent<ItemAttributes>(); if (!Object.op_Implicit((Object)(object)component) || !ItemPreservation.IsItemSecured(component)) { continue; } string instanceName = component.instanceName; string baseItemName = (((Object)(object)component.item != (Object)null) ? ((Object)component.item).name : (instanceName.Contains("/") ? instanceName.Split(new char[1] { '/' })[0] : instanceName)); if (num >= value2) { ExtractionPointRewardPlugin.Logger.LogInfo((object)$"ExtractionPointReward: Item '{baseItemName}' ({instanceName}) is secured, but MaxPreservedItems limit ({value2}) has been reached. Item will not persist."); } else if (ItemPreservation.TryPreserveSecuredItem(component, out baseItemName)) { ItemPreservation.RecordCount("extractionRewardPreserved", baseItemName); num++; if (!string.IsNullOrEmpty(instanceName)) { hashSet.Add(instanceName); } ExtractionPointRewardPlugin.Logger.LogInfo((object)$"ExtractionPointReward: Preserved item '{baseItemName}' ({instanceName}) into truck/next level. Total preserved: {num}/{value2}"); } } } List<string> list = new List<string>(rewardInstances); List<string> list2 = new List<string>(); for (int j = 0; j < list.Count; j++) { string text = list[j]; if (!string.IsNullOrEmpty(text) && !hashSet.Contains(text)) { ItemPreservation.CleanupUnpreservedInstance(text); list2.Add(text); } } if (list2.Count > 0) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"ExtractionPointReward: Cleaned up {list2.Count} non-preserved reward instance(s) from item + itemStatBattery:"); for (int k = 0; k < list2.Count; k++) { stringBuilder.AppendLine(" " + list2[k]); } ExtractionPointRewardPlugin.Logger.LogDebug((object)stringBuilder.ToString()); } ClearForNewLevel(); } internal static void OnItemRemoved(string instanceName) { if (ItemPreservation.DecrementCount("extractionRewardPreserved", instanceName, out string baseItemName, out int remaining)) { ExtractionPointRewardPlugin.Logger.LogDebug((object)$"ExtractionPointReward: Decremented preserved count for '{baseItemName}' ({remaining} remaining)."); } } internal static void Reset() { ExtractionPointRewardPlugin.Logger.LogDebug((object)$"ExtractionPointReward: Resetting preserved reward count (was {GetTotalPreservedCount()})."); ItemPreservation.ResetCounts("extractionRewardPreserved"); ClearForNewLevel(); } internal static void ClearForNewLevel() { if (rewardInstances.Count > 0) { ExtractionPointRewardPlugin.Logger.LogDebug((object)$"ExtractionPointReward: Clearing reward-instance tracker ({rewardInstances.Count} entries)."); } rewardInstances.Clear(); } } internal sealed class RewardItemTag : MonoBehaviour { } } namespace ExtractionPointReward.Providers { internal class RandomItemProvider { private readonly WeightedKeyTable?[] tables = new WeightedKeyTable[5]; private static int TotalSlots => 5; public RandomItemProvider() { RebuildTables(); } public void RebuildTables() { if (ConfigurationController.SlotWeights == null) { throw new InvalidOperationException("ConfigurationController.SlotWeights is not initialized."); } for (int i = 0; i < TotalSlots; i++) { int num = i + 1; if (!ConfigurationController.SlotWeights.IsSlotEnabled(num)) { tables[i] = null; continue; } IReadOnlyList<WeightedKey> forSlot = ConfigurationController.SlotWeights.GetForSlot(num); if (!forSlot.Any((WeightedKey e) => e.Weight > 0f)) { tables[i] = null; ExtractionPointRewardPlugin.Logger.LogDebug((object)$"RandomItemProvider: Slot {num} has all-zero weights — any spawn requests for this slot will be skipped."); } else { tables[i] = new WeightedKeyTable(forSlot); } } } public string? GetRandomKeyForSlot(int slot) { if (slot < 1 || slot > TotalSlots) { throw new ArgumentOutOfRangeException("slot", $"Slot must be 1..{TotalSlots}"); } return (tables[slot - 1] ?? throw new InvalidOperationException($"No positive weights configured or slot disabled for slot {slot}.")).GetRandomKey(); } public bool TrySpawnRandomItemForSlot(int slot, Vector3 position, Quaternion rotation, out GameObject? spawned, float upwardOffset = 0.15f) { //IL_00a3: 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) spawned = null; if (slot < 1 || slot > TotalSlots) { throw new ArgumentOutOfRangeException("slot", $"Slot must be 1..{TotalSlots}"); } if (!ConfigurationController.SlotWeights.IsSlotEnabled(slot)) { ExtractionPointRewardPlugin.Logger.LogDebug((object)$"RandomItemProvider: Slot {slot} is disabled — skipping spawn."); return false; } WeightedKeyTable weightedKeyTable = tables[slot - 1]; if (weightedKeyTable == null) { ExtractionPointRewardPlugin.Logger.LogWarning((object)$"RandomItemProvider: Slot {slot} has no positive weights configured — skipping spawn."); return false; } string randomKey = weightedKeyTable.GetRandomKey(); if (string.IsNullOrEmpty(randomKey)) { ExtractionPointRewardPlugin.Logger.LogWarning((object)"RandomItemProvider: Selected empty key."); return false; } if (ItemProvider.TrySpawnByKey(randomKey, position, rotation, out spawned, upwardOffset)) { if ((Object)(object)spawned != (Object)null) { RewardInstanceTracker.MarkReward(spawned); } return true; } return false; } } internal sealed class WeightedKeyTable { public List<WeightedKey> Entries { get; } public float TotalWeight => Entries.Sum((WeightedKey e) => e.Weight); public WeightedKeyTable(IEnumerable<WeightedKey> entries) { if (entries == null) { throw new ArgumentNullException("entries"); } Entries = entries.Where((WeightedKey e) => e != null).ToList(); } public string? GetRandomKey() { if (Entries.Count == 0 || TotalWeight <= 0f) { return null; } string text = null; float num = Random.value * TotalWeight; float num2 = 0f; foreach (WeightedKey entry in Entries) { num2 += entry.Weight; if (num <= num2) { text = entry.Key; break; } } return text ?? Entries[Entries.Count - 1].Key; } } } namespace ExtractionPointReward.Patches { [HarmonyPatch(typeof(EnemyDirector), "Start")] internal static class EnemyDirector_Start_Patch { private static void Postfix() { if (SemiFunc.RunIsLevel()) { ConfigurationController.Reload(); ExtractionPoint_StateComplete_Patch.Reset(); } } } [HarmonyPatch(typeof(ExtractionPoint), "StateComplete")] internal static class ExtractionPoint_StateComplete_Patch { private static readonly HashSet<int> spawnedInstanceIds = new HashSet<int>(); private static readonly object spawnLock = new object(); private static bool IsModEnabled => ConfigurationController.Enabled.Value; internal static void Reset() { lock (spawnLock) { spawnedInstanceIds.Clear(); } } private static void Prefix(ExtractionPoint __instance) { if (!SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.RunIsLevel() || !IsModEnabled || !IsStateStart(__instance)) { return; } int instanceID = ((Object)__instance).GetInstanceID(); lock (spawnLock) { if (spawnedInstanceIds.Contains(instanceID)) { return; } } AddInstanceId(instanceID); if (!SpawnRewards(__instance)) { RemoveInstanceId(instanceID); } } private static bool SpawnRewards(ExtractionPoint extractionPoint) { //IL_0003: 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) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) bool result = false; Vector3 spawnPosition = GetSpawnPosition(extractionPoint); RandomItemProvider randomItemProvider = new RandomItemProvider(); randomItemProvider.RebuildTables(); List<int> list = new List<int>(); for (int i = 1; i <= 5; i++) { if (ConfigurationController.SlotWeights.IsSlotEnabled(i)) { list.Add(i); } } ExtractionPointRewardPlugin.Logger.LogDebug((object)$"ExtractionPoint_StateComplete_Patch: Spawning {list.Count} enabled reward slots."); for (int j = 0; j < list.Count; j++) { int num = list[j]; float num2 = ((float)j - (float)(list.Count - 1) / 2f) * 0.5f; Vector3 val = spawnPosition + Vector3.right * num2 + Vector3.up * 0.05f * (float)num; try { if (randomItemProvider.TrySpawnRandomItemForSlot(num, val, Quaternion.identity, out GameObject spawned)) { result = true; ExtractionPointRewardPlugin.Logger.LogInfo((object)$"ExtractionPoint_StateComplete_Patch: Spawned '{((Object)spawned).name}' for slot {num} at {val}."); } else { ExtractionPointRewardPlugin.Logger.LogWarning((object)$"ExtractionPoint_StateComplete_Patch: Failed to spawn item for slot {num} at {val}."); } } catch (Exception arg) { ExtractionPointRewardPlugin.Logger.LogError((object)$"ExtractionPoint_StateComplete_Patch: Exception while spawning slot {num}: {arg}"); } } return result; } private static Vector3 GetSpawnPosition(ExtractionPoint extractionPoint) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) try { return ((Object)(object)extractionPoint.surplusSpawnTransform != (Object)null) ? extractionPoint.surplusSpawnTransform.position : ((Component)extractionPoint).transform.position; } catch { return ((Component)extractionPoint).transform.position; } } private static bool IsStateStart(ExtractionPoint extractionPoint) { FieldInfo field = ((object)extractionPoint).GetType().GetField("stateStart", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { return false; } return (bool)field.GetValue(extractionPoint); } private static void AddInstanceId(int instanceId) { lock (spawnLock) { spawnedInstanceIds.Add(instanceId); } } private static void RemoveInstanceId(int instanceId) { lock (spawnLock) { spawnedInstanceIds.Remove(instanceId); } } } [HarmonyPatch(typeof(PunManager), "SetItemNameLOGIC")] internal static class PunManager_SetItemNameLOGIC_Patch { private static void Postfix(string _name, int photonViewID, ItemAttributes _itemAttributes) { ItemAttributes val = _itemAttributes; if (SemiFunc.IsMultiplayer()) { PhotonView val2 = PhotonView.Find(photonViewID); if ((Object)(object)val2 != (Object)null) { val = ((Component)val2).GetComponent<ItemAttributes>(); } } if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Component)val).GetComponent<RewardItemTag>())) { RewardInstanceTracker.RegisterInstance(_name); } } } [HarmonyPatch(typeof(SemiFunc), "OnSceneSwitch")] internal static class SemiFunc_OnSceneSwitch_Patch { private static void Prefix(bool _gameOver, bool _leaveGame) { try { RewardInstanceTracker.ProcessSceneSwitch(_gameOver, _leaveGame); } catch (Exception arg) { ExtractionPointRewardPlugin.Logger.LogError((object)$"SemiFunc_OnSceneSwitch_Patch: Failed to process reward instances on scene switch: {arg}"); } } } [HarmonyPatch(typeof(StatsManager), "ItemRemove")] internal static class StatsManager_ItemRemove_Patch { private static void Postfix(string instanceName) { try { RewardInstanceTracker.OnItemRemoved(instanceName); } catch (Exception arg) { ExtractionPointRewardPlugin.Logger.LogError((object)$"StatsManager_ItemRemove_Patch: Failed to update preserved count on ItemRemove: {arg}"); } } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class StatsManager_RunStartStats_Patch { private static void Postfix() { try { RewardInstanceTracker.Reset(); } catch (Exception arg) { ExtractionPointRewardPlugin.Logger.LogError((object)$"StatsManager_RunStartStats_Patch: Failed to reset preserved tracker on RunStartStats: {arg}"); } } } }