Decompiled source of DspBlueprintEdit v0.1.0

plugins/DspBlueprintEdit/DspBlueprintEdit.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace DspBlueprintEdit;

internal static class BeltAnalyzer
{
	internal class Chain
	{
		public List<BlueprintBuilding> Belts = new List<BlueprintBuilding>();

		public bool HeadConnected;

		public bool TailConnected;

		public bool IsStraight;

		public int Length => Belts.Count;

		public bool FullyDangling => !HeadConnected && !TailConnected;

		public bool HalfDangling => HeadConnected ^ TailConnected;
	}

	internal const int BeltMin = 2001;

	internal const int BeltMax = 2003;

	internal static bool IsBelt(BlueprintBuilding b)
	{
		return b != null && b.itemId >= 2001 && b.itemId <= 2003;
	}

	internal static List<Chain> BuildChains(BlueprintData data)
	{
		List<Chain> list = new List<Chain>();
		if (data?.buildings == null)
		{
			return list;
		}
		HashSet<BlueprintBuilding> hashSet = new HashSet<BlueprintBuilding>();
		BlueprintBuilding[] buildings = data.buildings;
		foreach (BlueprintBuilding val in buildings)
		{
			if (val == null || !IsBelt(val) || hashSet.Contains(val))
			{
				continue;
			}
			BlueprintBuilding val2 = val;
			while (val2.inputObj != null && IsBelt(val2.inputObj) && !hashSet.Contains(val2.inputObj))
			{
				val2 = val2.inputObj;
				if (val2 == val)
				{
					break;
				}
			}
			Chain chain = new Chain();
			BlueprintBuilding val3 = val2;
			float yaw = val2.yaw;
			bool isStraight = true;
			while (val3 != null && IsBelt(val3) && !chain.Belts.Contains(val3))
			{
				chain.Belts.Add(val3);
				hashSet.Add(val3);
				if (Mathf.Abs(Mathf.DeltaAngle(val3.yaw, yaw)) > 2f)
				{
					isStraight = false;
				}
				val3 = val3.outputObj;
			}
			chain.IsStraight = isStraight;
			BlueprintBuilding val4 = chain.Belts[0];
			BlueprintBuilding val5 = chain.Belts[chain.Belts.Count - 1];
			chain.HeadConnected = val4.inputObj != null && !IsBelt(val4.inputObj);
			chain.TailConnected = val5.outputObj != null && !IsBelt(val5.outputObj);
			list.Add(chain);
		}
		return list;
	}

	internal static string StatsText(BlueprintData data, List<Chain> chains)
	{
		int num = data.buildings.Count(IsBelt);
		Chain chain = ((chains.Count > 0) ? chains.OrderByDescending((Chain c) => c.Length).First() : null);
		int num2 = chains.Count((Chain c) => c.FullyDangling);
		int num3 = chains.Count((Chain c) => c.HalfDangling);
		string text = $"传送带总数: {num}  链数: {chains.Count}\n";
		text += $"两端悬空链: {num2}  单端悬空链: {num3}\n";
		if (chain != null)
		{
			BlueprintBuilding val = chain.Belts[0];
			BlueprintBuilding val2 = chain.Belts[chain.Length - 1];
			text = text + $"最长链距离: {chain.Length} 格带  " + $"(起点 {val.localOffset_x:F1},{val.localOffset_z:F1} → 终点 {val2.localOffset_x:F1},{val2.localOffset_z:F1})" + (chain.IsStraight ? " [直线]" : " [弯折]");
		}
		return text;
	}

	private static void RemoveBuilding(BlueprintBuilding b, BlueprintData data)
	{
		BlueprintBuilding[] buildings = data.buildings;
		foreach (BlueprintBuilding val in buildings)
		{
			if (val != null && val != b)
			{
				if (val.outputObj == b)
				{
					val.outputObj = null;
				}
				if (val.inputObj == b)
				{
					val.inputObj = null;
				}
			}
		}
		b.outputObj = null;
		b.inputObj = null;
		b.index = -1;
	}

