Decompiled source of ValheimExpeditions v0.2.0

BepInEx/plugins/ValheimExpeditions.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Rendering;
using ValheimExpeditions.Config;
using ValheimExpeditions.Storage;
using ValheimExpeditions.Vehicles;
using ValheimExpeditions.Zones;
using ValheimHouseholds.Utils;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ValheimExpeditions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+69ddb477b4df4e9601adba9f4986dcc5782df902")]
[assembly: AssemblyProduct("ValheimExpeditions")]
[assembly: AssemblyTitle("ValheimExpeditions")]
[assembly: AssemblyVersion("0.1.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 ValheimExpeditions
{
	[BepInPlugin("com.householdheim.expeditions", "ValheimExpeditions", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "com.householdheim.expeditions";

		public const string NAME = "ValheimExpeditions";

		public const string VERSION = "0.2.0";

		public static ManualLogSource Log;

		private static Harmony _harmony;

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CartConfig.Init(((BaseUnityPlugin)this).Config);
			ZoneLinesConfig.Init(((BaseUnityPlugin)this).Config);
			ExpeditionsState.Load();
			_harmony = new Harmony("com.householdheim.expeditions");
			_harmony.PatchAll(typeof(CartDamageProtection));
			_harmony.PatchAll(typeof(CartStorageExpansion));
			_harmony.PatchAll(typeof(CartChildZNetViewBinding));
			_harmony.PatchAll(typeof(ZoneLineCommands));
			_harmony.PatchAll(typeof(ZoneLineRpc));
			_harmony.PatchAll(typeof(ZoneLineCrossingDetector));
			PrefabManager.OnVanillaPrefabsAvailable += CartPiece.Register;
			FreezeManager.OnFreezeChanged += OnVHFreezeChanged;
			Log.LogInfo((object)"ValheimExpeditions 0.2.0 loaded");
		}

		private void OnDestroy()
		{
			FreezeManager.OnFreezeChanged -= OnVHFreezeChanged;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchAll("com.householdheim.expeditions");
			}
		}

		private static void OnVHFreezeChanged(bool isFrozen, string reason)
		{
			Log.LogInfo((object)string.Format("VH freeze state changed: frozen={0} reason={1}", isFrozen, reason ?? "none"));
		}

		public static void TriggerVictoryFreeze(string householdName)
		{
			FreezeManager.SetFrozen(true, "Victory: " + householdName + " reached the Marker!");
		}
	}
}
namespace ValheimExpeditions.Zones
{
	public static class AuthHelper
	{
		public static bool IsAdmin(ZNetPeer peer)
		{
			if (peer == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			if (!ZNet.instance.IsServer())
			{
				return false;
			}
			ISocket socket = peer.m_socket;
			string text = ((socket != null) ? socket.GetHostName() : null) ?? "";
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			try
			{
				return ZNet.instance.IsAdmin(text);
			}
			catch
			{
				return false;
			}
		}
	}
	public static class HouseholdLookup
	{
		private static readonly object _lock = new object();

		private static Dictionary<string, int> _cache = new Dictionary<string, int>();

		private static float _lastReadTime = -999f;

		private const float CacheTtlSeconds = 5f;

		private static string VhJsonPath => Path.Combine(Paths.ConfigPath, "ValheimHouseholds.json");

		public static int GetFactionId(string playerName)
		{
			if (string.IsNullOrEmpty(playerName))
			{
				return 0;
			}
			RefreshIfStale();
			lock (_lock)
			{
				if (_cache.TryGetValue(playerName, out var value))
				{
					return value;
				}
				return 0;
			}
		}

		private static void RefreshIfStale()
		{
			if (Time.time - _lastReadTime < 5f)
			{
				return;
			}
			lock (_lock)
			{
				if (Time.time - _lastReadTime < 5f)
				{
					return;
				}
				_lastReadTime = Time.time;
				_cache.Clear();
				if (!File.Exists(VhJsonPath))
				{
					return;
				}
				try
				{
					ParsePlayerAssignments(File.ReadAllText(VhJsonPath));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("HouseholdLookup: failed to read VH JSON: " + ex.Message));
				}
			}
		}

		private static void ParsePlayerAssignments(string json)
		{
			foreach (string item in EnumerateObjects(ExtractArray(json, "playerAssignments")))
			{
				string text = ExtractString(item, "name");
				int value = ExtractInt(item, "factionId");
				if (!string.IsNullOrEmpty(text))
				{
					_cache[text] = value;
				}
			}
		}

		private static string ExtractArray(string json, string key)
		{
			int num = json.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return "";
			}
			int num2 = json.IndexOf(':', num);
			if (num2 < 0)
			{
				return "";
			}
			int num3 = json.IndexOf('[', num2);
			if (num3 < 0)
			{
				return "";
			}
			int num4 = 0;
			for (int i = num3; i < json.Length; i++)
			{
				switch (json[i])
				{
				case '[':
					num4++;
					break;
				case ']':
					num4--;
					if (num4 == 0)
					{
						return json.Substring(num3 + 1, i - num3 - 1);
					}
					break;
				}
			}
			return "";
		}

		private static IEnumerable<string> EnumerateObjects(string arrayBody)
		{
			if (string.IsNullOrEmpty(arrayBody))
			{
				yield break;
			}
			int depth = 0;
			int num = -1;
			bool inString = false;
			bool escape = false;
			for (int i = 0; i < arrayBody.Length; i++)
			{
				char c = arrayBody[i];
				if (escape)
				{
					escape = false;
					continue;
				}
				switch (c)
				{
				case '\\':
					escape = true;
					continue;
				case '"':
					inString = !inString;
					continue;
				}
				if (inString)
				{
					continue;
				}
				switch (c)
				{
				case '{':
					if (depth == 0)
					{
						num = i;
					}
					depth++;
					break;
				case '}':
					depth--;
					if (depth == 0 && num >= 0)
					{
						yield return arrayBody.Substring(num, i - num + 1);
						num = -1;
					}
					break;
				}
			}
		}

		private static string ExtractString(string obj, string key)
		{
			int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return "";
			}
			int num2 = obj.IndexOf(':', num);
			if (num2 < 0)
			{
				return "";
			}
			int num3 = obj.IndexOf('"', num2 + 1);
			if (num3 < 0)
			{
				return "";
			}
			int num4 = obj.IndexOf('"', num3 + 1);
			if (num4 < 0)
			{
				return "";
			}
			return obj.Substring(num3 + 1, num4 - num3 - 1);
		}

		private static int ExtractInt(string obj, string key)
		{
			int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return 0;
			}
			int num2 = obj.IndexOf(':', num);
			if (num2 < 0)
			{
				return 0;
			}
			int i;
			for (i = num2 + 1; i < obj.Length && (char.IsDigit(obj[i]) || obj[i] == '-' || char.IsWhiteSpace(obj[i])); i++)
			{
			}
			if (int.TryParse(obj.Substring(num2 + 1, i - num2 - 1).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0;
		}
	}
	public static class ZoneLineAnnouncer
	{
		public static void Announce(int factionId, int zoneIndex, ZoneLineSpec spec)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ExpeditionsState.MarkCrossed(factionId, zoneIndex);
				string householdName = ZoneLinesConfig.GetHouseholdName(factionId);
				string text = ZoneLinesConfig.AnnouncementTemplate.Value.Replace("{household}", householdName).Replace("{zone}", spec.Label);
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "VEXP_ZoneCrossed", new object[1] { text });
				Plugin.Log.LogInfo((object)$"ZoneCrossed: factionId={factionId} zoneIndex={zoneIndex} text='{text}'");
			}
		}
	}
	[HarmonyPatch]
	public static class ZoneLineCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__1_0;

			public static ConsoleEvent <>9__1_1;

			public static ConsoleEvent <>9__1_2;

			internal void <RegisterCommands>b__1_0(ConsoleEventArgs args)
			{
				HandleShowZones(args);
			}

			internal void <RegisterCommands>b__1_1(ConsoleEventArgs args)
			{
				HandleResetCrossings(args);
			}

			internal void <RegisterCommands>b__1_2(ConsoleEventArgs args)
			{
				HandleAnnounceTest(args);
			}
		}

		private static bool _registered;

		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		[HarmonyPostfix]
		private static void RegisterCommands()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_00b3: 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_00a9: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			_registered = true;
			object obj = <>c.<>9__1_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					HandleShowZones(args);
				};
				<>c.<>9__1_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("vexp_showzones", "<on|off> — Toggle zone-line visuals (planes + minimap pins) for ALL clients. Admin only.", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__1_1;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate(ConsoleEventArgs args)
				{
					HandleResetCrossings(args);
				};
				<>c.<>9__1_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("vexp_resetcrossings", "— Clear all zone-crossing records, allowing re-announcements. Admin only.", (ConsoleEvent)obj2, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj3 = <>c.<>9__1_2;
			if (obj3 == null)
			{
				ConsoleEvent val3 = delegate(ConsoleEventArgs args)
				{
					HandleAnnounceTest(args);
				};
				<>c.<>9__1_2 = val3;
				obj3 = (object)val3;
			}
			new ConsoleCommand("vexp_announce_test", "<zoneIndex> — Fire a synthetic announcement for the given zone (0-based). Admin only.", (ConsoleEvent)obj3, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			Plugin.Log.LogInfo((object)"Registered console commands: vexp_showzones, vexp_resetcrossings, vexp_announce_test");
		}

		private static void HandleShowZones(ConsoleEventArgs args)
		{
			if (args.Args.Length < 2)
			{
				args.Context.AddString("Usage: vexp_showzones <on|off>");
				return;
			}
			bool flag;
			switch (args.Args[1].Trim().ToLowerInvariant())
			{
			case "on":
			case "1":
			case "true":
				flag = true;
				break;
			case "off":
			case "0":
			case "false":
				flag = false;
				break;
			default:
				args.Context.AddString("Expected 'on' or 'off'.");
				return;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				args.Context.AddString("Not connected.");
				return;
			}
			if (ZNet.instance.IsServer())
			{
				ZoneLinesConfig.Enabled.Value = flag;
				ZoneLineRpc.BroadcastPush(flag);
				args.Context.AddString("ZoneLines " + (flag ? "ON" : "OFF") + " (broadcast to all clients).");
				return;
			}
			long num = 0L;
			try
			{
				num = Traverse.Create((object)ZRoutedRpc.instance).Field("m_serverPeerID").GetValue<long>();
			}
			catch
			{
			}
			if (num == 0L)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null)
				{
					num = serverPeer.m_uid;
				}
			}
			if (num == 0L)
			{
				args.Context.AddString("No server peer.");
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(num, "VEXP_ShowZonesReq", new object[1] { flag });
			args.Context.AddString("Requested ZoneLines " + (flag ? "ON" : "OFF") + " from server.");
		}

		private static void HandleResetCrossings(ConsoleEventArgs args)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				args.Context.AddString("Not connected.");
				return;
			}
			if (!ZNet.instance.IsServer())
			{
				args.Context.AddString("This command only works on the server.");
				return;
			}
			ExpeditionsState.Reset();
			args.Context.AddString("Zone crossing records cleared.");
		}

		private static void HandleAnnounceTest(ConsoleEventArgs args)
		{
			if (args.Args.Length < 2)
			{
				args.Context.AddString("Usage: vexp_announce_test <zoneIndex>");
				return;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				args.Context.AddString("Not connected.");
				return;
			}
			if (!ZNet.instance.IsServer())
			{
				args.Context.AddString("This command only works on the server.");
				return;
			}
			if (!int.TryParse(args.Args[1].Trim(), out var result) || result < 0 || result >= ZoneLineRegistry.GetAll().Count)
			{
				List<ZoneLineSpec> all = ZoneLineRegistry.GetAll();
				args.Context.AddString($"Invalid zone index. Valid range: 0 to {all.Count - 1}");
				return;
			}
			ZoneLineSpec zoneLineSpec = ZoneLineRegistry.GetAll()[result];
			if (ZRoutedRpc.instance != null)
			{
				string text = ZoneLinesConfig.AnnouncementTemplate.Value.Replace("{household}", "[TEST]").Replace("{zone}", zoneLineSpec.Label);
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "VEXP_ZoneCrossed", new object[1] { text });
				args.Context.AddString($"Test announcement fired for zone {result}.");
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Update")]
	public static class ZoneLineCrossingDetector
	{
		private static float _lastTick;

		[HarmonyPostfix]
		private static void Tick(ZNet __instance)
		{
			if ((Object)(object)__instance == (Object)null || !__instance.IsServer())
			{
				return;
			}
			float time = Time.time;
			if (time - _lastTick < 1f)
			{
				return;
			}
			_lastTick = time;
			List<ZoneLineSpec> all = ZoneLineRegistry.GetAll();
			if (all.Count == 0)
			{
				return;
			}
			float[] array = new float[all.Count];
			for (int i = 0; i < all.Count; i++)
			{
				array[i] = ZoneLineRegistry.XPercentToWorldX(all[i].XPercent);
			}
			foreach (ZNetPeer peer in __instance.GetPeers())
			{
				if (peer == null || string.IsNullOrEmpty(peer.m_playerName))
				{
					continue;
				}
				int factionId = HouseholdLookup.GetFactionId(peer.m_playerName);
				if (factionId == 0)
				{
					continue;
				}
				float x = peer.m_refPos.x;
				for (int j = 0; j < array.Length; j++)
				{
					if (!(x < array[j]) && !ExpeditionsState.HasCrossed(factionId, j))
					{
						ZoneLineAnnouncer.Announce(factionId, j, all[j]);
					}
				}
			}
		}
	}
	public static class ZoneLineManager
	{
		private static readonly List<GameObject> _planes = new List<GameObject>();

		private static readonly List<PinData> _pins = new List<PinData>();

		private static bool _shown;

		public static bool IsShown => _shown;

		public static void Show()
		{
			if (_shown)
			{
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated())
			{
				_shown = true;
				return;
			}
			foreach (ZoneLineSpec item in ZoneLineRegistry.GetAll())
			{
				_planes.Add(ZoneLineWorldVisual.Spawn(item));
				_pins.AddRange(ZoneLineMapPins.Spawn(item));
			}
			_shown = true;
			Plugin.Log.LogInfo((object)$"ZoneLines: shown ({_planes.Count} planes, {_pins.Count} pins)");
		}

		public static void Hide()
		{
			foreach (GameObject plane in _planes)
			{
				ZoneLineWorldVisual.Destroy(plane);
			}
			_planes.Clear();
			ZoneLineMapPins.Remove(_pins);
			_shown = false;
			Plugin.Log.LogInfo((object)"ZoneLines: hidden");
		}

		public static void Rebuild()
		{
			Hide();
			Show();
		}

		public static void ApplyDesiredState(bool wantShown)
		{
			if (wantShown && !_shown)
			{
				Show();
			}
			else if (!wantShown && _shown)
			{
				Hide();
			}
		}
	}
	public static class ZoneLineMapPins
	{
		private static readonly Dictionary<Color, Sprite> _spriteCache = new Dictionary<Color, Sprite>();

		private static Sprite GetSprite(Color color)
		{
			//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_001b: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (_spriteCache.TryGetValue(color, out var value))
			{
				return value;
			}
			Texture2D val = new Texture2D(8, 8, (TextureFormat)4, false);
			Color[] array = (Color[])(object)new Color[64];
			for (int i = 0; i < 64; i++)
			{
				array[i] = color;
			}
			val.SetPixels(array);
			val.Apply();
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, 8f, 8f), new Vector2(0.5f, 0.5f));
			_spriteCache[color] = val2;
			return val2;
		}

		public static List<PinData> Spawn(ZoneLineSpec spec)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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)
			List<PinData> list = new List<PinData>();
			if ((Object)(object)Minimap.instance == (Object)null)
			{
				return list;
			}
			float num = ZoneLineRegistry.XPercentToWorldX(spec.XPercent);
			float worldRadius = ZoneLineRegistry.GetWorldRadius();
			int num2 = Mathf.Max(2, ZoneLinesConfig.MapPinsPerZone.Value);
			float num3 = worldRadius * 2f / (float)(num2 - 1);
			Sprite sprite = GetSprite(spec.Color);
			Vector3 val = default(Vector3);
			for (int i = 0; i < num2; i++)
			{
				float num4 = 0f - worldRadius + num3 * (float)i;
				((Vector3)(ref val))..ctor(num, 0f, num4);
				string text = ((i == num2 / 2) ? spec.Label : "");
				PinData val2 = Minimap.instance.AddPin(val, (PinType)3, text, false, false, 0L, default(PlatformUserID));
				val2.m_icon = sprite;
				list.Add(val2);
			}
			return list;
		}

		public static void Remove(List<PinData> pins)
		{
			if ((Object)(object)Minimap.instance == (Object)null || pins == null)
			{
				return;
			}
			foreach (PinData pin in pins)
			{
				Minimap.instance.RemovePin(pin);
			}
			pins.Clear();
		}
	}
	public static class ZoneLineRegistry
	{
		public static List<ZoneLineSpec> GetAll()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00d4: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			return new List<ZoneLineSpec>
			{
				new ZoneLineSpec
				{
					XPercent = ZoneLinesConfig.Zone1XPercent.Value,
					Label = (ZoneLinesConfig.Zone1Label.Value ?? "ZONE 1"),
					Color = ParseHex(ZoneLinesConfig.Zone1ColorHex.Value ?? "#4488FF", new Color(0.267f, 0.533f, 1f))
				},
				new ZoneLineSpec
				{
					XPercent = ZoneLinesConfig.Zone2XPercent.Value,
					Label = (ZoneLinesConfig.Zone2Label.Value ?? "ZONE 2"),
					Color = ParseHex(ZoneLinesConfig.Zone2ColorHex.Value ?? "#FFAA00", new Color(1f, 0.667f, 0f))
				},
				new ZoneLineSpec
				{
					XPercent = ZoneLinesConfig.Zone3XPercent.Value,
					Label = (ZoneLinesConfig.Zone3Label.Value ?? "ZONE 3 — VICTORY"),
					Color = ParseHex(ZoneLinesConfig.Zone3ColorHex.Value ?? "#FF4444", new Color(1f, 0.267f, 0.267f))
				}
			};
		}

		public static float GetWorldRadius()
		{
			try
			{
				FieldInfo field = typeof(WorldGenerator).GetField("WorldRadius", BindingFlags.Static | BindingFlags.Public);
				if (field != null && field.FieldType == typeof(float))
				{
					return (float)field.GetValue(null);
				}
			}
			catch
			{
			}
			return 10000f;
		}

		public static float XPercentToWorldX(float xPercent)
		{
			float worldRadius = GetWorldRadius();
			return (xPercent - 0.5f) * 2f * worldRadius;
		}

		public static Color ParseHex(string hex, Color fallback)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Color result = default(Color);
			if (ColorUtility.TryParseHtmlString(hex, ref result))
			{
				return result;
			}
			return fallback;
		}
	}
	[HarmonyPatch]
	public static class ZoneLineRpc
	{
		private const string RpcReq = "VEXP_ShowZonesReq";

		private const string RpcPush = "VEXP_ShowZonesPush";

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void OnZRoutedRpcCtor(ZRoutedRpc __instance)
		{
			if (__instance != null)
			{
				__instance.Register<bool>("VEXP_ShowZonesReq", (Action<long, bool>)delegate(long sender, bool wantOn)
				{
					HandleReqServer(sender, wantOn);
				});
				__instance.Register<bool>("VEXP_ShowZonesPush", (Action<long, bool>)delegate(long sender, bool isOn)
				{
					HandlePushClient(isOn);
				});
				__instance.Register<string>("VEXP_ZoneCrossed", (Action<long, string>)delegate(long sender, string text)
				{
					HandleZoneCrossedClient(text);
				});
				Plugin.Log.LogInfo((object)"Registered RPCs VEXP_ShowZonesReq, VEXP_ShowZonesPush, VEXP_ZoneCrossed");
			}
		}

		private static void HandleZoneCrossedClient(string text)
		{
			try
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
				}
				Plugin.Log.LogInfo((object)("VEXP_ZoneCrossed: '" + text + "'"));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("VEXP_ZoneCrossed handler failed: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")]
		[HarmonyPostfix]
		private static void OnAddPeer(ZRoutedRpc __instance, ZNetPeer peer)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && peer != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "VEXP_ShowZonesPush", new object[1] { ZoneLinesConfig.Enabled.Value });
			}
		}

		private static void HandleReqServer(long sender, bool wantOn)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				if (!AuthHelper.IsAdmin(ZNet.instance.GetPeer(sender)))
				{
					Plugin.Log.LogWarning((object)$"VEXP_ShowZonesReq: non-admin {sender} denied");
					return;
				}
				ZoneLinesConfig.Enabled.Value = wantOn;
				BroadcastPush(wantOn);
			}
		}

		public static void BroadcastPush(bool isOn)
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "VEXP_ShowZonesPush", new object[1] { isOn });
			}
		}

		private static void HandlePushClient(bool isOn)
		{
			ZoneLineManager.ApplyDesiredState(isOn);
		}
	}
	public struct ZoneLineSpec
	{
		public float XPercent;

		public string Label;

		public Color Color;
	}
	public static class ZoneLineWorldVisual
	{
		public static GameObject Spawn(ZoneLineSpec spec)
		{
			//IL_007d: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			float num = ZoneLineRegistry.XPercentToWorldX(spec.XPercent);
			float worldRadius = ZoneLineRegistry.GetWorldRadius();
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = "VEXP_ZoneLine_" + spec.Label;
			Collider component = obj.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			float value = ZoneLinesConfig.PlaneHeight.Value;
			float value2 = ZoneLinesConfig.PlaneThickness.Value;
			float num2 = ZoneLinesConfig.PlaneBaseY.Value + value * 0.5f;
			obj.transform.position = new Vector3(num, num2, 0f);
			obj.transform.localScale = new Vector3(value2, value, worldRadius * 2f);
			MeshRenderer component2 = obj.GetComponent<MeshRenderer>();
			Material val = new Material(Shader.Find("Sprites/Default"));
			Color color = spec.Color;
			color.a = Mathf.Clamp01(ZoneLinesConfig.PlaneAlpha.Value);
			val.color = color;
			val.renderQueue = 3000;
			((Renderer)component2).material = val;
			((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component2).receiveShadows = false;
			Object.DontDestroyOnLoad((Object)(object)obj);
			return obj;
		}

		public static void Destroy(GameObject go)
		{
			if ((Object)(object)go != (Object)null)
			{
				Object.Destroy((Object)(object)go);
			}
		}
	}
}
namespace ValheimExpeditions.Vehicles
{
	public static class CartPiece
	{
		private static bool _registered;

