Decompiled source of SunnfjordheimModularv1 v0.0.1

SunnfjordheimModularv1.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using LocalizationManager;
using Microsoft.CodeAnalysis;
using ServerSync;
using SunnfjordheimModularv1;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;
using sunnfjordheimmodular;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SunnfjordheimModularv1")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Azathoth")]
[assembly: AssemblyProduct("SunnfjordheimModularv1")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("0.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		private readonly string <Name>k__BackingField;

		public string Name => <Name>k__BackingField;

		public ExtensionMarkerAttribute(string name)
		{
			<Name>k__BackingField = name;
		}
	}
}
namespace sunnfjordheimmodular
{
	internal static class sunnfjordheimmodularPlugin
	{
		internal enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string ModGUID = "Azathoth.SunnfjordheimModularv1";

		internal static CustomSyncedValue<string> SyncedDefinitions;

		internal static ConfigEntry<Toggle> Enabled;

		internal static ConfigEntry<Toggle> DebugLogging;

		internal static ConfigEntry<float> BossCreditRange;

		internal static ConfigEntry<int> BaseCommandCap;

		internal static ConfigEntry<int> EikthyrCommandCap;

		internal static ConfigEntry<int> ElderCommandCap;

		internal static ConfigEntry<int> BonemassCommandCap;

		internal static ConfigEntry<int> ModerCommandCap;

		internal static ConfigEntry<int> YagluthCommandCap;

		internal static ConfigEntry<int> QueenCommandCap;

		internal static ConfigEntry<int> FaderCommandCap;

		internal static ConfigEntry<string> CustomPersonalCommandKeys;

		internal static ConfigEntry<float> CommandRadius;

		internal static ConfigEntry<float> MinimumCommandCenterDistance;

		internal static ConfigEntry<int> BaseSettlementMilitaryCap;

		internal static ConfigEntry<int> BaseSettlementBarracksLimit;

		internal static ConfigEntry<int> MilitaryBannerCommandBonus;

		internal static ConfigEntry<int> LogisticsBannerBarracksBonus;

		internal static ConfigEntry<Toggle> DefaultGuildManagement;

		internal static ConfigEntry<string> CommandBannerSourcePrefab;

		internal static ConfigEntry<string> MilitaryBannerSourcePrefab;

		internal static ConfigEntry<string> LogisticsBannerSourcePrefab;

		internal static ConfigEntry<string> GenericBarracksSourcePrefab;

		internal static ConfigEntry<string> CommandBannerBuildCosts;

		internal static ConfigEntry<string> MilitaryBannerBuildCosts;

		internal static ConfigEntry<string> LogisticsBannerBuildCosts;

		internal static ConfigEntry<string> GenericBarracksBuildCosts;

		internal static ConfigEntry<string> GeneralBarracksBuildCosts;

		internal static ConfigEntry<float> BarracksResupplyRadius;

		internal static ConfigEntry<float> ArmoryLinkRadius;

		internal static ConfigEntry<Toggle> TgwIntegration;

		internal static ConfigEntry<Toggle> TgwSkipDecay;

		internal static ConfigEntry<Toggle> AutoPortraits;

		internal static ConfigEntry<int> PortraitResolution;

		private static FileSystemWatcher _definitionWatcher;

		private static bool _initialized;

		private static SunnfjordheimModularv1Plugin _hostPlugin;

		internal static ManualLogSource Log => SunnfjordheimModularv1Plugin.SunnfjordheimModularv1Logger;

		internal static ConfigSync ConfigSync { get; private set; }

		internal static bool IsEnabled => Enabled != null && Enabled.Value == Toggle.On;

		internal static bool IsDebug => DebugLogging != null && DebugLogging.Value == Toggle.On;

		internal static void Initialize(SunnfjordheimModularv1Plugin plugin)
		{
			if (_initialized || (Object)(object)plugin == (Object)null)
			{
				return;
			}
			_initialized = true;
			_hostPlugin = plugin;
			ConfigSync = SunnfjordheimModularv1Plugin.RTSConfigSync;
			BindConfiguration(((BaseUnityPlugin)plugin).Config);
			RTSDefinitionRegistry.EnsureDefaultDefinitionsFile();
			if (ConfigSync != null)
			{
				SyncedDefinitions = new CustomSyncedValue<string>(ConfigSync, "RTSDefinitions", "");
				SyncedDefinitions.ValueChanged += delegate
				{
					try
					{
						if (!ConfigSync.IsSourceOfTruth)
						{
							RTSDefinitionRegistry.ApplySyncedText(SyncedDefinitions.Value ?? "");
						}
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("[RTS] Error aplicando definiciones sincronizadas: " + ex.Message));
					}
				};
			}
			RTSDefinitionRegistry.LoadFromDisk();
			RTSRuntimeHost rTSRuntimeHost = ((Component)plugin).gameObject.GetComponent<RTSRuntimeHost>();
			if ((Object)(object)rTSRuntimeHost == (Object)null)
			{
				rTSRuntimeHost = ((Component)plugin).gameObject.AddComponent<RTSRuntimeHost>();
			}
			Object.DontDestroyOnLoad((Object)(object)((Component)rTSRuntimeHost).gameObject);
			SetupDefinitionWatcher();
			Log.LogInfo((object)("[RTS ADVANCED] Inicializado sobre la base limpia. " + RTSDefinitionRegistry.UnitCount + " unidades / " + RTSDefinitionRegistry.BarracksCount + " perfiles de barracón."));
		}

		internal static void Shutdown()
		{
			try
			{
				_definitionWatcher?.Dispose();
			}
			catch
			{
			}
			_definitionWatcher = null;
		}

