Decompiled source of CraftableSpawners v0.2.2

CraftableSpawners.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CraftableSpawners.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MushroomMods;
using MushroomSync;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Gonfreecss")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Craftable natural spawners for Valheim")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2+0f34766723a6669ef7b79dbcc093f8b56065e786")]
[assembly: AssemblyProduct("CraftableSpawners")]
[assembly: AssemblyTitle("CraftableSpawners")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.2.0")]
[module: UnverifiableCode]
[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 MushroomMods
{
	internal static class PatchIsolation
	{
		internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					num++;
					log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex));
				}
			}
			return num;
		}
	}
}
namespace CraftableSpawners
{
	public enum SpawnerId
	{
		Skeleton,
		Greydwarf,
		Draugr,
		Surtling,
		TarBlob
	}
	[BepInPlugin("Gonfreecss.CraftableSpawners", "CraftableSpawners", "0.2.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class CraftableSpawnersPlugin : BaseUnityPlugin
	{
		public const string PluginID = "Gonfreecss.CraftableSpawners";

		public const string PluginName = "CraftableSpawners";

		public const string Version = "0.2.2";

		internal static ManualLogSource Log = new ManualLogSource(" CraftableSpawners");

		internal static ModConfig Settings;

		internal static bool HammerRemoving;

		internal static ConfigSync Sync;

		private readonly Harmony harmony = new Harmony("Gonfreecss.CraftableSpawners");

		internal void Awake()
		{
			Logger.Sources.Add((ILogSource)(object)Log);
			ConfigFile val = ConfigPaths.CreateMergedConfig();
			Sync = ConfigSync.Create("Gonfreecss.CraftableSpawners", "0.2.2", Log).Protecting(val).OnApplied((Action)SpawnerSetup.RefreshFromConfig);
			Settings = new ModConfig(val);
			Sync.GatedBy((Func<bool>)(() => Settings == null || Settings.LockConfiguration)).WatchForChanges(val).Start();
			int num = PatchIsolation.PatchAllIsolated(harmony, Assembly.GetExecutingAssembly(), Log);
			Dbgl("Loaded CraftableSpawners 0.2.2" + ((num > 0) ? $" - {num} patch class(es) skipped, see the errors above." : string.Empty), num > 0);
		}

		internal static void Dbgl(string message, bool forceLog = false)
		{
			if (!forceLog)
			{
				ModConfig settings = Settings;
				if (settings == null || !settings.EnableDebugMessages)
				{
					return;
				}
			}
			Log.LogInfo((object)message);
		}
	}
	internal sealed class SpawnerDef
	{
		internal SpawnerId Id;

		internal string SourcePrefab;

		internal string CloneName;

		internal string DisplayName;

		internal string Description;

		internal string TrophyPrefab;

		internal GameObject Prefab;
	}
	internal static class SpawnerCatalog
	{
		internal static readonly List<SpawnerDef> All = new List<SpawnerDef>(5)
		{
			new SpawnerDef
			{
				Id = SpawnerId.Skeleton,
				SourcePrefab = "BonePileSpawner",
				CloneName = "CS_BonePileSpawner",
				DisplayName = "Evil bone pile",
				Description = "Spawns skeletons",
				TrophyPrefab = "TrophySkeleton"
			},
			new SpawnerDef
			{
				Id = SpawnerId.Greydwarf,
				SourcePrefab = "Spawner_GreydwarfNest",
				CloneName = "CS_GreydwarfNest",
				DisplayName = "Greydwarf nest",
				Description = "Spawns greydwarves",
				TrophyPrefab = "TrophyGreydwarf"
			},
			new SpawnerDef
			{
				Id = SpawnerId.Draugr,
				SourcePrefab = "Spawner_DraugrPile",
				CloneName = "CS_DraugrPile",
				DisplayName = "Body pile",
				Description = "Spawns draugr",
				TrophyPrefab = "TrophyDraugr"
			},
			new SpawnerDef
			{
				Id = SpawnerId.Surtling,
				SourcePrefab = "BonePileSpawner",
				CloneName = "CS_FirePillar",
				DisplayName = "Fire pillar",
				Description = "Spawns surtlings",
				TrophyPrefab = "TrophySurtling"
			},
			new SpawnerDef
			{
				Id = SpawnerId.TarBlob,
				SourcePrefab = "BonePileSpawner",
				CloneName = "CS_TarBonePile",
				DisplayName = "Bone pile",
				Description = "Spawns tar blobs",
				TrophyPrefab = "TrophyGrowth"
			}
		};

		internal static SpawnerDef FindByCloneName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			name = name.Replace("(Clone)", "").Trim();
			foreach (SpawnerDef item in All)
			{
				if (item.CloneName == name)
				{
					return item;
				}
			}
			return null;
		}

		internal static SpawnerDef FindByTrophyPrefab(string trophyPrefab)
		{
			foreach (SpawnerDef item in All)
			{
				if (item.TrophyPrefab == trophyPrefab)
				{
					return item;
				}
			}
			return null;
		}
	}
	internal static class Patches
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetSceneAwake
		{
			private static void Postfix()
			{
				SpawnerSetup.EnsureInitialized();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDBAwake
		{
			private static void Postfix()
			{
				SpawnerSetup.EnsureInitialized();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class ObjectDBCopyOtherDB
		{
			private static void Postfix()
			{
				SpawnerSetup.EnsureInitialized();
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static class PlayerOnSpawned
		{
			private static void Postfix(Player __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					SpawnerSetup.UnlockKnownSpawners(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "AddKnownItem")]
		private static class PlayerAddKnownItem
		{
			private static void Postfix(Player __instance, ItemData item)
			{
				SpawnerSetup.TryUnlockFromItem(__instance, item);
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateAvailablePiecesList")]
		private static class PlayerUpdateAvailablePiecesList
		{
			private static void Prefix()
			{
				SpawnerSetup.EnsurePiecesInHammerTables();
			}
		}

		[HarmonyPatch(typeof(Piece), "SetCreator")]
		private static class PieceSetCreator
		{
			private static void Postfix(Piece __instance)
			{
				SpawnerSetup.OnCraftableSpawnerPlaced(__instance);
			}
		}

		[HarmonyPatch(typeof(SpawnArea), "SpawnOne")]
		private static class SpawnAreaSpawnOne
		{
			private static void Postfix(SpawnArea __instance, bool __result)
			{
				ModConfig settings = CraftableSpawnersPlugin.Settings;
				if (settings != null && settings.EnableDebugMessages && SpawnerSetup.IsCraftableSpawner((Component)(object)__instance))
				{
					CraftableSpawnersPlugin.Log.LogInfo((object)($"SpawnOne on {((Object)__instance).name} => {__result} " + $"(prefabs={__instance.m_prefabs?.Count ?? 0}, interval={__instance.m_spawnIntervalSec})"));
				}
			}
		}

		[HarmonyPatch(typeof(Player), "RemovePiece")]
		private static class PlayerRemovePiece
		{
			private static bool Prefix(Player __instance, ref bool __result)
			{
				if (!SpawnerSetup.TryHammerRemoveCraftableSpawner(__instance))
				{
					return true;
				}
				__result = true;
				return false;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Destroy")]
		private static class WearNTearDestroy
		{
			private static void Prefix(WearNTear __instance)
			{
				if (!CraftableSpawnersPlugin.HammerRemoving && SpawnerSetup.IsCraftableSpawner((Component)(object)__instance))
				{
					SpawnerSetup.DropRecipeAsWorldPickups(((Component)__instance).gameObject);
				}
			}
		}

		[HarmonyPatch(typeof(Destructible), "Destroy")]
		private static class DestructibleDestroy
		{
			private static void Prefix(Destructible __instance)
			{
				if (!CraftableSpawnersPlugin.HammerRemoving && SpawnerSetup.IsCraftableSpawner((Component)(object)__instance))
				{
					SpawnerSetup.DropRecipeAsWorldPickups(((Component)__instance).gameObject);
				}
			}
		}
	}
	internal static class SpawnerSetup
	{
		private static GameObject cloneRoot;

		private static bool initialized;

		private static readonly HashSet<int> refundedInstanceIds = new HashSet<int>();

		internal static void EnsureInitialized()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			ZNetScene instance = ZNetScene.instance;
			if (initialized)
			{
				if (Object.op_Implicit((Object)(object)instance))
				{
					RegisterAllPrefabs(instance);
				}
				RefreshFromConfig();
			}
			else
			{
				if (!Object.op_Implicit((Object)(object)instance) || (Object)(object)ObjectDB.instance == (Object)null)
				{
					return;
				}
				cloneRoot = new GameObject("CS_SpawnerPrefabs");
				Object.DontDestroyOnLoad((Object)(object)cloneRoot);
				cloneRoot.SetActive(false);
				FindPlaceEffectSource(instance, out var piece);
				foreach (SpawnerDef item in SpawnerCatalog.All)
				{
					GameObject prefab = instance.GetPrefab(item.SourcePrefab);
					if (!Object.op_Implicit((Object)(object)prefab))
					{
						CraftableSpawnersPlugin.Log.LogError((object)("Could not find source prefab '" + item.SourcePrefab + "'"));
						continue;
					}
					GameObject val = Object.Instantiate<GameObject>(prefab, cloneRoot.transform);
					((Object)val).name = item.CloneName;
					item.Prefab = val;
					ConfigurePiece(item, piece);
					if (item.Id == SpawnerId.Surtling)
					{
						ConfigureFirePillar(item, instance);
					}
					else if (item.Id == SpawnerId.TarBlob)
					{
						ConfigureTarBonePile(item, instance);
					}
					RetargetHitboxColliders(val);
					RegisterPrefab(instance, val);
				}
				RefreshFromConfig();
				initialized = true;
				CraftableSpawnersPlugin.Dbgl("Craftable spawners initialized.", forceLog: true);
			}
		}

		private static void FindPlaceEffectSource(ZNetScene scene, out Piece piece)
		{
			piece = null;
			string[] array = new string[4] { "wood_wall", "wood_floor", "wood_door", "piece_chest_wood" };
			foreach (string text in array)
			{
				GameObject prefab = scene.GetPrefab(text);
				if (Object.op_Implicit((Object)(object)prefab))
				{
					piece = prefab.GetComponent<Piece>();
					if (Object.op_Implicit((Object)(object)piece))
					{
						break;
					}
				}
			}
		}

		private static void ConfigurePiece(SpawnerDef def, Piece placeEffectSource)
		{
			//IL_0035: 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)
			Piece val = def.Prefab.GetComponent<Piece>() ?? def.Prefab.AddComponent<Piece>();
			val.m_name = def.DisplayName;
			val.m_description = def.Description;
			val.m_category = (PieceCategory)0;
			val.m_usage = (UsageTagFlags)1;
			val.m_craftingStation = null;
			val.m_groundOnly = true;
			val.m_groundPiece = true;
			val.m_canBeRemoved = true;
			((StaticTarget)val).m_primaryTarget = false;
			((StaticTarget)val).m_randomTarget = true;
			val.m_targetNonPlayerBuilt = false;
			val.m_enabled = true;
			if ((Object)(object)placeEffectSource != (Object)null)
			{
				val.m_placeEffect = placeEffectSource.m_placeEffect;
			}
			WearNTear component = def.Prefab.GetComponent<WearNTear>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			ApplyRecipeAndIcon(def);
		}

		private static void RetargetHitboxColliders(GameObject prefab)
		{
			int num = LayerMask.NameToLayer("hitbox");
			int num2 = LayerMask.NameToLayer("piece");
			if (num < 0)
			{
				return;
			}
			Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Component)val).gameObject.layer == num)
				{
					if (num2 >= 0)
					{
						((Component)val).gameObject.layer = num2;
					}
					else
					{
						val.enabled = false;
					}
				}
			}
		}

		private static void ConfigureFirePillar(SpawnerDef def, ZNetScene scene)
		{
			ConfigureCustomSpawnArea(def, scene, "Surtling", "Spawner_imp_respawn", "Fire pillar");
			AttachBorrowedVisual(def.Prefab, scene, "CS_FireVisual", new string[5] { "bonfire", "fire_pit", "piece_groundtorch", "piece_groundtorch_wood", "hearth" }, (MonoBehaviour behaviour) => behaviour is LightFlicker, "No fire visual prefab found; Fire pillar will use bone pile collision only");
		}

		private static void ConfigureTarBonePile(SpawnerDef def, ZNetScene scene)
		{
			ConfigureCustomSpawnArea(def, scene, "BlobTar", "Spawner_BlobTar_respawn_30", "Bone pile (tar blob)");
			AttachBorrowedVisual(def.Prefab, scene, "CS_TarBoneVisual", new string[1] { "lox_ribs" }, (MonoBehaviour _) => false, "lox_ribs visual not found; Bone pile will keep Evil bone pile mesh");
		}

		private static void ConfigureCustomSpawnArea(SpawnerDef def, ZNetScene scene, string mobPrefabName, string vanillaCreatureSpawnerName, string label)
		{
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Expected O, but got Unknown
			GameObject prefab = scene.GetPrefab(mobPrefabName);
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				CraftableSpawnersPlugin.Log.LogError((object)("Could not find " + mobPrefabName + " prefab for " + label));
				return;
			}
			SpawnArea component = def.Prefab.GetComponent<SpawnArea>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				CraftableSpawnersPlugin.Log.LogError((object)(label + " base is missing SpawnArea"));
				return;
			}
			GameObject prefab2 = scene.GetPrefab("Spawner_GreydwarfNest");
			SpawnArea val = ((prefab2 != null) ? prefab2.GetComponent<SpawnArea>() : null);
			GameObject prefab3 = scene.GetPrefab("BonePileSpawner");
			SpawnArea val2 = ((prefab3 != null) ? prefab3.GetComponent<SpawnArea>() : null);
			SpawnArea val3 = (Object.op_Implicit((Object)(object)val) ? val : val2);
			GameObject prefab4 = scene.GetPrefab(vanillaCreatureSpawnerName);
			CreatureSpawner val4 = ((prefab4 != null) ? prefab4.GetComponent<CreatureSpawner>() : null);
			if (Object.op_Implicit((Object)(object)val3))
			{
				component.m_spawnIntervalSec = val3.m_spawnIntervalSec;
				component.m_triggerDistance = val3.m_triggerDistance;
				component.m_spawnRadius = val3.m_spawnRadius;
				component.m_nearRadius = val3.m_nearRadius;
				component.m_farRadius = val3.m_farRadius;
				component.m_maxNear = val3.m_maxNear;
				component.m_maxTotal = val3.m_maxTotal;
				component.m_setPatrolSpawnPoint = val3.m_setPatrolSpawnPoint;
				component.m_levelupChance = val3.m_levelupChance;
				component.m_spawnEffects = val3.m_spawnEffects;
			}
			component.m_onGroundOnly = false;
			component.m_spawnIntervalSec = 20f;
			if (component.m_triggerDistance <= 0f)
			{
				component.m_triggerDistance = 60f;
			}
			if (component.m_maxNear <= 0)
			{
				component.m_maxNear = 3;
			}
			if (component.m_maxTotal <= 0)
			{
				component.m_maxTotal = 3;
			}
			if (component.m_spawnRadius <= 0f)
			{
				component.m_spawnRadius = 4f;
			}
			int num = ((!Object.op_Implicit((Object)(object)val4)) ? 1 : val4.m_minLevel);
			int num2 = ((!Object.op_Implicit((Object)(object)val4)) ? 1 : val4.m_maxLevel);
			component.m_prefabs = new List<SpawnData>(1)
			{
				new SpawnData
				{
					m_prefab = prefab,
					m_weight = 1f,
					m_minLevel = num,
					m_maxLevel = Mathf.Max(num, num2)
				}
			};
			HoverText component2 = def.Prefab.GetComponent<HoverText>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.m_text = def.DisplayName;
			}
			CraftableSpawnersPlugin.Dbgl($"{label} SpawnArea: interval={component.m_spawnIntervalSec}s " + $"trigger={component.m_triggerDistance} maxNear={component.m_maxNear} maxTotal={component.m_maxTotal} " + "prefab=" + (Object.op_Implicit((Object)(object)component.m_prefabs[0].m_prefab) ? ((Object)component.m_prefabs[0].m_prefab).name : "null"));
		}

		private static void AttachBorrowedVisual(GameObject host, ZNetScene scene, string visualName, string[] candidatePrefabs, Func<MonoBehaviour, bool> keepBehaviours, string missingVisualWarning)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = host.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].enabled = false;
			}
			GameObject val = null;
			foreach (string text in candidatePrefabs)
			{
				val = scene.GetPrefab(text);
				if (Object.op_Implicit((Object)(object)val))
				{
					break;
				}
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				CraftableSpawnersPlugin.Log.LogWarning((object)missingVisualWarning);
				componentsInChildren = host.GetComponentsInChildren<Renderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					componentsInChildren[i].enabled = true;
				}
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, host.transform);
			((Object)val2).name = visualName;
			val2.transform.localPosition = Vector3.zero;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one;
			Collider[] componentsInChildren2 = val2.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			Rigidbody[] componentsInChildren3 = val2.GetComponentsInChildren<Rigidbody>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
			}
			MonoBehaviour[] componentsInChildren4 = val2.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val3 in componentsInChildren4)
			{
				if (Object.op_Implicit((Object)(object)val3) && !keepBehaviours(val3))
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			val2.SetActive(true);
		}

		private static void ApplyRecipeAndIcon(SpawnerDef def)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)def.Prefab) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			Piece component = def.Prefab.GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			List<(string item, int amount)> recipe = CraftableSpawnersPlugin.Settings.GetRecipe(def.Id);
			List<Requirement> list = new List<Requirement>();
			foreach (var (text, num) in recipe)
			{
				if (num > 0)
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
					if (!Object.op_Implicit((Object)(object)itemPrefab))
					{
						CraftableSpawnersPlugin.Log.LogWarning((object)("Missing recipe item prefab '" + text + "' for " + def.CloneName));
						continue;
					}
					list.Add(new Requirement
					{
						m_resItem = itemPrefab.GetComponent<ItemDrop>(),
						m_amount = num,
						m_recover = true
					});
				}
			}
			component.m_resources = list.ToArray();
			GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab(def.TrophyPrefab);
			ItemDrop val = default(ItemDrop);
			if (Object.op_Implicit((Object)(object)itemPrefab2) && itemPrefab2.TryGetComponent<ItemDrop>(ref val))
			{
				component.m_icon = val.m_itemData.GetIcon();
			}
		}

		private static bool RegisterPrefab(ZNetScene scene, GameObject prefab)
		{
			if (!scene.m_prefabs.Contains(prefab))
			{
				scene.m_prefabs.Add(prefab);
			}
			int prefabHash = scene.GetPrefabHash(prefab);
			bool result = !scene.m_namedPrefabs.ContainsKey(prefabHash);
			scene.m_namedPrefabs[prefabHash] = prefab;
			return result;
		}

		private static void RegisterAllPrefabs(ZNetScene scene)
		{
			int num = 0;
			foreach (SpawnerDef item in SpawnerCatalog.All)
			{
				if (Object.op_Implicit((Object)(object)item.Prefab) && RegisterPrefab(scene, item.Prefab))
				{
					num++;
				}
			}
			if (num > 0)
			{
				CraftableSpawnersPlugin.Log.LogInfo((object)$"Registered {num} spawner prefab(s) with ZNetScene.");
			}
		}

		internal static void RefreshFromConfig()
		{
			if (initialized)
			{
				EnsurePiecesInHammerTables();
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					UnlockKnownSpawners(Player.m_localPlayer);
				}
			}
		}

		internal static void EnsurePiecesInHammerTables()
		{
			if (!initialized)
			{
				return;
			}
			List<PieceTable> hammerTables = GetHammerTables();
			if (hammerTables.Count == 0)
			{
				if ((Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab("Hammer"))) || (Object.op_Implicit((Object)(object)ZNetScene.instance) && Object.op_Implicit((Object)(object)ZNetScene.instance.GetPrefab("Hammer"))))
				{
					CraftableSpawnersPlugin.Log.LogWarning((object)"The Hammer has no build PieceTable; spawner pieces were not added.");
				}
				return;
			}
			ModConfig settings = CraftableSpawnersPlugin.Settings;
			foreach (SpawnerDef item in SpawnerCatalog.All)
			{
				if (!Object.op_Implicit((Object)(object)item.Prefab))
				{
					continue;
				}
				ApplyRecipeAndIcon(item);
				bool flag = settings.IsEnabled(item.Id);
				foreach (PieceTable item2 in hammerTables)
				{
					bool flag2 = item2.m_pieces.Contains(item.Prefab);
					if (flag && !flag2)
					{
						item2.m_pieces.Add(item.Prefab);
						CraftableSpawnersPlugin.Dbgl($"Added {item.CloneName} to Hammer PieceTable ({item2.m_pieces.Count} pieces)");
					}
					else if (!flag && flag2)
					{
						item2.m_pieces.Remove(item.Prefab);
					}
				}
			}
		}

		private static List<PieceTable> GetHammerTables()
		{
			List<PieceTable> tables = new List<PieceTable>();
			HashSet<int> seen = new HashSet<int>();
			if (Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				TryAdd(ZNetScene.instance.GetPrefab("Hammer"));
			}
			if (Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				TryAdd(ObjectDB.instance.GetItemPrefab("Hammer"));
			}
			if (IsHoldingHammer(Player.m_localPlayer))
			{
				TryAdd(((Humanoid)Player.m_localPlayer).GetRightItem()?.m_dropPrefab);
				PieceTable buildPieces = Player.m_localPlayer.m_buildPieces;
				if (Object.op_Implicit((Object)(object)buildPieces))
				{
					int instanceID = ((Object)buildPieces).GetInstanceID();
					if (seen.Add(instanceID))
					{
						tables.Add(buildPieces);
					}
				}
			}
			return tables;
			void TryAdd(GameObject hammer)
			{
				PieceTable val = ((hammer == null) ? null : hammer.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces);
				if (Object.op_Implicit((Object)(object)val))
				{
					int instanceID2 = ((Object)val).GetInstanceID();
					if (seen.Add(instanceID2))
					{
						tables.Add(val);
					}
				}
			}
		}

		private static bool IsHoldingHammer(Player player)
		{
			ItemData val = ((player != null) ? ((Humanoid)player).GetRightItem() : null);
			if (val == null)
			{
				return false;
			}
			if ((Object.op_Implicit((Object)(object)val.m_dropPrefab) ? ((Object)val.m_dropPrefab).name.Replace("(Clone)", "").Trim() : null) == "Hammer")
			{
				return true;
			}
			return val.m_shared?.m_name == "$item_hammer";
		}

		internal static void UnlockKnownSpawners(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			ModConfig settings = CraftableSpawnersPlugin.Settings;
			bool flag = false;
			foreach (SpawnerDef item in SpawnerCatalog.All)
			{
				if (Object.op_Implicit((Object)(object)item.Prefab) && settings.IsEnabled(item.Id))
				{
					Piece component = item.Prefab.GetComponent<Piece>();
					if (Object.op_Implicit((Object)(object)component) && PlayerKnowsTrophy(player, item.TrophyPrefab) && !player.IsRecipeKnown(component.m_name))
					{
						player.AddKnownPiece(component);
						flag = true;
						CraftableSpawnersPlugin.Dbgl("Retro-unlocked " + item.DisplayName);
					}
				}
			}
			if (flag && (Object)(object)player == (Object)(object)Player.m_localPlayer && IsHoldingHammer(player))
			{
				player.UpdateAvailablePiecesList();
			}
		}

		internal static void TryUnlockFromItem(Player player, ItemData item)
		{
			if (!Object.op_Implicit((Object)(object)player) || item?.m_shared == null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			ItemDrop val = default(ItemDrop);
			foreach (SpawnerDef item2 in SpawnerCatalog.All)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item2.TrophyPrefab);
				if (!Object.op_Implicit((Object)(object)itemPrefab) || !itemPrefab.TryGetComponent<ItemDrop>(ref val) || item.m_shared.m_name != val.m_itemData.m_shared.m_name || !CraftableSpawnersPlugin.Settings.IsEnabled(item2.Id) || !Object.op_Implicit((Object)(object)item2.Prefab))
				{
					continue;
				}
				Piece component = item2.Prefab.GetComponent<Piece>();
				if (Object.op_Implicit((Object)(object)component) && !player.IsRecipeKnown(component.m_name))
				{
					player.AddKnownPiece(component);
					CraftableSpawnersPlugin.Dbgl("Unlocked " + item2.DisplayName + " from trophy " + item2.TrophyPrefab);
					if ((Object)(object)player == (Object)(object)Player.m_localPlayer && IsHoldingHammer(player))
					{
						player.UpdateAvailablePiecesList();
					}
				}
				break;
			}
		}

		private static bool PlayerKnowsTrophy(Player player, string trophyPrefabName)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(trophyPrefabName);
			ItemDrop val = default(ItemDrop);
			if (!Object.op_Implicit((Object)(object)itemPrefab) || !itemPrefab.TryGetComponent<ItemDrop>(ref val))
			{
				return false;
			}
			return player.IsMaterialKnown(val.m_itemData.m_shared.m_name);
		}

		internal static void OnCraftableSpawnerPlaced(Piece piece)
		{
			if (!IsCraftableSpawner((Component)(object)piece))
			{
				return;
			}
			RetargetHitboxColliders(((Component)piece).gameObject);
			SpawnArea component = ((Component)piece).GetComponent<SpawnArea>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			SpawnerId? spawnerId = SpawnerCatalog.FindByCloneName(((Object)((Component)piece).transform.root).name)?.Id;
			bool flag;
			if (spawnerId.HasValue)
			{
				SpawnerId valueOrDefault = spawnerId.GetValueOrDefault();
				if ((uint)(valueOrDefault - 3) <= 1u)
				{
					flag = true;
					goto IL_0071;
				}
			}
			flag = false;
			goto IL_0071;
			IL_0071:
			if (flag)
			{
				component.m_spawnIntervalSec = 20f;
				component.m_spawnTimer = 0f;
			}
			else
			{
				component.m_spawnTimer = component.m_spawnIntervalSec;
			}
			CraftableSpawnersPlugin.Dbgl($"Placed {((Object)piece).name}: interval={component.m_spawnIntervalSec}s " + $"timer={component.m_spawnTimer} prefabs={component.m_prefabs?.Count ?? 0}");
		}

		internal static bool IsCraftableSpawner(Component component)
		{
			if (!Object.op_Implicit((Object)(object)component))
			{
				return false;
			}
			return SpawnerCatalog.FindByCloneName(((Object)component.transform.root).name) != null;
		}

		internal static bool TryHammerRemoveCraftableSpawner(Player player)
		{
			//IL_0022: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: 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_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)GameCamera.instance))
			{
				return false;
			}
			Transform transform = ((Component)GameCamera.instance).transform;
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(transform.position, transform.forward, ref val, 50f, player.m_removeRayMask))
			{
				return false;
			}
			if (Vector3.Distance(((RaycastHit)(ref val)).point, ((Character)player).m_eye.position) >= player.m_maxPlaceDistance)
			{
				return false;
			}
			Piece componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>();
			if (!IsCraftableSpawner((Component)(object)componentInParent) || !componentInParent.m_canBeRemoved)
			{
				return false;
			}
			if (Location.IsInsideNoBuildLocation(((Component)componentInParent).transform.position))
			{
				((Character)player).Message((MessageType)2, "$msg_nobuildzone", 0, (Sprite)null, false);
				return true;
			}
			if (!PrivateArea.CheckAccess(((Component)componentInParent).transform.position, 0f, true, false))
			{
				((Character)player).Message((MessageType)2, "$msg_privatezone", 0, (Sprite)null, false);
				return true;
			}
			if (!player.CheckCanRemovePiece(componentInParent))
			{
				return true;
			}
			ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return true;
			}
			if (!componentInParent.CanBeRemoved())
			{
				((Character)player).Message((MessageType)2, "$msg_cantremovenow", 0, (Sprite)null, false);
				return true;
			}
			CraftableSpawnersPlugin.HammerRemoving = true;
			try
			{
				WearNTear component2 = ((Component)componentInParent).GetComponent<WearNTear>();
				if (Object.op_Implicit((Object)(object)component2))
				{
					Object.DestroyImmediate((Object)(object)component2);
				}
				component.ClaimOwnership();
				RefundToInventory(player, componentInParent);
				if (componentInParent.m_placeEffect != null)
				{
					componentInParent.m_placeEffect.Create(((Component)componentInParent).transform.position, ((Component)componentInParent).transform.rotation, ((Component)componentInParent).transform, 1f, -1, default(ZDOID));
				}
				player.m_removeEffects.Create(((Component)componentInParent).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				ZNetScene.instance.Destroy(((Component)componentInParent).gameObject);
				ItemData rightItem = ((Humanoid)player).GetRightItem();
				if (rightItem != null)
				{
					player.FaceLookDirection();
					((Character)player).m_zanim.SetTrigger(rightItem.m_shared.m_attack.m_attackAnimation);
				}
				CraftableSpawnersPlugin.Dbgl("Hammer-removed " + ((Object)componentInParent).name);
			}
			finally
			{
				CraftableSpawnersPlugin.HammerRemoving = false;
			}
			return true;
		}

		private static void RefundToInventory(Player player, Piece piece)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if (piece?.m_resources == null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			Vector3 val = ((Component)piece).transform.position + Vector3.up * 0.5f;
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val2 in resources)
			{
				if ((Object)(object)val2?.m_resItem == (Object)null || !val2.m_recover || val2.m_amount <= 0)
				{
					continue;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(((Object)val2.m_resItem).name);
				if (!Object.op_Implicit((Object)(object)itemPrefab))
				{
					continue;
				}
				int num = val2.m_amount;
				while (num > 0)
				{
					ItemData val3 = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
					int num2 = (val3.m_stack = Mathf.Min(num, val3.m_shared.m_maxStackSize));
					val3.m_dropPrefab = itemPrefab;
					if (inventory.AddItem(val3))
					{
						num -= num2;
						continue;
					}
					ItemDrop.DropItem(val3, num2, val, Quaternion.identity);
					num -= num2;
				}
			}
			piece.m_resources = Array.Empty<Requirement>();
		}

		internal static void DropRecipeAsWorldPickups(GameObject go)
		{
			//IL_006a: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)go) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)go).GetInstanceID();
			if (!refundedInstanceIds.Add(instanceID))
			{
				return;
			}
			SpawnerDef spawnerDef = SpawnerCatalog.FindByCloneName(((Object)go.transform.root).name);
			if (spawnerDef == null)
			{
				return;
			}
			Piece componentInParent = go.GetComponentInParent<Piece>();
			if (componentInParent?.m_resources == null || componentInParent.m_resources.Length == 0)
			{
				return;
			}
			Vector3 val = go.transform.position + Vector3.up * 0.5f;
			Requirement[] resources = componentInParent.m_resources;
			componentInParent.m_resources = Array.Empty<Requirement>();
			try
			{
				Requirement[] array = resources;
				foreach (Requirement val2 in array)
				{
					if ((Object)(object)val2?.m_resItem == (Object)null || val2.m_amount <= 0)
					{
						continue;
					}
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(((Object)val2.m_resItem).name);
					if (!Object.op_Implicit((Object)(object)itemPrefab))
					{
						CraftableSpawnersPlugin.Log.LogWarning((object)("Missing drop prefab for " + ((Object)val2.m_resItem).name + "; that part of the refund was not dropped."));
						continue;
					}
					int num = val2.m_amount;
					while (num > 0)
					{
						ItemData itemData = itemPrefab.GetComponent<ItemDrop>().m_itemData;
						int num2 = Mathf.Min(num, itemData.m_shared.m_maxStackSize);
						ItemDrop component = Object.Instantiate<GameObject>(itemPrefab, val, Quaternion.identity).GetComponent<ItemDrop>();
						component.SetStack(num2);
						ItemDrop.OnCreateNew(component, false);
						num -= num2;
					}
				}
				CraftableSpawnersPlugin.Dbgl("Dropped world refund for " + spawnerDef.CloneName);
			}
			catch (Exception arg)
			{
				CraftableSpawnersPlugin.Log.LogError((object)$"Combat refund failed for {spawnerDef.CloneName}: {arg}");
			}
		}
	}
}
namespace CraftableSpawners.Configuration
{
	internal static class ConfigPaths
	{
		internal const string ConfigFileName = "CraftableSpawners.cfg";

