Decompiled source of DogHausTweaks v1.0.0

DogHausTweaks.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CreatureTerrainDamage;
using DogHausAnchorRecipe;
using DogHausBalance;
using DogHausModSettings;
using DogHausPieceBalance;
using DogHausTerrain;
using DogHausTorchWarmth;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace CreatureTerrainDamage
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("doghaus.creatureterraindamage", "Creature Terrain Damage", "1.11.2")]
	public class CreatureTerrainDamagePlugin : BaseUnityPlugin
	{
		public const string GUID = "doghaus.creatureterraindamage";

		public const string NAME = "Creature Terrain Damage";

		public const string VERSION = "1.11.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<bool> LogUnknown;

		internal static ConfigEntry<float> MaxDepth;

		internal static ConfigEntry<float> StarBonus;

		internal static ConfigEntry<float> Cooldown;

		internal static ConfigEntry<float> ContactRadius;

		internal static ConfigEntry<bool> RespectWards;

		internal static ConfigEntry<bool> ClaimOwnership;

		internal static Dictionary<string, ConfigEntry<float>> Depth = new Dictionary<string, ConfigEntry<float>>(StringComparer.OrdinalIgnoreCase);

		internal static Dictionary<string, ConfigEntry<float>> Radius = new Dictionary<string, ConfigEntry<float>>(StringComparer.OrdinalIgnoreCase);

		private static readonly object[][] Table = new object[63][]
		{
			new object[4] { "Greyling", 0f, 0.34f, "Too small and light to break ground. Meadows. Claws, barely reaches." },
			new object[4] { "Boar", 0.058f, 0.46f, "Meadows. Charge, hooves." },
			new object[4] { "Eikthyr", 0.5f, 4f, "Meadows boss. Charge and stomp." },
			new object[4] { "Greydwarf", 0.042f, 0.34f, "Black Forest. Claws." },
			new object[4] { "Greydwarf_Elite", 0.333f, 2.66f, "Greydwarf Brute. Overhead club into earth." },
			new object[4] { "Troll", 0.625f, 5f, "Black Forest. Ground slam and log swing." },
			new object[4] { "Bjorn", 0.4f, 3.2f, "Black Forest bear. Heavy paws, a good deal lighter than a troll." },
			new object[4] { "gd_king", 1.042f, 8.34f, "The Elder. Stomp and root spikes." },
			new object[4] { "Draugr_Elite", 0.042f, 0.34f, "Swamp. Heavy weapon, marginal contact." },
			new object[4] { "BlobElite", 0f, 0.66f, "A slime has nothing to strike the ground with. Oozer. Body splat, little force." },
			new object[4] { "Abomination", 0.917f, 7.34f, "Swamp. Tentacle slams." },
			new object[4] { "Bonemass", 1.167f, 9.34f, "Swamp boss. Body slam, enormous mass." },
			new object[4] { "Wolf", 0.1f, 0.8f, "Mountains. Claws and paws." },
			new object[4] { "Ulv", 0.1f, 0.8f, "Mountains. Claws." },
			new object[4] { "Fenring", 0.125f, 1f, "Mountains. Heavier lunge." },
			new object[4] { "Fenring_Cultist", 0.083f, 0.66f, "Cultist. Mostly fire breath." },
			new object[4] { "StoneGolem", 0.75f, 6f, "Mountains. Stone fists into earth." },
			new object[4] { "Dragon", 0f, 5f, "Flies. No ground contact to dig with. Moder. Only contacts ground on landing." },
			new object[4] { "Goblin", 0.042f, 0.34f, "Fuling. Weapons." },
			new object[4] { "GoblinBrute", 0.417f, 3.34f, "Fuling Berserker. Rage slam." },
			new object[4] { "BlobTar", 0f, 0.34f, "A slime has nothing to strike the ground with. Plains growth / tar blob. Splat. (Prefab is BlobTar, not Growth.)" },
			new object[4] { "Lox", 0.542f, 4.34f, "Plains. Stomp, huge mass." },
			new object[4] { "GoblinKing", 1.25f, 10f, "Yagluth. Fist slam and meteors." },
			new object[4] { "Seeker", 0f, 0.34f, "Flies. Mistlands. Mostly airborne." },
			new object[4] { "SeekerBrute", 0.5f, 4f, "Seeker Soldier. Ground slam." },
			new object[4] { "SeekerQueen", 0.833f, 6.66f, "The Queen. Melee and AoE." },
			new object[4] { "Asksvin", 0.125f, 1f, "Ashlands. Charge, large bird." },
			new object[4] { "Morgen", 0.75f, 6f, "Ashlands. Heavy slams." },
			new object[4] { "Fader", 1.25f, 10f, "Ashlands boss. Slams." },
			new object[4] { "Neck", 0f, 0.24f, "Too small and light. Meadows. Small lizard, barely marks the ground." },
			new object[4] { "Greydwarf_Shaman", 0.04f, 0.32f, "Black Forest. Caster, little ground contact." },
			new object[4] { "Skeleton", 0.04f, 0.32f, "Black Forest burial chambers. Light bones." },
			new object[4] { "Skeleton_Meadows", 0.04f, 0.32f, "Meadows skeleton variant." },
			new object[4] { "Skeleton_Poison", 0.05f, 0.4f, "Poison skeleton, slightly heavier." },
			new object[4] { "Ghost", 0f, 0.2f, "Incorporeal - no mass. Incorporeal - deliberately disabled, it has no mass to dig with." },
			new object[4] { "Draugr", 0.05f, 0.4f, "Swamp. Weapon strikes, marginal contact." },
			new object[4] { "Blob", 0f, 0.48f, "A slime has nothing to strike the ground with. Swamp. Body splat, little force." },
			new object[4] { "Surtling", 0f, 0.24f, "Too small and light. Swamp/fire. Small, ranged fire." },
			new object[4] { "Wraith", 0f, 0.2f, "Incorporeal - no mass. Incorporeal - deliberately disabled." },
			new object[4] { "Hatchling", 0f, 0.4f, "Flies. Drake. Airborne, ranged frost." },
			new object[4] { "Bat", 0f, 0.2f, "Flies. Airborne, negligible." },
			new object[4] { "Bat_Swamp", 0f, 0.2f, "Flies." },
			new object[4] { "Deathsquito", 0f, 0.2f, "Flies. Airborne, negligible ground contact." },
			new object[4] { "GoblinShaman", 0.05f, 0.4f, "Fuling Shaman. Caster." },
			new object[4] { "Gjall", 0f, 6.4f, "Flies. Huge floating grub, drops explosive ticks." },
			new object[4] { "Tick", 0f, 0.24f, "Too small and light. Small, latches on." },
			new object[4] { "SeekerBrood", 0f, 0.24f, "Flies. Small seeker spawn." },
			new object[4] { "Charred_Melee", 0.08f, 0.64f, "Ashlands. Armed skeleton, weapon strikes." },
			new object[4] { "Charred_Twitcher", 0.04f, 0.32f, "Ashlands. Small and fast." },
			new object[4] { "Charred_Archer", 0.04f, 0.32f, "Ashlands. Ranged." },
			new object[4] { "Goblin_Gem", 0.06f, 0.48f, "Gem fuling." },
			new object[4] { "Frysling", 0.05f, 0.4f, "Deep North. Small. CONFIRMED name." },
			new object[4] { "ElakingMole", 0.3f, 2.4f, "Deep North. A burrower - digs by nature. CONFIRMED name." },
			new object[4] { "ElakingLantern", 0.06f, 0.48f, "Deep North. CONFIRMED name." },
			new object[4] { "JotunWarrior", 1.04f, 8.32f, "Deep North frost giant. CONFIRMED name." },
			new object[4] { "JotunWarriorDualWield", 1.04f, 8.32f, "Deep North frost giant, dual wield. CONFIRMED name." },
			new object[4] { "JotunWitch", 0.6f, 4.8f, "Deep North giant caster. CONFIRMED name." },
			new object[4] { "FrozenKing", 1.25f, 10f, "Deep North BOSS. CONFIRMED name - this is what 'Kall' guessed at." },
			new object[4] { "Writhan", 0.8f, 6.4f, "Deep North. CONFIRMED name, size unverified." },
			new object[4] { "Seal", 0.042f, 0.34f, "Deep North. Blubber, slaps. (Name confirmed in the game files 2026-10-01.)" },
			new object[4] { "Elaking", 0.083f, 0.66f, "Deep North. Claws and lunges. (Name confirmed 2026-10-01.)" },
			new object[4] { "Moose", 0.25f, 2f, "Deep North. Kicks deal heavy damage. (Name confirmed 2026-10-01.)" },
			new object[4] { "Barka", 0.667f, 5.34f, "Deep North. Giant treant. (Name confirmed 2026-10-01.)" }
		};

		private void Awake()
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Expected O, but got Unknown
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if (Kill.Off("doghaus.creatureterraindamage", ((BaseUnityPlugin)this).Logger))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch. When false no creature digs.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Verbose Logging", false, "Log each dig with the creature, star level and resulting depth. Useful while tuning.");
			LogUnknown = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Log Unknown Creatures", false, "Log the prefab name of any attacking creature that has no entry below. Use this to discover the exact names for creatures you want to add.");
			MaxDepth = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Maximum Depth (m)", 2.5f, new ConfigDescription("Hard ceiling. No creature can dig deeper than this no matter its star level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 8f), new object[0]));
			StarBonus = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Depth Bonus Per Star", 0.5f, new ConfigDescription("Each star multiplies depth by (1 + this). 0.5 means a 1-star digs 1.5x and a 2-star digs 2x. Radius is not affected by stars.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[0]));
			Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Global Cooldown (s)", 3f, new ConfigDescription("Seconds after any creature digs before another may. Shared by all creatures, which keeps a large fight from shredding the map.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), new object[0]));
			ContactRadius = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Ground-Only Check Radius (m)", 2.5f, new ConfigDescription("Only a blow that lands on the GROUND leaves a crater. A melee swing counts as having hit something else if a character or a building piece is within this distance of where the crater would go. Raise it to make creatures more reluctant to dig near people and bases; 0 disables the check and every swing craters again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[0]));
			RespectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Respect Wards", true, "Creatures will not dig inside an active ward's area. Checks whether a ward COVERS the spot, not whether anyone has access to it, so a raid cannot crater a base just because its owner happens to be standing there. Strongly recommended on a shared server.");
			ClaimOwnership = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Claim Terrain Ownership", true, "Take network ownership of a terrain chunk before digging it. Valheim's TerrainComp.Save() silently discards changes made by a non-owner, so with this off a crater is visible only to the attacker and vanishes on reload. Leave on except when debugging.");
			for (int i = 0; i < Table.Length; i++)
			{
				string text = (string)Table[i][0];
				float num = (float)Table[i][1];
				float num2 = (float)Table[i][2];
				string text2 = (string)Table[i][3];
				Depth[text] = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Depth (m)", text, num, new ConfigDescription(text2 + " Base dig depth before star scaling. 0 disables this creature.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 8f), new object[0]));
				Radius[text] = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Crater Radius (m)", text, num2, new ConfigDescription(text2 + " Crater radius. Not affected by star level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 12f), new object[0]));
			}
			try
			{
				Harmony val = new Harmony("doghaus.creatureterraindamage");
				val.CreateClassProcessor(typeof(AttackHooks)).Patch();
				val.CreateClassProcessor(typeof(CraterWallBand)).Patch();
				val.CreateClassProcessor(typeof(TerrainComp_Awake_RemoteOp)).Patch();
				val.CreateClassProcessor(typeof(AtmModifierGuard)).Patch();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Creature Terrain Damage 1.11.2 loaded with " + Table.Length + " creature entries."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patching failed, mod inactive: " + ex.Message));
			}
		}

		internal static string MatchKey(string rawName)
		{
			if (string.IsNullOrEmpty(rawName))
			{
				return null;
			}
			string text = rawName;
			int num = text.IndexOf('(');
			if (num > 0)
			{
				text = text.Substring(0, num);
			}
			text = text.Trim();
			string text2 = null;
			foreach (string key in Depth.Keys)
			{
				if (text.StartsWith(key, StringComparison.OrdinalIgnoreCase) && (text2 == null || key.Length > text2.Length))
				{
					text2 = key;
				}
			}
			return text2;
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "RaiseTerrain")]
	[HarmonyPatch]
	[HarmonyBefore(new string[] { "com.jubaroo.smarterhoe" })]
	internal static class CraterWallBand
	{
		[ThreadStatic]
		internal static bool Active;

		[ThreadStatic]
		internal static float Band;

		private static FieldInfo s_hmap;

		private static FieldInfo s_width;

		private static FieldInfo s_smooth;

		private static FieldInfo s_level;

		private static FieldInfo s_modified;

		private static bool Prefix(TerrainComp __instance, Vector3 worldPos, float radius, float delta, bool square, float power)
		{
			//IL_0153: 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_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			if (!Active || delta >= 0f)
			{
				return true;
			}
			if (s_hmap == null)
			{
				s_hmap = AccessTools.Field(typeof(TerrainComp), "m_hmap");
				s_width = AccessTools.Field(typeof(TerrainComp), "m_width");
				s_smooth = AccessTools.Field(typeof(TerrainComp), "m_smoothDelta");
				s_level = AccessTools.Field(typeof(TerrainComp), "m_levelDelta");
				s_modified = AccessTools.Field(typeof(TerrainComp), "m_modifiedHeight");
			}
			Heightmap val = (Heightmap)((s_hmap != null) ? /*isinst with value type is only supported in some contexts*/: null);
			float[] array = ((s_smooth != null) ? (s_smooth.GetValue(__instance) as float[]) : null);
			float[] array2 = ((s_level != null) ? (s_level.GetValue(__instance) as float[]) : null);
			bool[] array3 = ((s_modified != null) ? (s_modified.GetValue(__instance) as bool[]) : null);
			if ((Object)(object)val == (Object)null || array == null || array2 == null || array3 == null || s_width == null)
			{
				return true;
			}
			int num = (int)s_width.GetValue(__instance);
			int num2 = default(int);
			int num3 = default(int);
			val.WorldToVertex(worldPos, ref num2, ref num3);
			Vector3 val2 = worldPos - ((Component)__instance).transform.position;
			float num4 = val2.y + Band;
			float num5 = radius / val.m_scale;
			int num6 = Mathf.CeilToInt(num5);
			int num7 = num + 1;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor((float)num2, (float)num3);
			for (int i = num3 - num6; i <= num3 + num6; i++)
			{
				for (int j = num2 - num6; j <= num2 + num6; j++)
				{
					if (j < 0 || i < 0 || j >= num7 || i >= num7)
					{
						continue;
					}
					float num8 = 1f;
					if (!square)
					{
						float num9 = Vector2.Distance(val3, new Vector2((float)j, (float)i));
						if (num9 > num5)
						{
							continue;
						}
						if (power > 0f)
						{
							num8 = 1f - num9 / num5;
							if (power != 1f)
							{
								num8 = Mathf.Pow(num8, power);
							}
						}
					}
					float height = val.GetHeight(j, i);
					float num10 = ((!(height > num4)) ? (val2.y + delta * num8) : (height + delta * num8));
					if (!(num10 > height))
					{
						int num11 = i * num7 + j;
						float num12 = num10 - height + array[num11];
						array[num11] = 0f;
						array2[num11] = Mathf.Clamp(array2[num11] + num12, -8f, 8f);
						array3[num11] = true;
					}
				}
			}
			return false;
		}
	}
	internal static class CraterNumbers
	{
		internal static float Depth(string key)
		{
			if (Balance.Current.Craters.TryGetValue(key, out var value))
			{
				return value[0];
			}
			return CreatureTerrainDamagePlugin.Depth[key].Value;
		}

		internal static float Radius(string key)
		{
			if (Balance.Current.Craters.TryGetValue(key, out var value))
			{
				return value[1];
			}
			return CreatureTerrainDamagePlugin.Radius[key].Value;
		}

		internal static float Num(string setting, float fallback)
		{
			if (Balance.Current.CraterSettings.TryGetValue(setting, out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return fallback;
		}

		internal static bool Flag(string setting, bool fallback)
		{
			if (Balance.Current.CraterSettings.TryGetValue(setting, out var value))
			{
				return value == "true";
			}
			return fallback;
		}
	}
	internal static class AttackHooks
	{
		private static FieldInfo _charField;

		private static bool _charResolved;

		private static MethodInfo _doOperation;

		private static bool _doOpResolved;

		private static bool _loggedOps;

		private static float _lastDig = -999f;

		private static FieldInfo _fAllAreas;

		private static MethodInfo _mIsEnabled;

		private static bool _wardResolved;

		private static FieldInfo _fCompNview;

		private static bool _compNviewResolved;

		private static FieldInfo _projOwner;

		private static bool _projOwnerResolved;

		private static readonly List<Character> _nearby = new List<Character>();

		private static readonly Collider[] _pieceBuf = (Collider[])(object)new Collider[8];

		private static int _pieceMask = -1;

		private static Character Attacker(Attack atk)
		{
			if (!_charResolved)
			{
				_charResolved = true;
				_charField = AccessTools.Field(typeof(Attack), "m_character");
				if (_charField == null)
				{
					CreatureTerrainDamagePlugin.Log.LogError((object)"Attack.m_character not found - mod cannot identify creatures.");
				}
			}
			if (_charField == null)
			{
				return null;
			}
			object? value = _charField.GetValue(atk);
			return (Character)((value is Character) ? value : null);
		}

		private static MethodInfo DoOp()
		{
			if (_doOpResolved)
			{
				return _doOperation;
			}
			_doOpResolved = true;
			_doOperation = AccessTools.Method(typeof(TerrainComp), "DoOperation", new Type[3]
			{
				typeof(Vector3),
				typeof(Vector3),
				typeof(Settings)
			}, (Type[])null);
			if (_doOperation == null)
			{
				CreatureTerrainDamagePlugin.Log.LogError((object)"TerrainComp.DoOperation(Vector3, Vector3, TerrainOp.Settings) not found. Creature digging disabled.");
			}
			return _doOperation;
		}

		private static bool InsideWard(Vector3 pos, float radius)
		{
			//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)
			if (!CraterNumbers.Flag("respect_wards", CreatureTerrainDamagePlugin.RespectWards.Value))
			{
				return false;
			}
			try
			{
				if (!_wardResolved)
				{
					_wardResolved = true;
					_fAllAreas = AccessTools.Field(typeof(PrivateArea), "m_allAreas");
					_mIsEnabled = AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null);
					if (_fAllAreas == null)
					{
						CreatureTerrainDamagePlugin.Log.LogWarning((object)"PrivateArea.m_allAreas not found - ward protection unavailable.");
					}
				}
				if (_fAllAreas == null)
				{
					return false;
				}
				if (!(_fAllAreas.GetValue(null) is List<PrivateArea> list))
				{
					return false;
				}
				for (int i = 0; i < list.Count; i++)
				{
					PrivateArea val = list[i];
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (_mIsEnabled != null)
					{
						object obj = _mIsEnabled.Invoke(val, null);
						if (obj is bool && !(bool)obj)
						{
							continue;
						}
					}
					Vector3 position = ((Component)val).transform.position;
					float num = position.x - pos.x;
					float num2 = position.z - pos.z;
					float num3 = val.m_radius + radius;
					if (num * num + num2 * num2 < num3 * num3)
					{
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogError((object)("Ward check failed: " + ex.Message));
			}
			return false;
		}

		private static void TakeOwnership(TerrainComp comp)
		{
			if (!CraterNumbers.Flag("claim_ownership", CreatureTerrainDamagePlugin.ClaimOwnership.Value))
			{
				return;
			}
			try
			{
				if (!_compNviewResolved)
				{
					_compNviewResolved = true;
					_fCompNview = AccessTools.Field(typeof(TerrainComp), "m_nview");
					if (_fCompNview == null)
					{
						CreatureTerrainDamagePlugin.Log.LogWarning((object)"TerrainComp.m_nview not found - digs may not persist for other players.");
					}
				}
				if (!(_fCompNview == null))
				{
					object? value = _fCompNview.GetValue(comp);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if (!((Object)(object)val == (Object)null) && val.IsValid() && !val.IsOwner())
					{
						val.ClaimOwnership();
					}
				}
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogError((object)("Could not claim terrain ownership: " + ex.Message));
			}
		}

		private static void LogRegisteredOps()
		{
			if (_loggedOps)
			{
				return;
			}
			try
			{
				if ((Object)(object)ObjectDB.instance == (Object)null)
				{
					return;
				}
				_loggedOps = true;
				FieldInfo fieldInfo = AccessTools.Field(typeof(ObjectDB), "m_terrainOps");
				if (fieldInfo == null || !(fieldInfo.GetValue(ObjectDB.instance) is List<TerrainOp> list))
				{
					return;
				}
				StringBuilder stringBuilder = new StringBuilder();
				for (int i = 0; i < list.Count; i++)
				{
					if (!((Object)(object)list[i] == (Object)null))
					{
						if (stringBuilder.Length > 0)
						{
							stringBuilder.Append(", ");
						}
						stringBuilder.Append(((Object)list[i]).name);
					}
				}
				CreatureTerrainDamagePlugin.Log.LogInfo((object)("Terrain ops registered in ObjectDB: " + stringBuilder));
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogWarning((object)("Could not list registered terrain ops: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(Attack), "DoMeleeAttack")]
		[HarmonyPostfix]
		private static void OnMelee(Attack __instance)
		{
			Dig(__instance);
		}

		[HarmonyPatch(typeof(Attack), "DoAreaAttack")]
		[HarmonyPostfix]
		private static void OnArea(Attack __instance)
		{
			Dig(__instance);
		}

		private static Character ProjectileOwner(Projectile p)
		{
			if (!_projOwnerResolved)
			{
				_projOwnerResolved = true;
				_projOwner = AccessTools.Field(typeof(Projectile), "m_owner");
				if (_projOwner == null)
				{
					CreatureTerrainDamagePlugin.Log.LogError((object)"Projectile.m_owner not found - ranged attacks will not dig.");
				}
			}
			if (_projOwner == null)
			{
				return null;
			}
			object? value = _projOwner.GetValue(p);
			return (Character)((value is Character) ? value : null);
		}

		[HarmonyPatch(typeof(Projectile), "OnHit")]
		[HarmonyPostfix]
		private static void OnProjectile(Projectile __instance, Collider collider, Vector3 hitPoint, bool water)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__instance == (Object)null || water || !CraterNumbers.Flag("enabled", CreatureTerrainDamagePlugin.ModEnabled.Value))
				{
					return;
				}
				Character val = ProjectileOwner(__instance);
				if ((Object)(object)val == (Object)null || val is Player)
				{
					return;
				}
				if ((Object)(object)collider == (Object)null || (Object)(object)((Component)collider).GetComponentInParent<Heightmap>() == (Object)null)
				{
					if (CreatureTerrainDamagePlugin.Verbose.Value)
					{
						CreatureTerrainDamagePlugin.Log.LogInfo((object)("Projectile hit " + (((Object)(object)collider != (Object)null) ? ((Object)collider).name : "nothing") + ", not the ground - no crater."));
					}
				}
				else
				{
					DigFor(val, hasPos: true, hitPoint, "thrown");
				}
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogError((object)("Projectile dig failed: " + ex.Message));
			}
		}

		private static bool StruckSomethingSolid(Vector3 pos, float radius, Character self)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_nearby.Clear();
				Character.GetCharactersInRange(pos, radius, _nearby);
				for (int i = 0; i < _nearby.Count; i++)
				{
					Character val = _nearby[i];
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)self))
					{
						return true;
					}
				}
				if (_pieceMask == -1)
				{
					_pieceMask = LayerMask.GetMask(new string[1] { "piece" });
				}
				int num = Physics.OverlapSphereNonAlloc(pos, radius, _pieceBuf, _pieceMask);
				return num > 0;
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogWarning((object)("Ground-contact check failed, allowing the dig: " + ex.Message));
				return false;
			}
		}

		private static void Dig(Attack atk)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (atk != null && CraterNumbers.Flag("enabled", CreatureTerrainDamagePlugin.ModEnabled.Value))
			{
				Character val = Attacker(atk);
				if (!((Object)(object)val == (Object)null) && !(val is Player))
				{
					DigFor(val, hasPos: false, Vector3.zero, "melee");
				}
			}
		}

		private static void DigFor(Character c, bool hasPos, Vector3 at, string via)
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//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_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Expected O, but got Unknown
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0565: Unknown result type (might be due to invalid IL or missing references)
			//IL_0446: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)c == (Object)null)
				{
					return;
				}
				string text = (((Object)(object)((Component)c).gameObject != (Object)null) ? ((Object)((Component)c).gameObject).name : null);
				string text2 = CreatureTerrainDamagePlugin.MatchKey(text);
				if (text2 == null)
				{
					if (CreatureTerrainDamagePlugin.LogUnknown.Value)
					{
						CreatureTerrainDamagePlugin.Log.LogInfo((object)("Unconfigured creature attacked: '" + text + "'"));
					}
					return;
				}
				float num = CraterNumbers.Depth(text2);
				if (num <= 0f || Time.time - _lastDig < CraterNumbers.Num("cooldown", CreatureTerrainDamagePlugin.Cooldown.Value))
				{
					return;
				}
				MethodInfo methodInfo = DoOp();
				if (methodInfo == null)
				{
					return;
				}
				int num2 = 0;
				try
				{
					num2 = Mathf.Max(0, c.GetLevel() - 1);
				}
				catch
				{
				}
				float num3 = num * (1f + CraterNumbers.Num("star_bonus", CreatureTerrainDamagePlugin.StarBonus.Value) * (float)num2);
				num3 = Mathf.Min(num3, CraterNumbers.Num("max_depth", CreatureTerrainDamagePlugin.MaxDepth.Value));
				float num4 = CraterNumbers.Radius(text2);
				Vector3 val = (hasPos ? at : (((Component)c).transform.position + ((Component)c).transform.forward * (num4 * 0.5f)));
				float num5 = default(float);
				if (!Heightmap.GetHeight(val, ref num5))
				{
					if (CreatureTerrainDamagePlugin.Verbose.Value)
					{
						CreatureTerrainDamagePlugin.Log.LogInfo((object)string.Concat("No ground under ", val, " - no dig for ", text2, "."));
					}
					return;
				}
				float num6 = CraterNumbers.Num("contact_radius", CreatureTerrainDamagePlugin.ContactRadius.Value);
				if (num6 > 0f)
				{
					if (hasPos && at.y - num5 > num6)
					{
						if (CreatureTerrainDamagePlugin.Verbose.Value)
						{
							CreatureTerrainDamagePlugin.Log.LogInfo((object)("Hit by " + text2 + " landed " + (at.y - num5).ToString("0.00") + " m above ground - no crater."));
						}
						return;
					}
					if (!hasPos && StruckSomethingSolid(val, num6, c))
					{
						if (CreatureTerrainDamagePlugin.Verbose.Value)
						{
							CreatureTerrainDamagePlugin.Log.LogInfo((object)("Swing by " + text2 + " landed on a target, not the ground - no crater."));
						}
						return;
					}
				}
				val.y = num5;
				float num7 = Ground.CraterScale(val);
				num3 = Mathf.Min(num3 * num7, CraterNumbers.Num("max_depth", CreatureTerrainDamagePlugin.MaxDepth.Value));
				num4 *= num7;
				if (InsideWard(val, num4))
				{
					if (CreatureTerrainDamagePlugin.Verbose.Value)
					{
						CreatureTerrainDamagePlugin.Log.LogInfo((object)("Dig by " + text2 + " blocked by a ward at " + val));
					}
					return;
				}
				Settings val2 = new Settings();
				val2.m_level = false;
				val2.m_levelOffset = 0f;
				val2.m_raise = true;
				val2.m_raiseRadius = num4;
				val2.m_raiseDelta = 0f - num3;
				val2.m_raisePower = 2f;
				val2.m_smooth = false;
				val2.m_square = false;
				val2.m_paintCleared = false;
				val2.m_rotation = false;
				LogRegisteredOps();
				List<Heightmap> list = new List<Heightmap>();
				Heightmap.FindHeightmap(val, num4, list);
				if (list.Count == 0)
				{
					if (CreatureTerrainDamagePlugin.Verbose.Value)
					{
						CreatureTerrainDamagePlugin.Log.LogWarning((object)string.Concat("No heightmap found at ", val, " - no dig for ", text2, "."));
					}
					return;
				}
				int num8 = 0;
				object[] parameters = new object[3]
				{
					val,
					Vector3.zero,
					val2
				};
				float craterWallBand = Balance.Current.CraterWallBand;
				for (int i = 0; i < list.Count; i++)
				{
					if ((Object)(object)list[i] == (Object)null)
					{
						continue;
					}
					TerrainComp andCreateTerrainCompiler = list[i].GetAndCreateTerrainCompiler();
					if ((Object)(object)andCreateTerrainCompiler == (Object)null)
					{
						continue;
					}
					if (RemoteOp.Send(andCreateTerrainCompiler, val, Vector3.zero, val2, 0, craterWallBand))
					{
						num8++;
						continue;
					}
					CraterWallBand.Band = craterWallBand;
					CraterWallBand.Active = craterWallBand > 0f;
					try
					{
						TakeOwnership(andCreateTerrainCompiler);
						RemoteOp.Applying = true;
						methodInfo.Invoke(andCreateTerrainCompiler, parameters);
						num8++;
					}
					finally
					{
						CraterWallBand.Active = false;
						RemoteOp.Applying = false;
					}
				}
				_lastDig = Time.time;
				if (CreatureTerrainDamagePlugin.Verbose.Value)
				{
					CreatureTerrainDamagePlugin.Log.LogInfo((object)("Dig by " + text2 + " (" + num2 + " star, " + via + "): " + num3.ToString("0.00") + " m deep, " + num4.ToString("0.0") + " m radius, " + num8 + " heightmap(s) at " + val));
				}
			}
			catch (Exception ex)
			{
				CreatureTerrainDamagePlugin.Log.LogError((object)("Dig failed: " + ex.Message));
			}
		}
	}
}
namespace DogHausSleepCap
{
	[BepInPlugin("doghaus.sleepcap", "Sleep Cap", "1.0.3")]
	public class SleepCapPlugin : BaseUnityPlugin
	{
		public const string GUID = "doghaus.sleepcap";

