Decompiled source of TravelButton v1.1.1

TravelButton.dll

Decompiled 5 months ago
using System;
using System.Collections;
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.Serialization;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TravelButton")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+b010cf8fb4bf25c1740146cc060f906386a13ccc")]
[assembly: AssemblyProduct("TravelButton")]
[assembly: AssemblyTitle("TravelButton")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
[Serializable]
public class CityConfig
{
	public string name;

	public int price = -1;

	public float[] coords;

	public string targetGameObjectName;

	public string sceneName;

	public string desc;

	public bool visited = false;

	[JsonIgnore]
	public bool Visited
	{
		get
		{
			return visited;
		}
		set
		{
			visited = value;
		}
	}

	public CityConfig()
	{
	}

	public CityConfig(string name)
	{
		this.name = name;
	}

	public CityConfig(string name, int price, float[] coords, string targetGameObjectName, string sceneName, string desc)
	{
		this.name = name;
		this.price = price;
		this.coords = coords;
		this.targetGameObjectName = targetGameObjectName;
		this.sceneName = sceneName;
		this.desc = desc;
		visited = false;
	}
}
[Serializable]
public class TravelConfig
{
	public List<CityConfig> cities = new List<CityConfig>();

	public static TravelConfig LoadFromFile(string filePath)
	{
		if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
		{
			return null;
		}
		try
		{
			string text = File.ReadAllText(filePath);
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			TravelConfig travelConfig = JsonUtility.FromJson<TravelConfig>(text);
			if (travelConfig != null && travelConfig.cities != null && travelConfig.cities.Count > 0)
			{
				return travelConfig;
			}
			return ParseObjectKeyedForm(text);
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("[TravelButton] TravelConfig.LoadFromFile failed: " + ex.Message));
			return null;
		}
	}

	private static TravelConfig ParseObjectKeyedForm(string json)
	{
		try
		{
			int num = json.IndexOf("\"cities\"", StringComparison.OrdinalIgnoreCase);
			if (num < 0)
			{
				return null;
			}
			int num2 = json.IndexOf(':', num);
			if (num2 < 0)
			{
				return null;
			}
			int i;
			for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
			{
			}
			if (i >= json.Length || json[i] != '{')
			{
				return null;
			}
			int num3 = i;
			int num4 = 0;
			bool flag = false;
			int num5 = -1;
			for (; i < json.Length; i++)
			{
				char c = json[i];
				if (c == '"' && (i == 0 || json[i - 1] != '\\'))
				{
					flag = !flag;
				}
				if (!flag)
				{
					switch (c)
					{
					case '{':
						num4++;
						continue;
					case '}':
						break;
					default:
						continue;
					}
					num4--;
					if (num4 == 0)
					{
						num5 = i;
						break;
					}
				}
			}
			if (num5 < 0)
			{
				return null;
			}
			string text = json.Substring(num3 + 1, num5 - num3 - 1);
			List<CityConfig> list = new List<CityConfig>();
			int j = 0;
			while (j < text.Length)
			{
				for (; j < text.Length && (char.IsWhiteSpace(text[j]) || text[j] == ','); j++)
				{
				}
				if (j >= text.Length || text[j] != '"')
				{
					break;
				}
				int num6 = j;
				int num7 = FindClosingQuote(text, num6);
				if (num7 < 0)
				{
					break;
				}
				string cityName = text.Substring(num6 + 1, num7 - num6 - 1);
				for (j = num7 + 1; j < text.Length && char.IsWhiteSpace(text[j]); j++)
				{
				}
				if (j < text.Length && text[j] == ':')
				{
					j++;
				}
				for (; j < text.Length && char.IsWhiteSpace(text[j]); j++)
				{
				}
				if (j >= text.Length || text[j] != '{')
				{
					break;
				}
				int num8 = j;
				int num9 = FindMatchingBrace(text, num8);
				if (num9 < 0)
				{
					break;
				}
				string cityJson = text.Substring(num8, num9 - num8 + 1);
				CityConfig cityConfig = ParseCityObject(cityName, cityJson);
				if (cityConfig != null)
				{
					list.Add(cityConfig);
				}
				j = num9 + 1;
			}
			if (list.Count > 0)
			{
				TravelConfig travelConfig = new TravelConfig();
				travelConfig.cities = list;
				return travelConfig;
			}
			return null;
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("[TravelButton] ParseObjectKeyedForm failed: " + ex.Message));
			return null;
		}
	}

	private static CityConfig ParseCityObject(string cityName, string cityJson)
	{
		try
		{
			CityConfig cityConfig = new CityConfig();
			cityConfig.name = cityName;
			cityConfig.coords = ExtractFloatArray(cityJson, "coords");
			cityConfig.targetGameObjectName = ExtractString(cityJson, "targetGameObjectName");
			cityConfig.sceneName = ExtractString(cityJson, "sceneName");
			cityConfig.desc = ExtractString(cityJson, "desc");
			cityConfig.price = ExtractInt(cityJson, "price").GetValueOrDefault(-1);
			cityConfig.visited = ExtractBool(cityJson, "visited").GetValueOrDefault();
			return cityConfig;
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("[TravelButton] ParseCityObject failed for " + cityName + ": " + ex.Message));
			return null;
		}
	}

	private static string ExtractString(string json, string propName)
	{
		int num = json.IndexOf("\"" + propName + "\"", StringComparison.OrdinalIgnoreCase);
		if (num < 0)
		{
			return null;
		}
		int num2 = json.IndexOf(':', num);
		if (num2 < 0)
		{
			return null;
		}
		int i;
		for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
		{
		}
		if (i >= json.Length || json[i] != '"')
		{
			return null;
		}
		int num3 = FindClosingQuote(json, i);
		if (num3 < 0)
		{
			return null;
		}
		return json.Substring(i + 1, num3 - i - 1);
	}

	private static int? ExtractInt(string json, string propName)
	{
		int num = json.IndexOf("\"" + propName + "\"", StringComparison.OrdinalIgnoreCase);
		if (num < 0)
		{
			return null;
		}
		int num2 = json.IndexOf(':', num);
		if (num2 < 0)
		{
			return null;
		}
		int i;
		for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
		{
		}
		if (i >= json.Length)
		{
			return null;
		}
		int num3 = i;
		for (; i < json.Length && (char.IsDigit(json[i]) || json[i] == '-' || json[i] == '+'); i++)
		{
		}
		string s = json.Substring(num3, i - num3);
		if (int.TryParse(s, out var result))
		{
			return result;
		}
		return null;
	}

	private static float[] ExtractFloatArray(string json, string propName)
	{
		int num = json.IndexOf("\"" + propName + "\"", StringComparison.OrdinalIgnoreCase);
		if (num < 0)
		{
			return null;
		}
		int num2 = json.IndexOf(':', num);
		if (num2 < 0)
		{
			return null;
		}
		int i;
		for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
		{
		}
		if (i >= json.Length || json[i] != '[')
		{
			return null;
		}
		int num3 = i;
		int num4 = FindMatchingBracket(json, num3);
		if (num4 < 0)
		{
			return null;
		}
		string text = json.Substring(num3 + 1, num4 - num3 - 1);
		string[] array = text.Split(new char[1] { ',' });
		List<float> list = new List<float>();
		string[] array2 = array;
		foreach (string text2 in array2)
		{
			string s = text2.Trim();
			if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				list.Add(result);
			}
		}
		return (list.Count > 0) ? list.ToArray() : null;
	}

	private static bool? ExtractBool(string json, string propName)
	{
		int num = json.IndexOf("\"" + propName + "\"", StringComparison.OrdinalIgnoreCase);
		if (num < 0)
		{
			return null;
		}
		int num2 = json.IndexOf(':', num);
		if (num2 < 0)
		{
			return null;
		}
		int i;
		for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
		{
		}
		if (i >= json.Length)
		{
			return null;
		}
		int length = Math.Min(5, json.Length - i);
		string text = json.Substring(i, length).ToLowerInvariant();
		if (text.StartsWith("true"))
		{
			return true;
		}
		if (text.StartsWith("false"))
		{
			return false;
		}
		return null;
	}

	private static int FindClosingQuote(string json, int startIdx)
	{
		if (startIdx < 0 || startIdx >= json.Length)
		{
			return -1;
		}
		if (json[startIdx] != '"')
		{
			return -1;
		}
		for (int i = startIdx + 1; i < json.Length; i++)
		{
			if (json[i] == '"' && json[i - 1] != '\\')
			{
				return i;
			}
		}
		return -1;
	}

	private static int FindMatchingBrace(string json, int startIdx)
	{
		if (startIdx < 0 || startIdx >= json.Length)
		{
			return -1;
		}
		if (json[startIdx] != '{')
		{
			return -1;
		}
		int num = 0;
		bool flag = false;
		for (int i = startIdx; i < json.Length; i++)
		{
			char c = json[i];
			if (c == '"' && (i == 0 || json[i - 1] != '\\'))
			{
				flag = !flag;
			}
			if (flag)
			{
				continue;
			}
			switch (c)
			{
			case '{':
				num++;
				break;
			case '}':
				num--;
				if (num == 0)
				{
					return i;
				}
				break;
			}
		}
		return -1;
	}

	private static int FindMatchingBracket(string json, int startIdx)
	{
		if (startIdx < 0 || startIdx >= json.Length)
		{
			return -1;
		}
		if (json[startIdx] != '[')
		{
			return -1;
		}
		int num = 0;
		bool flag = false;
		for (int i = startIdx; i < json.Length; i++)
		{
			char c = json[i];
			if (c == '"' && (i == 0 || json[i - 1] != '\\'))
			{
				flag = !flag;
			}
			if (flag)
			{
				continue;
			}
			switch (c)
			{
			case '[':
				num++;
				break;
			case ']':
				num--;
				if (num == 0)
				{
					return i;
				}
				break;
			}
		}
		return -1;
	}

	public static TravelConfig Default()
	{
		try
		{
			Type type = null;
			try
			{
				type = Type.GetType("ConfigManager") ?? typeof(ConfigManager);
			}
			catch
			{
				type = null;
			}
			if (type != null)
			{
				MethodInfo method = type.GetMethod("Default", BindingFlags.Static | BindingFlags.Public);
				if (method != null)
				{
					object obj2 = method.Invoke(null, null);
					if (obj2 != null)
					{
						object obj3 = null;
						PropertyInfo property = obj2.GetType().GetProperty("cities", BindingFlags.Instance | BindingFlags.Public);
						if (property != null)
						{
							obj3 = property.GetValue(obj2);
						}
						else
						{
							FieldInfo field = obj2.GetType().GetField("cities", BindingFlags.Instance | BindingFlags.Public);
							if (field != null)
							{
								obj3 = field.GetValue(obj2);
							}
						}
						if (obj3 != null)
						{
							TravelConfig travelConfig = new TravelConfig();
							if (obj3 is IDictionary)
							{
								CityMappingHelpers.MapDefaultCityToTarget(obj3, travelConfig.cities);
								return travelConfig;
							}
							if (obj3 is IEnumerable)
							{
								CityMappingHelpers.MapParsedCityToTarget(obj3, travelConfig.cities);
								return travelConfig;
							}
						}
					}
				}
			}
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("[TravelButton] TravelConfig.Default reflection/mapping failed: " + ex));
		}
		return new TravelConfig();
	}
}
[Serializable]
public class JsonCityConfig
{
	public string name;

	public int? price;

	public float[] coords;

	public string targetGameObjectName;

	public string sceneName;

	public string desc;

	public bool visited = false;

	public JsonCityConfig()
	{
	}