		internal static string ClientPath => Path.Combine(Paths.GameRootPath, "BepInEx", "config", "CraftableSpawners.cfg");

		internal static string ServerPath => Path.Combine(Paths.GameRootPath, "config", "bepinex", "CraftableSpawners.cfg");

		internal static bool IsDedicatedServer()
		{
			if (Process.GetCurrentProcess().ProcessName.IndexOf("valheim_server", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			for (int i = 0; i < commandLineArgs.Length; i++)
			{
				if (string.Equals(commandLineArgs[i], "-batchmode", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal static ConfigFile CreateMergedConfig()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			string clientPath = ClientPath;
			string serverPath = ServerPath;
			bool flag = File.Exists(clientPath);
			bool flag2 = File.Exists(serverPath);
			bool flag3 = IsDedicatedServer();
			string text = (flag ? clientPath : ((!flag2) ? (flag3 ? serverPath : clientPath) : serverPath));
			ConfigFile result = new ConfigFile(text, true);
			if (flag && flag2)
			{
				CraftableSpawnersPlugin.Log.LogInfo((object)("Config: using " + clientPath + " with overlay from " + serverPath + "."));
			}
			else if (flag2)
			{
				CraftableSpawnersPlugin.Log.LogInfo((object)("Config: loaded dedicated server path " + serverPath + "."));
			}
			else if (flag)
			{
				CraftableSpawnersPlugin.Log.LogInfo((object)("Config: loaded client path " + clientPath + "."));
			}
			else
			{
				CraftableSpawnersPlugin.Log.LogInfo((object)(flag3 ? ("Config: no file yet; will create " + serverPath + ".") : ("Config: no file yet; will create " + clientPath + " (also reads " + serverPath + " when present).")));
			}
			return result;
		}

		internal static void ApplyAlternateOverlay(ConfigFile config)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!File.Exists(ClientPath) || !File.Exists(ServerPath))
			{
				return;
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in new ConfigFile(ServerPath, false))
			{
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item2 in config)
				{
					if (item2.Key.Equals(item.Key))
					{
						item2.Value.SetSerializedValue(item.Value.GetSerializedValue());
						break;
					}
				}
			}
		}
	}
	internal sealed class ModConfig
	{
		private readonly ConfigFile ConfigFile;

		private readonly ConfigEntry<bool> lockConfiguration;

		private readonly ConfigEntry<bool> enableSkeleton;

		private readonly ConfigEntry<bool> enableGreydwarf;

		private readonly ConfigEntry<bool> enableDraugr;

		private readonly ConfigEntry<bool> enableSurtling;

		private readonly ConfigEntry<bool> enableTarBlob;

		private readonly ConfigEntry<int> skeletonBoneFragments;

		private readonly ConfigEntry<int> skeletonTrophies;

		private readonly ConfigEntry<int> greydwarfEyes;

		private readonly ConfigEntry<int> greydwarfAncientSeeds;

		private readonly ConfigEntry<int> greydwarfTrophies;

		private readonly ConfigEntry<int> draugrEntrails;

		private readonly ConfigEntry<int> draugrTrophies;

		private readonly ConfigEntry<int> surtlingCores;

		private readonly ConfigEntry<int> surtlingCoal;

		private readonly ConfigEntry<int> surtlingTrophies;

		private readonly ConfigEntry<int> tarBlobTar;

		private readonly ConfigEntry<int> tarBlobTrophies;

		private readonly ConfigEntry<bool> enableDebugMessages;

		internal bool LockConfiguration => lockConfiguration.Value;

		internal bool EnableDebugMessages => enableDebugMessages.Value;

		private ConfigEntry<T> Config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			ConfigEntry<T> val = ConfigFile.Bind<T>(group, name, value, description);
			if (synchronizedSetting)
			{
				CraftableSpawnersPlugin.Sync.Register((ConfigEntryBase)(object)val);
			}
			return val;
		}

		private ConfigEntry<T> Config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return Config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		internal ModConfig(ConfigFile configFile)
		{
			ConfigFile = configFile;
			configFile.SaveOnConfigSet = false;
			lockConfiguration = Config("Server", "LockConfiguration", value: true, "If on, connected clients use the server's spawner settings instead of their local values.", synchronizedSetting: false);
			enableSkeleton = Config("Enable", "SkeletonSpawner", value: true, "Enable the craftable Evil bone pile (skeleton spawner).");
			enableGreydwarf = Config("Enable", "GreydwarfSpawner", value: true, "Enable the craftable Greydwarf nest.");
			enableDraugr = Config("Enable", "DraugrSpawner", value: true, "Enable the craftable Body pile (draugr spawner).");
			enableSurtling = Config("Enable", "SurtlingSpawner", value: true, "Enable the craftable Fire pillar (surtling spawner).");
			enableTarBlob = Config("Enable", "TarBlobSpawner", value: true, "Enable the craftable Bone pile (tar blob spawner).");
			skeletonBoneFragments = Config("Recipes.Skeleton", "BoneFragments", 40, "Bone fragments required for Evil bone pile.");
			skeletonTrophies = Config("Recipes.Skeleton", "TrophySkeleton", 5, "Skeleton trophies required for Evil bone pile.");
			greydwarfEyes = Config("Recipes.Greydwarf", "GreydwarfEye", 20, "Greydwarf eyes required for Greydwarf nest.");
			greydwarfAncientSeeds = Config("Recipes.Greydwarf", "AncientSeed", 10, "Ancient seeds required for Greydwarf nest.");
			greydwarfTrophies = Config("Recipes.Greydwarf", "TrophyGreydwarf", 5, "Greydwarf trophies required for Greydwarf nest.");
			draugrEntrails = Config("Recipes.Draugr", "Entrails", 40, "Entrails required for Body pile.");
			draugrTrophies = Config("Recipes.Draugr", "TrophyDraugr", 5, "Draugr trophies required for Body pile.");
			surtlingCores = Config("Recipes.Surtling", "SurtlingCore", 20, "Surtling cores required for Fire pillar.");
			surtlingCoal = Config("Recipes.Surtling", "Coal", 20, "Coal required for Fire pillar.");
			surtlingTrophies = Config("Recipes.Surtling", "TrophySurtling", 5, "Surtling trophies required for Fire pillar.");
			tarBlobTar = Config("Recipes.TarBlob", "Tar", 40, "Tar required for Bone pile (tar blob spawner).");
			tarBlobTrophies = Config("Recipes.TarBlob", "TrophyGrowth", 5, "Growth (tar blob) trophies required for Bone pile.");
			enableDebugMessages = Config("Troubleshooting", "EnableDebugMessages", value: false, "Enable extra debug logging.", synchronizedSetting: false);
			HookGameplaySettingChanged<bool>(enableSkeleton);
			HookGameplaySettingChanged<bool>(enableGreydwarf);
			HookGameplaySettingChanged<bool>(enableDraugr);
			HookGameplaySettingChanged<bool>(enableSurtling);
			HookGameplaySettingChanged<bool>(enableTarBlob);
			HookGameplaySettingChanged<int>(skeletonBoneFragments);
			HookGameplaySettingChanged<int>(skeletonTrophies);
			HookGameplaySettingChanged<int>(greydwarfEyes);
			HookGameplaySettingChanged<int>(greydwarfAncientSeeds);
			HookGameplaySettingChanged<int>(greydwarfTrophies);
			HookGameplaySettingChanged<int>(draugrEntrails);
			HookGameplaySettingChanged<int>(draugrTrophies);
			HookGameplaySettingChanged<int>(surtlingCores);
			HookGameplaySettingChanged<int>(surtlingCoal);
			HookGameplaySettingChanged<int>(surtlingTrophies);
			HookGameplaySettingChanged<int>(tarBlobTar);
			HookGameplaySettingChanged<int>(tarBlobTrophies);
			ConfigPaths.ApplyAlternateOverlay(configFile);
			configFile.Save();
			configFile.SaveOnConfigSet = true;
		}

		private static void HookGameplaySettingChanged<T>(ConfigEntry<T> entry)
		{
			entry.SettingChanged += delegate
			{
				SpawnerSetup.RefreshFromConfig();
			};
		}

		internal bool IsEnabled(SpawnerId id)
		{
			return id switch
			{
				SpawnerId.Skeleton => enableSkeleton.Value, 
				SpawnerId.Greydwarf => enableGreydwarf.Value, 
				SpawnerId.Draugr => enableDraugr.Value, 
				SpawnerId.Surtling => enableSurtling.Value, 
				SpawnerId.TarBlob => enableTarBlob.Value, 
				_ => false, 
			};
		}

		internal List<(string item, int amount)> GetRecipe(SpawnerId id)
		{
			return id switch
			{
				SpawnerId.Skeleton => new List<(string, int)>(2)
				{
					("BoneFragments", skeletonBoneFragments.Value),
					("TrophySkeleton", skeletonTrophies.Value)
				}, 
				SpawnerId.Greydwarf => new List<(string, int)>(3)
				{
					("GreydwarfEye", greydwarfEyes.Value),
					("AncientSeed", greydwarfAncientSeeds.Value),
					("TrophyGreydwarf", greydwarfTrophies.Value)
				}, 
				SpawnerId.Draugr => new List<(string, int)>(2)
				{
					("Entrails", draugrEntrails.Value),
					("TrophyDraugr", draugrTrophies.Value)
				}, 
				SpawnerId.Surtling => new List<(string, int)>(3)
				{
					("SurtlingCore", surtlingCores.Value),
					("Coal", surtlingCoal.Value),
					("TrophySurtling", surtlingTrophies.Value)
				}, 
				SpawnerId.TarBlob => new List<(string, int)>(2)
				{
					("Tar", tarBlobTar.Value),
					("TrophyGrowth", tarBlobTrophies.Value)
				}, 
				_ => new List<(string, int)>(), 
			};
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}