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));
}
}
}
}
}