	public JsonCityConfig(string name)
	{
		this.name = name;
	}
}
[Serializable]
public class JsonTravelConfig
{
	public List<JsonCityConfig> cities = new List<JsonCityConfig>();

	public static JsonTravelConfig Default()
	{
		JsonTravelConfig jsonTravelConfig = new JsonTravelConfig();
		TBLog.Info("JsonTravelConfig.Default: invoked to build defaults.");
		try
		{
			object obj = null;
			try
			{
				obj = ConfigManager.Default();
				TBLog.Info("JsonTravelConfig.Default: ConfigManager.Default() called directly.");
			}
			catch (Exception ex)
			{
				TBLog.Info("JsonTravelConfig.Default: direct call to ConfigManager.Default() failed: " + ex.Message);
				obj = null;
			}
			if (obj == null)
			{
				try
				{
					Type type = Type.GetType("ConfigManager");
					if (type != null)
					{
						MethodInfo method = type.GetMethod("Default", BindingFlags.Static | BindingFlags.Public);
						if (method != null)
						{
							obj = method.Invoke(null, null);
							TBLog.Info("JsonTravelConfig.Default: obtained defaults via reflection from ConfigManager.Default().");
						}
					}
				}
				catch (Exception ex2)
				{
					TBLog.Warn("JsonTravelConfig.Default: reflection call failed: " + ex2);
					obj = null;
				}
			}
			if (obj == null)
			{
				TBLog.Warn("JsonTravelConfig.Default: no defaults object available from ConfigManager.Default(); returning empty list.");
				return jsonTravelConfig;
			}
			object obj2 = null;
			try
			{
				Type type2 = obj.GetType();
				PropertyInfo property = type2.GetProperty("cities", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					obj2 = property.GetValue(obj);
				}
				else
				{
					FieldInfo field = type2.GetField("cities", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						obj2 = field.GetValue(obj);
					}
				}
			}
			catch (Exception ex3)
			{
				TBLog.Warn("JsonTravelConfig.Default: failed to get 'cities' member from defaultsObj: " + ex3);
				obj2 = null;
			}
			if (obj2 == null)
			{
				TBLog.Warn("JsonTravelConfig.Default: 'cities' member null on defaults object; returning empty list.");
				return jsonTravelConfig;
			}
			if (obj2 is IDictionary dictionary)
			{
				foreach (DictionaryEntry item3 in dictionary)
				{
					try
					{
						string text = item3.Key as string;
						object value = item3.Value;
						if (string.IsNullOrEmpty(text))
						{
							text = TryGetStringFromObject(value, "name") ?? TryGetStringFromObject(value, "Name") ?? "<unknown>";
						}
						JsonCityConfig item = new JsonCityConfig(text)
						{
							price = TryGetIntFromObject(value, "price"),
							coords = TryGetFloatArrayFromObject(value, "coords"),
							targetGameObjectName = (TryGetStringFromObject(value, "targetGameObjectName") ?? TryGetStringFromObject(value, "target")),
							sceneName = (TryGetStringFromObject(value, "sceneName") ?? TryGetStringFromObject(value, "scene")),
							desc = (TryGetStringFromObject(value, "desc") ?? TryGetStringFromObject(value, "description")),
							visited = TryGetBoolFromObject(value, "visited").GetValueOrDefault()
						};
						jsonTravelConfig.cities.Add(item);
					}
					catch
					{
					}
				}
				TBLog.Info($"JsonTravelConfig.Default: mapped {jsonTravelConfig.cities.Count} cities from dictionary defaults.");
				return jsonTravelConfig;
			}
			if (obj2 is IEnumerable enumerable)
			{
				int num = 0;
				foreach (object item4 in enumerable)
				{
					try
					{
						if (item4 != null)
						{
							string text2 = TryGetStringFromObject(item4, "name") ?? TryGetStringFromObject(item4, "Name") ?? item4.ToString();
							JsonCityConfig item2 = new JsonCityConfig(text2 ?? string.Empty)
							{
								price = TryGetIntFromObject(item4, "price"),
								coords = TryGetFloatArrayFromObject(item4, "coords"),
								targetGameObjectName = (TryGetStringFromObject(item4, "targetGameObjectName") ?? TryGetStringFromObject(item4, "target")),
								sceneName = (TryGetStringFromObject(item4, "sceneName") ?? TryGetStringFromObject(item4, "scene")),
								desc = (TryGetStringFromObject(item4, "desc") ?? TryGetStringFromObject(item4, "description")),
								visited = TryGetBoolFromObject(item4, "visited").GetValueOrDefault()
							};
							jsonTravelConfig.cities.Add(item2);
							num++;
						}
					}
					catch
					{
					}
				}
				TBLog.Info($"JsonTravelConfig.Default: mapped {num} cities from enumerable defaults (total {jsonTravelConfig.cities.Count}).");
				return jsonTravelConfig;
			}
			TBLog.Warn("JsonTravelConfig.Default: 'cities' member exists but is not IDictionary or IEnumerable; returning whatever was collected.");
			return jsonTravelConfig;
		}
		catch (Exception ex4)
		{
			TBLog.Warn("JsonTravelConfig.Default: unexpected error: " + ex4);
			return jsonTravelConfig;
		}
	}

	private static int? TryGetIntFromObject(object obj, string propName)
	{
		if (obj == null)
		{
			return null;
		}
		Type type = obj.GetType();
		try
		{
			PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanRead)
			{
				object value = property.GetValue(obj);
				if (value is int value2)
				{
					return value2;
				}
				try
				{
					return Convert.ToInt32(value);
				}
				catch
				{
				}
			}
			FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				object value3 = field.GetValue(obj);
				if (value3 is int value4)
				{
					return value4;
				}
				try
				{
					return Convert.ToInt32(value3);
				}
				catch
				{
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static bool? TryGetBoolFromObject(object obj, string propName)
	{
		if (obj == null)
		{
			return null;
		}
		Type type = obj.GetType();
		try
		{
			PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanRead)
			{
				object value = property.GetValue(obj);
				if (value is bool value2)
				{
					return value2;
				}
				if (value != null && bool.TryParse(value.ToString(), out var result))
				{
					return result;
				}
			}
			FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				object value3 = field.GetValue(obj);
				if (value3 is bool value4)
				{
					return value4;
				}
				if (value3 != null && bool.TryParse(value3.ToString(), out var result2))
				{
					return result2;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static string TryGetStringFromObject(object obj, string propName)
	{
		if (obj == null)
		{
			return null;
		}
		Type type = obj.GetType();
		try
		{
			PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanRead && property.PropertyType == typeof(string))
			{
				return property.GetValue(obj) as string;
			}
			FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && field.FieldType == typeof(string))
			{
				return field.GetValue(obj) as string;
			}
		}
		catch
		{
		}
		return null;
	}

	private static float[] TryGetFloatArrayFromObject(object obj, string propName)
	{
		if (obj == null)
		{
			return null;
		}
		Type type = obj.GetType();
		try
		{
			PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			object obj2 = null;
			if (property != null && property.CanRead)
			{
				obj2 = property.GetValue(obj);
			}
			else
			{
				FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					obj2 = field.GetValue(obj);
				}
			}
			if (obj2 == null)
			{
				return null;
			}
			if (obj2 is float[] result)
			{
				return result;
			}
			if (obj2 is IEnumerable enumerable)
			{
				List<float> list = new List<float>();
				foreach (object item in enumerable)
				{
					try
					{
						list.Add(Convert.ToSingle(item));
					}
					catch
					{
					}
				}
				if (list.Count > 0)
				{
					return list.ToArray();
				}
			}
		}
		catch
		{
		}
		return null;
	}

	public static JsonTravelConfig LoadFromJson(string path)
	{
		try
		{
			if (!File.Exists(path))
			{
				return null;
			}
			string text = File.ReadAllText(path, Encoding.UTF8);
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			List<string> list = new List<string>();
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				string text3 = text2.TrimStart(Array.Empty<char>());
				if (!text3.StartsWith("//"))
				{
					list.Add(text2);
				}
			}
			text = string.Join("\n", list);
			JsonTravelConfig jsonTravelConfig = JsonUtility.FromJson<JsonTravelConfig>(text);
			if (jsonTravelConfig != null && jsonTravelConfig.cities != null)
			{
				return jsonTravelConfig;
			}
			return null;
		}
		catch (Exception ex)
		{
			TBLog.Warn("LoadFromJson failed for " + path + ": " + ex.Message);
			return null;
		}
	}

