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.0", "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.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+716f6842d1cf35eb5493fb4776ee486e7e18d59d")]
[assembly: AssemblyProduct("EnemyDropLoot")]
[assembly: AssemblyTitle("EnemyDropLoot")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace 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_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: 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_0070: 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 = default(Vector3);
if (!((VActor)monster).VRoom.FindNearestPoly(positionVector, ref val2, num))
{
val2 = positionVector;
}
PosWithRot val3 = ((VActor)monster).Position.Clone();
val3.x = val2.x;
val3.y = val2.y;
val3.z = val2.z;
if (((VActor)monster).VRoom.SpawnLootingObject(newItemElement, val3, ((VActor)monster).IsIndoor, (ReasonOfSpawn)4, 0) == 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<RandomSpawnedItemActorData> LootSources = new List<RandomSpawnedItemActorData>();
private static readonly List<int> FallbackItemIds = new List<int>();
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<int, SpawnedActorData> fieldValue = ReflectionHelper.GetFieldValue<Dictionary<int, SpawnedActorData>>((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<int, (int, int)> 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<bool> _enabled;
private static MelonPreferences_Entry<float> _dropChance;
private static MelonPreferences_Entry<int> _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<T> CreateEntry<T>(string identifier, T defaultValue, string displayName, string? description = null)
{
if (_category == null)
{
throw new InvalidOperationException("Preference category not initialized.");
}
return _category.CreateEntry<T>(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;
}
}
}