		public const string NAME = "Sleep Cap";

		public const string VERSION = "1.0.3";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> MaxSleepMinutes;

		internal static ConfigEntry<bool> OneSleepPerNight;

		internal static ConfigEntry<string> RestedEnoughMessage;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if (Kill.Off("doghaus.sleepcap", ((BaseUnityPlugin)this).Logger))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			MaxSleepMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Max Sleep (minutes)", 8f, new ConfigDescription("The most in-game clock time one sleep can skip, in minutes of the normal day (a 24-minute day means 1 minute = 1 real minute of normal play). A night shorter than this is skipped to morning as usual. 0 disables the cap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[0]));
			OneSleepPerNight = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "One Sleep Per Night", true, "After a sleep that was cut short by the cap, nobody can sleep again until the morning it was heading for. Without this, players can get back into bed 30 seconds later and skip again.");
			RestedEnoughMessage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Rested Enough Message", "You have slept all you can tonight. Wait for dawn.", "Shown in the middle of every player's screen when they try to sleep again the same night.");
			_harmony = new Harmony("doghaus.sleepcap");
			_harmony.CreateClassProcessor(typeof(EnvMan_SkipToMorning_Cap)).Patch();
			_harmony.CreateClassProcessor(typeof(Game_EverybodyIsTryingToSleep_OnePerNight)).Patch();
			_harmony.CreateClassProcessor(typeof(ZNet_Awake_ResetSleep)).Patch();
			Log.LogInfo((object)("Sleep Cap 1.0.3 loaded: max " + MaxSleepMinutes.Value.ToString("0.#") + " min per sleep, one sleep per night = " + OneSleepPerNight.Value + "."));
		}
	}
	internal static class SleepState
	{
		internal static double BlockUntil;

		internal static double LastMessageAt = -1000.0;
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNet_Awake_ResetSleep
	{
		private static void Postfix()
		{
			SleepState.BlockUntil = 0.0;
			SleepState.LastMessageAt = -1000.0;
		}
	}
	[HarmonyPatch(typeof(EnvMan), "SkipToMorning")]
	internal static class EnvMan_SkipToMorning_Cap
	{
		[HarmonyPriority(0)]
		private static void Postfix(ref double ___m_skipToTime, ref double ___m_timeSkipSpeed)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			float value = SleepCapPlugin.MaxSleepMinutes.Value;
			if (!(value <= 0f))
			{
				double timeSeconds = ZNet.instance.GetTimeSeconds();
				double num = ___m_skipToTime;
				double num2 = (double)value * 60.0;
				if (num - timeSeconds <= num2)
				{
					SleepCapPlugin.Log.LogInfo((object)("Sleep: " + ((num - timeSeconds) / 60.0).ToString("0.0") + " min to dawn - within the cap, sleeping through to morning."));
					return;
				}
				___m_skipToTime = timeSeconds + num2;
				___m_timeSkipSpeed = num2 / 12.0;
				SleepState.BlockUntil = num;
				SleepCapPlugin.Log.LogInfo((object)("Sleep capped: " + ((num - timeSeconds) / 60.0).ToString("0.0") + " min to dawn, skipping " + value.ToString("0.#") + " min. Next sleep allowed at dawn."));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "EverybodyIsTryingToSleep")]
	internal static class Game_EverybodyIsTryingToSleep_OnePerNight
	{
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "server_devcommands" })]
		private static void Postfix(ref bool __result)
		{
			if (!__result || !SleepCapPlugin.OneSleepPerNight.Value || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			if (!(timeSeconds >= SleepState.BlockUntil))
			{
				__result = false;
				if (timeSeconds - SleepState.LastMessageAt >= 15.0 && ZRoutedRpc.instance != null)
				{
					SleepState.LastMessageAt = timeSeconds;
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2]
					{
						2,
						SleepCapPlugin.RestedEnoughMessage.Value
					});
				}
			}
		}
	}
}
namespace DogHausAnchorRecipe
{
	[BepInPlugin("doghaus.anchorrecipe", "Anchor Balance", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class AnchorRecipePlugin : BaseUnityPlugin
	{
		public const string GUID = "doghaus.anchorrecipe";

		public const string NAME = "Anchor Balance";

		public const string VERSION = "1.2.1";

		public const string ShipyardGUID = "balrond.astafaraios.BalrondShipyard";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if (Kill.Off("doghaus.anchorrecipe", ((BaseUnityPlugin)this).Logger))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			Harmony val = new Harmony("doghaus.anchorrecipe");
			val.CreateClassProcessor(typeof(AnchorRecipePatch)).Patch();
			val.CreateClassProcessor(typeof(AnchorAfterShipyardSync)).Patch();
			Log.LogInfo((object)"Anchor Balance 1.2.1 loaded.");
		}
	}
	[HarmonyPatch]
	internal static class AnchorAfterShipyardSync
	{
		private static MethodBase s_target;

		private static bool Prepare()
		{
			Type type = AccessTools.TypeByName("BalrondShipyard.Launch");
			s_target = ((type != null) ? AccessTools.Method(type, "ApplySynchronizedConfiguration", (Type[])null, (Type[])null) : null);
			return s_target != null;
		}

		private static MethodBase TargetMethod()
		{
			return s_target;
		}

		private static void Postfix()
		{
			AnchorRecipePatch.Apply(ObjectDB.instance);
		}
	}
	[HarmonyPatch]
	internal static class AnchorRecipePatch
	{
		private const string ItemName = "SledgeAnchor";

		[HarmonyAfter(new string[] { "balrond.astafaraios.BalrondShipyard" })]
		[HarmonyPriority(0)]
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		[HarmonyPostfix]
		private static void AfterAwake(ObjectDB __instance)
		{
			Apply(__instance);
		}

		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "balrond.astafaraios.BalrondShipyard" })]
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static void AfterCopy(ObjectDB __instance)
		{
			Apply(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "balrond.astafaraios.BalrondShipyard" })]
		private static void AfterScene()
		{
			Apply(ObjectDB.instance);
		}

		internal static void Apply(ObjectDB db)
		{
			Undo.Restore("anchor");
			ApplyStats(db);
			ApplyRecipe(db);
		}

		private static void ApplyStats(ObjectDB db)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)db == (Object)null || db.m_items == null || db.m_items.Count == 0)
			{
				return;
			}
			GameObject itemPrefab = db.GetItemPrefab("SledgeAnchor");
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
			{
				return;
			}
			Balance current = Balance.Current;
			if (current.HasAnchorStats)
			{
				SharedData shared = val.m_itemData.m_shared;
				DamageTypes oldD = shared.m_damages;
				DamageTypes oldL = shared.m_damagesPerLevel;
				Undo.Remember("anchor", shared, "damage", delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0018: Unknown result type (might be due to invalid IL or missing references)
					//IL_001d: Unknown result type (might be due to invalid IL or missing references)
					shared.m_damages = oldD;
					shared.m_damagesPerLevel = oldL;
				});
				DamageTypes damages = shared.m_damages;
				damages.m_blunt = current.Blunt;
				damages.m_pierce = current.Pierce;
				damages.m_pickaxe = current.Pickaxe;
				shared.m_damages = damages;
				DamageTypes damagesPerLevel = shared.m_damagesPerLevel;
				damagesPerLevel.m_blunt = current.BluntPerLevel;
				damagesPerLevel.m_pierce = current.PiercePerLevel;
				damagesPerLevel.m_pickaxe = current.PickaxePerLevel;
				shared.m_damagesPerLevel = damagesPerLevel;
			}
		}

		private static void ApplyRecipe(ObjectDB db)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			if ((Object)(object)db == (Object)null || db.m_recipes == null || db.m_recipes.Count == 0)
			{
				return;
			}
			Recipe val = null;
			foreach (Recipe recipe in db.m_recipes)
			{
				if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && ((Object)((Component)recipe.m_item).gameObject).name == "SledgeAnchor")
				{
					val = recipe;
					break;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			List<Req> anchorRecipe = Balance.Current.AnchorRecipe;
			if (anchorRecipe.Count == 0)
			{
				return;
			}
			List<Requirement> list = new List<Requirement>();
			foreach (Req item2 in anchorRecipe)
			{
				string item = item2.Item;
				GameObject itemPrefab = db.GetItemPrefab(item);
				ItemDrop val2 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					AnchorRecipePlugin.Log.LogWarning((object)("Item '" + item + "' not found - Anchor recipe left as Shipyard set it."));
					return;
				}
				Requirement val3 = new Requirement();
				val3.m_resItem = val2;
				val3.m_amount = item2.Amount;
				val3.m_amountPerLevel = item2.PerLevel;
				val3.m_recover = true;
				list.Add(val3);
			}
			Requirement[] oldR = val.m_resources;
			Recipe rcp = val;
			Undo.Remember("anchor", rcp, "recipe", delegate
			{
				rcp.m_resources = oldR;
			});
			val.m_resources = list.ToArray();
			AnchorRecipePlugin.Log.LogInfo((object)("Anchor recipe set from " + Balance.Current.Source + " (" + list.Count + " ingredients)."));
		}
	}
}
namespace DogHausPieceBalance
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("doghaus.piecebalance", "Piece Balance", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PieceBalancePlugin : BaseUnityPlugin
	{
		public const string GUID = "doghaus.piecebalance";

		public const string NAME = "Piece Balance";

		public const string VERSION = "1.2.1";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if (Kill.Off("doghaus.piecebalance", ((BaseUnityPlugin)this).Logger))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			Harmony val = new Harmony("doghaus.piecebalance");
			val.CreateClassProcessor(typeof(ZNetScene_Awake_PieceBalance)).Patch();
			val.CreateClassProcessor(typeof(WearNTear_Awake_ClampHealth)).Patch();
			val.CreateClassProcessor(typeof(ShipLights_Cap)).Patch();
			val.CreateClassProcessor(typeof(Fireplace_Awake_Build)).Patch();
			Log.LogInfo((object)"Piece Balance 1.2.1 loaded.");
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Awake")]
	internal static class Fireplace_Awake_Build
	{
		private static bool s_warned;

		private static void Prefix(Fireplace __instance)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_014d: 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)
			if ((ZNetScene_Awake_PieceBalance.s_flameFor.Count == 0 && ZNetScene_Awake_PieceBalance.s_smokeFor.Count == 0) || ZNetView.m_forceDisableInit)
			{
				return;
			}
			try
			{
				string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
				Transform transform = ((Component)__instance).transform;
				if ((Object)(object)__instance.m_enabledObject == (Object)null && ZNetScene_Awake_PieceBalance.s_flameFor.Contains(prefabName))
				{
					List<Transform> list = ZNetScene_Awake_PieceBalance.FlameParts(transform);
					if (list.Count > 0)
					{
						Transform transform2 = new GameObject("DogHaus_Flame").transform;
						transform2.SetParent(transform, false);
						foreach (Transform item in list)
						{
							item.SetParent(transform2, true);
						}
						__instance.m_enabledObject = ((Component)transform2).gameObject;
					}
				}
				if (!ZNetScene_Awake_PieceBalance.s_smokeFor.TryGetValue(prefabName, out var value) || !((Object)(object)value.Source != (Object)null))
				{
					return;
				}
				Transform val = (((Object)(object)__instance.m_enabledObject != (Object)null) ? __instance.m_enabledObject.transform : transform);
				GameObject val2 = new GameObject("DogHaus_SmokeHolder");
				val2.SetActive(false);
				GameObject val3 = Object.Instantiate<GameObject>(((Component)value.Source).gameObject, val2.transform);
				((Object)val3).name = "DogHaus_Smoke";
				val3.transform.SetParent(val, true);
				val3.transform.position = transform.TransformPoint(value.LocalPos);
				Object.Destroy((Object)(object)val2);
				SmokeSpawner component = val3.GetComponent<SmokeSpawner>();
				if ((Object)(object)component != (Object)null)
				{
					if (value.Light)
					{
						component.m_interval = 3f;
						component.m_stopFireOnStart = false;
					}
					else
					{
						__instance.m_smokeSpawner = component;
					}
				}
			}
			catch (Exception ex)
			{
				if (!s_warned)
				{
					s_warned = true;
					PieceBalancePlugin.Log.LogWarning((object)("Could not build flame/smoke on a placed light: " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ShipLights_Cap
	{
		private static MethodBase s_target;

		private static readonly FieldInfo s_isHeat = AccessTools.Field(typeof(EffectArea), "m_isHeatType");

		private static bool Prepare()
		{
			Type type = AccessTools.TypeByName("BalrondShipyard.ShipExtensionManager");
			s_target = ((type != null) ? AccessTools.Method(type, "ApplyLights", (Type[])null, (Type[])null) : null);
			return s_target != null;
		}

		private static MethodBase TargetMethod()
		{
			return s_target;
		}

		private static void Postfix(MonoBehaviour __instance)
		{
			//IL_00a6: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			float shipLightMax = Balance.Current.ShipLightMax;
			if (shipLightMax > 0f)
			{
				Light[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Light>(true);
				foreach (Light val in componentsInChildren)
				{
					if (val.intensity > shipLightMax)
					{
						val.intensity = shipLightMax;
					}
				}
			}
			string shipLampHeat = Balance.Current.ShipLampHeat;
			if (shipLampHeat == "heat")
			{
				return;
			}
			string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
			if (prefabName == "Holk" || prefabName == "VikingShip_Ashlands")
			{
				return;
			}
			EffectArea[] componentsInChildren2 = ((Component)__instance).GetComponentsInChildren<EffectArea>(true);
			foreach (EffectArea val2 in componentsInChildren2)
			{
				if ((byte)(val2.m_type & 1) != 0)
				{
					Type val3 = (Type)(byte)(val2.m_type & 0xFE);
					val2.m_type = (Type)((shipLampHeat == "warm") ? ((byte)(val3 | 0x40)) : ((int)val3));
					if (s_isHeat != null)
					{
						s_isHeat.SetValue(val2, false);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Awake")]
	internal static class WearNTear_Awake_ClampHealth
	{
		private static void Postfix(WearNTear __instance, ZNetView ___m_nview)
		{
			if (!((Object)(object)___m_nview == (Object)null) && ___m_nview.IsValid() && ___m_nview.IsOwner())
			{
				ZDO zDO = ___m_nview.GetZDO();
				float num = zDO.GetFloat(ZDOVars.s_health, __instance.m_health);
				if (num > __instance.m_health)
				{
					zDO.Set(ZDOVars.s_health, __instance.m_health);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetScene_Awake_PieceBalance
	{
		internal sealed class SmokeSpec
		{
			public SmokeSpawner Source;

			public Vector3 LocalPos;

			public bool Light;
		}

		private const string G = "piece";

		internal const float TorchSmokeInterval = 3f;

		private static readonly HashSet<Fireplace> s_ours = new HashSet<Fireplace>();

		private static readonly Type s_lightEnabler = AccessTools.TypeByName("LightController.LightEnabler");

		internal static readonly HashSet<string> s_flameFor = new HashSet<string>();

		internal static readonly Dictionary<string, SmokeSpec> s_smokeFor = new Dictionary<string, SmokeSpec>();

		[HarmonyAfter(new string[] { "balrond.astafaraios.BalrondConstructions", "balrond.astafaraios.BalrondFurnitureReborn", "balrond.astafaraios.BalrondShipyard", "org.bepinex.helpers.PieceManager", "Raelaziel.OdinArchitect", "odinplus.plugins.odinskingdom", "Harmony.Gravebear.OdinsFoodBarrels", "com.jotunn.jotunn" })]
		[HarmonyPriority(0)]
		[HarmonyPostfix]
		private static void Postfix(ZNetScene __instance)
		{
			ApplyTo(__instance, Balance.Current);
		}

		internal static void ApplyTo(ZNetScene scene, Balance b)
		{
			if ((Object)(object)scene == (Object)null || b == null)
			{
				return;
			}
			Undo.Restore("piece");
			List<string> missing = new List<string>();
			int health = 0;
			int costs = 0;
			int support = 0;
			int names = 0;
			int comfort = 0;
			int fire = 0;
			int fuelSystems = 0;
			int warmth = 0;
			int durability = 0;
			int lights = 0;
			int smoke = 0;
			foreach (KeyValuePair<string, float> kv in b.Health)
			{
				List<string> missing2 = missing;
				KeyValuePair<string, float> keyValuePair = kv;
				Try(missing2, keyValuePair.Key, delegate
				{
					ZNetScene scene2 = scene;
					KeyValuePair<string, float> keyValuePair12 = kv;
					WearNTear wnt = Get<WearNTear>(scene2, keyValuePair12.Key);
					if ((Object)(object)wnt == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, float> keyValuePair13 = kv;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						float old = wnt.m_health;
						Undo.Remember("piece", wnt, "health", delegate
						{
							wnt.m_health = old;
						});
						WearNTear obj = wnt;
						KeyValuePair<string, float> keyValuePair14 = kv;
						obj.m_health = keyValuePair14.Value;
						health++;
					}
				});
			}
			foreach (KeyValuePair<string, List<Req>> kv2 in b.Costs)
			{
				List<string> missing3 = missing;
				KeyValuePair<string, List<Req>> keyValuePair2 = kv2;
				Try(missing3, keyValuePair2.Key, delegate
				{
					//IL_010f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0115: Expected O, but got Unknown
					ZNetScene scene2 = scene;
					KeyValuePair<string, List<Req>> keyValuePair12 = kv2;
					Piece piece = Get<Piece>(scene2, keyValuePair12.Key);
					if ((Object)(object)piece == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, List<Req>> keyValuePair13 = kv2;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						KeyValuePair<string, List<Req>> keyValuePair14 = kv2;
						Requirement[] array2 = (Requirement[])(object)new Requirement[keyValuePair14.Value.Count];
						int num7 = 0;
						while (true)
						{
							int num8 = num7;
							KeyValuePair<string, List<Req>> keyValuePair15 = kv2;
							if (num8 >= keyValuePair15.Value.Count)
							{
								break;
							}
							ZNetScene scene3 = scene;
							KeyValuePair<string, List<Req>> keyValuePair16 = kv2;
							ItemDrop val7 = Get<ItemDrop>(scene3, keyValuePair16.Value[num7].Item);
							if ((Object)(object)val7 == (Object)null)
							{
								List<string> list2 = missing;
								KeyValuePair<string, List<Req>> keyValuePair17 = kv2;
								string key = keyValuePair17.Key;
								KeyValuePair<string, List<Req>> keyValuePair18 = kv2;
								list2.Add(key + " (item " + keyValuePair18.Value[num7].Item + ")");
								return;
							}
							Requirement val8 = new Requirement();
							val8.m_resItem = val7;
							KeyValuePair<string, List<Req>> keyValuePair19 = kv2;
							val8.m_amount = keyValuePair19.Value[num7].Amount;
							val8.m_amountPerLevel = 0;
							val8.m_recover = true;
							array2[num7] = val8;
							num7++;
						}
						Requirement[] old = piece.m_resources;
						Undo.Remember("piece", piece, "cost", delegate
						{
							piece.m_resources = old;
						});
						piece.m_resources = array2;
						costs++;
					}
				});
			}
			foreach (string name in b.StoneSupport)
			{
				Try(missing, name, delegate
				{
					//IL_0061: Unknown result type (might be due to invalid IL or missing references)
					//IL_0066: Unknown result type (might be due to invalid IL or missing references)
					//IL_0093: Unknown result type (might be due to invalid IL or missing references)
					WearNTear wnt = Get<WearNTear>(scene, name);
					if ((Object)(object)wnt == (Object)null)
					{
						missing.Add(name);
					}
					else
					{
						MaterialType old = wnt.m_materialType;
						Undo.Remember("piece", wnt, "material", delegate
						{
							//IL_0007: Unknown result type (might be due to invalid IL or missing references)
							//IL_000c: Unknown result type (might be due to invalid IL or missing references)
							wnt.m_materialType = old;
						});
						wnt.m_materialType = (MaterialType)5;
						support++;
					}
				});
			}
			int materials = 0;
			foreach (KeyValuePair<string, MaterialType> kv3 in b.Support)
			{
				List<string> missing4 = missing;
				KeyValuePair<string, MaterialType> keyValuePair3 = kv3;
				Try(missing4, keyValuePair3.Key, delegate
				{
					//IL_0071: Unknown result type (might be due to invalid IL or missing references)
					//IL_0076: Unknown result type (might be due to invalid IL or missing references)
					//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
					ZNetScene scene2 = scene;
					KeyValuePair<string, MaterialType> keyValuePair12 = kv3;
					WearNTear wnt = Get<WearNTear>(scene2, keyValuePair12.Key);
					if ((Object)(object)wnt == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, MaterialType> keyValuePair13 = kv3;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						MaterialType old = wnt.m_materialType;
						Undo.Remember("piece", wnt, "material", delegate
						{
							//IL_0007: Unknown result type (might be due to invalid IL or missing references)
							//IL_000c: Unknown result type (might be due to invalid IL or missing references)
							wnt.m_materialType = old;
						});
						WearNTear obj = wnt;
						KeyValuePair<string, MaterialType> keyValuePair14 = kv3;
						obj.m_materialType = keyValuePair14.Value;
						materials++;
					}
				});
			}
			foreach (KeyValuePair<string, string> kv4 in b.Names)
			{
				List<string> missing5 = missing;
				KeyValuePair<string, string> keyValuePair4 = kv4;
				Try(missing5, keyValuePair4.Key, delegate
				{
					ZNetScene scene2 = scene;
					KeyValuePair<string, string> keyValuePair12 = kv4;
					Piece piece = Get<Piece>(scene2, keyValuePair12.Key);
					if ((Object)(object)piece == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, string> keyValuePair13 = kv4;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						string old = piece.m_name;
						Undo.Remember("piece", piece, "name", delegate
						{
							piece.m_name = old;
						});
						Piece obj = piece;
						KeyValuePair<string, string> keyValuePair14 = kv4;
						obj.m_name = keyValuePair14.Value;
						names++;
					}
				});
			}
			foreach (KeyValuePair<string, KeyValuePair<string, int>> kv5 in b.Comfort)
			{
				List<string> missing6 = missing;
				KeyValuePair<string, KeyValuePair<string, int>> keyValuePair5 = kv5;
				Try(missing6, keyValuePair5.Key, delegate
				{
					//IL_0071: Unknown result type (might be due to invalid IL or missing references)
					//IL_0076: Unknown result type (might be due to invalid IL or missing references)
					//IL_00da: Unknown result type (might be due to invalid IL or missing references)
					//IL_00df: Unknown result type (might be due to invalid IL or missing references)
					ZNetScene scene2 = scene;
					KeyValuePair<string, KeyValuePair<string, int>> keyValuePair12 = kv5;
					Piece piece = Get<Piece>(scene2, keyValuePair12.Key);
					if ((Object)(object)piece == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, KeyValuePair<string, int>> keyValuePair13 = kv5;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						ComfortGroup oldG = piece.m_comfortGroup;
						int oldC = piece.m_comfort;
						Undo.Remember("piece", piece, "comfort", delegate
						{
							//IL_0007: Unknown result type (might be due to invalid IL or missing references)
							//IL_000c: Unknown result type (might be due to invalid IL or missing references)
							piece.m_comfortGroup = oldG;
							piece.m_comfort = oldC;
						});
						Piece obj = piece;
						Type? typeFromHandle = typeof(ComfortGroup);
						KeyValuePair<string, KeyValuePair<string, int>> keyValuePair14 = kv5;
						obj.m_comfortGroup = (ComfortGroup)Enum.Parse(typeFromHandle, keyValuePair14.Value.Key, ignoreCase: true);
						Piece obj2 = piece;
						KeyValuePair<string, KeyValuePair<string, int>> keyValuePair15 = kv5;
						obj2.m_comfort = keyValuePair15.Value.Value;
						comfort++;
					}
				});
			}
			foreach (KeyValuePair<string, float[]> kv6 in b.Fire)
			{
				List<string> missing7 = missing;
				KeyValuePair<string, float[]> keyValuePair6 = kv6;
				Try(missing7, keyValuePair6.Key, delegate
				{
					ZNetScene scene2 = scene;
					KeyValuePair<string, float[]> keyValuePair12 = kv6;
					Fireplace val7 = Get<Fireplace>(scene2, keyValuePair12.Key);
					if ((Object)(object)val7 == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, float[]> keyValuePair13 = kv6;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						RememberFuel(val7);
						KeyValuePair<string, float[]> keyValuePair14 = kv6;
						val7.m_maxFuel = keyValuePair14.Value[0];
						KeyValuePair<string, float[]> keyValuePair15 = kv6;
						val7.m_secPerFuel = keyValuePair15.Value[1];
						if (val7.m_startFuel > val7.m_maxFuel)
						{
							val7.m_startFuel = val7.m_maxFuel;
						}
						fire++;
					}
				});
			}
			foreach (KeyValuePair<string, KeyValuePair<string, float>> kv7 in b.FuelSystem)
			{
				List<string> missing8 = missing;
				KeyValuePair<string, KeyValuePair<string, float>> keyValuePair7 = kv7;
				Try(missing8, keyValuePair7.Key, delegate
				{
					ZNetScene obj = scene;
					KeyValuePair<string, KeyValuePair<string, float>> keyValuePair12 = kv7;
					GameObject prefab3 = obj.GetPrefab(keyValuePair12.Key);
					ZNetScene obj2 = scene;
					KeyValuePair<string, KeyValuePair<string, float>> keyValuePair13 = kv7;
					GameObject prefab4 = obj2.GetPrefab(keyValuePair13.Value.Key);
					Fireplace val7 = (((Object)(object)prefab4 != (Object)null) ? prefab4.GetComponent<Fireplace>() : null);
					if ((Object)(object)prefab3 == (Object)null || (Object)(object)val7 == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair14 = kv7;
						string key = keyValuePair14.Key;
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair15 = kv7;
						list.Add(key + " (or twin " + keyValuePair15.Value.Key + ")");
					}
					else
					{
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair16 = kv7;
						if (AddFuelSystem(prefab3, val7, keyValuePair16.Value.Value))
						{
							fuelSystems++;
						}
						else
						{
							List<string> list2 = missing;
							KeyValuePair<string, KeyValuePair<string, float>> keyValuePair17 = kv7;
							list2.Add(keyValuePair17.Key + " (no flame parts found)");
						}
					}
				});
			}
			foreach (KeyValuePair<string, KeyValuePair<string, float>> kv8 in b.Warmth)
			{
				List<string> missing9 = missing;
				KeyValuePair<string, KeyValuePair<string, float>> keyValuePair8 = kv8;
				Try(missing9, keyValuePair8.Key, delegate
				{
					ZNetScene obj = scene;
					KeyValuePair<string, KeyValuePair<string, float>> keyValuePair12 = kv8;
					GameObject prefab3 = obj.GetPrefab(keyValuePair12.Key);
					if ((Object)(object)prefab3 == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair13 = kv8;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair14 = kv8;
						string key = keyValuePair14.Value.Key;
						KeyValuePair<string, KeyValuePair<string, float>> keyValuePair15 = kv8;
						if (SetWarmth(prefab3, key, keyValuePair15.Value.Value))
						{
							warmth++;
						}
						else
						{
							List<string> list2 = missing;
							KeyValuePair<string, KeyValuePair<string, float>> keyValuePair16 = kv8;
							list2.Add(keyValuePair16.Key + " (no heat area)");
						}
					}
				});
			}
			foreach (KeyValuePair<string, float[]> kv9 in b.Durability)
			{
				List<string> missing10 = missing;
				KeyValuePair<string, float[]> keyValuePair9 = kv9;
				Try(missing10, keyValuePair9.Key, delegate
				{
					ZNetScene scene2 = scene;
					KeyValuePair<string, float[]> keyValuePair12 = kv9;
					ItemDrop val7 = Get<ItemDrop>(scene2, keyValuePair12.Key);
					if ((Object)(object)val7 == (Object)null || val7.m_itemData == null || val7.m_itemData.m_shared == null)
					{
						List<string> list = missing;
						KeyValuePair<string, float[]> keyValuePair13 = kv9;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						SharedData sh = val7.m_itemData.m_shared;
						float oldMax = sh.m_maxDurability;
						float oldPer = sh.m_durabilityPerLevel;
						Undo.Remember("piece", sh, "durability", delegate
						{
							sh.m_maxDurability = oldMax;
							sh.m_durabilityPerLevel = oldPer;
						});
						SharedData obj = sh;
						KeyValuePair<string, float[]> keyValuePair14 = kv9;
						obj.m_maxDurability = keyValuePair14.Value[0];
						SharedData obj2 = sh;
						KeyValuePair<string, float[]> keyValuePair15 = kv9;
						obj2.m_durabilityPerLevel = keyValuePair15.Value[1];
						durability++;
					}
				});
			}
			foreach (KeyValuePair<string, float> kv10 in b.LightRange)
			{
				List<string> missing11 = missing;
				KeyValuePair<string, float> keyValuePair10 = kv10;
				Try(missing11, keyValuePair10.Key, delegate
				{
					ZNetScene obj = scene;
					KeyValuePair<string, float> keyValuePair12 = kv10;
					GameObject prefab3 = obj.GetPrefab(keyValuePair12.Key);
					if ((Object)(object)prefab3 == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, float> keyValuePair13 = kv10;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						Dictionary<string, float> lightIntensity = b.LightIntensity;
						KeyValuePair<string, float> keyValuePair14 = kv10;
						if (!lightIntensity.TryGetValue(keyValuePair14.Key, out var value))
						{
							value = -1f;
						}
						KeyValuePair<string, float> keyValuePair15 = kv10;
						if (SetLightRange(prefab3, keyValuePair15.Value, value))
						{
							lights++;
						}
						else
						{
							List<string> list2 = missing;
							KeyValuePair<string, float> keyValuePair16 = kv10;
							list2.Add(keyValuePair16.Key + " (no light)");
						}
					}
				});
			}
			foreach (KeyValuePair<string, KeyValuePair<string, string>> kv11 in b.Smoke)
			{
				List<string> missing12 = missing;
				KeyValuePair<string, KeyValuePair<string, string>> keyValuePair11 = kv11;
				Try(missing12, keyValuePair11.Key, delegate
				{
					ZNetScene obj = scene;
					KeyValuePair<string, KeyValuePair<string, string>> keyValuePair12 = kv11;
					GameObject prefab3 = obj.GetPrefab(keyValuePair12.Key);
					if ((Object)(object)prefab3 == (Object)null)
					{
						List<string> list = missing;
						KeyValuePair<string, KeyValuePair<string, string>> keyValuePair13 = kv11;
						list.Add(keyValuePair13.Key);
					}
					else
					{
						KeyValuePair<string, KeyValuePair<string, string>> keyValuePair14 = kv11;
						if (keyValuePair14.Value.Value == "none")
						{
							RemoveSmoke(prefab3);
							smoke++;
						}
						else
						{
							ZNetScene obj2 = scene;
							KeyValuePair<string, KeyValuePair<string, string>> keyValuePair15 = kv11;
							GameObject prefab4 = obj2.GetPrefab(keyValuePair15.Value.Key);
							if ((Object)(object)prefab4 == (Object)null)
							{
								List<string> list2 = missing;
								KeyValuePair<string, KeyValuePair<string, string>> keyValuePair16 = kv11;
								string key = keyValuePair16.Key;
								KeyValuePair<string, KeyValuePair<string, string>> keyValuePair17 = kv11;
								list2.Add(key + " (twin " + keyValuePair17.Value.Key + ")");
							}
							else
							{
								KeyValuePair<string, KeyValuePair<string, string>> keyValuePair18 = kv11;
								if (AddSmoke(prefab3, prefab4, keyValuePair18.Value.Value))
								{
									smoke++;
								}
								else
								{
									List<string> list3 = missing;
									KeyValuePair<string, KeyValuePair<string, string>> keyValuePair19 = kv11;
									list3.Add(keyValuePair19.Key + " (no smoke on twin, or no fire)");
								}
							}
						}
					}
				});
			}
			PieceBalancePlugin.Log.LogInfo((object)("New fuel systems " + fuelSystems + "/" + b.FuelSystem.Count + ", light " + lights + "/" + b.LightRange.Count + ", smoke " + smoke + "/" + b.Smoke.Count + "."));
			PieceBalancePlugin.Log.LogInfo((object)("Comfort " + comfort + "/" + b.Comfort.Count + ", fuel " + fire + "/" + b.Fire.Count + ", warmth " + warmth + "/" + b.Warmth.Count + ", durability " + durability + "/" + b.Durability.Count + "."));
			int num = 0;
			int num2 = 0;
			string text = "";
			StationExtension[] componentsInChildren2;
			if (Mathf.Abs(b.StationBuildRange - 1f) > 0.001f || Mathf.Abs(b.StationUpgradeDistance - 1f) > 0.001f)
			{
				foreach (GameObject prefab5 in scene.m_prefabs)
				{
					if ((Object)(object)prefab5 == (Object)null)
					{
						continue;
					}
					CraftingStation[] componentsInChildren = prefab5.GetComponentsInChildren<CraftingStation>(true);
					foreach (CraftingStation val in componentsInChildren)
					{
						CraftingStation st = val;
						float range = st.m_rangeBuild;
						float extra = st.m_extraRangePerLevel;
						Undo.Remember("piece", st, "rangeBuild", delegate
						{
							st.m_rangeBuild = range;
							st.m_extraRangePerLevel = extra;
						});
						st.m_rangeBuild = range * b.StationBuildRange;
						st.m_extraRangePerLevel = extra * b.StationBuildRange;
						num++;
						if (((Object)prefab5).name == "piece_workbench")
						{
							text = " (workbench " + st.m_rangeBuild.ToString("0.#") + " m +" + st.m_extraRangePerLevel.ToString("0.#") + " per upgrade)";
						}
					}
					componentsInChildren2 = prefab5.GetComponentsInChildren<StationExtension>(true);
					foreach (StationExtension val2 in componentsInChildren2)
					{
						StationExtension e = val2;
						float dist = e.m_maxStationDistance;
						Undo.Remember("piece", e, "maxStationDistance", delegate
						{
							e.m_maxStationDistance = dist;
						});
						e.m_maxStationDistance = dist * b.StationUpgradeDistance;
						num2++;
					}
				}
			}
			int num5 = 0;
			CraftingStation[] array = Object.FindObjectsByType<CraftingStation>((FindObjectsSortMode)0);
			foreach (CraftingStation val3 in array)
			{
				GameObject prefab = scene.GetPrefab(Utils.GetPrefabName(((Component)val3).gameObject));
				CraftingStation val4 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren<CraftingStation>(true) : null);
				if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4 == (Object)(object)val3))
				{
					val3.m_rangeBuild = val4.m_rangeBuild;
					val3.m_extraRangePerLevel = val4.m_extraRangePerLevel;
					num5++;
				}
			}
			componentsInChildren2 = Object.FindObjectsByType<StationExtension>((FindObjectsSortMode)0);
			foreach (StationExtension val5 in componentsInChildren2)
			{
				GameObject prefab2 = scene.GetPrefab(Utils.GetPrefabName(((Component)val5).gameObject));
				StationExtension val6 = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponentInChildren<StationExtension>(true) : null);
				if (!((Object)(object)val6 == (Object)null) && !((Object)(object)val6 == (Object)(object)val5))
				{
					val5.m_maxStationDistance = val6.m_maxStationDistance;
					num5++;
				}
			}
			PieceBalancePlugin.Log.LogInfo((object)("Crafting stations x" + b.StationBuildRange.ToString("0.##") + " build radius: " + num + " stations" + text + "; upgrades x" + b.StationUpgradeDistance.ToString("0.##") + " distance: " + num2 + " (" + num5 + " already standing updated)."));
			PieceBalancePlugin.Log.LogInfo((object)("Piece balance applied (" + b.Source + "): " + health + "/" + b.Health.Count + " health, " + costs + "/" + b.Costs.Count + " costs, " + support + "/" + b.StoneSupport.Count + " stone-support, " + materials + "/" + b.Support.Count + " support, " + names + "/" + b.Names.Count + " names."));
			if (missing.Count > 0)
			{
				PieceBalancePlugin.Log.LogWarning((object)("Not found or skipped (left unchanged): " + string.Join(", ", missing.ToArray())));
			}
		}

		private static void Try(List<string> missing, string what, Action work)
		{
			try
			{
				work();
			}
			catch (Exception ex)
			{
				missing.Add(what + " (error: " + ex.Message + ")");
			}
		}

		private static void RememberFuel(Fireplace fp)
		{
			float max = fp.m_maxFuel;
			float sec = fp.m_secPerFuel;
			float start = fp.m_startFuel;
			bool inf = fp.m_infiniteFuel;
			ItemDrop item = fp.m_fuelItem;
			bool noCover = fp.m_disableCoverCheck;
			bool canOff = fp.m_canTurnOff;
			bool refill = fp.m_canRefill;
			float terrain = fp.m_checkTerrainOffset;
			float coverOff = fp.m_coverCheckOffset;
			float hold = fp.m_holdRepeatInterval;
			float half = fp.m_halfThreshold;
			EffectList added = fp.m_fuelAddedEffects;
			EffectList toggle = fp.m_toggleOnEffects;
			Undo.Remember("piece", fp, "fuel", delegate
			{
				fp.m_maxFuel = max;
				fp.m_secPerFuel = sec;
				fp.m_startFuel = start;
				fp.m_infiniteFuel = inf;
				fp.m_fuelItem = item;
				fp.m_disableCoverCheck = noCover;
				fp.m_canTurnOff = canOff;
				fp.m_canRefill = refill;
				fp.m_checkTerrainOffset = terrain;
				fp.m_coverCheckOffset = coverOff;
				fp.m_holdRepeatInterval = hold;
				fp.m_halfThreshold = half;
				fp.m_fuelAddedEffects = added;
				fp.m_toggleOnEffects = toggle;
			});
		}

		internal static List<Transform> FlameParts(Transform root)
		{
			List<Transform> list = new List<Transform>();
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)(object)root || (!((Object)(object)((Component)val).GetComponent<Light>() != (Object)null) && !((Object)(object)((Component)val).GetComponent<ParticleSystem>() != (Object)null) && !((Object)(object)((Component)val).GetComponent<EffectArea>() != (Object)null) && !((Object)(object)((Component)val).GetComponent<AudioSource>() != (Object)null)) || (Object)(object)((Component)val).GetComponent<MeshRenderer>() != (Object)null || ((Object)(object)((Component)val).GetComponent<Collider>() != (Object)null && (Object)(object)((Component)val).GetComponent<EffectArea>() == (Object)null))
				{
					continue;
				}
				bool flag = false;
				foreach (Transform item in list)
				{
					if (val.IsChildOf(item))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					list.Add(val);
				}
			}
			return list;
		}

		private static bool AddFuelSystem(GameObject prefab, Fireplace twin, float capacity)
		{
			Fireplace val = prefab.GetComponent<Fireplace>();
			if ((Object)(object)val == (Object)null)
			{
				if ((Object)(object)prefab.GetComponent<ZNetView>() == (Object)null || FlameParts(prefab.transform).Count == 0)
				{
					return false;
				}
				val = prefab.AddComponent<Fireplace>();
				val.m_enabledObject = null;
				val.m_infiniteFuel = true;
				Piece component = prefab.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null)
				{
					val.m_name = component.m_name;
				}
				s_ours.Add(val);
				if (s_lightEnabler != null && (Object)(object)prefab.GetComponent(s_lightEnabler) == (Object)null)
				{
					prefab.AddComponent(s_lightEnabler);
				}
			}
			if (s_ours.Contains(val))
			{
				string key = ((Object)prefab).name;
				s_flameFor.Add(key);
				Undo.Remember("piece", val, "flameSpec", delegate
				{
					s_flameFor.Remove(key);
				});
			}
			RememberFuel(val);
			val.m_fuelItem = twin.m_fuelItem;
			val.m_secPerFuel = twin.m_secPerFuel;
			val.m_maxFuel = capacity;
			val.m_startFuel = (s_ours.Contains(val) ? capacity : Mathf.Min(twin.m_startFuel, capacity));
			val.m_infiniteFuel = false;
			val.m_disableCoverCheck = twin.m_disableCoverCheck;
			val.m_checkTerrainOffset = twin.m_checkTerrainOffset;
			val.m_coverCheckOffset = twin.m_coverCheckOffset;
			val.m_holdRepeatInterval = twin.m_holdRepeatInterval;
			val.m_halfThreshold = twin.m_halfThreshold;
			val.m_canTurnOff = twin.m_canTurnOff;
			val.m_canRefill = true;
			val.m_fuelAddedEffects = twin.m_fuelAddedEffects;
			val.m_toggleOnEffects = twin.m_toggleOnEffects;
			return true;
		}

		private static bool SetLightRange(GameObject prefab, float range, float intensity)
		{
			Light[] componentsInChildren = prefab.GetComponentsInChildren<Light>(true);
			if (componentsInChildren.Length == 0)
			{
				return false;
			}
			float num = 0f;
			float num2 = 0f;
			Light[] array = componentsInChildren;
			foreach (Light val in array)
			{
				Light light = val;
				float r0 = light.range;
				float i2 = light.intensity;
				Undo.Remember("piece", light, "light", delegate
				{
					light.range = r0;
					light.intensity = i2;
				});
				if (light.range > num)
				{
					num = light.range;
				}
				if (light.intensity > num2)
				{
					num2 = light.intensity;
				}
			}
			if (num <= 0f)
			{
				return false;
			}
			float num3 = range / num;
			float num4 = ((intensity > 0f && num2 > 0f) ? (intensity / num2) : 1f);
			Light[] array2 = componentsInChildren;
			foreach (Light val2 in array2)
			{
				val2.range *= num3;
				val2.intensity *= num4;
			}
			return true;
		}

		private static void RemoveSmoke(GameObject prefab)
		{
			Fireplace fp = prefab.GetComponent<Fireplace>();
			if ((Object)(object)fp != (Object)null)
			{
				SmokeSpawner wired = fp.m_smokeSpawner;
				Undo.Remember("piece", fp, "smokeWire", delegate
				{
					fp.m_smokeSpawner = wired;
				});
				fp.m_smokeSpawner = null;
			}
			SmokeSpawner[] componentsInChildren = prefab.GetComponentsInChildren<SmokeSpawner>(true);
			foreach (SmokeSpawner val in componentsInChildren)
			{
				SmokeSpawner s = val;
				bool on = ((Behaviour)s).enabled;
				Undo.Remember("piece", s, "enabled", delegate
				{
					((Behaviour)s).enabled = on;
				});
				((Behaviour)s).enabled = false;
			}
		}

		private static bool AddSmoke(GameObject prefab, GameObject twin, string mode)
		{
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			bool flag = mode == "top";
			bool flag2 = mode == "light";
			Fireplace fp = prefab.GetComponent<Fireplace>();
			SmokeSpawner componentInChildren = twin.GetComponentInChildren<SmokeSpawner>(true);
			if ((Object)(object)fp == (Object)null || (Object)(object)componentInChildren == (Object)null)
			{
				return false;
			}
			SmokeSpawner wired = fp.m_smokeSpawner;
			Undo.Remember("piece", fp, "smokeWire", delegate
			{
				fp.m_smokeSpawner = wired;
			});
			SmokeSpawner val = (((Object)(object)fp.m_smokeSpawner != (Object)null) ? fp.m_smokeSpawner : prefab.GetComponentInChildren<SmokeSpawner>(true));
			if ((Object)(object)val != (Object)null)
			{
				SmokeSpawner e = val;
				bool on = ((Behaviour)e).enabled;
				float interval = e.m_interval;
				Undo.Remember("piece", e, "smoke", delegate
				{
					((Behaviour)e).enabled = on;
					e.m_interval = interval;
				});
				((Behaviour)e).enabled = true;
				if (flag2)
				{
					e.m_interval = 3f;
					fp.m_smokeSpawner = null;
				}
				return true;
			}
			Transform val2 = (((Object)(object)fp.m_enabledObject != (Object)null) ? fp.m_enabledObject.transform : prefab.transform);
			Vector3 val6 = default(Vector3);
			if (flag)
			{
				float num = float.MinValue;
				MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
				foreach (MeshFilter val3 in componentsInChildren)
				{
					if ((Object)(object)val3.sharedMesh == (Object)null || (((Component)val3).transform.IsChildOf(val2) && (Object)(object)val2 != (Object)(object)prefab.transform))
					{
						continue;
					}
					Bounds bounds = val3.sharedMesh.bounds;
					for (int num3 = 0; num3 < 8; num3++)
					{
						Vector3 val4 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((num3 & 1) != 0) ? 1 : (-1)), (float)(((num3 & 2) != 0) ? 1 : (-1)), (float)(((num3 & 4) != 0) ? 1 : (-1))));
						Vector3 val5 = ((Component)val3).transform.TransformPoint(val4);
						if (val5.y > num)
						{
							num = val5.y;
						}
					}
				}
				if (num == float.MinValue)
				{
					return false;
				}
				Vector3 position = val2.position;
				((Vector3)(ref val6))..ctor(position.x, num + 0.3f, position.z);
			}
			else
			{
				Fireplace component = twin.GetComponent<Fireplace>();
				Vector3 val7 = (((Object)(object)component != (Object)null && (Object)(object)component.m_enabledObject != (Object)null) ? component.m_enabledObject.transform.position : twin.transform.position);
				val6 = val2.position + (((Component)componentInChildren).transform.position - val7);
			}
			SmokeSpec smokeSpec = new SmokeSpec();
			smokeSpec.Source = componentInChildren;
			smokeSpec.LocalPos = prefab.transform.InverseTransformPoint(val6);
			smokeSpec.Light = flag2;
			string key = ((Object)prefab).name;
			s_smokeFor[key] = smokeSpec;
			Undo.Remember("piece", fp, "smokeSpec", delegate
			{
				s_smokeFor.Remove(key);
			});
			return true;
		}

		private static bool SetWarmth(GameObject prefab, string kind, float radius)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			bool result = false;
			EffectArea[] componentsInChildren = prefab.GetComponentsInChildren<EffectArea>(true);
			foreach (EffectArea val in componentsInChildren)
			{
				EffectArea a = val;
				Type val2 = (Type)65;
				if ((byte)(a.m_type & val2) == 0)
				{
					continue;
				}
				SphereCollider sc = ((Component)a).GetComponent<SphereCollider>();
				CapsuleCollider cc = ((Component)a).GetComponent<CapsuleCollider>();
				Type oldType = a.m_type;
				float oldR = (((Object)(object)sc != (Object)null) ? sc.radius : (((Object)(object)cc != (Object)null) ? cc.radius : 0f));
				Undo.Remember("piece", a, "warmth", delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					a.m_type = oldType;
					if ((Object)(object)sc != (Object)null)
					{
						sc.radius = oldR;
					}
					else if ((Object)(object)cc != (Object)null)
					{
						cc.radius = oldR;
					}
				});
				Type val3 = (Type)(byte)(a.m_type & (byte)(~val2));
				if (kind == "heat")
				{
					a.m_type = (Type)(byte)(val3 | 1);
				}
				else if (kind == "warm")
				{
					a.m_type = (Type)(byte)(val3 | 0x40);
				}
				else
				{
					a.m_type = val3;
				}
				if (kind != "none")
				{
					Vector3 lossyScale = ((Component)a).transform.lossyScale;
					float num = Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Max(Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z)));
					if (num <= 0f)
					{
						num = 1f;
					}
					if ((Object)(object)sc != (Object)null)
					{
						sc.radius = radius / num;
					}
					else if ((Object)(object)cc != (Object)null)
					{
						cc.radius = radius / num;
					}
				}
				result = true;
			}
			return result;
		}

		private static T Get<T>(ZNetScene scene, string prefab) where T : Component
		{
			GameObject prefab2 = scene.GetPrefab(prefab);
			if (!((Object)(object)prefab2 != (Object)null))
			{
				return default(T);
			}
			return prefab2.GetComponent<T>();
		}
	}
}
namespace DogHausBalance
{
	internal class Req
	{
		public string Item;

