Decompiled source of DSPEasy Exchange v0.5.6

DSPEasy.Exchange.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DSPEasy.Exchange.Pricing;
using DSPEasy.Exchange.Trade;
using DSPEasy.Exchange.UI;
using DSPEasy.Framework.Diagnostics;
using DSPEasy.Framework.Save;
using DSPEasy.Framework.UI;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DSPEasy.Exchange")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.6.0")]
[assembly: AssemblyInformationalVersion("0.5.6+8d07755fdac77ea113d82f799a4e08686c89e363")]
[assembly: AssemblyProduct("DSPEasy.Exchange")]
[assembly: AssemblyTitle("DSPEasy.Exchange")]
[assembly: AssemblyVersion("0.5.6.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DSPEasy.Exchange
{
	[BepInPlugin("DSPEasy.Exchange", "DSPEasy.Exchange", "0.5.6")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "DSPEasy.Exchange";

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyboardShortcut> PanelHotkey;

		internal static ConfigEntry<float> PriceScale;

		internal static ConfigEntry<bool> VerboseTradeLog;

		private void Awake()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[EasyExchange] 框架程序集:" + typeof(ModSave).Assembly.Location));
			PanelHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("界面", "面板热键", new KeyboardShortcut((KeyCode)107, Array.Empty<KeyCode>()), "开关 EMC 兑换面板。默认 K:O 已被原版蓝图库占用(VFInput._openBlueprintLibrary)。");
			PriceScale = ((BaseUnityPlugin)this).Config.Bind<float>("定价", "基价倍率", 1f, "所有内置基价乘以这个系数,用来整体抬高/压低物价(0.5 = 全部便宜一半)。CSV 里手填的固定价、以及沙土/水的口径不受影响。改完重启游戏生效。");
			EmcPricing.PriceScale = ((PriceScale.Value > 0f) ? PriceScale.Value : 1f);
			VerboseTradeLog = ((BaseUnityPlugin)this).Config.Bind<bool>("界面", "交易日志", false, "把每次买卖成功也写进 BepInEx 日志(Info 级)。默认只写调试日志,避免刷屏。");
			EmcPricing.LogInfo = delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)m);
			};
			EmcPricing.LogWarn = delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)m);
			};
			ModSave.LogInfo = delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)m);
			};
			ModSave.LogWarn = delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)m);
			};
			EmcAccount.Init();
			ExchangeStats.Initialize();
			ModProfiler.Install((Action<string>)delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)m);
			}, (Action<string>)delegate(string m)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)m);
			});
			try
			{
				new Harmony("DSPEasy.Exchange").PatchAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[EasyExchange] Harmony 补丁失败:" + ex));
			}
		}

		private void Update()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			EmcPricing.EnsureBuilt();
			if (PanelHotkey == null)
			{
				return;
			}
			KeyboardShortcut value = PanelHotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && !IsTyping())
			{
				ExchangePanel exchangePanel = ExchangePanel.Ensure();
				if ((Object)(object)exchangePanel != (Object)null)
				{
					exchangePanel.Toggle();
				}
			}
		}

		private static bool IsTyping()
		{
			EventSystem current = EventSystem.current;
			if ((Object)(object)current == (Object)null || (Object)(object)current.currentSelectedGameObject == (Object)null)
			{
				return false;
			}
			InputField component = current.currentSelectedGameObject.GetComponent<InputField>();
			if ((Object)(object)component != (Object)null)
			{
				return component.isFocused;
			}
			return false;
		}
	}
}
namespace DSPEasy.Exchange.UI
{
	internal class ExchangePanel : MonoBehaviour
	{
		private sealed class Column
		{
			public bool buildings;

			public Text label;

			public RectTransform viewport;

			public RectTransform content;

			public ScrollRect scroll;

			public Text empty;

			public readonly List<GameObject> rows = new List<GameObject>();

			public readonly List<int> items = new List<int>();

			public readonly List<Button> buttons = new List<Button>();
		}

		private const float PanelWidth = 620f;

		private const float PanelHeight = 590f;

		private const float HeaderHeight = 34f;

		private const float RowHeight = 30f;

		private const float SidePad = 14f;

		private const float ColumnGap = 10f;

		private const float ColumnWidth = 291f;

		private const float ColumnLabelTop = 200f;

		private const float ColumnLabelHeight = 18f;

		private const float ViewTop = 222f;

		private const float ViewHeight = 356f;

		private const float PickerWidth = 660f;

		private static readonly Color HintColor = new Color(0.65f, 0.72f, 0.78f, 1f);

		private static readonly Color DimColor = new Color(0.55f, 0.58f, 0.62f, 1f);

		private static readonly Color PriceColor = new Color(0.98f, 0.85f, 0.45f, 1f);

		private static readonly Color SelColor = new Color(0.45f, 0.8f, 0.6f, 1f);

		private static readonly Color HeaderColor = new Color(0.16f, 0.21f, 0.26f, 0.98f);

		private static readonly Color RowColor = new Color(0.14f, 0.18f, 0.22f, 0.95f);

		private GameObject root;

		private RectTransform rootRt;

		private RectTransform headerRt;

		private Camera uiCam;

		private Text balanceText;

		private Text sandText;

		private Text selectedText;

		private Text hintText;

		private Text pickLabel;

		private InputField countField;

		private Button buyButton;

		private int selectedItem;

		private bool built;

		private bool lastHolding;

		private bool sellMode;

		private Text modeLabel;

		private long lastSandText = -1L;

		private long lastBalanceText = -1L;

		private readonly Column[] columns = new Column[2];

		private readonly List<Button> blockable = new List<Button>();

		private static string lastWarning;

		public static ExchangePanel Instance { get; private set; }

		public bool Opened
		{
			get
			{
				if ((Object)(object)root != (Object)null)
				{
					return root.activeSelf;
				}
				return false;
			}
		}

