Terminal: home with three entries (Bank, Poinbase, Web3) plus report; entry icons

Bank shows real credits, rent and deposits/withdrawals; Poinbase is the
compliant exchange; Web3 is the wallet and decentralised trading.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-03 15:23:15 +08:00
1 parent ce95ec923d
commit 9441d4d1bd
8 files changed
+211 -23

No files matched your search

Binary file not shown.
+81
View File
@@ -30,6 +30,11 @@ C = {
'Y': (0xC8, 0xA4, 0x4A), # 金手指 'Y': (0xC8, 0xA4, 0x4A), # 金手指
'c': (0x2E, 0x2A, 0x2A), # 焦黑 'c': (0x2E, 0x2A, 0x2A), # 焦黑
'e': (0x6B, 0x4A, 0x3A), # 焦痕 'e': (0x6B, 0x4A, 0x3A), # 焦痕
'u': (0x4F, 0x86, 0xD0), # 蓝
'U': (0x2B, 0x4A, 0x82), # 深蓝
'v': (0x9B, 0x76, 0xD6), # 紫
'V': (0x5E, 0x45, 0x8F), # 深紫
'y': (0xF0, 0xCF, 0x64), # 亮金
'p': (0xC0, 0x7A, 0x4A), # 铜 'p': (0xC0, 0x7A, 0x4A), # 铜
'q': (0x8A, 0x4F, 0x34), # 暗铜 'q': (0x8A, 0x4F, 0x34), # 暗铜
'w': (0xE6, 0xE2, 0xD0), # 白 'w': (0xE6, 0xE2, 0xD0), # 白
@@ -514,6 +519,76 @@ def key(model):
return model.replace(' ', '').lower() return model.replace(' ', '').lower()
def ui_bank():
"""银行图标(32×32):带山花的石柱建筑,门前一枚金币。"""
c = Canvas(32, 32)
for k in range(0, 14): # 山花(三角)
c.rect(16 - k, 4 + k // 2, 16 + k, 4 + k // 2, 'h' if k < 3 else 'l')
c.rect(2, 11, 29, 12, 'b'); c.rect(2, 13, 29, 13, 'd') # 檐口
for x0 in (4, 10, 16, 22): # 四根柱子
c.rect(x0, 14, x0 + 3, 24, 'l')
c.rect(x0, 14, x0, 24, 'h'); c.rect(x0 + 3, 14, x0 + 3, 24, 'd')
c.rect(x0 - 1, 14, x0 + 4, 14, 'b'); c.rect(x0 - 1, 24, x0 + 4, 24, 'b')
c.rect(1, 25, 30, 26, 'b'); c.rect(0, 27, 31, 28, 'm'); c.rect(0, 29, 31, 29, 'd')
c.rect(14, 8, 17, 9, 'y'); c.put(15, 8, 'w') # 山花里的小徽标
for yy in range(19, 27): # 金币
for xx in range(23, 31):
d = (xx - 26.5) ** 2 + (yy - 22.5) ** 2
if d <= 14:
c.put(xx, yy, 'e' if d > 9 else 'y')
c.put(25, 21, 'w'); c.put(26, 21, 'w'); c.put(26, 22, 'y')
outline(c)
return c
def ui_poinbase():
"""Poinbase 图标(32×32):蓝色盾牌,中间一枚金色 P 币,两侧一上一下的箭头。"""
c = Canvas(32, 32)
for y in range(3, 29): # 盾牌
half = 12 if y < 16 else max(0, 12 - (y - 16) * 12 // 12)
for x in range(16 - half, 16 + half + 1):
c.put(x, y, 'u' if x < 16 else 'U')
c.put(16 - half, y, 'h') if half else None
for x in range(4, 29):
c.put(x, 3, 'h'); c.put(x, 4, 'l')
for y in range(7, 21): # 币
for x in range(9, 24):
d = (x - 16) ** 2 + (y - 13.5) ** 2
if d <= 36:
c.put(x, y, 'e' if d > 28 else 'y')
c.put(12, 9, 'w'); c.put(13, 8, 'w'); c.put(12, 10, 'w')
c.rect(13, 9, 14, 18, 'U'); c.rect(15, 9, 18, 10, 'U'); c.rect(18, 11, 19, 13, 'U'); c.rect(15, 14, 18, 15, 'U') # P
for k in range(4): # 下方两个箭头:买入、卖出
c.put(9 + k, 24 - k, 'w') if k < 3 else None
for x in range(8, 13): c.put(x, 22, 'w')
for x in range(19, 24): c.put(x, 22, 'w')
c.put(19, 23, 'w'); c.put(20, 24, 'w'); c.put(23, 23, 'w'); c.put(22, 24, 'w')
outline(c)
return c
def ui_web3():
"""Web3 图标(32×32):六边形节点网络,紫色底。"""
c = Canvas(32, 32)
nodes = [(16, 7), (7, 14), (25, 14), (11, 24), (21, 24), (16, 16)]
edges = [(0, 1), (0, 2), (1, 3), (2, 4), (3, 4), (5, 0), (5, 1), (5, 2), (5, 3), (5, 4)]
for (a, b) in edges:
(x0, y0), (x1, y1) = nodes[a], nodes[b]
n = max(abs(x1 - x0), abs(y1 - y0))
for i in range(n + 1):
px, py = x0 + (x1 - x0) * i // n, y0 + (y1 - y0) * i // n
c.put(px, py, 'v'); c.put(px + 1, py, 'v'); c.put(px, py + 1, 'v')
for i, (x, y) in enumerate(nodes):
r = 3 if i == 5 else 2
for dy in range(-r, r + 1):
for dx in range(-r, r + 1):
if dx * dx + dy * dy <= r * r + 1:
c.put(x + dx, y + dy, 'V' if (dx + dy) > 0 else 'h' if i != 5 else 'y')
c.put(x, y, 'w')
outline(c)
return c
def guide(): def guide():
"""鉴定手册(16×16,一格):竖放的小册子,封面带品牌色条,书脊在左。""" """鉴定手册(16×16,一格):竖放的小册子,封面带品牌色条,书脊在左。"""
c = Canvas(16, 16) c = Canvas(16, 16)
@@ -542,6 +617,8 @@ for _tier in range(4):
SPRITES['gpu_probidia_f_t%d' % _tier] = (lambda t=_tier: gpu_front('G', 'g2', tier=t)) SPRITES['gpu_probidia_f_t%d' % _tier] = (lambda t=_tier: gpu_front('G', 'g2', tier=t))
SPRITES['gpu_pmd_f_t%d' % _tier] = (lambda t=_tier: gpu_front('R', 'r2', tier=t)) SPRITES['gpu_pmd_f_t%d' % _tier] = (lambda t=_tier: gpu_front('R', 'r2', tier=t))
UI_ICONS = {'ui_bank': ui_bank, 'ui_poinbase': ui_poinbase, 'ui_web3': ui_web3}
# 叠加图:和底图同尺寸,位置 (0,0) 对齐,只画变化的像素,不参与判格 # 叠加图:和底图同尺寸,位置 (0,0) 对齐,只画变化的像素,不参与判格
OVERLAYS = { OVERLAYS = {
'gpu_ov_fin1': lambda: ov_fingers(1), 'gpu_ov_fin2': lambda: ov_fingers(2), 'gpu_ov_fin1': lambda: ov_fingers(1), 'gpu_ov_fin2': lambda: ov_fingers(2),
@@ -581,6 +658,8 @@ if __name__ == '__main__':
if oc.px[y][x] is not None: if oc.px[y][x] is not None:
b.px[y][x] = oc.px[y][x] b.px[y][x] = oc.px[y][x]
return b return b
for name, make in UI_ICONS.items():
make().png(os.path.join(sys.argv[2], name + '.png'), scale=8, bg=(0x2a, 0x2c, 0x34))
stack('gpu_probidia_f_t1', 'gpu_ov_fan2_2', 'gpu_ov_fin1', 'gpu_ov_brk1').png(os.path.join(sys.argv[2], 'prev_front_worn.png'), scale=6, bg=(0xA0, 0xA8, 0x9C)) stack('gpu_probidia_f_t1', 'gpu_ov_fan2_2', 'gpu_ov_fin1', 'gpu_ov_brk1').png(os.path.join(sys.argv[2], 'prev_front_worn.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
print(sys.argv[2]) print(sys.argv[2])
else: else:
@@ -594,6 +673,8 @@ if __name__ == '__main__':
canvas.png(os.path.join(out, name + '.png')) canvas.png(os.path.join(out, name + '.png'))
for name, make in OVERLAYS.items(): for name, make in OVERLAYS.items():
make().png(os.path.join(out, name + '.png')) make().png(os.path.join(out, name + '.png'))
for name, make in UI_ICONS.items():
make().png(os.path.join(out, name + '.png'))
import big, big_cases import big, big_cases
for name, make in big.BIG.items(): for name, make in big.BIG.items():
make().png(os.path.join(out, name + '.png')) make().png(os.path.join(out, name + '.png'))
+21 -2
View File
@@ -18,8 +18,6 @@ item.gpu.desc,"Rated hashrate {0} MH/s. Rated draw {1} kJ per day. Condition: {2
item.gpu.burnt.name,Burnt {0} {1} item.gpu.burnt.name,Burnt {0} {1}
item.gpu.burnt.desc,This card no longer works. item.gpu.burnt.desc,This card no longer works.
term.title,Trading Terminal term.title,Trading Terminal
tab.wallet,Wallet
tab.trade,Trade
tab.report,Report tab.report,Report
wallet.line,"{0}: {1} (about {2} credits)" wallet.line,"{0}: {1} (about {2} credits)"
wallet.empty,The wallet is empty. wallet.empty,The wallet is empty.
@@ -129,3 +127,24 @@ lbl.ev.print.1,"Print shows another model"
lbl.ev.print.2,"Print was redone" lbl.ev.print.2,"Print was redone"
news.prehalving.title,"PSC Halving Ahead" news.prehalving.title,"PSC Halving Ahead"
news.prehalving.desc,"Miners will receive half as many coins per night in three days. Holders expect the price to rise." news.prehalving.desc,"Miners will receive half as many coins per night in three days. Holders expect the price to rise."
tab.home,"Home"
tab.bank,"Bank"
tab.poinbase,"Poinbase"
tab.web3,"Web3"
home.enter,"Enter"
home.summary,"Credits {0} | On-chain assets about {1} credits"
home.lastnight,"Night of day {0}: mined {1} PSC, {2} / {3} rigs powered"
entry.bank,"Bank"
entry.bank.l1,"Deposits, withdrawals, rent"
entry.bank.l2,"Credits {0}"
entry.poinbase,"Poinbase"
entry.poinbase.l1,"Compliant exchange"
entry.poinbase.l2,"PSC {0} credits"
entry.web3,"Web3"
entry.web3.l1,"Wallet and decentralised trading"
entry.web3.l2,"About {0} credits"
bank.cash,"Credits: {0}"
bank.rent,"Rent: {0} credits, due in {1} days"
bank.crc,"Poinbase account (CRC): {0}"
bank.hint,"Deposit turns credits into CRC; withdrawing turns CRC back into credits and passes compliance review."
poin.account,"Poinbase account: CRC {0}, PSC {1}"
1 # Probably Decentralized localization table: English (en)
18 item.gpu.burnt.name
19 item.gpu.burnt.desc
20 term.title
tab.wallet
tab.trade
21 tab.report
22 wallet.line
23 wallet.empty
127 lbl.ev.print.2
128 news.prehalving.title
129 news.prehalving.desc
130 tab.home
131 tab.bank
132 tab.poinbase
133 tab.web3
134 home.enter
135 home.summary
136 home.lastnight
137 entry.bank
138 entry.bank.l1
139 entry.bank.l2
140 entry.poinbase
141 entry.poinbase.l1
142 entry.poinbase.l2
143 entry.web3
144 entry.web3.l1
145 entry.web3.l2
146 bank.cash
147 bank.rent
148 bank.crc
149 bank.hint
150 poin.account
+21 -2
View File
@@ -17,8 +17,6 @@ item.gpu.desc,标称算力 {0} MH/s。标称耗电 {1} kJ/天。成色 {2}。
item.gpu.burnt.name,烧毁的 {0} {1} item.gpu.burnt.name,烧毁的 {0} {1}
item.gpu.burnt.desc,此显卡已无法工作。 item.gpu.burnt.desc,此显卡已无法工作。
term.title,交易终端 term.title,交易终端
tab.wallet,钱包
tab.trade,交易
tab.report,报告 tab.report,报告
wallet.line,{0}:{1}(约 {2} 信用点) wallet.line,{0}:{1}(约 {2} 信用点)
wallet.empty,钱包为空。 wallet.empty,钱包为空。
@@ -128,3 +126,24 @@ lbl.ev.print.1,"丝印印着另一个型号"
lbl.ev.print.2,"丝印重印过" lbl.ev.print.2,"丝印重印过"
news.prehalving.title,"PSC 即将减半" news.prehalving.title,"PSC 即将减半"
news.prehalving.desc,"三天后矿工每晚获得的币减半。持币者预期币价上行。" news.prehalving.desc,"三天后矿工每晚获得的币减半。持币者预期币价上行。"
tab.home,"首页"
tab.bank,"银行"
tab.poinbase,"Poinbase"
tab.web3,"Web3"
home.enter,"进入"
home.summary,"信用点 {0} | 链上资产约 {1} 信用点"
home.lastnight,"第 {0} 天夜间:挖出 {1} PSC,{2} / {3} 个矿架有电"
entry.bank,"银行"
entry.bank.l1,"信用点存取、房租"
entry.bank.l2,"信用点 {0}"
entry.poinbase,"Poinbase"
entry.poinbase.l1,"合规交易所"
entry.poinbase.l2,"PSC {0} 信用点"
entry.web3,"Web3"
entry.web3.l1,"钱包与去中心化交易"
entry.web3.l2,"约 {0} 信用点"
bank.cash,"信用点:{0}"
bank.rent,"房租:{0} 信用点,{1} 天后支付"
bank.crc,"Poinbase 账户(CRC):{0}"
bank.hint,"存入把信用点换成 CRC,提取把 CRC 换回信用点,提取要过合规审查。"
poin.account,"Poinbase 账户:CRC {0},PSC {1}"
1 # Probably Decentralized 本地化表:简体中文(zh)
17 item.gpu.burnt.name
18 item.gpu.burnt.desc
19 term.title
tab.wallet
tab.trade
20 tab.report
21 wallet.line
22 wallet.empty
126 lbl.ev.print.2
127 news.prehalving.title
128 news.prehalving.desc
129 tab.home
130 tab.bank
131 tab.poinbase
132 tab.web3
133 home.enter
134 home.summary
135 home.lastnight
136 entry.bank
137 entry.bank.l1
138 entry.bank.l2
139 entry.poinbase
140 entry.poinbase.l1
141 entry.poinbase.l2
142 entry.web3
143 entry.web3.l1
144 entry.web3.l2
145 bank.cash
146 bank.rent
147 bank.crc
148 bank.hint
149 poin.account
Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

+88 -19
View File
@@ -6,13 +6,16 @@ using Decentralized.Domain;
namespace Decentralized namespace Decentralized
{ {
/// <summary>交易终端窗口:钱包、交易、报告三页。每次操作后按当前页重建窗口。</summary> /// <summary>
/// 交易终端窗口:首页三个入口(银行、Poinbase、Web3)加报告。银行是真实的信用点(存取、房租);
/// Poinbase 是合规交易所;Web3 是去中心化交易和链上钱包。每次操作后按当前页重建窗口。
/// </summary>
public static class TerminalWindow public static class TerminalWindow
{ {
public const string Id = "pd_terminal"; public const string Id = "pd_terminal";
private enum Page { Wallet, Trade, Report } private enum Page { Home, Bank, Poinbase, Web3, Report }
private static Page _page = Page.Wallet; private static Page _page = Page.Home;
private static string _amount = ""; private static string _amount = "";
private static string _message; private static string _message;
@@ -21,7 +24,7 @@ namespace Decentralized
if (Session.Data == null) return; if (Session.Data == null) return;
var b = CustomUIManager.Window(Id, L.T("term.title")); var b = CustomUIManager.Window(Id, L.T("term.title"));
if (b == null) return; if (b == null) return;
b.SetSize(620, 560).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle(); b.SetSize(640, 580).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
b.BeginRow(); b.BeginRow();
foreach (Page p in Enum.GetValues(typeof(Page))) foreach (Page p in Enum.GetValues(typeof(Page)))
{ {
@@ -31,11 +34,13 @@ namespace Decentralized
} }
b.End(); b.End();
b.AddSeparator(); b.AddSeparator();
b.BeginScroll(440); b.BeginScroll(450);
switch (_page) switch (_page)
{ {
case Page.Wallet: WalletPage(b); break; case Page.Home: HomePage(b); break;
case Page.Trade: TradePage(b); break; case Page.Bank: BankPage(b); break;
case Page.Poinbase: ExchangePage(b, Venue.Exchange); break;
case Page.Web3: Web3Page(b); break;
default: ReportPage(b); break; default: ReportPage(b); break;
} }
b.End(); b.End();
@@ -43,32 +48,67 @@ namespace Decentralized
b.Show(); b.Show();
} }
private static void WalletPage(CustomUIBuilder b) private static void Go(Page p) { _page = p; _message = null; Open(); }
private static void Entry(CustomUIBuilder b, string icon, string name, string line1, string line2, Page target)
{ {
var w = Session.Data.Hot; b.BeginPanel(190f, 250f, TextAnchor.UpperCenter, 10f, 6f);
if (w.Balances.Count == 0) b.AddLabel(L.T("wallet.empty")); b.AddImage(ModInfo.Id + ":" + icon, 96f, 96f);
foreach (var kv in w.Balances.OrderBy(k => k.Key)) b.AddHeader(name);
b.AddLabel(L.T("wallet.line", kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key)))); b.AddLabel(line1);
b.AddSeparator(); b.AddLabel(line2);
b.AddLabel(L.T("wallet.total", Fmt.Credits(w.Value(Session.Price)))); b.AddFlexibleSpace();
if (w.Taint > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint))); b.AddButton(L.T("home.enter"), () => Go(target));
b.End();
} }
private static void TradePage(CustomUIBuilder b) private static void HomePage(CustomUIBuilder b)
{
var d = Session.Data;
double chain = d.Hot.Value(Session.Price);
b.AddLabel(L.T("home.summary", PlayerStore.Instance.playerCash, Fmt.Credits(chain)));
b.AddSpace(6f);
b.BeginRow(8f, TextAnchor.UpperCenter);
Entry(b, "ui_bank", L.T("entry.bank"), L.T("entry.bank.l1"), L.T("entry.bank.l2", PlayerStore.Instance.playerCash), Page.Bank);
Entry(b, "ui_poinbase", L.T("entry.poinbase"), L.T("entry.poinbase.l1"), L.T("entry.poinbase.l2", Fmt.Num(d.Market.Price)), Page.Poinbase);
Entry(b, "ui_web3", L.T("entry.web3"), L.T("entry.web3.l1"), L.T("entry.web3.l2", Fmt.Credits(chain)), Page.Web3);
b.End();
b.AddSpace(8f);
b.AddSeparator();
var r = d.LastNight;
b.AddLabel(r == null ? L.T("report.none") : L.T("home.lastnight", r.Day, Fmt.Num(r.Mined), r.PoweredRigs, r.Rigs));
}
private static void AmountRow(CustomUIBuilder b)
{ {
b.AddLabel(L.T("trade.hint"));
b.BeginRow(); b.BeginRow();
b.AddLabel(L.T("trade.amount")); b.AddLabel(L.T("trade.amount"));
b.BeginPanel(240f, -1f, TextAnchor.MiddleCenter, 0f, 0f); // 给输入框一个固定宽度,容得下大额数字 b.BeginPanel(240f, -1f, TextAnchor.MiddleCenter, 0f, 0f); // 给输入框一个固定宽度,容得下大额数字
b.AddInput("0", _amount, s => _amount = s, "amount", s => _amount = s); b.AddInput("0", _amount, s => _amount = s, "amount", s => _amount = s);
b.End(); b.End();
b.End(); b.End();
}
/// <summary>银行:真实的信用点。现金、房租,以及信用点与 Poinbase 账户(CRC)之间的存取。</summary>
private static void BankPage(CustomUIBuilder b)
{
var ps = PlayerStore.Instance;
b.AddHeader(L.T("entry.bank"));
b.AddLabel(L.T("bank.cash", ps.playerCash));
b.AddLabel(L.T("bank.rent", Session.Rent(), ps.GetRentDayCounter()));
b.AddLabel(L.T("bank.crc", Fmt.Num(Session.Data.Hot.Get(Assets.CRC))));
b.AddSeparator();
b.AddLabel(L.T("bank.hint"));
AmountRow(b);
b.BeginRow(); b.BeginRow();
b.AddButton(L.T("trade.deposit"), Deposit); b.AddButton(L.T("trade.deposit"), Deposit);
b.AddButton(L.T("trade.withdraw"), Withdraw); b.AddButton(L.T("trade.withdraw"), Withdraw);
b.End(); b.End();
b.AddSeparator(); }
foreach (var pool in Session.Data.Market.Pools)
private static void TradeRows(CustomUIBuilder b, Venue venue)
{
foreach (var pool in Session.Data.Market.Pools.Where(x => x.Venue == venue))
{ {
var p = pool; var p = pool;
b.AddLabel(L.T("trade.pair", p.Base, p.Quote, L.T("venue." + p.Venue)) + " " + L.T("trade.price", Fmt.Num(p.Price))); b.AddLabel(L.T("trade.pair", p.Base, p.Quote, L.T("venue." + p.Venue)) + " " + L.T("trade.price", Fmt.Num(p.Price)));
@@ -79,6 +119,35 @@ namespace Decentralized
} }
} }
/// <summary>Poinbase:合规交易所。账户余额与交易对;信用点的存取在银行。</summary>
private static void ExchangePage(CustomUIBuilder b, Venue venue)
{
var w = Session.Data.Hot;
b.AddHeader(L.T("entry.poinbase"));
b.AddLabel(L.T("poin.account", Fmt.Num(w.Get(Assets.CRC)), Fmt.Num(w.Get(Assets.PSC))));
b.AddLabel(L.T("trade.hint"));
AmountRow(b);
b.AddSeparator();
TradeRows(b, venue);
}
/// <summary>Web3:链上钱包与去中心化交易。</summary>
private static void Web3Page(CustomUIBuilder b)
{
var w = Session.Data.Hot;
b.AddHeader(L.T("entry.web3"));
if (w.Balances.Count == 0) b.AddLabel(L.T("wallet.empty"));
foreach (var kv in w.Balances.OrderBy(k => k.Key))
b.AddLabel(L.T("wallet.line", kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key))));
b.AddLabel(L.T("wallet.total", Fmt.Credits(w.Value(Session.Price))));
if (w.Taint > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
b.AddSeparator();
b.AddLabel(L.T("trade.hint"));
AmountRow(b);
b.AddSeparator();
TradeRows(b, Venue.Dex);
}
private static void ReportPage(CustomUIBuilder b) private static void ReportPage(CustomUIBuilder b)
{ {
var r = Session.Data.LastNight; var r = Session.Data.LastNight;