		public int Amount;

		public int PerLevel;
	}
	internal class MiningDrop
	{
		public string Item;

		public float Percent;

		public int Tier;

		public float NearWater;
	}
	internal static class Kill
	{
		private static HashSet<string> s_off;

		internal static bool Off(string guid, ManualLogSource log)
		{
			if (s_off == null)
			{
				s_off = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				try
				{
					string path = Path.Combine(Paths.ConfigPath, "DogHausTweaks.disable.txt");
					if (File.Exists(path))
					{
						string[] array = File.ReadAllLines(path);
						foreach (string text in array)
						{
							string text2 = text.Trim();
							if (text2.Length > 0 && !text2.StartsWith("#"))
							{
								s_off.Add(text2);
							}
						}
					}
				}
				catch (Exception)
				{
				}
			}
			if (!s_off.Contains(guid))
			{
				return false;
			}
			log.LogWarning((object)(guid + " is switched OFF by DogHausTweaks.disable.txt (troubleshooting)."));
			return true;
		}
	}
	internal static class Undo
	{
		private static readonly Dictionary<string, List<Action>> s_restore = new Dictionary<string, List<Action>>();

		private static readonly Dictionary<string, HashSet<string>> s_seen = new Dictionary<string, HashSet<string>>();

		internal static void Remember(string group, object target, string field, Action restore)
		{
			if (!s_seen.TryGetValue(group, out var value))
			{
				value = new HashSet<string>();
				s_seen[group] = value;
				s_restore[group] = new List<Action>();
			}
			Object uo = (Object)((target is Object) ? target : null);
			if (uo != (Object)null)
			{
				Action inner = restore;
				restore = delegate
				{
					if (Object.op_Implicit(uo))
					{
						inner();
					}
				};
			}
			string item = ((uo != (Object)null) ? uo.GetInstanceID().ToString() : RuntimeHelpers.GetHashCode(target).ToString()) + ":" + field;
			if (value.Add(item))
			{
				s_restore[group].Add(restore);
			}
		}