		public static ExchangePanel Ensure()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance != (Object)null)
			{
				return Instance;
			}
			if ((Object)(object)UIRoot.instance == (Object)null || (Object)(object)UIRoot.instance.uiGame == (Object)null)
			{
				return null;
			}
			GameObject val = new GameObject("DSPEasy.ExchangePanel");
			val.transform.SetParent(((Component)UIRoot.instance.uiGame).transform, false);
			Instance = val.AddComponent<ExchangePanel>();
			return Instance;
		}

		public void Open()
		{
			if (!built)
			{
				Build();
			}
			if (!((Object)(object)root == (Object)null))
			{
				Refresh();
				root.SetActive(true);
				UiFocus.BringToFront(root);
			}
		}

		public void Close()
		{
			if ((Object)(object)root != (Object)null)
			{
				root.SetActive(false);
			}
		}

		public void Toggle()
		{
			if (Opened)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		private unsafe void Update()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!Opened)
			{
				return;
			}
			TimingScope val = ExchangeStats.MeasurePanelRefresh();
			try
			{
				HandleSellClick();
				RefreshLive();
				UiFocus.Poll(root);
			}
			finally
			{
				((IDisposable)(*(TimingScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private void HandleSellClick()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			Player mainPlayer = GameMain.mainPlayer;
			bool flag = mainPlayer != null && mainPlayer.inhandItemId > 0 && mainPlayer.inhandItemCount > 0;
			if (flag != lastHolding)
			{
				lastHolding = flag;
				SetInteractable(!flag);
			}
			if (flag && Input.GetMouseButtonDown(0))
			{
				Vector2 val = Vector2.op_Implicit(Input.mousePosition);
				if ((!((Object)(object)headerRt != (Object)null) || !RectTransformUtility.RectangleContainsScreenPoint(headerRt, val, uiCam)) && RectTransformUtility.RectangleContainsScreenPoint(rootRt, val, uiCam))
				{
					DoTrade(TradeService.SellHeld(), "回收");
				}
			}
		}

		private void SetInteractable(bool on)
		{
			for (int i = 0; i < blockable.Count; i++)
			{
				if ((Object)(object)blockable[i] != (Object)null)
				{
					((Selectable)blockable[i]).interactable = on;
				}
			}
			for (int j = 0; j < columns.Length; j++)
			{
				Column column = columns[j];
				if (column == null)
				{
					continue;
				}
				for (int k = 0; k < column.buttons.Count; k++)
				{
					if ((Object)(object)column.buttons[k] != (Object)null)
					{
						((Selectable)column.buttons[k]).interactable = on;
					}
				}
			}
		}

		private void OnRowClick(int itemId, int held)
		{
			bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			if (sellMode)
			{
				int val = TradeService.StackSizeOf(itemId);
				int num = (flag ? held : Math.Min(val, held));
				if (num <= 0)
				{
					num = held;
				}
				DoTrade(TradeService.SellFromPackage(itemId, num), "回收");
			}
			else
			{
				int count = ((!flag) ? 1 : TradeService.StackSizeOf(itemId));
				DoTrade(TradeService.Buy(itemId, count), "购买");
			}
		}

		private void OnToggleMode()
		{
			sellMode = !sellMode;
			if ((Object)(object)modeLabel != (Object)null)
			{
				modeLabel.text = (sellMode ? "列表:我的物品" : "列表:可购买");
			}
			RebuildRows();
			Refresh();
		}

		private void OnBuyClick()
		{
			int result;
			if (selectedItem <= 0)
			{
				Warn("先点「选择物品」挑一个要买的东西");
			}
			else if (!int.TryParse(countField.text, out result) || result <= 0)
			{
				Warn("先填购买数量");
			}
			else
			{
				DoTrade(TradeService.Buy(selectedItem, result), "购买");
			}
		}

		private void OnQuickBuy(int mode)
		{
			if (selectedItem <= 0)
			{
				Warn("先点「选择物品」挑一个要买的东西");
				return;
			}
			int num = mode switch
			{
				0 => 1, 
				1 => TradeService.StackSizeOf(selectedItem), 
				_ => MaxAffordable(selectedItem), 
			};
			if (num > 0)
			{
				countField.text = num.ToString();
				DoTrade(TradeService.Buy(selectedItem, num), "购买");
			}
		}

		private static int MaxAffordable(int itemId)
		{
			EmcTable table = EmcPricing.Table;
			if (table == null || !table.IsPriced(itemId))
			{
				Warn("「" + TradeService.ItemName(itemId) + "」没有 EMC 价");
				return 0;
			}
			long num = table.Get(itemId);
			long num2 = ((num > 0) ? (EmcAccount.Balance / num) : 0);
			if (num2 <= 0)
			{
				Warn("EMC 不足:现有 " + EmcAccount.BalanceText);
				return 0;
			}
			long num3 = ((itemId == 1099) ? num2 : FreeSpaceFor(itemId));
			long num4 = ((num2 < num3) ? num2 : num3);
			if (num4 <= 0)
			{
				Warn("背包已满,放不下「" + TradeService.ItemName(itemId) + "」");
				return 0;
			}
			if (num4 <= int.MaxValue)
			{
				return (int)num4;
			}
			return int.MaxValue;
		}

		private static long FreeSpaceFor(int itemId)
		{
			Player mainPlayer = GameMain.mainPlayer;
			if (mainPlayer == null || mainPlayer.package == null || mainPlayer.package.grids == null)
			{
				return 0L;
			}
			int num = TradeService.StackSizeOf(itemId);
			long num2 = 0L;
			GRID[] grids = mainPlayer.package.grids;
			for (int i = 0; i < grids.Length; i++)
			{
				if (grids[i].itemId == itemId)
				{
					num2 += Math.Max(0, num - grids[i].count);
				}
				else if (grids[i].count <= 0)
				{
					num2 += num;
				}
			}
			return num2;
		}

		private void OnSellSandClick()
		{
			DoTrade(TradeService.SellSand(), "沙土");
		}

		private void DoTrade(TradeResult? maybe, string tag)
		{
			if (!maybe.HasValue)
			{
				return;
			}
			TradeResult value = maybe.Value;
			if (!value.Ok)
			{
				if (!string.IsNullOrEmpty(value.Message))
				{
					Warn(value.Message);
				}
				return;
			}
			string text = "[EMC] " + tag + ":" + value.Message + "(余额 " + EmcAccount.BalanceText + ",已解锁 " + EmcAccount.UnlockedCount + " 项)";
			if (Plugin.VerboseTradeLog != null && Plugin.VerboseTradeLog.Value)
			{
				Plugin.Log.LogInfo((object)text);
			}
			else
			{
				Plugin.Log.LogDebug((object)text);
			}
			if (!string.IsNullOrEmpty(value.Message))
			{
				UIRealtimeTip.Popup(value.Message, false, 0);
			}
			if (value.Unlocked || sellMode)
			{
				RebuildRows();
			}
			Refresh();
		}

		private static void Warn(string message)
		{
			if (message != lastWarning)
			{
				lastWarning = message;
				Plugin.Log.LogWarning((object)("[EMC] " + message));
			}
			UIRealtimeTip.Popup(message, false, 0);
		}

		private void Build()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0655: Unknown result type (might be due to invalid IL or missing references)
			built = true;
			try
			{
				UIGame val = (((Object)(object)UIRoot.instance != (Object)null) ? UIRoot.instance.uiGame : null);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)"[EasyExchange] UIGame 尚未创建,无法构建 EMC 面板");
					return;
				}
				UiFactory.EnsureFont();
				GameObject val2 = new GameObject("DSPEasy.ExchangePanelBody", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				rootRt = (RectTransform)val2.transform;
				((Transform)rootRt).SetParent(((Component)val).transform, false);
				RectTransform obj = rootRt;
				Vector2 anchorMin = (rootRt.anchorMax = new Vector2(0f, 0.5f));
				obj.anchorMin = anchorMin;
				rootRt.pivot = new Vector2(0f, 0.5f);
				rootRt.anchoredPosition = new Vector2(8f, 0f);
				rootRt.sizeDelta = new Vector2(620f, 590f);
				root = val2;
				uiCam = UiFactory.CanvasCamera((Component)(object)rootRt);
				Image component = val2.GetComponent<Image>();
				((Graphic)component).color = UiFactory.PanelColor;
				component.sprite = null;
				((Graphic)component).raycastTarget = true;
				headerRt = UiFactory.Place(UiFactory.NewRect("TitleBar", root.transform, 528f, 34f), 0f, 0f, 528f, 34f);
				Image obj2 = ((Component)headerRt).gameObject.AddComponent<Image>();
				((Graphic)obj2).color = HeaderColor;
				((Graphic)obj2).raycastTarget = true;
				((Component)headerRt).gameObject.AddComponent<UiDrag>().target = rootRt;
				UiFactory.MakeText("Title", (Transform)(object)headerRt, 12f, -2f, 220f, 30f, 17, (TextAnchor)3).text = "EMC 兑换";
				Text obj3 = UiFactory.MakeText("DragHint", (Transform)(object)headerRt, 118f, -2f, 260f, 30f, 11, (TextAnchor)3);
				obj3.text = "按住标题栏可拖动";
				((Graphic)obj3).color = HintColor;
				Text val4 = UiFactory.MakeButton("Close", root.transform, 536f, -4f, 70f, 26f, "关闭", (Action)Close);
				blockable.Add(((Component)val4).GetComponentInParent<Button>());
				balanceText = UiFactory.MakeText("Balance", root.transform, 14f, -42f, 320f, 24f, 14, (TextAnchor)3);
				balanceText.text = "余额:0 EMC";
				sandText = UiFactory.MakeText("Sand", root.transform, 14f, -74f, 470f, 24f, 12, (TextAnchor)3);
				((Graphic)sandText).color = HintColor;
				Text val5 = UiFactory.MakeButton("SellSand", root.transform, 496f, -74f, 110f, 26f, "卖出沙土", (Action)OnSellSandClick);
				blockable.Add(((Component)val5).GetComponentInParent<Button>());
				Text val6 = UiFactory.MakeButton("Pick", root.transform, 14f, -106f, 150f, 30f, "选择物品 ▾", (Action)OnPickClick);
				blockable.Add(((Component)val6).GetComponentInParent<Button>());
				pickLabel = val6;
				val6.fontSize = 13;
				selectedText = UiFactory.MakeText("Selected", root.transform, 172f, -106f, 210f, 30f, 12, (TextAnchor)3);
				((Graphic)selectedText).color = HintColor;
				countField = UiFactory.MakeInputField("Count", root.transform, 390f, -106f, 80f, 30f, "个数", 13);
				Text val7 = UiFactory.MakeButton("Buy", root.transform, 480f, -106f, 126f, 30f, "购买", (Action)OnBuyClick);
				buyButton = ((Component)val7).GetComponentInParent<Button>();
				blockable.Add(buyButton);
				Text val8 = UiFactory.MakeButton("BuyOne", root.transform, 14f, -140f, 62f, 26f, "买×1", (Action)delegate
				{
					OnQuickBuy(0);
				});
				val8.fontSize = 12;
				blockable.Add(((Component)val8).GetComponentInParent<Button>());
				Text val9 = UiFactory.MakeButton("BuyStack", root.transform, 84f, -140f, 74f, 26f, "买×一组", (Action)delegate
				{
					OnQuickBuy(1);
				});
				val9.fontSize = 12;
				blockable.Add(((Component)val9).GetComponentInParent<Button>());
				Text val10 = UiFactory.MakeButton("BuyMax", root.transform, 166f, -140f, 78f, 26f, "买×MAX", (Action)delegate
				{
					OnQuickBuy(2);
				});
				val10.fontSize = 12;
				blockable.Add(((Component)val10).GetComponentInParent<Button>());
				Text obj4 = UiFactory.MakeText("QuickHint", root.transform, 254f, -140f, 352f, 26f, 11, (TextAnchor)3);
				obj4.text = "快捷买入:按当前单价,MAX 受余额与背包空位限制";
				((Graphic)obj4).color = HintColor;
				modeLabel = UiFactory.MakeButton("Mode", root.transform, 14f, -172f, 122f, 24f, "列表:可购买", (Action)OnToggleMode);
				blockable.Add(((Component)modeLabel).GetComponentInParent<Button>());
				modeLabel.fontSize = 12;
				hintText = UiFactory.MakeText("Hint", root.transform, 146f, -172f, 460f, 24f, 11, (TextAnchor)3);
				((Graphic)hintText).color = HintColor;
				columns[0] = BuildColumn(0, "物品", buildings: false);
				columns[1] = BuildColumn(1, "建筑", buildings: true);
				root.SetActive(false);
				Plugin.Log.LogInfo((object)("[EasyExchange] EMC 面板已创建(font=" + ((Object)(object)UiFactory.DefaultFont != (Object)null) + ")"));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[EasyExchange] 构建 EMC 面板失败:" + ex));
				built = false;
				if ((Object)(object)root != (Object)null)
				{
					Object.Destroy((Object)(object)root);
				}
				root = null;
				columns[0] = null;
				columns[1] = null;
			}
		}

		private Column BuildColumn(int index, string label, bool buildings)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			float num = 14f + (float)index * 301f;
			Column column = new Column
			{
				buildings = buildings
			};
			column.label = UiFactory.MakeText("ColLabel" + index, root.transform, num, -200f, 291f, 18f, 12, (TextAnchor)3);
			column.label.text = label;
			column.viewport = UiFactory.Place(UiFactory.NewRect("Viewport" + index, root.transform, 291f, 356f), num, -222f, 291f, 356f);
			Image obj = ((Component)column.viewport).gameObject.AddComponent<Image>();
			((Graphic)obj).color = new Color(0f, 0f, 0f, 0.18f);
			((Graphic)obj).raycastTarget = true;
			((Component)column.viewport).gameObject.AddComponent<RectMask2D>();
			column.content = UiFactory.NewRect("Content" + index, (Transform)(object)column.viewport, 291f, 1f);
			RectTransform content = column.content;
			RectTransform content2 = column.content;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			content2.anchorMax = val;
			content.anchorMin = val;
			column.content.pivot = new Vector2(0f, 1f);
			column.content.anchoredPosition = Vector2.zero;
			column.content.sizeDelta = new Vector2(291f, 1f);
			column.scroll = ((Component)column.viewport).gameObject.AddComponent<ScrollRect>();
			column.scroll.content = column.content;
			column.scroll.viewport = column.viewport;
			column.scroll.horizontal = false;
			column.scroll.vertical = true;
			column.scroll.movementType = (MovementType)2;
			column.scroll.scrollSensitivity = 30f;
			column.empty = UiFactory.MakeText("Empty" + index, (Transform)(object)column.viewport, 8f, -8f, 275f, 90f, 11, (TextAnchor)0);
			((Graphic)column.empty).color = HintColor;
			((Component)column.empty).gameObject.SetActive(false);
			return column;
		}

		private void OnPickClick()
		{
			//IL_002b: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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)
			if (!((Object)(object)rootRt == (Object)null) && !((Object)(object)UIRoot.instance == (Object)null))
			{
				RectTransform val = (RectTransform)((Component)UIRoot.instance.uiGame).transform;
				Vector3 val2 = (PickerFitsOnRight() ? new Vector3(630f, -8f, 0f) : new Vector3(10f, -120f, 0f));
				ExchangePicker.Open(Vector2.op_Implicit(((Transform)val).InverseTransformPoint(((Transform)rootRt).TransformPoint(val2))), OnPicked);
			}
		}

		private bool PickerFitsOnRight()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rootRt == (Object)null || (Object)(object)UIRoot.instance == (Object)null)
			{
				return false;
			}
			try
			{
				return UIRoot.WorldToScreenPoint(((Transform)rootRt).TransformPoint(new Vector3(620f, 0f, 0f))).x + 660f <= (float)Screen.width;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private void OnPicked(ItemProto proto)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (proto != null)
			{
				selectedItem = ((Proto)proto).ID;
				if ((Object)(object)pickLabel != (Object)null)
				{
					pickLabel.text = ((Proto)proto).name;
				}
				if ((Object)(object)selectedText != (Object)null)
				{
					long milli = EmcPricing.Table.Get(((Proto)proto).ID);
					selectedText.text = "单价 " + EmcUnit.Format(milli) + " EMC";
					((Graphic)selectedText).color = SelColor;
				}
				RefreshBuyButton();
			}
		}

		private List<int> CurrentList()
		{
			List<int> list = new List<int>();
			if (sellMode)
			{
				List<TradeService.PackageEntry> list2 = TradeService.PackageItems();
				for (int i = 0; i < list2.Count; i++)
				{
					list.Add(list2[i].ItemId);
				}
			}
			else
			{
				list = TradeService.BuyableItems();
			}
			return list;
		}

		private static bool IsBuilding(int itemId)
		{
			ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			if (val != null)
			{
				return val.GridIndex / 1000 == 2;
			}
			return false;
		}

		private void RefreshRows()
		{
			if (columns[0] == null || columns[1] == null)
			{
				return;
			}
			if (EmcPricing.Table == null)
			{
				SetColumnMessage(columns[0], "定价表生成中…");
				SetColumnMessage(columns[1], "定价表生成中…");
				return;
			}
			List<int> list = CurrentList();
			Dictionary<int, int> dictionary = null;
			if (sellMode)
			{
				dictionary = new Dictionary<int, int>();
				List<TradeService.PackageEntry> list2 = TradeService.PackageItems();
				for (int i = 0; i < list2.Count; i++)
				{
					dictionary[list2[i].ItemId] = list2[i].Count;
				}
			}
			List<int> list3 = new List<int>();
			List<int> list4 = new List<int>();
			for (int j = 0; j < list.Count; j++)
			{
				if (IsBuilding(list[j]))
				{
					list4.Add(list[j]);
				}
				else
				{
					list3.Add(list[j]);
				}
			}
			FillColumn(columns[0], list3, dictionary);
			FillColumn(columns[1], list4, dictionary);
			if (selectedItem > 0 && !TradeService.IsPurchasable(selectedItem))
			{
				selectedItem = 0;
				if ((Object)(object)pickLabel != (Object)null)
				{
					pickLabel.text = "选择物品 ▾";
				}
				if ((Object)(object)selectedText != (Object)null)
				{
					selectedText.text = "";
				}
			}
		}

		private void FillColumn(Column col, List<int> ids, Dictionary<int, int> held)
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			bool flag = col.items.Count == ids.Count;
			if (flag)
			{
				for (int i = 0; i < ids.Count; i++)
				{
					if (col.items[i] != ids[i])
					{
						flag = false;
						break;
					}
				}
			}
			if (flag)
			{
				for (int j = 0; j < col.rows.Count && j < col.items.Count; j++)
				{
					StyleRow(col.rows[j], col.items[j], HeldOf(held, col.items[j]));
				}
			}
			else
			{
				ClearColumn(col);
				for (int k = 0; k < ids.Count; k++)
				{
					CreateRow(col, k, ids[k], HeldOf(held, ids[k]));
				}
				col.content.sizeDelta = new Vector2(291f, Mathf.Max(1f, (float)ids.Count * 30f));
				col.content.anchoredPosition = Vector2.zero;
				if ((Object)(object)col.scroll != (Object)null)
				{
					col.scroll.verticalNormalizedPosition = 1f;
				}
			}
			col.label.text = (col.buildings ? "建筑" : "物品") + "(" + ids.Count + ")";
			((Component)col.empty).gameObject.SetActive(ids.Count == 0);
			if (ids.Count == 0)
			{
				col.empty.text = EmptyText(col);
			}
		}

		private void StyleRow(GameObject row, int itemId, int held)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)row == (Object)null)
			{
				return;
			}
			ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			long milli = EmcPricing.Table.Get(itemId);
			bool flag = sellMode || EmcAccount.CanAfford(milli);
			bool flag2 = val != null && val.GridIndex > 0;
			Text[] componentsInChildren = row.GetComponentsInChildren<Text>(true);
			foreach (Text val2 in componentsInChildren)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				if (((Object)val2).name == "Name")
				{
					string text = ((val == null) ? ("物品 " + itemId) : ((Proto)val).name);
					if (sellMode && held > 0)
					{
						text = text + "  ×" + held;
					}
					val2.text = text;
					((Graphic)val2).color = ((!flag) ? DimColor : (flag2 ? UiFactory.TextColor : HintColor));
				}
				else if (((Object)val2).name == "Price")
				{
					val2.text = EmcUnit.Format(milli) + " EMC";
					((Graphic)val2).color = (flag ? PriceColor : DimColor);
				}
			}
		}

		private static int HeldOf(Dictionary<int, int> held, int itemId)
		{
			if (held != null && held.TryGetValue(itemId, out var value))
			{
				return value;
			}
			return 0;
		}

		private string EmptyText(Column col)
		{
			if (sellMode)
			{
				if (!col.buildings)
				{
					return "背包里没有可回收的物品(未定价/禁止交易的不会列出)。";
				}
				return "背包里没有可回收的建筑(未定价/禁止交易的不会列出)。";
			}
			if (TradeService.AllUnlocked)
			{
				if (!col.buildings)
				{
					return "没有可购买的物品。";
				}
				return "没有可购买的建筑。";
			}
			if (!col.buildings)
			{
				return "还没解锁物品。先在「我的物品」里卖点东西。";
			}
			return "还没解锁建筑。卖一个同类建筑就会解锁。";
		}

		private void RebuildRows()
		{
			for (int i = 0; i < columns.Length; i++)
			{
				if (columns[i] != null)
				{
					ClearColumn(columns[i]);
				}
			}
			RefreshRows();
		}

		private void ClearColumn(Column col)
		{
			for (int i = 0; i < col.rows.Count; i++)
			{
				if ((Object)(object)col.rows[i] != (Object)null)
				{
					Object.Destroy((Object)(object)col.rows[i]);
				}
			}
			col.rows.Clear();
			col.items.Clear();
			col.buttons.Clear();
		}

		private void SetColumnMessage(Column col, string message)
		{
			ClearColumn(col);
			col.label.text = (col.buildings ? "建筑" : "物品") + "(0)";
			col.empty.text = message;
			((Component)col.empty).gameObject.SetActive(true);
		}

		private void CreateRow(Column col, int index, int itemId, int held)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: 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)
			float num = (float)(-index) * 30f;
			Text obj = UiFactory.MakeButton("Row" + index, (Transform)(object)col.content, 0f, num, 291f, 28f, "", (Action)delegate
			{
				OnRowClick(itemId, held);
			});
			obj.fontSize = 12;
			obj.alignment = (TextAnchor)3;
			RectTransform val = (RectTransform)((Component)obj).transform.parent;
			col.rows.Add(((Component)val).gameObject);
			col.items.Add(itemId);
			Button component = ((Component)val).GetComponent<Button>();
			if ((Object)(object)component != (Object)null)
			{
				((Selectable)component).targetGraphic.color = RowColor;
				((Selectable)component).interactable = !lastHolding;
				col.buttons.Add(component);
			}
			ItemProto val2 = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			if (val2 != null && (Object)(object)val2.iconSprite != (Object)null)
			{
				UiFactory.MakeImage("Icon", (Transform)(object)val, 2f, -3f, 24f, 24f, Color.white, val2.iconSprite);
			}
			UiFactory.MakeText("Name", (Transform)(object)val, 30f, 0f, 166f, 28f, 12, (TextAnchor)3);
			((Graphic)UiFactory.MakeText("Price", (Transform)(object)val, 198f, 0f, 88f, 28f, 11, (TextAnchor)5)).color = PriceColor;
			StyleRow(((Component)val).gameObject, itemId, held);
		}

		private void RefreshLive()
		{
			Player mainPlayer = GameMain.mainPlayer;
			if (mainPlayer != null)
			{
				if ((Object)(object)balanceText != (Object)null && EmcAccount.Balance != lastBalanceText)
				{
					lastBalanceText = EmcAccount.Balance;
					balanceText.text = "余额:" + EmcAccount.BalanceText + " EMC";
				}
				if ((Object)(object)sandText != (Object)null && mainPlayer.sandCount != lastSandText)
				{
					lastSandText = mainPlayer.sandCount;
					sandText.text = "沙土:" + mainPlayer.sandCount.ToString("#,0") + "(每 " + 100 + " 沙土 = 1 EMC)";
				}
				RefreshBuyButton();
			}
		}

		private void Refresh()
		{
			if (!((Object)(object)root == (Object)null))
			{
				RefreshLive();
				RefreshRows();
				RefreshBuyButton();
				if ((Object)(object)hintText != (Object)null)
				{
					hintText.text = (sellMode ? "点一下卖一组,Shift+点卖光这一类。" : "点一下买 1 个,Shift+点买一组。");
				}
			}
		}

		private void RefreshBuyButton()
		{
			if (!((Object)(object)buyButton == (Object)null))
			{
				bool flag = selectedItem > 0;
				int result = 0;
				if (flag && !int.TryParse(countField.text, out result))
				{
					flag = false;
				}
				if (flag && result <= 0)
				{
					flag = false;
				}
				((Selectable)buyButton).interactable = flag && !lastHolding;
			}
		}
	}
	public static class ExchangePicker
	{
		internal static HashSet<int> Pending;

		internal static HashSet<int> Allowed;

		public static void Open(Vector2 position, Action<ItemProto> onPick)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Pending = new HashSet<int>(TradeService.BuyableItems());
			UIItemPicker.showAll = false;
			UIItemPicker.Popup(position, (Action<ItemProto>)delegate(ItemProto proto)
			{
				if (onPick != null)
				{
					onPick(proto);
				}
			});
		}
	}
	[HarmonyPatch(typeof(UIItemPicker), "Popup")]
	internal static class Patch_ExchangePickerPopup
	{
		private static void Prefix()
		{
			ExchangePicker.Allowed = ExchangePicker.Pending;
			ExchangePicker.Pending = null;
		}
	}
	[HarmonyPatch(typeof(UIItemPicker), "RefreshIcons")]
	internal static class Patch_ExchangePickerFilter
	{
		private static readonly FieldRef<UIItemPicker, ItemProto[]> ProtoArrayRef = AccessTools.FieldRefAccess<UIItemPicker, ItemProto[]>("protoArray");

		private static readonly FieldRef<UIItemPicker, uint[]> IndexArrayRef = AccessTools.FieldRefAccess<UIItemPicker, uint[]>("indexArray");

		private static readonly FieldRef<UIItemPicker, ComputeBuffer> IndexBufferRef = AccessTools.FieldRefAccess<UIItemPicker, ComputeBuffer>("indexBuffer");

		private static readonly FieldRef<UIItemPicker, int> CurrentTypeRef = AccessTools.FieldRefAccess<UIItemPicker, int>("currentType");

		private static void Postfix(UIItemPicker __instance)
		{
			HashSet<int> allowed = ExchangePicker.Allowed;
			if (allowed == null)
			{
				return;
			}
			try
			{
				ItemProto[] array = ProtoArrayRef.Invoke(__instance);
				uint[] array2 = IndexArrayRef.Invoke(__instance);
				if (array == null || array2 == null)
				{
					return;
				}
				int num = 1;
				try
				{
					num = CurrentTypeRef.Invoke(__instance);
				}
				catch
				{
					num = 1;
				}
				if (num < 1)
				{
					num = 1;
				}
				HashSet<int> hashSet = new HashSet<int>();
				foreach (int item in allowed)
				{
					if (item <= 0)
					{
						continue;
					}
					ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(item);
					if (val != null)
					{
						int num2 = val.GridIndex / 1000;
						if (num2 == num || (num2 != 1 && num2 != 2 && num == 1))
						{
							hashSet.Add(item);
						}
					}
				}
				bool flag = false;
				HashSet<int> hashSet2 = new HashSet<int>();
				for (int i = 0; i < array.Length && i < array2.Length; i++)
				{
					ItemProto val2 = array[i];
					if (val2 != null)
					{
						if (hashSet.Contains(((Proto)val2).ID))
						{
							hashSet2.Add(((Proto)val2).ID);
							continue;
						}
						array[i] = null;
						array2[i] = 0u;
						flag = true;
					}
				}
				uint[] array3 = ((GameMain.iconSet != null) ? GameMain.iconSet.itemIconIndex : null);
				if (array3 != null)
				{
					foreach (int item2 in hashSet)
					{
						if (item2 <= 0 || item2 >= array3.Length || hashSet2.Contains(item2))
						{
							continue;
						}
						uint num3 = array3[item2];
						if (num3 == 0)
						{
							continue;
						}
						int num4 = -1;
						for (int j = 0; j < array.Length && j < array2.Length; j++)
						{
							if (array[j] == null && array2[j] == 0)
							{
								num4 = j;
								break;
							}
						}
						if (num4 >= 0)
						{
							ItemProto val3 = ((ProtoSet<ItemProto>)(object)LDB.items).Select(item2);
							if (val3 != null)
							{
								array[num4] = val3;
								array2[num4] = num3;
								hashSet2.Add(item2);
								flag = true;
							}
							continue;
						}
						break;
					}
				}
				if (flag)
				{
					ComputeBuffer val4 = IndexBufferRef.Invoke(__instance);
					if (val4 != null)
					{
						val4.SetData((Array)array2);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[EasyExchange] 过滤物品选择器失败:" + ex.Message));
			}
		}
	}
}
namespace DSPEasy.Exchange.Trade
{
	public static class EmcAccount
	{
		public const string SaveNamespace = "DSPEasyExchange";

		private static readonly HashSet<int> unlocked = new HashSet<int>();

		private static long balance;

		private static bool registered;

		public static bool Persisted { get; private set; }

		public static long Balance => balance;

		public static string BalanceText => EmcUnit.Format(balance);

		public static int UnlockedCount => unlocked.Count;

		public static void Init()
		{
			if (registered)
			{
				return;
			}
			registered = true;
			try
			{
				ModSave.Register("DSPEasyExchange", (IModSaveChunk)(object)new EmcAccountChunk());
				Persisted = true;
			}
			catch (Exception ex)
			{
				ModSave.LogWarn("[EMC] 注册旁车块 EMC1 失败,本局账户不会落盘:" + ex.Message);
			}
		}

		public static List<int> SnapshotUnlocked()
		{
			List<int> list = new List<int>(unlocked);
			list.Sort();
			return list;
		}

		public static bool IsUnlocked(int itemId)
		{
			if (itemId > 0)
			{
				return unlocked.Contains(itemId);
			}
			return false;
		}

		public static bool Unlock(int itemId)
		{
			if (itemId <= 0)
			{
				return false;
			}
			return unlocked.Add(itemId);
		}

		public static void Add(long milli)
		{
			if (milli > 0)
			{
				balance += milli;
			}
		}

		public static bool CanAfford(long milli)
		{
			if (milli >= 0)
			{
				return balance >= milli;
			}
			return false;
		}

		public static bool TrySpend(long milli)
		{
			if (!CanAfford(milli))
			{
				return false;
			}
			balance -= milli;
			return true;
		}

		internal static void Restore(long milli, List<int> itemIds)
		{
			balance = ((milli > 0) ? milli : 0);
			unlocked.Clear();
			if (itemIds == null)
			{
				return;
			}
			for (int i = 0; i < itemIds.Count; i++)
			{
				int num = itemIds[i];
				if (num > 0)
				{
					unlocked.Add(num);
				}
			}
		}

		public static void Reset()
		{
			unlocked.Clear();
			balance = 0L;
		}
	}
	internal sealed class EmcAccountChunk : IModSaveChunk
	{
		public const string ChunkId = "EMC1";

		private const int MaxUnlocked = 12000;

		public string Id => "EMC1";

		public int Version => 1;

		public int Count => 1 + EmcAccount.UnlockedCount;

		public void Write(BinaryWriter writer)
		{
			writer.Write(EmcAccount.Balance);
			List<int> list = EmcAccount.SnapshotUnlocked();
			writer.Write(list.Count);
			for (int i = 0; i < list.Count; i++)
			{
				writer.Write(list[i]);
			}
		}

		public void Read(BinaryReader reader, int version)
		{
			long milli = reader.ReadInt64();
			int num = reader.ReadInt32();
			if (num < 0 || num > 12000)
			{
				throw new InvalidDataException("EMC1 解锁条目数非法:" + num);
			}
			List<int> list = new List<int>(num);
			for (int i = 0; i < num; i++)
			{
				int num2 = reader.ReadInt32();
				if (EmcTable.IsValidId(num2))
				{
					list.Add(num2);
				}
			}
			EmcAccount.Restore(milli, list);
		}

		public void Clear()
		{
			EmcAccount.Reset();
		}
	}
	internal static class EmcStatistics
	{
		private const int MaxDepth = 16;

		private const int MaxRecords = 2048;

		private static readonly Dictionary<int, RecipeProto> RecipeCache = new Dictionary<int, RecipeProto>();

		private static int recipeCacheRecipeCount = -1;

		public static void OnBuy(int itemId, int count)
		{
			if (itemId > 0 && count > 0)
			{
				PlanetFactory val = CurrentFactory();
				if (val != null)
				{
					int records = 0;
					List<int> path = new List<int>(16);
					Produce(itemId, count, 0, path, val, ref records);
				}
			}
		}

		public static void OnSell(int itemId, int count)
		{
			if (itemId > 0 && count > 0)
			{
				PlanetFactory val = CurrentFactory();
				if (val != null)
				{
					AddConsumption(itemId, count, val);
				}
			}
		}

		private static PlanetFactory CurrentFactory()
		{
			Player mainPlayer = GameMain.mainPlayer;
			if (mainPlayer == null)
			{
				return null;
			}
			return mainPlayer.nearestFactory;
		}

		private static void Produce(int itemId, long count, int depth, List<int> path, PlanetFactory factory, ref int records)
		{
			if (count <= 0 || itemId <= 0 || records >= 2048)
			{
				return;
			}
			records++;
			AddProduction(itemId, count, factory);
			RecipeProto val = PickRecipe(itemId);
			if (val == null || depth >= 16)
			{
				return;
			}
			int num = OutputCountOf(val, itemId);
			if (num <= 0)
			{
				return;
			}
			long num2 = (count + num - 1) / num;
			path.Add(itemId);
			for (int i = 0; i < val.Items.Length; i++)
			{
				int num3 = val.Items[i];
				int num4 = val.ItemCounts[i];
				if (num3 <= 0 || num4 <= 0)
				{
					continue;
				}
				long num5 = num2 * num4;
				if (num5 > 0)
				{
					if (!path.Contains(num3))
					{
						Produce(num3, num5, depth + 1, path, factory, ref records);
					}
					else
					{
						AddProduction(num3, num5, factory);
					}
					if (records < 2048)
					{
						AddConsumption(num3, num5, factory);
					}
				}
			}
			path.RemoveAt(path.Count - 1);
		}

		private static RecipeProto PickRecipe(int itemId)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			if (dataArray == null)
			{
				return null;
			}
			if (recipeCacheRecipeCount != dataArray.Length)
			{
				RecipeCache.Clear();
				recipeCacheRecipeCount = dataArray.Length;
			}
			if (RecipeCache.TryGetValue(itemId, out var value))
			{
				return value;
			}
			EmcTable table = EmcPricing.Table;
			RecipeProto val = null;
			long num = long.MaxValue;
			foreach (RecipeProto val2 in dataArray)
			{
				if (val2 == null || (int)val2.Type == 0 || val2.Results == null || val2.ResultCounts == null || val2.Items == null || val2.ItemCounts == null || OutputCountOf(val2, itemId) <= 0)
				{
					continue;
				}
				long num2 = 0L;
				bool flag = true;
				for (int j = 0; j < val2.Items.Length; j++)
				{
					int num3 = val2.Items[j];
					int num4 = val2.ItemCounts[j];
					if (num3 > 0 && num4 > 0)
					{
						long num5 = table?.Get(num3) ?? 0;
						if (!EmcUnit.IsPriced(num5))
						{
							flag = false;
							break;
						}
						num2 += num5 * num4;
						if (num2 < 0 || num2 > 144115188075855871L)
						{
							flag = false;
							break;
						}
					}
				}
				if (flag && num2 < num)
				{
					num = num2;
					val = val2;
				}
			}
			RecipeCache[itemId] = val;
			return val;
		}

		private static int OutputCountOf(RecipeProto r, int itemId)
		{
			if (r.Results == null || r.ResultCounts == null)
			{
				return 0;
			}
			for (int i = 0; i < r.Results.Length; i++)
			{
				if (r.Results[i] == itemId && r.ResultCounts[i] > 0)
				{
					return r.ResultCounts[i];
				}
			}
			return 0;
		}

		private static void AddProduction(int itemId, long count, PlanetFactory factory)
		{
			int num = ClampToInt(count);
			if (num > 0)
			{
				GameMain.mainPlayer.mecha.AddProductionStat(itemId, num, factory);
			}
		}

		private static void AddConsumption(int itemId, long count, PlanetFactory factory)
		{
			int num = ClampToInt(count);
			if (num > 0)
			{
				GameMain.mainPlayer.mecha.AddConsumptionStat(itemId, num, factory);
			}
		}

		private static int ClampToInt(long v)
		{
			if (v <= 0)
			{
				return 0;
			}
			if (v <= int.MaxValue)
			{
				return (int)v;
			}
			return int.MaxValue;
		}
	}
	internal static class ExchangeStats
	{
		internal static ProfilerNode Module;

		internal static ProfilerNode PanelRefresh;

		internal static ProfilerNode Trade;

		private static bool initialized;

		internal static void Initialize()
		{
			if (!initialized)
			{
				initialized = true;
				Module = ModProfiler.RegisterModule("模组 DSPEasy.Exchange");
				PanelRefresh = Module.GetOrAddChild("交易面板刷新");
				Trade = Module.GetOrAddChild("交易结算");
			}
		}

		static ExchangeStats()
		{
			Initialize();
		}

		internal static TimingScope MeasurePanelRefresh()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return PanelRefresh.Measure();
		}

		internal static TimingScope MeasureTrade()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return Trade.Measure();
		}
	}
	public struct TradeResult
	{
		public bool Ok;

		public int ItemId;

		public int Count;

		public long Milli;

		public string Message;

		public bool Unlocked;

		public static TradeResult Fail(string message)
		{
			return new TradeResult
			{
				Ok = false,
				Message = message
			};
		}
	}
	public static class TradeService
	{
		public struct PackageEntry
		{
			public int ItemId;

			public int Count;
		}

		private static Player Me => GameMain.mainPlayer;

		public static bool AllUnlocked => GameMain.sandboxToolsEnabled;

		public static string ItemName(int itemId)
		{
			ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			if (val != null)
			{
				return ((Proto)val).name;
			}
			return "物品 " + itemId;
		}

		public static bool IsPurchasable(int itemId)
		{
			if (!AllUnlocked)
			{
				return EmcAccount.IsUnlocked(itemId);
			}
			return true;
		}

		public static bool IsTradable(int itemId)
		{
			EmcTable table = EmcPricing.Table;
			if (table != null && table.IsPriced(itemId))
			{
				return table.IsTradeable(itemId);
			}
			return false;
		}

		public static int StackSizeOf(int itemId)
		{
			ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
			if (val != null && val.StackSize > 0)
			{
				return val.StackSize;
			}
			return 1;
		}

		public unsafe static TradeResult? SellHeld()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			TimingScope val = ExchangeStats.MeasureTrade();
			try
			{
				Player me = Me;
				if (me == null)
				{
					return TradeResult.Fail("没有玩家");
				}
				int inhandItemId = me.inhandItemId;
				int inhandItemCount = me.inhandItemCount;
				if (inhandItemId <= 0 || inhandItemCount <= 0)
				{
					return null;
				}
				if (!IsTradable(inhandItemId))
				{
					return TradeResult.Fail("「" + ItemName(inhandItemId) + "」不能回收:" + (EmcPricing.Table.IsPriced(inhandItemId) ? "该物品禁止交易" : "没有 EMC 价"));
				}
				long num = EmcPricing.Table.Get(inhandItemId);
				int num2 = default(int);
				me.UseHandItems(inhandItemCount, ref num2);
				int inhandItemCount2 = me.inhandItemCount;
				int num3 = inhandItemCount - inhandItemCount2;
				if (num3 <= 0 && inhandItemCount2 == 0)
				{
					num3 = inhandItemCount;
				}
				if (num3 <= 0)
				{
					return TradeResult.Fail("手上物品没有取走,回收失败");
				}
				long milli = num3 * num;
				EmcAccount.Add(milli);
				bool unlocked = EmcAccount.Unlock(inhandItemId);
				EmcStatistics.OnSell(inhandItemId, num3);
				return new TradeResult
				{
					Ok = true,
					ItemId = inhandItemId,
					Count = num3,
					Milli = milli,
					Unlocked = unlocked,
					Message = "回收 " + ItemName(inhandItemId) + " ×" + num3 + ",+" + EmcUnit.Format(milli) + " EMC"
				};
			}
			finally
			{
				((IDisposable)(*(TimingScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static TradeResult SellFromPackage(int itemId, int count)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			TimingScope val = ExchangeStats.MeasureTrade();
			try
			{
				Player me = Me;
				if (me == null)
				{
					return TradeResult.Fail("没有玩家");
				}
				if (count <= 0)
				{
					return TradeResult.Fail("数量必须大于 0");
				}
				if (!IsTradable(itemId))
				{
					return TradeResult.Fail("「" + ItemName(itemId) + "」不能回收");
				}
				int itemCount = me.package.GetItemCount(itemId);
				if (itemCount < count)
				{
					return TradeResult.Fail("背包里只有 " + itemCount + " 个「" + ItemName(itemId) + "」");
				}
				int num2 = default(int);
				int num = me.package.TakeItem(itemId, count, ref num2);
				if (num <= 0)
				{
					return TradeResult.Fail("没有从背包里取出物品");
				}
				long milli = num * EmcPricing.Table.Get(itemId);
				EmcAccount.Add(milli);
				bool unlocked = EmcAccount.Unlock(itemId);
				EmcStatistics.OnSell(itemId, num);
				return new TradeResult
				{
					Ok = true,
					ItemId = itemId,
					Count = num,
					Milli = milli,
					Unlocked = unlocked,
					Message = "回收 " + ItemName(itemId) + " ×" + num + ",+" + EmcUnit.Format(milli) + " EMC"
				};
			}
			finally
			{
				((IDisposable)(*(TimingScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static TradeResult SellSand()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			TimingScope val = ExchangeStats.MeasureTrade();
			try
			{
				Player me = Me;
				if (me == null)
				{
					return TradeResult.Fail("没有玩家");
				}
				long sandCount = me.sandCount;
				if (sandCount <= 0)
				{
					return TradeResult.Fail("没有沙土可回收");
				}
				long num = EmcPricing.Table.Get(1099);
				if (!EmcPricing.Table.IsPriced(1099))
				{
					return TradeResult.Fail("沙土没有 EMC 价");
				}
				long milli = sandCount * num;
				me.SetSandCount(0L, (ESandSource)3);
				EmcAccount.Add(milli);
				bool unlocked = EmcAccount.Unlock(1099);
				EmcStatistics.OnSell(1099, (int)((sandCount > int.MaxValue) ? int.MaxValue : sandCount));
				return new TradeResult
				{
					Ok = true,
					ItemId = 1099,
					Count = (int)((sandCount > int.MaxValue) ? int.MaxValue : sandCount),
					Milli = milli,
					Unlocked = unlocked,
					Message = "回收沙土 ×" + sandCount + ",+" + EmcUnit.Format(milli) + " EMC"
				};
			}
			finally
			{
				((IDisposable)(*(TimingScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static TradeResult Buy(int itemId, int count)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			TimingScope val = ExchangeStats.MeasureTrade();
			try
			{
				Player me = Me;
				if (me == null)
				{
					return TradeResult.Fail("没有玩家");
				}
				if (count <= 0)
				{
					return TradeResult.Fail("数量必须大于 0");
				}
				EmcTable table = EmcPricing.Table;
				if (table == null)
				{
					return TradeResult.Fail("定价表还没构建完成");
				}
				if (!table.IsPriced(itemId))
				{
					return TradeResult.Fail("「" + ItemName(itemId) + "」没有 EMC 价");
				}
				if (!table.IsTradeable(itemId))
				{
					return TradeResult.Fail("「" + ItemName(itemId) + "」禁止交易");
				}
				if (!IsPurchasable(itemId))
				{
					return TradeResult.Fail("「" + ItemName(itemId) + "」还没解锁:先把手上的同种物品卖进面板");
				}
				long num = table.Get(itemId);
				long milli = num * count;
				if (!EmcAccount.CanAfford(milli))
				{
					return TradeResult.Fail("EMC 不足:需要 " + EmcUnit.Format(milli) + ",现有 " + EmcAccount.BalanceText);
				}
				int num2 = count;
				if (itemId == 1099)
				{
					me.SetSandCount(me.sandCount + count, (ESandSource)3);
				}
				else
				{
					num2 = me.TryAddItemToPackage(itemId, count, 0, false, 0, false);
				}
				if (num2 <= 0)
				{
					return TradeResult.Fail("背包已满,放不下「" + ItemName(itemId) + "」");
				}
				long milli2 = num * num2;
				if (!EmcAccount.TrySpend(milli2))
				{
					Plugin.Log.LogWarning((object)("[EMC] 扣费失败:" + EmcUnit.Format(milli2) + " EMC(物品已入包)"));
				}
				EmcStatistics.OnBuy(itemId, num2);
				return new TradeResult
				{
					Ok = true,
					ItemId = itemId,
					Count = num2,
					Milli = milli2,
					Message = "购买 " + ItemName(itemId) + " ×" + num2 + ",-" + EmcUnit.Format(milli2) + " EMC" + ((num2 < count) ? ("(背包放不下,只放进 " + num2 + " 个)") : "")
				};
			}
			finally
			{
				((IDisposable)(*(TimingScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public static List<PackageEntry> PackageItems()
		{
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			Player me = Me;
			if (me != null && me.package != null && me.package.grids != null)
			{
				GRID[] grids = me.package.grids;
				for (int i = 0; i < grids.Length; i++)
				{
					int itemId = grids[i].itemId;
					int count = grids[i].count;
					if (itemId > 0 && count > 0)
					{
						dictionary.TryGetValue(itemId, out var value);
						dictionary[itemId] = value + count;
					}
				}
			}
			List<PackageEntry> list = new List<PackageEntry>(dictionary.Count);
			foreach (KeyValuePair<int, int> item in dictionary)
			{
				if (IsTradable(item.Key))
				{
					list.Add(new PackageEntry
					{
						ItemId = item.Key,
						Count = item.Value
					});
				}
			}
			list.Sort((PackageEntry a, PackageEntry b) => a.ItemId.CompareTo(b.ItemId));
			return list;
		}

		public static List<int> BuyableItems()
		{
			List<int> list = new List<int>();
			EmcTable table = EmcPricing.Table;
			if (table == null)
			{
				return list;
			}
			if (AllUnlocked)
			{
				for (int i = 1; i < 12000; i++)
				{
					if (table.IsPriced(i) && table.IsTradeable(i))
					{
						list.Add(i);
					}
				}
				return list;
			}
			List<int> list2 = EmcAccount.SnapshotUnlocked();
			for (int j = 0; j < list2.Count; j++)
			{
				int num = list2[j];
				if (table.IsPriced(num) && table.IsTradeable(num))
				{
					list.Add(num);
				}
			}
			return list;
		}
	}
}
namespace DSPEasy.Exchange.Pricing
{
	public static class EmcPricing
	{
		public static Action<string> LogInfo;

		public static Action<string> LogWarn;

		private static EmcTable table;

		private static bool built;

		private static bool failed;

		public static double PriceScale = 1.0;

		public static EmcTable Table => table;

		public static bool Built => built;

		public static void EnsureBuilt()
		{
			if (built || failed || !ProtosReady())
			{
				return;
			}
			try
			{
				Build();
				built = true;
			}
			catch (Exception ex)
			{
				failed = true;
				Warn("[EMC] 定价表构建失败,本局不再重试:" + ex);
			}
		}

		private static bool ProtosReady()
		{
			if (GameMain.data == null)
			{
				return false;
			}
			if ((Object)(object)LDB.items == (Object)null || ((ProtoSet<ItemProto>)(object)LDB.items).dataArray == null || ((ProtoSet<ItemProto>)(object)LDB.items).dataArray.Length == 0)
			{
				return false;
			}
			if ((Object)(object)LDB.recipes == (Object)null || ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray == null)
			{
				return false;
			}
			return true;
		}

		private static void Build()
		{
			EmcTable emcTable = new EmcTable();
			int num = PriceCsv.LoadFixed(emcTable);
			List<int> list = new List<int>();
			int num2 = SeedPrices.Apply(emcTable, list, PriceScale);
			if (!emcTable.IsPinned(1099))
			{
				emcTable.Pin(1099, 10L);
			}
			if (!emcTable.IsPinned(1000))
			{
				emcTable.Pin(1000, 5L);
			}
			Derive(emcTable, out var rounds, out var updates);
			emcTable.Assign(2207, emcTable.Get(2206));
			Dictionary<int, string> notes = BuildNotes(emcTable);
			PriceCsv.Dump(emcTable, notes);
			RecipeDump.Dump();
			int num3 = 0;
			int num4 = 0;
			List<int> list2 = new List<int>();
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val in dataArray)
			{
				if (val != null)
				{
					if (emcTable.IsPriced(((Proto)val).ID))
					{
						num3++;
					}
					else
					{
						list2.Add(((Proto)val).ID);
					}
					if (!emcTable.IsTradeable(((Proto)val).ID))
					{
						num4++;
					}
				}
			}
			table = emcTable;
			Info("[EMC] 定价表已构建:已定价 " + num3 + ",未定价 " + list2.Count + ",禁止交易 " + num4 + ",固定价 " + emcTable.PinnedCount + "(CSV " + num + " / 基价表 " + num2 + "),推导 " + rounds + " 轮 / " + updates + " 次下调");
			if (list2.Count > 0)
			{
				Info("[EMC] 未定价明细:" + DescribeUnpriced(emcTable, list2));
			}
			if (num4 > 0)
			{
				Info("[EMC] 禁止交易(仍参与推导,只挡买卖):" + DescribeUntradeable(emcTable));
			}
			if (list.Count > 0)
			{
				Warn("[EMC] 基价表有 " + list.Count + " 个 ID 在本版本找不到物品:" + JoinIds(list));
			}
			Info("[EMC] 已导出 " + PriceCsv.FilePath + " 与 " + RecipeDump.FilePath);
		}

		private static void Info(string message)
		{
			LogInfo?.Invoke(message);
		}

		private static void Warn(string message)
		{
			LogWarn?.Invoke(message);
		}

		private static void Derive(EmcTable t, out int rounds, out int updates)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			rounds = 0;
			updates = 0;
			int num;
			do
			{
				num = 0;
				foreach (RecipeProto val in dataArray)
				{
					if (val == null || (int)val.Type == 0 || val.Results == null || val.ResultCounts == null || val.Results.Length == 0 || val.Items == null || val.ItemCounts == null)
					{
						continue;
					}
					long num2 = 0L;
					bool flag = true;
					for (int j = 0; j < val.Items.Length; j++)
					{
						int num3 = val.Items[j];
						int num4 = val.ItemCounts[j];
						if (num3 > 0 && num4 > 0)
						{
							long num5 = t.Get(num3);
							if (!EmcUnit.IsPriced(num5))
							{
								flag = false;
								break;
							}
							if (num5 > 144115188075855871L / num4)
							{
								flag = false;
								break;
							}
							num2 += num5 * num4;
							if (num2 > 144115188075855871L)
							{
								flag = false;
								break;
							}
						}
					}
					if (!flag || num2 <= 0)
					{
						continue;
					}
					long num6 = 0L;
					for (int k = 0; k < val.Results.Length; k++)
					{
						int num7 = val.ResultCounts[k];
						if (val.Results[k] > 0 && num7 > 0)
						{
							num6 += num7;
						}
					}
					if (num6 <= 0)
					{
						continue;
					}
					long num8 = num2 / num6;
					if (num8 <= 0)
					{
						continue;
					}
					for (int l = 0; l < val.Results.Length; l++)
					{
						int num9 = val.Results[l];
						if (num9 > 0 && val.ResultCounts[l] > 0)
						{
							num += t.TryLower(num9, num8);
						}
					}
				}
				if (t.Assign(2207, t.Get(2206)))
				{
					num++;
				}
				updates += num;
				rounds++;
			}
			while (num != 0 && rounds < 200);
		}

		private static Dictionary<int, string> BuildNotes(EmcTable t)
		{
			bool[] array = new bool[12000];
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val == null || val.Results == null || val.ResultCounts == null)
				{
					continue;
				}
				int num = 0;
				for (int j = 0; j < val.Results.Length; j++)
				{
					if (val.Results[j] > 0 && val.ResultCounts[j] > 0)
					{
						num++;
					}
				}
				if (num < 2)
				{
					continue;
				}
				for (int k = 0; k < val.Results.Length; k++)
				{
					if (EmcTable.IsValidId(val.Results[k]))
					{
						array[val.Results[k]] = true;
					}
				}
			}
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			ItemProto[] dataArray2 = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val2 in dataArray2)
			{
				if (val2 != null)
				{
					StringBuilder stringBuilder = new StringBuilder();
					if (!t.IsTradeable(((Proto)val2).ID))
					{
						Append(stringBuilder, "禁止交易");
					}
					if (!t.IsPriced(((Proto)val2).ID))
					{
						Append(stringBuilder, "未定价(" + ExplainUnpriced(t, ((Proto)val2).ID) + ")");
					}
					if (EmcTable.IsValidId(((Proto)val2).ID) && array[((Proto)val2).ID])
					{
						Append(stringBuilder, "多产出等分");
					}
					if (t.IsPinned(((Proto)val2).ID) && HasCheaperRecipe(t, ((Proto)val2).ID))
					{
						Append(stringBuilder, "覆盖价高于合成成本,可能被套利");
					}
					if (stringBuilder.Length > 0)
					{
						dictionary[((Proto)val2).ID] = stringBuilder.ToString();
					}
				}
			}
			return dictionary;
		}

		private static string ExplainUnpriced(EmcTable t, int itemId)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			bool flag2 = false;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val == null || (int)val.Type == 0 || val.Results == null || val.ResultCounts == null || val.Items == null)
				{
					continue;
				}
				bool flag3 = false;
				for (int j = 0; j < val.Results.Length; j++)
				{
					if (val.Results[j] == itemId && val.ResultCounts[j] > 0)
					{
						flag3 = true;
						break;
					}
				}
				if (!flag3)
				{
					continue;
				}
				flag = true;
				for (int k = 0; k < val.Items.Length; k++)
				{
					int num = val.Items[k];
					if (num <= 0)
					{
						continue;
					}
					ItemProto val2 = ((ProtoSet<ItemProto>)(object)LDB.items).Select(num);
					string item = ((val2 == null) ? num.ToString() : ((Proto)val2).name);
					if (val.ItemCounts[k] <= 0)
					{
						if (!list2.Contains(item))
						{
							list2.Add(item);
						}
						continue;
					}
					flag2 = true;
					if (!t.IsPriced(num) && !list.Contains(item))
					{
						list.Add(item);
					}
				}
			}
			if (!flag)
			{
				return "无配方";
			}
			if (list.Count > 0)
			{
				if (list.Count > 3)
				{
					return "缺 " + string.Join("、", list.GetRange(0, 3).ToArray()) + " 等 " + list.Count + " 项";
				}
				return "缺 " + string.Join("、", list.ToArray());
			}
			if (!flag2)
			{
				if (list2.Count > 0)
				{
					return "配方原料数量为 0(" + string.Join("、", list2.ToArray()) + ")";
				}
				return "配方没有原料";
			}
			return "配方成本为 0";
		}

		private static bool HasCheaperRecipe(EmcTable t, int itemId)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			long num = t.Get(itemId);
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val == null || val.Results == null || val.ResultCounts == null || (int)val.Type == 0)
				{
					continue;
				}
				bool flag = false;
				for (int j = 0; j < val.Results.Length; j++)
				{
					if (val.Results[j] == itemId && val.ResultCounts[j] > 0)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				long num2 = 0L;
				bool flag2 = true;
				for (int k = 0; k < val.Items.Length; k++)
				{
					int num3 = val.Items[k];
					int num4 = val.ItemCounts[k];
					if (num3 > 0 && num4 > 0)
					{
						long num5 = t.Get(num3);
						if (!EmcUnit.IsPriced(num5))
						{
							flag2 = false;
							break;
						}
						num2 += num5 * num4;
					}
				}
				if (!flag2 || num2 <= 0)
				{
					continue;
				}
				long num6 = 0L;
				for (int l = 0; l < val.Results.Length; l++)
				{
					if (val.Results[l] > 0 && val.ResultCounts[l] > 0)
					{
						num6 += val.ResultCounts[l];
					}
				}
				if (num6 > 0 && num2 / num6 < num)
				{
					return true;
				}
			}
			return false;
		}

		private static string DescribeUnpriced(EmcTable t, List<int> ids)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < ids.Count; i++)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append('、');
				}
				ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(ids[i]);
				stringBuilder.Append((val == null) ? ids[i].ToString() : ((Proto)val).name);
				stringBuilder.Append('(').Append(ids[i]).Append(')');
				stringBuilder.Append("〔").Append(ExplainUnpriced(t, ids[i])).Append('〕');
			}
			return stringBuilder.ToString();
		}

		private static string DescribeUntradeable(EmcTable t)
		{
			StringBuilder stringBuilder = new StringBuilder();
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val in dataArray)
			{
				if (val != null && !t.IsTradeable(((Proto)val).ID))
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append('、');
					}
					stringBuilder.Append(((Proto)val).name).Append('(').Append(((Proto)val).ID)
						.Append(") ")
						.Append(EmcUnit.Format(t.Get(((Proto)val).ID)));
				}
			}
			return stringBuilder.ToString();
		}

		private static string JoinIds(List<int> ids)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < ids.Count; i++)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append('、');
				}
				stringBuilder.Append(ids[i]);
			}
			return stringBuilder.ToString();
		}

		private static void Append(StringBuilder sb, string text)
		{
			if (sb.Length > 0)
			{
				sb.Append(';');
			}
			sb.Append(text);
		}
	}
	public static class EmcUnit
	{
		public const long MilliPerEmc = 1000L;

		public const long Unpriced = long.MaxValue;

		public static bool IsPriced(long milli)
		{
			if (milli != long.MaxValue)
			{
				return milli > 0;
			}
			return false;
		}

		public static string Format(long milli)
		{
			if (!IsPriced(milli))
			{
				return "—";
			}
			return ((double)milli / 1000.0).ToString("0.###");
		}
	}
	public sealed class EmcTable
	{
		private readonly long[] prices = new long[12000];

		private readonly HashSet<int> pinned = new HashSet<int>();

		private readonly HashSet<int> untradeable = new HashSet<int>();

		public int PinnedCount => pinned.Count;

		public int UntradeableCount => untradeable.Count;

		public EmcTable()
		{
			for (int i = 0; i < prices.Length; i++)
			{
				prices[i] = long.MaxValue;
			}
		}

		public static bool IsValidId(int itemId)
		{
			if (itemId > 0)
			{
				return itemId < 12000;
			}
			return false;
		}

		public long Get(int itemId)
		{
			if (!IsValidId(itemId))
			{
				return long.MaxValue;
			}
			return prices[itemId];
		}

		public bool IsPriced(int itemId)
		{
			return EmcUnit.IsPriced(Get(itemId));
		}

		public bool IsPinned(int itemId)
		{
			return pinned.Contains(itemId);
		}

		public void Pin(int itemId, long milli)
		{
			if (IsValidId(itemId) && EmcUnit.IsPriced(milli))
			{
				prices[itemId] = milli;
				pinned.Add(itemId);
			}
		}

		public bool Assign(int itemId, long milli)
		{
			if (!IsValidId(itemId) || !EmcUnit.IsPriced(milli))
			{
				return false;
			}
			if (pinned.Contains(itemId))
			{
				return false;
			}
			if (prices[itemId] == milli)
			{
				return false;
			}
			prices[itemId] = milli;
			return true;
		}

		public void ForbidTrade(int itemId)
		{
			if (IsValidId(itemId))
			{
				untradeable.Add(itemId);
			}
		}

		public bool IsTradeable(int itemId)
		{
			return !untradeable.Contains(itemId);
		}

		public int TryLower(int itemId, long milli)
		{
			if (!IsValidId(itemId) || !EmcUnit.IsPriced(milli))
			{
				return 0;
			}
			if (pinned.Contains(itemId))
			{
				return 0;
			}
			if (milli >= prices[itemId])
			{
				return 0;
			}
			prices[itemId] = milli;
			return 1;
		}
	}
	public static class PriceCsv
	{
		private const string Header = "物品ID(v3),名称,英文键,类型,原矿,液体,建筑,可交易,基价(milli),最终价(milli),备注";

		private const string FormatTag = "(v3)";

		private const string BaseColumn = "基价";

		private const string IdColumn = "物品ID";

		private static readonly HashSet<int> FromCsv = new HashSet<int>();

		public static string FilePath => Path.Combine(Paths.ConfigPath, "DSPEasy.Exchange", "pricing.csv");

		public static int LoadFixed(EmcTable table)
		{
			FromCsv.Clear();
			string filePath = FilePath;
			if (!File.Exists(filePath))
			{
				return 0;
			}
			string[] array;
			try
			{
				array = File.ReadAllLines(filePath, Encoding.UTF8);
			}
			catch (Exception ex)
			{
				Warn("[EMC] 读定价表失败,本次全部走内置基价:" + ex.Message);
				return 0;
			}
			if (array.Length < 2)
			{
				return 0;
			}
			string[] array2 = Split(array[0]);
			int num = -1;
			int num2 = -1;
			for (int i = 0; i < array2.Length; i++)
			{
				if (!string.IsNullOrEmpty(array2[i]) && array2[i].StartsWith("物品ID", StringComparison.Ordinal))
				{
					num = i;
				}
				else if (!string.IsNullOrEmpty(array2[i]) && array2[i].StartsWith("基价", StringComparison.Ordinal))
				{
					num2 = i;
				}
			}
			if (num < 0 || num2 < 0)
			{
				Warn("[EMC] 定价表表头不对(找不到「物品ID」或「基价」列),本次全部走内置基价:" + FilePath);
				return 0;
			}
			if (array2[num].IndexOf("(v3)", StringComparison.Ordinal) < 0)
			{
				Warn("[EMC] 定价表是旧格式(" + array2[num] + "),本次忽略「基价」列、全部走内置基价;如果你在旧表里手填过基价,需要在新表里重填一次:" + FilePath);
				return 0;
			}
			int num3 = 0;
			for (int j = 1; j < array.Length; j++)
			{
				if (!string.IsNullOrEmpty(array[j]))
				{
					string[] array3 = Split(array[j]);
					if (array3.Length > Math.Max(num, num2) && int.TryParse(array3[num].Trim(), out var result) && long.TryParse(array3[num2].Trim(), out var result2) && EmcTable.IsValidId(result))
					{
						table.Pin(result, result2);
						FromCsv.Add(result);
						num3++;
					}
				}
			}
			return num3;
		}

		public static void Dump(EmcTable table, Dictionary<int, string> notes)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("物品ID(v3),名称,英文键,类型,原矿,液体,建筑,可交易,基价(milli),最终价(milli),备注");
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val in dataArray)
			{
				if (val != null)
				{
					long milli = table.Get(((Proto)val).ID);
					stringBuilder.Append(((Proto)val).ID).Append(',');
					stringBuilder.Append(Cell(((Proto)val).name)).Append(',');
					stringBuilder.Append(Cell(((Proto)val).Name)).Append(',');
					stringBuilder.Append(val.Type).Append(',');
					stringBuilder.Append(val.isRaw ? "是" : "").Append(',');
					stringBuilder.Append(val.IsFluid ? "是" : "").Append(',');
					stringBuilder.Append(val.CanBuild ? "是" : "").Append(',');
					stringBuilder.Append(table.IsTradeable(((Proto)val).ID) ? "是" : "否").Append(',');
					stringBuilder.Append(FromCsv.Contains(((Proto)val).ID) ? milli.ToString() : "").Append(',');
					stringBuilder.Append(EmcUnit.IsPriced(milli) ? milli.ToString() : "").Append(',');
					notes.TryGetValue(((Proto)val).ID, out var value);
					stringBuilder.Append(Cell(value));
					stringBuilder.AppendLine();
				}
			}
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
				File.WriteAllText(FilePath, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
			}
			catch (Exception ex)
			{
				Warn("[EMC] 写定价表失败:" + ex.Message);
			}
		}

		private static string Cell(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			return text.Replace(',', ' ').Replace('"', '\'').Replace('\r', ' ')
				.Replace('\n', ' ');
		}

		private static string[] Split(string line)
		{
			return line.Split(',');
		}

		private static void Warn(string message)
		{
			EmcPricing.LogWarn?.Invoke(message);
		}
	}
	public static class RecipeDump
	{
		public static string FilePath { get; private set; }

		public static void Dump()
		{
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(Paths.ConfigPath, "DSPEasy.Exchange");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, "recipes.csv");
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("配方ID,名称,类型,手工,耗时(秒),非产出型,输入,产物,备注");
			StringBuilder stringBuilder2 = new StringBuilder();
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val == null)
				{
					continue;
				}
				stringBuilder2.Length = 0;
				if (val.Items == null || val.Items.Length == 0)
				{
					stringBuilder2.Append("无输入");
				}
				else
				{
					bool flag = false;
					for (int j = 0; j < val.Items.Length; j++)
					{
						if (val.Items[j] > 0 && val.ItemCounts[j] > 0)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						stringBuilder2.Append("原料计数为 0");
					}
				}
				if (val.Results == null || val.Results.Length == 0)
				{
					if (stringBuilder2.Length > 0)
					{
						stringBuilder2.Append(";");
					}
					stringBuilder2.Append("无产物");
				}
				stringBuilder.Append(((Proto)val).ID).Append(',').Append(Escape(((Proto)val).name))
					.Append(',')
					.Append(val.Type)
					.Append(',')
					.Append(val.Handcraft)
					.Append(',')
					.Append(val.TimeSpend)
					.Append(',')
					.Append(val.NonProductive)
					.Append(',')
					.Append(Escape(FormatItems(val.Items, val.ItemCounts)))
					.Append(',')
					.Append(Escape(FormatItems(val.Results, val.ResultCounts)))
					.Append(',')
					.Append(Escape(stringBuilder2.ToString()))
					.AppendLine();
			}
			File.WriteAllText(text2, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
			FilePath = text2;
		}

		private static string FormatItems(int[] ids, int[] counts)
		{
			if (ids == null || ids.Length == 0)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < ids.Length; i++)
			{
				int num = ids[i];
				if (num > 0)
				{
					int value = ((counts != null && i < counts.Length) ? counts[i] : 0);
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append('、');
					}
					ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(num);
					stringBuilder.Append((val == null) ? num.ToString() : ((Proto)val).name);
					stringBuilder.Append('×').Append(value);
				}
			}
			return stringBuilder.ToString();
		}

		private static string Escape(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			if (s.IndexOf(',') < 0 && s.IndexOf('"') < 0 && s.IndexOf('\n') < 0 && s.IndexOf('\r') < 0)
			{
				return s;
			}
			return "\"" + s.Replace("\"", "\"\"") + "\"";
		}
	}
	public static class SeedPrices
	{
		public const int SandPerEmc = 100;

		public const int WaterPerEmc = 200;

		private static readonly Dictionary<int, long> Table = new Dictionary<int, long>
		{
			{ 1001, 100L },
			{ 1002, 120L },
			{ 1005, 60L },
			{ 1006, 80L },
			{ 1003, 250L },
			{ 1004, 500L },
			{ 1000, 5L },
			{ 1007, 60L },
			{ 1116, 80L },
			{ 1120, 20L },
			{ 1011, 300L },
			{ 1012, 400L },
			{ 1013, 400L },
			{ 1014, 500L },
			{ 1015, 350L },
			{ 1016, 2500L },
			{ 1030, 20L },
			{ 1031, 15L },
			{ 1208, 55200L },
			{ 5201, 3000L },
			{ 5202, 400L },
			{ 5203, 600L },
			{ 5204, 2000L },
			{ 5205, 1200L },
			{ 5206, 200L }
		};

		public static int Count => Table.Count;

		public static int Apply(EmcTable table, List<int> missing, double scale = 1.0)
		{
			int num = 0;
			foreach (KeyValuePair<int, long> item in Table)
			{
				if ((Object)(object)LDB.items == (Object)null || !((ProtoSet<ItemProto>)(object)LDB.items).Exist(item.Key))
				{
					missing?.Add(item.Key);
				}
				else if (!table.IsPinned(item.Key))
				{
					long num2 = ((scale == 1.0) ? item.Value : ((long)Math.Round((double)item.Value * scale)));
					if (num2 > 0)
					{
						table.Pin(item.Key, num2);
						num++;
					}
				}
			}
			return num;
		}
	}
}
namespace DSPEasy.Exchange.Persistence
{
	internal static class EmcSavePatches
	{
		[HarmonyPatch(typeof(GameData), "Import")]
		private static class Patch_ClearBeforeImport
		{
			private static void Prefix()
			{
				EmcAccount.Reset();
			}
		}