	public void SaveToJson(string path)
	{
		try
		{
			TBLog.Info("JsonTravelConfig.SaveToJson: begin write to '" + path + "'");
			try
			{
				if (File.Exists(path))
				{
					FileInfo fileInfo = new FileInfo(path);
					TBLog.Info($"JsonTravelConfig.SaveToJson: existing file detected. FullName='{fileInfo.FullName}', Length={fileInfo.Length}, LastWriteTime={fileInfo.LastWriteTimeUtc:O}");
				}
				else
				{
					TBLog.Info("JsonTravelConfig.SaveToJson: no existing file at target path.");
				}
			}
			catch (Exception ex)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: failed querying existing file info: " + ex);
			}
			string directoryName = Path.GetDirectoryName(path);
			if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			if (cities == null)
			{
				cities = new List<JsonCityConfig>();
			}
			try
			{
				TBLog.Info($"JsonTravelConfig.SaveToJson: cities.Count = {cities.Count}");
				int num = 0;
				foreach (JsonCityConfig city in cities)
				{
					if (city == null)
					{
						TBLog.Info($"  city[{num}]: <null>");
					}
					else
					{
						string text = "<null>";
						try
						{
							text = ((city.coords != null) ? ("[" + string.Join(", ", city.coords) + "]") : "<null>");
						}
						catch
						{
							text = "<err>";
						}
						TBLog.Info(string.Format("  city[{0}] name='{1}' scene='{2}' coords={3} price={4} visited={5}", num, city.name ?? "", city.sceneName ?? "", text, city.price, city.visited));
					}
					num++;
					if (num >= 50)
					{
						TBLog.Info("  ...stopping city listing after 50 entries");
						break;
					}
				}
			}
			catch (Exception ex2)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: failed to enumerate cities for debug: " + ex2);
			}
			try
			{
				int num2 = 12;
				StackTrace stackTrace = new StackTrace(1, fNeedFileInfo: true);
				StackFrame[] frames = stackTrace.GetFrames();
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine($"JsonTravelConfig.SaveToJson: writing {cities.Count} entries to {path}. Callers:");
				if (frames != null)
				{
					int num3 = Math.Min(num2, frames.Length);
					for (int i = 0; i < num3; i++)
					{
						StackFrame stackFrame = frames[i];
						MethodBase method = stackFrame.GetMethod();
						Type type = method?.DeclaringType;
						string text2 = ((type != null) ? (type.FullName + "." + method.Name) : (method?.Name ?? "<unknown>"));
						string fileName = stackFrame.GetFileName();
						int fileLineNumber = stackFrame.GetFileLineNumber();
						stringBuilder.AppendLine("  at " + text2 + " (file: " + (fileName ?? "<unknown>") + ":" + ((fileLineNumber > 0) ? fileLineNumber.ToString() : "0") + ")");
					}
					if (frames.Length > num2)
					{
						stringBuilder.AppendLine($"  ... ({frames.Length - num2} more frames)");
					}
				}
				else
				{
					stringBuilder.AppendLine("  <no stack frames available>");
				}
				TBLog.Info(stringBuilder.ToString());
			}
			catch (Exception ex3)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: failed to build trimmed stack trace: " + ex3.Message);
				TBLog.Info("JsonTravelConfig.SaveToJson full stack:\n" + Environment.StackTrace);
			}
			string text3 = "TravelButton_Cities.json";
			string value;
			try
			{
				value = JsonConvert.SerializeObject((object)this, (Formatting)1);
			}
			catch (Exception ex4)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: Newtonsoft serialization failed, falling back to JsonUtility: " + ex4);
				value = JsonUtility.ToJson((object)this, true) ?? "{}";
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			stringBuilder2.AppendLine("// " + text3);
			stringBuilder2.AppendLine("// Schema: { \"cities\": [ { \"name\": \"...\", \"coords\": [x,y,z], \"targetGameObjectName\": \"...\", \"sceneName\": \"...\", \"desc\": \"...\" }, ... ] }");
			stringBuilder2.AppendLine("// Note: 'enabled' and 'price' are managed by BepInEx config, not this file.");
			stringBuilder2.AppendLine();
			stringBuilder2.Append(value);
			try
			{
				int length = stringBuilder2.Length;
				TBLog.Info($"JsonTravelConfig.SaveToJson: serialized JSON length = {length} chars");
				int num4 = Math.Min(1024, length);
				if (num4 > 0)
				{
					string arg = stringBuilder2.ToString(0, num4).Replace("\r\n", "\\n");
					TBLog.Info($"JsonTravelConfig.SaveToJson: JSON preview (first {num4} chars):\n{arg}");
				}
			}
			catch (Exception ex5)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: failed to log JSON preview: " + ex5);
			}
			try
			{
				File.WriteAllText(path, stringBuilder2.ToString(), Encoding.UTF8);
			}
			catch (Exception arg2)
			{
				TBLog.Warn($"JsonTravelConfig.SaveToJson: File.WriteAllText failed for {path}: {arg2}");
				throw;
			}
			try
			{
				FileInfo fileInfo2 = new FileInfo(path);
				TBLog.Info($"SaveToJson: wrote {Path.GetFileName(path)} to {path} (length={fileInfo2.Length}, lastWriteUtc={fileInfo2.LastWriteTimeUtc:O})");
			}
			catch (Exception ex6)
			{
				TBLog.Warn("JsonTravelConfig.SaveToJson: failed to stat file after write: " + ex6);
			}
		}
		catch (Exception arg3)
		{
			TBLog.Warn($"SaveToJson failed for {path}: {arg3}");
		}
	}

	public static JsonTravelConfig TryLoadFromCommonLocations()
	{
		List<string> candidatePaths = GetCandidatePaths();
		foreach (string item in candidatePaths)
		{
			try
			{
				if (File.Exists(item))
				{
					JsonTravelConfig jsonTravelConfig = LoadFromJson(item);
					if (jsonTravelConfig != null)
					{
						TBLog.Info("TryLoadFromCommonLocations: loaded from " + item);
						return jsonTravelConfig;
					}
				}
			}
			catch (Exception ex)
			{
				TBLog.Warn("TryLoadFromCommonLocations: error checking " + item + ": " + ex.Message);
			}
		}
		return null;
	}

	private static List<string> GetCandidatePaths()
	{
		List<string> list = new List<string>();
		string text = "TravelButton_Cities.json";
		try
		{
			string text2 = AppDomain.CurrentDomain.BaseDirectory ?? "";
			if (!string.IsNullOrEmpty(text2))
			{
				list.Add(Path.Combine(text2, "BepInEx", "config", text));
				list.Add(Path.Combine(text2, "config", text));
			}
		}
		catch
		{
		}
		try
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (!string.IsNullOrEmpty(directoryName))
			{
				list.Add(Path.Combine(directoryName, text));
			}
		}
		catch
		{
		}
		try
		{
			string currentDirectory = Directory.GetCurrentDirectory();
			if (!string.IsNullOrEmpty(currentDirectory))
			{
				list.Add(Path.Combine(currentDirectory, text));
			}
		}
		catch
		{
		}
		try
		{
			string dataPath = Application.dataPath;
			if (!string.IsNullOrEmpty(dataPath))
			{
				list.Add(Path.Combine(dataPath, text));
			}
		}
		catch
		{
		}
		return list;
	}

	public static string GetPreferredWritePath()
	{
		string path = "TravelButton_Cities.json";
		try
		{
			string text = AppDomain.CurrentDomain.BaseDirectory ?? "";
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = Path.Combine(text, "BepInEx", "config");
				if (Directory.Exists(text2))
				{
					return Path.Combine(text2, path);
				}
				string path2 = Path.Combine(text, "BepInEx");
				if (Directory.Exists(path2))
				{
					try
					{
						Directory.CreateDirectory(text2);
						return Path.Combine(text2, path);
					}
					catch
					{
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (!string.IsNullOrEmpty(directoryName))
			{
				return Path.Combine(directoryName, path);
			}
		}
		catch
		{
		}
		return Path.Combine(Directory.GetCurrentDirectory(), path);
	}
}
public class CityDiscovery : MonoBehaviour
{
	private float timer = 0f;

	private const float PollInterval = 1f;

	private const float DiscoverRadius = 6f;

	private void Awake()
	{
		Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
	}

	private void Start()
	{
		TBLog.Info("CityDiscovery.Start: initializing city discovery system.");
		try
		{
			TravelButtonVisitedManager.LogPlayerCandidateVisitedFields();
		}
		catch
		{
		}
	}

	private void Update()
	{
		try
		{
			if (Input.GetKeyDown((KeyCode)290))
			{
				LogPlayerPosition();
			}
			if (Input.GetKeyDown((KeyCode)291))
			{
				ForceMarkNearestCity();
			}
			timer += Time.unscaledDeltaTime;
			if (timer >= 1f)
			{
				timer = 0f;
				PollForNearbyCities();
				EnforceVisitedGating();
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("CityDiscovery.Update failed: " + ex);
		}
	}

	private void PollForNearbyCities()
	{
		//IL_0028: 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_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Transform val = FindPlayerTransform();
			if ((Object)(object)val == (Object)null)
			{
				TBLog.Warn("CityDiscovery: PollForNearbyCities - player transform not found.");
				return;
			}
			Vector3 position = val.position;
			IList<TravelButton.City> citiesList = GetCitiesList();
			if (citiesList == null)
			{
				TBLog.Warn("CityDiscovery: PollForNearbyCities - could not locate TravelButtonMod.Cities.");
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			string text = ((Scene)(ref activeScene)).name ?? "";
			foreach (TravelButton.City item in citiesList)
			{
				try
				{
					if (string.IsNullOrEmpty(item.name) || TravelButtonVisitedManager.IsCityVisited(item.name))
					{
						continue;
					}
					Vector3? val2 = GetCityPosition(item);
					if (!val2.HasValue && !string.IsNullOrEmpty(text) && text.IndexOf(item.name, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						TBLog.Info("CityDiscovery: Scene '" + text + "' matches city '" + item.name + "' - using player position as candidate.");
						val2 = position;
					}
					if (!val2.HasValue)
					{
						TBLog.Info("CityDiscovery: No candidate position for city '" + item.name + "' (skipping).");
						continue;
					}
					float num = Vector3.Distance(position, val2.Value);
					TBLog.Info($"CityDiscovery: Dist to '{item.name}' = {num:F1} (threshold {6f}).");
					if (num <= 6f)
					{
						TravelButtonVisitedManager.MarkVisited(item.name, val2.Value);
						TBLog.Info($"CityDiscovery: Auto-discovered city '{item.name}' at distance {num:F1}.");
					}
				}
				catch (Exception ex)
				{
					TBLog.Warn("CityDiscovery.PollForNearbyCities failed for a city: " + ex);
				}
			}
		}
		catch (Exception ex2)
		{
			TBLog.Warn("CityDiscovery.PollForNearbyCities failed: " + ex2);
		}
	}

	private void ForceMarkNearestCity()
	{
		//IL_0027: 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_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		Transform val = FindPlayerTransform();
		if ((Object)(object)val == (Object)null)
		{
			TBLog.Warn("CityDiscovery: ForceMarkNearestCity - player transform not found.");
			return;
		}
		Vector3 position = val.position;
		IList<TravelButton.City> citiesList = GetCitiesList();
		if (citiesList == null)
		{
			TBLog.Warn("CityDiscovery: ForceMarkNearestCity - could not locate TravelButtonMod.Cities.");
			return;
		}
		float num = float.MaxValue;
		TravelButton.City city = null;
		Vector3? position2 = null;
		foreach (TravelButton.City item in citiesList)
		{
			try
			{
				Vector3? val2 = GetCityPosition(item);
				if (!val2.HasValue)
				{
					Scene activeScene = SceneManager.GetActiveScene();
					string text = ((Scene)(ref activeScene)).name ?? "";
					if (!string.IsNullOrEmpty(text) && text.IndexOf(item.name, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val2 = position;
					}
				}
				if (val2.HasValue)
				{
					float num2 = Vector3.Distance(position, val2.Value);
					if (num2 < num)
					{
						num = num2;
						city = item;
						position2 = val2;
					}
				}
			}
			catch
			{
			}
		}
		if (city != null)
		{
			TravelButtonVisitedManager.MarkVisited(city.name, position2);
			TBLog.Info($"CityDiscovery: Force-marked nearest city '{city.name}' (dist {num:F1}).");
		}
		else
		{
			TBLog.Warn("CityDiscovery: ForceMarkNearestCity - no city positions available to mark.");
		}
	}

	private void EnforceVisitedGating()
	{
		//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_0123: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Scene activeScene = SceneManager.GetActiveScene();
			GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects();
			GameObject[] array = rootGameObjects;
			foreach (GameObject val in array)
			{
				if ((Object)(object)val == (Object)null || !val.activeInHierarchy)
				{
					continue;
				}
				Transform val2 = val.transform.Find("ScrollArea/Viewport/Content");
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				for (int j = 0; j < val2.childCount; j++)
				{
					Transform child = val2.GetChild(j);
					Button component = ((Component)child).GetComponent<Button>();
					Image component2 = ((Component)child).GetComponent<Image>();
					if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
					{
						continue;
					}
					string name = ((Object)child).name;
					if (name.StartsWith("CityButton_"))
					{
						string cityName = name.Substring("CityButton_".Length);
						if (!TravelButtonVisitedManager.IsCityVisited(cityName) && ((Selectable)component).interactable)
						{
							((Selectable)component).interactable = false;
							((Graphic)component2).color = new Color(0.18f, 0.18f, 0.18f, 1f);
						}
					}
				}
				break;
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("CityDiscovery.EnforceVisitedGating failed: " + ex);
		}
	}

	private IList<TravelButton.City> GetCitiesList()
	{
		IList<TravelButton.City> list = null;
		FieldInfo field = typeof(TravelButton).GetField("Cities", BindingFlags.Static | BindingFlags.Public);
		if (field != null)
		{
			list = field.GetValue(null) as IList<TravelButton.City>;
		}
		if (list == null)
		{
			PropertyInfo property = typeof(TravelButton).GetProperty("Cities", BindingFlags.Static | BindingFlags.Public);
			if (property != null)
			{
				list = property.GetValue(null, null) as IList<TravelButton.City>;
			}
		}
		return list;
	}

	private Transform FindPlayerTransform()
	{
		try
		{
			GameObject val = GameObject.FindWithTag("Player");
			if ((Object)(object)val != (Object)null)
			{
				return val.transform;
			}
		}
		catch
		{
		}
		string[] array = new string[7] { "PlayerCharacter", "PlayerEntity", "Character", "PC_Player", "PlayerController", "LocalPlayer", "Player" };
		string[] array2 = array;
		foreach (string text in array2)
		{
			try
			{
				Type type = ReflectionUtils.SafeGetType(text + ", Assembly-CSharp");
				if (!(type != null))
				{
					continue;
				}
				Object[] array3 = Object.FindObjectsOfType(type);
				if (array3 != null && array3.Length != 0)
				{
					Object obj2 = array3[0];
					Component val2 = (Component)(object)((obj2 is Component) ? obj2 : null);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2.transform;
					}
				}
			}
			catch
			{
			}
		}
		try
		{
			if ((Object)(object)Camera.main != (Object)null)
			{
				return ((Component)Camera.main).transform;
			}
		}
		catch
		{
		}
		try
		{
			Transform[] array4 = Object.FindObjectsOfType<Transform>();
			Transform[] array5 = array4;
			foreach (Transform val3 in array5)
			{
				if (!((Object)(object)val3 == (Object)null) && ((Object)val3).name.IndexOf("player", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return val3;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	public Vector3? GetCityPosition(TravelButton.City city)
	{
		//IL_004f: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
		if (city == null)
		{
			return null;
		}
		try
		{
			if (!string.IsNullOrEmpty(city.targetGameObjectName))
			{
				GameObject val = GameObject.Find(city.targetGameObjectName);
				if ((Object)(object)val != (Object)null)
				{
					return val.transform.position;
				}
			}
			if (city.coords != null && city.coords.Length >= 3)
			{
				return new Vector3(city.coords[0], city.coords[1], city.coords[2]);
			}
			try
			{
				Transform[] array = Object.FindObjectsOfType<Transform>();
				Transform[] array2 = array;
				foreach (Transform val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null) && ((Object)val2).name.IndexOf(city.name ?? "", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return val2.position;
					}
				}
			}
			catch
			{
			}
			return null;
		}
		catch (Exception ex)
		{
			TBLog.Warn("GetCityPosition exception: " + ex);
			return null;
		}
	}

	private void LogPlayerPosition()
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		Transform val = FindPlayerTransform();
		if ((Object)(object)val == (Object)null)
		{
			TBLog.Info("CityDiscovery: player transform not found for LogPlayerPosition.");
		}
		else
		{
			TBLog.Info($"CityDiscovery: Player pos = {val.position}");
		}
	}
}
public static class CityMappingHelpers
{
	private static List<TravelButtonUI.CityEntry> loadedCities;

	public static void InitCities()
	{
		try
		{
			try
			{
				string text = TryGetCanonicalCitiesJsonPath();
				if (!string.IsNullOrEmpty(text))
				{
					try
					{
						string fullPath = Path.GetFullPath(text);
						if (File.Exists(fullPath))
						{
							List<TravelButtonUI.CityEntry> list = ParseCitiesJsonFile(fullPath);
							if (list != null && list.Count > 0)
							{
								loadedCities = list;
								TBLog.Info($"InitCities: loaded {loadedCities.Count} cities from canonical path: {fullPath}");
								DumpLoadedCitiesPreview();
								return;
							}
						}
						else
						{
							TBLog.Info("InitCities: canonical TravelButton_Cities.json not found at: " + fullPath);
						}
					}
					catch (Exception ex)
					{
						TBLog.Warn("InitCities: error while trying canonical path '" + text + "': " + ex);
					}
				}
			}
			catch (Exception ex2)
			{
				TBLog.Warn("InitCities: canonical-path attempt threw: " + ex2);
			}
			List<string> list2 = new List<string>();
			try
			{
				Type type = Type.GetType("BepInEx.Paths, BepInEx");
				if (type != null)
				{
					PropertyInfo property = type.GetProperty("ConfigPath", BindingFlags.Static | BindingFlags.Public);
					if (property != null)
					{
						string text2 = property.GetValue(null) as string;
						if (!string.IsNullOrEmpty(text2))
						{
							string text3 = "TravelButton_Cities.json";
							try
							{
								text3 = ((typeof(TravelButtonPlugin).GetField("CitiesJsonFileName", BindingFlags.Static | BindingFlags.Public) != null) ? "TravelButton_Cities.json" : text3);
							}
							catch
							{
							}
							string item = Path.Combine(text2, text3);
							list2.Add(item);
						}
					}
				}
			}
			catch (Exception ex3)
			{
				TBLog.Warn("InitCities: reflection attempt to read BepInEx.Paths.ConfigPath failed: " + ex3);
			}
			try
			{
				list2.Add(Path.GetFullPath(Path.Combine(Application.dataPath, "..", "BepInEx", "config", "TravelButton_Cities.json")));
			}
			catch
			{
			}
			try
			{
				list2.Add(Path.GetFullPath(Path.Combine(TravelButtonPlugin.GetCitiesJsonPath(), "TravelButton_Cities.json")));
			}
			catch
			{
			}
			try
			{
				string text4 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "";
				if (!string.IsNullOrEmpty(text4))
				{
					list2.Add(Path.Combine(text4, "TravelButton_Cities.json"));
				}
			}
			catch
			{
			}
			foreach (string item2 in list2)
			{
				try
				{
					if (string.IsNullOrEmpty(item2))
					{
						continue;
					}
					string fullPath2 = Path.GetFullPath(item2);
					if (File.Exists(fullPath2))
					{
						List<TravelButtonUI.CityEntry> list3 = ParseCitiesJsonFile(fullPath2);
						if (list3 != null && list3.Count > 0)
						{
							loadedCities = list3;
							TBLog.Info($"InitCities: loaded {loadedCities.Count} cities from: {fullPath2}");
							DumpLoadedCitiesPreview();
							return;
						}
					}
				}
				catch (Exception ex4)
				{
					TBLog.Warn("InitCities: error while trying candidate path '" + item2 + "': " + ex4);
				}
			}
			TBLog.Warn("InitCities: no valid TravelButton_Cities.json found in canonical or candidate locations; loadedCities is null or empty.");
		}
		catch (Exception ex5)
		{
			TBLog.Warn("InitCities: unexpected outer error: " + ex5);
		}
	}

	public static List<TravelButtonUI.CityEntry> ParseCitiesJsonFile(string filePath)
	{
		//IL_051e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0525: Invalid comparison between Unknown and I4
		//IL_0646: Unknown result type (might be due to invalid IL or missing references)
		//IL_064c: Invalid comparison between Unknown and I4
		//IL_065e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0665: Expected O, but got Unknown
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Invalid comparison between Unknown and I4
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Expected O, but got Unknown
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Invalid comparison between Unknown and I4
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Invalid comparison between Unknown and I4
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Expected O, but got Unknown
		//IL_020b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Expected O, but got Unknown
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Invalid comparison between Unknown and I4
		//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e3: Invalid comparison between Unknown and I4
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e6: Expected O, but got Unknown
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Expected O, but got Unknown
		try
		{
			if (string.IsNullOrEmpty(filePath))
			{
				TBLog.Warn("ParseCitiesJsonFile: filePath is null/empty");
				return null;
			}
			string fullPath = Path.GetFullPath(filePath);
			if (!File.Exists(fullPath))
			{
				TBLog.Warn("ParseCitiesJsonFile: file not found: " + fullPath);
				return null;
			}
			string text = File.ReadAllText(fullPath);
			if (string.IsNullOrWhiteSpace(text))
			{
				TBLog.Warn("ParseCitiesJsonFile: file is empty: " + fullPath);
				return null;
			}
			JToken val;
			try
			{
				string text4;
				using (StringReader stringReader = new StringReader(text))
				{
					StringBuilder stringBuilder = new StringBuilder();
					bool flag = false;
					string text2;
					while ((text2 = stringReader.ReadLine()) != null)
					{
						if (!flag)
						{
							if (string.IsNullOrWhiteSpace(text2))
							{
								continue;
							}
							string text3 = text2.TrimStart(Array.Empty<char>());
							if (text3.StartsWith("//"))
							{
								continue;
							}
							flag = true;
						}
						stringBuilder.AppendLine(text2);
					}
					text4 = stringBuilder.ToString();
				}
				if (string.IsNullOrWhiteSpace(text4))
				{
					TBLog.Warn("ParseCitiesJsonFile: file contained only comments/whitespace: " + fullPath);
					return null;
				}
				val = JToken.Parse(text4);
			}
			catch (Exception ex)
			{
				TBLog.Warn("ParseCitiesJsonFile: JSON parse error for file " + fullPath + ": " + ex.Message);
				return null;
			}
			JArray val2 = null;
			if ((int)val.Type == 1)
			{
				JObject val3 = (JObject)val;
				JToken val4 = val3["cities"] ?? val3["Cities"] ?? val3["list"];
				if (val4 != null && (int)val4.Type == 2)
				{
					val2 = (JArray)val4;
				}
				else
				{
					TBLog.Info("ParseCitiesJsonFile: root is an object but does not contain 'cities' array; attempting to interpret root as single city object.");
					val2 = new JArray((object)val3);
				}
			}
			else
			{
				if ((int)val.Type != 2)
				{
					TBLog.Warn($"ParseCitiesJsonFile: unexpected JSON root type {val.Type} in file {fullPath}");
					return null;
				}
				val2 = (JArray)val;
			}
			List<TravelButtonUI.CityEntry> list = new List<TravelButtonUI.CityEntry>(((JContainer)val2).Count);
			for (int i = 0; i < ((JContainer)val2).Count; i++)
			{
				try
				{
					JToken val5 = val2[i];
					if (val5 == null || (int)val5.Type != 1)
					{
						continue;
					}
					JObject val6 = (JObject)val5;
					TravelButtonUI.CityEntry cityEntry = new TravelButtonUI.CityEntry();
					SetStringMember(cityEntry, ((JToken)val6).Value<string>((object)"name") ?? ((JToken)val6).Value<string>((object)"Name") ?? string.Empty, "name", "Name");
					SetStringMember(cityEntry, ((JToken)val6).Value<string>((object)"sceneName") ?? ((JToken)val6).Value<string>((object)"SceneName") ?? ((JToken)val6).Value<string>((object)"scene") ?? string.Empty, "sceneName", "SceneName", "scene");
					SetStringMember(cityEntry, ((JToken)val6).Value<string>((object)"targetGameObjectName") ?? ((JToken)val6).Value<string>((object)"target") ?? string.Empty, "targetGameObjectName", "target", "targetName");
					SetStringMember(cityEntry, ((JToken)val6).Value<string>((object)"desc") ?? ((JToken)val6).Value<string>((object)"description") ?? string.Empty, "desc", "description", "descText", "Description");
					int num = -1;
					JToken val7 = val6["price"] ?? val6["Price"];
					if (val7 != null && (int)val7.Type != 10)
					{
						try
						{
							num = Extensions.Value<int>((IEnumerable<JToken>)val7);
						}
						catch
						{
							try
							{
								num = Convert.ToInt32(Extensions.Value<double>((IEnumerable<JToken>)val7));
							}
							catch
							{
								num = -1;
							}
						}
					}
					try
					{
						Type type = cityEntry.GetType();
						PropertyInfo propertyInfo = type.GetProperty("price", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperty("Price", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType == typeof(int))
						{
							propertyInfo.SetValue(cityEntry, num);
						}
						else
						{
							FieldInfo fieldInfo = type.GetField("price", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("Price", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (fieldInfo != null && fieldInfo.FieldType == typeof(int))
							{
								fieldInfo.SetValue(cityEntry, num);
							}
						}
					}
					catch
					{
					}
					bool flag2 = false;
					JToken val8 = val6["visited"] ?? val6["Visited"];
					if (val8 != null && (int)val8.Type != 10)
					{
						try
						{
							flag2 = Extensions.Value<bool>((IEnumerable<JToken>)val8);
						}
						catch
						{
							flag2 = false;
						}
					}
					try
					{
						Type type2 = cityEntry.GetType();
						PropertyInfo propertyInfo2 = type2.GetProperty("visited", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetProperty("Visited", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (propertyInfo2 != null && propertyInfo2.CanWrite && propertyInfo2.PropertyType == typeof(bool))
						{
							propertyInfo2.SetValue(cityEntry, flag2);
						}
						else
						{
							FieldInfo fieldInfo2 = type2.GetField("visited", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetField("Visited", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (fieldInfo2 != null && fieldInfo2.FieldType == typeof(bool))
							{
								fieldInfo2.SetValue(cityEntry, flag2);
							}
						}
					}
					catch
					{
					}
					JToken val9 = val6["coords"] ?? val6["Coords"];
					if (val9 != null && (int)val9.Type == 2)
					{
						try
						{
							JArray val10 = (JArray)val9;
							if (((JContainer)val10).Count >= 3)
							{
								SetFloatArrayOnTarget(cityEntry, new float[3]
								{
									Extensions.Value<float>((IEnumerable<JToken>)val10[0]),
									Extensions.Value<float>((IEnumerable<JToken>)val10[1]),
									Extensions.Value<float>((IEnumerable<JToken>)val10[2])
								}, "coords", "Coords", "position", "Position");
							}
						}
						catch
						{
						}
					}
					list.Add(cityEntry);
				}
				catch (Exception ex2)
				{
					TBLog.Warn($"ParseCitiesJsonFile: failed to parse city entry index {i} in {fullPath}: {ex2.Message}");
				}
			}
			if (list.Count == 0)
			{
				TBLog.Warn("ParseCitiesJsonFile: parsed JSON but found 0 cities in " + fullPath);
				return null;
			}
			TBLog.Info($"ParseCitiesJsonFile: successfully parsed {list.Count} cities from: {fullPath}");
			return list;
		}
		catch (Exception ex3)
		{
			TBLog.Warn("ParseCitiesJsonFile: unexpected error: " + ex3);
			return null;
		}
	}

	private static void DumpLoadedCitiesPreview()
	{
		try
		{
			for (int i = 0; i < Math.Min(loadedCities.Count, 10); i++)
			{
				TravelButtonUI.CityEntry cityEntry = loadedCities[i];
				TBLog.Info(string.Format("  city[{0}] name='{1}' scene='{2}' coords={3}", i, cityEntry?.name, cityEntry?.sceneName, (cityEntry?.coords == null) ? "null" : string.Join(",", cityEntry.coords)));
			}
		}
		catch
		{
		}
	}

	private static bool TryFindCityByNameOrScene(string candidate, out TravelButtonUI.CityEntry outCity)
	{
		outCity = null;
		if (string.IsNullOrEmpty(candidate))
		{
			return false;
		}
		try
		{
			List<TravelButtonUI.CityEntry> list = loadedCities;
			if (list == null || list.Count == 0)
			{
				return false;
			}
			string text = candidate.Trim().ToLowerInvariant();
			foreach (TravelButtonUI.CityEntry item in list)
			{
				if (item != null)
				{
					if (!string.IsNullOrEmpty(item.name) && item.name.Trim().ToLowerInvariant() == text)
					{
						outCity = item;
						return true;
					}
					if (!string.IsNullOrEmpty(item.sceneName) && item.sceneName.Trim().ToLowerInvariant() == text)
					{
						outCity = item;
						return true;
					}
				}
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("TryFindCityByNameOrScene: lookup failed: " + ex);
		}
		return false;
	}

	private static Vector3 haveCoordsHintOrResolved(Vector3 coordsHint, bool haveCoordsHint, Vector3 resolvedCoords, bool resolvedHaveCoords, out bool haveCoordsFinal)
	{
		//IL_000b: 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_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		if (haveCoordsHint)
		{
			haveCoordsFinal = true;
			return coordsHint;
		}
		if (resolvedHaveCoords)
		{
			haveCoordsFinal = true;
			return resolvedCoords;
		}
		haveCoordsFinal = false;
		return Vector3.zero;
	}

	public static bool TryResolveCityDataFromObject(object cityObj, out string sceneName, out string targetName, out Vector3 coords, out bool haveCoords, out int price)
	{
		return TryResolveCityDataFromObject_Internal(cityObj, out sceneName, out targetName, out coords, out haveCoords, out price);
	}

	private static bool TryResolveCityDataFromObject_Internal(object cityObj, out string outSceneName, out string outTargetName, out Vector3 outCoords, out bool outHaveCoords, out int outPrice)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: 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)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c2a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c2f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0602: Unknown result type (might be due to invalid IL or missing references)
		//IL_0607: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a15: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a1a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0655: Unknown result type (might be due to invalid IL or missing references)
		//IL_065a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a68: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a6d: Unknown result type (might be due to invalid IL or missing references)
		//IL_059a: Unknown result type (might be due to invalid IL or missing references)
		//IL_059f: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04db: Unknown result type (might be due to invalid IL or missing references)
		//IL_09ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_08e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_08ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ae3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ae8: Unknown result type (might be due to invalid IL or missing references)
		outSceneName = null;
		outTargetName = null;
		outCoords = Vector3.zero;
		outHaveCoords = false;
		outPrice = 0;
		if (cityObj == null)
		{
			return false;
		}
		if (cityObj is TravelButtonUI.CityEntry cityEntry)
		{
			outSceneName = cityEntry.sceneName;
			outTargetName = cityEntry.targetGameObjectName;
			if (cityEntry.coords != null && cityEntry.coords.Length >= 3)
			{
				outCoords = new Vector3(cityEntry.coords[0], cityEntry.coords[1], cityEntry.coords[2]);
				outHaveCoords = true;
			}
			outPrice = cityEntry.price;
			return !string.IsNullOrEmpty(outSceneName) | outHaveCoords;
		}
		if (cityObj is string text)
		{
			if (TryFindCityByNameOrScene(text, out var outCity))
			{
				outSceneName = outCity.sceneName;
				outTargetName = outCity.targetGameObjectName;
				if (outCity.coords != null && outCity.coords.Length >= 3)
				{
					outCoords = new Vector3(outCity.coords[0], outCity.coords[1], outCity.coords[2]);
					outHaveCoords = true;
				}
				outPrice = outCity.price;
				return !string.IsNullOrEmpty(outSceneName) | outHaveCoords;
			}
			outSceneName = text;
			return true;
		}
		try
		{
			string text2 = null;
			PropertyInfo propertyInfo = cityObj.GetType().GetProperty("name", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) ?? cityObj.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public);
			if (propertyInfo != null)
			{
				text2 = propertyInfo.GetValue(cityObj) as string;
			}
			if (string.IsNullOrEmpty(text2))
			{
				try
				{
					text2 = cityObj.ToString();
				}
				catch
				{
					text2 = null;
				}
			}
			if (!string.IsNullOrEmpty(text2) && TryFindCityByNameOrScene(text2, out var outCity2))
			{
				outSceneName = outCity2.sceneName;
				outTargetName = outCity2.targetGameObjectName;
				if (outCity2.coords != null && outCity2.coords.Length >= 3)
				{
					outCoords = new Vector3(outCity2.coords[0], outCity2.coords[1], outCity2.coords[2]);
					outHaveCoords = true;
				}
				outPrice = outCity2.price;
				return !string.IsNullOrEmpty(outSceneName) | outHaveCoords;
			}
		}
		catch
		{
		}
		try
		{
			Type type = cityObj.GetType();
			string[] array = new string[4] { "scenename", "scene", "scene_name", "sceneName" };
			string[] array2 = new string[5] { "targetgameobjectname", "target", "targetname", "target_gameobject_name", "targetGameObjectName" };
			string[] array3 = new string[3] { "name", "displayname", "title" };
			string[] array4 = new string[3] { "price", "cost", "fee" };
			string[] array5 = new string[7] { "coords", "position", "pos", "location", "transform", "vector", "coordinate" };
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo2 in properties)
			{
				try
				{
					object obj3 = null;
					try
					{
						obj3 = propertyInfo2.GetValue(cityObj);
					}
					catch
					{
						goto end_IL_0377;
					}
					if (obj3 == null)
					{
						continue;
					}
					string propName = propertyInfo2.Name.ToLowerInvariant();
					if (obj3 is string text3)
					{
						TravelButtonUI.CityEntry outCity4;
						if (Array.Exists(array, (string k) => propName.Contains(k)))
						{
							if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = text3;
							}
						}
						else if (Array.Exists(array2, (string k) => propName.Contains(k)))
						{
							if (string.IsNullOrEmpty(outTargetName))
							{
								outTargetName = text3;
							}
						}
						else if (Array.Exists(array3, (string k) => propName.Contains(k)))
						{
							if (TryFindCityByNameOrScene(text3, out var outCity3))
							{
								if (string.IsNullOrEmpty(outSceneName))
								{
									outSceneName = outCity3.sceneName;
								}
								if (string.IsNullOrEmpty(outTargetName))
								{
									outTargetName = outCity3.targetGameObjectName;
								}
								if (!outHaveCoords && outCity3.coords != null && outCity3.coords.Length >= 3)
								{
									outCoords = new Vector3(outCity3.coords[0], outCity3.coords[1], outCity3.coords[2]);
									outHaveCoords = true;
								}
								if (outPrice == 0)
								{
									outPrice = outCity3.price;
								}
							}
							else if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = text3;
							}
						}
						else if (TryFindCityByNameOrScene(text3, out outCity4))
						{
							if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = outCity4.sceneName;
							}
							if (string.IsNullOrEmpty(outTargetName))
							{
								outTargetName = outCity4.targetGameObjectName;
							}
							if (!outHaveCoords && outCity4.coords != null && outCity4.coords.Length >= 3)
							{
								outCoords = new Vector3(outCity4.coords[0], outCity4.coords[1], outCity4.coords[2]);
								outHaveCoords = true;
							}
							if (outPrice == 0)
							{
								outPrice = outCity4.price;
							}
						}
						continue;
					}
					if (obj3 is float[] array6 && array6.Length >= 3)
					{
						if (!outHaveCoords)
						{
							outCoords = new Vector3(array6[0], array6[1], array6[2]);
							outHaveCoords = true;
						}
						continue;
					}
					if (obj3 is double[] array7 && array7.Length >= 3)
					{
						if (!outHaveCoords)
						{
							outCoords = new Vector3((float)array7[0], (float)array7[1], (float)array7[2]);
							outHaveCoords = true;
						}
						continue;
					}
					if (obj3 is IList list && list.Count >= 3)
					{
						try
						{
							float num = Convert.ToSingle(list[0]);
							float num2 = Convert.ToSingle(list[1]);
							float num3 = Convert.ToSingle(list[2]);
							if (!outHaveCoords)
							{
								outCoords = new Vector3(num, num2, num3);
								outHaveCoords = true;
							}
						}
						catch
						{
						}
						continue;
					}
					GameObject val = (GameObject)((obj3 is GameObject) ? obj3 : null);
					if ((Object)(object)val != (Object)null && string.IsNullOrEmpty(outTargetName))
					{
						outTargetName = ((Object)val).name;
						continue;
					}
					Component val2 = (Component)((obj3 is Component) ? obj3 : null);
					if ((Object)(object)val2 != (Object)null && string.IsNullOrEmpty(outTargetName))
					{
						GameObject gameObject = val2.gameObject;
						outTargetName = ((gameObject != null) ? ((Object)gameObject).name : null);
					}
					end_IL_0377:;
				}
				catch
				{
				}
			}
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				try
				{
					object obj7 = null;
					try
					{
						obj7 = fieldInfo.GetValue(cityObj);
					}
					catch
					{
						goto end_IL_078a;
					}
					if (obj7 == null)
					{
						continue;
					}
					string fieldName = fieldInfo.Name.ToLowerInvariant();
					if (obj7 is string text4)
					{
						TravelButtonUI.CityEntry outCity6;
						if (Array.Exists(array, (string k) => fieldName.Contains(k)))
						{
							if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = text4;
							}
						}
						else if (Array.Exists(array2, (string k) => fieldName.Contains(k)))
						{
							if (string.IsNullOrEmpty(outTargetName))
							{
								outTargetName = text4;
							}
						}
						else if (Array.Exists(array3, (string k) => fieldName.Contains(k)))
						{
							if (TryFindCityByNameOrScene(text4, out var outCity5))
							{
								if (string.IsNullOrEmpty(outSceneName))
								{
									outSceneName = outCity5.sceneName;
								}
								if (string.IsNullOrEmpty(outTargetName))
								{
									outTargetName = outCity5.targetGameObjectName;
								}
								if (!outHaveCoords && outCity5.coords != null && outCity5.coords.Length >= 3)
								{
									outCoords = new Vector3(outCity5.coords[0], outCity5.coords[1], outCity5.coords[2]);
									outHaveCoords = true;
								}
								if (outPrice == 0)
								{
									outPrice = outCity5.price;
								}
							}
							else if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = text4;
							}
						}
						else if (TryFindCityByNameOrScene(text4, out outCity6))
						{
							if (string.IsNullOrEmpty(outSceneName))
							{
								outSceneName = outCity6.sceneName;
							}
							if (string.IsNullOrEmpty(outTargetName))
							{
								outTargetName = outCity6.targetGameObjectName;
							}
							if (!outHaveCoords && outCity6.coords != null && outCity6.coords.Length >= 3)
							{
								outCoords = new Vector3(outCity6.coords[0], outCity6.coords[1], outCity6.coords[2]);
								outHaveCoords = true;
							}
							if (outPrice == 0)
							{
								outPrice = outCity6.price;
							}
						}
						continue;
					}
					if (obj7 is float[] array8 && array8.Length >= 3)
					{
						if (!outHaveCoords)
						{
							outCoords = new Vector3(array8[0], array8[1], array8[2]);
							outHaveCoords = true;
						}
						continue;
					}
					if (obj7 is double[] array9 && array9.Length >= 3)
					{
						if (!outHaveCoords)
						{
							outCoords = new Vector3((float)array9[0], (float)array9[1], (float)array9[2]);
							outHaveCoords = true;
						}
						continue;
					}
					if (obj7 is IList list2 && list2.Count >= 3)
					{
						try
						{
							float num4 = Convert.ToSingle(list2[0]);
							float num5 = Convert.ToSingle(list2[1]);
							float num6 = Convert.ToSingle(list2[2]);
							if (!outHaveCoords)
							{
								outCoords = new Vector3(num4, num5, num6);
								outHaveCoords = true;
							}
						}
						catch
						{
						}
						continue;
					}
					GameObject val3 = (GameObject)((obj7 is GameObject) ? obj7 : null);
					if ((Object)(object)val3 != (Object)null && string.IsNullOrEmpty(outTargetName))
					{
						outTargetName = ((Object)val3).name;
						continue;
					}
					Component val4 = (Component)((obj7 is Component) ? obj7 : null);
					if ((Object)(object)val4 != (Object)null && string.IsNullOrEmpty(outTargetName))
					{
						GameObject gameObject2 = val4.gameObject;
						outTargetName = ((gameObject2 != null) ? ((Object)gameObject2).name : null);
					}
					end_IL_078a:;
				}
				catch
				{
				}
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("TryResolveCityDataFromObject: reflection fallback failed: " + ex);
		}
		try
		{
			if (!string.IsNullOrEmpty(outSceneName) && TryFindCityByNameOrScene(outSceneName, out var outCity7))
			{
				if (string.IsNullOrEmpty(outTargetName))
				{
					outTargetName = outCity7.targetGameObjectName;
				}
				if (!outHaveCoords && outCity7.coords != null && outCity7.coords.Length >= 3)
				{
					outCoords = new Vector3(outCity7.coords[0], outCity7.coords[1], outCity7.coords[2]);
					outHaveCoords = true;
				}
				if (outPrice == 0)
				{
					outPrice = outCity7.price;
				}
			}
		}
		catch
		{
		}
		bool flag = !string.IsNullOrEmpty(outSceneName) | outHaveCoords;
		if (!flag)
		{
			try
			{
				Type type2 = cityObj.GetType();
				List<string> list3 = new List<string>();
				PropertyInfo[] properties2 = type2.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo propertyInfo3 in properties2)
				{
					list3.Add("P:" + propertyInfo3.Name);
				}
				FieldInfo[] fields2 = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo2 in fields2)
				{
					list3.Add("F:" + fieldInfo2.Name);
				}
				TBLog.Warn("TryResolveCityDataFromObject: failed to resolve data for object type=" + type2.FullName + ". Members: " + string.Join(", ", list3));
			}
			catch
			{
			}
		}
		return flag;
	}

	public static object ConvertParsedCitiesToRuntime(List<TravelButtonUI.CityEntry> parsed)
	{
		if (parsed == null)
		{
			return null;
		}
		try
		{
			FieldInfo field = typeof(TravelButton).GetField("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			PropertyInfo property = typeof(TravelButton).GetProperty("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			Type type = null;
			if (field != null)
			{
				type = field.FieldType;
			}
			else if (property != null)
			{
				type = property.PropertyType;
			}
			if (type != null)
			{
				Type elementTypeFromCollectionType = GetElementTypeFromCollectionType(type);
				if (elementTypeFromCollectionType != null)
				{
					Type type2 = typeof(List<>).MakeGenericType(elementTypeFromCollectionType);
					IList list = (IList)Activator.CreateInstance(type2);
					foreach (TravelButtonUI.CityEntry item in parsed)
					{
						try
						{
							object obj = null;
							try
							{
								obj = Activator.CreateInstance(elementTypeFromCollectionType);
								MapParsedCityToTarget(item, obj);
							}
							catch
							{
								obj = null;
							}
							if (obj == null)
							{
								try
								{
									string text = JsonConvert.SerializeObject((object)item);
									obj = JsonConvert.DeserializeObject(text, elementTypeFromCollectionType);
								}
								catch (Exception ex)
								{
									TBLog.Warn($"ConvertParsedCitiesToRuntime: json roundtrip conversion failed for elementType={elementTypeFromCollectionType}: {ex.Message}");
									obj = null;
								}
							}
							if (obj == null)
							{
								try
								{
									object uninitializedObject = FormatterServices.GetUninitializedObject(elementTypeFromCollectionType);
									if (uninitializedObject != null)
									{
										MapParsedCityToTarget(item, uninitializedObject);
										obj = uninitializedObject;
									}
								}
								catch (Exception ex2)
								{
									TBLog.Warn($"ConvertParsedCitiesToRuntime: GetUninitializedObject failed for {elementTypeFromCollectionType}: {ex2.Message}");
								}
							}
							if (obj != null)
							{
								list.Add(obj);
								continue;
							}
							if (elementTypeFromCollectionType.FullName == typeof(TravelButtonUI.CityEntry).FullName)
							{
								list.Add(item);
								continue;
							}
							TBLog.Warn($"ConvertParsedCitiesToRuntime: unable to convert parsed item to {elementTypeFromCollectionType}; aborting conversion.");
							return null;
						}
						catch (Exception ex3)
						{
							TBLog.Warn("ConvertParsedCitiesToRuntime: failed converting a parsed city entry: " + ex3);
						}
					}
					return list;
				}
			}
		}
		catch (Exception ex4)
		{
			TBLog.Warn("CityMappingHelpers.ConvertParsedCitiesToRuntime: reflection-based conversion failed: " + ex4);
		}
		return parsed;
	}

	public static object ConvertConfigManagerDefaultsToRuntime(object configManagerDefaults)
	{
		if (configManagerDefaults == null)
		{
			return null;
		}
		try
		{
			Type type = configManagerDefaults.GetType();
			IEnumerable<object> enumerable = null;
			PropertyInfo property = type.GetProperty("cities", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				object value = property.GetValue(configManagerDefaults);
				if (value is IEnumerable enumerable2)
				{
					List<object> list = new List<object>();
					foreach (object item in enumerable2)
					{
						list.Add(item);
					}
					enumerable = list;
				}
			}
			else
			{
				FieldInfo field = type.GetField("cities", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value2 = field.GetValue(configManagerDefaults);
					if (value2 is IEnumerable enumerable3)
					{
						List<object> list2 = new List<object>();
						foreach (object item2 in enumerable3)
						{
							list2.Add(item2);
						}
						enumerable = list2;
					}
				}
			}
			if (enumerable == null)
			{
				TBLog.Warn("CityMappingHelpers.ConvertConfigManagerDefaultsToRuntime: could not locate a 'cities' collection on defaults - falling back to empty list.");
				return new List<TravelButtonUI.CityEntry>();
			}
			FieldInfo field2 = typeof(TravelButton).GetField("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			PropertyInfo property2 = typeof(TravelButton).GetProperty("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			Type type2 = ((field2 != null) ? field2.FieldType : ((property2 != null) ? property2.PropertyType : null));
			if (type2 != null)
			{
				Type elementTypeFromCollectionType = GetElementTypeFromCollectionType(type2);
				if (elementTypeFromCollectionType != null)
				{
					Type type3 = typeof(List<>).MakeGenericType(elementTypeFromCollectionType);
					IList list3 = (IList)Activator.CreateInstance(type3);
					foreach (object item3 in enumerable)
					{
						object obj = Activator.CreateInstance(elementTypeFromCollectionType);
						MapDefaultCityToTarget(item3, obj);
						list3.Add(obj);
					}
					return list3;
				}
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("CityMappingHelpers.ConvertConfigManagerDefaultsToRuntime: conversion failed: " + ex);
		}
		try
		{
			List<TravelButtonUI.CityEntry> list4 = new List<TravelButtonUI.CityEntry>();
			Type type4 = configManagerDefaults.GetType();
			PropertyInfo property3 = type4.GetProperty("cities", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property3 != null && property3.GetValue(configManagerDefaults) is IEnumerable enumerable4)
			{
				foreach (object item4 in enumerable4)
				{
					TravelButtonUI.CityEntry cityEntry = new TravelButtonUI.CityEntry();
					SetStringOnTarget(cityEntry, GetStringFromSource(item4, "name"), "name");
					SetStringOnTarget(cityEntry, GetStringFromSource(item4, "sceneName") ?? GetStringFromSource(item4, "scene"), "sceneName");
					SetStringOnTarget(cityEntry, GetStringFromSource(item4, "targetGameObjectName") ?? GetStringFromSource(item4, "target"), "targetGameObjectName");
					SetStringOnTarget(cityEntry, GetStringFromSource(item4, "desc") ?? GetStringFromSource(item4, "description"), "desc");
					cityEntry.price = GetIntFromSource(item4, "price").GetValueOrDefault(-1);
					cityEntry.visited = GetBoolFromSource(item4, "visited").GetValueOrDefault();
					float[] floatArrayFromSource = GetFloatArrayFromSource(item4, "coords");
					cityEntry.coords = floatArrayFromSource;
					list4.Add(cityEntry);
				}
				return list4;
			}
		}
		catch
		{
		}
		return new List<TravelButtonUI.CityEntry>();
	}

	public static void EnsureCitiesInitializedFromJsonOrDefaults()
	{
		try
		{
			string citiesJsonPath = TravelButtonPlugin.GetCitiesJsonPath();
			if (File.Exists(citiesJsonPath))
			{
				TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: loading cities from canonical JSON: " + citiesJsonPath);
				List<TravelButtonUI.CityEntry> list = ParseCitiesJsonFile(citiesJsonPath);
				if (list != null && list.Count > 0)
				{
					object converted = ConvertParsedCitiesToRuntime(list);
					if (AssignConvertedCitiesToTravelButton(converted))
					{
						TBLog.Info($"EnsureCitiesInitializedFromJsonOrDefaults: loaded {GetRuntimeCitiesCount()} cities from JSON.");
						return;
					}
					TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: converted collection was not assignable to TravelButton.Cities (attempting fallbacks).");
					TBLog.Info($"EnsureCitiesInitializedFromJsonOrDefaults: loaded {TravelButton.Cities.Count} cities from JSON.");
					return;
				}
				TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: JSON file present but parsing returned no entries. Falling back to defaults.");
			}
			else
			{
				TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: canonical JSON not found; falling back to ConfigManager defaults.");
			}
			ConfigManager.LegacyTravelConfig configManagerDefaults = ConfigManager.Default();
			object obj = ConvertConfigManagerDefaultsToRuntime(configManagerDefaults);
			if (AssignConvertedCitiesToTravelButton(obj))
			{
				TBLog.Info($"EnsureCitiesInitializedFromJsonOrDefaults: populated TravelButton.Cities from ConfigManager defaults (count={GetRuntimeCitiesCount()})");
			}
			else
			{
				TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: failed to assign ConfigManager defaults to TravelButton.Cities via reflection.");
				TryAssignFallbackListOfCityEntry(obj);
			}
			TBLog.Info($"EnsureCitiesInitializedFromJsonOrDefaults: populated TravelButton.Cities from ConfigManager defaults (count={TravelButton.Cities?.Count ?? 0})");
			try
			{
				try
				{
					JsonTravelConfig jsonTravelConfig = JsonTravelConfig.Default();
					int valueOrDefault = (jsonTravelConfig?.cities?.Count).GetValueOrDefault();
					TBLog.Info($"EnsureCitiesInitializedFromJsonOrDefaults: JsonTravelConfig.Default() produced {valueOrDefault} entries.");
					try
					{
						string path = Path.Combine(Application.dataPath ?? ".", "..", "TravelButton_Cities_debug.json");
						jsonTravelConfig.SaveToJson(Path.GetFullPath(path));
						TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: wrote debug JSON to " + Path.GetFullPath(path));
					}
					catch (Exception ex)
					{
						TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: failed to write debug JSON: " + ex);
					}
					if (valueOrDefault > 0)
					{
						if (!File.Exists(citiesJsonPath))
						{
							TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: canonical JSON missing -> persisting initial JSON from defaults.");
							try
							{
								jsonTravelConfig.SaveToJson(citiesJsonPath);
								TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: persisted canonical JSON from JsonTravelConfig.Default().");
								return;
							}
							catch (Exception ex2)
							{
								TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: failed to persist canonical JSON: " + ex2);
								return;
							}
						}
						TBLog.Info("EnsureCitiesInitializedFromJsonOrDefaults: canonical JSON already exists -> not overwriting.");
					}
					else
					{
						TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: JsonTravelConfig.Default() produced 0 entries; skipping canonical JSON write.");
					}
				}
				catch (Exception ex3)
				{
					TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: error in guarded persist logic: " + ex3);
				}
			}
			catch (Exception ex4)
			{
				TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: failed to conditionally persist initial JSON: " + ex4);
			}
		}
		catch (Exception ex5)
		{
			TBLog.Warn("EnsureCitiesInitializedFromJsonOrDefaults: unexpected error: " + ex5);
		}
	}

	private static bool AssignConvertedCitiesToTravelButton(object converted)
	{
		if (converted == null)
		{
			return false;
		}
		Type typeFromHandle = typeof(TravelButton);
		FieldInfo field = typeFromHandle.GetField("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			if (field.FieldType.IsAssignableFrom(converted.GetType()))
			{
				field.SetValue(null, converted);
				return true;
			}
			if (TryBuildAndAssignCollectionFromEnumerable(converted as IEnumerable, field.FieldType, out var built))
			{
				field.SetValue(null, built);
				return true;
			}
			TBLog.Warn($"AssignConvertedCitiesToTravelButton: field 'Cities' exists but type '{converted.GetType()}' is not assignable to '{field.FieldType}'.");
			return false;
		}
		PropertyInfo property = typeFromHandle.GetProperty("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.CanWrite)
		{
			if (property.PropertyType.IsAssignableFrom(converted.GetType()))
			{
				property.SetValue(null, converted, null);
				return true;
			}
			if (TryBuildAndAssignCollectionFromEnumerable(converted as IEnumerable, property.PropertyType, out var built2))
			{
				property.SetValue(null, built2, null);
				return true;
			}
			TBLog.Warn($"AssignConvertedCitiesToTravelButton: property 'Cities' exists but type '{converted.GetType()}' is not assignable to '{property.PropertyType}'.");
			return false;
		}
		TBLog.Warn("AssignConvertedCitiesToTravelButton: no static field/property named 'Cities' found on TravelButton.");
		return false;
	}

	private static bool TryBuildAndAssignCollectionFromEnumerable(IEnumerable sourceEnum, Type targetCollectionType, out object built)
	{
		built = null;
		if (sourceEnum == null || targetCollectionType == null)
		{
			return false;
		}
		Type elementTypeFromCollectionType = GetElementTypeFromCollectionType(targetCollectionType);
		if (elementTypeFromCollectionType == null)
		{
			return false;
		}
		try
		{
			Type type = typeof(List<>).MakeGenericType(elementTypeFromCollectionType);
			IList list = (IList)Activator.CreateInstance(type);
			foreach (object item in sourceEnum)
			{
				if (item == null)
				{
					list.Add(null);
					continue;
				}
				if (elementTypeFromCollectionType.IsAssignableFrom(item.GetType()))
				{
					list.Add(item);
					continue;
				}
				if (item is TravelButtonUI.CityEntry src)
				{
					object obj = Activator.CreateInstance(elementTypeFromCollectionType);
					MapParsedCityToTarget(src, obj);
					list.Add(obj);
					continue;
				}
				try
				{
					string text = JsonConvert.SerializeObject(item);
					object value = JsonConvert.DeserializeObject(text, elementTypeFromCollectionType);
					list.Add(value);
				}
				catch
				{
					TBLog.Warn($"TryBuildAndAssignCollectionFromEnumerable: unable to convert item of type {item.GetType()} to {elementTypeFromCollectionType}");
					return false;
				}
			}
			if (targetCollectionType.IsArray)
			{
				MethodInfo method = type.GetMethod("ToArray");
				object obj3 = method.Invoke(list, null);
				built = obj3;
			}
			else if (targetCollectionType.IsAssignableFrom(list.GetType()))
			{
				built = list;
			}
			else
			{
				try
				{
					string text2 = JsonConvert.SerializeObject((object)list);
					built = JsonConvert.DeserializeObject(text2, targetCollectionType);
				}
				catch
				{
					TBLog.Warn($"TryBuildAndAssignCollectionFromEnumerable: cannot create target collection of type {targetCollectionType}");
					return false;
				}
			}
			return built != null;
		}
		catch (Exception ex)
		{
			TBLog.Warn("TryBuildAndAssignCollectionFromEnumerable: exception: " + ex);
			return false;
		}
	}

	private static void TryAssignFallbackListOfCityEntry(object convertedDefaults)
	{
		try
		{
			if (!(convertedDefaults is List<TravelButtonUI.CityEntry> value))
			{
				return;
			}
			Type typeFromHandle = typeof(TravelButton);
			FieldInfo field = typeFromHandle.GetField("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && field.FieldType.IsAssignableFrom(typeof(List<TravelButtonUI.CityEntry>)))
			{
				field.SetValue(null, value);
				TBLog.Info("Assigned List<CityEntry> fallback to TravelButton.Cities.");
				return;
			}
			PropertyInfo property = typeFromHandle.GetProperty("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanWrite && property.PropertyType.IsAssignableFrom(typeof(List<TravelButtonUI.CityEntry>)))
			{
				property.SetValue(null, value, null);
				TBLog.Info("Assigned List<CityEntry> fallback to TravelButton.Cities (prop).");
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("TryAssignFallbackListOfCityEntry: " + ex);
		}
	}

	private static int GetRuntimeCitiesCount()
	{
		try
		{
			Type typeFromHandle = typeof(TravelButton);
			FieldInfo field = typeFromHandle.GetField("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			object obj = null;
			if (field != null)
			{
				obj = field.GetValue(null);
			}
			else
			{
				PropertyInfo property = typeFromHandle.GetProperty("Cities", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					obj = property.GetValue(null, null);
				}
			}
			if (obj is ICollection collection)
			{
				return collection.Count;
			}
			if (obj is IEnumerable enumerable)
			{
				int num = 0;
				foreach (object item in enumerable)
				{
					num++;
				}
				return num;
			}
		}
		catch
		{
		}
		return -1;
	}

	private static string TryGetCanonicalCitiesJsonPath()
	{
		try
		{
			Type typeFromHandle = typeof(TravelButton);
			if (typeFromHandle != null)
			{
				MethodInfo method = typeFromHandle.GetMethod("GetCitiesJsonPath", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (method != null)
				{
					string text = method.Invoke(null, null) as string;
					if (!string.IsNullOrEmpty(text))
					{
						return text;
					}
				}
			}
		}
		catch (Exception ex)
		{
			TBLog.Warn("TryGetCanonicalCitiesJsonPath: TravelButton.GetCitiesJsonPath() reflection failed: " + ex.Message);
		}
		try
		{
			Type type = Type.GetType("BepInEx.Paths, BepInEx");
			if (type != null)
			{
				PropertyInfo property = type.GetProperty("ConfigPath", BindingFlags.Static | BindingFlags.Public);
				if (property != null)
				{
					string text2 = property.GetValue(null) as string;
					if (!string.IsNullOrEmpty(text2))
					{
						string text3 = null;
						try
						{
							text3 = "TravelButton_Cities.json";
						}
						catch
						{
						}
						if (string.IsNullOrEmpty(text3))
						{
							text3 = "TravelButton_Cities.json";
						}
						string text4 = Path.Combine(text2, text3);
						if (!string.IsNullOrEmpty(text4))
						{
							return text4;
						}
					}
				}
			}
		}
		catch (Exception ex2)
		{
			TBLog.Warn("TryGetCanonicalCitiesJsonPath: BepInEx.Paths.ConfigPath reflection failed: " + ex2.Message);
		}
		try
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (!string.IsNullOrEmpty(directoryName))
			{
				string path = "TravelButton_Cities.json";
				try
				{
					FieldInfo field = typeof(TravelButtonPlugin).GetField("CitiesJsonFileName", BindingFlags.Static | BindingFlags.Public);
					if (field != null)
					{
						path = "TravelButton_Cities.json";
					}
				}
				catch
				{
				}
				return Path.Combine(directoryName, path);
			}
		}
		catch
		{
		}
		try
		{
			string currentDirectory = Directory.GetCurrentDirectory();
			if (!string.IsNullOrEmpty(currentDirectory))
			{
				string path2 = "TravelButton_Cities.json";
				try
				{
					FieldInfo field2 = typeof(TravelButtonPlugin).GetField("CitiesJsonFileName", BindingFlags.Static | BindingFlags.Public);
					if (field2 != null)
					{
						path2 = "TravelButton_Cities.json";
					}
				}
				catch
				{
				}
				return Path.Combine(currentDirectory, path2);
			}
		}
		catch
		{
		}
		return null;
	}

	private static Type GetElementTypeFromCollectionType(Type collectionType)
	{
		if (collectionType.IsArray)
		{
			return collectionType.GetElementType();
		}
		if (collectionType.IsGenericType)
		{
			Type[] genericArguments = collectionType.GetGenericArguments();
			if (genericArguments != null && genericArguments.Length == 1)
			{
				return genericArguments[0];
			}
		}
		Type type = collectionType.GetInterfaces().FirstOrDefault((Type i) => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
		if (type != null)
		{
			return type.GetGenericArguments()[0];
		}
		return null;
	}

	public static void MapParsedCityToTarget(object src, object dst)
	{
		if (src == null || dst == null)
		{
			return;
		}
		try
		{
			SetStringOnTarget(dst, GetStringFromSource(src, "name") ?? GetStringFromSource(src, "Name"), "name");
			SetIntOnTarget(dst, (GetIntFromSource(src, "price") ?? GetIntFromSource(src, "Price")).GetValueOrDefault(-1), "price");
			float[] arr = GetFloatArrayFromSource(src, "coords") ?? GetFloatArrayFromSource(src, "Coords");
			SetFloatArrayOnTarget(dst, arr, "coords");
			string value = GetStringFromSource(src, "targetGameObjectName") ?? GetStringFromSource(src, "target") ?? GetStringFromSource(src, "targetName");
			SetStringOnTarget(dst, value, "targetGameObjectName", "target", "targetName");
			string value2 = GetStringFromSource(src, "sceneName") ?? GetStringFromSource(src, "scene");
			SetStringOnTarget(dst, value2, "sceneName", "scene");
			string value3 = GetStringFromSource(src, "desc") ?? GetStringFromSource(src, "description") ?? GetStringFromSource(src, "descText");
			SetStringOnTarget(dst, value3, "desc", "description", "descText");
			SetBoolOnTarget(dst, (GetBoolFromSource(src, "visited") ?? GetBoolFromSource(src, "Visited")).GetValueOrDefault(), "visited", "Visited");
		}
		catch (Exception ex)
		{
			TBLog.Warn("CityMappingHelpers.MapParsedCityToTarget: failed mapping city '" + GetStringFromSource(src, "name") + "': " + ex);
		}
	}

	public static void MapDefaultCityToTarget(object src, object dst)
	{
		if (src == null || dst == null)
		{
			return;
		}
		try
		{
			SetStringOnTarget(dst, GetStringFromSource(src, "name"), "name");
			SetIntOnTarget(dst, GetIntFromSource(src, "price").GetValueOrDefault(-1), "price");
			SetFloatArrayOnTarget(dst, GetFloatArrayFromSource(src, "coords"), "coords");
			SetStringOnTarget(dst, GetStringFromSource(src, "targetGameObjectName") ?? GetStringFromSource(src, "target"), "targetGameObjectName", "target", "targetName");
			SetStringOnTarget(dst, GetStringFromSource(src, "sceneName") ?? GetStringFromSource(src, "scene"), "sceneName", "scene");
			SetStringOnTarget(dst, GetStringFromSource(src, "desc") ?? GetStringFromSource(src, "description"), "desc", "description");
			SetBoolOnTarget(dst, GetBoolFromSource(src, "visited").GetValueOrDefault(), "visited", "Visited");
		}
		catch (Exception ex)
		{
			TBLog.Warn("CityMappingHelpers.MapDefaultCityToTarget: failed mapping default city: " + ex);
		}
	}

	private static void SetStringOnTarget(object target, string value, params string[] candidateNames)
	{
		if (target == null)
		{
			return;
		}
		Type type = target.GetType();
		foreach (string name in candidateNames)
		{
			try
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && property.PropertyType == typeof(string))
				{
					property.SetValue(target, value);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(string))
				{
					field.SetValue(target, value);
					break;
				}
			}
			catch
			{
			}
		}
	}

	private static void SetIntOnTarget(object target, int value, params string[] candidateNames)
	{
		if (target == null)
		{
			return;
		}
		Type type = target.GetType();
		foreach (string name in candidateNames)
		{
			try
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?)))
				{
					property.SetValue(target, value);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && (field.FieldType == typeof(int) || field.FieldType == typeof(int?)))
				{
					field.SetValue(target, value);
					break;
				}
			}
			catch
			{
			}
		}
	}

	private static void SetBoolOnTarget(object target, bool value, params string[] candidateNames)
	{
		if (target == null)
		{
			return;
		}
		Type type = target.GetType();
		foreach (string name in candidateNames)
		{
			try
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?)))
				{
					property.SetValue(target, value);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && (field.FieldType == typeof(bool) || field.FieldType == typeof(bool?)))
				{
					field.SetValue(target, value);
					break;
				}
			}
			catch
			{
			}
		}
	}

	public static string GetStringFromSource(object src, string propName)
	{
		if (src == null)
		{
			return null;
		}
		Type type = src.GetType();
		PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.CanRead && property.PropertyType == typeof(string))
		{
			return property.GetValue(src) as string;
		}
		FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null && field.FieldType == typeof(string))
		{
			return field.GetValue(src) as string;
		}
		return null;
	}

	private static int? GetIntFromSource(object src, string propName)
	{
		if (src == null)
		{
			return null;
		}
		Type type = src.GetType();
		PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.CanRead)
		{
			object value = property.GetValue(src);
			if (value is int value2)
			{
				return value2;
			}
			try
			{
				return Convert.ToInt32(value);
			}
			catch
			{
			}
		}
		FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			object value3 = field.GetValue(src);
			if (value3 is int value4)
			{
				return value4;
			}
			try
			{
				return Convert.ToInt32(value3);
			}
			catch
			{
			}
		}
		return null;
	}

	private static bool? GetBoolFromSource(object src, string propName)
	{
		if (src == null)
		{
			return null;
		}
		Type type = src.GetType();
		PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.CanRead)
		{
			object value = property.GetValue(src);
			if (value is bool value2)
			{
				return value2;
			}
			if (value != null && bool.TryParse(value.ToString(), out var result))
			{
				return result;
			}
		}
		FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			object value3 = field.GetValue(src);
			if (value3 is bool value4)
			{
				return value4;
			}
			if (value3 != null && bool.TryParse(value3.ToString(), out var result2))
			{
				return result2;
			}
		}
		return null;
	}

	private static float[] GetFloatArrayFromSource(object src, string propName)
	{
		if (src == null)
		{
			return null;
		}
		Type type = src.GetType();
		PropertyInfo property = type.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		object obj = null;
		if (property != null && property.CanRead)
		{
			obj = property.GetValue(src);
		}
		else
		{
			FieldInfo field = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				obj = field.GetValue(src);
			}
		}
		if (obj == null)
		{
			return null;
		}
		if (obj is float[] result)
		{
			return result;
		}
		if (obj is IEnumerable enumerable)
		{
			List<float> list = new List<float>();
			foreach (object item in enumerable)
			{
				try
				{
					list.Add(Convert.ToSingle(item));
				}
				catch
				{
				}
			}
			if (list.Count > 0)
			{
				return list.ToArray();
			}
		}
		return null;
	}

	private static void SetStringMember(object target, string value, params string[] candidateNames)
	{
		if (target == null || candidateNames == null || candidateNames.Length == 0)
		{
			return;
		}
		Type type = target.GetType();
		foreach (string name in candidateNames)
		{
			try
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && property.PropertyType == typeof(string))
				{
					property.SetValue(target, value);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(string))
				{
					field.SetValue(target, value);
					break;
				}
			}
			catch
			{
			}
		}
	}

	private static void SetFloatArrayOnTarget(object target, float[] arr, params string[] candidateNames)
	{
		if (target == null || candidateNames == null || candidateNames.Length == 0)
		{
			return;
		}
		Type type = target.GetType();
		foreach (string name in candidateNames)
		{
			try
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && property.PropertyType == typeof(float[]))
				{
					property.SetValue(target, arr);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(float[]))
				{
					field.SetValue(target, arr);
					break;
				}
			}
			catch
			{
			}
		}
	}
}
public static class ConfigManager
{
	[Serializable]
	public class LegacyTravelConfig
	{
		public bool enabled = true;