		internal static void Restore(string group)
		{
			if (!s_restore.TryGetValue(group, out var value))
			{
				return;
			}
			for (int num = value.Count - 1; num >= 0; num--)
			{
				try
				{
					value[num]();
				}
				catch (Exception ex)
				{
					Balance.Log.LogWarning((object)("Undo failed (" + group + "): " + ex.Message));
				}
			}
			value.Clear();
			s_seen[group].Clear();
		}
	}
	internal class Balance
	{
		public float Blunt = 42f;

		public float BluntPerLevel = 4f;

		public float Pierce = 10f;

		public float PiercePerLevel = 1f;

		public float Pickaxe = 30f;

		public float PickaxePerLevel = 4f;

		public bool HasAnchorStats;

		public List<Req> AnchorRecipe = new List<Req>();

		public float TorchRadius = 1.5f;

		public float SmallRadius = 0.5f;

		public float HeldSummerHeat = 1f;

		public float StationBuildRange = 1.25f;

		public float StationUpgradeDistance = 1.25f;

		public Dictionary<string, float> LightRange = new Dictionary<string, float>(StringComparer.Ordinal);

		public Dictionary<string, float> LightIntensity = new Dictionary<string, float>(StringComparer.Ordinal);

		public Dictionary<string, KeyValuePair<string, string>> Smoke = new Dictionary<string, KeyValuePair<string, string>>(StringComparer.Ordinal);

