Decompiled source of EnemyDropLoot v1.0.2

EnemyDropLoot.dll

Decompiled a month ago
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;
		}
	}
}