using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using TMPro;
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("CityPlanner")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4+6bbc7710946fc89f05f5ea7793f431eeb7433293")]
[assembly: AssemblyProduct("CityPlanner")]
[assembly: AssemblyTitle("CityPlanner")]
[assembly: AssemblyVersion("1.0.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace CityPlanner
{
internal class PlannedDistrict
{
public char Key;
public string Name;
public string Type;
public LandValue LandValue;
public Density Density;
}
internal class PlannedTile
{
public PlannedDistrict District;
public string Building;
public Direction? Facing;
}
internal class Plan
{
public int Width;
public int Height;
public float Population = 1f;
public readonly Dictionary<char, PlannedDistrict> Districts = new Dictionary<char, PlannedDistrict>();
public readonly Dictionary<(int x, int y), PlannedTile> Tiles = new Dictionary<(int, int), PlannedTile>();
public static Plan Load(string path, out string error)
{
//IL_0214: Unknown result type (might be due to invalid IL or missing references)
//IL_0219: Unknown result type (might be due to invalid IL or missing references)
//IL_0229: Unknown result type (might be due to invalid IL or missing references)
//IL_022e: Unknown result type (might be due to invalid IL or missing references)
error = null;
if (!File.Exists(path))
{
error = "no plan file at " + path;
return null;
}
Plan plan = new Plan();
Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
List<string[]> list = new List<string[]>();
string text = "";
string[] array = File.ReadAllLines(path);
for (int i = 0; i < array.Length; i++)
{
string text2 = array[i].Trim();
if (text2.Length == 0 || text2.StartsWith("#"))
{
continue;
}
if (text2.StartsWith("[") && text2.EndsWith("]"))
{
string text3 = text2;
text = text3.Substring(1, text3.Length - 1 - 1).Trim().ToLowerInvariant();
continue;
}
switch (text)
{
case "city":
var (text4, text5) = KeyValue(text2);
if (text4.Equals("Size", StringComparison.OrdinalIgnoreCase))
{
string[] array3 = text5.ToLowerInvariant().Split('x');
int.TryParse(array3[0], out plan.Width);
if (array3.Length > 1)
{
int.TryParse(array3[1], out plan.Height);
}
}
else if (text4.Equals("Population", StringComparison.OrdinalIgnoreCase))
{
float.TryParse(text5, NumberStyles.Float, CultureInfo.InvariantCulture, out plan.Population);
}
break;
case "districts":
{
(string, string) tuple2 = KeyValue(text2);
string item3 = tuple2.Item1;
string[] array2 = tuple2.Item2.Split('|');
if (item3.Length != 1 || array2.Length < 4)
{
throw new FormatException("bad district line: " + text2);
}
plan.Districts[item3[0]] = new PlannedDistrict
{
Key = item3[0],
Name = array2[0].Trim(),
Type = array2[1].Trim(),
LandValue = Enum.Parse<LandValue>(array2[2].Trim(), ignoreCase: true),
Density = Enum.Parse<Density>(array2[3].Trim(), ignoreCase: true)
};
break;
}
case "buildings":
{
(string, string) tuple = KeyValue(text2);
string item = tuple.Item1;
string item2 = tuple.Item2;
dictionary[item] = item2;
break;
}
case "map":
list.Add(text2.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries));
break;
}
}
if (plan.Width < 5 || plan.Height < 5)
{
error = "missing or bad Size";
return null;
}
if (list.Count != plan.Height - 2)
{
error = $"map has {list.Count} rows, a {plan.Width}x{plan.Height} city needs {plan.Height - 2}";
return null;
}
for (int j = 0; j < list.Count; j++)
{
if (list[j].Length != plan.Width - 2)
{
error = $"map row {j + 1} has {list[j].Length} cells, needs {plan.Width - 2}";
return null;
}
for (int k = 0; k < list[j].Length; k++)
{
string[] array4 = list[j][k].Split(':');
if (array4.Length != 2 || array4[0].Length != 1 || !plan.Districts.TryGetValue(array4[0][0], out var value))
{
error = $"map row {j + 1}, column {k + 1}: '{list[j][k]}' isn't District:Building with a known district";
return null;
}
string text6 = array4[1];
Direction? facing = null;
int num = text6.IndexOf('/');
if (num >= 0)
{
string text7 = text6;
int i = num + 1;
facing = text7.Substring(i, text7.Length - i).ToUpperInvariant() switch
{
"N" => (Direction)2,
"E" => (Direction)3,
"S" => (Direction)0,
"W" => (Direction)1,
_ => null,
};
if (!facing.HasValue)
{
error = $"map row {j + 1}, column {k + 1}: facing must be /N, /E, /S or /W in '{list[j][k]}'";
return null;
}
text6 = text6.Substring(0, num);
}
if (!dictionary.TryGetValue(text6, out var value2))
{
error = $"map row {j + 1}, column {k + 1}: unknown building code '{text6}'";
return null;
}
plan.Tiles[(k + 1, plan.Height - 2 - j)] = new PlannedTile
{
District = value,
Building = value2,
Facing = facing
};
}
}
return plan;
}
private static (string, string) KeyValue(string line)
{
int num = line.IndexOf('=');
if (num >= 0)
{
string item = line.Substring(0, num).Trim();
int num2 = num + 1;
return (item, line.Substring(num2, line.Length - num2).Trim());
}
return (line.Trim(), "");
}
public PlannedTile Nearest(int x, int y)
{
return Tiles[(Math.Clamp(x, 1, Width - 2), Math.Clamp(y, 1, Height - 2))];
}
}
[BepInPlugin("sodmods.cityplanner", "City Planner", "1.0.4")]
public class Plugin : BasePlugin
{
internal static ManualLogSource Logger;
internal static Plan Plan;
internal static ConfigEntry<bool> HomeWealth;
internal static ConfigEntry<string> KeepSizes;
internal static ConfigEntry<bool> LogPerformance;
internal static ConfigEntry<bool> StreetWealth;
internal static ConfigEntry<string> GritClusters;
internal static ConfigEntry<bool> StreetThemes;
internal static ConfigEntry<bool> EdgeGates;
internal static ConfigEntry<string> OpenAlleysIn;
internal static ConfigEntry<int> EdgeGateReach;
internal static ConfigEntry<LandValue> GritFreeFrom;
internal static bool Active
{
get
{
if (Plan != null && (Object)(object)CityConstructor.Instance != (Object)null && CityConstructor.Instance.generateNew && RestartSafeController.Instance.cityX == Plan.Width)
{
return RestartSafeController.Instance.cityY == Plan.Height;
}
return false;
}
}
public override void Load()
{
//IL_027f: Unknown result type (might be due to invalid IL or missing references)
//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
//IL_01de: Expected O, but got Unknown
Logger = ((BasePlugin)this).Log;
KeepSizes = ((BasePlugin)this).Config.Bind<string>("Sizes", "Keep", "10x10,9x10", "Sizes of cities made with earlier plans, so they still load. Only ever add to the end of this list; never remove or reorder.");
HomeWealth = ((BasePlugin)this).Config.Bind<bool>("Wealth", "DistrictHomeWealth", true, "Homes take their wealth mainly from their district (otherwise mostly from floor height, as in vanilla).");
StreetWealth = ((BasePlugin)this).Config.Bind<bool>("Wealth", "AverageStreetWealth", true, "Streets take the average wealth of the tiles they cover (vanilla: their first tile), for street decoration.");
StreetThemes = ((BasePlugin)this).Config.Bind<bool>("Wealth", "StreetThemes", true, "District-themed street decoration (Chinatown gates etc.) follows the districts a street actually runs through.");
OpenAlleysIn = ((BasePlugin)this).Config.Bind<string>("Streets", "OpenAlleysIn", "Chinatown,Red Light,The Bricks,Brickrow", "Districts (planned names) where alleys become back streets, so no dead-end walls split them. Empty = vanilla.");
EdgeGates = ((BasePlugin)this).Config.Bind<bool>("Wealth", "ChinatownGatesAtEdges", true, "Paifang gates only where a street enters or leaves Chinatown.");
EdgeGateReach = ((BasePlugin)this).Config.Bind<int>("Wealth", "EdgeGateReach", 8, "How close (in path nodes; a city block is about 19) a gate must be to Chinatown's edge.");
GritClusters = ((BasePlugin)this).Config.Bind<string>("Wealth", "GritDecorations", "Shanty,BurningBarrel,StreetJunk,StaticLitter", "Street decorations (name fragments) kept out of wealthy districts in planned cities.");
GritFreeFrom = ((BasePlugin)this).Config.Bind<LandValue>("Wealth", "GritFreeFrom", (LandValue)3, "Districts at this land value or above get none of the grit decorations.");
LogPerformance = ((BasePlugin)this).Config.Bind<bool>("Debug", "LogPerformance", false, "Write frame rate and memory use to the log every minute.");
InstallBundledCities();
string path = Path.Combine(Paths.ConfigPath, "cityplan.txt");
try
{
string error = null;
Plan = (File.Exists(path) ? Plan.Load(path, out error) : null);
if (Plan == null && File.Exists(path))
{
((BasePlugin)this).Log.LogError((object)("City plan not loaded: " + error));
}
else if (Plan == null)
{
((BasePlugin)this).Log.LogInfo((object)"No city plan in the config folder: city generation is unchanged.");
}
else
{
ManualLogSource log = ((BasePlugin)this).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(45, 4, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("City plan loaded: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(Plan.Width);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("x");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(Plan.Height);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(Plan.Districts.Count);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" districts, population x");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(Plan.Population);
}
log.LogInfo(val);
}
}
catch (Exception ex)
{
((BasePlugin)this).Log.LogError((object)("City plan not loaded: " + ex.Message));
}
new Harmony("sodmods.cityplanner").PatchAll(typeof(Plugin).Assembly);
((BasePlugin)this).Log.LogInfo((object)"City Planner loaded");
}
private void InstallBundledCities()
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Expected O, but got Unknown
try
{
string text = Directory.GetFiles(Paths.PluginPath, "CityPlanner.dll", SearchOption.AllDirectories).FirstOrDefault();
string text2 = ((text == null) ? null : Path.GetDirectoryName(text));
string[] array = ((text2 == null) ? new string[0] : Directory.GetFiles(text2, "*.citb", SearchOption.AllDirectories));
if (array.Length == 0)
{
array = Directory.GetFiles(Paths.PluginPath, "Margin City*.citb", SearchOption.AllDirectories);
}
ManualLogSource log = ((BasePlugin)this).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Mod folder: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2 ?? "not found");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Bundled city files found: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(array.Length);
}
log.LogInfo(val);
if (array.Length == 0)
{
return;
}
string text3 = Path.Combine(Application.persistentDataPath, "Cities");
Directory.CreateDirectory(text3);
string[] array2 = array;
foreach (string text4 in array2)
{
string text5 = Path.ChangeExtension(text4, ".txt");
string[] array3 = ((!File.Exists(text5)) ? new string[1] { text4 } : new string[2] { text4, text5 });
foreach (string text6 in array3)
{
string text7 = Path.Combine(text3, Path.GetFileName(text6));
if (!File.Exists(text7) || new FileInfo(text7).Length != new FileInfo(text6).Length)
{
File.Copy(text6, text7, overwrite: true);
((BasePlugin)this).Log.LogInfo((object)("Installed city file " + Path.GetFileName(text6) + " into " + text3));
}
}
}
}
catch (Exception ex)
{
((BasePlugin)this).Log.LogError((object)("Couldn't install the bundled cities: " + ex.Message));
}
}
}
internal static class Sizes
{
internal static int VanillaCount = -1;
public static void Ensure()
{
CityControls instance = CityControls.Instance;
List<CitySize> val = ((instance != null) ? instance.citySizes : null);
if (val == null || VanillaCount >= 0)
{
return;
}
VanillaCount = val.Count;
string[] array = Plugin.KeepSizes.Value.Split(',', StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < array.Length; i++)
{
string[] array2 = array[i].Trim().ToLowerInvariant().Split('x');
if (array2.Length == 2 && int.TryParse(array2[0], out var result) && int.TryParse(array2[1], out var result2))
{
Add(val, result, result2);
}
}
if (Plugin.Plan != null)
{
Add(val, Plugin.Plan.Width, Plugin.Plan.Height);
}
}
private static void Add(List<CitySize> list, int w, int h)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: 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_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Expected O, but got Unknown
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < list.Count; i++)
{
if (Mathf.RoundToInt(list[i].v2.x) == w && Mathf.RoundToInt(list[i].v2.y) == h)
{
return;
}
}
list.Add(new CitySize
{
size = (Size)3,
v2 = new Vector2((float)w, (float)h)
});
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(18, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("City size slot ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(list.Count - 1);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(w);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("x");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(h);
}
logger.LogInfo(val);
}
}
[HarmonyPatch(typeof(MainMenuController), "LoadDropdownContent")]
internal static class DropdownPatch
{
private static void Prefix()
{
Sizes.Ensure();
}
private static void Postfix(MainMenuController __instance)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: 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_0129: Expected O, but got Unknown
try
{
DropdownController citySizeDropdown = __instance.citySizeDropdown;
TMP_Dropdown val = ((citySizeDropdown != null) ? citySizeDropdown.dropdown : null);
List<CitySize> citySizes = CityControls.Instance.citySizes;
if (!((Object)(object)val == (Object)null) && Sizes.VanillaCount >= 0)
{
Plan plan = Plugin.Plan;
for (int i = Sizes.VanillaCount; i < citySizes.Count; i++)
{
int num = Mathf.RoundToInt(citySizes[i].v2.x);
int num2 = Mathf.RoundToInt(citySizes[i].v2.y);
bool flag = plan != null && num == plan.Width && num2 == plan.Height;
val.options.Add(new OptionData(flag ? $"Planned city ({num}x{num2})" : $"Extra large {num}x{num2}"));
}
val.RefreshShownValue();
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)ex);
}
}
}
[HarmonyPatch(typeof(Toolbox), "GetCitySizeFromValue")]
internal static class SizeLookupPatch
{
private static void Prefix()
{
Sizes.Ensure();
}
}
[HarmonyPatch(typeof(CitizenCreator), "Populate")]
internal static class PopulationPatch
{
private static void Prefix()
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
if (Plugin.Active)
{
CityData.Instance.populationMultiplier = Mathf.Clamp(Plugin.Plan.Population, 0.1f, 2f);
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(27, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Populating with multiplier ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(CityData.Instance.populationMultiplier);
}
logger.LogInfo(val);
}
}
}
internal static class DistrictsPatch
{
internal static readonly Dictionary<int, PlannedDistrict> Created = new Dictionary<int, PlannedDistrict>();
public static void Run()
{
try
{
Apply(Plugin.Plan);
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Applying districts failed: " + ex));
}
}
private static void Apply(Plan plan)
{
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Expected O, but got Unknown
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_0221: Unknown result type (might be due to invalid IL or missing references)
//IL_0228: Expected O, but got Unknown
Dictionary<string, DistrictPreset> dictionary = new Dictionary<string, DistrictPreset>(StringComparer.OrdinalIgnoreCase);
Enumerator<DistrictPreset> enumerator = Toolbox.Instance.allDistricts.GetEnumerator();
while (enumerator.MoveNext())
{
DistrictPreset current = enumerator.Current;
dictionary[((Object)current).name] = current;
}
CityDistricts instance = HighlanderSingleton<CityDistricts>.Instance;
List<DistrictController> list = ((IEnumerable<DistrictController>)instance.districtDirectory.ToArray()).ToList();
Dictionary<char, DistrictController> dictionary2 = new Dictionary<char, DistrictController>();
Created.Clear();
bool flag = default(bool);
foreach (PlannedDistrict value3 in plan.Districts.Values)
{
if (!dictionary.TryGetValue(value3.Type, out var value))
{
ManualLogSource logger = Plugin.Logger;
BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(29, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unknown district type '");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(value3.Type);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' for ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(value3.Name);
}
logger.LogError(val);
}
else
{
DistrictController component = Object.Instantiate<GameObject>(instance.districtPrefab, PrefabControls.Instance.cityContainer.transform).GetComponent<DistrictController>();
component.Setup(value);
dictionary2[value3.Key] = component;
Created[component.districtID] = value3;
}
}
int num = 0;
Enumerator<Vector2Int, CityTile> enumerator3 = HighlanderSingleton<CityBoundaryAndTiles>.Instance.cityTiles.GetEnumerator();
while (enumerator3.MoveNext())
{
KeyValuePair<Vector2Int, CityTile> current3 = enumerator3.Current;
Vector2Int key = current3.Key;
PlannedTile plannedTile = plan.Nearest(((Vector2Int)(ref key)).x, ((Vector2Int)(ref key)).y);
if (dictionary2.TryGetValue(plannedTile.District.Key, out var value2))
{
value2.AddCityTile(current3.Value);
num++;
}
}
foreach (DistrictController item in list)
{
if (item.cityTiles.Count <= 0)
{
instance.districtDirectory.Remove(item);
Object.Destroy((Object)(object)((Component)item).gameObject);
}
}
ManualLogSource logger2 = Plugin.Logger;
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(75, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Districts applied: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(dictionary2.Count);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" planned districts, ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" tiles, removed ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(list.Count);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" generated districts");
}
logger2.LogInfo(val2);
}
}
internal static class BuildingsPatch
{
public static void Run()
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
//IL_01b5: Expected O, but got Unknown
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Expected O, but got Unknown
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
CityBuildings instance = HighlanderSingleton<CityBuildings>.Instance;
try
{
Plan plan = Plugin.Plan;
Dictionary<string, BuildingPreset> dictionary = new Dictionary<string, BuildingPreset>(StringComparer.OrdinalIgnoreCase);
Enumerator<BuildingPreset> enumerator = AssetLoader.Instance.GetAllBuildingPresets().GetEnumerator();
while (enumerator.MoveNext())
{
BuildingPreset current = enumerator.Current;
dictionary[((Object)current).name] = current;
}
Dictionary<Vector2Int, CityTile> cityTiles = HighlanderSingleton<CityBoundaryAndTiles>.Instance.cityTiles;
int num = 0;
Enumerator<Vector2Int, CityTile> enumerator2 = cityTiles.GetEnumerator();
bool flag = default(bool);
while (enumerator2.MoveNext())
{
KeyValuePair<Vector2Int, CityTile> current2 = enumerator2.Current;
Vector2Int key = current2.Key;
CityTile value = current2.Value;
PlannedTile plannedTile = plan.Nearest(((Vector2Int)(ref key)).x, ((Vector2Int)(ref key)).y);
value.landValue = plannedTile.District.LandValue;
value.density = plannedTile.District.Density;
if (!plan.Tiles.TryGetValue((((Vector2Int)(ref key)).x, ((Vector2Int)(ref key)).y), out var value2) || (Object)(object)value.building != (Object)null)
{
continue;
}
if (!dictionary.TryGetValue(value2.Building, out var value3))
{
ManualLogSource logger = Plugin.Logger;
BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(23, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unknown building '");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(value2.Building);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' at ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Vector2Int>(key);
}
logger.LogError(val);
}
else
{
Object.Instantiate<GameObject>(instance.buildingPrefab, ((Component)value).transform).GetComponent<NewBuilding>().Setup(value, value3, false, 0);
num++;
}
}
Enumerator<BlockController> enumerator3 = HighlanderSingleton<CityBlocks>.Instance.blocksDirectory.GetEnumerator();
while (enumerator3.MoveNext())
{
enumerator3.Current.UpdateAverageDensity();
}
ManualLogSource logger2 = Plugin.Logger;
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(18, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Buildings placed: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
}
logger2.LogInfo(val2);
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Placing buildings failed: " + ex));
}
}
}
[HarmonyPatch(typeof(CityConstructor), "Update")]
internal static class StagePatch
{
internal static bool DistrictsDone;
internal static bool BuildingsDone;
internal static bool AlleysDone;
private static void Prefix(CityConstructor __instance)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Invalid comparison between Unknown and I4
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Invalid comparison between Unknown and I4
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Invalid comparison between Unknown and I4
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Invalid comparison between Unknown and I4
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Invalid comparison between Unknown and I4
if (!__instance.loadingOperationActive && Plugin.Active)
{
if ((int)__instance.loadState == 3 && !DistrictsDone)
{
DistrictsDone = true;
DistrictsPatch.Run();
}
else if ((int)__instance.loadState == 5 && !BuildingsDone)
{
BuildingsDone = true;
BuildingsPatch.Run();
}
else if ((int)__instance.loadState == 7 && !AlleysDone)
{
AlleysDone = true;
OpenAlleys.Run();
}
else if ((int)__instance.loadState == 15 && !StreetThemes.Prepared)
{
StreetThemes.Prepare();
}
else if ((int)__instance.loadState >= 19 && StreetThemes.Prepared)
{
StreetThemes.Restore();
}
}
}
}
internal static class OpenAlleys
{
public static void Run()
{
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
//IL_0188: Expected O, but got Unknown
try
{
HashSet<string> hashSet = new HashSet<string>(from d in Plugin.OpenAlleysIn.Value.Split(',', StringSplitOptions.RemoveEmptyEntries)
select d.Trim(), StringComparer.OrdinalIgnoreCase);
if (hashSet.Count == 0)
{
return;
}
int num = 0;
Enumerator<StreetController> enumerator = CityData.Instance.streetDirectory.GetEnumerator();
while (enumerator.MoveNext())
{
StreetController current = enumerator.Current;
if (!current.isAlley || current.tiles.Count == 0)
{
continue;
}
string text = (from n in ((IEnumerable<NewTile>)current.tiles.ToArray()).Select(delegate(NewTile t)
{
object obj;
if (t == null)
{
obj = null;
}
else
{
CityTile cityTile = t.cityTile;
obj = ((cityTile != null) ? cityTile.district : null);
}
DistrictController val2 = (DistrictController)obj;
PlannedDistrict value;
return (val2 == null || !DistrictsPatch.Created.TryGetValue(val2.districtID, out value)) ? null : value.Name;
})
where n != null
group n by n into g
orderby g.Count() descending
select g.Key).FirstOrDefault();
if (text != null && hashSet.Contains(text))
{
current.isAlley = false;
current.SetAsBackstreet();
num++;
}
}
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(36, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Opened ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" alleys into back streets in ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", hashSet));
}
logger.LogInfo(val);
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Opening alleys failed: " + ex));
}
}
}
[HarmonyPatch(typeof(NewBuilding), "CalculateFacing")]
internal static class FacingPatch
{
internal static int Count;
private static bool Prefix(NewBuilding __instance)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Expected O, but got Unknown
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
if (!Plugin.Active || (Object)(object)__instance.cityTile == (Object)null)
{
return true;
}
Vector2Int cityCoord = __instance.cityTile.cityCoord;
if (!Plugin.Plan.Tiles.TryGetValue((((Vector2Int)(ref cityCoord)).x, ((Vector2Int)(ref cityCoord)).y), out var value) || !value.Facing.HasValue)
{
return true;
}
__instance.SetFacing(value.Facing.Value, true);
Count++;
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(21, 3, ref flag);
if (flag)
{
BuildingPreset preset = __instance.preset;
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>((preset != null) ? ((Object)preset).name : null);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" at ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Vector2Int>(cityCoord);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" faces ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Direction>(value.Facing.Value);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (planned)");
}
logger.LogInfo(val);
return false;
}
}
[HarmonyPatch(typeof(DistrictController), "UpdateName")]
internal static class DistrictNamePatch
{
private static void Postfix(DistrictController __instance)
{
if (DistrictsPatch.Created.TryGetValue(__instance.districtID, out var value))
{
((Object)__instance).name = value.Name;
((Object)((Component)__instance).transform).name = value.Name;
}
}
}
[HarmonyPatch(typeof(NewAddress), "CalculateLandValue")]
internal static class AddressWealthPatch
{
private static readonly float[] BandStart = new float[5] { 0.05f, 0.15f, 0.28f, 0.45f, 0.6f };
private static readonly float[] BandEnd = new float[5] { 0.18f, 0.3f, 0.45f, 0.62f, 0.78f };
internal static int Count;
private static void Postfix(NewAddress __instance)
{
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected I4, but got Unknown
if (!Plugin.Active || !Plugin.HomeWealth.Value)
{
return;
}
NewBuilding building = ((NewGameLocation)__instance).building;
if (!((Object)(object)((building != null) ? building.cityTile : null) == (Object)null) && !((Object)(object)((NewGameLocation)__instance).floor == (Object)null))
{
int num = Mathf.Clamp((int)((NewGameLocation)__instance).building.cityTile.landValue, 0, 4);
int num2 = 0;
Enumerator<NewRoom> enumerator = ((NewGameLocation)__instance).rooms.GetEnumerator();
while (enumerator.MoveNext())
{
NewRoom current = enumerator.Current;
num2 += current.nodes.Count;
}
float num3 = Mathf.Clamp01((Mathf.Min((float)((NewGameLocation)__instance).floor.floor / 4f, 5f) + (float)num2 * 0.05f) / 11f);
float num4 = Mathf.Lerp(BandStart[num], BandEnd[num], num3);
if ((SoCustomComparison)(object)__instance.addressPreset != (SoCustomComparison)null)
{
num4 = Mathf.Clamp(num4, __instance.addressPreset.minimumLandValue, __instance.addressPreset.maximumLandValue);
}
__instance.normalizedLandValue = num4;
Count++;
}
}
}
[HarmonyPatch(typeof(Toolbox), "GetNormalizedLandValue", new Type[]
{
typeof(NewGameLocation),
typeof(bool)
})]
internal static class StreetWealthPatch
{
private static readonly ConcurrentDictionary<IntPtr, float> Cache = new ConcurrentDictionary<IntPtr, float>();
internal static void Reset()
{
Cache.Clear();
}
private static void Postfix(NewGameLocation location, ref float __result)
{
if (!Plugin.Active || !Plugin.StreetWealth.Value || (Object)(object)location == (Object)null || (Object)(object)location.thisAsAddress != (Object)null || location.nodes == null || location.nodes.Count == 0)
{
return;
}
float fallback = __result;
__result = Cache.GetOrAdd(((Il2CppObjectBase)location).Pointer, delegate
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
float num = 0f;
int num2 = 0;
Enumerator<NewNode> enumerator = location.nodes.GetEnumerator();
while (enumerator.MoveNext())
{
NewNode current = enumerator.Current;
object obj;
if (current == null)
{
obj = null;
}
else
{
NewTile tile = current.tile;
obj = ((tile != null) ? tile.cityTile : null);
}
CityTile val = (CityTile)obj;
if (!((Object)(object)val == (Object)null))
{
num += (float)val.landValue / 4f;
num2++;
}
}
return (num2 <= 0) ? fallback : (num / (float)num2);
});
}
}
internal static class StreetThemes
{
internal sealed class Rule
{
public HashSet<string> Limit;
public HashSet<string> Ban;
public bool Gate;
public FurnitureCluster Cluster;
public bool OriginalLimit;
public bool OriginalBan;
}
internal static readonly Dictionary<IntPtr, Rule> Rules = new Dictionary<IntPtr, Rule>();
internal static readonly HashSet<IntPtr> Grit = new HashSet<IntPtr>();
internal static bool Prepared;
internal static int Placed;
internal static int Refused;
private static bool listed;
public static void Prepare()
{
//IL_031d: Unknown result type (might be due to invalid IL or missing references)
//IL_0324: Expected O, but got Unknown
Restore();
Prepared = true;
try
{
ListThemes();
string[] source = (from g in Plugin.GritClusters.Value.Split(',', StringSplitOptions.RemoveEmptyEntries)
select g.Trim()).ToArray();
Enumerator<FurnitureCluster> enumerator = Toolbox.Instance.allFurnitureClusters.GetEnumerator();
while (enumerator.MoveNext())
{
FurnitureCluster current = enumerator.Current;
if ((SoCustomComparison)(object)current == (SoCustomComparison)null || !current.allowOnStreets)
{
continue;
}
string name = ((Object)current).name;
if (source.Any((string g) => name.IndexOf(g, StringComparison.OrdinalIgnoreCase) >= 0))
{
Grit.Add(((Il2CppObjectBase)current).Pointer);
}
if (Plugin.StreetThemes.Value && (current.limitToDistricts || current.banFromDistricts))
{
Rules[((Il2CppObjectBase)current).Pointer] = new Rule
{
Limit = (current.limitToDistricts ? new HashSet<string>(((IEnumerable<DistrictPreset>)current.allowedInDistricts.ToArray()).Select((DistrictPreset d) => ((Object)d).name)) : null),
Ban = (current.banFromDistricts ? new HashSet<string>(((IEnumerable<DistrictPreset>)current.notAllowedInDistricts.ToArray()).Select((DistrictPreset d) => ((Object)d).name)) : null),
Gate = (name.IndexOf("Paifang", StringComparison.OrdinalIgnoreCase) >= 0),
Cluster = current,
OriginalLimit = current.limitToDistricts,
OriginalBan = current.banFromDistricts
};
current.limitToDistricts = false;
current.banFromDistricts = false;
}
}
int num = 0;
if (Plugin.StreetThemes.Value)
{
Enumerator<StreetController> enumerator2 = CityData.Instance.streetDirectory.GetEnumerator();
while (enumerator2.MoveNext())
{
StreetController current2 = enumerator2.Current;
Dictionary<int, (DistrictController, int)> dictionary = new Dictionary<int, (DistrictController, int)>();
Enumerator<NewRoom> enumerator3 = ((NewGameLocation)current2).rooms.GetEnumerator();
while (enumerator3.MoveNext())
{
Enumerator<NewNode> enumerator4 = enumerator3.Current.nodes.GetEnumerator();
while (enumerator4.MoveNext())
{
NewNode current3 = enumerator4.Current;
object obj;
if (current3 == null)
{
obj = null;
}
else
{
NewTile tile = current3.tile;
if (tile == null)
{
obj = null;
}
else
{
CityTile cityTile = tile.cityTile;
obj = ((cityTile != null) ? cityTile.district : null);
}
}
DistrictController val = (DistrictController)obj;
if (!((Object)(object)val == (Object)null))
{
dictionary.TryGetValue(val.districtID, out var value);
dictionary[val.districtID] = (val, value.Item2 + 1);
}
}
}
if (dictionary.Count != 0)
{
DistrictController item = dictionary.Values.OrderByDescending<(DistrictController, int), int>(((DistrictController d, int n) c) => c.n).First().Item1;
if ((Object)(object)((NewGameLocation)current2).district != (Object)(object)item)
{
((NewGameLocation)current2).district = item;
num++;
}
}
}
}
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(86, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Street themes ready: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(Rules.Count);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" district rules lifted, ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(Grit.Count);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" grit decorations, ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" streets re-districted");
}
logger.LogInfo(val2);
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Street themes: " + ex));
}
}
public static void Restore()
{
foreach (Rule value in Rules.Values)
{
if (!((SoCustomComparison)(object)value.Cluster == (SoCustomComparison)null))
{
value.Cluster.limitToDistricts = value.OriginalLimit;
value.Cluster.banFromDistricts = value.OriginalBan;
}
}
Rules.Clear();
Grit.Clear();
Prepared = false;
}
private static void ListThemes()
{
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Expected O, but got Unknown
if (listed)
{
return;
}
listed = true;
Enumerator<FurnitureCluster> enumerator = Toolbox.Instance.allFurnitureClusters.GetEnumerator();
bool flag = default(bool);
while (enumerator.MoveNext())
{
FurnitureCluster current = enumerator.Current;
if (!((SoCustomComparison)(object)current == (SoCustomComparison)null) && current.allowOnStreets && (current.limitToDistricts || current.banFromDistricts))
{
string text = (current.limitToDistricts ? ("only " + string.Join("+", ((IEnumerable<DistrictPreset>)current.allowedInDistricts.ToArray()).Select((DistrictPreset d) => ((Object)d).name))) : "");
string text2 = (current.banFromDistricts ? (" not " + string.Join("+", ((IEnumerable<DistrictPreset>)current.notAllowedInDistricts.ToArray()).Select((DistrictPreset d) => ((Object)d).name))) : "");
ManualLogSource logger = Plugin.Logger;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(47, 5, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Themed decoration ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)current).name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (max ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(current.maximumPerRoom);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" per street, chance ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(current.placementChance);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")");
}
logger.LogInfo(val);
}
}
}
}
[HarmonyPatch(typeof(GenerationController), "GetBestFurnitureClusterLocation")]
internal static class PlacementPatch
{
private static void Postfix(NewRoom room, FurnitureCluster cluster, ref FurnitureClusterLocation __result)
{
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
if (__result == null || !StreetThemes.Prepared || (SoCustomComparison)(object)cluster == (SoCustomComparison)null || (Object)(object)((room != null) ? room.gameLocation : null) == (Object)null)
{
return;
}
bool flag = (Object)(object)room.gameLocation.thisAsStreet != (Object)null;
if (StreetThemes.Rules.TryGetValue(((Il2CppObjectBase)cluster).Pointer, out var value))
{
object obj;
if (!flag)
{
obj = room.gameLocation.district;
}
else
{
NewNode anchorNode = __result.anchorNode;
if (anchorNode == null)
{
obj = null;
}
else
{
NewTile tile = anchorNode.tile;
if (tile == null)
{
obj = null;
}
else
{
CityTile cityTile = tile.cityTile;
obj = ((cityTile != null) ? cityTile.district : null);
}
}
}
DistrictController val = (DistrictController)obj;
DistrictPreset val2 = ((val != null) ? val.preset : null);
bool flag2 = (SoCustomComparison)(object)val2 != (SoCustomComparison)null && (value.Limit == null || value.Limit.Contains(((Object)val2).name)) && (value.Ban == null || !value.Ban.Contains(((Object)val2).name));
if (flag2 && flag && value.Gate && Plugin.EdgeGates.Value)
{
flag2 = NearDistrictEdge(__result.anchorNode, val, Plugin.EdgeGateReach.Value);
}
if (!flag2)
{
__result = null;
return;
}
if (flag)
{
Interlocked.Increment(ref StreetThemes.Placed);
}
}
if (flag && StreetThemes.Grit.Contains(((Il2CppObjectBase)cluster).Pointer))
{
NewNode anchorNode2 = __result.anchorNode;
object obj2;
if (anchorNode2 == null)
{
obj2 = null;
}
else
{
NewTile tile2 = anchorNode2.tile;
obj2 = ((tile2 != null) ? tile2.cityTile : null);
}
CityTile val3 = (CityTile)obj2;
if (!((Object)(object)val3 == (Object)null) && val3.landValue >= Plugin.GritFreeFrom.Value)
{
__result = null;
Interlocked.Increment(ref StreetThemes.Refused);
}
}
}
private static bool NearDistrictEdge(NewNode node, DistrictController district, int reach)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
if (node == null || (Object)(object)district == (Object)null)
{
return false;
}
Dictionary<Vector3, NewNode> nodeMap = PathFinder.Instance.nodeMap;
Vector3Int nodeCoord = node.nodeCoord;
NewNode val = default(NewNode);
for (int i = 1; i <= reach; i++)
{
(int, int)[] array = new(int, int)[4]
{
(i, 0),
(-i, 0),
(0, i),
(0, -i)
};
for (int j = 0; j < array.Length; j++)
{
var (num, num2) = array[j];
if (!nodeMap.TryGetValue(Vector3Int.op_Implicit(new Vector3Int(((Vector3Int)(ref nodeCoord)).x + num, ((Vector3Int)(ref nodeCoord)).y + num2, ((Vector3Int)(ref nodeCoord)).z)), ref val))
{
continue;
}
object obj;
if (val == null)
{
obj = null;
}
else
{
NewTile tile = val.tile;
if (tile == null)
{
obj = null;
}
else
{
CityTile cityTile = tile.cityTile;
obj = ((cityTile != null) ? cityTile.district : null);
}
}
DistrictController val2 = (DistrictController)obj;
if ((Object)(object)val2 != (Object)null && val2.districtID != district.districtID)
{
return true;
}
}
}
return false;
}
}
[HarmonyPatch(typeof(CityConstructor), "GenerateNewCity")]
internal static class GenerateTimer
{
internal static Stopwatch Timer;
private static void Prefix()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Expected O, but got Unknown
StagePatch.DistrictsDone = (StagePatch.BuildingsDone = (StagePatch.AlleysDone = false));
AddressWealthPatch.Count = 0;
StreetThemes.Restore();
StreetThemes.Refused = 0;
StreetThemes.Placed = 0;
StreetWealthPatch.Reset();
Timer = Stopwatch.StartNew();
RestartSafeController instance = RestartSafeController.Instance;
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(16, 4, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Generating ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(instance.cityName);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" at ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(instance.cityX);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("x");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(instance.cityY);
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Plugin.Active ? " from the plan" : "");
}
logger.LogInfo(val);
}
}
[HarmonyPatch(typeof(Player), "Update")]
internal static class Telemetry
{
private static bool started;
private static float windowStart;
private static float lastFrame;
private static int frames;
private static float minFps = float.MaxValue;
private static long MemoryMb()
{
return Process.GetCurrentProcess().WorkingSet64 / 1048576;
}
private static void Postfix()
{
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0219: Unknown result type (might be due to invalid IL or missing references)
//IL_0220: Expected O, but got Unknown
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
if (!Plugin.LogPerformance.Value || (Object)(object)SessionData.Instance == (Object)null || !SessionData.Instance.startedGame)
{
return;
}
float unscaledTime = Time.unscaledTime;
if (!started)
{
started = true;
windowStart = unscaledTime;
Vector2 citySize = CityData.Instance.citySize;
string value = ((GenerateTimer.Timer != null) ? $"{GenerateTimer.Timer.Elapsed.TotalSeconds:0}s since generation started" : "loaded from file");
Plugin.Logger.LogInfo((object)($"In game: city {citySize.x}x{citySize.y}, {CityData.Instance.citizenDirectory.Count} citizens, {value}, memory {MemoryMb()} MB" + ((AddressWealthPatch.Count > 0) ? $", {AddressWealthPatch.Count} addresses re-valued, {StreetThemes.Refused} grit decorations kept out of wealthy streets, {StreetThemes.Placed} themed decorations placed" : "")));
GenerateTimer.Timer = null;
}
frames++;
if (lastFrame > 0f && unscaledTime > lastFrame)
{
minFps = Mathf.Min(minFps, 1f / (unscaledTime - lastFrame));
}
lastFrame = unscaledTime;
if (!(unscaledTime - windowStart < 60f))
{
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(43, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Perf: avg ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>((float)frames / (unscaledTime - windowStart), "0");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" fps, worst frame ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(1000f / Mathf.Max(minFps, 0.1f), "0");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ms, memory ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<long>(MemoryMb());
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" MB");
}
logger.LogInfo(val);
windowStart = unscaledTime;
frames = 0;
minFps = float.MaxValue;
}
}
}
}