Decompiled source of MonsterLabZBalance v0.1.2

MonsterLabZBalance.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MonsterLabZBalance")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("MonsterLabZBalance")]
[assembly: AssemblyTitle("MonsterLabZBalance")]
[assembly: AssemblyVersion("0.1.2.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 MonsterLabZBalance
{
	[BepInPlugin("xnolde.monsterlabzbalance", "MonsterLabZ Balance", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "xnolde.monsterlabzbalance";

		public const string PluginName = "MonsterLabZ Balance";

		public const string PluginVersion = "0.1.2";

		internal static ManualLogSource Log;

		private static readonly WaitForSeconds ScenePollDelay = new WaitForSeconds(1f);

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("xnolde.monsterlabzbalance");
			_harmony.PatchAll();
			((MonoBehaviour)this).StartCoroutine(SceneWatcher());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"MonsterLabZ Balance 0.1.2 loaded. Runtime world-spawn guard installed.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private IEnumerator SceneWatcher()
		{
			ZNetScene configuredScene = null;
			bool draugrCargoConfigured = false;
			while (true)
			{
				Balance.EnsureLocalization();
				ZNetScene instance = ZNetScene.instance;
				if ((Object)(object)instance == (Object)null)
				{
					configuredScene = null;
					draugrCargoConfigured = false;
					Balance.ResetRuntimeGuardLog();
					yield return ScenePollDelay;
					continue;
				}
				if ((Object)(object)configuredScene != (Object)(object)instance && Balance.IsReady(instance))
				{
					Balance.ApplyPrefabChanges(instance);
					configuredScene = instance;
					draugrCargoConfigured = false;
					Balance.ResetRuntimeGuardLog();
				}
				if ((Object)(object)configuredScene == (Object)(object)instance && !draugrCargoConfigured)
				{
					draugrCargoConfigured = Balance.ConfigureDraugrCargo(instance);
				}
				yield return ScenePollDelay;
			}
		}
	}
	internal static class Balance
	{
		private sealed class ContainerDropSpec
		{
			internal readonly string Prefab;

			internal readonly int Min;

			internal readonly int Max;

			internal readonly float Weight;

			internal ContainerDropSpec(string prefab, int min, int max, float weight = 1f)
			{
				Prefab = prefab;
				Min = min;
				Max = max;
				Weight = weight;
			}
		}

		private sealed class SpawnSystemState
		{
		}

		private const string PurpleNameKey = "mlzb_mycotic_greydwarf";

		private static readonly ConditionalWeakTable<SpawnSystem, SpawnSystemState> ConfiguredSpawnSystems = new ConditionalWeakTable<SpawnSystem, SpawnSystemState>();

		private static readonly string[] RequiredScenePrefabs = new string[7] { "Greydwarf_Purple_Shroom", "DwarfGoblin", "DwarfGoblinShaman", "DwarfGoblinLoot", "ML_GoblinShip", "ML_DraugrShip", "DeepSea_Serpent" };

		private static readonly string[] RequiredItemPrefabs = new string[11]
		{
			"Greydwarf_elite_attack", "DeepSea_Serpent_Cast", "MushroomBlue", "Mushroom", "Coins", "Amber", "Ruby", "SerpentMeat", "SerpentScale", "FishRaw",
			"TrophyDeepSeaSerpent"
		};

		private static readonly ContainerDropSpec[] DraugrCargoSpecs = new ContainerDropSpec[4]
		{
			new ContainerDropSpec("BoarJerky", 3, 6),
			new ContainerDropSpec("IronScrap", 3, 6),
			new ContainerDropSpec("AmberPearl", 4, 6),
			new ContainerDropSpec("RottenMeat", 2, 4)
		};

		private static Type LocalizationType;

		private static FieldInfo LocalizationInstanceField;

		private static PropertyInfo LocalizationInstanceProperty;

		private static MethodInfo LocalizationAddWordMethod;

		private static MethodInfo LocalizationLanguageMethod;

		private static object LocalizedInstance;

		private static string LocalizedLanguage;

		private static bool RuntimeGuardCorrectionLogged;

		private static readonly HashSet<string> MonsterLabZCreaturePrefabs = new HashSet<string>(StringComparer.Ordinal)
		{
			"BlackSpider", "BrownSpider", "BrownSpider_Spawn", "ChaosSkeletonWarrior", "ChaosSkeletonWarriorNoFX", "DeepSea_Serpent", "DeepSea_Serpent_Flying", "DwarfGoblin", "DwarfGoblinLoot", "DwarfGoblinShaman",
			"DwarfGoblinShaman_Boat", "DwarfGoblin_Boat", "DwarfGoblin_NoAttack", "DwarfGoblin_Spawn", "EvilShadow", "FireGolem", "FireSkeletonWarrior", "FireSkeletonWarriorNoFx", "ForestSpider", "FrigidSpider",
			"FrostSpider", "GhostWarrior", "GreenSpider", "Green_Butterfly", "Greydwarf_Purple", "Greydwarf_Purple_Shroom", "IceGolem", "IceSkeletonWarrior", "IceSkeletonWarriorNoFx", "IceSkeletonWarriorT6",
			"KrakenLD", "Kraken_Blob", "MLabz1_DummyTest", "MLNPC_Female0", "MLNPC_Female1", "MLNPC_Male0", "MLNPC_Male1", "MLNPC_Male2", "MLNPC_Male3", "MLNPC_Svartalfar0",
			"ML_AshHatchling", "ML_AshHuldra", "ML_AshHuldraQueen1", "ML_AshHuldraQueen2", "ML_AshHuldraQueen3", "ML_BlueMistile_Aggressive", "ML_BlueMistile_Passive", "ML_DraugrBomber_Boat", "ML_DraugrShip", "ML_Draugr_Boat",
			"ML_Draugr_Spawn", "ML_FrostHatchling", "ML_FrostHatchling_Spawn", "ML_GoblinShip", "ML_HailStorm", "ML_JellyFish0", "ML_LavaRoot", "ML_LightningStorm", "ML_MeteorShower", "ML_RedMistile_Aggressive",
			"ML_RedMistile_Passive", "ML_Surtling", "ML_Sword_Frigga_Spider_1", "ML_Sword_Frigga_Spider_2", "ML_Sword_Frigga_Spider_3_Cold", "ML_Sword_Frigga_Spider_3_Fire", "ML_Sword_Frigga_Spider_3_Light", "Molluscan", "MolluscanLand", "NightmareDragon",
			"NightmareDragonEV", "NormalGhostWarrior", "NormalSkeletonWarrior", "ObsidianGolem", "PoisonSkeletonWarrior", "PoisonSkeletonWarriorNoFx", "Rainbow_Butterfly", "Silkworm_Butterfly", "SpiderBoss_Egg", "Spider_Boss",
			"Spider_Hatchling", "Surtling_Spawn", "Surtr", "TanSpider", "TreeSpider", "TreeSpider_Spawn"
		};

		internal static bool IsReady(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return false;
			}
			string[] requiredScenePrefabs = RequiredScenePrefabs;
			foreach (string text in requiredScenePrefabs)
			{
				if ((Object)(object)scene.GetPrefab(text) == (Object)null)
				{
					return false;
				}
			}
			requiredScenePrefabs = RequiredItemPrefabs;
			foreach (string prefabName in requiredScenePrefabs)
			{
				if ((Object)(object)FindPrefab(scene, prefabName) == (Object)null)
				{
					return false;
				}
			}
			return true;
		}

		internal static void ApplyPrefabChanges(ZNetScene scene)
		{
			ConfigurePurple(scene);
			ConfigureDwarfGoblin(scene);
			ConfigureDwarfGoblinShaman(scene);
			ConfigureDwarfGoblinLoot(scene);
			ConfigureGoblinShip(scene);
			ConfigureDraugrShip(scene);
			ConfigureDeepSeaSerpent(scene);
			Plugin.Log.LogInfo((object)"MonsterLabZ curated prefab balance applied.");
		}

		internal static void EnsureLocalization()
		{
			object localizationInstance = GetLocalizationInstance();
			if (localizationInstance == null)
			{
				return;
			}
			string selectedLanguage = GetSelectedLanguage(localizationInstance);
			if (localizationInstance == LocalizedInstance && string.Equals(selectedLanguage, LocalizedLanguage, StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			string text = ((selectedLanguage.IndexOf("Spanish", StringComparison.OrdinalIgnoreCase) >= 0) ? "Grisucio micótico" : "Mycotic Greydwarf");
			try
			{
				LocalizationAddWordMethod?.Invoke(localizationInstance, new object[2] { "mlzb_mycotic_greydwarf", text });
				LocalizedInstance = localizationInstance;
				LocalizedLanguage = selectedLanguage;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not register Purple Shroom localization: " + ex.Message));
			}
		}

		private static object GetLocalizationInstance()
		{
			try
			{
				if (LocalizationType == null)
				{
					ResolveLocalizationMetadata();
				}
				if (LocalizationType == null)
				{
					return null;
				}
				object obj = LocalizationInstanceField?.GetValue(null);
				if (obj != null)
				{
					return obj;
				}
				return LocalizationInstanceProperty?.GetValue(null, null);
			}
			catch
			{
				return null;
			}
		}

		private static void ResolveLocalizationMetadata()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType("Localization", throwOnError: false);
				if (!(type == null))
				{
					LocalizationType = type;
					LocalizationInstanceField = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					LocalizationInstanceProperty = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					LocalizationAddWordMethod = type.GetMethod("AddWord", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
					{
						typeof(string),
						typeof(string)
					}, null);
					LocalizationLanguageMethod = type.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
					break;
				}
			}
		}

		private static string GetSelectedLanguage(object localization)
		{
			try
			{
				return (LocalizationLanguageMethod?.Invoke(localization, null) as string) ?? "English";
			}
			catch
			{
				return "English";
			}
		}

		private static GameObject FindPrefab(ZNetScene scene, string prefabName)
		{
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
				if ((Object)(object)itemPrefab != (Object)null)
				{
					return itemPrefab;
				}
			}
			if (!((Object)(object)scene != (Object)null))
			{
				return null;
			}
			return scene.GetPrefab(prefabName);
		}

		private static Character GetCharacter(ZNetScene scene, string prefabName)
		{
			GameObject prefab = scene.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Prefab '" + prefabName + "' not found."));
				return null;
			}
			Character component = prefab.GetComponent<Character>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Prefab '" + prefabName + "' has no Character component."));
			}
			return component;
		}

		private static void SetHealth(ZNetScene scene, string prefabName, float health)
		{
			Character character = GetCharacter(scene, prefabName);
			if ((Object)(object)character != (Object)null)
			{
				character.m_health = health;
			}
		}

		private static void ConfigurePurple(ZNetScene scene)
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Expected O, but got Unknown
			GameObject prefab = scene.GetPrefab("Greydwarf_Purple_Shroom");
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			Character component = prefab.GetComponent<Character>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_health = 150f;
				component.m_name = "$mlzb_mycotic_greydwarf";
			}
			Humanoid component2 = prefab.GetComponent<Humanoid>();
			GameObject val = FindPrefab(scene, "Greydwarf_elite_attack");
			int num = 0;
			if ((Object)(object)component2 != (Object)null && (Object)(object)val != (Object)null)
			{
				num += ReplaceAttackReference(component2.m_defaultItems, "Greydwarf_attack", val);
				num += ReplaceAttackReference(component2.m_randomWeapon, "Greydwarf_attack", val);
				if (component2.m_randomSets != null)
				{
					ItemSet[] randomSets = component2.m_randomSets;
					foreach (ItemSet val2 in randomSets)
					{
						if (val2?.m_items != null)
						{
							num += ReplaceAttackReference(val2.m_items, "Greydwarf_attack", val);
						}
					}
				}
			}
			SetCharacterDrops(scene, prefab, new Drop
			{
				m_prefab = FindPrefab(scene, "MushroomBlue"),
				m_amountMin = 1,
				m_amountMax = 2,
				m_chance = 1f,
				m_onePerPlayer = false,
				m_levelMultiplier = false
			}, new Drop
			{
				m_prefab = FindPrefab(scene, "Mushroom"),
				m_amountMin = 1,
				m_amountMax = 2,
				m_chance = 1f,
				m_onePerPlayer = false,
				m_levelMultiplier = false
			});
			Plugin.Log.LogInfo((object)("Greydwarf_Purple_Shroom: Grisucio micótico / Mycotic Greydwarf, HP 150, " + $"Brute melee replacements={num}, MushroomBlue 1-2, Mushroom 1-2."));
		}

		private static void ConfigureDwarfGoblin(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("DwarfGoblin");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "DwarfGoblin", 80f);
				SetCharacterDrops(scene, prefab, MakeDrop(scene, "Coins", 3, 5, 1f));
				Plugin.Log.LogInfo((object)"DwarfGoblin: HP 80, Coins 3-5.");
			}
		}

		private static void ConfigureDwarfGoblinShaman(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("DwarfGoblinShaman");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "DwarfGoblinShaman", 60f);
				SetCharacterDrops(scene, prefab, MakeDrop(scene, "Coins", 4, 6, 1f));
				Plugin.Log.LogInfo((object)"DwarfGoblinShaman: HP 60, Coins 4-6; original attacks preserved.");
			}
		}

		private static void ConfigureDwarfGoblinLoot(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("DwarfGoblinLoot");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "DwarfGoblinLoot", 60f);
				SetCharacterDrops(scene, prefab, MakeDrop(scene, "Coins", 5, 8, 1f), MakeDrop(scene, "Amber", 2, 4, 1f), MakeDrop(scene, "Ruby", 1, 3, 1f));
				Plugin.Log.LogInfo((object)"DwarfGoblinLoot: HP 60, Coins 5-8, Amber 2-4, Ruby 1-3; original AI preserved.");
			}
		}

		private static void ConfigureGoblinShip(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("ML_GoblinShip");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "ML_GoblinShip", 800f);
				FilterShipDrops(prefab, "ML_GoblinShip_Cargo", includeDwarfNoAttack: true);
				Plugin.Log.LogInfo((object)"ML_GoblinShip: HP 800; structural material drops removed; cargo/crew drops preserved.");
			}
		}

		private static void ConfigureDraugrShip(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("ML_DraugrShip");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "ML_DraugrShip", 500f);
				NormalizeDropAmount(prefab, "ML_DraugrShip_Cargo", 3, 5);
				Plugin.Log.LogInfo((object)"ML_DraugrShip: HP 500; original hull drops preserved; cargo count normalized to 3-5.");
			}
		}

		private static void ConfigureDeepSeaSerpent(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("DeepSea_Serpent");
			if (!((Object)(object)prefab == (Object)null))
			{
				SetHealth(scene, "DeepSea_Serpent", 800f);
				GameObject val = FindPrefab(scene, "DeepSea_Serpent_Cast");
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if (val2?.m_itemData?.m_shared != null)
				{
					val2.m_itemData.m_shared.m_damages.m_blunt = 35f;
					val2.m_itemData.m_shared.m_damages.m_fire = 40f;
				}
				else
				{
					Plugin.Log.LogWarning((object)"DeepSea_Serpent_Cast could not be adjusted.");
				}
				SetCharacterDrops(scene, prefab, MakeDrop(scene, "SerpentMeat", 4, 6, 1f), MakeDrop(scene, "SerpentScale", 8, 12, 1f), MakeDrop(scene, "FishRaw", 2, 4, 1f), MakeDrop(scene, "TrophyDeepSeaSerpent", 1, 1, 0.125f));
				Plugin.Log.LogInfo((object)"DeepSea_Serpent: HP 800; Cast 35 blunt + 40 fire; loot 4-6 SerpentMeat, 8-12 SerpentScale, 2-4 FishRaw, TrophyDeepSeaSerpent 12.5%. Other attacks/resistances preserved.");
			}
		}

		private static Drop MakeDrop(ZNetScene scene, string item, int min, int max, float chance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return new Drop
			{
				m_prefab = FindPrefab(scene, item),
				m_amountMin = min,
				m_amountMax = max,
				m_chance = chance,
				m_onePerPlayer = false,
				m_levelMultiplier = false
			};
		}

		private static void SetCharacterDrops(ZNetScene scene, GameObject prefab, params Drop[] desiredDrops)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			if (desiredDrops.Any((Drop x) => x == null || (Object)(object)x.m_prefab == (Object)null))
			{
				Plugin.Log.LogWarning((object)(((Object)prefab).name + ": one or more desired drop prefabs were not found; drops left unchanged."));
				return;
			}
			CharacterDrop val = prefab.GetComponent<CharacterDrop>();
			if ((Object)(object)val == (Object)null)
			{
				val = prefab.AddComponent<CharacterDrop>();
			}
			val.m_drops = desiredDrops.ToList();
		}

		private static void FilterShipDrops(GameObject ship, string cargoName, bool includeDwarfNoAttack)
		{
			CharacterDrop component = ship.GetComponent<CharacterDrop>();
			if (component?.m_drops != null)
			{
				component.m_drops.RemoveAll(delegate(Drop drop)
				{
					string text = (((Object)(object)drop?.m_prefab != (Object)null) ? ((Object)drop.m_prefab).name : "");
					return text == "DeerHide" || text == "ElderBark" || text == "FineWood";
				});
				NormalizeDropAmount(ship, cargoName, 3, 5);
				if (includeDwarfNoAttack)
				{
					NormalizeDropAmount(ship, "DwarfGoblin_NoAttack", 3, 5);
				}
			}
		}

		private static void NormalizeDropAmount(GameObject prefab, string dropPrefabName, int min, int max)
		{
			CharacterDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CharacterDrop>() : null);
			if (val?.m_drops == null)
			{
				return;
			}
			foreach (Drop drop in val.m_drops)
			{
				if (!((Object)(object)drop?.m_prefab == (Object)null) && !(((Object)drop.m_prefab).name != dropPrefabName))
				{
					drop.m_amountMin = min;
					drop.m_amountMax = max;
					drop.m_chance = 1f;
					drop.m_levelMultiplier = false;
				}
			}
		}

		internal static void ResetRuntimeGuardLog()
		{
			RuntimeGuardCorrectionLogged = false;
		}

		internal static void EnforceWorldSpawnWhitelist(List<SpawnData> spawners)
		{
			if (spawners == null)
			{
				return;
			}
			bool flag = false;
			foreach (SpawnData spawner in spawners)
			{
				if (!((Object)(object)spawner?.m_prefab == (Object)null))
				{
					string text = CleanPrefabName(((Object)spawner.m_prefab).name);
					if (MonsterLabZCreaturePrefabs.Contains(text) && !ConfigureWhitelistedSpawn(spawner, text) && spawner.m_enabled)
					{
						spawner.m_enabled = false;
						flag = true;
					}
				}
			}
			if (flag && !RuntimeGuardCorrectionLogged)
			{
				RuntimeGuardCorrectionLogged = true;
				Plugin.Log.LogInfo((object)"Runtime guard corrected non-curated MonsterLabZ world-spawn state.");
			}
		}

		internal static bool ConfigureDraugrCargo(ZNetScene scene)
		{
			GameObject prefab = scene.GetPrefab("ML_DraugrShip_Cargo");
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			Container val = prefab.GetComponent<Container>() ?? prefab.GetComponentInChildren<Container>(true);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"ML_DraugrShip_Cargo has no Container component.");
				return false;
			}
			ContainerDropSpec[] draugrCargoSpecs = DraugrCargoSpecs;
			foreach (ContainerDropSpec containerDropSpec in draugrCargoSpecs)
			{
				if ((Object)(object)FindPrefab(scene, containerDropSpec.Prefab) == (Object)null)
				{
					return false;
				}
			}
			try
			{
				object obj = FindField(((object)val).GetType(), "m_defaultItems")?.GetValue(val);
				if (obj == null)
				{
					Plugin.Log.LogWarning((object)"ML_DraugrShip_Cargo default drop table not found.");
					return false;
				}
				if (!(FindField(obj.GetType(), "m_drops")?.GetValue(obj) is IList list))
				{
					Plugin.Log.LogWarning((object)"ML_DraugrShip_Cargo drop list not found.");
					return false;
				}
				Type listElementType = GetListElementType(list.GetType());
				if (listElementType == null)
				{
					Plugin.Log.LogWarning((object)"ML_DraugrShip_Cargo drop element type not found.");
					return false;
				}
				list.Clear();
				draugrCargoSpecs = DraugrCargoSpecs;
				foreach (ContainerDropSpec containerDropSpec2 in draugrCargoSpecs)
				{
					object obj2 = Activator.CreateInstance(listElementType);
					SetField(obj2, "m_item", FindPrefab(scene, containerDropSpec2.Prefab));
					SetField(obj2, "m_stackMin", containerDropSpec2.Min);
					SetField(obj2, "m_stackMax", containerDropSpec2.Max);
					SetFieldIfPresent(obj2, "m_weight", containerDropSpec2.Weight);
					SetFieldIfPresent(obj2, "m_dontScale", true);
					list.Add(obj2);
				}
				SetFieldIfPresent(obj, "m_dropMin", 1);
				SetFieldIfPresent(obj, "m_dropMax", 3);
				Plugin.Log.LogInfo((object)"ML_DraugrShip_Cargo: 1-3 entries from BoarJerky 3-6, IronScrap 3-6, AmberPearl 4-6, RottenMeat 2-4. Processed Iron and Entrails removed.");
				return true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"ML_DraugrShip_Cargo could not be configured: {arg}");
				return false;
			}
		}

		internal static void EnsureSpawnSystemConfigured(SpawnSystem spawnSystem)
		{
			if (!((Object)(object)spawnSystem == (Object)null) && !ConfiguredSpawnSystems.TryGetValue(spawnSystem, out var _) && ConfigureSpawnSystem(spawnSystem, ZNetScene.instance))
			{
				ConfiguredSpawnSystems.Add(spawnSystem, new SpawnSystemState());
			}
		}

		private static bool ConfigureSpawnSystem(SpawnSystem spawnSystem, ZNetScene scene)
		{
			if (spawnSystem?.m_spawnLists == null || spawnSystem.m_spawnLists.Count == 0 || (Object)(object)scene == (Object)null)
			{
				return false;
			}
			bool flag = false;
			foreach (SpawnSystemList spawnList in spawnSystem.m_spawnLists)
			{
				if (spawnList?.m_spawners == null)
				{
					continue;
				}
				foreach (SpawnData spawner in spawnList.m_spawners)
				{
					if ((Object)(object)spawner?.m_prefab == (Object)null)
					{
						continue;
					}
					string text = CleanPrefabName(((Object)spawner.m_prefab).name);
					if (MonsterLabZCreaturePrefabs.Contains(text))
					{
						if (text == "Greydwarf_Purple_Shroom")
						{
							flag = true;
						}
						if (!ConfigureWhitelistedSpawn(spawner, text))
						{
							spawner.m_enabled = false;
						}
					}
				}
			}
			if (!flag && !EnsurePurpleSpawn(spawnSystem, scene))
			{
				return false;
			}
			return true;
		}

		private static bool ConfigureWhitelistedSpawn(SpawnData spawn, string prefabName)
		{
			switch (prefabName)
			{
			case "Greydwarf_Purple_Shroom":
				ConfigureLandSpawn(spawn, (Biome)8, (BiomeArea)3, day: false, night: true, 1, 1, 600f, 5f, 1, 0f, 1000f);
				return true;
			case "DwarfGoblin":
				ConfigureLandSpawn(spawn, (Biome)16, (BiomeArea)3, day: true, night: true, 3, 4, 450f, 15f, 5, 1f, 1000f);
				return true;
			case "DwarfGoblinShaman":
				ConfigureLandSpawn(spawn, (Biome)16, (BiomeArea)3, day: true, night: true, 1, 2, 450f, 10f, 2, 1f, 1000f);
				return true;
			case "DwarfGoblinLoot":
				ConfigureLandSpawn(spawn, (Biome)16, (BiomeArea)3, day: true, night: false, 1, 1, 300f, 5f, 1, 1f, 1000f);
				return true;
			case "ML_GoblinShip":
				ConfigureSeaSpawn(spawn, (Biome)16, (BiomeArea)1, day: true, night: false, "defeated_dragon", 15f, 20f, 1000f, 10f, 1);
				return true;
			case "ML_DraugrShip":
				ConfigureSeaSpawn(spawn, (Biome)8, (BiomeArea)1, day: false, night: true, "defeated_gdking", 15f, 20f, 1000f, 10f, 1, "Misty");
				return true;
			case "DeepSea_Serpent":
				ConfigureSeaSpawn(spawn, (Biome)256, (BiomeArea)2, day: false, night: true, "defeated_bonemass", 20f, 30f, 1000f, 5f, 1, null, -10f, -10f);
				return true;
			default:
				return false;
			}
		}

		private static void ConfigureLandSpawn(SpawnData spawn, Biome biome, BiomeArea area, bool day, bool night, int groupMin, int groupMax, float interval, float chance, int maxSpawned, float minAltitude, float maxAltitude)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			spawn.m_enabled = true;
			spawn.m_biome = biome;
			spawn.m_biomeArea = area;
			spawn.m_spawnAtDay = day;
			spawn.m_spawnAtNight = night;
			spawn.m_requiredGlobalKey = "";
			SetRequiredEnvironment(spawn, null);
			spawn.m_groupSizeMin = groupMin;
			spawn.m_groupSizeMax = groupMax;
			spawn.m_groupRadius = 3f;
			spawn.m_spawnInterval = interval;
			spawn.m_spawnChance = chance;
			spawn.m_maxSpawned = maxSpawned;
			spawn.m_spawnDistance = 10f;
			spawn.m_spawnRadiusMin = 0f;
			spawn.m_spawnRadiusMax = 0f;
			spawn.m_minAltitude = minAltitude;
			spawn.m_maxAltitude = maxAltitude;
			spawn.m_minTilt = 0f;
			spawn.m_maxTilt = 35f;
			spawn.m_inForest = true;
			spawn.m_outsideForest = true;
			spawn.m_minOceanDepth = 0f;
			spawn.m_maxOceanDepth = 0f;
			spawn.m_huntPlayer = false;
			spawn.m_groundOffset = 0.5f;
			DisableStars(spawn);
		}

		private static void ConfigureSeaSpawn(SpawnData spawn, Biome biome, BiomeArea area, bool day, bool night, string globalKey, float minDepth, float maxDepth, float interval, float chance, int maxSpawned, string requiredEnvironment = null, float groundOffset = 20f, float maxAltitude = 0f)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			spawn.m_enabled = true;
			spawn.m_biome = biome;
			spawn.m_biomeArea = area;
			spawn.m_spawnAtDay = day;
			spawn.m_spawnAtNight = night;
			spawn.m_requiredGlobalKey = globalKey ?? "";
			SetRequiredEnvironment(spawn, requiredEnvironment);
			spawn.m_groupSizeMin = 1;
			spawn.m_groupSizeMax = 1;
			spawn.m_groupRadius = 3f;
			spawn.m_spawnInterval = interval;
			spawn.m_spawnChance = chance;
			spawn.m_maxSpawned = maxSpawned;
			spawn.m_spawnDistance = 10f;
			spawn.m_spawnRadiusMin = 0f;
			spawn.m_spawnRadiusMax = 0f;
			spawn.m_minAltitude = -1000f;
			spawn.m_maxAltitude = maxAltitude;
			spawn.m_minTilt = 0f;
			spawn.m_maxTilt = 35f;
			spawn.m_inForest = true;
			spawn.m_outsideForest = true;
			spawn.m_minOceanDepth = minDepth;
			spawn.m_maxOceanDepth = maxDepth;
			spawn.m_huntPlayer = false;
			spawn.m_groundOffset = groundOffset;
			DisableStars(spawn);
		}

		private static void SetRequiredEnvironment(SpawnData spawn, string environment)
		{
			List<string> list = spawn.m_requiredEnvironments;
			if (list == null)
			{
				list = (spawn.m_requiredEnvironments = new List<string>((!string.IsNullOrEmpty(environment)) ? 1 : 0));
			}
			if (string.IsNullOrEmpty(environment))
			{
				if (list.Count != 0)
				{
					list.Clear();
				}
			}
			else if (list.Count != 1 || !(list[0] == environment))
			{
				list.Clear();
				list.Add(environment);
			}
		}

		private static void DisableStars(SpawnData spawn)
		{
			spawn.m_minLevel = 1;
			spawn.m_maxLevel = 1;
			spawn.m_overrideLevelupChance = 0f;
		}

		private static bool EnsurePurpleSpawn(SpawnSystem spawnSystem, ZNetScene scene)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			GameObject prefab = scene.GetPrefab("Greydwarf_Purple_Shroom");
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			foreach (SpawnSystemList spawnList in spawnSystem.m_spawnLists)
			{
				if (spawnList?.m_spawners != null)
				{
					SpawnData val = ((IEnumerable<SpawnData>)spawnList.m_spawners).FirstOrDefault((Func<SpawnData, bool>)((SpawnData x) => (Object)(object)x?.m_prefab != (Object)null && CleanPrefabName(((Object)x.m_prefab).name) == "Greydwarf_Purple_Shroom"));
					if (val != null)
					{
						ConfigureWhitelistedSpawn(val, "Greydwarf_Purple_Shroom");
						return true;
					}
				}
			}
			SpawnSystemList val2 = ((IEnumerable<SpawnSystemList>)spawnSystem.m_spawnLists).FirstOrDefault((Func<SpawnSystemList, bool>)((SpawnSystemList x) => (Object)(object)x != (Object)null && x.m_spawners != null));
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			SpawnData val3 = new SpawnData
			{
				m_name = "MLZB_Purple_Shroom",
				m_prefab = prefab
			};
			ConfigureWhitelistedSpawn(val3, "Greydwarf_Purple_Shroom");
			val2.m_spawners.Add(val3);
			return true;
		}

		private static int ReplaceAttackReference(GameObject[] items, string oldName, GameObject replacement)
		{
			if (items == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < items.Length; i++)
			{
				if ((Object)(object)items[i] != (Object)null && CleanPrefabName(((Object)items[i]).name) == oldName)
				{
					items[i] = replacement;
					num++;
				}
			}
			return num;
		}

		private static string CleanPrefabName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "";
			}
			if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring(0, name.Length - "(Clone)".Length);
		}

		private static FieldInfo FindField(Type type, string fieldName)
		{
			while (type != null)
			{
				FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type = type.BaseType;
			}
			return null;
		}

		private static void SetField(object target, string fieldName, object value)
		{
			FieldInfo fieldInfo = FindField(target.GetType(), fieldName);
			if (fieldInfo == null)
			{
				throw new MissingFieldException(target.GetType().FullName, fieldName);
			}
			fieldInfo.SetValue(target, value);
		}

		private static void SetFieldIfPresent(object target, string fieldName, object value)
		{
			FindField(target.GetType(), fieldName)?.SetValue(target, value);
		}

		private static Type GetListElementType(Type listType)
		{
			if (listType.IsGenericType)
			{
				return listType.GetGenericArguments().FirstOrDefault();
			}
			Type[] interfaces = listType.GetInterfaces();
			foreach (Type type in interfaces)
			{
				if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>))
				{
					return type.GetGenericArguments()[0];
				}
			}
			return null;
		}
	}
	[HarmonyPatch]
	internal static class SpawnSystemUpdateSpawnListPatch
	{
		private static MethodBase TargetMethod()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(SpawnSystem), "UpdateSpawnList", new Type[4]
			{
				typeof(List<SpawnData>),
				typeof(DateTime),
				typeof(bool),
				typeof(string)
			}, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("SpawnSystem.UpdateSpawnList(List<SpawnData>, DateTime, bool, string) was not found.");
			}
			return methodInfo;
		}

		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Prefix(SpawnSystem __instance, List<SpawnData> __0, bool __2)
		{
			if (!__2)
			{
				Balance.EnsureSpawnSystemConfigured(__instance);
				Balance.EnforceWorldSpawnWhitelist(__0);
			}
		}
	}
}