	internal static int RemoveDanglingChains(BlueprintData data, List<Chain> chains)
	{
		int num = 0;
		foreach (Chain chain in chains)
		{
			if (!chain.FullyDangling)
			{
				continue;
			}
			foreach (BlueprintBuilding belt in chain.Belts)
			{
				RemoveBuilding(belt, data);
				num++;
			}
		}
		return num;
	}

	internal static int TrimHalfDanglingChains(BlueprintData data, List<Chain> chains, int keepCells)
	{
		if (keepCells < 1)
		{
			keepCells = 1;
		}
		int num = 0;
		foreach (Chain chain in chains)
		{
			if (!chain.HalfDangling || chain.Length <= keepCells)
			{
				continue;
			}
			List<BlueprintBuilding> list = ((!chain.TailConnected) ? chain.Belts.Skip(keepCells).ToList() : chain.Belts.Take(chain.Length - keepCells).ToList());
			foreach (BlueprintBuilding item in list)
			{
				RemoveBuilding(item, data);
				num++;
			}
		}
		return num;
	}
}
internal static class BpeEditor
{
	internal static UIBlueprintInspector TargetInspector;

	private static bool _open;

	private static Rect _winRect = new Rect(240f, 120f, 760f, 640f);

	private static Vector2 _listScroll;

	private static string _status = "";

	private static string _undoBase64;

	private static string _filterText = "";

	private static int _keepCells = 1;

	private static bool _showBeltsOnly;

	private static BlueprintData Data => ((Object)(object)TargetInspector != (Object)null) ? TargetInspector.blueprint : null;

	internal static void Toggle()
	{
		if (_open)
		{
			Close();
		}
		else if ((Object)(object)TargetInspector != (Object)null && TargetInspector.blueprint != null)
		{
			Open(TargetInspector);
		}
	}

	internal static void Open(UIBlueprintInspector inspector)
	{
		TargetInspector = inspector;
		if (inspector?.blueprint == null || !inspector.blueprint.isValid)
		{
			_status = "当前没有可编辑的蓝图";
			return;
		}
		_undoBase64 = inspector.blueprint.ToBase64String();
		_open = true;
		_status = "已载入蓝图(原始内容已备份,可一键撤销)";
	}

	internal static void Close()
	{
		_open = false;
	}

