Decompiled source of EssentialShops v1.0.0

plugins/EssentialShops/SoDEssentialShops.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SoDEssentialShops")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1")]
[assembly: AssemblyProduct("SoDEssentialShops")]
[assembly: AssemblyTitle("SoDEssentialShops")]
[assembly: AssemblyVersion("0.2.1.0")]
[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;
		}
	}
}
namespace SoDEssentialShops
{
	[BepInPlugin("sm3gm.sod.essentialshops", "Essential Shops", "1.0.0")]
	public sealed class Plugin : BasePlugin
	{
		public const string VER = "1.0.0";

		internal static ManualLogSource L;

		internal static ConfigEntry<string> Shops;

		internal static ConfigEntry<int> MinimumPerCity;

		internal static ConfigEntry<int> OneMorePerTiles;

		internal static ConfigEntry<int> SpreadAddresses;

		internal static ConfigEntry<string> RequiredBuildings;

		internal static ConfigEntry<string> OptionalInSmallCities;

		internal static ConfigEntry<int> SmallCityTiles;

		internal static ConfigEntry<string> SmallCityMustHave;

		internal static ConfigEntry<bool> SwapIn;

		internal static ConfigEntry<string> SwapOut;

		public override void Load()
		{
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Expected O, but got Unknown
			L = ((BasePlugin)this).Log;
			Shops = ((BasePlugin)this).Config.Bind<string>("General", "ShopTypes", "SyncClinic,PawnShop,BlackmarketTrader:1", "Shop types every newly generated city gets. Internal names, comma separated. Name alone = the size-based number below; Name:count = exactly that many at least (the black market trader needs only one). Empty = off.");
			MinimumPerCity = ((BasePlugin)this).Config.Bind<int>("General", "MinimumPerCity", 1, "Every new city gets at least this many of each listed shop type.");
			OneMorePerTiles = ((BasePlugin)this).Config.Bind<int>("General", "OneMorePerTiles", 8, "One more of each shop type per this many city tiles (the largest vanilla city, 5x4, has 20 tiles, so 8 gives 1 + 2 = 3). Shops the game places by itself count too. 0 = only MinimumPerCity.");
			SpreadAddresses = ((BasePlugin)this).Config.Bind<int>("General", "SpreadAddresses", 25, "After each shop, this many other addresses are filled before the next one is forced, so the shops spread out instead of lining up in one street. 0 = no spacing.");
			RequiredBuildings = ((BasePlugin)this).Config.Bind<string>("Buildings", "RequiredInEveryCity", "", "Building types every new city must contain, as Name:count, comma separated (internal names are listed in the log when a city is generated). Without a building that has shop spaces, no shop can be placed at all.");
			OptionalInSmallCities = ((BasePlugin)this).Config.Bind<string>("Buildings", "SmallCityLimits", "ChemicalPlant:0,BrandyNetherland:0,MixedIndustrial:0,Hotel:0,AmericanDiner:1", "Small cities have only a few building plots, and the game tends to fill the last one with a Chemical Plant or the Brandy Netherland, neither of which has shop spaces. In small cities these types get at most this many (Name:count, 0 = none). Everything else stays allowed, so every plot can still be filled. Empty = keep the game's rules.");
			SmallCityTiles = ((BasePlugin)this).Config.Bind<int>("Buildings", "SmallCityTiles", 4, "A city with this many tiles or fewer counts as small (a 2x2 city has 4 tiles, the same count as OneMorePerTiles).");
			SmallCityMustHave = ((BasePlugin)this).Config.Bind<string>("Buildings", "SmallCityMustHave", "EdenTower,AmericanDiner", "Generation of a small city pauses right after the buildings are chosen (the way the city editor does) and the city is checked for these buildings. Empty = no check.");
			SwapIn = ((BasePlugin)this).Config.Bind<bool>("Buildings", "SwapInEdenTower", false, "If a small city has no EdenTower (the commercial tower with offices and shop spaces), one of the buildings in SwapOut is replaced by it at that pause, the way the city editor changes a building type. Experimental, off by default: the building limits already give small cities an EdenTower.");
			SwapOut = ((BasePlugin)this).Config.Bind<string>("Buildings", "SwapOut", "Townhouse,ShantyTown", "Buildings without shop spaces that may be replaced by the EdenTower in a small city.");
			((BasePlugin)this).AddComponent<CityCheck>();
			Harmony val = new Harmony("sm3gm.sod.essentialshops");
			val.PatchAll(typeof(GeneratePatch));
			val.PatchAll(typeof(LoadPatch));
			val.PatchAll(typeof(StartPatch));
			val.PatchAll(typeof(PlacedPatch));
			ManualLogSource l = L;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(23, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Essential Shops ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("1.0.0");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded");
			}
			l.LogInfo(val2);
		}
	}
	internal static class Forcing
	{
		private sealed class Armed
		{
			internal AddressPreset Preset;