		public const string KilnChildName = "Cart_Kiln";

		public static void Register()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				PieceConfig val = new PieceConfig();
				val.PieceTable = "Hammer";
				val.Category = "Expeditions";
				val.Name = "Expeditions Cart";
				val.Description = "Admin-only cart for expeditions. Requires SwordCheat to craft.";
				val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
				{
					new RequirementConfig
					{
						Item = "SwordCheat",
						Amount = 1
					}
				};
				CustomPiece val2 = new CustomPiece("piece_expeditions_cart", "Cart", val);
				val2.PiecePrefab.GetComponent<Piece>().m_canBeRemoved = true;
				if ((Object)(object)val2.PiecePrefab.GetComponent<S01_WinterCart>() == (Object)null)
				{
					val2.PiecePrefab.AddComponent<S01_WinterCart>();
				}
				BakeKilnChild(val2.PiecePrefab);
				PieceManager.Instance.AddPiece(val2);
				Plugin.Log.LogInfo((object)"CartPiece registered");
			}
		}

		private static void BakeKilnChild(GameObject cartPrefab)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			string value = CartConfig.S01_KilnPrefab.Value;
			GameObject prefab = Cache.GetPrefab<GameObject>(value);
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Kiln prefab '" + value + "' not found in PrefabManager.Cache; cart will have no warmth source"));
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab, cartPrefab.transform);
			((Object)val).name = "Cart_Kiln";
			ZNetView component = val.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			Piece component2 = val.GetComponent<Piece>();
			if ((Object)(object)component2 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component2);
			}
			WearNTear component3 = val.GetComponent<WearNTear>();
			if ((Object)(object)component3 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component3);
			}
			Vector3 val2 = CartConfig.ParseVector3(CartConfig.S01_KilnOffset.Value, new Vector3(0f, 0.5f, 0f));
			float value2 = CartConfig.S01_KilnScale.Value;
			val.transform.localPosition = val2;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = new Vector3(value2, value2, value2);
			if ((Object)(object)val.GetComponent<CartWarmthZone>() == (Object)null)
			{
				val.AddComponent<CartWarmthZone>();
			}
			Plugin.Log.LogInfo((object)$"Baked kiln '{value}' onto cart prefab at offset {val2} scale {value2}");
		}
	}
	public class CartTemplate : MonoBehaviour
	{
		private void Awake()
		{
			Plugin.Log.LogInfo((object)$"CartTemplate initialized; runtime type={((object)this).GetType().Name}; hasS01={(Object)(object)((Component)this).GetComponent<S01_WinterCart>() != (Object)null}");
		}
	}
	[HarmonyPatch]
	public static class CartDamageProtection
	{
		[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
		[HarmonyPrefix]
		private static void BlockCartDamage(WearNTear __instance, ref float damage)
		{
			if (CartConfig.DamageProtectionEnabled.Value && !((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponentInParent<CartTemplate>() != (Object)null)
			{
				damage = 0f;
			}
		}
	}
	[HarmonyPatch]
	public static class CartChildZNetViewBinding
	{
		private static readonly FieldRef<Smelter, ZNetView> SmelterNview = AccessTools.FieldRefAccess<Smelter, ZNetView>("m_nview");

		private static readonly FieldRef<Fire, ZNetView> FireNview = AccessTools.FieldRefAccess<Fire, ZNetView>("m_nview");

		[HarmonyPatch(typeof(Smelter), "Awake")]
		[HarmonyPostfix]
		private static void BindSmelterToCartZNetView(Smelter __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)SmelterNview.Invoke(__instance) != (Object)null)
			{
				return;
			}
			CartTemplate componentInParent = ((Component)__instance).GetComponentInParent<CartTemplate>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null))
				{
					SmelterNview.Invoke(__instance) = component;
					Plugin.Log.LogInfo((object)("[Cart] Bound child Smelter '" + ((Object)__instance).name + "' to parent cart ZNetView"));
				}
			}
		}

		[HarmonyPatch(typeof(Fire), "Awake")]
		[HarmonyPostfix]
		private static void BindFireToCartZNetView(Fire __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)FireNview.Invoke(__instance) != (Object)null)
			{
				return;
			}
			CartTemplate componentInParent = ((Component)__instance).GetComponentInParent<CartTemplate>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null))
				{
					FireNview.Invoke(__instance) = component;
					Plugin.Log.LogInfo((object)("[Cart] Bound child Fire '" + ((Object)__instance).name + "' to parent cart ZNetView"));
				}
			}
		}
	}
	[HarmonyPatch]
	public static class CartStorageExpansion
	{
		private static readonly FieldRef<Inventory, int> InventoryWidth = AccessTools.FieldRefAccess<Inventory, int>("m_width");

		private static readonly FieldRef<Inventory, int> InventoryHeight = AccessTools.FieldRefAccess<Inventory, int>("m_height");

		[HarmonyPatch(typeof(Container), "Awake")]
		[HarmonyPostfix]
		private static void ExpandCartStorage(Container __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			CartTemplate componentInParent = ((Component)__instance).GetComponentInParent<CartTemplate>();
			ManualLogSource log = Plugin.Log;
			string[] obj = new string[6]
			{
				"[CartStorage] Container.Awake fired on '",
				((Object)__instance).name,
				"' parent='",
				null,
				null,
				null
			};
			Transform parent = ((Component)__instance).transform.parent;
			obj[3] = ((parent != null) ? ((Object)parent).name : null) ?? "<none>";
			obj[4] = "' CartTemplate=";
			obj[5] = (((Object)(object)componentInParent != (Object)null) ? "FOUND" : "null");
			log.LogInfo((object)string.Concat(obj));
			if (!((Object)(object)componentInParent == (Object)null))
			{
				int value = CartConfig.CartStorageWidth.Value;
				int value2 = CartConfig.CartStorageHeight.Value;
				__instance.m_width = value;
				__instance.m_height = value2;
				Inventory inventory = __instance.GetInventory();
				if (inventory != null)
				{
					InventoryWidth.Invoke(inventory) = value;
					InventoryHeight.Invoke(inventory) = value2;
					Plugin.Log.LogInfo((object)$"[CartStorage] Resized to {value}x{value2}; inventory now {inventory.GetWidth()}x{inventory.GetHeight()}");
				}
				else
				{
					Plugin.Log.LogWarning((object)"[CartStorage] inv was null after Awake — could not resize");
				}
			}
		}
	}
	public class CartWarmthZone : MonoBehaviour
	{
		private Fire _fire;

		private Smelter _smelter;

		private FieldInfo _fireBurningField;

		private MethodInfo _smelterGetFuelMethod;

		private readonly List<ParticleSystem> _particleBuffer = new List<ParticleSystem>();

		public float WarmthRadius => CartConfig.WarmthRadius.Value;

		private void Awake()
		{
			_fire = ((Component)this).GetComponentInChildren<Fire>();
			if ((Object)(object)_fire != (Object)null)
			{
				_fireBurningField = typeof(Fire).GetField("m_burning", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			_smelter = ((Component)this).GetComponentInChildren<Smelter>();
			if ((Object)(object)_smelter != (Object)null)
			{
				_smelterGetFuelMethod = typeof(Smelter).GetMethod("GetFuel", BindingFlags.Instance | BindingFlags.NonPublic);
			}
		}

		private void Update()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			if (!CartConfig.ColdProtectionEnabled.Value || !IsBurning())
			{
				return;
			}
			List<Player> allPlayers = Player.GetAllPlayers();
			if (allPlayers == null)
			{
				return;
			}
			Vector3 position = ((Component)this).transform.position;
			foreach (Player item in allPlayers)
			{
				if (!((Object)(object)item == (Object)null) && !(Vector3.Distance(((Component)item).transform.position, position) > WarmthRadius))
				{
					((Character)item).OnNearFire(position);
				}
			}
		}

		private bool IsBurning()
		{
			if ((Object)(object)_fire != (Object)null && _fireBurningField != null)
			{
				return (bool)_fireBurningField.GetValue(_fire);
			}
			if ((Object)(object)_smelter != (Object)null && _smelterGetFuelMethod != null && (float)_smelterGetFuelMethod.Invoke(_smelter, null) > 0f)
			{
				return true;
			}
			((Component)this).GetComponentsInChildren<ParticleSystem>(_particleBuffer);
			for (int i = 0; i < _particleBuffer.Count; i++)
			{
				if (_particleBuffer[i].isPlaying)
				{
					return true;
				}
			}
			return false;
		}
	}
	public class S01_WinterCart : CartTemplate
	{
		private void Start()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)this).transform.Find("Cart_Kiln");
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 localPosition = CartConfig.ParseVector3(CartConfig.S01_KilnOffset.Value, new Vector3(0f, 0.5f, 0f));
				float value = CartConfig.S01_KilnScale.Value;
				val.localPosition = localPosition;
				val.localScale = new Vector3(value, value, value);
			}
		}
	}
}
namespace ValheimExpeditions.Storage
{
	public class ExpeditionsState
	{
		private static readonly object _lock = new object();