	internal static void DrawGUI()
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		if (_open)
		{
			if (Data == null || !Data.isValid)
			{
				Close();
			}
			else
			{
				_winRect = GUILayout.Window(9527, _winRect, new WindowFunction(WindowFn), "蓝图编辑器 DspBlueprintEdit", Array.Empty<GUILayoutOption>());
			}
		}
	}

	private static void WindowFn(int id)
	{
		//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0302: Unknown result type (might be due to invalid IL or missing references)
		//IL_0307: Unknown result type (might be due to invalid IL or missing references)
		//IL_044e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0455: Expected O, but got Unknown
		//IL_0462: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f6: Expected O, but got Unknown
		//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0503: Invalid comparison between Unknown and I4
		//IL_0588: Unknown result type (might be due to invalid IL or missing references)
		BlueprintData data = Data;
		if (data == null || data.buildings == null)
		{
			Close();
			return;
		}
		GUILayout.Label($"名称: {data.shortDesc}    作者: {data.author}    建筑数: {data.buildings.Length}", Array.Empty<GUILayoutOption>());
		GUILayout.Label("简介: " + (string.IsNullOrEmpty(data.desc) ? "-" : data.desc), Array.Empty<GUILayoutOption>());
		GUILayout.Space(6f);
		GUILayout.Label("—— 传送带分析(自动判定最长距离 / 省带子)——", GUI.skin.box, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
		List<BeltAnalyzer.Chain> chains;
		try
		{
			chains = BeltAnalyzer.BuildChains(data);
		}
		catch (Exception ex)
		{
			chains = new List<BeltAnalyzer.Chain>();
			GUILayout.Label("链分析失败: " + ex.Message, Array.Empty<GUILayoutOption>());
		}
		GUILayout.Label(BeltAnalyzer.StatsText(data, chains), Array.Empty<GUILayoutOption>());
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("删除两端悬空的整条带链", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }))
		{
			int num = BeltAnalyzer.RemoveDanglingChains(data, chains);
			ApplyChanges();
			_status = $"已删除 {num} 条悬空传送带";
		}
		GUILayout.Label("单端悬空链保留格数:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
		string s = GUILayout.TextField(_keepCells.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) });
		int.TryParse(s, out _keepCells);
		if (_keepCells < 1)
		{
			_keepCells = 1;
		}
		if (GUILayout.Button("截短单端悬空带链(省带子)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }))
		{
			int num2 = BeltAnalyzer.TrimHalfDanglingChains(data, chains, _keepCells);
			ApplyChanges();
			_status = $"已截短删除 {num2} 条多余传送带";
		}
		GUILayout.EndHorizontal();
		GUILayout.Space(6f);
		GUILayout.Label("—— 建筑列表 ——", GUI.skin.box, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		GUILayout.Label("过滤:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) });
		_filterText = GUILayout.TextField(_filterText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
		_showBeltsOnly = GUILayout.Toggle(_showBeltsOnly, "只看传送带", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
		if (GUILayout.Button("清除", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }))
		{
			_filterText = "";
			_showBeltsOnly = false;
		}
		GUILayout.EndHorizontal();
		_listScroll = GUILayout.BeginScrollView(_listScroll, GUI.skin.box, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(260f) });
		List<KeyValuePair<string, int>> list = ItemSummary(data);
		foreach (KeyValuePair<string, int> item in list)
		{
			if (string.IsNullOrEmpty(_filterText) || item.Key.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				GUILayout.Label($"  {item.Key}  ×{item.Value}", Array.Empty<GUILayoutOption>());
			}
		}
		GUILayout.EndScrollView();
		GUILayout.Space(6f);
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("应用修改到蓝图", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }))
		{
			ApplyChanges();
			_status = "修改已应用,记得在蓝图界面点保存/使用";
		}
		if (GUILayout.Button("导出到剪贴板", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
		{
			GUIUtility.systemCopyBuffer = data.ToBase64String();
			_status = "蓝图 Base64 已复制到剪贴板";
		}
		if (GUILayout.Button("从剪贴板导入", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
		{
			string systemCopyBuffer = GUIUtility.systemCopyBuffer;
			BlueprintData val = new BlueprintData();
			if (!string.IsNullOrEmpty(systemCopyBuffer) && (int)val.FromBase64String(systemCopyBuffer) == 0 && val.isValid)
			{
				val.time = data.time;
				TargetInspector.blueprint = val;
				_undoBase64 = data.ToBase64String();
				RefreshInspector();
				_status = "已从剪贴板导入蓝图";
			}
			else
			{
				_status = "剪贴板内容不是有效蓝图";
			}
		}
		if (GUILayout.Button("撤销全部修改", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }) && _undoBase64 != null)
		{
			BlueprintData val2 = new BlueprintData();
			if ((int)val2.FromBase64String(_undoBase64) == 0)
			{
				TargetInspector.blueprint = val2;
				RefreshInspector();
				_status = "已恢复原始蓝图";
			}
		}
		if (GUILayout.Button("关闭", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
		{
			Close();
		}
		GUILayout.EndHorizontal();
		GUILayout.Space(4f);
		GUILayout.Label(_status, Array.Empty<GUILayoutOption>());
		GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f));
	}

	private static List<KeyValuePair<string, int>> ItemSummary(BlueprintData data)
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		BlueprintBuilding[] buildings = data.buildings;
		foreach (BlueprintBuilding val in buildings)
		{
			if (val != null && (!_showBeltsOnly || BeltAnalyzer.IsBelt(val)))
			{
				string key = ItemName(val.itemId);
				dictionary.TryGetValue(key, out var value);
				dictionary[key] = value + 1;
			}
		}
		return dictionary.OrderByDescending((KeyValuePair<string, int> kv) => kv.Value).ToList();
	}

	private static string ItemName(int itemId)
	{
		ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
		if (val == null)
		{
			return $"物品#{itemId}";
		}
		string text = ((Proto)val).name;
		try
		{
			text = Localization.Translate(text);
		}
		catch
		{
		}
		return string.IsNullOrEmpty(text) ? $"物品#{itemId}" : text;
	}

	private static void ApplyChanges()
	{
		BlueprintData data = Data;
		if (data != null)
		{
			try
			{
				data.CheckBuildingData();
			}
			catch (Exception ex)
			{
				Plugin.L.LogWarning((object)("CheckBuildingData: " + ex.Message));
			}
			RefreshInspector();
		}
	}

	private static void RefreshInspector()
	{
		UIBlueprintInspector targetInspector = TargetInspector;
		if ((Object)(object)targetInspector == (Object)null)
		{
			return;
		}
		try
		{
			typeof(UIBlueprintInspector).GetMethod("Refresh", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
			{
				typeof(bool),
				typeof(bool),
				typeof(bool)
			}, null)?.Invoke(targetInspector, new object[3] { false, true, true });
		}
		catch (Exception ex)
		{
			Plugin.L.LogWarning((object)("刷新蓝图界面失败: " + ex.Message));
		}
	}
}
[BepInPlugin("byboy.dspblueprintedit", "DspBlueprintEdit", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
	internal static ManualLogSource L;

	public static Plugin Instance;

	private void Awake()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		L = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		new Harmony("byboy.dspblueprintedit").PatchAll(Assembly.GetExecutingAssembly());
		L.LogInfo((object)"DspBlueprintEdit 已加载:蓝图界面点「修改蓝图」或按 F10 打开编辑器");
	}

	private void Update()
	{
		if (Input.GetKeyDown((KeyCode)291) && (Object)(object)BpeEditor.TargetInspector != (Object)null && ((ManualBehaviour)BpeEditor.TargetInspector).active)
		{
			BpeEditor.Toggle();
		}
	}

	private void OnGUI()
	{
		BpeEditor.DrawGUI();
	}
}
[HarmonyPatch(typeof(UIBlueprintInspector), "_OnOpen")]
internal static class InspectorOpenPatch
{
	private static void Postfix(UIBlueprintInspector __instance)
	{
		try
		{
			AddEditButton(__instance);
			BpeEditor.TargetInspector = __instance;
		}
		catch (Exception ex)
		{
			Plugin.L.LogError((object)("注入修改蓝图按钮失败: " + ex));
		}
	}

	internal static void AddEditButton(UIBlueprintInspector inspector)
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Expected O, but got Unknown
		if ((Object)(object)inspector == (Object)null || (Object)(object)inspector.copyBtnTrans == (Object)null)
		{
			return;
		}
		Transform val = ((Transform)inspector.copyBtnTrans).parent.Find("BpeEditBtn");
		if ((Object)(object)val != (Object)null)
		{
			return;
		}
		GameObject val2 = Object.Instantiate<GameObject>(((Component)inspector.copyBtnTrans).gameObject, ((Transform)inspector.copyBtnTrans).parent);
		((Object)val2).name = "BpeEditBtn";
		val2.transform.localScale = Vector3.one;
		Transform transform = val2.transform;
		RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		val3.anchoredPosition = inspector.copyBtnTrans.anchoredPosition + new Vector2(0f, -38f);
		Text[] componentsInChildren = val2.GetComponentsInChildren<Text>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if (((Object)((Component)componentsInChildren[i]).gameObject).name.Contains("text") || ((Object)((Component)componentsInChildren[i]).gameObject).name.Contains("Text") || i == 0)
			{
				componentsInChildren[i].text = "修改蓝图";
				componentsInChildren[i].fontSize = Mathf.Max(componentsInChildren[i].fontSize - 2, 10);
				break;
			}
		}
		Button component = val2.GetComponent<Button>();
		((UnityEventBase)component.onClick).RemoveAllListeners();
		((UnityEvent)component.onClick).AddListener((UnityAction)delegate
		{
			if (inspector.blueprint != null && inspector.blueprint.isValid)
			{
				BpeEditor.Open(inspector);
			}
		});
		Plugin.L.LogInfo((object)"已为蓝图界面添加「修改蓝图」按钮");
	}
}