Decompiled source of StationFlow v0.2.0

plugins\StationFlow.dll

Decompiled 3 days ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using StationFlow.Data;
using StationFlow.Exec;
using StationFlow.PluginCore;
using StationFlow.Rules;
using StationFlow.UI;
using StationFlow.VanillaBridge;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("StationFlow")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+f8138e3bed8779c652cd67fc4b512302e05ba351")]
[assembly: AssemblyProduct("StationFlow")]
[assembly: AssemblyTitle("StationFlow")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StationFlow
{
	[BepInPlugin("LILINGLONG.dsp.StationFlow", "StationFlow", "0.2.0")]
	public class StationFlowPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "LILINGLONG.dsp.StationFlow";

		public const string PluginName = "StationFlow";

		public const string PluginVersion = "0.2.0";

		internal static ManualLogSource LogSource;

		internal static bool DebugWindowVisible;

		internal static ConfigEntry<float> CfgPanelX;

		internal static ConfigEntry<float> CfgPanelY;

		internal static ConfigEntry<int> CfgPairSource;

		internal static ConfigEntry<int> CfgPairMethod;

		internal static ConfigEntry<int> CfgTopK;

		internal static ConfigEntry<float> CfgMinRatio;

		internal static ConfigEntry<bool> CfgCrossAstro;

		internal static ConfigEntry<bool> CfgAutoEnabled;

		internal static ConfigEntry<int> CfgAutoMaxActions;

		private DebugWindow debugWindow;

		private void Awake()
		{
			LogSource = ((BaseUnityPlugin)this).Logger;
			LogInfo("StationFlow v0.2.0 正在加载…");
			try
			{
				MainThreadDispatcher.Ensure();
				CfgPanelX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelX", 60f, "管理面板初始 X 位置(像素,左上角原点)");
				CfgPanelY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "PanelY", 60f, "管理面板初始 Y 位置(像素,左上角原点)");
				CfgPairSource = ((BaseUnityPlugin)this).Config.Bind<int>("Rule", "PairSource", 0, "数据源:0=按名字 1=按槽位");
				CfgPairMethod = ((BaseUnityPlugin)this).Config.Bind<int>("Rule", "PairMethod", 0, "配对方式:0=点对点 1=星球航路 2=行星系航路");
				CfgTopK = ((BaseUnityPlugin)this).Config.Bind<int>("Rule", "TopK", 3, "每个需求塔最多配对几个供应塔(1~5)");
				CfgMinRatio = ((BaseUnityPlugin)this).Config.Bind<float>("Rule", "MinSupplyRatio", 0.2f, "最小供应能力占需求容量的比例(0.1~0.5)");
				CfgCrossAstro = ((BaseUnityPlugin)this).Config.Bind<bool>("Rule", "AllowCrossAstro", true, "是否允许跨行星系配对");
				CfgAutoEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto", "Enabled", false, "自动巡检:发现新建塔时自动增量配对(默认关)");
				CfgAutoMaxActions = ((BaseUnityPlugin)this).Config.Bind<int>("Auto", "MaxActionsPerSweep", 30, "每次自动巡检最多执行的配对数(1~200)");
				debugWindow = ((Component)this).gameObject.AddComponent<DebugWindow>();
				((Component)this).gameObject.AddComponent<Executor>();
				((Component)this).gameObject.AddComponent<AutoPilot>();
				((Component)this).gameObject.AddComponent<MainPanel>();
				LogInfo("初始化完成(F8 主面板[总览/规则配对/物流网/诊断/批量工具] / F9 调试窗)。");
			}
			catch (Exception arg)
			{
				LogError($"初始化失败(mod 将被禁用): {arg}");
			}
		}

		private void Update()
		{
			try
			{
				if (Input.GetKeyDown((KeyCode)289))
				{
					MainPanel.Instance?.Toggle();
				}
				if (Input.GetKeyDown((KeyCode)290))
				{
					DebugWindowVisible = !DebugWindowVisible;
					LogInfo("调试窗 " + (DebugWindowVisible ? "打开" : "关闭"));
				}
			}
			catch (Exception arg)
			{
				LogError($"热键处理异常: {arg}");
			}
		}

		public static void LogInfo(string msg)
		{
			ManualLogSource logSource = LogSource;
			if (logSource != null)
			{
				logSource.LogInfo((object)("[StationFlow] " + msg));
			}
		}

		public static void LogWarn(string msg)
		{
			ManualLogSource logSource = LogSource;
			if (logSource != null)
			{
				logSource.LogWarning((object)("[StationFlow] " + msg));
			}
		}

		public static void LogError(string msg)
		{
			ManualLogSource logSource = LogSource;
			if (logSource != null)
			{
				logSource.LogError((object)("[StationFlow] " + msg));
			}
		}
	}
}
namespace StationFlow.VanillaBridge
{
	public static class DumpTool
	{
		public static string DumpRootDir => Path.Combine(Paths.BepInExRootPath, "StationFlow");

		public static string Dump()
		{
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Expected I4, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Expected I4, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected I4, but got Unknown
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			if (!StationOps.GameReady)
			{
				return null;
			}
			Directory.CreateDirectory(DumpRootDir);
			string text = Path.Combine(DumpRootDir, $"dump-{DateTime.Now:yyyyMMdd-HHmmss}.csv");
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("gid,entityId,name,planetId,planetName,isStellar,isCollector,routePriority,groupMask,pairTotal,pairNormal,pairP2P,pairPlanetRoute,pairAstroRoute,pairGroup,pairFallback,slots(itemId:ll:rl:count:max)");
			foreach (StationComponent item in StationOps.AllStations())
			{
				string text2 = CsvEscape(StationOps.GetName(item));
				string text3 = CsvEscape(StationOps.PlanetName(item.planetId));
				string text4 = "0,0,0,0,0,0,0";
				int[] remotePairOffsets = item.remotePairOffsets;
				if (remotePairOffsets != null && remotePairOffsets.Length >= 7)
				{
					text4 = string.Join(",", remotePairOffsets[6].ToString(), (remotePairOffsets[1] - remotePairOffsets[0]).ToString(), (remotePairOffsets[2] - remotePairOffsets[1]).ToString(), (remotePairOffsets[3] - remotePairOffsets[2]).ToString(), (remotePairOffsets[4] - remotePairOffsets[3]).ToString(), (remotePairOffsets[5] - remotePairOffsets[4]).ToString(), (remotePairOffsets[6] - remotePairOffsets[5]).ToString());
				}
				StringBuilder stringBuilder2 = new StringBuilder();
				if (item.storage != null)
				{
					for (int i = 0; i < item.storage.Length; i++)
					{
						StationStore val = item.storage[i];
						if (val.itemId > 0)
						{
							if (stringBuilder2.Length > 0)
							{
								stringBuilder2.Append(';');
							}
							stringBuilder2.Append($"{val.itemId}:{(int)val.localLogic}:{(int)val.remoteLogic}:{val.count}:{val.max}");
						}
					}
				}
				stringBuilder.AppendLine($"{item.gid},{item.entityId},{text2},{item.planetId},{text3},{item.isStellar},{item.isCollector}," + $"{(int)item.routePriority},{item.remoteGroupMask},{text4},{CsvEscape(stringBuilder2.ToString())}");
			}
			File.WriteAllText(text, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
			StationFlowPlugin.LogInfo($"Dump 完成:{text}({StationOps.CountStations()} 个塔)");
			return text;
		}

		private static string CsvEscape(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			if (s.IndexOfAny(new char[5] { ',', '"', '\n', '\r', ';' }) >= 0)
			{
				return "\"" + s.Replace("\"", "\"\"") + "\"";
			}
			return s;
		}
	}
	public static class StationOps
	{
		public static bool GameReady
		{
			get
			{
				try
				{
					return GameMain.data != null && GameMain.data.galacticTransport != null && GameMain.data.galacticTransport.stationPool != null;
				}
				catch
				{
					return false;
				}
			}
		}

		public static GalacticTransport GT
		{
			get
			{
				if (!GameReady)
				{
					return null;
				}
				return GameMain.data.galacticTransport;
			}
		}

		public static int StationCursor
		{
			get
			{
				if (GT == null)
				{
					return 0;
				}
				return GT.stationCursor;
			}
		}

		public static int RouteCount => GT?.astro2astroRoutes.Count ?? 0;

		public static int P2PRouteCount => GT?.station2stationRoutes.Count ?? 0;

		public static IEnumerable<StationComponent> AllStations()
		{
			GalacticTransport gT = GT;
			if (gT == null)
			{
				yield break;
			}
			StationComponent[] pool = gT.stationPool;
			int cursor = Math.Min(gT.stationCursor, (pool != null) ? pool.Length : 0);
			for (int i = 1; i < cursor; i++)
			{
				StationComponent val = pool[i];
				if (val != null && val.gid == i && val.id > 0)
				{
					yield return val;
				}
			}
		}

		public static int CountStations()
		{
			int num = 0;
			foreach (StationComponent item in AllStations())
			{
				_ = item;
				num++;
			}
			return num;
		}

		public static StationComponent GetStation(int gid)
		{
			GalacticTransport gT = GT;
			if (gT == null || gid <= 0 || gid >= gT.stationPool.Length)
			{
				return null;
			}
			StationComponent val = gT.stationPool[gid];
			if (val != null && val.gid == gid && val.id > 0)
			{
				return val;
			}
			return null;
		}

		public static PlanetFactory GetFactory(int planetId)
		{
			try
			{
				GameData data = GameMain.data;
				GalaxyData galaxy = GameMain.galaxy;
				if (data == null || data.factories == null || galaxy == null)
				{
					return null;
				}
				PlanetData val = galaxy.PlanetById(planetId);
				if (val == null)
				{
					return null;
				}
				int factoryIndex = val.factoryIndex;
				if (factoryIndex < 0 || factoryIndex >= data.factories.Length)
				{
					return null;
				}
				return data.factories[factoryIndex];
			}
			catch (Exception ex)
			{
				StationFlowPlugin.LogWarn($"GetFactory({planetId}) 异常: {ex.Message}");
				return null;
			}
		}

		public static string GetName(StationComponent st)
		{
			if (st == null)
			{
				return "";
			}
			PlanetFactory factory = GetFactory(st.planetId);
			if (factory == null || st.entityId <= 0)
			{
				return "";
			}
			try
			{
				return factory.ReadExtraInfoOnEntity(st.entityId) ?? "";
			}
			catch (Exception ex)
			{
				StationFlowPlugin.LogWarn($"GetName(gid={st.gid}) 异常: {ex.Message}");
				return "";
			}
		}

		public static bool SetName(StationComponent st, string name)
		{
			if (st == null)
			{
				StationFlowPlugin.LogWarn("SetName: 目标塔为空");
				return false;
			}
			PlanetFactory factory = GetFactory(st.planetId);
			if (factory == null)
			{
				StationFlowPlugin.LogWarn($"SetName: 无法定位工厂 (gid={st.gid}, planetId={st.planetId})");
				return false;
			}
			if (st.entityId <= 0)
			{
				StationFlowPlugin.LogWarn($"SetName: entityId 无效 (gid={st.gid})");
				return false;
			}
			try
			{
				factory.WriteExtraInfoOnEntity(st.entityId, (name ?? "").Trim());
				return true;
			}
			catch (Exception arg)
			{
				StationFlowPlugin.LogError($"SetName 异常 (gid={st.gid}, entityId={st.entityId}): {arg}");
				return false;
			}
		}

		public static bool AddP2PRaw(int gidA, int gidB)
		{
			GalacticTransport gT = GT;
			if (gT == null || gidA <= 0 || gidB <= 0 || gidA == gidB)
			{
				return false;
			}
			if (GetStation(gidA) == null || GetStation(gidB) == null)
			{
				return false;
			}
			return gT.station2stationRoutes.Add(gT.CalculateStation2StationKey(gidA, gidB));
		}

		public static bool RemoveP2PRaw(int gidA, int gidB)
		{
			GalacticTransport gT = GT;
			if (gT == null || gidA <= 0 || gidB <= 0 || gidA == gidB)
			{
				return false;
			}
			return gT.station2stationRoutes.Remove(gT.CalculateStation2StationKey(gidA, gidB));
		}

