Decompiled source of LootExpansion v1.0.2

LootExpansion.dll

Decompiled 10 hours ago
using System;
using System.Collections;
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 Bifrost.Cooked;
using HarmonyLib;
using LootExpansion;
using LootExpansion.Config;
using LootExpansion.Managers;
using LootExpansion.Services;
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), "LootExpansion", "1.0.1", "GOXH", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: MelonOptionalDependencies(new string[] { "MimicAPI" })]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LootExpansion")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+716f6842d1cf35eb5493fb4776ee486e7e18d59d")]
[assembly: AssemblyProduct("LootExpansion")]
[assembly: AssemblyTitle("LootExpansion")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
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 LootExpansion.Patches
{
	[HarmonyPatch(typeof(DungeonRoom), "Initialize")]
	internal static class DungeonRoomInitializePatch
	{
		private static void Postfix(DungeonRoom __instance)
		{
			LootExpansionManager.ConfigureForDungeon(__instance);
		}
	}
	[HarmonyPatch(typeof(IVroom), "OnVacateRoom", new Type[] { typeof(bool) })]
	internal static class DungeonRoomVacatePatch
	{
		private static void Postfix(IVroom __instance)
		{
			if (__instance is DungeonRoom)
			{
				LootExpansionManager.Reset();
			}
		}
	}
	[HarmonyPatch(typeof(VMonster), "OnDead")]
	internal static class VMonsterOnDeadPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(VMonster __instance)
		{
			if (__instance != null)
			{
				LootExpansionManager.TryHandleMonsterDeath(__instance);
			}
		}
	}
}
namespace LootExpansion.Services
{
	internal static class LootSpawnService
	{
		internal static bool TrySpawnLoot(VMonster monster, int itemMasterId)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (monster == null || ((VActor)monster).VRoom == null)
				{
					return false;
				}
				ItemElement newItemElement = ((VActor)monster).VRoom.GetNewItemElement(itemMasterId, false, 1, 0, 0, 0);
				if (newItemElement == null)
				{
					return false;
				}
				Vector3 positionVector = ((VActor)monster).PositionVector;
				float num = 1.2f;
				float num2 = Random.Range(0f, MathF.PI * 2f);
				float num3 = Random.Range(0.2f, num);
				float num4 = (float)Math.Cos(num2) * num3;
				float num5 = (float)Math.Sin(num2) * num3;
				PosWithRot val = ((VActor)monster).Position.Clone();
				if (val != null)
				{
					val.x = positionVector.x + num4;
					val.y = positionVector.y + 0.5f;
					val.z = positionVector.z + num5;
					val.yaw = Random.Range(0f, 360f);
				}
				return ((VActor)monster).VRoom.SpawnLootingObject(newItemElement, val, ((VActor)monster).IsIndoor, (ReasonOfSpawn)0, 0, 0, 0L, false) != 0;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[ ! ] LootExpansion - Spawn Service Error: " + ex.Message);
				return false;
			}
		}
	}
}
namespace LootExpansion.Managers
{
	internal static class LootExpansionManager
	{
		private static readonly List<int> FallbackItemIds;

		private static readonly List<int> ExcludedItemIds;

		private static readonly List<int> ExcludedMonsterIds;

		private static int _lastRoomHash;

		private static float _lastLoadTime;

		private static int _lastDeathObjId;

		private static readonly HashSet<int> _processedMonsterHashes;

		private static readonly HashSet<int> _loggedObjectIds;

		private static readonly Dictionary<int, int> _itemPriceCache;

		private static bool _isCollecting;

		internal static void Reset()
		{
			FallbackItemIds.Clear();
			_itemPriceCache.Clear();
			_loggedObjectIds.Clear();
			_processedMonsterHashes.Clear();
			_lastDeathObjId = -1;
			_isCollecting = false;
		}

		internal static void ConfigureForDungeon(DungeonRoom room)
		{
			if (room != null)
			{
				int hashCode = ((object)room).GetHashCode();
				if (_lastRoomHash != hashCode || !(Time.time - _lastLoadTime < 0.1f))
				{
					_lastRoomHash = hashCode;
					_lastLoadTime = Time.time;
					Reset();
					_isCollecting = true;
					MelonLogger.Msg("[ ! ] Area change detected: Starting new item collection.");
					MelonCoroutines.Start(DelayedSummaryLog(5f));
				}
			}
		}