		[HarmonyPatch(typeof(GameMain), "Start")]
		private static class Patch_ClearOnNewGame
		{
			private static void Prefix()
			{
				EmcAccount.Reset();
			}
		}

		[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
		private static class Patch_WriteSidecar
		{
			private static void Postfix(string saveName, bool __result)
			{
				if (!__result)
				{
					return;
				}
				Stopwatch stopwatch = Stopwatch.StartNew();
				try
				{
					if (ModSave.WriteForSaveName(saveName, "DSPEasyExchange"))
					{
						Plugin.Log.LogInfo((object)$"已写出 EMC 旁车:{ModSave.SidecarPath(ModSave.SavePathOf(saveName))}({stopwatch.ElapsedMilliseconds} ms,余额 {EmcAccount.BalanceText} EMC,解锁 {EmcAccount.UnlockedCount} 项)");
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("写 EMC 旁车失败:" + ex));
				}
			}
		}

		[HarmonyPatch(typeof(GameSave), "LoadCurrentGame")]
		private static class Patch_ReadSidecar
		{
			private static void Postfix(string saveName, bool __result)
			{
				if (!__result)
				{
					return;
				}
				try
				{
					if (ModSave.ReadForSaveName(saveName, "DSPEasyExchange"))
					{
						Plugin.Log.LogInfo((object)string.Format("已读取 EMC 旁车:余额 {0} EMC,解锁 {1} 项({2})", EmcAccount.BalanceText, EmcAccount.UnlockedCount, ModSave.DescribeCounts("DSPEasyExchange")));
					}
					else
					{
						Plugin.Log.LogWarning((object)("没有可用的 EMC 旁车(新档、未配对或校验失败),本局从 0 EMC 开始:" + saveName));
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("读 EMC 旁车失败,按无模组数据处理:" + ex));
					EmcAccount.Reset();
				}
			}
		}

		[HarmonyPatch(typeof(GameSave), "AutoSave")]
		private static class Patch_RotateAutoSaveSidecar
		{
			private static void Postfix()
			{
				try
				{
					RotateSidecars();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("轮转自动存档旁车失败:" + ex));
				}
			}

			private static void RotateSidecars()
			{
				string gameSaveFolder = GameConfig.gameSaveFolder;
				if (string.IsNullOrEmpty(gameSaveFolder))
				{
					return;
				}
				string[] array = new string[5]
				{
					gameSaveFolder + GameSave.AutoSaveTmp + ".mod.dat",
					gameSaveFolder + GameSave.AutoSave0 + ".mod.dat",
					gameSaveFolder + GameSave.AutoSave1 + ".mod.dat",
					gameSaveFolder + GameSave.AutoSave2 + ".mod.dat",
					gameSaveFolder + GameSave.AutoSave3 + ".mod.dat"
				};
				if (!File.Exists(array[0]))
				{
					return;
				}
				if (File.Exists(array[4]))
				{
					File.Delete(array[4]);
				}
				for (int num = 3; num >= 1; num--)
				{
					if (File.Exists(array[num]))
					{
						File.Move(array[num], array[num + 1]);
					}
				}
				File.Move(array[0], array[1]);
			}
		}

		[HarmonyPatch(typeof(UIGameSaveEntry), "DeleteSaveFile")]
		private static class Patch_DeleteSidecar
		{
			private static readonly FieldRef<UIGameSaveEntry, FileInfo> FileInfoRef = AccessTools.FieldRefAccess<UIGameSaveEntry, FileInfo>("fileInfo");

			private static void Postfix(UIGameSaveEntry __instance)
			{
				try
				{
					FileInfo fileInfo = FileInfoRef.Invoke(__instance);
					if (fileInfo != null)
					{
						ModSave.DeleteSidecar(fileInfo.FullName);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("删除存档旁车失败:" + ex.Message));
				}
			}
		}
	}
}