		public static bool AddP2P(int gidA, int gidB)
		{
			bool num = AddP2PRaw(gidA, gidB);
			if (num)
			{
				RefreshGalacticTraffic("AddP2P");
			}
			return num;
		}

		public static bool RemoveP2P(int gidA, int gidB)
		{
			bool num = RemoveP2PRaw(gidA, gidB);
			if (num)
			{
				RefreshGalacticTraffic("RemoveP2P");
			}
			return num;
		}

		public static bool HasP2P(int gidA, int gidB)
		{
			GalacticTransport gT = GT;
			return gT?.station2stationRoutes.Contains(gT.CalculateStation2StationKey(gidA, gidB)) ?? false;
		}

		public static int CountP2PRoutesOf(int gid)
		{
			GalacticTransport gT = GT;
			if (gT == null || gid <= 0)
			{
				return 0;
			}
			int num = 0;
			foreach (long station2stationRoute in gT.station2stationRoutes)
			{
				int num2 = (int)(station2stationRoute & 0xFFFFFFFFu);
				int num3 = (int)(station2stationRoute >> 32);
				if (num2 == gid || num3 == gid)
				{
					num++;
				}
			}
			return num;
		}

		public static bool AddAstroRouteRaw(int aId, int bId, int itemId)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			GalacticTransport gT = GT;
			if (gT == null || aId <= 0 || bId <= 0 || aId == bId || itemId <= 0)
			{
				return false;
			}
			long key = gT.CalculateAstro2AstroKey(aId, bId, itemId);
			if (gT.astro2astroRoutes.TryGetValue(key, out var value))
			{
				if (value.enable)
				{
					return false;
				}
				value.enable = true;
				return true;
			}
			LogisticShipRoute val = new LogisticShipRoute();
			val.Init();
			gT.astro2astroRoutes.Add(key, val);
			return true;
		}

		public static bool RemoveAstroRouteRaw(int aId, int bId, int itemId)
		{
			GalacticTransport gT = GT;
			if (gT == null || aId <= 0 || bId <= 0 || aId == bId || itemId <= 0)
			{
				return false;
			}
			return gT.astro2astroRoutes.Remove(gT.CalculateAstro2AstroKey(aId, bId, itemId));
		}

		public static bool HasAstroRoute(int aId, int bId, int itemId)
		{
			GalacticTransport gT = GT;
			if (gT == null || itemId <= 0)
			{
				return false;
			}
			return gT.IsAstro2AstroRouteEnable(aId, bId, itemId);
		}

		private static void ClassifyEndpoint(int id, HashSet<int> planets, HashSet<int> astros)
		{
			if (id > 0)
			{
				if (id % 100 == 0)
				{
					astros.Add(id);
				}
				else
				{
					planets.Add(id);
				}
			}
		}

		public static void CollectRouteEndpointsByItem(out Dictionary<int, HashSet<int>> planetsByItem, out Dictionary<int, HashSet<int>> astrosByItem)
		{
			planetsByItem = new Dictionary<int, HashSet<int>>();
			astrosByItem = new Dictionary<int, HashSet<int>>();
			GalacticTransport gT = GT;
			if (gT == null)
			{
				return;
			}
			try
			{
				foreach (long key in gT.astro2astroRoutes.Keys)
				{
					int num = (int)(key >> 44);
					if (num > 0)
					{
						if (!planetsByItem.TryGetValue(num, out var value))
						{
							value = new HashSet<int>();
							HashSet<int> value2 = new HashSet<int>();
							planetsByItem[num] = value;
							astrosByItem[num] = value2;
						}
						ClassifyEndpoint((int)(key & 0x3FFFFF), planetsByItem[num], astrosByItem[num]);
						ClassifyEndpoint((int)((key >> 22) & 0x3FFFFF), planetsByItem[num], astrosByItem[num]);
					}
				}
			}
			catch
			{
			}
		}

		public static bool IsPlanetCoveredByRouteForItem(int planetId, int itemId, Dictionary<int, HashSet<int>> planetsByItem, Dictionary<int, HashSet<int>> astrosByItem)
		{
			if (planetsByItem != null && planetsByItem.TryGetValue(itemId, out var value) && value.Contains(planetId))
			{
				return true;
			}
			if (astrosByItem != null && astrosByItem.TryGetValue(itemId, out var value2) && value2.Contains(AstroId(planetId)))
			{
				return true;
			}
			return false;
		}

		public static Dictionary<int, int> GetP2PRouteCounts()
		{
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			GalacticTransport gT = GT;
			if (gT == null)
			{
				return dictionary;
			}
			foreach (long station2stationRoute in gT.station2stationRoutes)
			{
				int key = (int)(station2stationRoute & 0xFFFFFFFFu);
				int key2 = (int)(station2stationRoute >> 32);
				dictionary.TryGetValue(key, out var value);
				dictionary[key] = value + 1;
				dictionary.TryGetValue(key2, out value);
				dictionary[key2] = value + 1;
			}
			return dictionary;
		}

		public static void SetBehavior(StationComponent st, ERemoteRoutePriority priority)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (st != null)
			{
				st.routePriority = priority;
			}
		}

		public static void ToggleGroup(StationComponent st, int bit)
		{
			if (st != null && bit >= 0 && bit <= 62)
			{
				st.remoteGroupMask ^= 1L << bit;
				RefreshGalacticTraffic("ToggleGroup");
			}
		}

		public static bool SetGroupBitRaw(StationComponent st, int bit, bool on)
		{
			if (st == null || bit < 0 || bit > 62)
			{
				return false;
			}
			long remoteGroupMask = st.remoteGroupMask;
			long num = (on ? (remoteGroupMask | (1L << bit)) : (remoteGroupMask & ~(1L << bit)));
			if (num == remoteGroupMask)
			{
				return false;
			}
			st.remoteGroupMask = num;
			return true;
		}

		public static bool SetSlotRemoteLogicRaw(int gid, int slotIndex, ELogisticStorage logic)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			StationComponent station = GetStation(gid);
			if (station == null || station.storage == null)
			{
				return false;
			}
			if (slotIndex < 0 || slotIndex >= station.storage.Length)
			{
				return false;
			}
			if (station.storage[slotIndex].itemId <= 0)
			{
				return false;
			}
			if (station.storage[slotIndex].remoteLogic == logic)
			{
				return false;
			}
			station.storage[slotIndex].remoteLogic = logic;
			return true;
		}

		public static void RefreshGalacticTraffic(string reason)
		{
			GalacticTransport gT = GT;
			if (gT != null)
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				gT.RefreshTraffic(0);
				stopwatch.Stop();
				StationFlowPlugin.LogInfo($"RefreshTraffic({reason}) 耗时 {stopwatch.ElapsedMilliseconds}ms");
			}
		}

		public static string PlanetName(int planetId)
		{
			try
			{
				GalaxyData galaxy = GameMain.galaxy;
				if (galaxy == null)
				{
					return planetId.ToString();
				}
				PlanetData val = galaxy.PlanetById(planetId);
				return (val != null) ? val.displayName : planetId.ToString();
			}
			catch
			{
				return planetId.ToString();
			}
		}

		public static string GalaxyName(int planetId)
		{
			try
			{
				GalaxyData galaxy = GameMain.galaxy;
				if (galaxy == null)
				{
					return "";
				}
				StarData val = galaxy.StarById(planetId / 100);
				return (val != null) ? val.displayName : "";
			}
			catch
			{
				return "";
			}
		}

		public static string ShortPlanetName(int planetId)
		{
			string text = PlanetName(planetId);
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			int num = text.LastIndexOf('-');
			if (num < 0)
			{
				return text;
			}
			return text.Substring(num + 1).Trim();
		}

		public static int AstroId(int planetId)
		{
			return planetId / 100 * 100;
		}

		public static string ItemName(int itemId)
		{
			try
			{
				if (itemId <= 0)
				{
					return "";
				}
				ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
				return (val != null) ? ((Proto)val).name : itemId.ToString();
			}
			catch
			{
				return itemId.ToString();
			}
		}

		public static Sprite ItemIcon(int itemId)
		{
			try
			{
				if (itemId <= 0)
				{
					return null;
				}
				ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
				return (val != null) ? val.iconSprite : null;
			}
			catch
			{
				return null;
			}
		}

		public static List<KeyValuePair<int, string>> AllItemNames()
		{
			List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
			try
			{
				ItemProtoSet items = LDB.items;
				if ((Object)(object)items == (Object)null || ((ProtoSet<ItemProto>)(object)items).dataArray == null)
				{
					return list;
				}
				ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)items).dataArray;
				foreach (ItemProto val in dataArray)
				{
					if (val != null && ((Proto)val).ID > 0 && !string.IsNullOrEmpty(((Proto)val).name))
					{
						string text = ((Proto)val).name.Trim();
						if (text.Length != 0)
						{
							list.Add(new KeyValuePair<int, string>(((Proto)val).ID, text));
						}
					}
				}
			}
			catch (Exception ex)
			{
				StationFlowPlugin.LogWarn("AllItemNames 异常: " + ex.Message);
			}
			return list;
		}

		public static bool TryGetPlanetPosition(int planetId, out double px, out double py, out double pz)
		{
			px = (py = (pz = 0.0));
			try
			{
				GalaxyData galaxy = GameMain.galaxy;
				if (galaxy == null)
				{
					return false;
				}
				PlanetData val = galaxy.PlanetById(planetId);
				if (val == null)
				{
					return false;
				}
				px = val.uPosition.x;
				py = val.uPosition.y;
				pz = val.uPosition.z;
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static void GetTraffic(int planetId, int itemId, out long inputPerMin, out long outputPerMin, out long inputTotal, out long outputTotal)
		{
			inputPerMin = (outputPerMin = (inputTotal = (outputTotal = 0L)));
			try
			{
				GameData data = GameMain.data;
				if (data == null || data.statistics == null)
				{
					return;
				}
				TrafficStatistics traffic = data.statistics.traffic;
				if (traffic == null || traffic.factoryTrafficPool == null)
				{
					return;
				}
				GalaxyData galaxy = GameMain.galaxy;
				if (galaxy == null)
				{
					return;
				}
				PlanetData val = galaxy.PlanetById(planetId);
				if (val == null)
				{
					return;
				}
				int factoryIndex = val.factoryIndex;
				if (factoryIndex < 0 || factoryIndex >= traffic.factoryTrafficPool.Length)
				{
					return;
				}
				AstroTrafficStat val2 = traffic.factoryTrafficPool[factoryIndex];
				if (val2 == null || val2.itemIndices == null || val2.trafficPool == null || itemId <= 0 || itemId >= val2.itemIndices.Length)
				{
					return;
				}
				int num = val2.itemIndices[itemId];
				if (num > 0 && num < val2.trafficCursor)
				{
					TrafficStat val3 = val2.trafficPool[num];
					if (val3 != null && val3.total != null && val3.total.Length >= 21)
					{
						inputPerMin = val3.total[3];
						outputPerMin = val3.total[10];
						inputTotal = val3.total[6];
						outputTotal = val3.total[13];
					}
				}
			}
			catch
			{
			}
		}

		public static int MainItemId(StationComponent st)
		{
			if (st == null || st.storage == null)
			{
				return 0;
			}
			for (int i = 0; i < st.storage.Length; i++)
			{
				if (st.storage[i].itemId > 0)
				{
					return st.storage[i].itemId;
				}
			}
			return 0;
		}

		public static StationComponent CurrentOpenedStation()
		{
			try
			{
				UIGame val = (((Object)(object)UIRoot.instance != (Object)null) ? UIRoot.instance.uiGame : null);
				UIStationWindow val2 = (((Object)(object)val != (Object)null) ? val.stationWindow : null);
				if ((Object)(object)val2 == (Object)null || !((ManualBehaviour)val2).active)
				{
					return null;
				}
				int stationId = val2.stationId;
				return (stationId > 0) ? GetStation(stationId) : null;
			}
			catch
			{
				return null;
			}
		}
	}
}
namespace StationFlow.UI
{
	public class BatchTab
	{
		private enum TopoMode
		{
			Ring,
			Chain,
			Star,
			AllToAll
		}