		internal static void TryHandleMonsterDeath(VMonster monster)
		{
			if (monster == null || !LootExpansionM.Enabled)
			{
				return;
			}
			int objectID = ((VActor)monster).ObjectID;
			int hashCode = ((object)monster).GetHashCode();
			if (_lastDeathObjId == objectID || _processedMonsterHashes.Contains(hashCode))
			{
				return;
			}
			int num = ReflectionHelper.GetFieldValue<MonsterInfo>((object)monster, "_monsterInfo")?.MasterID ?? 0;
			if (ExcludedMonsterIds.Contains(num))
			{
				return;
			}
			_lastDeathObjId = objectID;
			_processedMonsterHashes.Add(hashCode);
			switch (num)
			{
			case 20000011:
				LootSpawnService.TrySpawnLoot(monster, 1000);
				LootSpawnService.TrySpawnLoot(monster, 3000);
				return;
			case 20000009:
				LootSpawnService.TrySpawnLoot(monster, 5139);
				LootSpawnService.TrySpawnLoot(monster, 5163);
				return;
			}
			float dropChance = LootExpansionM.DropChance;
			int dropCountForMonster = LootExpansionM.GetDropCountForMonster(num);
			int num2 = Mathf.Clamp(Mathf.RoundToInt(dropChance * 100f), 0, 100);
			if (Random.Range(1, 101) > num2)
			{
				return;
			}
			for (int i = 0; i < dropCountForMonster; i++)
			{
				if (TryPickItem(out var itemMasterId, num))
				{
					LootSpawnService.TrySpawnLoot(monster, itemMasterId);
				}
			}
		}

		static LootExpansionManager()
		{
			FallbackItemIds = new List<int>();
			_itemPriceCache = new Dictionary<int, int>();
			_processedMonsterHashes = new HashSet<int>();
			_loggedObjectIds = new HashSet<int>();
			_lastRoomHash = -1;
			_lastLoadTime = -1f;
			_lastDeathObjId = -1;
			ExcludedItemIds = new List<int> { 5124, 5110 };
			ExcludedMonsterIds = new List<int> { 20000010, 20000001 };
		}

		internal static void LogLootingObjectSpawn(VLootingObject lootingObj)
		{
			if (lootingObj == null || !_loggedObjectIds.Add(((VActor)lootingObj).ObjectID))
			{
				return;
			}
			ItemElement itemElement = lootingObj.GetItemElement();
			if (itemElement != null)
			{
				int itemMasterID = itemElement.ItemMasterID;
				int price = itemElement.Price;
				bool flag = ((VActor)lootingObj).VRoom is VWaitingRoom;
				bool isIndoor = ((VActor)lootingObj).IsIndoor;
				if (_isCollecting && !flag && isIndoor && !ExcludedItemIds.Contains(itemMasterID) && !FallbackItemIds.Contains(itemMasterID))
				{
					FallbackItemIds.Add(itemMasterID);
					_itemPriceCache[itemMasterID] = price;
				}
			}
		}

		private static string GetLocalizedText(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return "Unknown";
			}
			if (!key.StartsWith("STRING_"))
			{
				return key;
			}
			try
			{
				DataManager val = Object.FindAnyObjectByType<DataManager>();
				ExcelDataManager val2 = (((Object)(object)val != (Object)null) ? val.ExcelDataManager : null);
				if (((val2 != null) ? val2.LocalizationDict : null) != null && val2.LocalizationDict.TryGetValue(key, out var value))
				{
					return value.ko;
				}
			}
			catch
			{
			}
			return key;
		}

		internal static void ClearProcessedMonsters()
		{
			_processedMonsterHashes.Clear();
		}

		private static IEnumerator DelayedSummaryLog(float delay)
		{
			yield return (object)new WaitForSeconds(delay);
			_isCollecting = false;
			if (FallbackItemIds.Count > 0)
			{
				MelonLogger.Msg($"[ ! ] Loot pool confirmed for current area ({FallbackItemIds.Count} types)");
			}
			else
			{
				MelonLogger.Warning("[ ! ] No indoor items collected within 5 seconds of landing.");
			}
		}

		private static bool TryPickItem(out int itemMasterId, int monsterId)
		{
			itemMasterId = 0;
			if (FallbackItemIds.Count == 0)
			{
				return false;
			}
			double y = monsterId switch
			{
				20000004 => 3.5, 
				20000005 => 1.0, 
				20000006 => 0.9, 
				20000007 => 0.5, 
				20000008 => 2.0, 
				20000012 => 4.0, 
				_ => 1.0, 
			};
			double num = 0.0;
			List<double> list = new List<double>();
			foreach (int fallbackItemId in FallbackItemIds)
			{
				if (_itemPriceCache.TryGetValue(fallbackItemId, out var value))
				{
					double num2 = Math.Pow(Math.Max(1, value), y);
					list.Add(num2);
					num += num2;
				}
				else
				{
					list.Add(0.0);
				}
			}
			if (num <= 0.0)
			{
				return false;
			}
			double num3 = (double)Random.value * num;
			double num4 = 0.0;
			for (int i = 0; i < list.Count; i++)
			{
				num4 += list[i];
				if (num3 <= num4)
				{
					itemMasterId = FallbackItemIds[i];
					break;
				}
			}
			return itemMasterId != 0;
		}
	}
}
namespace LootExpansion.Config
{
	public static class LootExpansionM
	{
		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _enabled;

		private static MelonPreferences_Entry<float> _dropChance;

		private static MelonPreferences_Entry<int> _countSentryper;

		private static MelonPreferences_Entry<int> _countStandingOwl;

		private static MelonPreferences_Entry<int> _countBruteBunny;

		private static MelonPreferences_Entry<int> _countQuakka;

		private static MelonPreferences_Entry<int> _countMaegoostro;

