2.1.0:Web3 商店周一周四上新、NPC 买家、商家价格调整;换币客户报价;PLEDGER 读条与余额表;清洗效率;修正模块运转效果;删除假客服

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Opus 5.5 committed 2026-11-09 01:35:10 +08:00
1 parent 685762d110
commit 528886b633
26 files changed
+224 -202

No files matched your search

+39 -30
View File
@@ -20,7 +20,12 @@ namespace Decentralized
public static class WalletWindow
{
private const string IdPrefix = "pd_wallet_";
private const float WinW = 452f, WinH = 694f, ScreenW = 384f, ScreenH = 540f, Bezel = 6f;
private const float WinW = 452f, WinH = 694f, ScreenW = 384f, ScreenH = 540f, Bezel = 6f, InputH = 32f, BalHeaderH = 24f;
/// <summary>主页余额表的列:币种、数量、脏币、估值。</summary>
private static readonly float[] BalW = { 66f, 100f, 104f, ScreenW - 40f - 270f };
private static readonly TextAnchor[] BalAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleLeft, TextAnchor.MiddleRight };
/// <summary>发送页的"最近"列表最多几条;有待付账单时多出账单和填入键两行,少列几条。</summary>
private const int RecentMax = 4, RecentMaxWithBill = 2;
private enum Screen { Home, Send, Review, Broadcast, Done }
/// <summary>终端开出的账单:转账(有收款地址)或合约调用(DEX 兑换,没有地址)。付清后用付款的钱包执行。</summary>
@@ -87,14 +92,18 @@ namespace Decentralized
/// <summary>手续费折成币的数量。</summary>
private static double FeeIn(string asset, FeeTier tier) { double p = Session.Price(asset); return p > 0 ? TxRules.FeeCredits(tier, Congested, Session.Data.Gas) / p : 0; }
/// <summary>出块动画的时间表:签名 0.8 秒,内存池 1.2 秒,打包 1 秒,每次确认 0.6 秒;拥堵时按档位放慢。</summary>
private static float[] Timeline(int confs, double slow, bool dropped)
/// <summary>出块动画的时间表:签名、内存池、打包、每次确认按 0.8 : 1.2 : 1 : 0.6 的比例分配,整条读条共 secs 秒;被挤出内存池时只有前两段再加一段等待。</summary>
private static float[] Timeline(int confs, double secs, bool dropped)
{
var t = new List<float> { 0.8f * (float)slow, (0.8f + 1.2f) * (float)slow };
if (dropped) { t.Add(t[1] + 1.5f * (float)slow); return t.ToArray(); }
t.Add(t[1] + 1.0f * (float)slow);
for (int i = 0; i < confs; i++) t.Add(t[t.Count - 1] + 0.6f * (float)slow);
return t.ToArray();
var t = new List<float> { 0.8f, 0.8f + 1.2f };
if (dropped) t.Add(t[1] + 1.5f);
else
{
t.Add(t[1] + 1.0f);
for (int i = 0; i < confs; i++) t.Add(t[t.Count - 1] + 0.6f);
}
float k = (float)secs / t[t.Count - 1];
return t.Select(x => x * k).ToArray();
}
@@ -242,26 +251,25 @@ namespace Decentralized
b.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(w.Address); dev.Note = L.T("hw.copied"); Open(dev); });
b.End();
b.AddSeparator();
// 余额:每个币一行,左边币种、右边数量与估值,全部显示(含 0);点一行选中,发送时默认用它
// 余额:币种 | 数量 / 脏币 | 估值,全部显示(含 0);数量右对齐、脏币左对齐,斜杠上下对齐;点一行选中,发送时默认用它
PledgerSkin.TableHeader(b, RowW, BalHeaderH, BalW, BalAligns, new[] { L.T("col.asset"), L.T("col.amount"), " / " + L.T("col.dirty"), L.T("col.value") }, _clear);
foreach (var a in SendAssets)
{
double q = w.Get(a);
double q = w.Get(a), dq = w.DirtyOf(a);
b.BeginPanel(RowW, 32f, TextAnchor.MiddleLeft, 6f, 0f);
_rows.Add((b.Last.root, b.Last.image, a));
b.BeginRow(0f, TextAnchor.MiddleLeft);
b.BeginPanel(90f, 26f, TextAnchor.MiddleLeft, 0f, 0f);
_clear.Add(b.Last.image);
b.AddLabel(a);
b.End();
b.BeginPanel(130f, 26f, TextAnchor.MiddleRight, 0f, 0f);
_clear.Add(b.Last.image);
double dq = w.DirtyOf(a);
b.AddLabel(Amt(q) + (w.Frozen.TryGetValue(a, out var fz) && fz > 0 ? L.T("hw.balance.frozen", Amt(fz)) : "") + (dq > 1e-9 ? L.T("hw.balance.dirty", Amt(dq)) : ""));
b.End();
b.BeginPanel(RowW - 12f - 90f - 130f, 26f, TextAnchor.MiddleRight, 0f, 0f);
_clear.Add(b.Last.image);
b.AddLabel(L.T("hw.balance.value", Fmt.Credits(q * Session.Price(a))));
b.End();
string dirty = dq > 1e-9 ? $"<color={(a == dev.Asset ? PledgerSkin.DirtyOnKey : PledgerSkin.DirtyOnPaper)}> / {Amt(dq)}</color>" : "";
string frozen = w.Frozen.TryGetValue(a, out var fz) && fz > 0 ? L.T("hw.balance.frozen", Amt(fz)) : "";
var cells = new[] { a, Amt(q), dirty + frozen, L.T("hw.balance.value", Fmt.Credits(q * Session.Price(a))) };
for (int i = 0; i < cells.Length; i++)
{
b.BeginPanel(BalW[i], 26f, BalAligns[i], 0f, 0f);
_clear.Add(b.Last.image);
b.AddLabel(cells[i]);
PledgerSkin.FitCurrent(b);
b.End();
}
b.End();
b.End();
}
@@ -305,13 +313,17 @@ namespace Decentralized
b.AddLabel(L.T("hw.avail", dev.Asset, Amt(w.Avail(dev.Asset))), "avail_dim");
b.AddLabel(L.T("hw.amount"));
b.AddInput("0", dev.Amount, s => dev.Amount = s, "amount", s => dev.Amount = s);
if (Pending != null && !Pending.Contract)
// 输入框没有最小高度,屏幕放不下时会被纵向布局压扁
var amountLayout = b.Last.root.GetComponent<LayoutElement>() ?? b.Last.root.AddComponent<LayoutElement>();
amountLayout.minHeight = InputH; amountLayout.preferredHeight = InputH; amountLayout.flexibleHeight = 0f;
bool hasBill = Pending != null && !Pending.Contract;
if (hasBill)
{
b.AddLabel(L.T("hw.bill", Pending.Summary, Amt(Pending.Amount), Pending.Asset), "bill_dim");
b.AddButton(L.T("hw.fill"), () => { dev.Asset = Pending.Asset; dev.Amount = Amt(Pending.Amount); Open(dev); });
}
// 最近转账:点一下填入地址(地址投毒的 0 币转入也混在这里,缩写看起来一样)
var recent = w.History.GroupBy(r => r.Addr + (r.In ? "<" : ">")).Select(g => g.First()).Take(4).ToList();
var recent = w.History.GroupBy(r => r.Addr + (r.In ? "<" : ">")).Select(g => g.First()).Take(hasBill ? RecentMaxWithBill : RecentMax).ToList();
if (recent.Count > 0)
{
b.AddLabel(L.T("hw.recent"), "recent_dim");
@@ -422,8 +434,6 @@ namespace Decentralized
b.AddHeader(L.T("hw.tx.h"));
b.AddLabel(StatusText(dev), "status");
b.AddProgressBar(Progress(dev), "progress");
b.AddSpace(10f);
b.AddButton(L.T("hw.skip"), () => Finish(dev));
}
private static void DoneScreen(CustomUIBuilder b, Device dev)
@@ -542,7 +552,7 @@ namespace Decentralized
dev.Tx.Fee = FeeIn(dev.Tx.FeeAsset, dev.Tier);
dev.Tx.Confs = TxRules.Confirmations(ValueOf(dev));
dev.Tx.Dropped = d.Rng.Chance(TxRules.DropChance(dev.Tier, cong));
dev.Tx.Times = Timeline(dev.Tx.Confs, TxRules.Slowdown(dev.Tier, cong), dev.Tx.Dropped);
dev.Tx.Times = Timeline(dev.Tx.Confs, Tuning.TxBarSecs * TxRules.Slowdown(dev.Tier, cong), dev.Tx.Dropped);
if (dev.Tx.Bill != null)
{
if (dev.Tx.Bill == OtcClient.Standing) OtcClient.Standing = null; // 签了名就不再挂着,免得重复付
@@ -553,7 +563,7 @@ namespace Decentralized
Open(dev);
}
/// <summary>动画走完(或点了跳过):交易落地。付账单的执行终端那边的动作;转给自己的钱包的按比例带上污点,转给 Poinbase 的只转干净的、污点留在钱包;没人用的地址,币就没了。</summary>
/// <summary>动画走完:交易落地。付账单的执行终端那边的动作;转给自己的钱包的按比例带上污点,转给 Poinbase 的只转干净的、污点留在钱包;没人用的地址,币就没了。</summary>
private static void Finish(Device dev)
{
var tx = dev.Tx;
@@ -606,7 +616,6 @@ namespace Decentralized
if (target == d.Poinbase) Wallet.TransferClean(w, target, tx.Asset, amt, Session.Price);
else Wallet.Transfer(w, target, tx.Asset, amt, Session.Price);
if (own == null && target != d.Poinbase) Log.Info($"转进没人用的地址 {tx.To}:{amt} {tx.Asset}");
if (own == null && d.Scams.ScamAddrs.Any(a => ChainAddress.Same(a, tx.To))) PlayerStore.Instance.AddNightLog(L.T("nightlog.scam.sent", Fmt.Num(amt), tx.Asset), "cc7755");
TerminalWindow.Refresh(null);
if (own != null) RefreshDevice(own);
});