		private static Dictionary<int, HashSet<int>> _crossedZones = new Dictionary<int, HashSet<int>>();

		private static bool _loaded;

		private static string FilePath => Path.Combine(Paths.ConfigPath, "Expeditions.json");

		public static void Load()
		{
			lock (_lock)
			{
				_crossedZones.Clear();
				if (!File.Exists(FilePath))
				{
					Save();
					return;
				}
				try
				{
					ParseJson(File.ReadAllText(FilePath));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Failed to load ExpeditionsState: " + ex.Message));
				}
				_loaded = true;
			}
		}

		public static bool HasCrossed(int factionId, int zoneIndex)
		{
			lock (_lock)
			{
				if (!_crossedZones.TryGetValue(factionId, out var value))
				{
					return false;
				}
				return value.Contains(zoneIndex);
			}
		}

		public static void MarkCrossed(int factionId, int zoneIndex)
		{
			lock (_lock)
			{
				if (!_crossedZones.TryGetValue(factionId, out var value))
				{
					value = new HashSet<int>();
					_crossedZones[factionId] = value;
				}
				value.Add(zoneIndex);
				Save();
			}
		}

		public static void Reset()
		{
			lock (_lock)
			{
				_crossedZones.Clear();
				Save();
				Plugin.Log.LogInfo((object)"ExpeditionsState: reset all zone crossing records.");
			}
		}