		public Dictionary<string, float[]> Craters = new Dictionary<string, float[]>(StringComparer.OrdinalIgnoreCase);

		public Dictionary<string, string> CraterSettings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		public float TamingTime = 900f;

		public int BreedingLimit = 10;

		public bool AutoRepair = true;

		public bool ChestNames = true;

		public bool ChestNameNeedsTool = true;

		public bool BuildSearchByMod = true;

		public bool RightClickSplit = true;

		public bool BuildingHealth = true;

		public bool SnowCurve = true;

		public float SnowStartCeiling = 0.4f;

		public int SnowPeakFrom = 11;

		public int SnowPeakTo = 17;

		public int SnowBareOn = 20;

		public bool SnowSweep = true;

		public bool SnowFreshClean = true;

		public bool WinterFreeze = true;

		public int WinterStartDay = 7;

		public int WinterEndDay = 13;

		public float WinterColdSeconds = 60f;

		public float DmComboA = 1.43f;

		public float DmComboB = 1f;

		public float DmComboC = 1.1f;

		public float DmSpecial = 1.65f;

		public float DmChainAfterHit = 0.5f;

		public float DmMaxTailSpeed = 1.5f;

		public List<string[]> ModSettings = new List<string[]>();

		public float WinterNightMinutes = 8f;

