Decompiled source of StationFlow v0.2.0
plugins\StationFlow.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
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(" 条(详见