		private static MelonPreferences_Entry<int> _countMrDelpit;

		public static bool Enabled
		{
			get
			{
				if (_enabled != null)
				{
					return _enabled.Value;
				}
				return false;
			}
		}

		public static float DropChance
		{
			get
			{
				if (_dropChance == null)
				{
					return 0.1f;
				}
				return Mathf.Clamp01(_dropChance.Value);
			}
		}

		public static void Initialize()
		{
			if (_category == null)
			{
				_category = MelonPreferences.CreateCategory("LootExpansion", "2077 Loot Expansion Settings");
				_enabled = CreateEntry("Enabled", def: true, "Toggle Mod Activation (True/False)");
				_dropChance = CreateEntry("Drop_Probability", 1f, "Drop Probability (0.1=10% / 1.0=100%)");
				string display = "Number of items dropped on kill (Min:0 / Max:10)";
				_countSentryper = CreateEntry("Sentryper_Count", 5, display);
				_countBruteBunny = CreateEntry("BruteBunny_Count", 5, display);
				_countMaegoostro = CreateEntry("Maegoostro_Count", 3, display);
				_countStandingOwl = CreateEntry("StandingOwl_Count", 1, display);
				_countMrDelpit = CreateEntry("MrDelpit_Count", 1, display);
				_countQuakka = CreateEntry("Quakka_Count", 1, display);
				_category.SaveToFile(false);
				MelonLogger.Msg("[ ! ] Configuration has been successfully initialized.");
			}
		}

		private static MelonPreferences_Entry<T> CreateEntry<T>(string id, T def, string display)
		{
			return _category.CreateEntry<T>(id, def, display, display, false, false, (ValueValidator)null, (string)null);
		}

		public static int GetDropCountForMonster(int monsterId)
		{
			int num = 0;
			int num2 = 10;
			return monsterId switch
			{
				20000004 => Mathf.Clamp(_countBruteBunny.Value, num, num2), 
				20000005 => Mathf.Clamp(_countMrDelpit.Value, num, num2), 
				20000006 => Mathf.Clamp(_countStandingOwl.Value, num, num2), 
				20000007 => Mathf.Clamp(_countQuakka.Value, num, num2), 
				20000008 => Mathf.Clamp(_countMaegoostro.Value, num, num2), 
				20000012 => Mathf.Clamp(_countSentryper.Value, num, num2), 
				_ => 0, 
			};
		}
	}
}
namespace LootExpansion.Patches
{
	[HarmonyPatch(typeof(VMonster), "OnAlive")]
	internal static class VMonsterOnAlivePatch
	{
		[HarmonyPostfix]
		internal static void Postfix(VMonster __instance)
		{
		}
	}
	[HarmonyPatch(typeof(VLootingObject), "OnEnterSpace")]
	internal static class VLootingObjectOnEnterSpacePatch
	{
		[HarmonyPostfix]
		internal static void Postfix(VLootingObject __instance)
		{
			if (__instance != null)
			{
				LootExpansionManager.LogLootingObjectSpawn(__instance);
			}
		}
	}
}
namespace LootExpansion
{
	public sealed class Core : MelonMod
	{
		public override void OnInitializeMelon()
		{
			LootExpansionM.Initialize();
			((MelonBase)this).HarmonyInstance.PatchAll();
			MelonLogger.Msg("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+");
			MelonLogger.Msg(">> [ 2077 // LOOT EXPANSION ] v1.0.1 INITIALIZED");
			MelonLogger.Msg(">> AUTHORED BY : github.com/xxxx-6666");
			MelonLogger.Msg(">> DISCORD SERVER : discord.com/cyber2077");
			MelonLogger.Msg("---------------------------------------------------");
			string arg = (LootExpansionM.Enabled ? "ONLINE" : "OFFLINE");
			MelonLogger.Msg($" > SYSTEM_STATUS : {arg}");
			MelonLogger.Msg($" > DROP_PROBABILITY   : {LootExpansionM.DropChance:P0}");
			MelonLogger.Msg(" > GLOBAL_LIMIT  : 10 units (Safety Lock)");
			MelonLogger.Msg("---------------------------------------------------");
			MelonLogger.Msg(" > TARGET_DATABASE_INJECTION_REPORT:");
			string[] array = new string[6] { "Sentryper  ", "BruteBunny ", "Maegoostro ", "StandingOwl", "MrDelpit   ", "Quakka     " };
			int[] array2 = new int[6] { 20000012, 20000004, 20000008, 20000006, 20000005, 20000007 };
			for (int i = 0; i < array.Length; i++)
			{
				int dropCountForMonster = LootExpansionM.GetDropCountForMonster(array2[i]);
				string arg2 = ((dropCountForMonster > 0) ? "[*]" : "[ ]");
				MelonLogger.Msg($"   {arg2} {array[i]} : {dropCountForMonster} qty");
			}
			MelonLogger.Msg("---------------------------------------------------");
			MelonLogger.Msg(">> ACCESS GRANTED: SYSTEM_OVERRIDE_COMPLETE");
			MelonLogger.Msg("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+");
		}
	}
}