		public float GroundHealth = 17.5f;

		public float MaxDig = 2f;

		public float CraterWallBand = 1f;

		public Dictionary<string, float> Hardness = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		public float VeinRadius = 30f;

		public float VeinMultiplier = 2f;

		public float DistanceMinMultiplier = 0.5f;

		public float DistanceMaxMultiplier = 1.5f;

		public Dictionary<string, List<MiningDrop>> Mining = new Dictionary<string, List<MiningDrop>>(StringComparer.OrdinalIgnoreCase);

		public Dictionary<string, List<Req>> Costs = new Dictionary<string, List<Req>>(StringComparer.Ordinal);

		public Dictionary<string, float> Health = new Dictionary<string, float>(StringComparer.Ordinal);

		public List<string> StoneSupport = new List<string>();

		public Dictionary<string, MaterialType> Support = new Dictionary<string, MaterialType>(StringComparer.Ordinal);

		public float ShipLightMax = 1.5f;

		public string ShipLampHeat = "warm";

		public Dictionary<string, string> Names = new Dictionary<string, string>(StringComparer.Ordinal);

		public Dictionary<string, KeyValuePair<string, int>> Comfort = new Dictionary<string, KeyValuePair<string, int>>(StringComparer.Ordinal);

		public Dictionary<string, float[]> Fire = new Dictionary<string, float[]>(StringComparer.Ordinal);

