Decompiled source of InteriorScrapTweaks v1.0.0

InteriorScrapTweaks.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DunGen;
using DunGen.Graph;
using HarmonyLib;
using LethalLevelLoader;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("InteriorScrapTweaks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("InteriorScrapTweaks")]
[assembly: AssemblyTitle("InteriorScrapTweaks")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 InteriorScrapTweaks
{
	internal static class InteriorResolver
	{
		private static bool _lethalLevelLoaderResolutionFailed;

		internal static List<string> GetInteriorNameCandidates(RoundManager roundManager)
		{
			List<string> list = new List<string>();
			try
			{
				string text = TryGetLethalLevelLoaderDungeonName();
				if (!string.IsNullOrEmpty(text))
				{
					list.Add(text);
				}
			}
			catch (Exception arg)
			{
				ManualLogSource modLogger = Plugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogWarning((object)$"[InteriorScrapTweaks] Failed to resolve LLL dungeon name, skipping it: {arg}");
				}
			}
			try
			{
				RuntimeDungeon dungeonGenerator = roundManager.dungeonGenerator;
				object obj;
				if (dungeonGenerator == null)
				{
					obj = null;
				}
				else
				{
					DungeonGenerator generator = dungeonGenerator.Generator;
					if (generator == null)
					{
						obj = null;
					}
					else
					{
						DungeonFlow dungeonFlow = generator.DungeonFlow;
						obj = ((dungeonFlow != null) ? ((Object)dungeonFlow).name : null);
					}
				}
				string text2 = (string)obj;
				if (!string.IsNullOrEmpty(text2) && !ContainsInteriorName(list, text2))
				{
					list.Add(text2);
				}
			}
			catch (Exception arg2)
			{
				ManualLogSource modLogger2 = Plugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogWarning((object)$"[InteriorScrapTweaks] Failed to read DungeonFlow name from dungeonGenerator, skipping it: {arg2}");
				}
			}
			try
			{
				if (roundManager.dungeonFlowTypes != null && roundManager.currentDungeonType >= 0 && roundManager.currentDungeonType < roundManager.dungeonFlowTypes.Length)
				{
					IndoorMapType obj2 = roundManager.dungeonFlowTypes[roundManager.currentDungeonType];
					object obj3;
					if (obj2 == null)
					{
						obj3 = null;
					}
					else
					{
						DungeonFlow dungeonFlow2 = obj2.dungeonFlow;
						obj3 = ((dungeonFlow2 != null) ? ((Object)dungeonFlow2).name : null);
					}
					string text3 = (string)obj3;
					if (!string.IsNullOrEmpty(text3) && !ContainsInteriorName(list, text3))
					{
						list.Add(text3);
					}
				}
			}
			catch (Exception arg3)
			{
				ManualLogSource modLogger3 = Plugin.ModLogger;
				if (modLogger3 != null)
				{
					modLogger3.LogWarning((object)$"[InteriorScrapTweaks] Failed to read DungeonFlow name from dungeonFlowTypes[{roundManager?.currentDungeonType}], skipping it: {arg3}");
				}
			}
			try
			{
				list.Add("Type_" + roundManager?.currentDungeonType);
			}
			catch (Exception arg4)
			{
				ManualLogSource modLogger4 = Plugin.ModLogger;
				if (modLogger4 != null)
				{
					modLogger4.LogWarning((object)$"[InteriorScrapTweaks] Failed to add numeric interior fallback key: {arg4}");
				}
			}
			return list;
		}

		private static bool ContainsInteriorName(List<string> candidates, string candidateName)
		{
			foreach (string candidate in candidates)
			{
				if (string.Equals(candidate, candidateName, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static string TryGetLethalLevelLoaderDungeonName()
		{
			if (_lethalLevelLoaderResolutionFailed)
			{
				return null;
			}
			try
			{
				return LethalLevelLoaderIntegration.GetCurrentDungeonName();
			}
			catch (FileNotFoundException ex)
			{
				_lethalLevelLoaderResolutionFailed = true;
				ManualLogSource modLogger = Plugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)("[InteriorScrapTweaks] LethalLevelLoader not found, matching interiors by DungeonFlow name only. (" + ex.Message + ")"));
				}
				return null;
			}
			catch (FileLoadException ex2)
			{
				_lethalLevelLoaderResolutionFailed = true;
				ManualLogSource modLogger2 = Plugin.ModLogger;
				if (modLogger2 != null)
				{
					modLogger2.LogInfo((object)("[InteriorScrapTweaks] LethalLevelLoader could not be loaded, matching interiors by DungeonFlow name only. (" + ex2.Message + ")"));
				}
				return null;
			}
			catch (Exception arg)
			{
				_lethalLevelLoaderResolutionFailed = true;
				ManualLogSource modLogger3 = Plugin.ModLogger;
				if (modLogger3 != null)
				{
					modLogger3.LogWarning((object)$"[InteriorScrapTweaks] Failed to read LethalLevelLoader CurrentExtendedDungeonFlow.DungeonName, LLL matching disabled for this session: {arg}");
				}
				return null;
			}
		}
	}
	internal static class LethalLevelLoaderIntegration
	{
		internal static string GetCurrentDungeonName()
		{
			ExtendedDungeonFlow currentExtendedDungeonFlow = DungeonManager.CurrentExtendedDungeonFlow;
			if (currentExtendedDungeonFlow == null)
			{
				return null;
			}
			return currentExtendedDungeonFlow.DungeonName;
		}
	}
	[BepInPlugin("CunningFox146.InteriorScrapTweaks", "InteriorScrapTweaks", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "CunningFox146.InteriorScrapTweaks";

		public const string PluginName = "InteriorScrapTweaks";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource ModLogger;

		internal static ConfigEntry<float> DefaultScrapCountMultiplier;

		internal static ConfigEntry<float> DefaultScrapValueMultiplier;

		internal static ConfigEntry<string> ScrapCountMultipliers;

		internal static ConfigEntry<string> ScrapValueMultipliers;

		internal static ConfigEntry<bool> LogDetectedInteriorNames;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			ModLogger = ((BaseUnityPlugin)this).Logger;
			DefaultScrapCountMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DefaultScrapCountMultiplier", 1f, new ConfigDescription("Scrap-count multiplier used for interiors not listed in ScrapCountMultipliers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			DefaultScrapValueMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DefaultScrapValueMultiplier", 1f, new ConfigDescription("Scrap-value multiplier used for interiors not listed in ScrapValueMultipliers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			ScrapCountMultipliers = ((BaseUnityPlugin)this).Config.Bind<string>("Interiors", "ScrapCountMultipliers", "Factory=1.0, Mansion=1.5, Mineshaft=0.7, Sewer=1.0, Level1Flow=1.0, Level2Flow=1.5, Level3Flow=0.7", "Per-interior scrap-COUNT multipliers. Format: \"Name=Multiplier, Name2=Multiplier2\" (separators: comma/semicolon/newline between entries, '=' or ':' between key and value). Keys match the LethalLevelLoader dungeon name when LLL is installed, otherwise the DungeonFlow asset name. Check the BepInEx console/log for the exact \"Interior keys\" line logged on level generation.");
			ScrapValueMultipliers = ((BaseUnityPlugin)this).Config.Bind<string>("Interiors", "ScrapValueMultipliers", "Factory=1.0, Mansion=1.0, Mineshaft=1.0, Sewer=1.0, Level1Flow=1.0, Level2Flow=1.0, Level3Flow=1.0", "Per-interior scrap-VALUE multipliers (average value per item). Same format and key matching as ScrapCountMultipliers.");
			LogDetectedInteriorNames = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogDetectedInteriorNames", true, "Log the detected interior keys and applied multipliers every time scrap spawns. Use the logged keys in ScrapCountMultipliers/ScrapValueMultipliers to target modded interiors. You can turn this off once your config is settled.");
			new Harmony("CunningFox146.InteriorScrapTweaks").PatchAll(typeof(ScrapSpawnPatches));
			ModLogger.LogInfo((object)"InteriorScrapTweaks v1.0.0 loaded. Scrap amount/value will be scaled per interior.");
		}
	}
	internal static class ScrapMultiplierConfig
	{
		public static Dictionary<string, float> ParseMultiplierTable(string rawTableText, ManualLogSource logger, string tableName)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(rawTableText))
			{
				return dictionary;
			}
			string[] array = rawTableText.Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[2] { '=', ':' }, 2);
				if (array2.Length != 2)
				{
					if (logger != null)
					{
						logger.LogWarning((object)("[InteriorScrapTweaks] Ignoring malformed entry in " + tableName + ": \"" + text.Trim() + "\" (expected Key=Multiplier)."));
					}
					continue;
				}
				string text2 = array2[0].Trim();
				string s = array2[1].Trim();
				float result;
				if (text2.Length == 0)
				{
					if (logger != null)
					{
						logger.LogWarning((object)("[InteriorScrapTweaks] Ignoring entry with empty key in " + tableName + ": \"" + text.Trim() + "\"."));
					}
				}
				else if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out result))
				{
					if (!float.IsFinite(result))
					{
						if (logger != null)
						{
							logger.LogWarning((object)("[InteriorScrapTweaks] Ignoring non-finite multiplier in " + tableName + ": \"" + text.Trim() + "\"."));
						}
						continue;
					}
					if (result < 0f)
					{
						if (logger != null)
						{
							logger.LogWarning((object)("[InteriorScrapTweaks] Clamping negative multiplier to 0 in " + tableName + ": \"" + text.Trim() + "\"."));
						}
						result = 0f;
					}
					dictionary[text2] = result;
				}
				else if (logger != null)
				{
					logger.LogWarning((object)("[InteriorScrapTweaks] Ignoring entry with non-numeric multiplier in " + tableName + ": \"" + text.Trim() + "\"."));
				}
			}
			return dictionary;
		}

		public static float FindMultiplierForInterior(IReadOnlyList<string> interiorNameCandidates, Dictionary<string, float> multipliersByInteriorName, float defaultMultiplier)
		{
			if (interiorNameCandidates == null)
			{
				return defaultMultiplier;
			}
			foreach (string interiorNameCandidate in interiorNameCandidates)
			{
				if (interiorNameCandidate != null && multipliersByInteriorName.TryGetValue(interiorNameCandidate, out var value))
				{
					return value;
				}
			}
			return defaultMultiplier;
		}
	}
	[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
	internal static class ScrapSpawnPatches
	{
		private static int _originalMinimumScrapCount;

		private static int _originalMaximumScrapCount;

		private static int _originalMinimumTotalScrapValue;

		private static int _originalMaximumTotalScrapValue;

		private static float _originalScrapValueMultiplier;

		private static bool _scrapValuesWereAdjusted;

		private const int MaximumSpawnedScrapCount = 1000;

		private const float MaximumScrapValueMultiplier = 100000f;

		private static void Prefix(RoundManager __instance)
		{
			_scrapValuesWereAdjusted = false;
			try
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.currentLevel == (Object)null)
				{
					return;
				}
				bool flag;
				try
				{
					flag = ((NetworkBehaviour)__instance).IsServer;
				}
				catch (Exception arg)
				{
					ManualLogSource modLogger = Plugin.ModLogger;
					if (modLogger != null)
					{
						modLogger.LogWarning((object)$"[InteriorScrapTweaks] Failed to check IsServer, assuming host: {arg}");
					}
					flag = true;
				}
				if (!flag)
				{
					return;
				}
				List<string> interiorNameCandidates = InteriorResolver.GetInteriorNameCandidates(__instance);
				Dictionary<string, float> multipliersByInteriorName = ScrapMultiplierConfig.ParseMultiplierTable(Plugin.ScrapCountMultipliers.Value, Plugin.ModLogger, "ScrapCountMultipliers");
				Dictionary<string, float> multipliersByInteriorName2 = ScrapMultiplierConfig.ParseMultiplierTable(Plugin.ScrapValueMultipliers.Value, Plugin.ModLogger, "ScrapValueMultipliers");
				float num = ScrapMultiplierConfig.FindMultiplierForInterior(interiorNameCandidates, multipliersByInteriorName, Plugin.DefaultScrapCountMultiplier.Value);
				float num2 = ScrapMultiplierConfig.FindMultiplierForInterior(interiorNameCandidates, multipliersByInteriorName2, Plugin.DefaultScrapValueMultiplier.Value);
				if (Plugin.LogDetectedInteriorNames.Value)
				{
					ManualLogSource modLogger2 = Plugin.ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)("[InteriorScrapTweaks] Interior keys: " + string.Join(" | ", interiorNameCandidates) + " " + $"-> scrap amount x{num}, scrap value x{num2} " + $"(base min={__instance.currentLevel.minScrap} max={__instance.currentLevel.maxScrap} " + $"valueMult={__instance.scrapValueMultiplier})."));
					}
				}
				if (Mathf.Approximately(num, 1f) && Mathf.Approximately(num2, 1f))
				{
					return;
				}
				SelectableLevel currentLevel = __instance.currentLevel;
				_originalMinimumScrapCount = currentLevel.minScrap;
				_originalMaximumScrapCount = currentLevel.maxScrap;
				_originalMinimumTotalScrapValue = currentLevel.minTotalScrapValue;
				_originalMaximumTotalScrapValue = currentLevel.maxTotalScrapValue;
				_originalScrapValueMultiplier = __instance.scrapValueMultiplier;
				int num3 = Math.Min(1000, Math.Max(0, RoundToInteger((float)_originalMinimumScrapCount * num)));
				int num4 = Math.Min(1000, Math.Max(num3, RoundToInteger((float)_originalMaximumScrapCount * num)));
				if (num3 == 1000 || num4 == 1000)
				{
					ManualLogSource modLogger3 = Plugin.ModLogger;
					if (modLogger3 != null)
					{
						modLogger3.LogWarning((object)$"[InteriorScrapTweaks] Scrap count for this interior hit the safety cap of {1000} items; check your ScrapCountMultipliers for typos.");
					}
				}
				float num5 = Math.Min(100000f, _originalScrapValueMultiplier * num2);
				if (num5 < _originalScrapValueMultiplier * num2)
				{
					ManualLogSource modLogger4 = Plugin.ModLogger;
					if (modLogger4 != null)
					{
						modLogger4.LogWarning((object)$"[InteriorScrapTweaks] Scrap value multiplier for this interior hit the safety cap of x{100000f}; check your ScrapValueMultipliers for typos.");
					}
				}
				currentLevel.minScrap = num3;
				currentLevel.maxScrap = num4;
				currentLevel.minTotalScrapValue = Math.Max(0, RoundToInteger((float)_originalMinimumTotalScrapValue * num * num2));
				currentLevel.maxTotalScrapValue = Math.Max(currentLevel.minTotalScrapValue, RoundToInteger((float)_originalMaximumTotalScrapValue * num * num2));
				__instance.scrapValueMultiplier = num5;
				_scrapValuesWereAdjusted = true;
			}
			catch (Exception arg2)
			{
				ManualLogSource modLogger5 = Plugin.ModLogger;
				if (modLogger5 != null)
				{
					modLogger5.LogError((object)$"[InteriorScrapTweaks] Failed to apply interior scrap multipliers: {arg2}");
				}
			}
		}

		private static void Postfix(RoundManager __instance)
		{
			if (!_scrapValuesWereAdjusted)
			{
				return;
			}
			try
			{
				if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.currentLevel == (Object)null))
				{
					SelectableLevel currentLevel = __instance.currentLevel;
					currentLevel.minScrap = _originalMinimumScrapCount;
					currentLevel.maxScrap = _originalMaximumScrapCount;
					currentLevel.minTotalScrapValue = _originalMinimumTotalScrapValue;
					currentLevel.maxTotalScrapValue = _originalMaximumTotalScrapValue;
					__instance.scrapValueMultiplier = _originalScrapValueMultiplier;
				}
			}
			catch (Exception arg)
			{
				ManualLogSource modLogger = Plugin.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogError((object)$"[InteriorScrapTweaks] Failed to restore scrap values: {arg}");
				}
			}
			finally
			{
				_scrapValuesWereAdjusted = false;
			}
		}

		private static int RoundToInteger(double valueToRound)
		{
			if (double.IsNaN(valueToRound) || valueToRound <= 0.0)
			{
				return 0;
			}
			if (valueToRound >= 2147483647.0)
			{
				return int.MaxValue;
			}
			return (int)Math.Round(valueToRound, MidpointRounding.AwayFromZero);
		}
	}
}