		private RectTransform root;

		private MainPanel owner;

		private Text selText;

		private Text topoStat;

		private Text topoPreview;

		private InputField groupInput;

		private Text statusText;

		private List<PlanAction> topoPlan;

		public void Init(RectTransform parent, MainPanel owner)
		{
			this.owner = owner;
			root = UIFactory.CreateNode((Transform)(object)parent, "BatchTab");
			UIFactory.Stretch(root);
			BuildUI();
		}

		private void BuildUI()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Expected O, but got Unknown
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Expected O, but got Unknown
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cb: Expected O, but got Unknown
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Expected O, but got Unknown
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			float num = 8f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "T", "批量工具 · 对「总览与命名」页选中的塔生效", 15, (TextAnchor)3, UIFactory.ColAccent)).rectTransform, 12f, num, 600f, 24f);
			num += 30f;
			selText = UIFactory.CreateText((Transform)(object)root, "Sel", "未选中任何塔——请先到「总览与命名」页选择(Ctrl/Shift 多选)。", 13, (TextAnchor)3, UIFactory.ColText);
			UIFactory.PlaceTopLeft(((Graphic)selText).rectTransform, 12f, num, 1080f, 22f);
			num += 32f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "S1", "── 拓扑配对(按选中顺序生成 P2P 边;同星球自动跳过)──", 13, (TextAnchor)3, UIFactory.ColAccent)).rectTransform, 12f, num, 800f, 20f);
			num += 26f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "D", "Ring 环线(首尾相连)· Chain 链式 · Star 星形(第一个为心)· AllToAll 全连(边数 = n×(n-1)/2,多选时慎用)", 12, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 12f, num, 1080f, 18f);
			num += 22f;
			float x = 12f;
			AddTopoButton("Ring 环线", TopoMode.Ring, ref x, num);
			AddTopoButton("Chain 链式", TopoMode.Chain, ref x, num);
			AddTopoButton("Star 星形", TopoMode.Star, ref x, num);
			AddTopoButton("AllToAll 全连", TopoMode.AllToAll, ref x, num);
			num += 32f;
			topoStat = UIFactory.CreateText((Transform)(object)root, "TS", "", 12, (TextAnchor)3, UIFactory.ColWarn);
			UIFactory.PlaceTopLeft(((Graphic)topoStat).rectTransform, 12f, num, 1080f, 18f);
			num += 20f;
			topoPreview = UIFactory.CreateText((Transform)(object)root, "TP", "点上面的按钮生成配对预览。", 12, (TextAnchor)0, UIFactory.ColText);
			topoPreview.horizontalOverflow = (HorizontalWrapMode)0;
			topoPreview.verticalOverflow = (VerticalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)topoPreview).rectTransform, 12f, num, 1080f, 92f);
			num += 98f;
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "ET", "执行配对", 13, DoExecuteTopo)).transform, 12f, num, 120f, 28f);
			num += 42f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "S2", "── 批量设置(立即应用到选中塔,可撤销)──", 13, (TextAnchor)3, UIFactory.ColAccent)).rectTransform, 12f, num, 800f, 20f);
			num += 26f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "BL", "行为:", 13, (TextAnchor)3, UIFactory.ColText)).rectTransform, 12f, num, 52f, 24f);
			x = 64f;
			AddBehaviorButton("忽略", 1, ref x, num);
			AddBehaviorButton("优先", 2, ref x, num);
			AddBehaviorButton("仅配对", 3, ref x, num);
			AddBehaviorButton("指定", 4, ref x, num);
			num += 32f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "GL", "分组(1~30):", 13, (TextAnchor)3, UIFactory.ColText)).rectTransform, 12f, num, 104f, 24f);
			groupInput = UIFactory.CreateInputField((Transform)(object)root, "GI", "组号", 13);
			UIFactory.PlaceTopLeft((RectTransform)((Component)groupInput).transform, 118f, num, 56f, 24f);
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "Join", "加入组", 12, delegate
			{
				DoGroup(join: true);
			})).transform, 182f, num, 76f, 24f);
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "Leave", "移出组", 12, delegate
			{
				DoGroup(join: false);
			})).transform, 264f, num, 76f, 24f);
			num += 36f;
			statusText = UIFactory.CreateText((Transform)(object)root, "St", "就绪", 12, (TextAnchor)3, UIFactory.ColTextDim);
			UIFactory.PlaceTopLeft(((Graphic)statusText).rectTransform, 12f, num, 1080f, 20f);
		}

		private void AddTopoButton(string label, TopoMode mode, ref float x, float y)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "TM" + mode, label, 12, delegate
			{
				BuildTopoPreview(mode);
			})).transform, x, y, 122f, 26f);
			x += 126f;
		}

		private void AddBehaviorButton(string label, int behavior, ref float x, float y)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "BH" + behavior, label, 12, delegate
			{
				DoBehavior(behavior);
			})).transform, x, y, 66f, 24f);
			x += 70f;
		}

		public void Refresh()
		{
			UpdateSelText();
		}

		public void Update()
		{
		}

		private void UpdateSelText()
		{
			StationTable stationTable = (((Object)(object)owner != (Object)null) ? owner.Table : null);
			if (stationTable == null || (Object)(object)selText == (Object)null)
			{
				return;
			}
			int selectedCount = stationTable.SelectedCount;
			if (selectedCount == 0)
			{
				selText.text = "未选中任何塔——请先到「总览与命名」页选择(Ctrl/Shift 多选)。";
				return;
			}
			List<StationRow> selectedRows = stationTable.SelectedRows;
			StringBuilder stringBuilder = new StringBuilder($"已选 {selectedCount} 塔:");
			for (int i = 0; i < Mathf.Min(selectedRows.Count, 12); i++)
			{
				stringBuilder.Append(" #").Append(selectedRows[i].Gid);
				if (i == 11 && selectedRows.Count > 12)
				{
					stringBuilder.Append(" …");
				}
			}
			selText.text = stringBuilder.ToString();
		}

		private List<StationRow> GetSelectedRows()
		{
			StationTable stationTable = (((Object)(object)owner != (Object)null) ? owner.Table : null);
			if (stationTable == null)
			{
				return new List<StationRow>();
			}
			return stationTable.SelectedRows;
		}

		private void BuildTopoPreview(TopoMode mode)
		{
			List<StationRow> selectedRows = GetSelectedRows();
			if (selectedRows.Count < 2)
			{
				topoPlan = null;
				topoStat.text = "至少需要选中 2 个塔。";
				topoPreview.text = "";
				return;
			}
			Dictionary<int, StationRow> byGid = new Dictionary<int, StationRow>();
			List<int> list = new List<int>();
			foreach (StationRow item2 in selectedRows)
			{
				byGid[item2.Gid] = item2;
				list.Add(item2.Gid);
			}
			List<PlanAction> actions = new List<PlanAction>();
			HashSet<long> seen = new HashSet<long>();
			int sameSkip = 0;
			Action<int, int> action = delegate(int ga, int gb)
			{
				if (ga != gb)
				{
					StationRow stationRow = byGid[ga];
					StationRow stationRow2 = byGid[gb];
					if (stationRow.PlanetId == stationRow2.PlanetId)
					{
						sameSkip++;
					}
					else
					{
						int num8 = Math.Min(ga, gb);
						int num9 = Math.Max(ga, gb);
						long item = num8 | ((long)num9 << 32);
						if (seen.Add(item))
						{
							actions.Add(new PlanAction
							{
								Type = PlanActionType.AddP2P,
								Gid = num8,
								GidB = num9
							});
						}
					}
				}
			};
			int count = list.Count;
			string arg;
			switch (mode)
			{
			case TopoMode.Ring:
			{
				arg = "Ring 环线";
				for (int num5 = 0; num5 < count; num5++)
				{
					action(list[num5], list[(num5 + 1) % count]);
				}
				break;
			}
			case TopoMode.Chain:
			{
				arg = "Chain 链式";
				for (int num3 = 0; num3 + 1 < count; num3++)
				{
					action(list[num3], list[num3 + 1]);
				}
				break;
			}
			case TopoMode.Star:
			{
				arg = "Star 星形";
				for (int num4 = 1; num4 < count; num4++)
				{
					action(list[0], list[num4]);
				}
				break;
			}
			default:
			{
				arg = "AllToAll 全连";
				for (int num = 0; num < count; num++)
				{
					for (int num2 = num + 1; num2 < count; num2++)
					{
						action(list[num], list[num2]);
					}
				}
				break;
			}
			}
			topoPlan = actions;
			string text = ((mode == TopoMode.AllToAll && count > 12) ? " ⚠ 边数较多" : "");
			topoStat.text = $"{arg}:{count} 塔 → {actions.Count} 条边" + ((sameSkip > 0) ? $"(跳过同星球 {sameSkip} 条)" : "") + text;
			StringBuilder stringBuilder = new StringBuilder();
			int num6 = Mathf.Min(actions.Count, 8);
			for (int num7 = 0; num7 < num6; num7++)
			{
				PlanAction planAction = actions[num7];
				stringBuilder.Append('#').Append(planAction.Gid).Append(' ')
					.Append(NameOf(byGid[planAction.Gid]))
					.Append("  ↔  #")
					.Append(planAction.GidB)
					.Append(' ')
					.Append(NameOf(byGid[planAction.GidB]))
					.Append('\n');
			}
			if (actions.Count > num6)
			{
				stringBuilder.Append("… 共 ").Append(actions.Count).Append(" 条\n");
			}
			if (actions.Count == 0)
			{
				stringBuilder.Append("没有可生成的边(全部为同星球配对,已跳过)。");
			}
			topoPreview.text = stringBuilder.ToString();
			SetStatus($"已生成配对预览:{actions.Count} 条边,点「执行配对」写入。");
		}

		private void DoExecuteTopo()
		{
			if (topoPlan == null || topoPlan.Count == 0)
			{
				SetStatus("请先点拓扑按钮生成配对预览。");
				return;
			}
			RunPlan(new ExecutionPlan
			{
				Title = "拓扑配对",
				Actions = topoPlan
			});
		}

		private void DoBehavior(int behavior)
		{
			List<StationRow> selectedRows = GetSelectedRows();
			if (selectedRows.Count == 0)
			{
				SetStatus("未选中塔。");
				return;
			}
			ExecutionPlan executionPlan = new ExecutionPlan
			{
				Title = "批量设行为(" + BehaviorName(behavior) + ")"
			};
			foreach (StationRow item in selectedRows)
			{
				executionPlan.Actions.Add(new PlanAction
				{
					Type = PlanActionType.SetBehavior,
					Gid = item.Gid,
					IntValue = behavior
				});
			}
			RunPlan(executionPlan);
		}

		private void DoGroup(bool join)
		{
			List<StationRow> selectedRows = GetSelectedRows();
			if (selectedRows.Count == 0)
			{
				SetStatus("未选中塔。");
				return;
			}
			if ((Object)(object)groupInput == (Object)null || !int.TryParse(groupInput.text.Trim(), out var result) || result < 1 || result > 30)
			{
				SetStatus("请输入组号(1~30)。");
				return;
			}
			int intValue = result - 1;
			ExecutionPlan executionPlan = new ExecutionPlan
			{
				Title = string.Format("批量{0}分组{1}", join ? "加入" : "移出", result)
			};
			foreach (StationRow item in selectedRows)
			{
				executionPlan.Actions.Add(new PlanAction
				{
					Type = PlanActionType.SetGroupBit,
					Gid = item.Gid,
					IntValue = intValue,
					BoolValue = join
				});
			}
			RunPlan(executionPlan);
		}

		private void RunPlan(ExecutionPlan plan)
		{
			Executor instance = Executor.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				SetStatus("Executor 未初始化。");
				return;
			}
			if (instance.IsRunning)
			{
				SetStatus("已有执行任务进行中,请稍候…");
				return;
			}
			instance.OnCompleted += OnExecCompleted;
			if (!instance.Execute(plan))
			{
				instance.OnCompleted -= OnExecCompleted;
				SetStatus("执行未启动(游戏未就绪或任务冲突)。");
			}
			else
			{
				SetStatus($"执行中:{plan.Title}({plan.Actions.Count} 条)…");
			}
		}

		private void OnExecCompleted()
		{
			Executor instance = Executor.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.OnCompleted -= OnExecCompleted;
			}
			ExecutionReport executionReport = (((Object)(object)instance != (Object)null) ? instance.LastReport : null);
			SetStatus((executionReport != null) ? ("完成:" + executionReport.Summary) : "完成");
			if ((Object)(object)owner != (Object)null)
			{
				owner.RefreshData();
			}
		}

		private void SetStatus(string msg)
		{
			if ((Object)(object)statusText != (Object)null)
			{
				statusText.text = msg;
			}
			if ((Object)(object)owner != (Object)null)
			{
				owner.SetStatus(msg);
			}
			StationFlowPlugin.LogInfo("[批量] " + msg);
		}

		private static string BehaviorName(int b)
		{
			return b switch
			{
				1 => "忽略", 
				2 => "优先", 
				3 => "仅配对", 
				_ => "指定", 
			};
		}

		private static string NameOf(StationRow r)
		{
			if (r == null)
			{
				return "";
			}
			string text = (r.HasName ? r.Name : "未命名");
			if (text.Length > 14)
			{
				return text.Substring(0, 14) + "…";
			}
			return text;
		}
	}
	public class DiagTab
	{
		private RectTransform root;

		private MainPanel owner;

		private Text overviewText;

		private Text listText;

		private RectTransform content;

		public void Init(RectTransform parent, MainPanel owner)
		{
			this.owner = owner;
			root = UIFactory.CreateNode((Transform)(object)parent, "DiagTab");
			UIFactory.Stretch(root);
			BuildUI();
		}

		private void BuildUI()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)root, "Run", "执行诊断", 13, DoRun)).transform, 8f, 6f, 90f, 26f);
			overviewText = UIFactory.CreateText((Transform)(object)root, "Ov", "点「执行诊断」检查配置健康度…", 12, (TextAnchor)3, UIFactory.ColTextDim);
			UIFactory.PlaceTopLeft(((Graphic)overviewText).rectTransform, 106f, 6f, 970f, 26f);
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateScrollView((Transform)(object)root, "Scroll", out var viewport, out content)).transform, 8f, 38f, 1094f, 514f);
			((Component)viewport).gameObject.AddComponent<ModScrollZone>();
			listText = UIFactory.CreateText((Transform)(object)content, "List", "", 12, (TextAnchor)0, UIFactory.ColText);
			listText.supportRichText = true;
			listText.horizontalOverflow = (HorizontalWrapMode)0;
			listText.verticalOverflow = (VerticalWrapMode)1;
			((Graphic)listText).rectTransform.anchorMin = new Vector2(0f, 1f);
			((Graphic)listText).rectTransform.anchorMax = new Vector2(1f, 1f);
			((Graphic)listText).rectTransform.pivot = new Vector2(0.5f, 1f);
			((Graphic)listText).rectTransform.anchoredPosition = new Vector2(0f, -4f);
			((Graphic)listText).rectTransform.sizeDelta = new Vector2(-16f, 100f);
		}

		public void Refresh()
		{
			DoRun();
		}

		public void Update()
		{
		}

		private void DoRun()
		{
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			if (!StationOps.GameReady)
			{
				if ((Object)(object)overviewText != (Object)null)
				{
					overviewText.text = "游戏未就绪(请先进入存档)";
				}
				if ((Object)(object)listText != (Object)null)
				{
					listText.text = "";
				}
				return;
			}
			DiagReport diagReport = Diagnostics.Run(((Object)(object)owner != (Object)null) ? owner.AllRows : null);
			overviewText.text = diagReport.Overview;
			StringBuilder stringBuilder = new StringBuilder();
			if (diagReport.Items.Count == 0)
			{
				stringBuilder.Append("<color=#7fd68f>✔ 未发现高危问题——配对配置看起来正常。</color>\n\n");
				stringBuilder.Append("(诊断覆盖:停工风险塔 / 断供物品 / 供应偏紧。)\n");
				stringBuilder.Append("物流是否真的在流动,请看「物流网」页的流量数据。");
			}
			else
			{
				stringBuilder.Append($"<color=#ff6b5a>✖ 高危 {diagReport.DangerCount}</color>   <color=#ffb84d>▲ 警告 {diagReport.WarnCount}</color>\n\n");
				foreach (DiagItem item in diagReport.Items)
				{
					string text = ((item.Level == DiagLevel.Danger) ? "#ff6b5a" : ((item.Level == DiagLevel.Warn) ? "#ffb84d" : "#9adcff"));
					string text2 = ((item.Level == DiagLevel.Danger) ? "✖" : ((item.Level == DiagLevel.Warn) ? "▲" : "●"));
					stringBuilder.Append("<color=" + text + ">" + text2 + " " + item.Title + "</color>\n");
					if (!string.IsNullOrEmpty(item.Detail))
					{
						stringBuilder.Append("      → " + item.Detail + "\n");
					}
					stringBuilder.Append('\n');
				}
			}
			listText.text = stringBuilder.ToString();
			float preferredHeight = listText.preferredHeight;
			((Graphic)listText).rectTransform.sizeDelta = new Vector2(-16f, preferredHeight);
			content.sizeDelta = new Vector2(0f, preferredHeight + 12f);
		}
	}
	public class DragHandler : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler, IEndDragHandler
	{
		public RectTransform Target;

		public Action OnEndMove;

		private Vector2 startPos;

		private Vector2 startMouse;

		public void OnBeginDrag(PointerEventData e)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Target == (Object)null))
			{
				startPos = Target.anchoredPosition;
				startMouse = e.position;
			}
		}

		public void OnDrag(PointerEventData e)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (!((Object)(object)Target == (Object)null))
			{
				Target.anchoredPosition = startPos + (e.position - startMouse);
			}
		}

		public void OnEndDrag(PointerEventData e)
		{
			if (OnEndMove != null)
			{
				OnEndMove();
			}
		}
	}
	public class IconHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		public static Action<string> OnHoverText;

		public string Tip;

		public void OnPointerEnter(PointerEventData e)
		{
			if (!string.IsNullOrEmpty(Tip) && OnHoverText != null)
			{
				OnHoverText(Tip);
			}
		}

		public void OnPointerExit(PointerEventData e)
		{
			if (OnHoverText != null)
			{
				OnHoverText("");
			}
		}
	}
	public class MainPanel : MonoBehaviour
	{
		private class RenameUndoEntry
		{
			public int Gid;

			public string OldName;
		}

		public static MainPanel Instance;

		private const float PanelW = 1110f;

		private const float PanelH = 640f;

		private GameObject canvasGo;

		private RectTransform panelRt;

		private bool built;

		private bool visible;

		private StationTable table;

		private List<StationRow> allRows = new List<StationRow>();

		private GameObject tab1Group;

		private GameObject tab2Group;

		private GameObject tab3Group;

		private GameObject tab4Group;

		private GameObject tab5Group;

		private Button tab1Btn;

		private Button tab2Btn;

		private Button tab3Btn;

		private Button tab4Btn;

		private Button tab5Btn;

		private int tabIndex;

		private RuleTab ruleTab;

		private TopologyTab topoTab;

		private DiagTab diagTab;

		private BatchTab batchTab;

		private InputField templateInput;

		private Text previewText;

		private Text selectionText;

		private Text statusText;

		private Text exampleText;

		private Button skipModeBtn;

		private Button overwriteModeBtn;

		private NamingService.ExistingNameMode nameMode;

		private List<RenamePlanItem> currentPlan;

		private readonly List<RenameUndoEntry> lastUndo = new List<RenameUndoEntry>();

		private string lastStatus = "就绪";

		private static readonly (string label, string tpl, string desc)[] PresetTemplates = new(string, string, string)[4]
		{
			("全称", "供{供}需{需} {星系}{行星}{序号}号塔", "星际供需全称格式:供X需Y 星系+行星+几号塔"),
			("经典", "[{物品}][{角色}]-{序号:2}", "单物品塔经典格式:物品+角色+序号"),
			("星球", "[{星球}]-{物品}-{序号:2}", "按星球归类命名"),
			("仓储", "储{储} {星系}{行星}{序号}号塔", "星际仓储塔(无星际供需、纯中转存放)")
		};

		public List<StationRow> AllRows => allRows;

		public StationTable Table => table;

		public bool IsVisible => visible;

		public void NotifyAutoRun(string info)
		{
			if (visible)
			{
				RefreshData();
			}
			SetStatus("[自动巡检] " + info);
		}

		private void Awake()
		{
			Instance = this;
		}

		public void Toggle()
		{
			if (visible)
			{
				Hide();
			}
			else
			{
				Show();
			}
		}

		public void Show()
		{
			if (!built)
			{
				BuildUI();
			}
			canvasGo.SetActive(true);
			visible = true;
			RefreshData();
			ShowTab(tabIndex);
		}

		public void Hide()
		{
			if ((Object)(object)canvasGo != (Object)null)
			{
				canvasGo.SetActive(false);
			}
			visible = false;
		}

		private void Update()
		{
			if (visible)
			{
				if (ruleTab != null)
				{
					ruleTab.Update();
				}
				if (topoTab != null)
				{
					topoTab.Update();
				}
				if (batchTab != null)
				{
					batchTab.Update();
				}
			}
		}

		private void ShowTab(int idx)
		{
			tabIndex = idx;
			if ((Object)(object)tab1Group != (Object)null)
			{
				tab1Group.SetActive(idx == 0);
			}
			if ((Object)(object)tab2Group != (Object)null)
			{
				tab2Group.SetActive(idx == 1);
			}
			if ((Object)(object)tab3Group != (Object)null)
			{
				tab3Group.SetActive(idx == 2);
			}
			if ((Object)(object)tab4Group != (Object)null)
			{
				tab4Group.SetActive(idx == 3);
			}
			if ((Object)(object)tab5Group != (Object)null)
			{
				tab5Group.SetActive(idx == 4);
			}
			SetTabBtnColor(tab1Btn, idx == 0);
			SetTabBtnColor(tab2Btn, idx == 1);
			SetTabBtnColor(tab3Btn, idx == 2);
			SetTabBtnColor(tab4Btn, idx == 3);
			SetTabBtnColor(tab5Btn, idx == 4);
			if (idx == 1 && ruleTab != null)
			{
				ruleTab.Refresh();
			}
			if (idx == 2 && topoTab != null)
			{
				topoTab.Refresh();
			}
			if (idx == 3 && diagTab != null)
			{
				diagTab.Refresh();
			}
			if (idx == 4 && batchTab != null)
			{
				batchTab.Refresh();
			}
		}

		private static void SetTabBtnColor(Button b, bool active)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Image val = (((Object)(object)b != (Object)null) ? ((Component)b).GetComponent<Image>() : null);
			if ((Object)(object)val != (Object)null)
			{
				((Graphic)val).color = (active ? UIFactory.ColBtnHighlight : UIFactory.ColBtn);
			}
		}

		private void BuildUI()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Expected O, but got Unknown
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Expected O, but got Unknown
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Expected O, but got Unknown
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Expected O, but got Unknown
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Expected O, but got Unknown
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Expected O, but got Unknown
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c2: Expected O, but got Unknown
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0536: Unknown result type (might be due to invalid IL or missing references)
			//IL_0551: Expected O, but got Unknown
			//IL_056e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e4: Expected O, but got Unknown
			//IL_0600: Unknown result type (might be due to invalid IL or missing references)
			//IL_060b: Expected O, but got Unknown
			//IL_0631: Unknown result type (might be due to invalid IL or missing references)
			//IL_064b: Expected O, but got Unknown
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_0672: Expected O, but got Unknown
			//IL_0698: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b2: Expected O, but got Unknown
			//IL_06ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d9: Expected O, but got Unknown
			//IL_06ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0719: Expected O, but got Unknown
			//IL_0735: Unknown result type (might be due to invalid IL or missing references)
			//IL_0740: Expected O, but got Unknown
			//IL_075f: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a0: Unknown result type (might be due to invalid IL or missing references)
			canvasGo = UIFactory.CreateCanvasRoot("StationFlow_MainPanel", 31000);
			Image val = UIFactory.CreateImage(canvasGo.transform, "Panel", UIFactory.ColBg);
			panelRt = ((Graphic)val).rectTransform;
			RectTransform obj = panelRt;
			RectTransform obj2 = panelRt;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			obj2.anchorMax = val2;
			obj.anchorMin = val2;
			panelRt.pivot = new Vector2(0f, 1f);
			panelRt.anchoredPosition = new Vector2(StationFlowPlugin.CfgPanelX.Value, 0f - StationFlowPlugin.CfgPanelY.Value);
			panelRt.sizeDelta = new Vector2(1110f, 640f);
			Image obj3 = UIFactory.CreateImage((Transform)(object)panelRt, "TitleBar", UIFactory.ColPanel);
			UIFactory.PlaceTopLeft(((Graphic)obj3).rectTransform, 0f, 0f, 1110f, 32f);
			DragHandler dragHandler = ((Component)obj3).gameObject.AddComponent<DragHandler>();
			dragHandler.Target = panelRt;
			dragHandler.OnEndMove = delegate
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				StationFlowPlugin.CfgPanelX.Value = panelRt.anchoredPosition.x;
				StationFlowPlugin.CfgPanelY.Value = 0f - panelRt.anchoredPosition.y;
			};
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText(((Component)obj3).transform, "Title", "StationFlow · 物流塔管理", 15, (TextAnchor)3, UIFactory.ColAccent)).rectTransform, 10f, 4f, 400f, 24f);
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton(((Component)obj3).transform, "Close", "✕", 14, Hide)).transform, 1074f, 4f, 26f, 24f);
			Image val3 = UIFactory.CreateImage((Transform)(object)panelRt, "TabBar", UIFactory.ColPanel);
			UIFactory.PlaceTopLeft(((Graphic)val3).rectTransform, 0f, 32f, 1110f, 28f);
			tab1Btn = UIFactory.CreateButton(((Component)val3).transform, "Tab1", "总览与命名", 13, delegate
			{
				ShowTab(0);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)tab1Btn).transform, 4f, 3f, 116f, 22f);
			tab2Btn = UIFactory.CreateButton(((Component)val3).transform, "Tab2", "规则配对", 13, delegate
			{
				ShowTab(1);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)tab2Btn).transform, 124f, 3f, 100f, 22f);
			tab3Btn = UIFactory.CreateButton(((Component)val3).transform, "Tab3", "物流网", 13, delegate
			{
				ShowTab(2);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)tab3Btn).transform, 228f, 3f, 100f, 22f);
			tab4Btn = UIFactory.CreateButton(((Component)val3).transform, "Tab4", "诊断", 13, delegate
			{
				ShowTab(3);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)tab4Btn).transform, 332f, 3f, 84f, 22f);
			tab5Btn = UIFactory.CreateButton(((Component)val3).transform, "Tab5", "批量工具", 13, delegate
			{
				ShowTab(4);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)tab5Btn).transform, 420f, 3f, 96f, 22f);
			float num = 558f;
			tab1Group = ((Component)UIFactory.CreateNode((Transform)(object)panelRt, "Tab1")).gameObject;
			UIFactory.PlaceTopLeft((RectTransform)tab1Group.transform, 0f, 60f, 1110f, num);
			RectTransform parent = (RectTransform)tab1Group.transform;
			Image val4 = UIFactory.CreateImage((Transform)(object)parent, "Toolbar", UIFactory.ColPanel);
			UIFactory.PlaceTopLeft(((Graphic)val4).rectTransform, 0f, 0f, 1110f, 30f);
			float x = 8f;
			AddToolButton(((Component)val4).transform, "刷新", ref x, 64f, RefreshData);
			AddToolButton(((Component)val4).transform, "全选 (A)", ref x, 84f, delegate
			{
				if (table != null)
				{
					table.SelectAll();
				}
			});
			AddToolButton(((Component)val4).transform, "全不选", ref x, 64f, delegate
			{
				if (table != null)
				{
					table.SelectNone();
				}
			});
			AddToolButton(((Component)val4).transform, "反选", ref x, 56f, delegate
			{
				if (table != null)
				{
					table.InvertSelection();
				}
			});
			AddToolButton(((Component)val4).transform, "Dump CSV", ref x, 84f, delegate
			{
				string text = DumpTool.Dump();
				SetStatus((text != null) ? ("已导出: " + text) : "Dump 失败(游戏未就绪)");
			});
			selectionText = UIFactory.CreateText(((Component)val4).transform, "Sel", "已选 0", 13, (TextAnchor)3, UIFactory.ColText);
			UIFactory.PlaceTopLeft(((Graphic)selectionText).rectTransform, x + 10f, 3f, 200f, 24f);
			float num2 = num - 34f;
			table = new StationTable();
			table.Init((Transform)(object)parent, 810f, num2);
			UIFactory.PlaceTopLeft((RectTransform)table.Root.transform, 8f, 34f, 810f, num2);
			table.SelectionChanged = delegate
			{
				UpdateSelectionLabel();
				UpdateTemplateExample();
				currentPlan = null;
				if ((Object)(object)previewText != (Object)null)
				{
					previewText.text = "";
				}
			};
			Image val5 = UIFactory.CreateImage((Transform)(object)parent, "Side", UIFactory.ColPanel);
			UIFactory.PlaceTopLeft(((Graphic)val5).rectTransform, 826f, 34f, 276f, num2);
			BuildSidePanel(((Graphic)val5).rectTransform);
			tab2Group = ((Component)UIFactory.CreateNode((Transform)(object)panelRt, "Tab2")).gameObject;
			UIFactory.PlaceTopLeft((RectTransform)tab2Group.transform, 0f, 60f, 1110f, num);
			ruleTab = new RuleTab();
			ruleTab.Init((RectTransform)tab2Group.transform, this);
			tab3Group = ((Component)UIFactory.CreateNode((Transform)(object)panelRt, "Tab3")).gameObject;
			UIFactory.PlaceTopLeft((RectTransform)tab3Group.transform, 0f, 60f, 1110f, num);
			topoTab = new TopologyTab();
			topoTab.Init((RectTransform)tab3Group.transform, this);
			tab4Group = ((Component)UIFactory.CreateNode((Transform)(object)panelRt, "Tab4")).gameObject;
			UIFactory.PlaceTopLeft((RectTransform)tab4Group.transform, 0f, 60f, 1110f, num);
			diagTab = new DiagTab();
			diagTab.Init((RectTransform)tab4Group.transform, this);
			tab5Group = ((Component)UIFactory.CreateNode((Transform)(object)panelRt, "Tab5")).gameObject;
			UIFactory.PlaceTopLeft((RectTransform)tab5Group.transform, 0f, 60f, 1110f, num);
			batchTab = new BatchTab();
			batchTab.Init((RectTransform)tab5Group.transform, this);
			Image val6 = UIFactory.CreateImage((Transform)(object)panelRt, "StatusBar", new Color(0.03f, 0.05f, 0.08f, 0.95f));
			UIFactory.PlaceTopLeft(((Graphic)val6).rectTransform, 0f, 618f, 1110f, 22f);
			statusText = UIFactory.CreateText(((Component)val6).transform, "Status", "就绪", 12, (TextAnchor)3, UIFactory.ColTextDim);
			UIFactory.PlaceTopLeft(((Graphic)statusText).rectTransform, 8f, 1f, 1094f, 20f);
			IconHover.OnHoverText = delegate(string msg)
			{
				if (!((Object)(object)statusText == (Object)null))
				{
					statusText.text = (string.IsNullOrEmpty(msg) ? lastStatus : msg);
				}
			};
			built = true;
		}

		private void AddToolButton(Transform parent, string label, ref float x, float w, Action onClick)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton(parent, label, label, 13, onClick)).transform, x, 3f, w, 24f);
			x += w + 6f;
		}

		private void BuildSidePanel(RectTransform side)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00e9: 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)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Expected O, but got Unknown
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Expected O, but got Unknown
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Expected O, but got Unknown
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Expected O, but got Unknown
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Expected O, but got Unknown
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ed: Expected O, but got Unknown
			float num = 8f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "T", "批量命名(选中塔)", 14, (TextAnchor)3, UIFactory.ColAccent)).rectTransform, 8f, num, 260f, 22f);
			num += 26f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "TL", "命名模板:", 12, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 8f, num, 120f, 16f);
			num += 18f;
			templateInput = UIFactory.CreateInputField((Transform)(object)side, "Template", "[{物品}][{角色}]-{序号:2}", 13);
			UIFactory.PlaceTopLeft((RectTransform)((Component)templateInput).transform, 8f, num, 260f, 26f);
			num += 30f;
			((UnityEvent<string>)(object)templateInput.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				UpdateTemplateExample();
			});
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "Vars", "变量:{物品}{角色}{供}{需}{储}{星系}{行星}{星球}{gid}{序号:2}", 11, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 8f, num, 260f, 14f);
			num += 15f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "Vars2", "{供}星际供应 {需}星际需求 {储}星际仓储(只看星际逻辑)", 11, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 8f, num, 260f, 14f);
			num += 17f;
			exampleText = UIFactory.CreateText((Transform)(object)side, "Example", "示例:(未选中塔)", 12, (TextAnchor)3, new Color(0.55f, 0.9f, 0.6f, 1f));
			UIFactory.PlaceTopLeft(((Graphic)exampleText).rectTransform, 8f, num, 260f, 18f);
			num += 22f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "PL", "预设模板(点击填入):", 12, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 8f, num, 200f, 16f);
			num += 18f;
			float num2 = 8f;
			(string, string, string)[] presetTemplates = PresetTemplates;
			for (int num3 = 0; num3 < presetTemplates.Length; num3++)
			{
				(string, string, string) tuple = presetTemplates[num3];
				string tpl = tuple.Item2;
				string desc = tuple.Item3;
				Button obj = UIFactory.CreateButton((Transform)(object)side, "Pre_" + tuple.Item1, tuple.Item1, 12, delegate
				{
					if ((Object)(object)templateInput != (Object)null)
					{
						templateInput.text = tpl;
					}
					UpdateTemplateExample();
					SetStatus("已填入模板:" + tpl + "(" + desc + ")");
				});
				UIFactory.PlaceTopLeft((RectTransform)((Component)obj).transform, num2, num, 62f, 24f);
				((Component)obj).gameObject.AddComponent<IconHover>().Tip = desc + "|" + tpl;
				num2 += 65f;
			}
			num += 28f;
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)side, "AutoTpl", "✨ 自动生成模板(分析选中塔)", 12, DoAutoTemplate)).transform, 8f, num, 260f, 24f);
			num += 30f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)side, "ML", "对已有名字的塔:", 12, (TextAnchor)3, UIFactory.ColTextDim)).rectTransform, 8f, num, 160f, 16f);
			num += 18f;
			skipModeBtn = UIFactory.CreateButton((Transform)(object)side, "Skip", "跳过(推荐)", 12, delegate
			{
				SetNameMode(NamingService.ExistingNameMode.Skip);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)skipModeBtn).transform, 8f, num, 126f, 24f);
			overwriteModeBtn = UIFactory.CreateButton((Transform)(object)side, "Ow", "覆盖", 12, delegate
			{
				SetNameMode(NamingService.ExistingNameMode.Overwrite);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)overwriteModeBtn).transform, 140f, num, 126f, 24f);
			num += 30f;
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)side, "Prev", "生成预览", 13, DoPreview)).transform, 8f, num, 126f, 26f);
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)side, "Apply", "执行改名", 13, DoApplyRename)).transform, 140f, num, 126f, 26f);
			num += 32f;
			previewText = UIFactory.CreateText((Transform)(object)side, "Preview", "", 12, (TextAnchor)0, UIFactory.ColText);
			previewText.horizontalOverflow = (HorizontalWrapMode)0;
			previewText.verticalOverflow = (VerticalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)previewText).rectTransform, 8f, num, 260f, 178f);
			num += 184f;
			UIFactory.PlaceTopLeft((RectTransform)((Component)UIFactory.CreateButton((Transform)(object)side, "Undo", "撤销上次改名", 13, DoUndo)).transform, 8f, num, 258f, 26f);
			SetNameMode(NamingService.ExistingNameMode.Skip);
		}

		private void SetNameMode(NamingService.ExistingNameMode mode)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			nameMode = mode;
			Image val = (((Object)(object)skipModeBtn != (Object)null) ? ((Component)skipModeBtn).GetComponent<Image>() : null);
			Image val2 = (((Object)(object)overwriteModeBtn != (Object)null) ? ((Component)overwriteModeBtn).GetComponent<Image>() : null);
			if ((Object)(object)val != (Object)null)
			{
				((Graphic)val).color = ((mode == NamingService.ExistingNameMode.Skip) ? UIFactory.ColBtnHighlight : UIFactory.ColBtn);
			}
			if ((Object)(object)val2 != (Object)null)
			{
				((Graphic)val2).color = ((mode == NamingService.ExistingNameMode.Overwrite) ? UIFactory.ColBtnHighlight : UIFactory.ColBtn);
			}
		}

		public void RefreshData()
		{
			if (!StationOps.GameReady)
			{
				SetStatus("游戏未就绪(请先进入存档)");
				return;
			}
			allRows = StationSnapshot.Build();
			if (table != null)
			{
				table.SetData(allRows, keepSelection: true);
			}
			UpdateSelectionLabel();
			UpdateTemplateExample();
			SetStatus($"共 {allRows.Count} 个物流塔");
		}

		private void UpdateSelectionLabel()
		{
			if ((Object)(object)selectionText != (Object)null && table != null)
			{
				selectionText.text = $"已选 {table.SelectedCount} / {allRows.Count}";
			}
		}

		public void SetStatus(string msg, bool log = true)
		{
			lastStatus = msg;
			if ((Object)(object)statusText != (Object)null)
			{
				statusText.text = msg;
			}
			if (log)
			{
				StationFlowPlugin.LogInfo("[面板] " + msg);
			}
		}

		private void UpdateTemplateExample()
		{
			if ((Object)(object)exampleText == (Object)null)
			{
				return;
			}
			if (table == null)
			{
				exampleText.text = "示例:(先刷新数据)";
				return;
			}
			List<StationRow> selectedRows = table.SelectedRows;
			if (selectedRows.Count == 0)
			{
				exampleText.text = "示例:(未选中塔,先在左侧表格点选)";
				return;
			}
			string text = (((Object)(object)templateInput != (Object)null) ? templateInput.text : "");
			if (string.IsNullOrEmpty(text) || text.Trim().Length == 0)
			{
				exampleText.text = "示例:(模板为空)";
				return;
			}
			StationRow stationRow = selectedRows[0];
			string text2 = NamingService.Format(text, stationRow, 1).Trim();
			if (text2.Length > 34)
			{
				text2 = text2.Substring(0, 34) + "…";
			}
			exampleText.text = $"示例:#{stationRow.Gid} → {text2}";
		}

		private void DoAutoTemplate()
		{
			if (table == null)
			{
				return;
			}
			List<StationRow> selectedRows = table.SelectedRows;
			if (selectedRows.Count == 0)
			{
				SetStatus("请先在表格中选中一些塔,再点「自动生成模板」");
				return;
			}
			bool flag = false;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			foreach (StationRow item in selectedRows)
			{
				int num5 = 0;
				int num6 = 0;
				int num7 = 0;
				if (item.Slots != null)
				{
					foreach (SlotInfo slot in item.Slots)
					{
						if (slot.RemoteLogic == 1)
						{
							num5++;
						}
						else if (slot.RemoteLogic == 2)
						{
							num6++;
						}
						else
						{
							num7++;
						}
					}
				}
				if (num5 + num6 + num7 > 1)
				{
					flag = true;
				}
				if (num5 > 0 && num6 > 0)
				{
					num3++;
				}
				else if (num5 > 0)
				{
					num++;
				}
				else if (num6 > 0)
				{
					num2++;
				}
				else if (num7 > 0)
				{
					num4++;
				}
			}
			string text;
			string text2;
			if (num + num2 + num3 == 0 && num4 > 0)
			{
				text = "储{储} {星系}{行星}{序号}号塔";
				text2 = "选中的塔均为星际仓储(无星际供需) → 仓储格式";
			}
			else if (!flag)
			{
				text = "[{物品}][{角色}]-{序号:2}";
				text2 = "选中的塔均为单物品 → 经典格式";
			}
			else if (num3 > 0 || (num > 0 && num2 > 0))
			{
				text = "供{供}需{需} {星系}{行星}{序号}号塔";
				text2 = "存在多物品/供需混合塔 → 供需全称格式";
			}
			else if (num > 0)
			{
				text = "供{供} {星系}{行星}{序号}号塔";
				text2 = "多物品纯供应 → 全称格式";
			}
			else
			{
				text = "需{需} {星系}{行星}{序号}号塔";
				text2 = "多物品纯需求 → 全称格式";
			}
			if ((Object)(object)templateInput != (Object)null)
			{
				templateInput.text = text;
			}
			UpdateTemplateExample();
			string text3 = ((num4 > 0 && num + num2 + num3 > 0) ? $";另有 {num4} 个塔含星际仓储物品(可用「储」变量命名)" : "");
			SetStatus("自动模板:" + text2 + ",已填入" + text3);
		}

		private void DoPreview()
		{
			if (table == null)
			{
				return;
			}
			List<StationRow> selectedRows = table.SelectedRows;
			if (selectedRows.Count == 0)
			{
				SetStatus("请先在表格中选中要改名的塔");
				return;
			}
			string text = (((Object)(object)templateInput != (Object)null) ? templateInput.text : "");
			if (string.IsNullOrEmpty(text) || text.Trim().Length == 0)
			{
				SetStatus("请输入命名模板(可点「预设模板」或「自动生成模板」快速填入)");
				return;
			}
			currentPlan = NamingService.Generate(selectedRows, allRows, text, nameMode);
			StringBuilder stringBuilder = new StringBuilder($"预览: 选中 {selectedRows.Count}, 计划 {currentPlan.Count}, 模式={nameMode}");
			for (int i = 0; i < Mathf.Min(3, currentPlan.Count); i++)
			{
				RenamePlanItem renamePlanItem = currentPlan[i];
				int num = 0;
				int num2 = 0;
				if (renamePlanItem.Row.Slots != null)
				{
					foreach (SlotInfo slot in renamePlanItem.Row.Slots)
					{
						if (slot.RemoteLogic == 1)
						{
							num++;
						}
						else if (slot.RemoteLogic == 2)
						{
							num2++;
						}
					}
				}
				stringBuilder.Append($"; #{renamePlanItem.Row.Gid}(供{num}/需{num2}) \"{renamePlanItem.OldName}\"→\"{renamePlanItem.NewName}\"");
			}
			StationFlowPlugin.LogInfo(stringBuilder.ToString());
			if (currentPlan.Count == 0)
			{
				if ((Object)(object)previewText != (Object)null)
				{
					previewText.text = "";
				}
				if (nameMode == NamingService.ExistingNameMode.Skip)
				{
					SetStatus($"选中的 {selectedRows.Count} 个塔都已有名字,当前为「跳过」模式。可切换为「覆盖」,或改选未命名的塔");
				}
				else
				{
					SetStatus("模板生成结果为空,请检查模板内容");
				}
				return;
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			int num3 = Mathf.Min(currentPlan.Count, 10);
			for (int j = 0; j < num3; j++)
			{
				RenamePlanItem renamePlanItem2 = currentPlan[j];
				stringBuilder2.Append("#").Append(renamePlanItem2.Row.Gid).Append("  ")
					.Append(renamePlanItem2.NewName)
					.AppendLine();
			}
			if (currentPlan.Count > num3)
			{
				stringBuilder2.Append("… 共 ").Append(currentPlan.Count).Append(" 条");
			}
			if ((Object)(object)previewText != (Object)null)
			{
				previewText.text = stringBuilder2.ToString();
			}
			int num4 = selectedRows.Count - currentPlan.Count;
			SetStatus($"预览:将改名 {currentPlan.Count} 条" + ((num4 > 0) ? $",跳过 {num4} 个(已有名字)" : ""));
		}

		private void DoApplyRename()
		{
			if (currentPlan == null || currentPlan.Count == 0)
			{
				SetStatus("请先点「生成预览」");
				return;
			}
			int num = 0;
			int num2 = 0;
			lastUndo.Clear();
			foreach (RenamePlanItem item in currentPlan)
			{
				StationComponent station = StationOps.GetStation(item.Row.Gid);
				if (station == null)
				{
					num2++;
				}
				else if (StationOps.SetName(station, item.NewName))
				{
					num++;
					lastUndo.Add(new RenameUndoEntry
					{
						Gid = item.Row.Gid,
						OldName = item.OldName
					});
				}
				else
				{
					num2++;
				}
			}
			currentPlan = null;
			if ((Object)(object)previewText != (Object)null)
			{
				previewText.text = "";
			}
			SetStatus($"改名完成:成功 {num}" + ((num2 > 0) ? $",失败 {num2}" : "") + "(可点「撤销上次改名」回滚)");
			RefreshData();
		}

		private void DoUndo()
		{
			if (lastUndo.Count == 0)
			{
				SetStatus("没有可撤销的改名记录(仅保留最近一次执行)");
				return;
			}
			int num = 0;
			foreach (RenameUndoEntry item in lastUndo)
			{
				StationComponent station = StationOps.GetStation(item.Gid);
				if (station != null && StationOps.SetName(station, item.OldName))
				{
					num++;
				}
			}
			SetStatus($"已撤销 {num} 个改名");
			lastUndo.Clear();
			RefreshData();
		}
	}
	public class ModScrollZone : MonoBehaviour
	{
		private Zone zone;

		private bool registered;

		private void OnEnable()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)this).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (!((Object)(object)val == (Object)null))
			{
				zone = new Zone(val, (Camera)null);
				Register();
			}
		}

		private void OnDisable()
		{
			Unregister();
		}

		private void Register()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (registered)
			{
				return;
			}
			try
			{
				if (UIScrollZone.allScrollTrans == null)
				{
					UIScrollZone.allScrollTrans = new List<Zone>(64);
				}
				UIScrollZone.allScrollTrans.Add(zone);
				registered = true;
			}
			catch (Exception ex)
			{
				StationFlowPlugin.LogWarn("注册滚动区失败: " + ex.Message);
			}
		}

		private void Unregister()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!registered)
			{
				return;
			}
			try
			{
				List<Zone> allScrollTrans = UIScrollZone.allScrollTrans;
				if (allScrollTrans != null)
				{
					for (int i = 0; i < allScrollTrans.Count; i++)
					{
						if ((Object)(object)allScrollTrans[i].trans == (Object)(object)zone.trans)
						{
							allScrollTrans.RemoveAt(i);
							i--;
						}
					}
				}
			}
			catch
			{
			}
			registered = false;
		}
	}
	public class RuleTab
	{
		private const int PreviewLimit = 30;

		private const int WarnShowLimit = 7;

		private RectTransform root;

		private MainPanel owner;

		private PairSource source;

		private PairMethod method;

		private int topK = 3;

		private float minRatio = 0.2f;

		private bool allowCrossAstro = true;

		private static readonly int[] KOptions = new int[5] { 1, 2, 3, 4, 5 };

		private static readonly float[] RatioOptions = new float[4] { 0.1f, 0.2f, 0.3f, 0.5f };

		private const string DescByName = "解析塔名字中的 [物品][角色](严格按星际逻辑),自动为同物品的供需塔建立 P2P:\n需求塔按缺口从大到小、就近挑选供应塔(每塔最多配 K 个)。\n单供应源物品无需配对(官方兜底自动服务);星际仓储/本地逻辑不参与。";

		private const string DescBySlot = "直接读取全部塔的星际供/需槽位(无需命名),自动为同物品的供需塔建立 P2P:\n需求塔按缺口从大到小、就近挑选供应塔(每塔最多配 K 个)。\n未命名塔同样参与;星际仓储/本地逻辑不参与。";

		private Button[] kButtons;

		private Button[] ratioButtons;

		private Button crossBtn;

		private Button sourceNameBtn;

		private Button sourceSlotBtn;

		private Button methodP2PBtn;

		private Button methodPlanetBtn;

		private Button methodAstroBtn;

		private Button generateBtn;

		private Button executeBtn;

		private Button undoBtn;

		private Button autoBtn;

		private Text titleText;

		private Text descText;

		private Text statText;

		private Text progressLabel;

		private Text warnText;

		private Text previewTitle;

		private Text previewText;

		private Text autoStatusText;

		private string lastAutoInfo;

		private Image progressFill;

		private PairingResult lastResult;

		private List<RoutePlanItem> lastRoutes;

		private ExecutionPlan lastPlan;

		public void Init(RectTransform parent, MainPanel owner)
		{
			this.owner = owner;
			topK = Mathf.Clamp(StationFlowPlugin.CfgTopK.Value, 1, 5);
			minRatio = Mathf.Clamp(StationFlowPlugin.CfgMinRatio.Value, 0.1f, 0.5f);
			allowCrossAstro = StationFlowPlugin.CfgCrossAstro.Value;
			source = (PairSource)Mathf.Clamp(StationFlowPlugin.CfgPairSource.Value, 0, 1);
			method = (PairMethod)Mathf.Clamp(StationFlowPlugin.CfgPairMethod.Value, 0, 2);
			root = UIFactory.CreateNode((Transform)(object)parent, "RuleTab");
			UIFactory.Stretch(root);
			BuildUI();
		}

		private void BuildUI()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Expected O, but got Unknown
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Expected O, but got Unknown
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Expected O, but got Unknown
			//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ef: Expected O, but got Unknown
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Expected O, but got Unknown
			//IL_0581: Unknown result type (might be due to invalid IL or missing references)
			//IL_059b: Expected O, but got Unknown
			//IL_05cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Expected O, but got Unknown
			//IL_0605: Unknown result type (might be due to invalid IL or missing references)
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_071c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0766: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0801: Unknown result type (might be due to invalid IL or missing references)
			//IL_0848: Unknown result type (might be due to invalid IL or missing references)
			//IL_089b: Unknown result type (might be due to invalid IL or missing references)
			float num = 8f;
			titleText = UIFactory.CreateText((Transform)(object)root, "Title", "规则配对 · 模式一(命名驱动)", 15, (TextAnchor)3, UIFactory.ColAccent);
			UIFactory.PlaceTopLeft(((Graphic)titleText).rectTransform, 12f, num, 400f, 24f);
			autoBtn = UIFactory.CreateButton((Transform)(object)root, "Auto", "自动巡检:关", 12, ToggleAuto);
			UIFactory.PlaceTopLeft((RectTransform)((Component)autoBtn).transform, 560f, 8f, 130f, 24f);
			autoStatusText = UIFactory.CreateText((Transform)(object)root, "AutoSt", "", 11, (TextAnchor)3, UIFactory.ColTextDim);
			UIFactory.PlaceTopLeft(((Graphic)autoStatusText).rectTransform, 698f, 8f, 400f, 24f);
			num += 28f;
			sourceNameBtn = UIFactory.CreateButton((Transform)(object)root, "SrcName", "按名字(模式一)", 12, delegate
			{
				SetSource(PairSource.ByName);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)sourceNameBtn).transform, 12f, num, 160f, 24f);
			sourceSlotBtn = UIFactory.CreateButton((Transform)(object)root, "SrcSlot", "按槽位(模式二)", 12, delegate
			{
				SetSource(PairSource.BySlot);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)sourceSlotBtn).transform, 176f, num, 160f, 24f);
			num += 30f;
			methodP2PBtn = UIFactory.CreateButton((Transform)(object)root, "MtdP2P", "点对点", 12, delegate
			{
				SetMethod(PairMethod.P2P);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)methodP2PBtn).transform, 12f, num, 106f, 24f);
			methodPlanetBtn = UIFactory.CreateButton((Transform)(object)root, "MtdPl", "星球航路", 12, delegate
			{
				SetMethod(PairMethod.PlanetRoute);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)methodPlanetBtn).transform, 122f, num, 106f, 24f);
			methodAstroBtn = UIFactory.CreateButton((Transform)(object)root, "MtdAs", "行星系航路", 12, delegate
			{
				SetMethod(PairMethod.AstroRoute);
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)methodAstroBtn).transform, 232f, num, 106f, 24f);
			num += 30f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "KL", "每塔最多配几个供应塔(TopK):", 12, (TextAnchor)3, UIFactory.ColText)).rectTransform, 12f, num, 320f, 16f);
			num += 20f;
			kButtons = (Button[])(object)new Button[KOptions.Length];
			float num2 = 12f;
			for (int num3 = 0; num3 < KOptions.Length; num3++)
			{
				int k = KOptions[num3];
				kButtons[num3] = UIFactory.CreateButton((Transform)(object)root, "K" + k, k.ToString(), 12, delegate
				{
					topK = k;
					UpdateParamButtons();
					StationFlowPlugin.CfgTopK.Value = k;
				});
				UIFactory.PlaceTopLeft((RectTransform)((Component)kButtons[num3]).transform, num2, num, 44f, 24f);
				num2 += 48f;
			}
			num += 32f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "RL", "最小供应能力(占需求容量的比例):", 12, (TextAnchor)3, UIFactory.ColText)).rectTransform, 12f, num, 320f, 16f);
			num += 20f;
			ratioButtons = (Button[])(object)new Button[RatioOptions.Length];
			num2 = 12f;
			for (int num4 = 0; num4 < RatioOptions.Length; num4++)
			{
				float r = RatioOptions[num4];
				ratioButtons[num4] = UIFactory.CreateButton((Transform)(object)root, "R" + num4, (r * 100f).ToString("F0") + "%", 12, delegate
				{
					minRatio = r;
					UpdateParamButtons();
					StationFlowPlugin.CfgMinRatio.Value = r;
				});
				UIFactory.PlaceTopLeft((RectTransform)((Component)ratioButtons[num4]).transform, num2, num, 56f, 24f);
				num2 += 60f;
			}
			num += 32f;
			crossBtn = UIFactory.CreateButton((Transform)(object)root, "Cross", "", 12, delegate
			{
				allowCrossAstro = !allowCrossAstro;
				UpdateParamButtons();
				StationFlowPlugin.CfgCrossAstro.Value = allowCrossAstro;
			});
			UIFactory.PlaceTopLeft((RectTransform)((Component)crossBtn).transform, 12f, num, 200f, 24f);
			num += 34f;
			generateBtn = UIFactory.CreateButton((Transform)(object)root, "Gen", "生成配对计划", 13, DoGenerate);
			UIFactory.PlaceTopLeft((RectTransform)((Component)generateBtn).transform, 12f, num, 200f, 30f);
			num += 38f;
			executeBtn = UIFactory.CreateButton((Transform)(object)root, "Exec", "执行计划", 13, DoExecute);
			UIFactory.PlaceTopLeft((RectTransform)((Component)executeBtn).transform, 12f, num, 96f, 30f);
			undoBtn = UIFactory.CreateButton((Transform)(object)root, "Undo", "撤销执行", 13, DoUndo);
			UIFactory.PlaceTopLeft((RectTransform)((Component)undoBtn).transform, 116f, num, 96f, 30f);
			num += 38f;
			statText = UIFactory.CreateText((Transform)(object)root, "Stat", "尚未生成计划。", 12, (TextAnchor)0, UIFactory.ColText);
			statText.horizontalOverflow = (HorizontalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)statText).rectTransform, 12f, num, 336f, 32f);
			num += 36f;
			Image val = UIFactory.CreateImage((Transform)(object)root, "ProgressBar", new Color(0.1f, 0.15f, 0.2f, 0.9f));
			UIFactory.PlaceTopLeft(((Graphic)val).rectTransform, 12f, num, 336f, 12f);
			num += 16f;
			progressFill = UIFactory.CreateImage(((Component)val).transform, "Fill", UIFactory.ColAccent);
			UIFactory.Stretch(((Graphic)progressFill).rectTransform);
			progressFill.type = (Type)3;
			progressFill.fillMethod = (FillMethod)0;
			progressFill.fillAmount = 0f;
			((Graphic)progressFill).raycastTarget = false;
			progressLabel = UIFactory.CreateText((Transform)(object)root, "Progress", "", 12, (TextAnchor)3, UIFactory.ColTextDim);
			UIFactory.PlaceTopLeft(((Graphic)progressLabel).rectTransform, 12f, num, 336f, 16f);
			num += 22f;
			UIFactory.PlaceTopLeft(((Graphic)UIFactory.CreateText((Transform)(object)root, "WL", "不一致警告(名字与槽位不匹配):", 12, (TextAnchor)3, UIFactory.ColWarn)).rectTransform, 12f, num, 336f, 16f);
			num += 18f;
			warnText = UIFactory.CreateText((Transform)(object)root, "Warn", "(生成计划后显示)", 11, (TextAnchor)0, UIFactory.ColTextDim);
			warnText.horizontalOverflow = (HorizontalWrapMode)0;
			warnText.verticalOverflow = (VerticalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)warnText).rectTransform, 12f, num, 336f, 116f);
			previewTitle = UIFactory.CreateText((Transform)(object)root, "PT", "配对预览", 14, (TextAnchor)3, UIFactory.ColAccent);
			UIFactory.PlaceTopLeft(((Graphic)previewTitle).rectTransform, 360f, 8f, 736f, 22f);
			descText = UIFactory.CreateText((Transform)(object)root, "Desc", "解析塔名字中的 [物品][角色](严格按星际逻辑),自动为同物品的供需塔建立 P2P:\n需求塔按缺口从大到小、就近挑选供应塔(每塔最多配 K 个)。\n单供应源物品无需配对(官方兜底自动服务);星际仓储/本地逻辑不参与。", 12, (TextAnchor)0, UIFactory.ColTextDim);
			descText.horizontalOverflow = (HorizontalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)descText).rectTransform, 360f, 34f, 740f, 54f);
			previewText = UIFactory.CreateText((Transform)(object)root, "Preview", "点「生成配对计划」查看结果。\n\n符号说明:+ = 将新建   ✓ = 已存在", 12, (TextAnchor)0, UIFactory.ColText);
			previewText.horizontalOverflow = (HorizontalWrapMode)1;
			previewText.verticalOverflow = (VerticalWrapMode)0;
			UIFactory.PlaceTopLeft(((Graphic)previewText).rectTransform, 360f, 92f, 740f, 462f);
			UpdateParamButtons();
			SetSource(source, silent: true);
			SetMethod(method, silent: true);
		}

		private void SetMethod(PairMethod m)
		{
			SetMethod(m, silent: false);
		}

		private void SetMethod(PairMethod m, bool silent)
		{
			method = m;
			if (!silent)
			{
				StationFlowPlugin.CfgPairMethod.Value = (int)m;
			}
			SetBtnColor(methodP2PBtn, m == PairMethod.P2P);
			SetBtnColor(methodPlanetBtn, m == PairMethod.PlanetRoute);
			SetBtnColor(methodAstroBtn, m == PairMethod.AstroRoute);
			if (!silent && (Object)(object)owner != (Object)null)
			{
				switch (m)
				{
				case PairMethod.P2P:
					owner.SetStatus("配对方式:点对点(塔↔塔,精确到每一对塔)");
					break;
				case PairMethod.PlanetRoute:
					owner.SetStatus("配对方式:星球航路(星球对+物品聚合;1 条航路覆盖两地间该物品的全部塔对)");
					break;
				default:
					owner.SetStatus("配对方式:行星系航路(行星系对+物品聚合,粗粒度覆盖整片区域)");
					break;
				}
			}
		}

		private void SetSource(PairSource s)
		{
			SetSource(s, silent: false);
		}

		private void SetSource(PairSource s, bool silent)
		{
			source = s;
			if (!silent)
			{
				StationFlowPlugin.CfgPairSource.Value = (int)s;
			}
			SetBtnColor(sourceNameBtn, s == PairSource.ByName);
			SetBtnColor(sourceSlotBtn, s == PairSource.BySlot);
			if ((Object)(object)descText != (Object)null)
			{
				descText.text = ((s == PairSource.ByName) ? "解析塔名字中的 [物品][角色](严格按星际逻辑),自动为同物品的供需塔建立 P2P:\n需求塔按缺口从大到小、就近挑选供应塔(每塔最多配 K 个)。\n单供应源物品无需配对(官方兜底自动服务);星际仓储/本地逻辑不参与。" : "直接读取全部塔的星际供/需槽位(无需命名),自动为同物品的供需塔建立 P2P:\n需求塔按缺口从大到小、就近挑选供应塔(每塔最多配 K 个)。\n未命名塔同样参与;星际仓储/本地逻辑不参与。");
			}
			if ((Object)(object)titleText != (Object)null)
			{
				titleText.text = ((s == PairSource.ByName) ? "规则配对 · 模式一(命名驱动)" : "规则配对 · 模式二(槽位直读)");
			}
			if (!silent && (Object)(object)owner != (Object)null)
			{
				owner.SetStatus((s == PairSource.ByName) ? "数据源:按名字(解析 [物品][角色],需规范命名)" : "数据源:按槽位(直读星际供需,无需命名,未命名塔同样参与)");
			}
		}

		private void UpdateParamButtons()
		{
			if (kButtons != null)
			{
				for (int i = 0; i < kButtons.Length; i++)
				{
					SetBtnColor(kButtons[i], KOptions[i] == topK);
				}
			}
			if (ratioButtons != null)
			{
				for (int j = 0; j < ratioButtons.Length; j++)
				{
					SetBtnColor(ratioButtons[j], Mathf.Approximately(RatioOptions[j], minRatio));
				}
			}
			if ((Object)(object)crossBtn != (Object)null)
			{
				Text componentInChildren = ((Component)crossBtn).GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = (allowCrossAstro ? "允许跨星系:是" : "允许跨星系:否");
				}
				SetBtnColor(crossBtn, allowCrossAstro);
			}
		}

		private static void SetBtnColor(Button b, bool active)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Image val = (((Object)(object)b != (Object)null) ? ((Component)b).GetComponent<Image>() : null);
			if ((Object)(object)val != (Object)null)
			{
				((Graphic)val).color = (active ? UIFactory.ColBtnHighlight : UIFactory.ColBtn);
			}
		}

		public void Refresh()
		{
			if ((Object)(object)statText != (Object)null && lastResult == null)
			{
				statText.text = (((Object)(object)owner != (Object)null && owner.AllRows.Count > 0) ? $"当前星区共 {owner.AllRows.Count} 个塔。设置参数后点「生成配对计划」。" : "尚未生成计划。设置参数后点「生成配对计划」。");
			}
			UpdateAutoUI();
		}

		public void Update()
		{
			Executor instance = Executor.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (instance.IsRunning)
			{
				float progress = instance.Progress;
				if ((Object)(object)progressFill != (Object)null)
				{
					progressFill.fillAmount = progress;
				}
				if ((Object)(object)progressLabel != (Object)null)
				{
					progressLabel.text = $"{instance.Stage} {progress * 100f:F0}%";
				}
			}
			if ((Object)(object)generateBtn != (Object)null)
			{
				((Selectable)generateBtn).interactable = !instance.IsRunning;
			}
			if ((Object)(object)executeBtn != (Object)null)
			{
				((Selectable)executeBtn).interactable = !instance.IsRunning && lastPlan != null && lastPlan.Actions.Count > 0;
			}
			if ((Object)(object)undoBtn != (Object)null)
			{
				((Selectable)undoBtn).interactable = !instance.IsRunning && instance.UndoAvailable;
			}
			AutoPilot instance2 = AutoPilot.Instance;
			if ((Object)(object)instance2 != (Object)null && (Object)(object)autoStatusText != (Object)null && instance2.LastRunInfo != lastAutoInfo)
			{
				lastAutoInfo = instance2.LastRunInfo;
				autoStatusText.text = instance2.LastRunInfo;
			}
		}

		private void ToggleAuto()
		{
			AutoPilot instance = AutoPilot.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				owner.SetStatus("AutoPilot 未初始化");
				return;
			}
			instance.Enabled = !instance.Enabled;
			StationFlowPlugin.CfgAutoEnabled.Value = instance.Enabled;
			UpdateAutoUI();
			owner.SetStatus(instance.Enabled ? $"自动巡检已开启:每 30 秒检查新建塔,变化时自动增量配对(每次最多 {StationFlowPlugin.CfgAutoMaxActions.Value} 条)" : "自动巡检已关闭");
		}

		private void UpdateAutoUI()
		{
			AutoPilot instance = AutoPilot.Instance;
			bool flag = (Object)(object)instance != (Object)null && instance.Enabled;
			if ((Object)(object)autoBtn != (Object)null)
			{
				Text componentInChildren = ((Component)autoBtn).GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = (flag ? "自动巡检:开" : "自动巡检:关");
				}
				SetBtnColor(autoBtn, flag);
			}
			if ((Object)(object)autoStatusText != (Object)null && ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(instance.LastRunInfo)))
			{
				autoStatusText.text = (flag ? "等待检测新增塔…" : "");
			}
		}

		private void DoGenerate()
		{
			if (!StationOps.GameReady)
			{
				owner.SetStatus("游戏未就绪(请先进入存档)");
				return;
			}
			Executor instance = Executor.Instance;
			if ((Object)(object)instance != (Object)null && instance.IsRunning)
			{
				owner.SetStatus("有执行任务进行中,请稍候…");
				return;
			}
			List<StationRow> rows = StationSnapshot.Build();
			PairRule rule = new PairRule
			{
				Source = source,
				TopK = topK,
				MinSupplyRatio = minRatio,
				AllowCrossAstro = allowCrossAstro
			};
			Stopwatch stopwatch = Stopwatch.StartNew();
			lastResult = PairingEngine.Build(rows, rule);
			lastPlan = PlanBuilder.FromPairs(lastResult, source, method, 0, $"配对(K={topK})", out lastRoutes);
			stopwatch.Stop();
			RenderPreview();
			if (method == PairMethod.P2P)
			{
				statText.text = $"计划完成({stopwatch.ElapsedMilliseconds}ms):共 {lastResult.Pairs.Count} 条," + $"新增 {lastResult.NewCount}、已存在 {lastResult.ExistsCount}、" + $"提示 {lastResult.Notices.Count}、警告 {lastResult.Warnings.Count}";
				owner.SetStatus($"配对计划:新增 {lastResult.NewCount} 条,已存在 {lastResult.ExistsCount} 条({stopwatch.ElapsedMilliseconds}ms)");
			}
			else
			{
				int num = 0;
				int num2 = 0;
				int num3 = 0;
				foreach (RoutePlanItem lastRoute in lastRoutes)
				{
					num += lastRoute.PairCount;
					num2 += lastRoute.ItemIds.Count - lastRoute.ExistItemCount;
					if (lastRoute.PlanetLevel && method == PairMethod.AstroRoute)
					{
						num3++;
					}
				}
				string text = ((num3 > 0) ? $"(其中 {num3} 条为同恒星系内塔对,行星系航路无法表达、已降级为星球航路)" : "");
				statText.text = $"计划完成({stopwatch.ElapsedMilliseconds}ms):{lastRoutes.Count} 条航路键(新增 {num2}){text}、" + $"覆盖 {num} 对塔通信、提示 {lastResult.Notices.Count}、警告 {lastResult.Warnings.Count}";
				owner.SetStatus(string.Format("航路计划:{0} 条(新增键 {1}){2},覆盖 {3} 对塔通信({4}ms)", lastRoutes.Count, num2, (text.Length > 0) ? ",含同系降级" : "", num, stopwatch.ElapsedMilliseconds));
			}
			foreach (string notice in lastResult.Notices)
			{
				StationFlowPlugin.LogInfo("[配对提示] " + notice);
			}
			foreach (string warning in lastResult.Warnings)
			{
				StationFlowPlugin.LogInfo("[配对警告] " + warning);
			}
		}

		private void RenderPreview()
		{
			PairingResult pairingResult = lastResult;
			if (method != PairMethod.P2P && lastRoutes != null)
			{
				int num = 0;
				foreach (RoutePlanItem lastRoute in lastRoutes)
				{
					num += lastRoute.PairCount;
				}
				previewTitle.text = string.Format("航路预览({0}航路 {1} 条,覆盖 {2} 对塔通信;按覆盖数降序)", (method == PairMethod.AstroRoute) ? "行星系" : "星球", lastRoutes.Count, num);
				StringBuilder stringBuilder = new StringBuilder();
				int num2 = Mathf.Min(lastRoutes.Count, 30);
				for (int i = 0; i < num2; i++)
				{
					RoutePlanItem routePlanItem = lastRoutes[i];
					stringBuilder.Append(routePlanItem.AllExist ? "✓" : "+").Append(' ').Append(Short(routePlanItem.AName))
						.Append(" ↔ ")
						.Append(Short(routePlanItem.BName))
						.Append("  ")
						.Append(Trunc(routePlanItem.ItemText, 24))
						.Append("  (")
						.Append(routePlanItem.PairCount)
						.Append("对塔)")
						.Append('\n');
				}
				if (lastRoutes.Count == 0)
				{
					stringBuilder.Append("没有可聚合的配对。\n\n航路由塔对计划聚合而来——先用「点对点」模式生成计划,\n若有塔对可配,本模式即可把它们精简为星球/行星系级航路。");
				}
				previewText.text = stringBuilder.ToString();
			}
			else
			{
				previewTitle.text = $"配对预览(共 {pairingResult.Pairs.Count} 条,新增 {pairingResult.NewCount};按距离升序" + ((pairingResult.Pairs.Count > 30) ? $",显示前 {30} 条)" : ")");
				StringBuilder stringBuilder2 = new StringBuilder();
				int num3 = Mathf.Min(pairingResult.Pairs.Count, 30);
				for (int j = 0; j < num3; j++)
				{
					PairPlanItem pairPlanItem = pairingResult.Pairs[j];
					stringBuilder2.Append(pairPlanItem.Exists ? "✓" : "+").Append(' ').Append('#')
						.Append(pairPlanItem.SupplyGid)
						.Append('[')
						.Append(Short(pairPlanItem.SupplyPlanet))
						.Append(']')
						.Append(" → ")
						.Append('#')
						.Append(pairPlanItem.DemandGid)
						.Append('[')
						.Append(Short(pairPlanItem.DemandPlanet))
						.Append(']')
						.Append("  ")
						.Append(Trunc(pairPlanItem.ItemText, 20))
						.Append("  d=")
						.Append(FmtDist(pairPlanItem.Distance))
						.Append('\n');
				}
				if (pairingResult.Pairs.Count == 0)
				{
					stringBuilder2.Append("没有找到可配对的组合。\n\n检查左下提示/警告,或切换数据源(模式一需规范命名;模式二直读槽位)。");
				}
				previewText.text = stringBuilder2.ToString();
			}
			RenderNotices(pairingResult);
		}

		private void RenderNotices(PairingResult r)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int num = 7;
			foreach (string notice in r.Notices)
			{
				if (num-- <= 0)
				{
					break;
				}
				stringBuilder.Append("◆ ").Append(notice).Append('\n');
			}
			int num2 = Mathf.Min(r.Warnings.Count, Mathf.Max(0, num));
			for (int i = 0; i < num2; i++)
			{
				stringBuilder.Append("· ").Append(r.Warnings[i]).Append('\n');
			}
			int num3 = r.Notices.Count + r.Warnings.Count;
			if (num3 > 7)
			{
				stringBuilder.Append("……共 ").Append(num3).Append(" 条(详见