		public Dictionary<string, KeyValuePair<string, float>> Warmth = new Dictionary<string, KeyValuePair<string, float>>(StringComparer.Ordinal);

		public Dictionary<string, KeyValuePair<string, float>> FuelSystem = new Dictionary<string, KeyValuePair<string, float>>(StringComparer.Ordinal);

		public Dictionary<string, float[]> Durability = new Dictionary<string, float[]>(StringComparer.Ordinal);

		public List<string> Problems = new List<string>();

		public string Text;

		public string Source;

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("DogHaus Balance");

		private static Balance s_local;

		private static Balance s_current;

		public static string FilePath => Path.Combine(Paths.ConfigPath, "DogHausTweaks.balance.txt");

		public static Balance Local
		{
			get
			{
				if (s_local == null)
				{
					s_local = LoadLocal();
				}
				return s_local;
			}
		}

		public static Balance Current
		{
			get
			{
				if (s_current == null)
				{
					s_current = Local;
				}
				return s_current;
			}
		}

		private static Balance LoadLocal()
		{
			string source;
			string text;
			try
			{
				if (File.Exists(FilePath))
				{
					text = File.ReadAllText(FilePath);
					source = "local file";
				}
				else
				{
					File.WriteAllText(FilePath, "# DogHausTweaks balance file.\r\n# In multiplayer only the SERVER copy counts: players receive it when they join.\r\n# Edit on the server, restart the server, done - no pack update needed.\r\n# Lines: \"name = value\". Anything after # is a note. Unknown pieces or items are skipped and logged.\r\n\r\n[anchor]\r\n# Balrond Shipyard Anchor (SledgeAnchor): base damage, then +per upgrade level.\r\n# Vanilla: iron sledge 55 blunt +6; iron pickaxe 33 pickaxe +5.\r\nblunt = 42 +4\r\npierce = 10 +1\r\npickaxe = 30 +4\r\n# Recipe: item amount +per upgrade level.\r\nrecipe = Iron 44 +22, LeatherScraps 20 +10, Chain 4 +1, TrophySerpent 1\r\n\r\n[torch]\r\n# Warm radius around a LIT torch or sconce, in metres (removes Cold/Freezing; never gives Rested). DogHaus: 1.5 m.\r\nradius = 1.5\r\n# Candles and other small flames (DogHaus: 0.5 m).\r\nsmall_radius = 0.5\r\n# Summer daytime: a torch in your hand heats you (Seasons' summer heat meter) at this many times the rate of standing in\r\n# direct sun - in shade or indoors too. DogHaus: 1. 0 = off.\r\nheld_summer_heat = 1\r\n\r\n[craters]\r\n# Creature terrain damage (craters). Server-owned like everything else here. Creature = <depth m> <radius m>;\r\n# the depth grows by star_bonus per star, then biome hardness ([terrain]) and the wall band apply. 0 depth = never digs.\r\nenabled = true\r\nmax_depth = 2.5\r\nstar_bonus = 0.5\r\ncooldown = 3\r\ncontact_radius = 2.5\r\nrespect_wards = true\r\nclaim_ownership = true\r\nGreyling = 0 0.34\r\nBoar = 0.058 0.46\r\nEikthyr = 0.5 4\r\nGreydwarf = 0.042 0.34\r\nGreydwarf_Elite = 0.333 2.66\r\nTroll = 0.625 5\r\nBjorn = 0.4 3.2\r\ngd_king = 1.042 8.34\r\nDraugr_Elite = 0.042 0.34\r\nBlobElite = 0 0.66\r\nAbomination = 0.917 7.34\r\nBonemass = 1.167 9.34\r\nWolf = 0.1 0.8\r\nUlv = 0.1 0.8\r\nFenring = 0.125 1\r\nFenring_Cultist = 0.083 0.66\r\nStoneGolem = 0.75 6\r\nDragon = 0 5\r\nGoblin = 0.042 0.34\r\nGoblinBrute = 0.417 3.34\r\nBlobTar = 0 0.34\r\nLox = 0.542 4.34\r\nGoblinKing = 1.25 10\r\nSeeker = 0 0.34\r\nSeekerBrute = 0.5 4\r\nSeekerQueen = 0.833 6.66\r\nAsksvin = 0.125 1\r\nMorgen = 0.75 6\r\nFader = 1.25 10\r\nNeck = 0 0.24\r\nGreydwarf_Shaman = 0.04 0.32\r\nSkeleton = 0.04 0.32\r\nSkeleton_Meadows = 0.04 0.32\r\nSkeleton_Poison = 0.05 0.4\r\nGhost = 0 0.2\r\nDraugr = 0.05 0.4\r\nBlob = 0 0.48\r\nSurtling = 0 0.24\r\nWraith = 0 0.2\r\nHatchling = 0 0.4\r\nBat = 0 0.2\r\nBat_Swamp = 0 0.2\r\nDeathsquito = 0 0.2\r\nGoblinShaman = 0.05 0.4\r\nGjall = 0 6.4\r\nTick = 0 0.24\r\nSeekerBrood = 0 0.24\r\nCharred_Melee = 0.08 0.64\r\nCharred_Twitcher = 0.04 0.32\r\nCharred_Archer = 0.04 0.32\r\nGoblin_Gem = 0.06 0.48\r\nFrysling = 0.05 0.4\r\nElakingMole = 0.3 2.4\r\nElakingLantern = 0.06 0.48\r\nJotunWarrior = 1.04 8.32\r\nJotunWarriorDualWield = 1.04 8.32\r\nJotunWitch = 0.6 4.8\r\nFrozenKing = 1.25 10\r\nWrithan = 0.8 6.4\r\nSeal = 0.042 0.34\r\nElaking = 0.083 0.66\r\nMoose = 0.25 2\r\nBarka = 0.667 5.34\r\n\r\n[creatures]\r\n# Replaces the Adjustable Taming Speed and Adjustable Breeding Limit mods. 0 = leave the game value.\r\n# taming_time: seconds to tame (vanilla 1800). breeding_limit: tamed animals nearby before breeding stops (vanilla 4).\r\ntaming_time = 900\r\nbreeding_limit = 10\r\n\r\n[qol]\r\n# auto_repair (replaces ComfyAutoRepair): using a crafting station repairs everything it can in your inventory.\r\n# chest_names (replaces CustomChestName): hammer on your belt, Alt + Use on a chest to name it.\r\nauto_repair = true\r\nchest_names = true\r\nchest_name_needs_tool = true\r\n# building_health (replaces Building Health Display): the piece you look at gets a red-yellow-green health bar plus health and integrity text.\r\nbuilding_health = true\r\n# right_click_split: Minecraft-style stacks. Mouse: right-click picks up half, right-click while holding puts down one,\r\n# Ctrl + right-click uses the item. Controller: RT + X does the same; X alone still uses.\r\nright_click_split = true\r\n# snow_sweep: with the Seasons mod, the hammer repair swing on a snowy piece sweeps its snow off (costs a swing of stamina); snow builds up again.\r\nsnow_sweep = true\r\n# snow_fresh_clean: with the Seasons mod, a piece you build in winter starts with no snow and builds up from there.\r\nsnow_fresh_clean = true\r\n# build_search_by_mod: the build menu search also finds pieces by the mod that added them (kingdom, architect, balrond, vanilla...).\r\nbuild_search_by_mod = true\r\n\r\n[winter]\r\n# Winter Freezing (DogHaus 2026-10-02), only while the Seasons mod says it is Winter. Freezing replaces Cold:\r\n# 1) days start_day to end_day: Cold for more than cold_seconds real seconds in a row, not near a fire, becomes Freezing.\r\n# 2) all winter: Wet means Freezing.   3) all winter: the middle night_minutes real minutes of each night (centred on midnight) are Freezing.\r\n# No Freezing with frost resistance (gear or mead), near a fire AND under shelter, or inside a warm zone (lit torch). 0 night_minutes = no night rule.\r\nenabled = true\r\nstart_day = 7\r\nend_day = 13\r\ncold_seconds = 60\r\nnight_minutes = 8\r\n\r\n[snow]\r\n# Winter snow curve on buildings (Snow Care, needs Seasons). The most snow a piece can hold, by day of winter: start_ceiling\r\n# (0..1) on day 1 rising to full on peak_from, full until peak_to, then one step lower each day to none on bare_on, so spring\r\n# arrives on bare roofs. Snow still only builds when it snows. curve = false leaves Seasons' own rules.\r\ncurve = true\r\nstart_ceiling = 0.4\r\npeak_from = 11\r\npeak_to = 17\r\nbare_on = 20\r\n\r\n[dualmastery]\r\n# Dual Mastery Fix (Balrond DualMastery dual-wield swings). Total swing length in real seconds, swing start to the point the\r\n# swing ends; bigger = slower. combo_1..3 are the three chained swings, special is the secondary attack.\r\n# chain_after_hit: seconds after the last hit before the next chained swing may start. max_tail_speed: fastest the\r\n# follow-through may be sped up (1 = never faster than normal). DogHaus 2026-10-02: 1.43 / 1.00 / 1.10 / 1.65, 0.5, 1.5.\r\ncombo_1 = 1.43\r\ncombo_2 = 1.00\r\ncombo_3 = 1.10\r\nspecial = 1.65\r\nchain_after_hit = 0.5\r\nmax_tail_speed = 1.5\r\n\r\n[terrain]\r\n# Ground health (DogHaus). Each pickaxe swing removes pickaxe damage / (ground_health x hardness) m3 of ground.\r\n# 17.5 = the iron pickaxe digs exactly like vanilla on normal ground; antler about half, black metal about 1.5x.\r\n# max_dig caps one swing at that many vanilla swings. 0 ground_health = vanilla digging.\r\n# hardness.<Biome>: >1 harder ground, <1 softer. Creature craters use the same hardness.\r\nground_health = 17.5\r\nmax_dig = 2\r\n# Creature craters: ground more than this many metres above where the blow landed (terrain walls, cliffs) is worn\r\n# down from the top by the blow own crater depth instead of being cut to the floor. 0 = off (vanilla: a blow at\r\n# the foot of a terrain wall takes the whole column).\r\ncrater_wall_band = 1\r\nhardness.Swamp = 0.67\r\nhardness.Mistlands = 1.2\r\nha