Decompiled source of ValheimStructureImmunity v0.3.9

plugins/ValheimStructureImmunity.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ValheimStructureImmunity")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ValheimStructureImmunity")]
[assembly: AssemblyTitle("ValheimStructureImmunity")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimStructureImmunity
{
	[BepInPlugin("ValheimStructureImmunity", "Valheim Structure Immunity", "0.3.9")]
	public class Plugin : BaseUnityPlugin
	{
		internal static class StructuralDamageContext
		{
			[ThreadStatic]
			internal static int UpdateWearDepth;
		}

		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		internal static class WearNTearUpdateWearContextPatch
		{
			private static void Prefix()
			{
				StructuralDamageContext.UpdateWearDepth++;
			}

			private static void Postfix()
			{
				StructuralDamageContext.UpdateWearDepth = Math.Max(0, StructuralDamageContext.UpdateWearDepth - 1);
			}

			private static Exception Finalizer(Exception __exception)
			{
				if (__exception != null)
				{
					StructuralDamageContext.UpdateWearDepth = Math.Max(0, StructuralDamageContext.UpdateWearDepth - 1);
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
		internal static class WearNTearApplyDamagePatch
		{
			private static bool Prefix(WearNTear __instance)
			{
				if (!IsProtectedPiece(__instance))
				{
					return true;
				}
				if (StructuralDamageContext.UpdateWearDepth > 0)
				{
					return true;
				}
				if (DebugLog.Value)
				{
					Log.LogInfo((object)("Blocked damage to " + ((Object)((Component)__instance).gameObject).name));
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "HaveSupport")]
		internal static class WearNTearHaveSupportPatch
		{
			private static void Postfix(WearNTear __instance, ref bool __result)
			{
				if (IsProtectedPiece(__instance) && DisableStructuralIntegrity.Value)
				{
					__result = true;
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "GetSupport")]
		internal static class WearNTearGetSupportPatch
		{
			private static void Postfix(WearNTear __instance, ref float __result)
			{
				if (!IsProtectedPiece(__instance))
				{
					return;
				}
				if (DisableStructuralIntegrity.Value)
				{
					__result = 1f;
					return;
				}
				float value = StructuralSupportMultiplier.Value;
				if (value != 1f)
				{
					__result *= value;
				}
			}
		}

		[HarmonyPatch]
		internal static class ToolUseActionPatch
		{
			private sealed class DurabilityValues
			{
				internal float UseDurabilityDrain;

				internal float DurabilityDrain;
			}

			private static readonly Dictionary<SharedData, DurabilityValues> OriginalDurability = new Dictionary<SharedData, DurabilityValues>();

			private static readonly string[] ToolPrefabNames = new string[3] { "Hammer", "Hoe", "Cultivator" };

			[ThreadStatic]
			private static bool _toolUseInProgress;

			internal static bool IsToolUseInProgress => _toolUseInProgress;

			private static IEnumerable<MethodBase> TargetMethods()
			{
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Humanoid)))
				{
					if (declaredMethod.Name != "UseItem")
					{
						continue;
					}
					bool flag = false;
					ParameterInfo[] parameters = declaredMethod.GetParameters();
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].ParameterType == typeof(ItemData))
						{
							flag = true;
							break;
						}
					}
					if (flag)
					{
						yield return declaredMethod;
					}
				}
			}

			private static void Prefix(Humanoid __instance, object[] __args, out bool __state)
			{
				__state = false;
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !UnlimitedToolStamina.Value)
				{
					return;
				}
				ItemData val = null;
				foreach (object obj in __args)
				{
					val = (ItemData)((obj is ItemData) ? obj : null);
					if (val != null)
					{
						break;
					}
				}
				if (IsTargetTool(val))
				{
					_toolUseInProgress = true;
					__state = true;
				}
			}

			private static void Postfix(bool __state)
			{
				if (__state)
				{
					_toolUseInProgress = false;
				}
			}

			private static Exception Finalizer(Exception __exception, bool __state)
			{
				if (__state)
				{
					_toolUseInProgress = false;
				}
				return __exception;
			}

			internal static void ApplyToObjectDb()
			{
				if ((Object)(object)ObjectDB.instance == (Object)null)
				{
					return;
				}
				string[] toolPrefabNames = ToolPrefabNames;
				foreach (string text in toolPrefabNames)
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
					if ((Object)(object)itemPrefab == (Object)null)
					{
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogWarning((object)("Could not find tool prefab '" + text + "' in ObjectDB while applying durability settings."));
						}
						continue;
					}
					ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
					{
						ManualLogSource log2 = Log;
						if (log2 != null)
						{
							log2.LogWarning((object)("Tool prefab '" + text + "' has no usable ItemDrop data; durability settings were not applied to that prefab."));
						}
						continue;
					}
					SharedData shared = component.m_itemData.m_shared;
					if (!OriginalDurability.ContainsKey(shared))
					{
						OriginalDurability[shared] = new DurabilityValues
						{
							UseDurabilityDrain = shared.m_useDurabilityDrain,
							DurabilityDrain = shared.m_durabilityDrain
						};
					}
					DurabilityValues durabilityValues = OriginalDurability[shared];
					if (UnlimitedToolDurability.Value)
					{
						shared.m_useDurabilityDrain = 0f;
						shared.m_durabilityDrain = 0f;
					}
					else
					{
						shared.m_useDurabilityDrain = durabilityValues.UseDurabilityDrain;
						shared.m_durabilityDrain = durabilityValues.DurabilityDrain;
					}
					ManualLogSource log3 = Log;
					if (log3 != null)
					{
						log3.LogInfo((object)$"Tool durability settings applied to {text} (unlimited={UnlimitedToolDurability.Value}).");
					}
				}
			}

			internal static bool IsTargetTool(ItemData item)
			{
				if (item == null || item.m_shared == null)
				{
					return false;
				}
				string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty);
				if (string.IsNullOrEmpty(text))
				{
					text = item.m_shared.m_name;
				}
				text = text ?? string.Empty;
				if (text.IndexOf("Hammer", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Hoe", StringComparison.OrdinalIgnoreCase) < 0)
				{
					return text.IndexOf("Cultivator", StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "DrainEquipedItemDurability")]
		internal static class ToolEquippedDurabilityDrainPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(ItemData __0)
			{
				if (UnlimitedToolDurability.Value)
				{
					return !ToolUseActionPatch.IsTargetTool(__0);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Player), "GetPlaceDurability")]
		internal static class ToolPlacementDurabilityPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(ItemData __0, ref float __result)
			{
				if (!UnlimitedToolDurability.Value || !ToolUseActionPatch.IsTargetTool(__0))
				{
					return true;
				}
				__result = 0f;
				return false;
			}
		}

		[HarmonyPatch(typeof(Player), "UseStamina")]
		internal static class ToolStaminaPatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance, ref float v)
			{
				if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && UnlimitedToolStamina.Value && ToolUseActionPatch.IsToolUseInProgress)
				{
					v = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Awake")]
		internal static class ToolDurabilityPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				ToolUseActionPatch.ApplyToObjectDb();
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "SetupObjectDB")]
		internal static class ToolDurabilityObjectDbSetupPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				ToolUseActionPatch.ApplyToObjectDb();
			}
		}

		[HarmonyPatch(typeof(Piece), "Awake")]
		internal static class VehicleDismantlingAwakePatch
		{
			internal static void ApplyToLoadedPieces()
			{
				Piece[] array = Object.FindObjectsOfType<Piece>();
				for (int i = 0; i < array.Length; i++)
				{
					VehicleDismantlingSupport.ApplyRemovableFlag(array[i]);
				}
			}

			[HarmonyPostfix]
			private static void Postfix(Piece __instance)
			{
				VehicleDismantlingSupport.ApplyRemovableFlag(__instance);
			}
		}

		[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
		internal static class VehicleDismantlingCanBeRemovedPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(Piece __instance, ref bool __result)
			{
				if ((Object)(object)__instance == (Object)null || Enabled == null || !Enabled.Value || EnableBoatAndCartDismantling == null || !EnableBoatAndCartDismantling.Value)
				{
					return true;
				}
				if (!VehicleDismantlingSupport.TryGetVehicle(__instance, out var ship, out var cart))
				{
					return true;
				}
				if ((Object)(object)ship != (Object)null)
				{
					if (VehicleDismantlingSupport.IsShipOccupied(ship) || !VehicleDismantlingSupport.StorageIsEmpty(((Component)__instance).GetComponentInChildren<Container>()))
					{
						__result = false;
						return false;
					}
					__result = true;
					return false;
				}
				if ((Object)(object)cart != (Object)null)
				{
					if (cart.InUse() || !VehicleDismantlingSupport.StorageIsEmpty(((Component)__instance).GetComponentInChildren<Container>()))
					{
						__result = false;
						return false;
					}
					__result = true;
					return false;
				}
				return true;
			}
		}

		internal static class VehicleDismantlingSupport
		{
			private static readonly Dictionary<int, bool> OriginalRemovableFlags = new Dictionary<int, bool>();

			private static readonly MethodInfo IsPlayerInBoatMethod = AccessTools.Method(typeof(Ship), "IsPlayerInBoat", new Type[1] { typeof(Player) }, (Type[])null);

			private static readonly MethodInfo GetControlledShipMethod = AccessTools.Method(typeof(Player), "GetControlledShip", Type.EmptyTypes, (Type[])null);

			private static readonly MethodInfo GetStandingOnShipMethod = AccessTools.Method(typeof(Character), "GetStandingOnShip", Type.EmptyTypes, (Type[])null);

			internal static bool IsShipOccupied(Ship ship)
			{
				if ((Object)(object)ship == (Object)null)
				{
					return false;
				}
				Player[] array = Object.FindObjectsOfType<Player>();
				bool flag = IsPlayerInBoatMethod != null || GetControlledShipMethod != null || GetStandingOnShipMethod != null;
				Player[] array2 = array;
				foreach (Player val in array2)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (IsPlayerInBoatMethod != null)
					{
						try
						{
							object obj = IsPlayerInBoatMethod.Invoke(ship, new object[1] { val });
							if (obj is bool && (bool)obj)
							{
								return true;
							}
						}
						catch (Exception ex)
						{
							ManualLogSource log = Log;
							if (log != null)
							{
								log.LogDebug((object)("Could not query Ship.IsPlayerInBoat: " + ex.Message));
							}
						}
					}
					try
					{
						if (GetControlledShipMethod != null)
						{
							object? obj2 = GetControlledShipMethod.Invoke(val, null);
							Ship val2 = (Ship)((obj2 is Ship) ? obj2 : null);
							if (val2 != null && (Object)(object)val2 == (Object)(object)ship)
							{
								return true;
							}
						}
					}
					catch (Exception ex2)
					{
						ManualLogSource log2 = Log;
						if (log2 != null)
						{
							log2.LogDebug((object)("Could not query Player.GetControlledShip: " + ex2.Message));
						}
					}
					try
					{
						if (GetStandingOnShipMethod != null)
						{
							object? obj3 = GetStandingOnShipMethod.Invoke(val, null);
							Ship val3 = (Ship)((obj3 is Ship) ? obj3 : null);
							if (val3 != null && (Object)(object)val3 == (Object)(object)ship)
							{
								return true;
							}
						}
					}
					catch (Exception ex3)
					{
						ManualLogSource log3 = Log;
						if (log3 != null)
						{
							log3.LogDebug((object)("Could not query Character.GetStandingOnShip: " + ex3.Message));
						}
					}
				}
				return !flag;
			}

			internal static void ApplyRemovableFlag(Piece piece)
			{
				if (TryGetVehicle(piece, out var _, out var _))
				{
					int instanceID = ((Object)piece).GetInstanceID();
					if (!OriginalRemovableFlags.ContainsKey(instanceID))
					{
						OriginalRemovableFlags[instanceID] = piece.m_canBeRemoved;
					}
					bool flag = Enabled != null && Enabled.Value && EnableBoatAndCartDismantling != null && EnableBoatAndCartDismantling.Value;
					piece.m_canBeRemoved = flag || OriginalRemovableFlags[instanceID];
				}
			}

			internal static bool TryGetVehicle(Piece piece, out Ship ship, out Vagon cart)
			{
				ship = null;
				cart = null;
				if ((Object)(object)piece == (Object)null)
				{
					return false;
				}
				ship = ((Component)piece).GetComponentInChildren<Ship>();
				cart = ((Component)piece).GetComponentInChildren<Vagon>();
				if ((Object)(object)ship != (Object)null && IsSpecialMovingBaseShip(ship))
				{
					ship = null;
				}
				if (!((Object)(object)ship != (Object)null))
				{
					return (Object)(object)cart != (Object)null;
				}
				return true;
			}

			internal static bool StorageIsEmpty(Container container)
			{
				if ((Object)(object)container == (Object)null)
				{
					return true;
				}
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					return inventory.NrOfItems() == 0;
				}
				return true;
			}

			private static bool IsSpecialMovingBaseShip(Ship ship)
			{
				if ((Object)(object)ship == (Object)null)
				{
					return false;
				}
				if ((((Object)(object)((Component)ship).gameObject != (Object)null) ? ((Object)((Component)ship).gameObject).name : string.Empty).StartsWith("MBRaft", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				Component[] components = ((Component)ship).GetComponents<Component>();
				foreach (Component val in components)
				{
					if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "MoveableBaseShipComponent")
					{
						return true;
					}
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(CraftingStation), "Start")]
		internal static class CraftingStationRangePatch
		{
			private static readonly FieldInfo NameField = AccessTools.Field(typeof(CraftingStation), "m_name");

			private static readonly FieldInfo RangeField = AccessTools.Field(typeof(CraftingStation), "m_rangeBuild");

			private static readonly FieldInfo MarkerField = AccessTools.Field(typeof(CraftingStation), "m_areaMarker");

			private static bool _warnedMissingField;

			private static void Postfix(CraftingStation __instance)
			{
				Apply(__instance);
			}

			internal static void ApplyToLoadedStations()
			{
				CraftingStation[] array = Object.FindObjectsOfType<CraftingStation>();
				for (int i = 0; i < array.Length; i++)
				{
					Apply(array[i]);
				}
			}

			private static void Apply(CraftingStation station)
			{
				if ((Object)(object)station == (Object)null || !Enabled.Value || RangeField == null || NameField == null)
				{
					return;
				}
				string arg = (NameField.GetValue(station) as string) ?? string.Empty;
				float value = CraftingStationBuildRange.Value;
				RangeField.SetValue(station, value);
				object? obj = MarkerField?.GetValue(station);
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					CircleProjector component = val.GetComponent<CircleProjector>();
					if ((Object)(object)component != (Object)null)
					{
						component.m_radius = value;
						component.m_nrOfSegments = (int)Math.Ceiling(Math.Max(20f, 4f * value));
					}
				}
				if (DebugLog.Value)
				{
					Log.LogInfo((object)$"Set crafting station '{arg}' build range to {value:0.#} metres.");
				}
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		internal static class WarmthRadiusPatch
		{
			private sealed class ColliderOriginal
			{
				internal float SphereRadius;

				internal float CapsuleRadius;

				internal float CapsuleHeight;

				internal Vector3 BoxSize;

				internal bool IsSphere;

				internal bool IsCapsule;

				internal bool IsBox;
			}

			private static readonly Dictionary<int, ColliderOriginal> Originals = new Dictionary<int, ColliderOriginal>();

			private static readonly FieldInfo PrefabsField = AccessTools.Field(typeof(ZNetScene), "m_prefabs");

			private static float _lastAppliedBonus;

			private static bool _hasApplied;

			private static bool _warnedMissingPrefabList;

			[HarmonyPostfix]
			private static void Postfix(ZNetScene __instance)
			{
				Apply(__instance);
			}

			internal static void ApplyToCurrentScene()
			{
				ZNetScene instance = ZNetScene.instance;
				if ((Object)(object)instance != (Object)null)
				{
					Apply(instance);
				}
			}

			private static float GetConfiguredBonus()
			{
				if (ComfortRadius == null)
				{
					return 0f;
				}
				return Mathf.Max(0f, Mathf.Clamp(ComfortRadius.Value, 10f, 30f) - 10f);
			}

			private static void Apply(ZNetScene scene)
			{
				float configuredBonus = GetConfiguredBonus();
				if ((Object)(object)scene != (Object)null && PrefabsField != null)
				{
					if (PrefabsField.GetValue(scene) is List<GameObject> list)
					{
						foreach (GameObject item in list)
						{
							ApplyToObject(item, configuredBonus, liveInstance: false);
						}
					}
					else if (!_warnedMissingPrefabList)
					{
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogWarning((object)"Could not read ZNetScene prefab list; warmth changes may not apply to new heat-source instances.");
						}
						_warnedMissingPrefabList = true;
					}
				}
				EffectArea[] array = Object.FindObjectsOfType<EffectArea>();
				foreach (EffectArea val in array)
				{
					if (!((Object)(object)val == (Object)null) && HasHeat(val))
					{
						ApplyToArea(val, configuredBonus, _hasApplied ? _lastAppliedBonus : 0f, inferPreviousPrefabBonus: true);
					}
				}
				_lastAppliedBonus = configuredBonus;
				_hasApplied = true;
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"Warmth areas updated: added {configuredBonus:0.#}m beyond each heat source's vanilla radius (comfort setting {ComfortRadius?.Value ?? 10f:0.#}m).");
				}
			}

			private static void ApplyToObject(GameObject root, float newBonus, bool liveInstance)
			{
				if ((Object)(object)root == (Object)null)
				{
					return;
				}
				EffectArea[] componentsInChildren = root.GetComponentsInChildren<EffectArea>(true);
				foreach (EffectArea val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && HasHeat(val))
					{
						ApplyToArea(val, newBonus, (liveInstance && _hasApplied) ? _lastAppliedBonus : 0f, liveInstance);
					}
				}
			}

			private static bool HasHeat(EffectArea area)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Invalid comparison between Unknown and I4
				if ((Object)(object)area != (Object)null)
				{
					return (area.m_type & 2) > 0;
				}
				return false;
			}

			private static void ApplyToArea(EffectArea area, float newBonus, float previousBonus, bool inferPreviousPrefabBonus)
			{
				Collider[] components = ((Component)area).GetComponents<Collider>();
				foreach (Collider val in components)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					int instanceID = ((Object)val).GetInstanceID();
					if (!Originals.TryGetValue(instanceID, out var value))
					{
						value = CaptureOriginal(val, inferPreviousPrefabBonus ? previousBonus : 0f);
						if (value == null)
						{
							continue;
						}
						Originals[instanceID] = value;
					}
					SetColliderDimensions(val, value, newBonus);
				}
			}

			private static ColliderOriginal CaptureOriginal(Collider collider, float subtractWorldBonus)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: 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_0139: Unknown result type (might be due to invalid IL or missing references)
				//IL_0147: Unknown result type (might be due to invalid IL or missing references)
				//IL_014e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0153: Unknown result type (might be due to invalid IL or missing references)
				//IL_0158: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				//IL_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_00c7: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = AbsScale(((Component)collider).transform.lossyScale);
				ColliderOriginal colliderOriginal = new ColliderOriginal();
				SphereCollider val2 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
				if (val2 != null)
				{
					colliderOriginal.IsSphere = true;
					colliderOriginal.SphereRadius = val2.radius - subtractWorldBonus / Mathf.Max(val.x, Mathf.Max(val.y, val.z));
				}
				else
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
					if (val3 != null)
					{
						colliderOriginal.IsCapsule = true;
						float num = ((val3.direction == 0) ? Mathf.Max(val.y, val.z) : ((val3.direction == 1) ? Mathf.Max(val.x, val.z) : Mathf.Max(val.x, val.y)));
						float num2 = ((val3.direction == 0) ? val.x : ((val3.direction == 1) ? val.y : val.z));
						colliderOriginal.CapsuleRadius = val3.radius - subtractWorldBonus / num;
						colliderOriginal.CapsuleHeight = val3.height - 2f * subtractWorldBonus / num2;
					}
					else
					{
						BoxCollider val4 = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
						if (val4 == null)
						{
							return null;
						}
						colliderOriginal.IsBox = true;
						colliderOriginal.BoxSize = val4.size - new Vector3(2f * subtractWorldBonus / val.x, 2f * subtractWorldBonus / val.y, 2f * subtractWorldBonus / val.z);
					}
				}
				return colliderOriginal;
			}

			private static void SetColliderDimensions(Collider collider, ColliderOriginal original, float bonus)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: 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_0149: Unknown result type (might be due to invalid IL or missing references)
				//IL_0157: Unknown result type (might be due to invalid IL or missing references)
				//IL_0165: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0171: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_00db: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = AbsScale(((Component)collider).transform.lossyScale);
				if (original.IsSphere)
				{
					SphereCollider val2 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
					if (val2 != null)
					{
						val2.radius = Mathf.Max(0.01f, original.SphereRadius + bonus / Mathf.Max(val.x, Mathf.Max(val.y, val.z)));
						return;
					}
				}
				if (original.IsCapsule)
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
					if (val3 != null)
					{
						float num = ((val3.direction == 0) ? Mathf.Max(val.y, val.z) : ((val3.direction == 1) ? Mathf.Max(val.x, val.z) : Mathf.Max(val.x, val.y)));
						float num2 = ((val3.direction == 0) ? val.x : ((val3.direction == 1) ? val.y : val.z));
						val3.radius = Mathf.Max(0.01f, original.CapsuleRadius + bonus / num);
						val3.height = Mathf.Max(val3.radius * 2f, original.CapsuleHeight + 2f * bonus / num2);
						return;
					}
				}
				if (original.IsBox)
				{
					BoxCollider val4 = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
					if (val4 != null)
					{
						val4.size = original.BoxSize + new Vector3(2f * bonus / val.x, 2f * bonus / val.y, 2f * bonus / val.z);
					}
				}
			}

			private static Vector3 AbsScale(Vector3 scale)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				return new Vector3(Mathf.Max(0.001f, Mathf.Abs(scale.x)), Mathf.Max(0.001f, Mathf.Abs(scale.y)), Mathf.Max(0.001f, Mathf.Abs(scale.z)));
			}
		}

		[HarmonyPatch(typeof(SE_Rested), "GetNearbyComfortPieces")]
		internal static class ComfortRadiusPatch
		{
			private static readonly MethodInfo RadiusGetter = AccessTools.Method(typeof(ComfortRadiusPatch), "GetConfiguredRadius", (Type[])null, (Type[])null);

			private static float GetConfiguredRadius()
			{
				if (ComfortRadius == null)
				{
					return 10f;
				}
				return Mathf.Clamp(ComfortRadius.Value, 10f, 30f);
			}

			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				bool replaced = false;
				foreach (CodeInstruction instruction in instructions)
				{
					if (!replaced && instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num && Mathf.Abs(num - 10f) < 0.0001f)
					{
						yield return new CodeInstruction(OpCodes.Call, (object)RadiusGetter);
						replaced = true;
					}
					else
					{
						yield return instruction;
					}
				}
				if (replaced)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)"Comfort radius search patched; configured radius is read dynamically.");
					}
				}
				else
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"Could not locate the vanilla 10m comfort radius constant in SE_Rested.GetNearbyComfortPieces. Comfort radius may remain vanilla.");
					}
				}
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "Awake")]
		internal static class DroppedItemBuoyancyPatch
		{
			private static Floating _woodTemplate;

			private static bool _warnedMissingTemplate;

			private static bool _warnedApplyFailure;

			private static bool _loggedApplied;

			private static readonly Type FishComponentType = AccessTools.TypeByName("Fish");

			private static readonly FieldInfo CachedFloatingField = AccessTools.Field(typeof(ItemDrop), "m_floating");

			private static readonly string[] TemplateFields = new string[5] { "m_waterLevelOffset", "m_forceDistance", "m_force", "m_balanceForceFraction", "m_damping" };

			[HarmonyPrefix]
			private static void Prefix(ItemDrop __instance)
			{
				if ((Object)(object)__instance == (Object)null || Enabled == null || !Enabled.Value || FloatDroppedItemsInWater == null || !FloatDroppedItemsInWater.Value)
				{
					return;
				}
				try
				{
					if ((FishComponentType != null && (Object)(object)((Component)__instance).GetComponentInParent(FishComponentType) != (Object)null) || (Object)(object)GetCachedFloating(__instance) != (Object)null)
					{
						return;
					}
					Floating component = ((Component)__instance).GetComponent<Floating>();
					if ((Object)(object)component != (Object)null)
					{
						SetCachedFloating(__instance, component);
						return;
					}
					Floating woodTemplate = GetWoodTemplate();
					if ((Object)(object)woodTemplate == (Object)null)
					{
						if (!_warnedMissingTemplate)
						{
							ManualLogSource log = Log;
							if (log != null)
							{
								log.LogWarning((object)"Could not find the vanilla Wood Floating component; dropped-item buoyancy will retry on later item initializations.");
							}
							_warnedMissingTemplate = true;
						}
						return;
					}
					Floating val = ((Component)__instance).gameObject.AddComponent<Floating>();
					CopyTemplateSettings(woodTemplate, val);
					SetCachedFloating(__instance, val);
					if (!_loggedApplied)
					{
						ManualLogSource log2 = Log;
						if (log2 != null)
						{
							log2.LogInfo((object)"Dropped-item buoyancy enabled; using the vanilla Wood Floating settings. Existing floating items and living fish are left unchanged.");
						}
						_loggedApplied = true;
					}
				}
				catch (Exception ex)
				{
					if (!_warnedApplyFailure)
					{
						ManualLogSource log3 = Log;
						if (log3 != null)
						{
							log3.LogWarning((object)("Could not apply dropped-item buoyancy: " + ex.GetBaseException().Message));
						}
						_warnedApplyFailure = true;
					}
				}
			}

			private static Floating GetWoodTemplate()
			{
				if ((Object)(object)_woodTemplate != (Object)null)
				{
					return _woodTemplate;
				}
				GameObject val = null;
				ObjectDB instance = ObjectDB.instance;
				if ((Object)(object)instance != (Object)null)
				{
					val = instance.GetItemPrefab("Wood");
				}
				if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
				{
					val = ZNetScene.instance.GetPrefab("Wood");
				}
				if ((Object)(object)val != (Object)null)
				{
					_woodTemplate = val.GetComponent<Floating>();
				}
				return _woodTemplate;
			}

			private static Floating GetCachedFloating(ItemDrop itemDrop)
			{
				if (CachedFloatingField != null)
				{
					object? value = CachedFloatingField.GetValue(itemDrop);
					return (Floating)((value is Floating) ? value : null);
				}
				return ((Component)itemDrop).GetComponent<Floating>();
			}

			private static void SetCachedFloating(ItemDrop itemDrop, Floating floating)
			{
				if (CachedFloatingField != null)
				{
					CachedFloatingField.SetValue(itemDrop, floating);
				}
			}

			private static void CopyTemplateSettings(Floating template, Floating target)
			{
				string[] templateFields = TemplateFields;
				foreach (string text in templateFields)
				{
					FieldInfo fieldInfo = AccessTools.Field(typeof(Floating), text);
					if (!(fieldInfo == null) && !fieldInfo.IsStatic)
					{
						fieldInfo.SetValue(target, fieldInfo.GetValue(template));
					}
				}
			}
		}

		public const string ModGuid = "ValheimStructureImmunity";

		public const string ModName = "Valheim Structure Immunity";

		public const string ModVersion = "0.3.9";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ProtectPlayerPlacedPieces;

		internal static ConfigEntry<bool> DisableStructuralIntegrity;

		internal static ConfigEntry<float> StructuralSupportMultiplier;

		internal static ConfigEntry<bool> DebugLog;

		internal static ConfigEntry<float> CraftingStationBuildRange;

		internal static ConfigEntry<bool> EnableBoatAndCartDismantling;

		internal static ConfigEntry<bool> FloatDroppedItemsInWater;

		internal static ConfigEntry<float> ComfortRadius;

		internal static ConfigEntry<bool> UnlimitedToolDurability;

		internal static ConfigEntry<bool> UnlimitedToolStamina;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private static Plugin Instance;

		private void Awake()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for this mod. Damage protection remains enabled by default.");
			ProtectPlayerPlacedPieces = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ProtectPlayerPlacedPieces", true, "Only apply protection and structural settings to pieces placed by a player.");
			DisableStructuralIntegrity = ((BaseUnityPlugin)this).Config.Bind<bool>("Structural Integrity", "DisableStructuralIntegrity", false, "Completely disables structural integrity. Building pieces can be supported in free air.");
			StructuralSupportMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Structural Integrity", "StructuralSupportMultiplier", 1f, new ConfigDescription("Multiplies the support value used by structural integrity. 1 = vanilla, 2 = double support, 5 = five times support. Ignored when DisableStructuralIntegrity is true.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 100f), Array.Empty<object>()));
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Log blocked structure damage and structural-integrity changes.");
			CraftingStationBuildRange = ((BaseUnityPlugin)this).Config.Bind<float>("Crafting Station Range v0.3.7", "CraftingStationRangeMetres", 50f, new ConfigDescription("Shared build radius for all crafting stations, including Workbench, Forge, Stonecutter, Artisan Table and other stations. Default 50 metres; configurable from 1 to 100 metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			CraftingStationBuildRange.SettingChanged += OnStationRangeSettingChanged;
			EnableBoatAndCartDismantling = ((BaseUnityPlugin)this).Config.Bind<bool>("Vehicle Dismantling", "EnableBoatAndCartDismantling", true, "Allows the Hammer's Remove mode to dismantle ships and carts. Removal is blocked while a player is aboard/pulling the cart or while vehicle storage contains items. Special moving-base ships are left to their own mods.");
			EnableBoatAndCartDismantling.SettingChanged += OnVehicleDismantlingSettingChanged;
			Enabled.SettingChanged += OnVehicleDismantlingSettingChanged;
			FloatDroppedItemsInWater = ((BaseUnityPlugin)this).Config.Bind<bool>("Item Buoyancy", "FloatDroppedItemsInWater", true, "Makes dropped items float on water and tar using Valheim's native Floating behavior, matching wood. Items with existing buoyancy and living fish keep their existing behavior. Applies when item drops initialize.");
			ComfortRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Comfort and Warmth v0.3.6", "ComfortAndWarmthRadiusMetres", 20f, new ConfigDescription("Comfort items are detected within this radius (vanilla 10m). Heat areas from fires, hearths and other warmth sources are expanded by (this value - 10m) beyond their vanilla radius. Default 20m = +10m warmth; maximum setting 30m = +20m warmth.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 30f), Array.Empty<object>()));
			ComfortRadius.SettingChanged += OnComfortRadiusSettingChanged;
			UnlimitedToolDurability = ((BaseUnityPlugin)this).Config.Bind<bool>("Tool Quality of Life", "UnlimitedToolDurability", true, "When enabled, Hammer, Hoe, and Cultivator do not lose durability.");
			UnlimitedToolStamina = ((BaseUnityPlugin)this).Config.Bind<bool>("Tool Quality of Life", "UnlimitedToolStamina", true, "When enabled, using Hammer, Hoe, and Cultivator costs no stamina. This is scoped to tool-use actions, not running or dodging.");
			UnlimitedToolDurability.SettingChanged += OnToolSettingsChanged;
			UnlimitedToolStamina.SettingChanged += OnToolSettingsChanged;
			_harmony = new Harmony("ValheimStructureImmunity");
			ApplyPatchesIndividually();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim Structure Immunity 0.3.9 loaded.");
		}

		private void ApplyPatchesIndividually()
		{
			Type[] types = Assembly.GetExecutingAssembly().GetTypes();
			foreach (Type type in types)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Registered patch class: " + type.FullName));
					}
					catch (Exception ex)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)("Patch class failed: " + type.FullName + ": " + ex.GetBaseException().Message));
					}
				}
			}
		}

		private void OnStationRangeSettingChanged(object sender, EventArgs args)
		{
			CraftingStationRangePatch.ApplyToLoadedStations();
		}

		private void OnToolSettingsChanged(object sender, EventArgs args)
		{
			ToolUseActionPatch.ApplyToObjectDb();
		}

		private void OnVehicleDismantlingSettingChanged(object sender, EventArgs args)
		{
			VehicleDismantlingAwakePatch.ApplyToLoadedPieces();
		}

		private void OnComfortRadiusSettingChanged(object sender, EventArgs args)
		{
			WarmthRadiusPatch.ApplyToCurrentScene();
		}

		private void OnDestroy()
		{
			if (CraftingStationBuildRange != null)
			{
				CraftingStationBuildRange.SettingChanged -= OnStationRangeSettingChanged;
			}
			if (EnableBoatAndCartDismantling != null)
			{
				EnableBoatAndCartDismantling.SettingChanged -= OnVehicleDismantlingSettingChanged;
			}
			if (Enabled != null)
			{
				Enabled.SettingChanged -= OnVehicleDismantlingSettingChanged;
			}
			UnlimitedToolDurability.SettingChanged -= OnToolSettingsChanged;
			UnlimitedToolStamina.SettingChanged -= OnToolSettingsChanged;
			ComfortRadius.SettingChanged -= OnComfortRadiusSettingChanged;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static bool IsProtectedPiece(WearNTear wearNTear)
		{
			if (!Enabled.Value || (Object)(object)wearNTear == (Object)null)
			{
				return false;
			}
			Piece component = ((Component)wearNTear).GetComponent<Piece>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			if (ProtectPlayerPlacedPieces.Value && !component.IsPlacedByPlayer())
			{
				return false;
			}
			return true;
		}
	}
}