Decompiled source of FreeCamBuildAndTerrainTweaks v1.0.1

FreeCamBuildAndTerrainTweaks.dll

Decompiled 6 days 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 Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("FreeCamBuildAndTerrainTweaks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FreeCamBuildAndTerrainTweaks")]
[assembly: AssemblyTitle("FreeCamBuildAndTerrainTweaks")]
[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 FreeCamBuildAndTerrainTweaks
{
	internal static class Digging
	{
		[HarmonyPatch]
		private static class TerrainComp_Limits_Patch
		{
			private static readonly string[] Names = new string[4] { "ApplyToHeightmap", "LevelTerrain", "RaiseTerrain", "SmoothTerrain" };

			private static IEnumerable<MethodBase> TargetMethods()
			{
				string[] names = Names;
				foreach (string text in names)
				{
					MethodBase methodBase = AccessTools.Method(typeof(TerrainComp), text, (Type[])null, (Type[])null);
					if (methodBase != null)
					{
						yield return methodBase;
					}
				}
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
			{
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				MethodInfo operand = AccessTools.Method(typeof(Digging), "Depth", (Type[])null, (Type[])null);
				MethodInfo operand2 = AccessTools.Method(typeof(Digging), "NegativeDepth", (Type[])null, (Type[])null);
				MethodInfo operand3 = AccessTools.Method(typeof(Digging), "Height", (Type[])null, (Type[])null);
				int num = 0;
				for (int i = 0; i < list.Count; i++)
				{
					if (list[i].opcode != OpCodes.Ldc_R4 || !(list[i].operand is float num2))
					{
						continue;
					}
					if (num2 == -8f)
					{
						list[i].opcode = OpCodes.Call;
						list[i].operand = operand2;
						num++;
					}
					else if (num2 == 8f && i + 1 < list.Count)
					{
						OpCode opcode = list[i + 1].opcode;
						if (opcode == OpCodes.Sub)
						{
							list[i].opcode = OpCodes.Call;
							list[i].operand = operand;
							num++;
						}
						else if (opcode == OpCodes.Add || opcode == OpCodes.Call)
						{
							list[i].opcode = OpCodes.Call;
							list[i].operand = operand3;
							num++;
						}
					}
				}
				Plugin.Log.LogInfo((object)$"Dig depth: changed {num} limit(s) in TerrainComp.{original.Name}");
				return list;
			}
		}

		private const float VanillaDepth = 8f;

		internal static float Depth()
		{
			if (!Sync.Get<bool>(Plugin.DeeperDigging))
			{
				return 8f;
			}
			return Mathf.Max(8f, (float)Sync.Get<int>(Plugin.MaxDigDepth));
		}

		internal static float NegativeDepth()
		{
			return 0f - Depth();
		}

		internal static float Height()
		{
			if (!Sync.Get<bool>(Plugin.HigherRaising))
			{
				return 8f;
			}
			return Mathf.Max(8f, (float)Sync.Get<int>(Plugin.MaxRaiseHeight));
		}
	}
	internal static class ExtraRules
	{
		[HarmonyPatch(typeof(Player), "CheckPlacementGhostVSPlayers")]
		private static class Player_CheckPlacementGhostVSPlayers_Patch
		{
			private static void Postfix(ref bool __result)
			{
				if (__result && Sync.Get<bool>(Plugin.IgnorePlayersInTheWay))
				{
					__result = false;
				}
			}
		}
	}
	internal static class StationUpgrades
	{
		[HarmonyPatch(typeof(StationExtension), "FindClosestStationInRange")]
		private static class FindClosestStationInRange_Patch
		{
			private static void Prefix(StationExtension __instance, out float __state)
			{
				__state = __instance.m_maxStationDistance;
				__instance.m_maxStationDistance = __state * Scale;
			}

			private static void Finalizer(StationExtension __instance, float __state)
			{
				__instance.m_maxStationDistance = __state;
			}
		}

		[HarmonyPatch(typeof(StationExtension), "FindStationsInRange")]
		private static class FindStationsInRange_Patch
		{
			private static void Prefix(StationExtension __instance, out float __state)
			{
				__state = __instance.m_maxStationDistance;
				__instance.m_maxStationDistance = __state * Scale;
			}

			private static void Finalizer(StationExtension __instance, float __state)
			{
				__instance.m_maxStationDistance = __state;
			}
		}

		[HarmonyPatch(typeof(StationExtension), "FindExtensions")]
		private static class FindExtensions_Patch
		{
			private static void Prefix(out float[] __state)
			{
				List<StationExtension> list = AllExtensions();
				__state = new float[list.Count];
				float scale = Scale;
				for (int i = 0; i < list.Count; i++)
				{
					__state[i] = list[i].m_maxStationDistance;
					StationExtension obj = list[i];
					obj.m_maxStationDistance *= scale;
				}
			}

			private static void Finalizer(float[] __state)
			{
				if (__state != null)
				{
					List<StationExtension> list = AllExtensions();
					for (int i = 0; i < list.Count && i < __state.Length; i++)
					{
						list[i].m_maxStationDistance = __state[i];
					}
				}
			}
		}

		[HarmonyPatch(typeof(StationExtension), "OtherExtensionInRange")]
		private static class OtherExtensionInRange_Patch
		{
			private static void Postfix(ref bool __result)
			{
				if (__result && Sync.Get<bool>(Plugin.UpgradesIgnoreOverlap))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "TestGhostClipping")]
		private static class TestGhostClipping_Patch
		{
			private static void Postfix(GameObject ghost, ref bool __result)
			{
				if (__result && Sync.Get<bool>(Plugin.UpgradesIgnoreOverlap) && (Object)(object)ghost != (Object)null && (Object)(object)ghost.GetComponent<StationExtension>() != (Object)null)
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(StationExtension), "Awake")]
		private static class Awake_Patch
		{
			private static void Postfix(StationExtension __instance)
			{
				if (Sync.Get<bool>(Plugin.UpgradesIgnoreOverlap))
				{
					Piece component = ((Component)__instance).GetComponent<Piece>();
					if ((Object)(object)component != (Object)null)
					{
						component.m_blockRadius = 0f;
					}
				}
			}
		}

		private const float VanillaRange = 5f;

		private static float Scale => (float)Sync.Get<int>(Plugin.ExtensionRange) / 5f;

		private static List<StationExtension> AllExtensions()
		{
			return Traverse.Create(typeof(StationExtension)).Field("m_allExtensions").GetValue<List<StationExtension>>();
		}
	}
	internal static class FreeCam
	{
		[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
		private static class GameCamera_UpdateCamera_Patch
		{
			private static bool Prefix(float dt)
			{
				if (!Active || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return true;
				}
				MoveCamera(dt);
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerController), "TakeInput")]
		private static class PlayerController_TakeInput_Patch
		{
			private static void Postfix(ref bool __result)
			{
				if (Active)
				{
					__result = false;
				}
			}
		}

		private static Vector3 _position;

		private static float _yaw;

		private static float _pitch;

		private static float _savedPlaceDistance;

		private const float FastMultiplier = 4f;

		private static readonly Func<Player, bool> TakeInputFn = MakeTakeInput();

		internal static bool Active { get; private set; }

		private static Func<Player, bool> MakeTakeInput()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null);
			if (!(methodInfo == null))
			{
				return (Func<Player, bool>)Delegate.CreateDelegate(typeof(Func<Player, bool>), methodInfo);
			}
			return (Player p) => true;
		}

		private static bool CanInput(Player player)
		{
			if (TakeInputFn(player))
			{
				return !Hud.IsPieceSelectionVisible();
			}
			return false;
		}

		internal static void Tick()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Active && ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !((Character)localPlayer).InPlaceMode() || (Object)(object)GameCamera.instance == (Object)null))
			{
				Leave(localPlayer);
			}
			else
			{
				if ((Object)(object)localPlayer == (Object)null)
				{
					return;
				}
				KeyboardShortcut value = Plugin.ToggleKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && TakeInputFn(localPlayer))
				{
					if (Active)
					{
						Leave(localPlayer);
					}
					else if (((Character)localPlayer).InPlaceMode() && (Object)(object)GameCamera.instance != (Object)null)
					{
						Enter(localPlayer);
					}
				}
			}
		}

		private static void Enter(Player player)
		{
			//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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)GameCamera.instance).transform;
			_position = transform.position;
			_yaw = transform.eulerAngles.y;
			_pitch = transform.eulerAngles.x;
			if (_pitch > 180f)
			{
				_pitch -= 360f;
			}
			_savedPlaceDistance = player.m_maxPlaceDistance;
			player.m_maxPlaceDistance = Plugin.MaxDistance.Value + 55f;
			Active = true;
			((Character)player).Message((MessageType)1, "Free-cam on", 0, (Sprite)null, false);
		}

		private static void Leave(Player player)
		{
			if (Active)
			{
				Active = false;
				if ((Object)(object)player != (Object)null)
				{
					player.m_maxPlaceDistance = _savedPlaceDistance;
					((Character)player).Message((MessageType)1, "Free-cam off", 0, (Sprite)null, false);
				}
			}
		}

		private static void MoveCamera(float dt)
		{
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: 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_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: 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_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: 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_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Transform transform = ((Component)GameCamera.instance).transform;
			if (CanInput(localPlayer))
			{
				Vector2 val = ZInput.GetMouseDelta() * PlayerController.m_mouseSens;
				if (PlayerController.m_invertMouse)
				{
					val.y = 0f - val.y;
				}
				_yaw += val.x;
				_pitch = Mathf.Clamp(_pitch - val.y, -89f, 89f);
				Quaternion val2 = Quaternion.Euler(_pitch, _yaw, 0f);
				Vector3 val3 = Vector3.zero;
				if (ZInput.GetButton("Forward"))
				{
					val3 += val2 * Vector3.forward;
				}
				if (ZInput.GetButton("Backward"))
				{
					val3 -= val2 * Vector3.forward;
				}
				if (ZInput.GetButton("Right"))
				{
					val3 += val2 * Vector3.right;
				}
				if (ZInput.GetButton("Left"))
				{
					val3 -= val2 * Vector3.right;
				}
				if (ZInput.GetKey(Plugin.UpKey.Value, true))
				{
					val3 += Vector3.up;
				}
				if (ZInput.GetKey(Plugin.DownKey.Value, true))
				{
					val3 -= Vector3.up;
				}
				float num = Plugin.Speed.Value * (ZInput.GetButton("Run") ? 4f : 1f);
				if (((Vector3)(ref val3)).sqrMagnitude > 1f)
				{
					((Vector3)(ref val3)).Normalize();
				}
				_position += val3 * num * dt;
			}
			Vector3 val4 = _position - ((Component)localPlayer).transform.position;
			float value = Plugin.MaxDistance.Value;
			if (((Vector3)(ref val4)).sqrMagnitude > value * value)
			{
				_position = ((Component)localPlayer).transform.position + ((Vector3)(ref val4)).normalized * value;
			}
			transform.SetPositionAndRotation(_position, Quaternion.Euler(_pitch, _yaw, 0f));
		}
	}
	internal static class Integrity
	{
		private class MaterialSettings
		{
			public ConfigEntry<bool> Customize;

			public ConfigEntry<int> Vertical;

			public ConfigEntry<int> Horizontal;
		}

		[HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")]
		private static class WearNTear_GetMaterialProperties_Patch
		{
			private static void Postfix(WearNTear __instance, ref float horizontalLoss, ref float verticalLoss)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				Percents(__instance.m_materialType, out var vertical, out var horizontal);
				if (vertical != 100)
				{
					verticalLoss /= (float)vertical / 100f;
				}
				if (horizontal != 100)
				{
					horizontalLoss /= (float)horizontal / 100f;
				}
			}
		}

		private static ConfigEntry<int> _globalVertical;

		private static ConfigEntry<int> _globalHorizontal;

		private static readonly Dictionary<MaterialType, MaterialSettings> Materials = new Dictionary<MaterialType, MaterialSettings>();

		private const string Section = "Structural Integrity";

		private static readonly KeyValuePair<MaterialType, string>[] Names = new KeyValuePair<MaterialType, string>[9]
		{
			new KeyValuePair<MaterialType, string>((MaterialType)0, "Wood"),
			new KeyValuePair<MaterialType, string>((MaterialType)3, "Core wood"),
			new KeyValuePair<MaterialType, string>((MaterialType)1, "Stone"),
			new KeyValuePair<MaterialType, string>((MaterialType)2, "Iron"),
			new KeyValuePair<MaterialType, string>((MaterialType)4, "Marble"),
			new KeyValuePair<MaterialType, string>((MaterialType)5, "Ashstone"),
			new KeyValuePair<MaterialType, string>((MaterialType)6, "Ancient"),
			new KeyValuePair<MaterialType, string>((MaterialType)7, "Ice"),
			new KeyValuePair<MaterialType, string>((MaterialType)8, "Timberwood")
		};

		private const string VerticalHelp = "How far support reaches straight up from the ground, in percent of vanilla. 100 = vanilla. 200 roughly doubles how tall a structure can get before it needs more support. Angled pieces use a mix of vertical and horizontal.";

		private const string HorizontalHelp = "How far support reaches sideways, in percent of vanilla. 100 = vanilla. 200 roughly doubles how far a floor, beam or overhang can reach. (Materials that lose nearly everything per meter, like stone sideways, need much higher values before it shows.)";

		internal static void Setup(ConfigFile config)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			AcceptableValueRange<int> range = new AcceptableValueRange<int>(25, 500);
			_globalVertical = Sync.Bind(config, "Structural Integrity", "Vertical Strength", 100, "How far support reaches straight up from the ground, in percent of vanilla. 100 = vanilla. 200 roughly doubles how tall a structure can get before it needs more support. Angled pieces use a mix of vertical and horizontal.", 1000, (AcceptableValueBase)(object)range);
			_globalHorizontal = Sync.Bind(config, "Structural Integrity", "Horizontal Strength", 100, "How far support reaches sideways, in percent of vanilla. 100 = vanilla. 200 roughly doubles how far a floor, beam or overhang can reach. (Materials that lose nearly everything per meter, like stone sideways, need much higher values before it shows.)", 999, (AcceptableValueBase)(object)range);
			int num = 900;
			KeyValuePair<MaterialType, string>[] names = Names;
			for (int i = 0; i < names.Length; i++)
			{
				KeyValuePair<MaterialType, string> keyValuePair = names[i];
				string value = keyValuePair.Value;
				MaterialSettings materialSettings = new MaterialSettings();
				materialSettings.Customize = Sync.Bind(config, "Structural Integrity", value + ": Customize", defaultValue: false, "Turn on to set " + value + " separately from the global sliders. This reveals the two " + value + " sliders below it.", num);
				ConfigEntry<bool> customize = materialSettings.Customize;
				materialSettings.Vertical = Sync.Bind(config, "Structural Integrity", value + ": Vertical Strength", 100, "How far support reaches straight up from the ground, in percent of vanilla. 100 = vanilla. 200 roughly doubles how tall a structure can get before it needs more support. Angled pieces use a mix of vertical and horizontal.", num - 1, (AcceptableValueBase)(object)range, null, () => customize.Value);
				materialSettings.Horizontal = Sync.Bind(config, "Structural Integrity", value + ": Horizontal Strength", 100, "How far support reaches sideways, in percent of vanilla. 100 = vanilla. 200 roughly doubles how far a floor, beam or overhang can reach. (Materials that lose nearly everything per meter, like stone sideways, need much higher values before it shows.)", num - 2, (AcceptableValueBase)(object)range, null, () => customize.Value);
				Materials[keyValuePair.Key] = materialSettings;
				num -= 10;
			}
		}

		private static void Percents(MaterialType type, out int vertical, out int horizontal)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Materials.TryGetValue(type, out var value) && Sync.Get<bool>(value.Customize))
			{
				vertical = Sync.Get<int>(value.Vertical);
				horizontal = Sync.Get<int>(value.Horizontal);
			}
			else
			{
				vertical = Sync.Get<int>(_globalVertical);
				horizontal = Sync.Get<int>(_globalHorizontal);
			}
		}
	}
	internal static class Placement
	{
		[HarmonyPatch(typeof(Player), "TestGhostClipping")]
		private static class Player_TestGhostClipping_Patch
		{
			private static void Prefix(ref float maxPenetration)
			{
				if (Sync.Get<bool>(Plugin.RelaxedClipping) && Sync.Get<float>(Plugin.ClippingTolerance) > maxPenetration)
				{
					maxPenetration = Sync.Get<float>(Plugin.ClippingTolerance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "IsOverlappingOtherPiece")]
		private static class Player_IsOverlappingOtherPiece_Patch
		{
			private static void Prefix(ref bool allowRotatedOverlap)
			{
				allowRotatedOverlap = true;
			}
		}

		[HarmonyPatch(typeof(Attack), "SpawnOnHitTerrain")]
		private static class Attack_SpawnOnHitTerrain_Patch
		{
			private static void Prefix()
			{
				_terrainTool = true;
			}

			private static void Finalizer()
			{
				_terrainTool = false;
			}
		}

		[HarmonyPatch(typeof(Location), "IsInsideNoBuildLocation")]
		private static class Location_IsInsideNoBuildLocation_Patch
		{
			private static void Postfix(ref bool __result)
			{
				if (_terrainTool ? Sync.Get<bool>(Plugin.DigAnywhere) : Sync.Get<bool>(Plugin.BuildAnywhere))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(EnvMan), "CheckInteriorBuildingOverride")]
		private static class EnvMan_CheckInteriorBuildingOverride_Patch
		{
			private static void Postfix(ref bool __result)
			{
				if (Sync.Get<bool>(Plugin.BuildAnywhere))
				{
					__result = true;
				}
			}
		}

		private static bool _terrainTool;
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? Browsable;

		public bool? ReadOnly;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public Action<ConfigEntryBase> CustomDrawer;
	}
	[BepInPlugin("com.freecambuildandterraintweaks", "FreeCamBuildAndTerrainTweaks", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.freecambuildandterraintweaks";

		public const string PluginName = "FreeCamBuildAndTerrainTweaks";

		public const string PluginVersion = "1.0.1";

		internal static ConfigEntry<KeyboardShortcut> ToggleKey;

		internal static ConfigEntry<KeyCode> UpKey;

		internal static ConfigEntry<KeyCode> DownKey;

		internal static ConfigEntry<float> Speed;

		internal static ConfigEntry<float> MaxDistance;

		internal static ConfigEntry<bool> DeeperDigging;

		internal static ConfigEntry<int> MaxDigDepth;

		internal static ConfigEntry<bool> HigherRaising;

		internal static ConfigEntry<int> MaxRaiseHeight;

		internal static ConfigEntry<bool> BuildAnywhere;

		internal static ConfigEntry<bool> DigAnywhere;

		internal static ConfigEntry<bool> RelaxedClipping;

		internal static ConfigEntry<float> ClippingTolerance;

		internal static ConfigEntry<int> StationRange;

		internal static ConfigEntry<int> ExtensionRange;

		internal static ConfigEntry<bool> UpgradesIgnoreOverlap;

		internal static ConfigEntry<bool> IgnorePlayersInTheWay;

		internal static ConfigEntry<bool> ShowSupport;

		internal static ConfigEntry<SupportFormat> SupportStyle;

		internal static ConfigEntry<int> SupportFontSize;

		internal static ConfigEntry<SupportPosition> SupportPlacement;

		internal static ConfigEntry<SupportDirection> SupportReading;

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private const string IntegrityWarning = "May not work correctly unless this mod is also installed on the server and by all other players.";

		private const string TerrainWarning = "May not work correctly unless all other players have this mod too.";

		private static ConfigDescription Describe(string text, int order, AcceptableValueBase range = null)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			return new ConfigDescription(text, range, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			});
		}

		private void Awake()
		{
			//IL_002b: 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)
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ToggleFreeCam", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), Describe("Press while the hammer is out to enter or leave free-cam. Hold your sprint key to fly faster.", 3));
			UpKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "MoveUp", (KeyCode)32, Describe("Fly straight up.", 2));
			DownKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "MoveDown", (KeyCode)306, Describe("Fly straight down.", 1));
			Speed = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "Speed", 10f, Describe("Flying speed in meters per second.", 2, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f)));
			MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "MaxDistanceFromPlayer", 100f, Describe("How far the camera may fly from your character, in meters. The world only exists around your character, so much further shows empty land and building there will not work.", 1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f)));
			ShowSupport = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowSupport", false, Describe("With the hammer out, show a piece's structural support under its health bar when you aim at it. Only the number the game already tracks; nothing is sent anywhere.", 2));
			SupportStyle = ((BaseUnityPlugin)this).Config.Bind<SupportFormat>("Display", "SupportFormat", SupportFormat.Both, Describe("How to show it: the number, the percentage, or both. 0 means the piece collapses: the number counts support above the collapse point, out of the most the material can have above it.", 1));
			SupportFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "SupportFontSize", 18, Describe("Size of the support text.", 0, (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 60)));
			SupportPlacement = ((BaseUnityPlugin)this).Config.Bind<SupportPosition>("Display", "SupportPosition", SupportPosition.Below, Describe("Where the support text sits next to the health bar.", -1));
			SupportReading = ((BaseUnityPlugin)this).Config.Bind<SupportDirection>("Display", "SupportTextDirection", SupportDirection.Horizontal, Describe("Which way the text reads. Horizontal is normal. Vertical 1 is turned so you tilt your head to the right to read it; Vertical 2 so you tilt your head to the left.", -2));
			BuildAnywhere = Sync.Bind(((BaseUnityPlugin)this).Config, "Building", "BuildAnywhere", defaultValue: false, "Allow building in places the game normally blocks: dungeons and other interiors, and no-build areas such as the trader's bubble and the boss stone areas. Works with or without free-cam. Does not get around other players' wards. Digging is a separate setting (DigAnywhere).", 4);
			DigAnywhere = Sync.Bind(((BaseUnityPlugin)this).Config, "Building", "DigAnywhere", defaultValue: false, "Allow digging and hoeing the ground inside no-build areas, such as around the boss stones. Does not get around other players' wards.", 3);
			RelaxedClipping = Sync.Bind(((BaseUnityPlugin)this).Config, "Building", "RelaxedGroundPlacement", defaultValue: true, "Be forgiving when a piece (a workbench, say) sinks into slopes or overlaps things. The game normally refuses if it overlaps by more than 0.2 m.", 2);
			ClippingTolerance = Sync.Bind(((BaseUnityPlugin)this).Config, "Building", "OverlapTolerance", 1f, "How far, in meters, a piece may sink into the ground or other objects and still be placed (the game's own limit is 0.2).", 1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 3f));
			IgnorePlayersInTheWay = Sync.Bind(((BaseUnityPlugin)this).Config, "Building", "IgnorePlayersInTheWay", defaultValue: false, "Let pieces be placed where a player (or you) is standing. Normally the game refuses with a \"blocked\" message.", 0);
			DeeperDigging = Sync.Bind(((BaseUnityPlugin)this).Config, "Terrain", "DeeperDigging", defaultValue: false, "Allow digging deeper than the game's limit (below the ground's original height). Off = vanilla.", 4);
			MaxDigDepth = Sync.Bind(((BaseUnityPlugin)this).Config, "Terrain", "MaxDigDepth", 8, "How deep you may dig, in meters, when DeeperDigging is on. (Vanilla is 8.)", 3, (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 50));
			HigherRaising = Sync.Bind(((BaseUnityPlugin)this).Config, "Terrain", "HigherRaising", defaultValue: false, "Allow raising the ground higher than the game's limit (above the ground's original height). Off = vanilla.", 2);
			MaxRaiseHeight = Sync.Bind(((BaseUnityPlugin)this).Config, "Terrain", "MaxRaiseHeight", 8, "How high you may raise the ground, in meters, when HigherRaising is on. (Vanilla is 8.)", 1, (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 50));
			StationRange = Sync.Bind(((BaseUnityPlugin)this).Config, "Stations", "StationRange", 20, "How far from a crafting station (workbench, forge...) you can still build the pieces that need it, in meters. (Vanilla is 20.) Stations with a shorter vanilla range are scaled in proportion.", 99990, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100));
			ExtensionRange = Sync.Bind(((BaseUnityPlugin)this).Config, "Stations", "UpgradeRange", 5, "How far from its crafting station a station upgrade (the extra pieces that improve a workbench, forge and so on) can be placed, and still count, in meters. (Vanilla is 5.)", 99980, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100));
			UpgradesIgnoreOverlap = Sync.Bind(((BaseUnityPlugin)this).Config, "Stations", "UpgradesIgnoreOverlap", defaultValue: false, "Station upgrades can be placed right next to or inside each other and other things, with no \"not enough space\" or overlap message. Two upgrades of the same kind still only count once for the station's level.", 99970);
			AddWarning("Structural Integrity", "May not work correctly unless this mod is also installed on the server and by all other players.");
			AddWarning("Terrain", "May not work correctly unless all other players have this mod too.");
			Integrity.Setup(((BaseUnityPlugin)this).Config);
			new Harmony("com.freecambuildandterraintweaks").PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FreeCamBuildAndTerrainTweaks 1.0.1 loaded");
		}

		private static void AddWarning(string section, string text)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			((BaseUnityPlugin)Instance).Config.Bind<string>(section, "Warning", text, new ConfigDescription("Read me.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 99999,
					ReadOnly = true,
					HideDefaultButton = true,
					HideSettingName = true,
					CustomDrawer = delegate
					{
						DrawWarning(text);
					}
				}
			}));
		}

		private static void DrawWarning(string text)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = new Color(1f, 0.85f, 0.3f);
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				wordWrap = true
			};
			GUILayout.Label("WARNING: " + text, val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUI.color = color;
		}

		private void Update()
		{
			Sync.Tick();
			FreeCam.Tick();
		}
	}
	internal static class Stations
	{
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDB_Awake_Patch
		{
			private static void Postfix()
			{
				Scan();
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake_Patch
		{
			private static void Postfix()
			{
				Scan();
			}
		}

		[HarmonyPatch(typeof(CraftingStation), "GetStationBuildRange")]
		private static class CraftingStation_GetStationBuildRange_Patch
		{
			private static void Postfix(ref float __result)
			{
				__result *= (float)Sync.Get<int>(Plugin.StationRange) / 20f;
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static class Player_HaveRequirements_Patch
		{
			private static void Prefix(Piece piece)
			{
				_ignoringNow = ShouldIgnore(piece);
			}

			private static void Finalizer()
			{
				_ignoringNow = false;
			}
		}

		[HarmonyPatch(typeof(Player), "CheckCanRemovePiece")]
		private static class Player_CheckCanRemovePiece_Patch
		{
			private static void Prefix(Piece piece)
			{
				_ignoringNow = ShouldIgnore(piece);
			}

			private static void Finalizer()
			{
				_ignoringNow = false;
			}
		}

		[HarmonyPatch(typeof(ZoneSystem), "GetGlobalKey", new Type[] { typeof(GlobalKeys) })]
		private static class ZoneSystem_GetGlobalKey_Patch
		{
			private static void Postfix(GlobalKeys key, ref bool __result)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Invalid comparison between Unknown and I4
				if (_ignoringNow && (int)key == 27)
				{
					__result = true;
				}
			}
		}

		private static readonly Dictionary<string, ConfigEntry<bool>> Ignore = new Dictionary<string, ConfigEntry<bool>>();

		private static bool _ignoringNow;

		private static bool ShouldIgnore(Piece piece)
		{
			if ((Object)(object)piece != (Object)null && (Object)(object)piece.m_craftingStation != (Object)null && Ignore.TryGetValue(piece.m_craftingStation.m_name, out var value))
			{
				return Sync.Get<bool>(value);
			}
			return false;
		}

		private static string Localize(string token)
		{
			try
			{
				string text = ((Localization.instance != null) ? Localization.instance.Localize(token) : token);
				if (!string.IsNullOrEmpty(text) && !text.StartsWith("$") && !text.StartsWith("["))
				{
					return text;
				}
			}
			catch
			{
			}
			string text2 = token.TrimStart(new char[1] { '$' });
			if (text2.StartsWith("piece_"))
			{
				text2 = text2.Substring(6);
			}
			if (text2.Length != 0)
			{
				return char.ToUpper(text2[0]) + text2.Substring(1);
			}
			return token;
		}

		internal static void Scan()
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)Plugin.Instance == (Object)null)
			{
				return;
			}
			try
			{
				Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
				foreach (GameObject item2 in ObjectDB.instance.m_items)
				{
					PieceTable val = ((!Object.op_Implicit((Object)(object)item2)) ? null : item2.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					foreach (GameObject piece in val.m_pieces)
					{
						Piece val2 = (Object.op_Implicit((Object)(object)piece) ? piece.GetComponent<Piece>() : null);
						if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.m_craftingStation == (Object)null))
						{
							string name = val2.m_craftingStation.m_name;
							if (!dictionary.TryGetValue(name, out var value))
							{
								value = (dictionary[name] = new List<string>());
							}
							string item = Localize(val2.m_name);
							if (!value.Contains(item))
							{
								value.Add(item);
							}
						}
					}
				}
				foreach (KeyValuePair<string, List<string>> item3 in dictionary)
				{
					if (!Ignore.ContainsKey(item3.Key))
					{
						string text = Localize(item3.Key);
						item3.Value.Sort();
						string text2 = string.Join(", ", item3.Value.GetRange(0, Math.Min(40, item3.Value.Count)).ToArray());
						if (item3.Value.Count > 40)
						{
							text2 += $", and {item3.Value.Count - 40} more";
						}
						Ignore[item3.Key] = Sync.Bind(((BaseUnityPlugin)Plugin.Instance).Config, "Stations", "Ignore " + text, defaultValue: false, "Build things that need a " + text + " without one nearby. Other stations are still required. " + $"Pieces that need it ({item3.Value.Count}): {text2}.", 0, null, "Stations/Ignore:" + item3.Key);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not list the crafting stations: " + ex.Message));
			}
		}
	}
	internal enum SupportFormat
	{
		Number,
		Percent,
		Both
	}
	internal enum SupportPosition
	{
		Below,
		Above,
		Left,
		Right
	}
	internal enum SupportDirection
	{
		Horizontal,
		Vertical1,
		Vertical2
	}
	internal static class SupportDisplay
	{
		[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
		private static class Hud_UpdateCrosshair_Patch
		{
			private static void Postfix(Hud __instance, Player player)
			{
				if (!Plugin.ShowSupport.Value || (Object)(object)player == (Object)null || (Object)(object)__instance.m_hoverName == (Object)null || (Object)(object)__instance.m_pieceHealthRoot == (Object)null)
				{
					if ((Object)(object)_label != (Object)null && ((Component)_label).gameObject.activeSelf)
					{
						((Component)_label).gameObject.SetActive(false);
					}
					return;
				}
				EnsureLabel(__instance);
				string text = null;
				Piece hoveringPiece = player.GetHoveringPiece();
				WearNTear val = (Object.op_Implicit((Object)(object)hoveringPiece) ? ((Component)hoveringPiece).GetComponent<WearNTear>() : null);
				if ((Object)(object)val != (Object)null && val.m_supports)
				{
					text = Describe(val);
				}
				if (text == null)
				{
					((Component)_label).gameObject.SetActive(false);
					return;
				}
				((Component)_label).gameObject.SetActive(true);
				_label.text = text;
				ApplyLayout(__instance);
			}
		}

		private static readonly MethodInfo GetSupport = AccessTools.Method(typeof(WearNTear), "GetSupport", (Type[])null, (Type[])null);

		private static readonly MethodInfo GetMaterialProperties = AccessTools.Method(typeof(WearNTear), "GetMaterialProperties", (Type[])null, (Type[])null);

		private static TMP_Text _label;

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private static void EnsureLabel(Hud hud)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_label != (Object)null))
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)hud.m_hoverName).gameObject, (Transform)(object)hud.m_pieceHealthRoot);
				((Object)obj).name = "PCC_SupportLabel";
				RectTransform component = obj.GetComponent<RectTransform>();
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0.5f, 0.5f);
				component.pivot = val;
				Vector2 anchorMin = (component.anchorMax = val);
				component.anchorMin = anchorMin;
				component.anchoredPosition = new Vector2(0f, -28f);
				component.sizeDelta = new Vector2(500f, 30f);
				_label = obj.GetComponent<TMP_Text>();
				_label.alignment = (TextAlignmentOptions)514;
				_label.enableAutoSizing = false;
				_label.richText = true;
				_label.enableWordWrapping = false;
				_label.overflowMode = (TextOverflowModes)0;
			}
		}

		private static void ApplyLayout(Hud hud)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: 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_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			_label.fontSize = Plugin.SupportFontSize.Value;
			_label.alignment = (TextAlignmentOptions)514;
			float num = 0f;
			if (Plugin.SupportReading.Value == SupportDirection.Vertical1)
			{
				num = -90f;
			}
			else if (Plugin.SupportReading.Value == SupportDirection.Vertical2)
			{
				num = 90f;
			}
			RectTransform rectTransform = _label.rectTransform;
			rectTransform.pivot = new Vector2(0.5f, 0.5f);
			((Transform)rectTransform).rotation = Quaternion.Euler(0f, 0f, num);
			Vector2 val = (rectTransform.sizeDelta = _label.GetPreferredValues(_label.text));
			hud.m_pieceHealthRoot.GetWorldCorners(Corners);
			float num2 = Mathf.Min(new float[4]
			{
				Corners[0].x,
				Corners[2].x,
				Corners[1].x,
				Corners[3].x
			});
			float num3 = Mathf.Max(new float[4]
			{
				Corners[0].x,
				Corners[2].x,
				Corners[1].x,
				Corners[3].x
			});
			float num4 = Mathf.Min(new float[4]
			{
				Corners[0].y,
				Corners[2].y,
				Corners[1].y,
				Corners[3].y
			});
			float num5 = Mathf.Max(new float[4]
			{
				Corners[0].y,
				Corners[2].y,
				Corners[1].y,
				Corners[3].y
			});
			Vector3 val2 = new Vector3((num2 + num3) / 2f, (num4 + num5) / 2f, Corners[0].z);
			float x = ((Transform)rectTransform).lossyScale.x;
			bool num6 = num != 0f;
			float num7 = (num6 ? val.y : val.x) * x;
			float num8 = (num6 ? val.x : val.y) * x;
			float num9 = 6f * x;
			Vector3 position = val2;
			switch (Plugin.SupportPlacement.Value)
			{
			case SupportPosition.Above:
				position.y = num5 + num9 + num8 / 2f;
				break;
			case SupportPosition.Left:
				position.x = num2 - num9 - num7 / 2f;
				break;
			case SupportPosition.Right:
				position.x = num3 + num9 + num7 / 2f;
				break;
			default:
				position.y = num4 - num9 - num8 / 2f;
				break;
			}
			((Transform)rectTransform).position = position;
		}

		private static string Describe(WearNTear piece)
		{
			//IL_011f: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			if (GetSupport == null || GetMaterialProperties == null)
			{
				return null;
			}
			float num = (float)GetSupport.Invoke(piece, null);
			object[] array = new object[4];
			GetMaterialProperties.Invoke(piece, array);
			float num2 = (float)array[0];
			float num3 = (float)array[1];
			if (num2 <= 0f)
			{
				return null;
			}
			float num4 = Mathf.Max(1f, num2 - num3);
			float num5 = Mathf.Max(0f, num - num3);
			float num6 = num5 / num4 * 100f;
			string text = Plugin.SupportStyle.Value switch
			{
				SupportFormat.Number => $"{num5:0} / {num4:0}", 
				SupportFormat.Percent => $"{num6:0}%", 
				_ => $"{num5:0} / {num4:0} ({num6:0}%)", 
			};
			float num7 = Mathf.Clamp01((num - num3) / Mathf.Max(1f, num2 * 0.5f - num3));
			Color val = Color.Lerp(new Color(1f, 0.3f, 0.2f), new Color(0.6f, 1f, 0.6f), num7);
			return "<color=#" + ColorUtility.ToHtmlStringRGB(val) + ">" + text + "</color>";
		}
	}
	internal static class Sync
	{
		private class Info
		{
			public ConfigEntryBase Entry;

			public string Id;

			public string Section;

			public bool IsEnforce;

			public ConfigurationManagerAttributes Tags;

			public Func<bool> VisibleWhen;

			public bool Pending;

			public float SendAt;

			public float HoldUntil;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class ZNet_OnNewConnection_Patch
		{
			[CompilerGenerated]
			private static class <>O
			{
				public static Method <0>__RPC_Hello;

				public static Action<ZRpc, string, string> <1>__RPC_AdminSet;

				public static Action<ZRpc, ZPackage> <2>__RPC_State;
			}

			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				if (__instance.IsServer())
				{
					ZRpc rpc = peer.m_rpc;
					object obj = <>O.<0>__RPC_Hello;
					if (obj == null)
					{
						Method val = RPC_Hello;
						<>O.<0>__RPC_Hello = val;
						obj = (object)val;
					}
					rpc.Register("FCB_Hello", (Method)obj);
					peer.m_rpc.Register<string, string>("FCB_AdminSet", (Action<ZRpc, string, string>)RPC_AdminSet);
				}
				else
				{
					peer.m_rpc.Register<ZPackage>("FCB_State", (Action<ZRpc, ZPackage>)RPC_State);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class ZNet_Awake_Patch
		{
			private static void Postfix()
			{
				ResetState();
			}
		}

		private const string HelloRpc = "FCB_Hello";

		private const string StateRpc = "FCB_State";

		private const string AdminSetRpc = "FCB_AdminSet";

		internal const string EnforceKey = "Enforce Server Settings";

		private static readonly HashSet<string> EnforceableSections = new HashSet<string> { "Building", "Terrain", "Stations", "Structural Integrity" };

		private static readonly List<Info> All = new List<Info>();

		private static readonly Dictionary<string, Info> ById = new Dictionary<string, Info>();

		private static readonly Dictionary<ConfigEntryBase, Info> ByEntry = new Dictionary<ConfigEntryBase, Info>();

		private static readonly HashSet<string> SectionsWithSwitch = new HashSet<string>();

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

		private static readonly HashSet<string> Enforced = new HashSet<string>();

		private static readonly Dictionary<ConfigEntryBase, object> Parsed = new Dictionary<ConfigEntryBase, object>();

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

		private static bool _suppress;

		private static bool _dirty;

		private static float _dirtyAt;

		private static float _nextHello;

		private static int _helloAttempts;

		private static float _nextRefresh;

		internal static bool ServerHasMod { get; private set; }

		private static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private static bool IsClientOfModServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
				{
					return ServerHasMod;
				}
				return false;
			}
		}

		internal static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, string text, int order, AcceptableValueBase range = null, string id = null, Func<bool> visibleWhen = null)
		{
			if (EnforceableSections.Contains(section) && SectionsWithSwitch.Add(section))
			{
				CreateEntry(config, section, "Enforce Server Settings", defaultValue: true, "Server owners only. On: every player on this server is forced to use THIS server's settings for this section (their own settings are hidden and ignored). Off: each player uses their own settings. Admins can change this in-game.", 100000, null, section + "/Enforce Server Settings", null, isEnforce: true);
			}
			return CreateEntry(config, section, key, defaultValue, text, order, range, id ?? (section + "/" + key), visibleWhen, isEnforce: false);
		}

		private static ConfigEntry<T> CreateEntry<T>(ConfigFile config, string section, string key, T defaultValue, string text, int order, AcceptableValueBase range, string id, Func<bool> visibleWhen, bool isEnforce)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
			{
				Order = order
			};
			ConfigEntry<T> val = config.Bind<T>(section, key, defaultValue, new ConfigDescription(text, range, new object[1] { configurationManagerAttributes }));
			if (EnforceableSections.Contains(section))
			{
				Info info = new Info
				{
					Entry = (ConfigEntryBase)(object)val,
					Id = id,
					Section = section,
					IsEnforce = isEnforce,
					Tags = configurationManagerAttributes,
					VisibleWhen = visibleWhen
				};
				All.Add(info);
				ById[id] = info;
				ByEntry[(ConfigEntryBase)(object)val] = info;
				val.SettingChanged += delegate
				{
					OnLocalChange(info);
				};
				if (ServerValues.TryGetValue(id, out var value))
				{
					ParseOne(info, value);
				}
			}
			else if (visibleWhen != null)
			{
				configurationManagerAttributes.Browsable = visibleWhen();
			}
			return val;
		}

		internal static T Get<T>(ConfigEntry<T> entry)
		{
			if (ServerHasMod && !IsServer && ByEntry.TryGetValue((ConfigEntryBase)(object)entry, out var value) && !value.IsEnforce && Enforced.Contains(value.Section) && Parsed.TryGetValue((ConfigEntryBase)(object)entry, out var value2) && value2 is T)
			{
				return (T)value2;
			}
			return entry.Value;
		}

		private static void OnLocalChange(Info info)
		{
			if (!_suppress)
			{
				if (IsServer)
				{
					_dirty = true;
					_dirtyAt = Time.unscaledTime + 0.5f;
				}
				else if (ServerHasMod && LooksLikeAdmin() && (info.IsEnforce || Enforced.Contains(info.Section)))
				{
					info.Pending = true;
					info.SendAt = Time.unscaledTime + 0.5f;
				}
			}
		}

		private static bool LooksLikeAdmin()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			try
			{
				if (!ZNet.instance.IsServer() && SynchronizationManager.Instance.PlayerIsAdmin)
				{
					return true;
				}
			}
			catch (Exception)
			{
			}
			if (!ZNet.instance.LocalPlayerIsAdminOrHost())
			{
				return Terminal.m_cheat;
			}
			return true;
		}

		private static void SetSilently(Info info, string serialized)
		{
			_suppress = true;
			try
			{
				info.Entry.SetSerializedValue(serialized);
			}
			catch (Exception)
			{
			}
			finally
			{
				_suppress = false;
			}
		}

		internal static void Tick()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				if (ServerHasMod || ServerValues.Count > 0)
				{
					ResetState();
				}
				if (Time.unscaledTime >= _nextRefresh)
				{
					_nextRefresh = Time.unscaledTime + 1f;
					Refresh();
				}
				return;
			}
			if (instance.IsServer())
			{
				if (_dirty && Time.unscaledTime >= _dirtyAt)
				{
					_dirty = false;
					Broadcast();
				}
			}
			else if (!ServerHasMod)
			{
				if (_helloAttempts < 4 && Time.unscaledTime >= _nextHello && instance.GetServerRPC() != null)
				{
					_helloAttempts++;
					_nextHello = Time.unscaledTime + 10f;
					instance.GetServerRPC().Invoke("FCB_Hello", Array.Empty<object>());
				}
			}
			else
			{
				foreach (Info item in All)
				{
					if (item.Pending && Time.unscaledTime >= item.SendAt)
					{
						item.Pending = false;
						item.HoldUntil = Time.unscaledTime + 3f;
						string serializedValue = item.Entry.GetSerializedValue();
						ZRpc serverRPC = instance.GetServerRPC();
						if (serverRPC != null)
						{
							serverRPC.Invoke("FCB_AdminSet", new object[2] { item.Id, serializedValue });
						}
						Plugin.Log.LogInfo((object)("Sent setting change to the server: " + item.Id + " = " + serializedValue));
					}
				}
			}
			if (Time.unscaledTime >= _nextRefresh)
			{
				_nextRefresh = Time.unscaledTime + 1f;
				Refresh();
			}
		}

		private static void Refresh()
		{
			bool isServer = IsServer;
			bool flag = ServerHasMod && !isServer && LooksLikeAdmin();
			bool flag2 = ServerHasMod && !isServer;
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((BaseUnityPlugin)Plugin.Instance).Config.SaveOnConfigSet = !flag2;
			}
			foreach (Info item in All)
			{
				bool value = ((!item.IsEnforce) ? ((!flag2 || flag || !Enforced.Contains(item.Section)) && (item.VisibleWhen == null || item.VisibleWhen())) : ((Object)(object)ZNet.instance == (Object)null || isServer || flag));
				item.Tags.Browsable = value;
				if (flag && !item.Pending && Time.unscaledTime >= item.HoldUntil && ServerValues.TryGetValue(item.Id, out var value2) && (item.IsEnforce || Enforced.Contains(item.Section)) && item.Entry.GetSerializedValue() != value2)
				{
					if (!Originals.ContainsKey(item.Id))
					{
						Originals[item.Id] = item.Entry.GetSerializedValue();
					}
					SetSilently(item, value2);
				}
			}
		}

		private static void ResetState()
		{
			foreach (KeyValuePair<string, string> original in Originals)
			{
				if (ById.TryGetValue(original.Key, out var value))
				{
					SetSilently(value, original.Value);
				}
			}
			Originals.Clear();
			bool serverHasMod = ServerHasMod;
			ServerHasMod = false;
			ServerValues.Clear();
			Enforced.Clear();
			Parsed.Clear();
			_helloAttempts = 0;
			_nextHello = 0f;
			_dirty = false;
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((BaseUnityPlugin)Plugin.Instance).Config.SaveOnConfigSet = true;
				if (serverHasMod)
				{
					((BaseUnityPlugin)Plugin.Instance).Config.Save();
				}
			}
		}

		private static ZPackage BuildState()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(All.Count);
			foreach (Info item in All)
			{
				val.Write(item.Id);
				val.Write(item.Entry.GetSerializedValue());
			}
			return val;
		}

		private static void Broadcast()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				ZRpc rpc = peer.m_rpc;
				if (rpc != null)
				{
					rpc.Invoke("FCB_State", new object[1] { BuildState() });
				}
			}
		}

		private static void RPC_Hello(ZRpc rpc)
		{
			if (rpc != null)
			{
				rpc.Invoke("FCB_State", new object[1] { BuildState() });
			}
		}

		private static void RPC_AdminSet(ZRpc rpc, string id, string value)
		{
			object obj;
			if (rpc == null)
			{
				obj = null;
			}
			else
			{
				ISocket socket = rpc.GetSocket();
				obj = ((socket != null) ? socket.GetHostName() : null);
			}
			string text = (string)obj;
			if (!string.IsNullOrEmpty(text))
			{
				ZNet instance = ZNet.instance;
				if (IsOnAdminList((instance != null) ? instance.GetAdminList() : null, text))
				{
					if (id == null || value == null || value.Length > 200 || !ById.TryGetValue(id, out var value2))
					{
						Plugin.Log.LogWarning((object)("Ignored a settings change from " + text + " for an unknown setting: " + id));
						return;
					}
					try
					{
						value2.Entry.SetSerializedValue(value);
					}
					catch (Exception)
					{
					}
					Plugin.Log.LogInfo((object)("Admin " + text + " set " + id + " = " + value2.Entry.GetSerializedValue()));
					return;
				}
			}
			Plugin.Log.LogWarning((object)("Ignored a settings change from non-admin " + text));
		}

		private static bool IsOnAdminList(List<string> admins, string connectionName)
		{
			if (admins == null)
			{
				return false;
			}
			int num = connectionName.IndexOf('_');
			string text = ((num >= 0) ? connectionName.Substring(num + 1) : connectionName);
			foreach (string admin in admins)
			{
				string text2 = admin?.Trim();
				if (!string.IsNullOrEmpty(text2) && (text2 == connectionName || text2 == text))
				{
					return true;
				}
			}
			return false;
		}

		private static void RPC_State(ZRpc rpc, ZPackage pkg)
		{
			int num = pkg.ReadInt();
			ServerValues.Clear();
			for (int i = 0; i < num; i++)
			{
				string key = pkg.ReadString();
				ServerValues[key] = pkg.ReadString();
			}
			if (!ServerHasMod)
			{
				Plugin.Log.LogInfo((object)"This server has FreeCamBuildAndTerrainTweaks; its enforced settings apply.");
			}
			ServerHasMod = true;
			Enforced.Clear();
			Parsed.Clear();
			foreach (Info item in All)
			{
				if (ServerValues.TryGetValue(item.Id, out var value))
				{
					ParseOne(item, value);
				}
			}
			_nextRefresh = 0f;
		}

		private static void ParseOne(Info info, string value)
		{
			try
			{
				object obj = TomlTypeConverter.ConvertToValue(value, info.Entry.SettingType);
				if (info.IsEnforce)
				{
					bool flag = default(bool);
					int num;
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag ? 1u : 0u)) != 0)
					{
						Enforced.Add(info.Section);
					}
				}
				else
				{
					Parsed[info.Entry] = obj;
				}
			}
			catch (Exception)
			{
			}
		}
	}
}