			internal int Quota;

			internal int Count;

			internal int Pause;

			internal bool Important;

			internal bool ForcePick;

			internal bool HardSize;

			internal bool Waiting;

			internal float MinLand;

			internal float MaxLand;

			internal int MaxInstances;
		}

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

		internal static int Quota()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			try
			{
				Vector2 citySize = CityData.Instance.citySize;
				num = (int)(Math.Max(0f, citySize.x - 2f) * Math.Max(0f, citySize.y - 2f));
			}
			catch
			{
			}
			int value = Plugin.OneMorePerTiles.Value;
			return Math.Max(1, Plugin.MinimumPerCity.Value) + ((value > 0) ? (num / value) : 0);
		}

		internal static void Arm()
		{
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Expected O, but got Unknown
			Disarm("a new city starts generating");
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array = (Plugin.Shops.Value ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				string text = array2[0].Trim();
				if (text.Length != 0)
				{
					dictionary[text] = ((array2.Length > 1 && int.TryParse(array2[1], out var result)) ? result : 0);
				}
			}
			Toolbox instance = Toolbox.Instance;
			List<AddressPreset> val = ((instance != null) ? instance.allAddressPresets : null);
			if (dictionary.Count == 0 || val == null)
			{
				return;
			}
			int num = Quota();
			Buildings.Arm();
			try
			{
				List<string> list = new List<string>();
				for (int j = 0; j < val.Count; j++)
				{
					if ((SoCustomComparison)(object)val[j] != (SoCustomComparison)null && (SoCustomComparison)(object)val[j].company != (SoCustomComparison)null)
					{
						list.Add(((Object)val[j]).name);
					}
				}
				Plugin.L.LogDebug((object)("Business types in the game: " + string.Join(", ", list.OrderBy((string n) => n))));
			}
			catch
			{
			}
			for (int num2 = 0; num2 < val.Count; num2++)
			{
				AddressPreset val2 = val[num2];
				if (!((SoCustomComparison)(object)val2 == (SoCustomComparison)null) && dictionary.ContainsKey(((Object)val2).name) && !armed.ContainsKey(((Object)val2).name))
				{
					int num3 = ((dictionary[((Object)val2).name] > 0) ? dictionary[((Object)val2).name] : num);
					armed[((Object)val2).name] = new Armed
					{
						Preset = val2,
						Quota = num3,
						Important = val2.important,
						ForcePick = val2.forcePick,
						HardSize = val2.hardSizeLimits,
						MinLand = val2.minimumLandValue,
						MaxLand = val2.maximumLandValue,
						MaxInstances = val2.maxInstances
					};
					val2.important = true;
					val2.forcePick = true;
					val2.hardSizeLimits = false;
					val2.minimumLandValue = 0f;
					val2.maximumLandValue = 1f;
					if (val2.maxInstances < num3)
					{
						val2.maxInstances = num3;
					}
				}
			}
			ManualLogSource l = Plugin.L;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(54, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("New city: making sure it gets ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(string.Join(", ", armed.Values.Select((Armed x) => x.Quota + " " + ((Object)x.Preset).name)));
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" (");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(armed.Count);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" of ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(dictionary.Count);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" shop types found)");
			}
			l.LogInfo(val3);
		}

		internal static void Placed(NewAddress address)
		{
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			foreach (Armed value in armed.Values)
			{
				if (value.Pause > 0 && --value.Pause == 0 && !value.Waiting)
				{
					value.Preset.forcePick = true;
				}
			}
			AddressPreset val = ((address != null) ? address.addressPreset : null);
			if ((SoCustomComparison)(object)val == (SoCustomComparison)null || !armed.TryGetValue(((Object)val).name, out Armed st))
			{
				return;
			}
			st.Count++;
			ManualLogSource l = Plugin.L;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(25, 5, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Placed ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)val).name);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(st.Count);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" of ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(st.Quota);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" at ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)address).name);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" (floor ");
				BepInExInfoLogInterpolatedStringHandler obj = val2;
				NewFloor floor = ((NewGameLocation)address).floor;
				((BepInExLogInterpolatedStringHandler)obj).AppendFormatted<int?>((floor != null) ? new int?(floor.floor) : ((int?)null));
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(")");
			}
			l.LogInfo(val2);
			if (st.Count >= Minimum())
			{
				try
				{
					val.important = st.Important;
				}
				catch
				{
				}
			}
			if (st.Count >= st.Quota)
			{
				Restore(((Object)val).name);
			}
			else if (st.Count >= Minimum() && armed.Values.Any((Armed o) => o != st && o.Count < Minimum()))
			{
				val.forcePick = false;
				st.Waiting = true;
				st.Pause = 0;
				ManualLogSource l2 = Plugin.L;
				val2 = new BepInExInfoLogInterpolatedStringHandler(28, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)val).name);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" waits until every type has ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(Minimum());
				}
				l2.LogInfo(val2);
			}
			else if (Plugin.SpreadAddresses.Value > 0)
			{
				val.forcePick = false;
				st.Pause = Plugin.SpreadAddresses.Value;
			}
			ReleaseWaiting();
		}

		private static int Minimum()
		{
			return Math.Max(1, Plugin.MinimumPerCity.Value);
		}

		private static void ReleaseWaiting()
		{
			if (armed.Values.Any((Armed o) => o.Count < Minimum()))
			{
				return;
			}
			foreach (Armed value in armed.Values)
			{
				if (value.Waiting)
				{
					value.Waiting = false;
					value.Preset.forcePick = true;
				}
			}
		}

		internal static void Disarm(string why)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Buildings.Restore();
			bool flag = default(bool);
			foreach (string item in Forcing.armed.Keys.ToList())
			{
				Armed armed = Forcing.armed[item];
				ManualLogSource l = Plugin.L;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(60, 4, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Only ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(armed.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" of ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(armed.Quota);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" placed before ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(why);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; the game's own rules apply again.");
				}
				l.LogWarning(val);
				Restore(item);
			}
		}

		private static void Restore(string name)
		{
			Armed armed = Forcing.armed[name];
			Forcing.armed.Remove(name);
			try
			{
				AddressPreset preset = armed.Preset;
				preset.important = armed.Important;
				preset.forcePick = armed.ForcePick;
				preset.hardSizeLimits = armed.HardSize;
				preset.minimumLandValue = armed.MinLand;
				preset.maximumLandValue = armed.MaxLand;
				preset.maxInstances = Math.Max(armed.MaxInstances, armed.Count);
			}
			catch
			{
			}
			ReleaseWaiting();
		}
	}
	[HarmonyPatch(typeof(CityConstructor), "GenerateNewCity")]
	internal static class GeneratePatch
	{
		private static void Prefix()
		{
			try
			{
				Forcing.Arm();
			}
			catch (Exception ex)
			{
				Plugin.L.LogError((object)("Could not prepare the shops: " + ex.Message));
			}
			try
			{
				CityCheck.Arm();
			}
			catch (Exception ex2)
			{
				Plugin.L.LogError((object)("Could not set up the city check: " + ex2.Message));
			}
		}
	}
	[HarmonyPatch(typeof(CityConstructor), "LoadSaveGame")]
	internal static class LoadPatch
	{
		private static void Prefix()
		{
			Forcing.Disarm("the city finished");
		}
	}
	[HarmonyPatch(typeof(CityConstructor), "LoadCityStartNewGame")]
	internal static class StartPatch
	{
		private static void Prefix()
		{
			Forcing.Disarm("the new game started");
		}
	}
	[HarmonyPatch(typeof(NewAddress), "AssignPurpose")]
	internal static class PlacedPatch
	{
		private static void Postfix(NewAddress __instance)
		{
			try
			{
				Forcing.Placed(__instance);
			}
			catch
			{
			}
		}
	}
	internal static class Buildings
	{
		private static readonly Dictionary<string, (BuildingPreset preset, int minimum, int importance)> raised = new Dictionary<string, (BuildingPreset, int, int)>();

		private static readonly List<(BuildingPreset preset, int minimum, int importance, int limit)> changed = new List<(BuildingPreset, int, int, int)>();

		private static readonly List<(BuildingPreset preset, Density dMin, Density dMax, LandValue lMin, LandValue lMax)> looseLimits = new List<(BuildingPreset, Density, Density, LandValue, LandValue)>();

		internal static void Arm()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Expected O, but got Unknown
			Restore();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array = (Plugin.RequiredBuildings.Value ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				if (array2[0].Trim().Length != 0)
				{
					dictionary[array2[0].Trim()] = ((array2.Length <= 1 || !int.TryParse(array2[1], out var result)) ? 1 : result);
				}
			}
			int num = 0;
			try
			{
				Vector2 citySize = CityData.Instance.citySize;
				num = (int)(Math.Max(0f, citySize.x - 2f) * Math.Max(0f, citySize.y - 2f));
			}
			catch
			{
			}
			bool flag = num > 0 && num <= Plugin.SmallCityTiles.Value;
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			array = (Plugin.OptionalInSmallCities.Value ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array3 = array[i].Split(':');
				if (array3[0].Trim().Length != 0)
				{
					dictionary2[array3[0].Trim()] = ((array3.Length <= 1 || !int.TryParse(array3[1], out var result2)) ? 1 : result2);
				}
			}
			HashSet<string> optional = new HashSet<string>(dictionary2.Keys);
			List<BuildingPreset> list = new List<BuildingPreset>();
			foreach (Object item in (Il2CppArrayBase<Object>)(object)Resources.FindObjectsOfTypeAll(Il2CppType.Of<BuildingPreset>()))
			{
				BuildingPreset val = ((Il2CppObjectBase)item).TryCast<BuildingPreset>();
				if ((SoCustomComparison)(object)val != (SoCustomComparison)null)
				{
					list.Add(val);
				}
			}
			int top = ((list.Count != 0) ? list.Max((BuildingPreset val3) => val3.featureImportance) : 0);
			List<string> list2 = new List<string>();
			bool flag2 = default(bool);
			foreach (BuildingPreset b in list)
			{
				list2.Add($"{((Object)b).name} (min {b.minimum}, limit {b.hardLimit}, importance {b.featureImportance})");
				if (raised.ContainsKey(((Object)b).name))
				{
					if (((Il2CppObjectBase)raised[((Object)b).name].preset).Pointer != ((Il2CppObjectBase)b).Pointer)
					{
						Change(b, dictionary, optional, flag, top);
					}
					continue;
				}
				Change(b, dictionary, optional, flag, top);
				if (flag && dictionary2.TryGetValue(((Object)b).name, out var value) && b.hardLimit > value)
				{
					if (!changed.Any<(BuildingPreset, int, int, int)>(((BuildingPreset preset, int minimum, int importance, int limit) c) => ((Il2CppObjectBase)c.preset).Pointer == ((Il2CppObjectBase)b).Pointer))
					{
						changed.Add((b, b.minimum, b.featureImportance, b.hardLimit));
					}
					b.minimum = Math.Min(b.minimum, value);
					b.hardLimit = value;
					ManualLogSource l = Plugin.L;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(21, 2, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Small city: at most ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(value);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)b).name);
					}
					l.LogInfo(val2);
				}
			}
			Plugin.L.LogDebug((object)("Building types in the game: " + string.Join(", ", list2.OrderBy((string x) => x))));
		}

		private static void Change(BuildingPreset b, Dictionary<string, int> wanted, HashSet<string> optional, bool small, int top)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			if (wanted.TryGetValue(((Object)b).name, out var value))
			{
				changed.Add((b, b.minimum, b.featureImportance, b.hardLimit));
				if (b.minimum < value)
				{
					b.minimum = value;
				}
				if (b.hardLimit < value)
				{
					b.hardLimit = value;
				}
				b.featureImportance = top + 1;
				raised[((Object)b).name] = (b, 0, 0);
				ManualLogSource l = Plugin.L;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(34, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("New city: at least ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(value);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)b).name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", placed first");
				}
				l.LogInfo(val);
			}
		}

		internal static void Restore()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			foreach (var looseLimit in looseLimits)
			{
				try
				{
					looseLimit.preset.densityMinimum = looseLimit.dMin;
					looseLimit.preset.densityMaximum = looseLimit.dMax;
					looseLimit.preset.landValueMinimum = looseLimit.lMin;
					looseLimit.preset.landValueMaximum = looseLimit.lMax;
				}
				catch
				{
				}
			}
			looseLimits.Clear();
			foreach (var item in changed)
			{
				try
				{
					item.preset.minimum = item.minimum;
					item.preset.featureImportance = item.importance;
					item.preset.hardLimit = item.limit;
				}
				catch
				{
				}
			}
			changed.Clear();
			raised.Clear();
		}
	}
	public class CityCheck : MonoBehaviour
	{
		private static bool pending;

		private static float since;

		private LoadState last = (LoadState)(-1);

		public CityCheck(IntPtr ptr)
			: base(ptr)
		{
		}//IL_0002: Unknown result type (might be due to invalid IL or missing references)


		internal static void Arm()
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			pending = false;
			CityConstructor instance = CityConstructor.Instance;
			if ((Object)(object)instance == (Object)null || string.IsNullOrWhiteSpace(Plugin.SmallCityMustHave.Value))
			{
				return;
			}
			if (instance.IsUsingCityEditor())
			{
				Plugin.L.LogInfo((object)"City check: city editor in use, no check");
				return;
			}
			int num = 0;
			try
			{
				Vector2 citySize = CityData.Instance.citySize;
				num = (int)(Math.Max(0f, citySize.x - 2f) * Math.Max(0f, citySize.y - 2f));
			}
			catch
			{
			}
			if (num > 0 && num <= Plugin.SmallCityTiles.Value)
			{
				instance.StopCityConstructionAtEndOfLoadState((LoadState)5);
				pending = true;
				since = Time.unscaledTime;
				string text = "?";
				try
				{
					RestartSafeController instance2 = RestartSafeController.Instance;
					text = ((instance2 != null) ? instance2.seed : null) ?? "?";
				}
				catch
				{
				}
				ManualLogSource l = Plugin.L;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(50, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("City check: will pause after the buildings (seed ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")");
				}
				l.LogInfo(val);
			}
		}

		private static void SwapInEden(List<NewBuilding> dir)
		{
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Expected O, but got Unknown
			try
			{
				BuildingPreset val = null;
				List<BuildingPreset> buildingPresets = HighlanderSingleton<CityBuildings>.Instance.buildingPresets;
				for (int i = 0; i < buildingPresets.Count; i++)
				{
					BuildingPreset obj = buildingPresets[i];
					if (((obj != null) ? ((Object)obj).name : null) == "EdenTower")
					{
						val = buildingPresets[i];
						break;
					}
				}
				if ((SoCustomComparison)(object)val == (SoCustomComparison)null)
				{
					Plugin.L.LogInfo((object)"City check: no EdenTower preset found, no swap");
					return;
				}
				List<string> list = (from x in Plugin.SwapOut.Value.Split(',')
					select x.Trim() into x
					where x.Length > 0
					select x).ToList();
				NewBuilding val2 = null;
				foreach (string item in list)
				{
					for (int num = 0; num < dir.Count; num++)
					{
						if (!((Object)(object)val2 == (Object)null))
						{
							break;
						}
						NewBuilding obj2 = dir[num];
						object obj3;
						if (obj2 == null)
						{
							obj3 = null;
						}
						else
						{
							BuildingPreset preset = obj2.preset;
							obj3 = ((preset != null) ? ((Object)preset).name : null);
						}
						if ((string?)obj3 == item)
						{
							val2 = dir[num];
						}
					}
					if ((Object)(object)val2 != (Object)null)
					{
						break;
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.L.LogInfo((object)"City check: nothing to swap out");
					return;
				}
				CityTile cityTile = val2.cityTile;
				int buildingID = val2.buildingID;
				string name = ((Object)val2.preset).name;
				val2.RemoveBuilding();
				Object.Instantiate<GameObject>(HighlanderSingleton<CityBuildings>.Instance.buildingPrefab, ((Component)cityTile).transform).GetComponent<NewBuilding>().Setup(cityTile, val, false, buildingID);
				ManualLogSource l = Plugin.L;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(46, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("City check: swapped ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(name);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" for EdenTower (building ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(buildingID);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(")");
				}
				l.LogInfo(val3);
			}
			catch (Exception ex)
			{
				Plugin.L.LogWarning((object)("City check: swap failed, city unchanged: " + ex));
			}
		}

		private void Update()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Invalid comparison between Unknown and I4
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Invalid comparison between Unknown and I4
			if (!pending)
			{
				return;
			}
			try
			{
				CityConstructor instance = CityConstructor.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				if (instance.loadState != last)
				{
					last = instance.loadState;
					ManualLogSource l = Plugin.L;
					bool flag = default(bool);
					BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(36, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("City check: state ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<LoadState>(last);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", hold ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(instance.useCityConstructionHold);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", complete ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(instance.stateComplete);
					}
					l.LogDebug(val);
				}
				bool flag2 = instance.useCityConstructionHold && (int)instance.loadState == 5 && instance.stateComplete;
				bool flag3 = (int)instance.loadState > 5;
				if (!flag2 && !flag3 && Time.unscaledTime - since < 600f)
				{
					return;
				}
				Dictionary<string, int> got = new Dictionary<string, int>();
				CityBuildings instance2 = HighlanderSingleton<CityBuildings>.Instance;
				List<NewBuilding> val2 = ((instance2 != null) ? instance2.buildingDirectory : null);
				int num = 0;
				while (val2 != null && num < val2.Count)
				{
					NewBuilding obj = val2[num];
					object obj2;
					if (obj == null)
					{
						obj2 = null;
					}
					else
					{
						BuildingPreset preset = obj.preset;
						obj2 = ((preset != null) ? ((Object)preset).name : null);
					}
					if (obj2 == null)
					{
						obj2 = "?";
					}
					string text = (string)obj2;
					if (!text.StartsWith("Boundary"))
					{
						got[text] = ((!got.TryGetValue(text, out var value)) ? 1 : (value + 1));
					}
					num++;
				}
				List<string> list = (from x in (from x in Plugin.SmallCityMustHave.Value.Split(',')
						select x.Trim() into x
						where x.Length > 0
						select x).ToList()
					where !got.ContainsKey(x)
					select x).ToList();
				Plugin.L.LogInfo((object)("City check (" + (flag2 ? "paused" : (flag3 ? "already past the pause" : "timed out")) + "): " + string.Join(", ", got.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> kv) => kv.Value + " " + kv.Key)) + ((list.Count == 0) ? " -> OK" : (" -> missing " + string.Join(", ", list)))));
				if (flag2 && list.Contains("EdenTower") && Plugin.SwapIn.Value)
				{
					SwapInEden(val2);
				}
				pending = false;
				instance.ClearCityConstructionHoldStatus();
			}
			catch (Exception ex)
			{
				pending = false;
				Plugin.L.LogWarning((object)("City check failed, continuing: " + ex.Message));
				try
				{
					CityConstructor.Instance.ClearCityConstructionHoldStatus();
				}
				catch
				{
				}
			}
		}
	}
}