		private static void Save()
		{
			try
			{
				string contents = SerializeManual();
				File.WriteAllText(FilePath, contents);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to save ExpeditionsState: " + ex.Message));
			}
		}

		private static string SerializeManual()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\n");
			stringBuilder.Append("  \"crossedZones\": [\n");
			int num = 0;
			foreach (KeyValuePair<int, HashSet<int>> item in _crossedZones.OrderBy((KeyValuePair<int, HashSet<int>> k) => k.Key))
			{
				if (num > 0)
				{
					stringBuilder.Append(",\n");
				}
				stringBuilder.Append("    { \"factionId\": ").Append(item.Key.ToString()).Append(", \"zoneIndices\": [");
				int num2 = 0;
				foreach (int item2 in item.Value.OrderBy((int z) => z))
				{
					if (num2 > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(item2.ToString());
					num2++;
				}
				stringBuilder.Append("] }");
				num++;
			}
			stringBuilder.Append("\n  ]\n");
			stringBuilder.Append("}\n");
			return stringBuilder.ToString();
		}

		private static void ParseJson(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return;
			}
			foreach (string item in EnumerateObjects(ExtractArray(json, "crossedZones")))
			{
				int num = ExtractInt(item, "factionId");
				if (num != 0)
				{
					HashSet<int> value = new HashSet<int>(ExtractIntArray(item, "zoneIndices"));
					_crossedZones[num] = value;
				}
			}
		}