		private static void BindConfiguration(ConfigFile config)
		{
			Enabled = Bind(config, "RTS 1 - General", "Enabled", Toggle.On, "Activa o desactiva el sistema RTS.");
			DebugLogging = Bind(config, "RTS 1 - General", "Debug Logging", Toggle.Off, "Muestra diagnóstico adicional del RTS.", synchronized: false);
			BaseCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "00 - Base", 5, "Capacidad personal antes de derrotar jefes.");
			EikthyrCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "01 - Eikthyr", 8, "Capacidad personal tras crédito personal de Eikthyr.");
			ElderCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "02 - Elder", 12, "Capacidad personal tras crédito personal de Elder.");
			BonemassCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "03 - Bonemass", 16, "Capacidad personal tras crédito personal de Bonemass.");
			ModerCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "04 - Moder", 20, "Capacidad personal tras crédito personal de Moder.");
			YagluthCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "05 - Yagluth", 25, "Capacidad personal tras crédito personal de Yagluth.");
			QueenCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "06 - Queen", 30, "Capacidad personal tras crédito personal de Queen.");
			FaderCommandCap = Bind(config, "RTS 2 - Personal Command Capacity", "07 - Fader", 35, "Capacidad personal tras crédito personal de Fader.");
			CustomPersonalCommandKeys = Bind(config, "RTS 2 - Personal Command Capacity", "08 - Custom Personal Keys", "", "Overrides opcionales formato key:capacidad,key2:capacidad. También revisa VV_PrivateKeys.");
			BossCreditRange = Bind(config, "RTS 2 - Personal Command Capacity", "Boss Credit Range", 100f, "Radio para recibir crédito personal por muerte de jefe.");
			CommandRadius = Bind(config, "RTS 3 - Settlement Command", "Command Banner Radius", 200f, "Radio del asentamiento creado por el Estandarte de Mando.");
			MinimumCommandCenterDistance = Bind(config, "RTS 3 - Settlement Command", "Minimum Same-Affiliation Center Distance", 350f, "Distancia mínima entre centros de la misma afiliación.");
			BaseSettlementMilitaryCap = Bind(config, "RTS 3 - Settlement Command", "Base Military Capacity", 30, "Capacidad militar compartida inicial de la base.");
			BaseSettlementBarracksLimit = Bind(config, "RTS 3 - Settlement Command", "Base Barracks Limit", 2, "Cantidad inicial máxima de barracones registrados.");
			MilitaryBannerCommandBonus = Bind(config, "RTS 3 - Settlement Command", "Military Banner Command Bonus", 20, "Capacidad militar extra del Estandarte Militar.");
			LogisticsBannerBarracksBonus = Bind(config, "RTS 3 - Settlement Command", "Logistics Banner Barracks Bonus", 1, "Barracón adicional permitido por Estandarte Logístico.");
			DefaultGuildManagement = Bind(config, "RTS 3 - Settlement Command", "Guild Management By Default", Toggle.Off, "Permite gestión automática por guild cuando TameGuildWars puede resolverlo.");
			CommandBannerSourcePrefab = Bind(config, "RTS 4 - Banner Clones", "Command Banner Source", "piece_banner01", "Prefab fuente clonado para RTS_CommandBanner.");
			MilitaryBannerSourcePrefab = Bind(config, "RTS 4 - Banner Clones", "Military Banner Source", "piece_banner02", "Prefab fuente clonado para RTS_MilitaryBanner.");
			LogisticsBannerSourcePrefab = Bind(config, "RTS 4 - Banner Clones", "Logistics Banner Source", "piece_banner03", "Prefab fuente clonado para RTS_LogisticsBanner.");
			GenericBarracksSourcePrefab = Bind(config, "RTS 4 - Banner Clones", "Generic Barracks Source", "piece_banner04", "Prefab fuente clonado para RTS_GenericBarracks de prueba/configurable.");
			CommandBannerBuildCosts = Bind(config, "RTS 4 - Banner Clones", "Command Banner Costs", "Wood:20,Coins:50", "Costes de construcción Prefab:Amount,Prefab:Amount.");
			MilitaryBannerBuildCosts = Bind(config, "RTS 4 - Banner Clones", "Military Banner Costs", "FineWood:20,Iron:5,Coins:100", "Costes del Estandarte Militar.");
			LogisticsBannerBuildCosts = Bind(config, "RTS 4 - Banner Clones", "Logistics Banner Costs", "Wood:30,Iron:5", "Costes del Estandarte Logístico.");
			GenericBarracksBuildCosts = Bind(config, "RTS 4 - Banner Clones", "Generic Barracks Costs", "Wood:40,Stone:10", "Costes del barracón configurable de prueba.");
			GeneralBarracksBuildCosts = Bind(config, "RTS 4 - Banner Clones", "General Barracks Costs", "Wood:40,Stone:10", "Costes del Barracón General que recluta RTS_HumanSettler.");
			BarracksResupplyRadius = Bind(config, "RTS 5 - Barracks & Logistics", "Barracks Resupply Radius", 12f, "Distancia para reabastecer munición desde el barracón/armería.");
			ArmoryLinkRadius = Bind(config, "RTS 5 - Barracks & Logistics", "Armory Link Radius", 10f, "Radio para vincular un cofre cercano como armería.");
			TgwIntegration = Bind(config, "RTS 6 - Compatibility", "TameGuildWars Integration", Toggle.On, "Usa TameGuildWars por reflexión si está instalado.");
			TgwSkipDecay = Bind(config, "RTS 6 - Compatibility", "TameGuildWars Skip Decay", Toggle.On, "Evita pérdida de obediencia de las tropas RTS cuando TGW está disponible.");
			AutoPortraits = Bind(config, "RTS 7 - UI", "Automatic 3D Portraits", Toggle.On, "Genera retratos 3D cacheados cuando no hay icono custom.", synchronized: false);
			PortraitResolution = Bind(config, "RTS 7 - UI", "Portrait Resolution", 256, "Resolución de retratos 3D (64-512).", synchronized: false);
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile config, string group, string name, T value, string description, bool synchronized = true)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description + (synchronized ? " [Synced with Server]" : " [Local]"), (AcceptableValueBase)null, Array.Empty<object>());
			ConfigEntry<T> val2 = config.Bind<T>(group, name, value, val);
			if (ConfigSync != null)
			{
				SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
				syncedConfigEntry.SynchronizedConfig = synchronized;
			}
			return val2;
		}

		private static void SetupDefinitionWatcher()
		{
			try
			{
				string definitionsPath = RTSDefinitionRegistry.DefinitionsPath;
				string directoryName = Path.GetDirectoryName(definitionsPath);
				string fileName = Path.GetFileName(definitionsPath);
				if (!string.IsNullOrWhiteSpace(directoryName) && !string.IsNullOrWhiteSpace(fileName))
				{
					_definitionWatcher = new FileSystemWatcher(directoryName, fileName)
					{
						NotifyFilter = (NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
						IncludeSubdirectories = false,
						SynchronizingObject = ThreadingHelper.SynchronizingObject,
						EnableRaisingEvents = true
					};
					_definitionWatcher.Changed += delegate
					{
						RTSDefinitionRegistry.RequestReload();
					};
					_definitionWatcher.Created += delegate
					{
						RTSDefinitionRegistry.RequestReload();
					};
					_definitionWatcher.Renamed += delegate
					{
						RTSDefinitionRegistry.RequestReload();
					};
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[RTS] Watcher de definiciones desactivado: " + ex.Message));
			}
		}

		internal static void Debug(string message)
		{
			if (IsDebug)
			{
				Log.LogInfo((object)("[RTS] " + message));
			}
		}
	}
	internal sealed class RTSCost
	{
		internal string Prefab = "";

		internal int Amount;
	}
	internal enum RTSUnitMode
	{
		ReadyPrefab,
		HumanoidRecruit,
		PlayerCloneRecruit
	}
	internal sealed class RTSUnitDefinition
	{
		internal string Id = "";

		internal bool Enabled = true;

		internal RTSUnitMode Mode = RTSUnitMode.ReadyPrefab;

		internal string Prefab = "";

		internal string DisplayName = "";

		internal string DisplayNameEnglish = "";

		internal int CommandCost = 1;

		internal float RecruitTime = 30f;

		internal readonly List<RTSCost> RecruitCosts = new List<RTSCost>();

		internal readonly List<RTSCost> Loadout = new List<RTSCost>();

		internal bool RequireArmoryLoadout = true;

		internal bool DropEquipmentOnDeath = true;

		internal bool CustomizeAppearance = true;

		internal readonly List<string> RecruitNames = new List<string>();

		internal readonly List<string> HairOptions = new List<string>();

		internal readonly List<string> BeardOptions = new List<string>();

		internal string AmmoItem = "";

		internal int AmmoCapacity;

		internal int AmmoPerAttack = 1;

		internal float PortraitZoom = 1f;

		internal float PortraitYOffset;

		internal float PortraitYaw;

		internal string LocalizedDisplayName => RTSUtil.T(DisplayName, string.IsNullOrWhiteSpace(DisplayNameEnglish) ? DisplayName : DisplayNameEnglish);
	}
	internal sealed class RTSBarracksDefinition
	{
		internal string Id = "";

		internal bool Enabled = true;

		internal string Prefab = "";

		internal bool ProfileOnly;

		internal string DisplayName = "";

		internal string DisplayNameEnglish = "";

		internal bool AllowRename = true;

		internal bool AllowProfileChange;

		internal readonly List<string> SelectableProfiles = new List<string>();

		internal readonly List<RTSCost> BuildCosts = new List<RTSCost>();

		internal int LocalCommandLimit = 10;

		internal int QueueLimit = 3;

		internal int MaxPerSettlement = 0;

		internal Vector3 SpawnOffset = new Vector3(0f, 0f, 3f);

		internal readonly List<string> UnitIds = new List<string>();

		internal string LocalizedDisplayName => RTSUtil.T(DisplayName, string.IsNullOrWhiteSpace(DisplayNameEnglish) ? DisplayName : DisplayNameEnglish);
	}
	internal static class RTSDefinitionRegistry
	{
		private static readonly Dictionary<string, RTSUnitDefinition> Units = new Dictionary<string, RTSUnitDefinition>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, RTSBarracksDefinition> Barracks = new Dictionary<string, RTSBarracksDefinition>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, RTSBarracksDefinition> BarracksByPrefab = new Dictionary<string, RTSBarracksDefinition>(StringComparer.OrdinalIgnoreCase);

		private static bool _reloadRequested;

		private static float _reloadNotBefore;

		internal static string DefinitionsPath => Path.Combine(Paths.ConfigPath, "Azathoth.SunnfjordheimModularv1.RTSDefinitions.cfg");

		internal static int UnitCount => Units.Count;

		internal static int BarracksCount => Barracks.Count;

		internal static IEnumerable<RTSBarracksDefinition> AllBarracks => Barracks.Values;

		internal static IEnumerable<RTSBarracksDefinition> AllPhysicalBarracks => Barracks.Values.Where((RTSBarracksDefinition x) => x != null && !x.ProfileOnly && !string.IsNullOrWhiteSpace(x.Prefab));

		internal static string DefaultDefinitionsText => "# =============================================================\n# SunnfjordheimModularv1 RTS PRO 1.2.2 - DEFINITIONS\n# =============================================================\n# The ID is the text after Unit. or Barracks. and is INTERNAL to RTS.\n# Prefab is the REAL prefab registered by Valheim/another mod/MonsterDB.\n# Costs/List syntax: Prefab:Amount,Prefab:Amount\n# SpawnOffset: X,Y,Z local to the physical barracks.\n# DisplayName is the Spanish/fallback name; DisplayNameEnglish is shown in English.\n#\n# UNIT MODES\n#   ReadyPrefab       = spawn the creature exactly as it already exists.\n#   HumanoidRecruit   = spawn an existing Humanoid prefab and equip Loadout.\n#   PlayerCloneRecruit = spawn RTS_HumanSettler, a registered clone of Valheim Player\n#                        with the real Player body/VisEquipment plus NPC AI.\n#\n# BARRACKS\n#   A normal [Barracks.X] with Prefab is a FIXED physical barracks.\n#   A ProfileOnly barracks has no physical prefab and can be selected by a\n#   generic host through SelectableProfiles.\n# =============================================================\n\n\n# Ready general human resident. No external humanoid prefab is required.\n[Unit.GeneralHuman]\nEnabled = true\nMode = PlayerCloneRecruit\nPrefab = RTS_HumanSettler\nDisplayName = Colono Humano\nDisplayNameEnglish = Human Settler\nCommandCost = 1\nRecruitTime = 20\nRecruitCosts = Coins:25\nLoadout =\nRequireArmoryLoadout = false\nDropEquipmentOnDeath = true\nCustomizeAppearance = true\nNames = Erik,Bjorn,Leif,Ulf,Runa,Freya,Astrid,Sigrid,Ingrid,Harald,Einar,Liv\nHairOptions =\nBeardOptions =\nAmmoItem =\nAmmoCapacity = 0\nAmmoPerAttack = 0\n\n[Unit.CloneTrooper]\nEnabled = true\nMode = ReadyPrefab\nPrefab = STA_ClonTrooper\nDisplayName = Soldado Clon\nDisplayNameEnglish = Clone Trooper\nCommandCost = 1\nRecruitTime = 30\nRecruitCosts = Coins:50,Iron:2\nAmmoItem = ArrowWood\nAmmoCapacity = 30\nAmmoPerAttack = 1\nPortraitZoom = 1.00\nPortraitYOffset = 0.00\nPortraitYaw = 0\n\n[Unit.EwokWarrior]\nEnabled = true\nMode = ReadyPrefab\nPrefab = STA_Ewook\nDisplayName = Guerrero Ewok\nDisplayNameEnglish = Ewok Warrior\nCommandCost = 1\nRecruitTime = 20\nRecruitCosts = Coins:25,Wood:10\nAmmoItem =\nAmmoCapacity = 0\nAmmoPerAttack = 0\nPortraitZoom = 1.00\nPortraitYOffset = 0.00\nPortraitYaw = 0\n\n# Example humanoid recruit. Replace the prefab with a real Humanoid prefab.\n[Unit.HumanGuard]\nEnabled = false\nMode = HumanoidRecruit\nPrefab = Dverger\nDisplayName = Guardia Equipable\nDisplayNameEnglish = Equipable Guard\nCommandCost = 1\nRecruitTime = 35\nRecruitCosts = Coins:60\nLoadout = SwordIron:1,ShieldBanded:1,HelmetIron:1,ArmorIronChest:1\nRequireArmoryLoadout = true\nDropEquipmentOnDeath = true\nCustomizeAppearance = true\nNames = Erik,Bjorn,Leif,Ulf,Runa,Freya,Astrid,Sigrid\n# Leave HairOptions/BeardOptions blank to auto-discover loaded vanilla/custom options.\nHairOptions =\nBeardOptions =\nAmmoItem =\nAmmoCapacity = 0\nAmmoPerAttack = 0\n\n# FIXED BARRACKS: every instance of the physical prefab uses this profile.\n\n# GENERAL BARRACKS: ready immediately for Hearthfolk-style human residents.\n[Barracks.GeneralHuman]\nEnabled = true\nPrefab = RTS_GeneralBarracks\nProfileOnly = false\nDisplayName = Barracón General\nDisplayNameEnglish = General Barracks\nAllowRename = true\nAllowProfileChange = false\nBuildCosts = Wood:40,Stone:10\nUnits = GeneralHuman\nLocalCommandLimit = 12\nQueueLimit = 4\nMaxPerSettlement = 2\nSpawnOffset = 0,0,4\n\n[Barracks.CloneBarracks]\nEnabled = true\nPrefab = STA_CloneBarracks\nProfileOnly = false\nDisplayName = Cuartel de la Republica\nDisplayNameEnglish = Republic Barracks\nAllowRename = true\nAllowProfileChange = false\nBuildCosts = Wood:50,Stone:20,Iron:5\nUnits = CloneTrooper\nLocalCommandLimit = 10\nQueueLimit = 3\nMaxPerSettlement = 2\nSpawnOffset = 0,0,4\n\n[Barracks.EwokVillage]\nEnabled = true\nPrefab = STA_EwokHut\nProfileOnly = false\nDisplayName = Aldea Ewok\nDisplayNameEnglish = Ewok Village\nAllowRename = true\nAllowProfileChange = false\nBuildCosts = Wood:40,RoundLog:10\nUnits = EwokWarrior\nLocalCommandLimit = 6\nQueueLimit = 2\nMaxPerSettlement = 2\nSpawnOffset = 0,0,3\n\n# PROFILE-ONLY examples for a GENERIC physical barracks.\n\n[Barracks.ProfileHumans]\nEnabled = true\nProfileOnly = true\nDisplayName = Perfil Humanos\nDisplayNameEnglish = Human Profile\nUnits = GeneralHuman\nLocalCommandLimit = 12\nQueueLimit = 4\nMaxPerSettlement = 2\nSpawnOffset = 0,0,4\n\n[Barracks.ProfileRepublic]\nEnabled = true\nProfileOnly = true\nDisplayName = Perfil Republica\nDisplayNameEnglish = Republic Profile\nUnits = CloneTrooper\nLocalCommandLimit = 10\nQueueLimit = 3\nMaxPerSettlement = 2\nSpawnOffset = 0,0,4\n\n[Barracks.ProfileEndor]\nEnabled = true\nProfileOnly = true\nDisplayName = Perfil Endor\nDisplayNameEnglish = Endor Profile\nUnits = EwokWarrior\nLocalCommandLimit = 6\nQueueLimit = 2\nMaxPerSettlement = 2\nSpawnOffset = 0,0,3\n\n# GENERIC HOST de prueba: el mod clona piece_banner04 como RTS_GenericBarracks.\n# Puedes cambiarlo por otro Piece real cuando quieras.\n# On first E interaction the commander chooses one of SelectableProfiles.\n[Barracks.GenericHost]\nEnabled = true\nPrefab = RTS_GenericBarracks\nProfileOnly = false\nDisplayName = Barracon Configurable\nDisplayNameEnglish = Configurable Barracks\nAllowRename = true\nAllowProfileChange = true\nSelectableProfiles = ProfileHumans,ProfileRepublic,ProfileEndor\nBuildCosts = Wood:60,Stone:30\nUnits =\nLocalCommandLimit = 1\nQueueLimit = 1\nMaxPerSettlement = 0\nSpawnOffset = 0,0,4\n";

		internal static void EnsureDefaultDefinitionsFile()
		{
			if (File.Exists(DefinitionsPath))
			{
				return;
			}
			try
			{
				File.WriteAllText(DefinitionsPath, DefaultDefinitionsText);
			}
			catch (Exception ex)
			{
				sunnfjordheimmodularPlugin.Log.LogError((object)("Could not create RTS definitions file: " + ex.Message));
			}
		}

		internal static void RequestReload()
		{
			_reloadRequested = true;
			_reloadNotBefore = Time.unscaledTime + 0.5f;
		}

		internal static void TickReload()
		{
			if (_reloadRequested && !(Time.unscaledTime < _reloadNotBefore))
			{
				_reloadRequested = false;
				if (sunnfjordheimmodularPlugin.ConfigSync.IsSourceOfTruth)
				{
					LoadFromDisk();
				}
				else
				{
					ApplySyncedText(sunnfjordheimmodularPlugin.SyncedDefinitions?.Value ?? "");
				}
				RTSPortraitCache.Clear();
				RTSPieceCostOverrides.ClearCache();
				sunnfjordheimmodularPlugin.Log.LogInfo((object)$"RTS definitions reloaded: {UnitCount} units / {BarracksCount} barracks profiles/hosts.");
			}
		}

		internal static void LoadFromDisk()
		{
			if (File.Exists(DefinitionsPath))
			{
				string text = File.ReadAllText(DefinitionsPath);
				ApplySyncedText(text);
				if (sunnfjordheimmodularPlugin.SyncedDefinitions != null && sunnfjordheimmodularPlugin.ConfigSync.IsSourceOfTruth)
				{
					sunnfjordheimmodularPlugin.SyncedDefinitions.Value = text;
				}
			}
		}

		internal static void ApplySyncedText(string text)
		{
			Units.Clear();
			Barracks.Clear();
			BarracksByPrefab.Clear();
			Dictionary<string, Dictionary<string, string>> dictionary = ParseIniText(text ?? "");
			foreach (KeyValuePair<string, Dictionary<string, string>> item in dictionary)
			{
				string key = item.Key;
				Dictionary<string, string> value = item.Value;
				if (key.StartsWith("Unit.", StringComparison.OrdinalIgnoreCase))
				{
					string text2 = key.Substring("Unit.".Length).Trim();
					RTSUnitDefinition rTSUnitDefinition = ParseUnit(text2, value);
					if (rTSUnitDefinition != null && rTSUnitDefinition.Enabled && !string.IsNullOrWhiteSpace(rTSUnitDefinition.Prefab))
					{
						Units[text2] = rTSUnitDefinition;
					}
				}
				else if (key.StartsWith("Barracks.", StringComparison.OrdinalIgnoreCase))
				{
					string text3 = key.Substring("Barracks.".Length).Trim();
					RTSBarracksDefinition rTSBarracksDefinition = ParseBarracks(text3, value);
					if (rTSBarracksDefinition != null && rTSBarracksDefinition.Enabled)
					{
						Barracks[text3] = rTSBarracksDefinition;
					}
				}
			}
			EnsureBuiltInGeneralHumanDefinitions();
			foreach (RTSBarracksDefinition value3 in Barracks.Values)
			{
				value3.UnitIds.RemoveAll((string id) => !Units.ContainsKey(id));
				value3.SelectableProfiles.RemoveAll((string id) => !Barracks.TryGetValue(id, out RTSBarracksDefinition value2) || value2 == null);
				if (!value3.ProfileOnly && !string.IsNullOrWhiteSpace(value3.Prefab))
				{
					string text4 = RTSUtil.CleanPrefabName(value3.Prefab);
					if (!BarracksByPrefab.ContainsKey(text4))
					{
						BarracksByPrefab[text4] = value3;
						continue;
					}
					sunnfjordheimmodularPlugin.Log.LogWarning((object)("RTSDefinitions: more than one physical Barracks section uses prefab '" + text4 + "'. Only '" + BarracksByPrefab[text4].Id + "' will be attached automatically."));
				}
			}
		}

		private static void EnsureBuiltInGeneralHumanDefinitions()
		{
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			if (!Units.ContainsKey("GeneralHuman"))
			{
				RTSUnitDefinition rTSUnitDefinition = new RTSUnitDefinition
				{
					Id = "GeneralHuman",
					Enabled = true,
					Mode = RTSUnitMode.PlayerCloneRecruit,
					Prefab = "RTS_HumanSettler",
					DisplayName = "Colono Humano",
					DisplayNameEnglish = "Human Settler",
					CommandCost = 1,
					RecruitTime = 20f,
					RequireArmoryLoadout = false,
					DropEquipmentOnDeath = true,
					CustomizeAppearance = true
				};
				rTSUnitDefinition.RecruitCosts.Add(new RTSCost
				{
					Prefab = "Coins",
					Amount = 25
				});
				string[] array = new string[12]
				{
					"Erik", "Bjorn", "Leif", "Ulf", "Runa", "Freya", "Astrid", "Sigrid", "Ingrid", "Harald",
					"Einar", "Liv"
				};
				foreach (string item in array)
				{
					rTSUnitDefinition.RecruitNames.Add(item);
				}
				Units[rTSUnitDefinition.Id] = rTSUnitDefinition;
			}
			if (!Barracks.ContainsKey("GeneralHuman"))
			{
				RTSBarracksDefinition rTSBarracksDefinition = new RTSBarracksDefinition
				{
					Id = "GeneralHuman",
					Enabled = true,
					Prefab = "RTS_GeneralBarracks",
					ProfileOnly = false,
					DisplayName = "Barracón General",
					DisplayNameEnglish = "General Barracks",
					AllowRename = true,
					AllowProfileChange = false,
					LocalCommandLimit = 12,
					QueueLimit = 4,
					MaxPerSettlement = 2,
					SpawnOffset = new Vector3(0f, 0f, 4f)
				};
				rTSBarracksDefinition.UnitIds.Add("GeneralHuman");
				Barracks[rTSBarracksDefinition.Id] = rTSBarracksDefinition;
			}
			if (!Barracks.ContainsKey("ProfileHumans"))
			{
				RTSBarracksDefinition rTSBarracksDefinition2 = new RTSBarracksDefinition
				{
					Id = "ProfileHumans",
					Enabled = true,
					Prefab = "",
					ProfileOnly = true,
					DisplayName = "Perfil Humanos",
					DisplayNameEnglish = "Human Profile",
					AllowRename = true,
					AllowProfileChange = false,
					LocalCommandLimit = 12,
					QueueLimit = 4,
					MaxPerSettlement = 2,
					SpawnOffset = new Vector3(0f, 0f, 4f)
				};
				rTSBarracksDefinition2.UnitIds.Add("GeneralHuman");
				Barracks[rTSBarracksDefinition2.Id] = rTSBarracksDefinition2;
			}
			if (Barracks.TryGetValue("GenericHost", out RTSBarracksDefinition value) && value != null && !value.SelectableProfiles.Contains<string>("ProfileHumans", StringComparer.OrdinalIgnoreCase))
			{
				value.SelectableProfiles.Insert(0, "ProfileHumans");
			}
		}

		internal static bool TryGetUnit(string id, out RTSUnitDefinition definition)
		{
			return Units.TryGetValue(id ?? "", out definition);
		}

		internal static bool TryGetBarracks(string id, out RTSBarracksDefinition definition)
		{
			return Barracks.TryGetValue(id ?? "", out definition);
		}

		internal static bool TryGetBarracksByPrefab(string prefabName, out RTSBarracksDefinition definition)
		{
			return BarracksByPrefab.TryGetValue(RTSUtil.CleanPrefabName(prefabName), out definition);
		}

		internal static RTSBarracksDefinition ResolveProfile(RTSBarracksDefinition host, string selectedProfileId)
		{
			if (host == null)
			{
				return null;
			}
			if (host.SelectableProfiles.Count == 0)
			{
				return host;
			}
			if (string.IsNullOrWhiteSpace(selectedProfileId))
			{
				return null;
			}
			if (!host.SelectableProfiles.Any((string x) => string.Equals(x, selectedProfileId, StringComparison.OrdinalIgnoreCase)))
			{
				return null;
			}
			RTSBarracksDefinition definition;
			return TryGetBarracks(selectedProfileId, out definition) ? definition : null;
		}

		internal static List<RTSBarracksDefinition> GetSelectableProfiles(RTSBarracksDefinition host)
		{
			List<RTSBarracksDefinition> list = new List<RTSBarracksDefinition>();
			if (host == null)
			{
				return list;
			}
			foreach (string selectableProfile in host.SelectableProfiles)
			{
				if (TryGetBarracks(selectableProfile, out RTSBarracksDefinition definition) && definition != null)
				{
					list.Add(definition);
				}
			}
			return list;
		}

		internal static List<RTSUnitDefinition> GetUnitsFor(RTSBarracksDefinition barracks)
		{
			List<RTSUnitDefinition> list = new List<RTSUnitDefinition>();
			if (barracks == null)
			{
				return list;
			}
			foreach (string unitId in barracks.UnitIds)
			{
				if (TryGetUnit(unitId, out RTSUnitDefinition definition))
				{
					list.Add(definition);
				}
			}
			return list;
		}

		private static RTSUnitDefinition ParseUnit(string id, Dictionary<string, string> v)
		{
			if (string.IsNullOrWhiteSpace(id))
			{
				return null;
			}
			string a = Get(v, "Mode", "ReadyPrefab");
			RTSUnitMode mode = (string.Equals(a, "PlayerCloneRecruit", StringComparison.OrdinalIgnoreCase) ? RTSUnitMode.PlayerCloneRecruit : (string.Equals(a, "HumanoidRecruit", StringComparison.OrdinalIgnoreCase) ? RTSUnitMode.HumanoidRecruit : RTSUnitMode.ReadyPrefab));
			RTSUnitDefinition rTSUnitDefinition = new RTSUnitDefinition
			{
				Id = id,
				Enabled = GetBool(v, "Enabled", fallback: true),
				Mode = mode,
				Prefab = Get(v, "Prefab", ""),
				DisplayName = Get(v, "DisplayName", id),
				DisplayNameEnglish = Get(v, "DisplayNameEnglish", BuiltInUnitEnglishName(id, Get(v, "DisplayName", id))),
				CommandCost = Mathf.Max(1, GetInt(v, "CommandCost", 1)),
				RecruitTime = Mathf.Max(0f, GetFloat(v, "RecruitTime", 30f)),
				RequireArmoryLoadout = GetBool(v, "RequireArmoryLoadout", fallback: true),
				DropEquipmentOnDeath = GetBool(v, "DropEquipmentOnDeath", fallback: true),
				CustomizeAppearance = GetBool(v, "CustomizeAppearance", fallback: true),
				AmmoItem = Get(v, "AmmoItem", ""),
				AmmoCapacity = Mathf.Max(0, GetInt(v, "AmmoCapacity", 0)),
				AmmoPerAttack = Mathf.Max(0, GetInt(v, "AmmoPerAttack", 1)),
				PortraitZoom = Mathf.Clamp(GetFloat(v, "PortraitZoom", 1f), 0.25f, 4f),
				PortraitYOffset = GetFloat(v, "PortraitYOffset", 0f),
				PortraitYaw = GetFloat(v, "PortraitYaw", 0f)
			};
			rTSUnitDefinition.RecruitCosts.AddRange(ParseCosts(Get(v, "RecruitCosts", "")));
			rTSUnitDefinition.Loadout.AddRange(ParseCosts(Get(v, "Loadout", "")));
			foreach (string item in SplitList(Get(v, "Names", "")))
			{
				if (!rTSUnitDefinition.RecruitNames.Contains<string>(item, StringComparer.OrdinalIgnoreCase))
				{
					rTSUnitDefinition.RecruitNames.Add(item);
				}
			}
			foreach (string item2 in SplitList(Get(v, "HairOptions", "")))
			{
				if (!rTSUnitDefinition.HairOptions.Contains<string>(item2, StringComparer.OrdinalIgnoreCase))
				{
					rTSUnitDefinition.HairOptions.Add(item2);
				}
			}
			foreach (string item3 in SplitList(Get(v, "BeardOptions", "")))
			{
				if (!rTSUnitDefinition.BeardOptions.Contains<string>(item3, StringComparer.OrdinalIgnoreCase))
				{
					rTSUnitDefinition.BeardOptions.Add(item3);
				}
			}
			if (string.IsNullOrWhiteSpace(rTSUnitDefinition.AmmoItem) || rTSUnitDefinition.AmmoCapacity <= 0)
			{
				rTSUnitDefinition.AmmoItem = "";
				rTSUnitDefinition.AmmoCapacity = 0;
				rTSUnitDefinition.AmmoPerAttack = 0;
			}
			return rTSUnitDefinition;
		}

		private static RTSBarracksDefinition ParseBarracks(string id, Dictionary<string, string> v)
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(id))
			{
				return null;
			}
			RTSBarracksDefinition rTSBarracksDefinition = new RTSBarracksDefinition
			{
				Id = id,
				Enabled = GetBool(v, "Enabled", fallback: true),
				Prefab = Get(v, "Prefab", ""),
				ProfileOnly = GetBool(v, "ProfileOnly", fallback: false),
				DisplayName = Get(v, "DisplayName", id),
				DisplayNameEnglish = Get(v, "DisplayNameEnglish", BuiltInBarracksEnglishName(id, Get(v, "DisplayName", id))),
				AllowRename = GetBool(v, "AllowRename", fallback: true),
				AllowProfileChange = GetBool(v, "AllowProfileChange", fallback: false),
				LocalCommandLimit = Mathf.Max(1, GetInt(v, "LocalCommandLimit", 10)),
				QueueLimit = Mathf.Max(1, GetInt(v, "QueueLimit", 3)),
				MaxPerSettlement = Mathf.Max(0, GetInt(v, "MaxPerSettlement", 0)),
				SpawnOffset = ParseVector3(Get(v, "SpawnOffset", "0,0,3"), new Vector3(0f, 0f, 3f))
			};
			rTSBarracksDefinition.BuildCosts.AddRange(ParseCosts(Get(v, "BuildCosts", "")));
			foreach (string item in SplitList(Get(v, "Units", "")))
			{
				if (!rTSBarracksDefinition.UnitIds.Contains<string>(item, StringComparer.OrdinalIgnoreCase))
				{
					rTSBarracksDefinition.UnitIds.Add(item);
				}
			}
			foreach (string item2 in SplitList(Get(v, "SelectableProfiles", "")))
			{
				if (!rTSBarracksDefinition.SelectableProfiles.Contains<string>(item2, StringComparer.OrdinalIgnoreCase))
				{
					rTSBarracksDefinition.SelectableProfiles.Add(item2);
				}
			}
			if (rTSBarracksDefinition.ProfileOnly)
			{
				rTSBarracksDefinition.Prefab = "";
			}
			return rTSBarracksDefinition;
		}

		private static string BuiltInUnitEnglishName(string id, string fallback)
		{
			return (id ?? "") switch
			{
				"GeneralHuman" => "Human Settler", 
				"CloneTrooper" => "Clone Trooper", 
				"EwokWarrior" => "Ewok Warrior", 
				"HumanGuard" => "Equipable Guard", 
				_ => fallback, 
			};
		}

		private static string BuiltInBarracksEnglishName(string id, string fallback)
		{
			return (id ?? "") switch
			{
				"GeneralHuman" => "General Barracks", 
				"CloneBarracks" => "Republic Barracks", 
				"EwokVillage" => "Ewok Village", 
				"ProfileHumans" => "Human Profile", 
				"ProfileRepublic" => "Republic Profile", 
				"ProfileEndor" => "Endor Profile", 
				"GenericHost" => "Configurable Barracks", 
				_ => fallback, 
			};
		}

		internal static List<RTSCost> ParseCostList(string value)
		{
			return ParseCosts(value).ToList();
		}

		private static Dictionary<string, Dictionary<string, string>> ParseIniText(string text)
		{
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			string text2 = "";
			string text3 = (text ?? "").Replace("\r\n", "\n").Replace("\r", "\n");
			string[] array = text3.Split(new char[1] { '\n' });
			foreach (string text4 in array)
			{
				string text5 = text4.Trim();
				if (text5.Length == 0 || text5.StartsWith("#") || text5.StartsWith(";"))
				{
					continue;
				}
				if (text5.StartsWith("[") && text5.EndsWith("]"))
				{
					text2 = text5.Substring(1, text5.Length - 2).Trim();
					if (!dictionary.ContainsKey(text2))
					{
						dictionary[text2] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
					}
				}
				else if (!string.IsNullOrWhiteSpace(text2))
				{
					int num = text5.IndexOf('=');
					if (num > 0)
					{
						string key = text5.Substring(0, num).Trim();
						string value = text5.Substring(num + 1).Trim();
						dictionary[text2][key] = value;
					}
				}
			}
			return dictionary;
		}

		private static IEnumerable<RTSCost> ParseCosts(string value)
		{
			foreach (string token in SplitList(value))
			{
				int colon = token.LastIndexOf(':');
				if (colon > 0)
				{
					string prefab = token.Substring(0, colon).Trim();
					if (int.TryParse(token.Substring(colon + 1).Trim(), out var amount) && amount > 0)
					{
						yield return new RTSCost
						{
							Prefab = prefab,
							Amount = amount
						};
					}
				}
			}
		}

		private static IEnumerable<string> SplitList(string value)
		{
			return from x in (value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select x.Trim() into x
				where x.Length > 0
				select x;
		}

		private static string Get(Dictionary<string, string> v, string key, string fallback)
		{
			string value;
			return v.TryGetValue(key, out value) ? value.Trim() : fallback;
		}

		private static int GetInt(Dictionary<string, string> v, string key, int fallback)
		{
			int result;
			return int.TryParse(Get(v, key, ""), NumberStyles.Integer, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}

		private static float GetFloat(Dictionary<string, string> v, string key, float fallback)
		{
			float result;
			return float.TryParse(Get(v, key, ""), NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}

		private static bool GetBool(Dictionary<string, string> v, string key, bool fallback)
		{
			string text = Get(v, key, fallback ? "true" : "false");
			if (bool.TryParse(text, out var result))
			{
				return result;
			}
			if (int.TryParse(text, out var result2))
			{
				return result2 != 0;
			}
			return fallback;
		}

		private static Vector3 ParseVector3(string raw, Vector3 fallback)
		{
			//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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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)
			string[] array = (raw ?? "").Split(new char[1] { ',' });
			if (array.Length != 3)
			{
				return fallback;
			}
			if (!float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return fallback;
			}
			return new Vector3(result, result2, result3);
		}
	}
	internal static class RTSUtil
	{
		private static bool _languageCached;

		private static bool _cachedSpanish;

		private static float _nextLanguageRefresh;

		internal static long UtcTicks => DateTime.UtcNow.Ticks;

		internal static string CleanPrefabName(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "";
			}
			string text = value.Trim();
			if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "(Clone)".Length).Trim();
			}
			return text;
		}

		internal static string PrefabName(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return "";
			}
			try
			{
				return CleanPrefabName(Utils.GetPrefabName(go));
			}
			catch
			{
				return CleanPrefabName(((Object)go).name);
			}
		}

		internal static GameObject ResolvePrefab(string prefabName)
		{
			string clean = CleanPrefabName(prefabName);
			if (clean.Length == 0)
			{
				return null;
			}
			try
			{
				if ((Object)(object)ZNetScene.instance != (Object)null)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(clean);
					if ((Object)(object)prefab != (Object)null)
					{
						return prefab;
					}
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(clean);
					if ((Object)(object)itemPrefab != (Object)null)
					{
						return itemPrefab;
					}
				}
			}
			catch
			{
			}
			try
			{
				return ((IEnumerable<GameObject>)Resources.FindObjectsOfTypeAll<GameObject>()).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && string.Equals(CleanPrefabName(((Object)x).name), clean, StringComparison.OrdinalIgnoreCase)));
			}
			catch
			{
				return null;
			}
		}

		internal static string ResolveSharedItemName(string itemPrefab)
		{
			GameObject val = ResolvePrefab(itemPrefab);
			try
			{
				return ((val == null) ? null : val.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name) ?? "";
			}
			catch
			{
				return "";
			}
		}

		internal static int CountItem(Player player, string itemPrefab)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0;
			}
			string text = ResolveSharedItemName(itemPrefab);
			if (string.IsNullOrWhiteSpace(text))
			{
				return 0;
			}
			try
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				return (inventory != null) ? inventory.CountItems(text, -1, true) : 0;
			}
			catch
			{
				return 0;
			}
		}

		internal static bool HasCosts(Player player, IEnumerable<RTSCost> costs, out string missing)
		{
			missing = "";
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			foreach (RTSCost item in costs ?? Enumerable.Empty<RTSCost>())
			{
				int num = CountItem(player, item.Prefab);
				if (num >= item.Amount)
				{
					continue;
				}
				missing = item.Prefab + " " + num + "/" + item.Amount;
				return false;
			}
			return true;
		}

		internal static bool RemoveCosts(Player player, IEnumerable<RTSCost> costs)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			List<RTSCost> list = (costs ?? Enumerable.Empty<RTSCost>()).ToList();
			if (!HasCosts(player, list, out string _))
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			foreach (RTSCost item in list)
			{
				string text = ResolveSharedItemName(item.Prefab);
				if (string.IsNullOrWhiteSpace(text))
				{
					return false;
				}
				try
				{
					inventory.RemoveItem(text, item.Amount, -1, true);
				}
				catch
				{
					return false;
				}
			}
			try
			{
				inventory.Changed(false, false);
			}
			catch
			{
			}
			return true;
		}

		internal static bool RemoveItem(Player player, string itemPrefab, int amount)
		{
			if ((Object)(object)player == (Object)null || amount <= 0)
			{
				return false;
			}
			string text = ResolveSharedItemName(itemPrefab);
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			try
			{
				if (inventory.CountItems(text, -1, true) < amount)
				{
					return false;
				}
				inventory.RemoveItem(text, amount, -1, true);
				inventory.Changed(false, false);
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static Player FindOnlinePlayer(long playerId)
		{
			try
			{
				return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => (Object)(object)p != (Object)null && p.GetPlayerID() == playerId));
			}
			catch
			{
				return null;
			}
		}

		internal static void Message(Player player, string text, MessageType type = (MessageType)2)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			try
			{
				((Character)player).Message(type, text, 0, (Sprite)null, false);
			}
			catch
			{
			}
		}

		internal static bool IsSpanish()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_languageCached && realtimeSinceStartup < _nextLanguageRefresh)
			{
				return _cachedSpanish;
			}
			try
			{
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "";
				_cachedSpanish = text.IndexOf("span", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch
			{
				_cachedSpanish = false;
			}
			_languageCached = true;
			_nextLanguageRefresh = realtimeSinceStartup + 2f;
			return _cachedSpanish;
		}

		internal static string T(string spanish, string english)
		{
			return IsSpanish() ? spanish : english;
		}

		internal static string SanitizeName(string value, int max = 40)
		{
			string input = (value ?? "").Trim();
			input = Regex.Replace(input, "<[^>]*>", "");
			input = input.Replace("<", "").Replace(">", "").Replace("\r", " ")
				.Replace("\n", " ");
			while (input.Contains("  "))
			{
				input = input.Replace("  ", " ");
			}
			if (input.Length > max)
			{
				input = input.Substring(0, max);
			}
			return input.Trim();
		}

		internal static string FormatSeconds(double seconds)
		{
			if (seconds < 0.0)
			{
				seconds = 0.0;
			}
			TimeSpan timeSpan = TimeSpan.FromSeconds(seconds);
			if (timeSpan.TotalHours >= 1.0)
			{
				return $"{(int)timeSpan.TotalHours:00}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
			}
			return $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
		}

		internal static void SetTamed(Character character, bool value)
		{
			if ((Object)(object)character == (Object)null)
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method(((object)character).GetType(), "SetTamed", new Type[1] { typeof(bool) }, (Type[])null);
				if (methodInfo != null)
				{
					methodInfo.Invoke(character, new object[1] { value });
					return;
				}
			}
			catch
			{
			}
			try
			{
				AccessTools.Field(((object)character).GetType(), "m_tamed")?.SetValue(character, value);
			}
			catch
			{
			}
		}

		internal static void SetTamedFlag(Character character, bool value)
		{
			if ((Object)(object)character == (Object)null)
			{
				return;
			}
			try
			{
				(AccessTools.Field(typeof(Character), "m_tamed") ?? AccessTools.Field(((object)character).GetType(), "m_tamed"))?.SetValue(character, value);
			}
			catch
			{
			}
			try
			{
				ZNetView component = ((Component)character).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					return;
				}
				if (!component.IsOwner())
				{
					component.ClaimOwnership();
				}
				if (component.IsOwner())
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null)
					{
						zDO.Set(ZDOVars.s_tamed, value);
					}
				}
			}
			catch
			{
			}
		}

		internal static void SetFriendlyFaction(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(Character), "m_faction") ?? AccessTools.Field(((object)character).GetType(), "m_faction");
				if (fieldInfo != null && fieldInfo.FieldType.IsEnum)
				{
					fieldInfo.SetValue(character, Enum.ToObject(fieldInfo.FieldType, 0));
				}
			}
			catch
			{
			}
		}

		internal static Character FindCharacterArg(object[] args)
		{
			if (args == null)
			{
				return null;
			}
			foreach (object obj in args)
			{
				Character val = (Character)((obj is Character) ? obj : null);
				if (val != null)
				{
					return val;
				}
				GameObject val2 = (GameObject)((obj is GameObject) ? obj : null);
				if (val2 != null)
				{
					Character component = val2.GetComponent<Character>();
					if ((Object)(object)component != (Object)null)
					{
						return component;
					}
				}
			}
			return null;
		}

		internal static long GetPieceCreator(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return 0L;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method(((object)piece).GetType(), "GetCreator", Type.EmptyTypes, (Type[])null);
				if (methodInfo != null)
				{
					object obj = methodInfo.Invoke(piece, null);
					if (obj != null)
					{
						return Convert.ToInt64(obj);
					}
				}
			}
			catch
			{
			}
			try
			{
				object obj3 = AccessTools.Field(((object)piece).GetType(), "m_creator")?.GetValue(piece);
				if (obj3 != null)
				{
					return Convert.ToInt64(obj3);
				}
			}
			catch
			{
			}
			try
			{
				ZNetView component = ((Component)piece).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					return component.GetZDO().GetLong("creator", 0L);
				}
			}
			catch
			{
			}
			return 0L;
		}

		internal static void SetMonsterAiFollow(GameObject mob, GameObject target)
		{
			if ((Object)(object)mob == (Object)null)
			{
				return;
			}
			MonsterAI component = mob.GetComponent<MonsterAI>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			try
			{
				AccessTools.Method(((object)component).GetType(), "SetFollowTarget", new Type[1] { typeof(GameObject) }, (Type[])null)?.Invoke(component, new object[1] { target });
			}
			catch
			{
			}
		}

		internal static void SetMonsterAiPatrol(GameObject mob, Vector3 point)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mob == (Object)null)
			{
				return;
			}
			MonsterAI component = mob.GetComponent<MonsterAI>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			try
			{
				AccessTools.Method(((object)component).GetType(), "SetPatrolPoint", new Type[1] { typeof(Vector3) }, (Type[])null)?.Invoke(component, new object[1] { point });
			}
			catch
			{
			}
		}

		internal static void ResetMonsterAiPatrol(GameObject mob)
		{
			if ((Object)(object)mob == (Object)null)
			{
				return;
			}
			MonsterAI component = mob.GetComponent<MonsterAI>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			try
			{
				AccessTools.Method(((object)component).GetType(), "ResetPatrolPoint", Type.EmptyTypes, (Type[])null)?.Invoke(component, null);
			}
			catch
			{
			}
		}

		internal static void ClearMonsterAiCombat(GameObject mob)
		{
			if (!((Object)(object)mob == (Object)null))
			{
				ClearMonsterAiCombat(mob.GetComponent<MonsterAI>());
			}
		}

		internal static void ClearMonsterAiCombat(MonsterAI ai)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ai == (Object)null)
			{
				return;
			}
			try
			{
				FindAiField(ai, "m_targetCreature")?.SetValue(ai, null);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_targetStatic")?.SetValue(ai, null);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_lastKnownTargetPos")?.SetValue(ai, ((Component)ai).transform.position);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_beenAtLastPos")?.SetValue(ai, true);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_timeSinceAttacking")?.SetValue(ai, 0f);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_timeSinceSensedTargetCreature")?.SetValue(ai, 999f);
			}
			catch
			{
			}
			try
			{
				FindAiField(ai, "m_updateTargetTimer")?.SetValue(ai, 0.75f);
			}
			catch
			{
			}
			try
			{
				(AccessTools.Method(((object)ai).GetType(), "SetTargetInfo", new Type[1] { typeof(ZDOID) }, (Type[])null) ?? AccessTools.Method(typeof(BaseAI), "SetTargetInfo", new Type[1] { typeof(ZDOID) }, (Type[])null))?.Invoke(ai, new object[1] { ZDOID.None });
			}
			catch
			{
			}
			try
			{
				(AccessTools.Method(((object)ai).GetType(), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null) ?? AccessTools.Method(typeof(BaseAI), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null))?.Invoke(ai, new object[1] { false });
			}
			catch
			{
			}
		}

		internal static bool TryGetMonsterAiCombatTarget(MonsterAI ai, out Vector3 point, out bool isStatic)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			isStatic = false;
			if ((Object)(object)ai == (Object)null)
			{
				return false;
			}
			try
			{
				object? obj = FindAiField(ai, "m_targetCreature")?.GetValue(ai);
				Character val = (Character)((obj is Character) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					point = ((Component)val).transform.position;
					return true;
				}
			}
			catch
			{
			}
			try
			{
				object? obj3 = FindAiField(ai, "m_targetStatic")?.GetValue(ai);
				StaticTarget val2 = (StaticTarget)((obj3 is StaticTarget) ? obj3 : null);
				if ((Object)(object)val2 != (Object)null)
				{
					point = val2.GetCenter();
					isStatic = true;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool SetMonsterAiStaticTarget(MonsterAI ai, StaticTarget target)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ai == (Object)null || (Object)(object)target == (Object)null)
			{
				return false;
			}
			try
			{
				FindAiField(ai, "m_targetCreature")?.SetValue(ai, null);
				FindAiField(ai, "m_targetStatic")?.SetValue(ai, target);
				FindAiField(ai, "m_lastKnownTargetPos")?.SetValue(ai, target.GetCenter());
				FindAiField(ai, "m_beenAtLastPos")?.SetValue(ai, false);
				FindAiField(ai, "m_timeSinceAttacking")?.SetValue(ai, 0f);
				(AccessTools.Method(((object)ai).GetType(), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null) ?? AccessTools.Method(typeof(BaseAI), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null))?.Invoke(ai, new object[1] { true });
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static FieldInfo FindAiField(MonsterAI ai, string name)
		{
			if ((Object)(object)ai == (Object)null || string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			Type type = ((object)ai).GetType();
			while (type != null)
			{
				FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type = type.BaseType;
			}
			return null;
		}
	}
	internal static class RTSPieceCostOverrides
	{
		private static readonly HashSet<string> LastAppliedSignature = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static void ClearCache()
		{
			LastAppliedSignature.Clear();
		}

		internal static void ApplyAll()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			foreach (RTSBarracksDefinition allPhysicalBarrack in RTSDefinitionRegistry.AllPhysicalBarracks)
			{
				if (allPhysicalBarrack == null || allPhysicalBarrack.BuildCosts.Count == 0)
				{
					continue;
				}
				GameObject val = RTSUtil.ResolvePrefab(allPhysicalBarrack.Prefab);
				Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					string item = allPhysicalBarrack.Id + "=" + string.Join(",", allPhysicalBarrack.BuildCosts.Select((RTSCost c) => c.Prefab + ":" + c.Amount));
					if (!LastAppliedSignature.Contains(item) && ApplyCostsToPiece(val2, allPhysicalBarrack.BuildCosts))
					{
						LastAppliedSignature.Add(item);
					}
				}
			}
		}

		internal static bool ApplyCostsToPiece(Piece piece, IList<RTSCost> costs)
		{
			if ((Object)(object)piece == (Object)null || costs == null || costs.Count == 0)
			{
				return false;
			}
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(Piece), "m_resources");
				Type type = (fieldInfo?.FieldType)?.GetElementType();
				if (fieldInfo == null || type == null)
				{
					return false;
				}
				Array array = Array.CreateInstance(type, costs.Count);
				for (int i = 0; i < costs.Count; i++)
				{
					RTSCost rTSCost = costs[i];
					GameObject val = RTSUtil.ResolvePrefab(rTSCost.Prefab);
					ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val2 == (Object)null)
					{
						return false;
					}
					object obj = Activator.CreateInstance(type);
					AccessTools.Field(type, "m_resItem")?.SetValue(obj, val2);
					AccessTools.Field(type, "m_amount")?.SetValue(obj, Mathf.Max(1, rTSCost.Amount));
					AccessTools.Field(type, "m_amountPerLevel")?.SetValue(obj, 0);
					AccessTools.Field(type, "m_recover")?.SetValue(obj, true);
					array.SetValue(obj, i);
				}
				fieldInfo.SetValue(piece, array);
				return true;
			}
			catch (Exception ex)
			{
				sunnfjordheimmodularPlugin.Debug("Could not apply piece costs to " + ((Object)piece).name + ": " + ex.Message);
				return false;
			}
		}
	}
	internal static class RTSTameGuildWarsCompat
	{
		private static bool _resolved;

		private static float _nextResolveAttempt;

		private static Type _tamedGuildMobType;

		private static Type _stanceType;

		private static MethodInfo _getPlayerGuildName;

		private static MethodInfo _isAlly;

		private static MethodInfo _tryClaim;

		private static MethodInfo _setStance;

		private static PropertyInfo _skipDecay;

		internal static bool Available => Resolve();

		private static bool Resolve()
		{
			if (_resolved && _tamedGuildMobType != null)
			{
				return true;
			}
			if (Time.realtimeSinceStartup < _nextResolveAttempt)
			{
				return false;
			}
			_nextResolveAttempt = Time.realtimeSinceStartup + 2f;
			try
			{
				Assembly assembly = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly2 in assemblies)
				{
					if (!(assembly2 == null))
					{
						Type type = null;
						try
						{
							type = assembly2.GetType("TameGuildWars.TamedGuildMob", throwOnError: false);
						}
						catch
						{
						}
						if (type != null)
						{
							assembly = assembly2;
							_tamedGuildMobType = type;
							break;
						}
					}
				}
				if (assembly == null || _tamedGuildMobType == null)
				{
					return false;
				}
				_stanceType = assembly.GetType("TameGuildWars.GuildMobStance", throwOnError: false);
				Type type2 = assembly.GetType("TameGuildWars.GuildUtils", throwOnError: false);
				if (_stanceType == null)
				{
					return false;
				}
				_getPlayerGuildName = type2?.GetMethod("GetPlayerGuildName", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Player) }, null);
				_isAlly = type2?.GetMethod("IsAlly", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(string),
					typeof(string)
				}, null);
				_tryClaim = _tamedGuildMobType.GetMethod("TryClaim", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(Player),
					typeof(string)
				}, null);
				_setStance = _tamedGuildMobType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "SetStance" && m.GetParameters().Length >= 1 && m.GetParameters()[0].ParameterType == _stanceType);
				_skipDecay = _tamedGuildMobType.GetProperty("SkipDecay", BindingFlags.Instance | BindingFlags.Public);
				_resolved = true;
				sunnfjordheimmodularPlugin.Debug("TameGuildWars integration detected and ready.");
				return true;
			}
			catch (Exception ex)
			{
				sunnfjordheimmodularPlugin.Debug("TGW reflection not ready yet: " + ex.Message);
				_resolved = false;
				return false;
			}
		}

		internal static string GetPlayerGuildName(Player player)
		{
			if ((Object)(object)player == (Object)null || !Resolve() || _getPlayerGuildName == null)
			{
				return "";
			}
			try
			{
				return (_getPlayerGuildName.Invoke(null, new object[1] { player }) as string) ?? "";
			}
			catch
			{
				return "";
			}
		}

		internal static bool ArePlayersGuildFriendly(Player first, Player second)
		{
			if ((Object)(object)first == (Object)null || (Object)(object)second == (Object)null)
			{
				return false;
			}
			string playerGuildName = GetPlayerGuildName(first);
			string playerGuildName2 = GetPlayerGuildName(second);
			if (string.IsNullOrWhiteSpace(playerGuildName) || string.IsNullOrWhiteSpace(playerGuildName2) || string.Equals(playerGuildName, "No Guild", StringComparison.OrdinalIgnoreCase) || string.Equals(playerGuildName2, "No Guild", StringComparison.OrdinalIgnoreCase) || string.Equals(playerGuildName, "Sin Guild", StringComparison.OrdinalIgnoreCase) || string.Equals(playerGuildName2, "Sin Guild", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (string.Equals(playerGuildName, playerGuildName2, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (_isAlly == null)
			{
				return false;
			}
			try
			{
				object obj = _isAlly.Invoke(null, new object[2] { playerGuildName, playerGuildName2 });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static void Integrate(GameObject mob, Player recruiter)
		{
			if ((Object)(object)mob == (Object)null || (Object)(object)recruiter == (Object)null)
			{
				return;
			}
			ConfigEntry<sunnfjordheimmodularPlugin.Toggle> tgwIntegration = sunnfjordheimmodularPlugin.TgwIntegration;
			if (tgwIntegration == null || tgwIntegration.Value != sunnfjordheimmodularPlugin.Toggle.On || !Resolve() || _tamedGuildMobType == null)
			{
				return;
			}
			try
			{
				Component obj = mob.GetComponent(_tamedGuildMobType) ?? mob.AddComponent(_tamedGuildMobType);
				string playerGuildName = GetPlayerGuildName(recruiter);
				if (!string.IsNullOrWhiteSpace(playerGuildName) && !string.Equals(playerGuildName, "No Guild", StringComparison.OrdinalIgnoreCase) && _tryClaim != null)
				{
					_tryClaim.Invoke(obj, new object[2] { recruiter, playerGuildName });
				}
				if (_skipDecay != null && _skipDecay.CanWrite)
				{
					ConfigEntry<sunnfjordheimmodularPlugin.Toggle> tgwSkipDecay = sunnfjordheimmodularPlugin.TgwSkipDecay;
					if (tgwSkipDecay != null && tgwSkipDecay.Value == sunnfjordheimmodularPlugin.Toggle.On)
					{
						_skipDecay.SetValue(obj, true, null);
					}
				}
			}
			catch (Exception ex)
			{
				sunnfjordheimmodularPlugin.Debug("TGW integration failed: " + ex.Message);
			}
		}

		internal static bool TrySetStance(GameObject mob, Player commander, string stanceName)
		{
			if (!((Object)(object)mob == (Object)null))
			{
				ConfigEntry<sunnfjordheimmodularPlugin.Toggle> tgwIntegration = sunnfjordheimmodularPlugin.TgwIntegration;
				if (tgwIntegration != null && tgwIntegration.Value == sunnfjordheimmodularPlugin.Toggle.On)
				{
					if (!Resolve() || _tamedGuildMobType == null || _stanceType == null || _setStance == null)
					{
						return false;
					}
					try
					{
						Component component = mob.GetComponent(_tamedGuildMobType);
						if ((Object)(object)component == (Object)null)
						{
							return false;
						}
						object obj = Enum.Parse(_stanceType, stanceName, ignoreCase: true);
						ParameterInfo[] parameters = _setStance.GetParameters();
						object[] array = new object[parameters.Length];
						array[0] = obj;
						if (parameters.Length > 1)
						{
							array[1] = commander;
						}
						if (parameters.Length > 2)
						{
							array[2] = false;
						}
						object obj2 = _setStance.Invoke(component, array);
						bool flag = default(bool);
						int num;
						if (obj2 is bool)
						{
							flag = (bool)obj2;
							num = ((1 == 0) ? 1 : 0);
						}
						else
						{
							num = 1;
						}
						return (byte)((uint)num | (flag ? 1u : 0u)) != 0;
					}
					catch (Exception ex)
					{
						sunnfjordheimmodularPlugin.Debug("TGW SetStance " + stanceName + " failed: " + ex.Message);
						return false;
					}
				}
			}
			return false;
		}
	}
	internal static class RTSPortablePalsCompat
	{
		private const string StateKey = "Sunnfjordheim_RTS_State_v1";

		private const string HarmonyId = "Azathoth.SunnfjordheimModularv1.PortablePals";

		private const string TameGuildWarsHarmonyId = "WackyMole.TameGuildWars";

		private static bool _patched;

		private static float _nextAttempt;

		internal static void Tick()
		{
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			if (_patched || Time.unscaledTime < _nextAttempt)
			{
				return;
			}
			_nextAttempt = Time.unscaledTime + 2f;
			Type type = FindLoadedType("PortablePals.API");
			if (type == null)
			{
				return;
			}
			try
			{
				Type typeFromHandle = typeof(Dictionary<string, string>);
				MethodInfo method = type.GetMethod("SaveDataToPalStone", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(Character),
					typeFromHandle
				}, null);
				MethodInfo method2 = type.GetMethod("ReleaseCreature_PostAwake", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(Character),
					typeFromHandle
				}, null);
				MethodInfo method3 = type.GetMethod("CanCapture", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(Character),
					typeof(Player)
				}, null);
				if (method == null || method2 == null || method3 == null)
				{
					return;
				}
				MethodInfo method4 = type.GetMethod("AddZDOToSavedValues", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
				if (method4 != null)
				{
					string[] portableZdoKeys = RTSRecruitUnit.PortableZdoKeys;
					foreach (string text in portableZdoKeys)
					{
						method4.Invoke(null, new object[1] { text });
					}
				}
				Harmony val = new Harmony("Azathoth.SunnfjordheimModularv1.PortablePals");
				val.Patch((MethodBase)method, (HarmonyMethod)null, OrderedPatch("SaveDataPostfix", 0), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)method2, (HarmonyMethod)null, OrderedPatch("ReleaseDataPostfix", 0), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)method3, (HarmonyMethod)null, OrderedPatch("CanCapturePostfix", 0), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_patched = true;
				sunnfjordheimmodularPlugin.Log.LogInfo((object)"[RTS Compat] Portable Pals detected: RTS owner, group, barracks and order state will be preserved.");
			}
			catch (Exception ex)
			{
				sunnfjordheimmodularPlugin.Log.LogWarning((object)("[RTS Compat] Portable Pals bridge is not ready yet: " + ex.Message));
			}
		}

		private static HarmonyMethod OrderedPatch(string methodName, int priority)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(typeof(RTSPortablePalsCompat), methodName, (Type[])null);
			val.priority = priority;
			val.after = new string[1] { "WackyMole.TameGuildWars" };
			return val;
		}

		private static Type FindLoadedType(string fullName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly?.GetType(fullName, throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		public static void SaveDataPostfix(Character character, Dictionary<string, string> customData)
		{
			if (!sunnfjordheimmodularPlugin.IsEnabled || (Object)(object)character == (Object)null || customData == null)
			{
				return;
			}
			RTSRecruitUnit rTSRecruitUnit = ((Component)character).GetComponent<RTSRecruitUnit>();
			if ((Object)(object)rTSRecruitUnit == (Object)null && RTSRecruitUnit.IsMarkedZdo(character))
			{
				rTSRecruitUnit = ((Component)character).gameObject.AddComponent<RTSRecruitUnit>();
			}
			if (!((Object)(object)rTSRecruitUnit == (Object)null))
			{
				string value = rTSRecruitUnit.ExportPortableState();
				if (!string.IsNullOrWhiteSpace(value))
				{
					customData["Sunnfjordheim_RTS_State_v1"] = value;
				}
			}
		}

		public static void ReleaseDataPostfix(Character character, Dictionary<string, string> customData)
		{
			if (!sunnfjordheimmodularPlugin.IsEnabled || (Object)(object)character == (Object)null || customData == null)
			{
				return;
			}
			customData.TryGetValue("Sunnfjordheim_RTS_State_v1", out string value);
			RTSRecruitUnit rTSRecruitUnit = ((Component)character).GetComponent<RTSRecruitUnit>();
			if ((Object)(object)rTSRecruitUnit == (Object)null && (!string.IsNullOrWhiteSpace(value) || RTSRecruitUnit.IsMarkedZdo(character)))
			{
				rTSRecruitUnit = ((Component)character).gameObject.AddComponent<RTSRecruitUnit>();
			}
			if (!((Object)(object)rTSRecruitUnit == (Object)null))
			{
				if (!string.IsNullOrWhiteSpace(value) && !rTSRecruitUnit.RestorePortableState(value))
				{
					sunnfjordheimmodularPlugin.Log.LogWarning((object)"[RTS Compat] A Portable Pal contained invalid RTS state; it was not relinked.");
					return;
				}
				Player owner = RTSUtil.FindOnlinePlayer(rTSRecruitUnit.OwnerPlayerId);
				rTSRecruitUnit.PrepareAfterPortableRelease(owner);
				sunnfjordheimmodularPlugin.Log.LogInfo((object)("[RTS Compat] Portable Pal restored as RTS unit " + rTSRecruitUnit.UnitId + " (owner=" + rTSRecruitUnit.OwnerPlayerId + ", G" + rTSRecruitUnit.GroupNumber + ")."));
			}
		}

		public static void CanCapturePostfix(Character character, Player player, ref bool __result)
		{
			if (sunnfjordheimmodularPlugin.IsEnabled && !((Object)(object)character == (Object)null) && !((Object)(object)player == (Object)null))
			{
				RTSRecruitUnit component = ((Component)character).GetComponent<RTSRecruitUnit>();
				if (!((Object)(object)component == (Object)null) && component.OwnerPlayerId != 0L && component.OwnerPlayerId == player.GetPlayerID() && character.IsTamed())
				{
					__result = true;
				}
			}
		}
	}
	internal static class RTSFriendlyIdentity
	{
		internal static bool AreFriendly(Character first, Character second)
		{
			if ((Object)(object)first == (Object)null || (Object)(object)second == (Object)null)
			{
				return false;
			}
			RTSRecruitUnit component = ((Component)first).GetComponent<RTSRecruitUnit>();
			RTSRecruitUnit component2 = ((Component)second).GetComponent<RTSRecruitUnit>();
			if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && component.OwnerPlayerId != 0L && component.OwnerPlayerId == component2.OwnerPlayerId)
			{
				return true;
			}
			if ((Object)(object)component != (Object)null)
			{
				Player val = (Player)(object)((second is Player) ? second : null);
				if (val != null && component.OwnerPlayerId == val.GetPlayerID())
				{
					return true;
				}
			}
			if ((Object)(object)component2 != (Object)null)
			{
				Player val2 = (Player)(object)((first is Player) ? first : null);
				if (val2 != null && component2.OwnerPlayerId == val2.GetPlayerID())
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character) })]
	internal static class RTSFriendlyIdentityInstancePatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "WackyMole.TameGuildWars" })]
		private static void Postfix(BaseAI __instance, Character other, ref bool __result)
		{
			if (sunnfjordheimmodularPlugin.IsEnabled && __result && !((Object)(object)__instance == (Object)null))
			{
				Character component = ((Component)__instance).GetComponent<Character>();
				if (RTSFriendlyIdentity.AreFriendly(component, other))
				{
					__result = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
	{
		typeof(Character),
		typeof(Character)
	})]
	internal static class RTSFriendlyIdentityStaticPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "WackyMole.TameGuildWars" })]
		private static void Postfix(Character a, Character b, ref bool __result)
		{
			if (sunnfjordheimmodularPlugin.IsEnabled && __result && RTSFriendlyIdentity.AreFriendly(a, b))
			{
				__result = false;
			}
		}
	}
	internal static class RTSPortalFollowerTransport
	{
		internal sealed class Batch
		{
			internal Player Player;

			internal readonly List<UnitEntry> Units = new List<UnitEntry>();

			internal Vector3 Destination;

			internal Quaternion Rotation;

			internal float StartedAt;
		}

		internal sealed class UnitEntry
		{
			internal RTSRecruitUnit Unit;

			internal ZDOID NetworkId;

			internal long OwnerId;
		}

		private static readonly List<Batch> Pending = new List<Batch>();

		internal static Batch Capture(Player player)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			if (!sunnfjordheimmodularPlugin.IsEnabled || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return null;
			}
			long playerID = player.GetPlayerID();
			Batch batch = new Batch
			{
				Player = player,
				StartedAt = Time.unscaledTime
			};
			RTSRecruitUnit[] array = RTSRecruitUnit.ActiveUnits.ToArray();
			foreach (RTSRecruitUnit rTSRecruitUnit in array)
			{
				if (!((Object)(object)rTSRecruitUnit == (Object)null) && ((Component)rTSRecruitUnit).gameObject.activeInHierarchy && rTSRecruitUnit.OwnerPlayerId == playerID && rTSRecruitUnit.Order == RTSOrderMode.FollowMe && !(Vector3.Distance(((Component)rTSRecruitUnit).transform.position, ((Component)player).transform.position) > 32f))
				{
					batch.Units.Add(new UnitEntry
					{
						Unit = rTSRecruitUnit,
						NetworkId = rTSRecruitUnit.GetNetworkId(),
						OwnerId = playerID
					});
				}
			}
			return (batch.Units.Count == 0) ? null : batch;
		}

		internal static void Queue(Batch batch, Player player)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			if (batch == null || (Object)(object)player == (Object)null || batch.Units.Count == 0)
			{
				return;
			}
			try
			{
				if (((Character)player).IsTeleporting())
				{
					batch.Destination = player.m_teleportTargetPos;
					batch.Rotation = player.m_teleportTargetRot;
					batch.StartedAt = Time.unscaledTime;
					Pending.RemoveAll((Batch p) => p == null || (Object)(object)p.Player == (Object)(object)player);
					Pending.Add(batch);
					sunnfjordheimmodularPlugin.Debug("[RTS Portal] Waiting to move " + batch.Units.Count + " following units.");
				}
			}
			catch
			{
			}
		}

		internal static void Tick()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			if (!sunnfjordheimmodularPlugin.IsEnabled)
			{
				Pending.Clear();
				return;
			}
			Vector3 val = default(Vector3);
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				Batch batch = Pending[num];
				if (batch == null || (Object)(object)batch.Player == (Object)null || Time.unscaledTime - batch.StartedAt > 25f)
				{
					Pending.RemoveAt(num);
					continue;
				}
				try
				{
					if (((Character)batch.Player).IsTeleporting() || Vector3.Distance(((Component)batch.Player).transform.position, batch.Destination) > 18f)
					{
						continue;
					}
					goto IL_00d7;
				}
				catch
				{
					Pending.RemoveAt(num);
				}
				continue;
				IL_00d7:
				int num2 = 0;
				int count = batch.Units.Count;
				for (int i = 0; i < count; i++)
				{
					UnitEntry unitEntry = batch.Units[i];
					if (unitEntry == null || unitEntry.OwnerId != batch.Player.GetPlayerID())
					{
						continue;
					}
					float num3 = ((count <= 1) ? 0f : ((float)Math.PI * 2f * (float)i / (float)count));
					float num4 = ((count <= 1) ? 2.2f : (3f + (float)(i / 8) * 1.5f));
					((Vector3)(ref val))..ctor(Mathf.Cos(num3) * num4, 0.25f, Mathf.Sin(num3) * num4);
					Vector3 destination = batch.Destination + batch.Rotation * val;
					RTSRecruitUnit unit = unitEntry.Unit;
					if ((Object)(object)unit != (Object)null && unit.Order == RTSOrderMode.FollowMe && unit.OwnerPlayerId == unitEntry.OwnerId)
					{
						if (unit.TeleportWithCommander(batch.Player, destination, batch.Rotation))
						{
							num2++;
						}
					}
					else if (RTSRecruitUnit.TeleportUnloadedFollower(unitEntry.NetworkId, unitEntry.OwnerId, destination, batch.Rotation))
					{
						num2++;
					}
				}
				Pending.RemoveAt(num);
				if (num2 > 0)
				{
					RTSUtil.Message(batch.Player, RTSUtil.T($"{num2} tropa(s) cruzaron el portal.", $"{num2} unit(s) crossed the portal."), (MessageType)1);
					sunnfjordheimmodularPlugin.Log.LogInfo((object)("[RTS Portal] Transported " + num2 + "/" + count + " following units."));
				}
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
	internal static class RTSPortalFollowerPatch
	{
		private static void Prefix(Player player, ref RTSPortalFollowerTransport.Batch __state)
		{
			__state = RTSPortalFollowerTransport.Capture(player);
		}

		private static void Postfix(Player player, RTSPortalFollowerTransport.Batch __state)
		{
			RTSPortalFollowerTransport.Queue(__state, player);
		}
	}
	internal enum RTSOrderMode
	{
		FollowMe,
		DefendCenter,
		Stay,
		ReturnHome,
		Patrol,
		MoveTo,
		AssaultArea,
		AttackTarget
	}
	internal enum RTSSettlementUpgradeKind
	{
		Military,
		Logistics
	}
	internal sealed class RTSCommandCenter : MonoBehaviour, Hoverable, Interactable, TextReceiver
	{
		internal static readonly HashSet<RTSCommandCenter> ActiveCenters = new HashSet<RTSCommandCenter>();

		private const string KeySettlementId = "RTS_SettlementId";

		private const string KeyCustomName = "RTS_CommandName";

		private const string KeyOwnerId = "RTS_CommandOwner";

		private const string KeyOwnerName = "RTS_CommandOwnerName";

		private const string KeyGuildName = "RTS_CommandGuild";

		private const string KeyAllowGuild = "RTS_CommandAllowGuild";

		private const string KeyRegistrationOpen = "RTS_CommandRegistrationOpen";

		private const string KeyManagers = "RTS_CommandManagers";

		private const string KeyValid = "RTS_CommandValid";

		private const string KeyMilitaryUsed = "RTS_CommandMilitaryUsed";

		private const string KeyCreatedTicks = "RTS_CommandCreatedTicks";

		private ZNetView _nview;

		private Piece _piece;

		private bool _renaming;

		private float _nextTick;

		internal string SettlementId
		{
			get
			{
				ZDO zdo = GetZdo();
				return ((zdo != null) ? zdo.GetString("RTS_SettlementId", "") : null) ?? "";
			}
		}

		internal long OwnerId
		{
			get
			{
				ZDO zdo = GetZdo();
				return (zdo != null) ? zdo.GetLong("RTS_CommandOwner", 0L) : 0;
			}
		}

		internal string OwnerName
		{
			get
			{
				ZDO zdo = GetZdo();
				return ((zdo != null) ? zdo.GetString("RTS_CommandOwnerName", "") : null) ?? "";
			}
		}

		internal string GuildName
		{
			get
			{
				ZDO zdo = GetZdo();
				return ((zdo != null) ? zdo.GetString("RTS_CommandGuild", "") : null) ?? "";
			}
		}

		internal bool AllowGuild
		{
			get
			{
				ZDO zdo = GetZdo();
				int result;
				if (zdo == null)
				{
					result = 0;
				}
				else
				{
					ConfigEntry<sunnfjordheimmodularPlugin.Toggle> defaultGuildManagement = sunnfjordheimmodularPlugin.DefaultGuildManagement;
					result = (zdo.GetBool("RTS_CommandAllowGuild", defaultGuildManagement != null && defaultGuildManagement.Value == sunnfjordheimmodularPlugin.Toggle.On) ? 1 : 0);
				}
				return (byte)result != 0;
			}
		}

		internal bool RegistrationOpen
		{
			get
			{
				ZDO zdo = GetZdo();
				return zdo != null && zdo.GetBool("RTS_CommandRegistrationOpen", false);
			}
		}

		internal bool IsValidCenter
		{
			get
			{
				ZDO zdo = GetZdo();
				return zdo == null || zdo.GetBool("RTS_CommandValid", true);
			}
		}

		internal float Radius => Mathf.Max(20f, sunnfjordheimmodularPlugin.CommandRadius?.Value ?? 200f);

		private string AffiliationKey
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(GuildName))
				{
					return "G:" + GuildName.Trim().ToLowerInvariant();
				}
				return "P:" + OwnerId;
			}
		}

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			_piece = ((Component)this).GetComponent<Piece>();
		}

		private void Start()
		{
			if ((Object)(object)_nview != (Object)null && _nview.IsValid())
			{
				EnsureIdentity(null);
			}
		}

		private void OnEnable()
		{
			ActiveCenters.Add(this);
		}

		private void OnDisable()
		{
			ActiveCenters.Remove(this);
		}

		private void OnDestroy()
		{
			ActiveCenters.Remove(this);
		}

		private void Update()
		{
			if (!sunnfjordheimmodularPlugin.IsEnabled || Time.time < _nextTick)
			{
				return;
			}
			_nextTick = Time.time + 2f;
			if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner())
			{
				EnsureIdentity(null);
				if (IsValidCenter)
				{
					ValidateDistanceRule();
				}
			}
		}

		private ZDO GetZdo()
		{
			try
			{
				return ((Object)(object)_nview != (Object)null && _nview.IsValid()) ? _nview.GetZDO() : null;
			}
			catch
			{
				return null;
			}
		}

		private bool EnsureOwnership()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return false;
			}
			try
			{
				if (!_nview.IsOwner())
				{
					_nview.ClaimOwnership();
				}
				return _nview.IsOwner();
			}
			catch
			{
				return false;
			}
		}

		private void EnsureIdentity(Player fallbackOwner)
		{
			ZDO zdo = GetZdo();
			if (zdo == null || !EnsureOwnership())
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(zdo.GetString("RTS_SettlementId", "")))
			{
				zdo.Set("RTS_SettlementId", Guid.NewGuid().ToString("N"));
			}
			if (zdo.GetLong("RTS_CommandCreatedTicks", 0L) == 0)
			{
				zdo.Set("RTS_CommandCreatedTicks", RTSUtil.UtcTicks);
			}
			long num = zdo.GetLong("RTS_CommandOwner", 0L);
			if (num == 0)
			{
				num = RTSUtil.GetPieceCreator(_piece);
				if (num == 0L && (Object)(object)fallbackOwner != (Object)null)
				{
					num = fallbackOwner.GetPlayerID();
				}
				if (num != 0)
				{
					zdo.Set("RTS_CommandOwner", num);
					Player val = RTSUtil.FindOnlinePlayer(num) ?? fallbackOwner;
					if ((Object)(object)val != (Object)null)
					{
						zdo.Set("RTS_CommandOwnerName", val.GetPlayerName() ?? "");
						string playerGuildName = RTSTameGuildWarsCompat.GetPlayerGuildName(val);
						if (!string.IsNullOrWhiteSpace(playerGuildName) && !string.Equals(playerGuildName, "No Guild", StringComparison.OrdinalIgnoreCase))
						{
							zdo.Set("RTS_CommandGuild", playerGuildName.Trim());
						}
					}
				}
			}
			if (string.IsNullOrWhiteSpace(zdo.GetString("RTS_CommandGuild", "")) && num != 0)
			{
				Player val2 = RTSUtil.FindOnlinePlayer(num);
				if ((Object)(object)val2 != (Object)null)
				{
					if (string.IsNullOrWhiteSpace(zdo.GetString("RTS_CommandOwnerName", "")))
					{
						zdo.Set("RTS_CommandOwnerName", val2.GetPlayerName() ?? "");
					}
					string playerGuildName2 = RTSTameGuildWarsCompat.GetPlayerGuildName(val2);
					if (!string.IsNullOrWhiteSpace(playerGuildName2) && !string.Equals(playerGuildName2, "No Guild", StringComparison.OrdinalIgnoreCase))
					{
						zdo.Set("RTS_CommandGuild", playerGuildName2.Trim());
					}
				}
			}
			if (!zdo.GetBool("RTS_CommandDefaultsSet", false))
			{
				ConfigEntry<sunnfjordheimmodularPlugin.Toggle> defaultGuildManagement = sunnfjordheimmodularPlugin.DefaultGuildManagement;
				zdo.Set("RTS_CommandAllowGuild", defaultGuildManagement != null && defaultGuildManagement.Value == sunnfjordheimmodularPlugin.Toggle.On);
				zdo.Set("RTS_CommandRegistrationOpen", false);
				zdo.Set("RTS_CommandValid", true);
				zdo.Set("RTS_CommandDefaultsSet", true);
			}
		}

		private void ValidateDistanceRule()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!IsNetworkCenter(this) || CanPlaceCommandCenterAt(((Component)this).transform.position, this, out RTSCommandCenter conflict, out float minimumDistance, out float actualDistance))
			{
				return;
			}
			ZDO zdo = GetZdo();
			long num = ((zdo != null) ? zdo.GetLong("RTS_CommandCreatedTicks", long.MaxValue) : long.MaxValue);
			long? obj;
			if (conflict == null)
			{
				obj = null;
			}
			else
			{
				ZDO zdo2 = conflict.GetZdo();
				obj = ((zdo2 != null) ? new long?(zdo2.GetLong("RTS_CommandCreatedTicks", long.MaxValue)) : ((long?)null));
			}
			long num2 = obj ?? long.MaxValue;
			if (num >= num2)
			{
				ZDO zdo3 = GetZdo();
				if (zdo3 != null)
				{
					zdo3.Set("RTS_CommandValid", false);
				}
				Player player = RTSUtil.FindOnlinePlayer(OwnerId);
				RTSUtil.Message(player, RTSUtil.T($"Estandarte de Mando inválido: hay otro centro a {actualDistance:0} m. Distancia mínima: {minimumDistance:0} m.", $"Invalid Command Banner: another center is {actualDistance:0} m away. Minimum distance: {minimumDistance:0} m."), (MessageType)2);
			}
		}

		internal static bool CanPlaceCommandCenterAt(Vector3 position, RTSCommandCenter ignore, out RTSCommandCenter conflict, out float minimumDistance, out float actualDistance)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			minimumDistance = Mathf.Max(20f, sunnfjordheimmodularPlugin.MinimumCommandCenterDistance?.Value ?? 350f);
			actualDistance = float.MaxValue;
			conflict = null;
			float num = minimumDistance * minimumDistance;
			RTSCommandCenter[] array = ActiveCenters.ToArray();
			foreach (RTSCommandCenter rTSCommandCenter in array)
			{
				if ((Object)(object)rTSCommandCenter == (Object)null || (Object)(object)rTSCommandCenter == (Object)(object)ignore || !IsNetworkCenter(rTSCommandCenter) || !rTSCommandCenter.IsValidCenter)
				{
					continue;
				}
				Vector3 val = ((Component)rTSCommandCenter).transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (!(sqrMagnitude >= num))
				{
					float num2 = Mathf.Sqrt(sqrMagnitude);
					if ((Object)(object)conflict == (Object)null || num2 < actualDistance)
					{
						conflict = rTSCommandCenter;
						actualDistance = num2;
					}
				}
			}
			return (Object)(object)conflict == (Object)null;
		}

		private static bool IsNetworkCenter(RTSCommandCenter center)
		{
			if ((Object)(object)center == (Object)null)
			{
				return false;
			}
			try
			{
				return (Object)(object)center._nview != (Object)null && center._nview.IsValid() && center.GetZdo() != null;
			}
			catch
			{
				return false;
			}
		}

		internal bool CanManage(Player player)
		{
			if ((Object)(object)player == (Object)null || !IsValidCenter)
			{
				return false;
			}
			EnsureIdentity(player);
			long playerID = player.GetPlayerID();
			if (playerID == OwnerId)
			{
				return true;
			}
			if (GetManagerIds().Contains(playerID))
			{
				return true;
			}
			if (AllowGuild && !string.IsNullOrWhiteSpace(GuildName) && string.Equals(RTSTameGuildWarsCompat.GetPlayerGuildName(player)?.Trim(), GuildName.Trim(), StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return false;
		}

		internal bool CanRegister(Player player)
		{
			if ((Object)(object)player == (Object)null || !RegistrationOpen || IsOwner(player))
			{
				return false;
			}
			if (!string.IsNullOrWhiteSpace(GuildName))
			{
				return string.Equals(RTSTameGuildWarsCompat.GetPlayerGuildName(player)?.Trim(), GuildName.Trim(), StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		internal bool IsOwner(Player player)
		{
			return (Object)(object)player != (Object)null && player.GetPlayerID() == OwnerId;
		}

		internal HashSet<long> GetManagerIds()
		{
			HashSet<long> hashSet = new HashSet<long>();
			ZDO zdo = GetZdo();
			string text = ((zdo != null) ? zdo.GetString("RTS_CommandManagers", "") : null) ?? "";
			string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				if (long.TryParse(text2.Trim(), out var result) && result != 0)
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}

		internal List<string> GetManualManagerDisplayNames()
		{
			List<string> list = new List<string>();
			foreach (long item in from x in GetManagerIds()
				orderby x
				select x)
			{
				Player val = RTSUtil.FindOnlinePlayer(item);
				if ((Object)(object)val != (Object)null)
				{
					string playerName = val.GetPlayerName();
					list.Add(string.IsNullOrWhiteSpace(playerName) ? item.ToString() : playerName);
				}
				else
				{
					list.Add("ID " + item);
				}
			}
			return list;
		}

		internal List<string> GetOnlineGuildAccessNames()
		{
			List<string> list = new List<string>();
			if (!AllowGuild || string.IsNullOrWhiteSpace(GuildName))
			{
				return list;
			}
			try
			{
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					if (!((Object)(object)allPlayer == (Object)null) && allPlayer.GetPlayerID() != OwnerId && string.Equals(RTSTameGuildWarsCompat.GetPlayerGuildName(allPlayer)?.Trim(), GuildName.Trim(), StringComparison.OrdinalIgnoreCase))
					{
						string playerName = allPlayer.GetPlayerName();
						if (!string.IsNullOrWhiteSpace(playerName))
						{
							list.Add(playerName);
						}
					}
				}
			}
			catch
			{
			}
			return (from x in list.Distinct<string>(StringComparer.OrdinalIgnoreCase)
				orderby x
				select x).ToList();
		}

		private void SaveManagers(IEnumerable<long> managers)
		{
			if (EnsureOwnership())
			{
				string text = string.Join(",", from x in (managers ?? Enumerable.Empty<long>()).Where((long x) => x != 0).Distinct()
					select x.ToString());
				ZDO zdo = GetZdo();
				if (zdo != null)
				{
					zdo.Set("RTS_CommandManagers", text);
				}
			}
		}

		internal bool RegisterManager(Player player)
		{
			if (!CanRegister(player) || !EnsureOwnership())
			{
				return false;
			}
			HashSet<long> managerIds = GetManagerIds();
			managerIds.Add(player.GetPlayerID());
			SaveManagers(managerIds);
			return true;
		}

		internal bool UnregisterManager(Player player)
		{
			if ((Object)(object)player == (Object)null || IsOwner(player) || !EnsureOwnership())
			{
				return false;
			}
			HashSet<long> managerIds = GetManagerIds();
			bool flag = managerIds.Remove(player.GetPlayerID());
			if (flag)
			{
				SaveManagers(managerIds);
			}
			return flag;
		}

		internal void ClearManagers(Player owner)
		{
			if (IsOwner(owner) && EnsureOwnership())
			{
				SaveManagers(Array.Empty<long>());
			}
		}

		internal void ToggleGuildAccess(Player owner)
		{
			if (IsOwner(owner) && EnsureOwnership())
			{
				ZDO zdo = GetZdo();
				if (zdo != null)
				{
					zdo.Set("RTS_CommandAllowGuild", !AllowGuild);
				}
			}
		}

		internal void ToggleRegistration(Player owner)
		{
			if (IsOwner(owner) && EnsureOwnership())
			{
				ZDO zdo = GetZdo();
				if (zdo != null)
				{
					zdo.Set("RTS_CommandRegistrationOpen", !RegistrationOpen);
				}
			}
		}

		internal int GetMilitaryCapacity()
		{
			int num = Mathf.Max(1, sunnfjordheimmodularPlugin.BaseSettlementMilitaryCap?.Value ?? 30);
			int num2 = RTSSettlementUpgrade.ActiveUpgrades.Count((RTSSettlementUpgrade u) => (Object)(object)u != (Object)null && u.Kind == RTSSettlementUpgradeKind.Military && u.IsBoundTo(SettlementId));
			return num + num2 * Mathf.Max(0, sunnfjordheimmodularPlugin.MilitaryBannerCommandBonus?.Value ?? 20);
		}

		internal int GetBarracksLimit()
		{
			int num = Mathf.Max(1, sunnfjordheimmodularPlugin.BaseSettlementBarracksLimit?.Value ?? 2);
			int num2 = RTSSettlementUpgrade.ActiveUpgrades.Count((RTSSettlementUpgrade u) => (Object)(object)u != (Object)null && u.Kind == RTSSettlementUpgradeKind.Logistics && u.IsBoundTo(SettlementId));
			return num + num2 * Mathf.Max(0, sunnfjordheimmodularPlugin.LogisticsBannerBarracksBonus?.Value ?? 1);
		}

		internal int GetMilitaryUsed()
		{
			ZDO zdo = GetZdo();
			return Mathf.Max(0, (zdo != null) ? zdo.GetInt("RTS_CommandMilitaryUsed", 0) : 0);
		}

		internal bool TryReserveMilitary(int amount)
		{
			if (amount <= 0 || !EnsureOwnership())
			{
				return false;
			}
			int militaryUsed = GetMilitaryUsed();
			if (militaryUsed + amount > GetMilitaryCapacity())
			{
				return false;
			}
			ZDO zdo = GetZdo();
			if (zdo != null)
			{
				zdo.Set("RTS_CommandMilitaryUsed", militaryUsed + amount);
			}
			return true;
		}

		internal void ReleaseMilitary(int amount)
		{
			if (amount > 0 && EnsureOwnership())
			{
				ZDO zdo = GetZdo();
				if (zdo != null)
				{
					zdo.Set("RTS_CommandMilitaryUsed", Mathf.Max(0, GetMilitaryUsed() - amount));
				}
			}
		}

		internal void RecountLoadedMilitary(Player manager)
		{
			if (!CanManage(manager) || !EnsureOwnership())
			{
				return;
			}
			string id = SettlementId;
			int num = RTSRecruitUnit.ActiveUnits.Where((RTSRecruitUnit u) => (Object)(object)u != (Object)null && string.Equals(u.SettlementId, id, StringComparison.Ordinal)).Sum((RTSRecruitUnit u) => Mathf.Max(1, u.CommandCost));
			int num2 = RTSRecruitmentStation.ActiveStations.Where((RTSRecruitmentStation s) => (Object)(object)s != (Object)null && string.Equals(s.SettlementId, id, StringComparison.Ordinal)).Sum((RTSRecruitmentStation s) => s.GetQueueSnapshot().Sum((RTSQueueEntry q) => Mathf.Max(1, q.CommandCost)));
			ZDO zdo = GetZdo();
			if (zdo != null)
			{
				zdo.Set("RTS_CommandMilitaryUsed", num + num2);
			}
		}

		internal int GetRegisteredBarracksCount()
		{
			string id = SettlementId;
			return RTSRecruitmentStation.ActiveStations.Count((RTSRecruitmentStation s) => (Object)(object)s != (Object)null && s.IsRegistered && string.Equals(s.SettlementId, id, StringComparison.Ordinal));
		}

		internal int GetProfileCount(string profileId, RTSRecruitmentStation? except = null)
		{
			if (string.IsNullOrWhiteSpace(profileId))
			{
				return 0;
			}
			string id = SettlementId;
			return RTSRecruitmentStation.ActiveStations.Count((RTSRecruitmentStation s) => (Object)(object)s != (Object)null && (Object)(object)s != (Object)(object)except && s.IsRegistered && string.Equals(s.SettlementId, id, StringComparison.Ordinal) && string.Equals(s.ActiveProfileId, profileId, StringComparison.OrdinalIgnoreCase));
		}

		internal List<RTSRecruitUnit> GetUnits()
		{
			return RTSRecruitUnit.ActiveUnits.Where((RTSRecruitUnit u) => (Object)(object)u != (Object)null && string.Equals(u.SettlementId, SettlementId, StringComparison.Ordinal)).ToList();
		}

		internal List<RTSRecruitmentStation> GetBarracks()
		{
			return RTSRecruitmentStation.ActiveStations.Where((RTSRecruitmentStation s) => (Object)(object)s != (Object)null && s.IsRegistered && string.Equals(s.SettlementId, SettlementId, StringComparison.Ordinal)).ToList();
		}

		internal void IssueOrderToAll(Player commander, RTSOrderMode order)
		{
			if (!CanManage(commander))
			{
				return;
			}
			foreach (RTSRecruitUnit unit in GetUnits())
			{
				unit?.IssueOrder(commander, order, this);
			}
		}

		public string GetHoverName()
		{
			ZDO zdo = GetZdo();
			string text = ((zdo != null) ? zdo.GetString("RTS_CommandName", "") : null) ?? "";
			return string.IsNullOrWhiteSpace(text) ? RTSUtil.T("Estandarte de Mando", "Command Banner") : text;
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			string text