		public string currencyItem = "Silver";

		public int globalTeleportPrice = 100;

		public Dictionary<string, LegacyCityConfig> cities = new Dictionary<string, LegacyCityConfig>(StringComparer.OrdinalIgnoreCase);
	}

	[Serializable]
	public class LegacyCityConfig
	{
		public bool enabled = false;

		public int? price = null;

		public float[] coords = null;

		public string targetGameObjectName = null;

		public string sceneName = null;

		public string desc = null;

		public bool visited = false;
	}

	public static LegacyTravelConfig Default()
	{
		LegacyTravelConfig legacyTravelConfig = new LegacyTravelConfig
		{
			enabled = true,
			currencyItem = "Silver",
			globalTeleportPrice = 200,
			cities = new Dictionary<string, LegacyCityConfig>(StringComparer.OrdinalIgnoreCase)
		};
		legacyTravelConfig.cities["Cierzo"] = new LegacyCityConfig
		{
			enabled = true,
			price = 200,
			coords = new float[3] { 1410.3f, 9.7f, 1665.6f },
			targetGameObjectName = "Cierzo",
			sceneName = "CierzoNewTerrain",
			desc = "Cierzo - example description",
			visited = false
		};
		legacyTravelConfig.cities["Levant"] = new LegacyCityConfig
		{
			enabled = true,
			price = 200,
			coords = new float[3] { -55.2f, 1f, 79.3f },
			targetGameObjectName = "Levant_Location",
			sceneName = "Levant",
			desc = "Levant - example description",
			visited = false
		};
		legacyTravelConfig.cities["Monsoon"] = new LegacyCityConfig
		{
			enabled = true,
			price = 200,
			coords = new float[3] { 56.893f, -4.853f, 114.147f },
			targetGameObjectName = "Monsoon_Location",
			sceneName = "Monsoon",
			desc = "Monsoon - example description",
			visited = false
		};
		legacyTravelConfig.cities["Berg"] = new LegacyCityConfig
		{
			enabled = true,
			price = 200,
			coords = new float[3] { 1202.4f, -10f, 1378.8f },
			targetGameObjectName = "Berg",
			sceneName = "Berg",
			desc = "Berg - example description",
			visited = false
		};
		legacyTravelConfig.cities["Harmattan"] = new LegacyCityConfig
		{
			enabled = true,
			price = 200,
			coords = new float[3] { 93.7f, 65.4f, 767.8f },
			targetGameObjectName = "Harmattan_Location",
			sceneName = "Harmattan",
			desc = "Harmattan - example description",
			visited = false
		};
		legacyTravelConfig.cities["Sirocco"] = new LegacyCityConfig
		{
			enabled = false,
			price = 200,
			coords = new float[3] { 62.5f, 56.8f, -54f },
			targetGameObjectName = "Sirocco_Location",
			sceneName = "NewSirocco",
			desc = "Sirocco - example description",
			visited = false
		};
		return legacyTravelConfig;
	}

	publi