		private static string Quote(string s)
		{
			if (s == null)
			{
				return "\"\"";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("X4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}

		private static string ExtractArray(string json, string key)
		{
			int num = json.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return "";
			}
			int num2 = json.IndexOf(':', num);
			if (num2 < 0)
			{
				return "";
			}
			int num3 = json.IndexOf('[', num2);
			if (num3 < 0)
			{
				return "";
			}
			int num4 = 0;
			for (int i = num3; i < json.Length; i++)
			{
				switch (json[i])
				{
				case '[':
					num4++;
					break;
				case ']':
					num4--;
					if (num4 == 0)
					{
						return json.Substring(num3 + 1, i - num3 - 1);
					}
					break;
				}
			}
			return "";
		}

		private static IEnumerable<string> EnumerateObjects(string arrayBody)
		{
			if (string.IsNullOrEmpty(arrayBody))
			{
				yield break;
			}
			int depth = 0;
			int num = -1;
			bool inString = false;
			bool escape = false;
			for (int i = 0; i < arrayBody.Length; i++)
			{
				char c = arrayBody[i];
				if (escape)
				{
					escape = false;
					continue;
				}
				switch (c)
				{
				case '\\':
					escape = true;
					continue;
				case '"':
					inString = !inString;
					continue;
				}
				if (inString)
				{
					continue;
				}
				switch (c)
				{
				case '{':
					if (depth == 0)
					{
						num = i;
					}
					depth++;
					break;
				case '}':
					depth--;
					if (depth == 0 && num >= 0)
					{
						yield return arrayBody.Substring(num, i - num + 1);
						num = -1;
					}
					break;
				}
			}
		}

		private static int ExtractInt(string obj, string key)
		{
			int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return 0;
			}
			int num2 = obj.IndexOf(':', num);
			if (num2 < 0)
			{
				return 0;
			}
			int i;
			for (i = num2 + 1; i < obj.Length && (char.IsDigit(obj[i]) || obj[i] == '-' || char.IsWhiteSpace(obj[i])); i++)
			{
			}
			if (int.TryParse(obj.Substring(num2 + 1, i - num2 - 1).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0;
		}

		private static List<int> ExtractIntArray(string obj, string key)
		{
			List<int> list = new List<int>();
			int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return list;
			}
			int num2 = obj.IndexOf(':', num);
			if (num2 < 0)
			{
				return list;
			}
			int num3 = obj.IndexOf('[', num2);
			if (num3 < 0)
			{
				return list;
			}
			int num4 = 0;
			for (int i = num3; i < obj.Length; i++)
			{
				switch (obj[i])
				{
				case '[':
					num4++;
					continue;
				case ']':
					num4--;
					if (num4 != 0)
					{
						continue;
					}
					break;
				default:
					continue;
				}
				string[] array = obj.Substring(num3 + 1, i - num3 - 1).Split(new char[1] { ',' });
				for (int j = 0; j < array.Length; j++)
				{
					if (int.TryParse(array[j].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
					{
						list.Add(result);
					}
				}
				break;
			}
			return list;
		}
	}
}
namespace ValheimExpeditions.Config
{
	internal class ConfigurationManagerAttributes
	{
		public bool? IsAdminOnly;
	}
	public static class CartConfig
	{
		public static ConfigEntry<bool> DamageProtectionEnabled;

		public static ConfigEntry<int> CartStorageWidth;

		public static ConfigEntry<int> CartStorageHeight;

		public static ConfigEntry<bool> ColdProtectionEnabled;

		public static ConfigEntry<float> WarmthRadius;

		public static ConfigEntry<string> S01_KilnPrefab;

		public static ConfigEntry<string> S01_KilnOffset;

		public static ConfigEntry<float> S01_KilnScale;

		public static void Init(ConfigFile config)
		{
			DamageProtectionEnabled = config.Bind<bool>("Cart", "DamageProtectionEnabled", true, Synced("[Hot-reload] Block all damage to the expedition cart (Character.DamageFloat prefix)."));
			CartStorageWidth = config.Bind<int>("Cart.Storage", "Width", 8, Synced("[New carts only] Inventory grid width for the cart's native storage. Vanilla cart is 6."));
			CartStorageHeight = config.Bind<int>("Cart.Storage", "Height", 4, Synced("[New carts only] Inventory grid height for the cart's native storage. Vanilla cart is 3."));
			ColdProtectionEnabled = config.Bind<bool>("Cart.Warmth", "ColdProtectionEnabled", true, Synced("[Hot-reload] Players within WarmthRadius of a lit kiln-attached CartWarmthZone are kept warm (vanilla OnNearFire). Disable globally here."));
			WarmthRadius = config.Bind<float>("Cart.Warmth", "WarmthRadius", 10f, Synced("[Hot-reload] Meters from the warmth source within which players receive the OnNearFire pulse each frame."));
			S01_KilnPrefab = config.Bind<string>("Cart.S01_Winter", "KilnPrefab", "charcoal_kiln", Synced("[Restart required] Prefab name baked into the cart prefab as the warmth source at registration. Default vanilla Kiln is 'charcoal_kiln'. Changing this only affects newly placed carts after a restart."));
			S01_KilnOffset = config.Bind<string>("Cart.S01_Winter", "KilnOffset", "0,0.5,0", Synced("[New carts only] Local offset of kiln on cart bed. Format: x,y,z"));
			S01_KilnScale = config.Bind<float>("Cart.S01_Winter", "KilnScale", 0.4f, Synced("[New carts only] Uniform scale applied to the spawned kiln. 1.0 = normal size, 0.4 = 40%."));
		}

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

		public static Vector3 ParseVector3(string s, Vector3 fallback)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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)
			//IL_005c: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(s))
			{
				return fallback;
			}
			string[] array = s.Split(new char[1] { ',' });
			if (array.Length != 3)
			{
				return fallback;
			}
			if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return fallback;
			}
			if (!float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return fallback;
			}
			if (!float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return fallback;
			}
			return new Vector3(result, result2, result3);
		}
	}
	public static class ZoneLinesConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> Zone1XPercent;

