using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using EnemyDropLoot; using EnemyDropLoot.Config; using EnemyDropLoot.Managers; using EnemyDropLoot.Services; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using MimicAPI.GameAPI; using ReluProtocol; using ReluProtocol.Enum; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "EnemyDropLoot", "1.0.5", "DooDesch", null)] [assembly: MelonGame("ReLUGames", "MIMESIS")] [assembly: MelonOptionalDependencies(new string[] { "MimicAPI" })] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("EnemyDropLoot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.5.0")] [assembly: AssemblyInformationalVersion("1.0.5+1e211d8c72fbdc24a47c5e9ad73bcb62acd14a9a")] [assembly: AssemblyProduct("EnemyDropLoot")] [assembly: AssemblyTitle("EnemyDropLoot")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.5.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 EnemyDropLoot { public sealed class Core : MelonMod { public override void OnInitializeMelon() { EnemyDropLootPreferences.Initialize(); ((MelonBase)this).HarmonyInstance.PatchAll(); MelonLogger.Msg($"EnemyDropLoot initialized. Enabled={EnemyDropLootPreferences.Enabled}, DropChance={EnemyDropLootPreferences.DropChance:P0}, MaxDropsPerKill={EnemyDropLootPreferences.MaxDropsPerKill}"); } } } namespace EnemyDropLoot.Patches { [HarmonyPatch(typeof(DungeonRoom), "Initialize")] internal static class DungeonRoomInitializePatch { private static void Postfix(DungeonRoom __instance) { LootPoolManager.ConfigureForDungeon(__instance); } } [HarmonyPatch(typeof(IVroom), "OnVacateRoom", new Type[] { typeof(bool) })] internal static class DungeonRoomVacatePatch { private static void Postfix(IVroom __instance) { if (__instance is DungeonRoom) { LootPoolManager.Reset(); } } } [HarmonyPatch(typeof(VMonster), "OnDead")] internal static class VMonsterOnDeadPatch { private static void Prefix(VMonster __instance) { LootPoolManager.TryHandleMonsterDeath(__instance); } } } namespace EnemyDropLoot.Services { internal static class LootSpawnService { private const float DropScatterRadius = 2f; internal static bool TrySpawnLoot(VMonster monster, int itemMasterId) { //IL_0018: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0086: Unknown result type (might be due to invalid IL or missing references) ItemElement newItemElement = ((VActor)monster).VRoom.GetNewItemElement(itemMasterId, false, 1, 0, 0, 0); if (newItemElement == null) { return false; } Vector3 positionVector = ((VActor)monster).PositionVector; Vector2 val = Random.insideUnitCircle * 2f; positionVector += new Vector3(val.x, 0f, val.y); float num = Mathf.Max(1.5f, 2f); Vector3 val2 = positionVector; VWorld vWorld = CoreAPI.GetVWorld(); if (vWorld != null) { Vector3 val3 = vWorld.FindNearestPoly(positionVector, num); if (val3 != NavMeshConstants.INVALID_POSITION) { val2 = val3; } } PosWithRot val4 = ((VActor)monster).Position.Clone(); val4.x = val2.x; val4.y = val2.y; val4.z = val2.z; if (((VActor)monster).VRoom.SpawnLootingObject(newItemElement, val4, ((VActor)monster).IsIndoor, (ReasonOfSpawn)4, 0, 0, 0L, false, false) == 0) { MelonLogger.Warning($"EnemyDropLoot: Failed to spawn loot item {itemMasterId} for monster {((VActor)monster).MasterID}."); return false; } return true; } } } namespace EnemyDropLoot.Managers { internal static class LootPoolManager { private static readonly List LootSources = new List(); private static readonly List FallbackItemIds = new List(); private static IVroom _activeRoom = null; private static bool _hasActiveRoom; internal static void Reset() { LootSources.Clear(); FallbackItemIds.Clear(); _hasActiveRoom = false; } internal static void ConfigureForDungeon(DungeonRoom room) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 Reset(); _activeRoom = (IVroom)(object)room; _hasActiveRoom = true; try { Dictionary fieldValue = ReflectionHelper.GetFieldValue>((object)room, "_spawnedActorDatas"); if (fieldValue == null) { MelonLogger.Warning("EnemyDropLoot: Unable to fetch spawn data for current dungeon."); return; } foreach (SpawnedActorData value in fieldValue.Values) { if (value == null || (int)value.MarkerType != 1) { continue; } RandomSpawnedItemActorData val = (RandomSpawnedItemActorData)(object)((value is RandomSpawnedItemActorData) ? value : null); if (val == null) { continue; } LootSources.Add(val); foreach (KeyValuePair candidate in val.Candidates) { if (!FallbackItemIds.Contains(candidate.Key)) { FallbackItemIds.Add(candidate.Key); } } } MelonLogger.Msg("EnemyDropLoot: Loaded {0} loot spawn bundles with {1} unique items.", new object[2] { LootSources.Count, FallbackItemIds.Count }); } catch (Exception arg) { MelonLogger.Error($"EnemyDropLoot: Failed to read dungeon loot pool. {arg}"); } } internal static void TryHandleMonsterDeath(VMonster monster) { if (!EnemyDropLootPreferences.Enabled || LootSources.Count == 0 || !_hasActiveRoom || ((VActor)monster).VRoom != _activeRoom) { return; } int num = EnemyDropLootPreferences.RollDropCount(); if (num <= 0) { return; } for (int i = 0; i < num; i++) { if (TryPickItem(out var itemMasterId)) { LootSpawnService.TrySpawnLoot(monster, itemMasterId); } } } private static bool TryPickItem(out int itemMasterId) { itemMasterId = 0; if (LootSources.Count == 0) { return false; } int num = LootSources[Random.Range(0, LootSources.Count)].GetPickedItemValue(); if (num == 0 && FallbackItemIds.Count > 0) { num = FallbackItemIds[Random.Range(0, FallbackItemIds.Count)]; } if (num == 0) { return false; } itemMasterId = num; return true; } } } namespace EnemyDropLoot.Config { internal static class EnemyDropLootPreferences { private const string CategoryId = "EnemyDropLoot"; private static MelonPreferences_Category _category; private static MelonPreferences_Entry _enabled; private static MelonPreferences_Entry _dropChance; private static MelonPreferences_Entry _maxDropsPerKill; internal static bool Enabled => _enabled.Value; internal static float DropChance => Mathf.Clamp01(_dropChance.Value); internal static int MaxDropsPerKill => Mathf.Clamp(_maxDropsPerKill.Value, 0, 100); internal static void Initialize() { if (_category == null) { _category = MelonPreferences.CreateCategory("EnemyDropLoot", "Enemy Drop Loot"); _enabled = CreateEntry("Enabled", defaultValue: true, "Enabled", "Toggle the Enemy Drop Loot mod. Default: true"); _dropChance = CreateEntry("DropChance", 0.1f, "Drop chance", "Chance (0-1) per roll that a slain enemy drops loot. Default: 0.1"); _maxDropsPerKill = CreateEntry("MaxDropsPerKill", 1, "Max drops per kill", "Maximum number of loot rolls performed per enemy. Default: 1"); } } private static MelonPreferences_Entry CreateEntry(string identifier, T defaultValue, string displayName, string? description = null) { if (_category == null) { throw new InvalidOperationException("Preference category not initialized."); } return _category.CreateEntry(identifier, defaultValue, displayName, description, false, false, (ValueValidator)null, (string)null); } internal static int RollDropCount() { if (!Enabled) { return 0; } int num = Math.Max(1, MaxDropsPerKill); int num2 = 0; for (int i = 0; i < num; i++) { if (Random.value <= DropChance) { num2++; } } return num2; } } }