		public static ConfigEntry<string> Zone1Label;

		public static ConfigEntry<string> Zone1ColorHex;

		public static ConfigEntry<float> Zone2XPercent;

		public static ConfigEntry<string> Zone2Label;

		public static ConfigEntry<string> Zone2ColorHex;

		public static ConfigEntry<float> Zone3XPercent;

		public static ConfigEntry<string> Zone3Label;

		public static ConfigEntry<string> Zone3ColorHex;

		public static ConfigEntry<float> PlaneHeight;

		public static ConfigEntry<float> PlaneThickness;

		public static ConfigEntry<float> PlaneBaseY;

		public static ConfigEntry<float> PlaneAlpha;

		public static ConfigEntry<int> MapPinsPerZone;

		public static ConfigEntry<string> Household1Name;

		public static ConfigEntry<string> Household2Name;

		public static ConfigEntry<string> Household3Name;

		public static ConfigEntry<string> AnnouncementTemplate;

		public static string GetHouseholdName(int factionId)
		{
			return factionId switch
			{
				1 => Household1Name?.Value ?? "Ironforge (Red Team)", 
				2 => Household2Name?.Value ?? "Frosthelm (White Team)", 
				3 => Household3Name?.Value ?? "Stormwind (Blue Team)", 
				_ => "Unknown", 
			};
		}

		public static void Init(ConfigFile config)
		{
			Enabled = config.Bind<bool>("ZoneLines", "Enabled", false, Synced("[Hot-reload] Enable zone-line visuals (planes + minimap pins)."));
			Zone1XPercent = config.Bind<float>("ZoneLines.Zone1", "XPercent", 0.25f, Synced("[Hot-reload] West-east position as 0..1 (0=west edge, 0.5=center, 1=east edge)."));
			Zone1Label = config.Bind<string>("ZoneLines.Zone1", "Label", "ZONE 1", Synced("[Hot-reload] Label shown on minimap."));
			Zone1ColorHex = config.Bind<string>("ZoneLines.Zone1", "ColorHex", "#4488FF", Synced("[Hot-reload] Color in hex format (e.g. #FF0000 for red)."));
			Zone2XPercent = config.Bind<float>("ZoneLines.Zone2", "XPercent", 0.5f, Synced("[Hot-reload] West-east position as 0..1."));
			Zone2Label = config.Bind<string>("ZoneLines.Zone2", "Label", "ZONE 2", Synced("[Hot-reload] Label shown on minimap."));
			Zone2ColorHex = config.Bind<string>("ZoneLines.Zone2", "ColorHex", "#FFAA00", Synced("[Hot-reload] Color in hex format."));
			Zone3XPercent = config.Bind<float>("ZoneLines.Zone3", "XPercent", 0.75f, Synced("[Hot-reload] West-east position as 0..1."));
			Zone3Label = config.Bind<string>("ZoneLines.Zone3", "Label", "ZONE 3 — VICTORY", Synced("[Hot-reload] Label shown on minimap."));
			Zone3ColorHex = config.Bind<string>("ZoneLines.Zone3", "ColorHex", "#FF4444", Synced("[Hot-reload] Color in hex format."));
			PlaneHeight = config.Bind<float>("ZoneLines.Visuals", "PlaneHeight", 500f, Synced("[Hot-reload] Height of the zone-line plane in metres."));
			PlaneThickness = config.Bind<float>("ZoneLines.Visuals", "PlaneThickness", 1f, Synced("[Hot-reload] Thickness of the plane along X axis in metres."));
			PlaneBaseY = config.Bind<float>("ZoneLines.Visuals", "PlaneBaseY", -50f, Synced("[Hot-reload] Base Y position (bottom of plane). Water level is y=30."));
			PlaneAlpha = config.Bind<float>("ZoneLines.Visuals", "PlaneAlpha", 0.35f, Synced("[Hot-reload] Plane opacity (0..1). 0=invisible, 1=opaque."));
			MapPinsPerZone = config.Bind<int>("ZoneLines.Minimap", "PinsPerZone", 25, Synced("[Hot-reload] Number of pins to place along each zone line. More = smoother line, higher cost."));
			Household1Name = config.Bind<string>("ZoneLines.Households", "Household1Name", "Ironforge (Red Team)", Synced("[Hot-reload] Display name for household/faction 1."));
			Household2Name = config.Bind<string>("ZoneLines.Households", "Household2Name", "Frosthelm (White Team)", Synced("[Hot-reload] Display name for household/faction 2."));
			Household3Name = config.Bind<string>("ZoneLines.Households", "Household3Name", "Stormwind (Blue Team)", Synced("[Hot-reload] Display name for household/faction 3."));
			AnnouncementTemplate = config.Bind<string>("ZoneLines.Announcement", "AnnouncementTemplate", "{household} reached {zone}!", Synced("[Hot-reload] Message template for zone-crossing announcements. Use {household} and {zone} as placeholders."));
		}

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