17 Commits
Author SHA1 Message Date
ZHU MINCHAOandClaude Sonnet 5.5 45f73580a5 Changelog: phase 1 complete
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:45:50 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 e95588a76f Terminal: stablecoin price/status table in Web3; AML preview (window total, taint share, review chance) in the bank withdrawal
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:44:49 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 00f7811e7c Terminal: two-column pages with table cells (holdings on the left, trading on the right)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:41:42 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 a2dc1dd2f6 Terminal: buy-average price per coin in Poinbase and Web3; drop the price line on the Poinbase card
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:35:48 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 9441d4d1bd 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>
2026-11-03 15:23:15 +08:00
ZHU MINCHAO ce95ec923d Terminal: fixed-width amount field 2026-11-03 15:15:19 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 0560c34231 Halving now raises the price anchor (pass-through 0.6, ramp the week before) with a pre-halving news item
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:13:57 +08:00
ZHU MINCHAO 967077a2db comment: 50% 2026-11-03 15:06:03 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 4b10c6ad7f Buyer spots each hidden flaw with 50% chance
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:06:00 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 9165c9edf4 Reputation loss when a buyer catches a false label scales with the price gap (per weekly rent, 1 to 25); drop old-save model fallback
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 15:03:52 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 393dd8057a Seller claim lapses when the card is bought unless edited with a labeler; test helpers for sell and buy sides
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:57:28 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 f564a895d0 Label bar: one model-authenticity row with a hint line; evidence listed after exposure
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:30:47 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 d6a456c986 Exposing the model reveals all four authenticity rows
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:28:39 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 559a702ee3 Label bar: authenticity rows first, quality rows second; single-fan brand text moved off the fan
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:19:43 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 7f458b0139 Tiers drive physical traits and serial checks; guide item
Fans (1/2/3/3) and power connectors (6/8/8/8+8) follow the real chip's tier,
so a fake shows its real tier physically. Serial numbers (brand prefix, tier
code, factory code, four digits) are drawn on the sticker from card state;
the label bar gets power-and-fans and serial rows, eight in total, with a
framed panel. A graphics card appraisal guide item holds the rules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:18:01 +08:00
ZHU MINCHAO 9d19df407a Labels: eight rows (model, build, fan blades, contacts, bracket, print, serial, seal); serial text rules by brand and tier 2026-11-03 14:07:40 +08:00
ZHU MINCHAOandClaude Sonnet 5.5 202bdbbfbc Four tiers per brand: RZX 60/70/80/90 and RZ 70/80/800/900
Model name is display only; performance and price follow brand and tier.
Tier physical traits: power pins 6/8/8/8+8 and fans 1/2/3/3.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 14:05:47 +08:00
128 changed files with 936 additions and 257 deletions

No files matched your search

+13
View File
@@ -2,6 +2,19 @@
## 未发布
### 第一阶段完成(入门、挖矿、显卡鉴定、终端)
以下条目取代下面较早条目里已过时的部分(型号、入口价、终端页面):
- 显卡改成每个牌子四档(低端、中端、高端、旗舰):Probidia RZX 60 / 70 / 80 / 90,PMD RZ 70 / 80 / 800 / 900;型号只是显示名,性能由牌子和档次定;档次对应供电口(6 / 8 / 8 / 8+8 针)和风扇数(1 / 2 / 3 / 3)。显卡 3×2 格,正反面各一张图;价格约为最初的 1.8 倍,出块奖励 90。
- 显卡外观:叶片、金手指、挡板、封条,随每晚损耗变差,影响价值、算力、损耗、暴毙。
- 二手卡贩子(老实人、中间商、骗子)、假卡(低档芯片套高档壳)、序列号(牌子前缀、档次码、工厂码、流水)、背面丝印、鉴定公会收显卡、显卡鉴定手册。
- 放大镜检视显卡:专门的大图和标签栏(型号真伪加叶片、金手指、挡板、封条四项成色),卖家说法与逐项指出、标签机改说法、买家验货与按价差扣信誉。
- 减半抬高价格锚(传导 0.6,减半前一周有预期行情,减半前 3 天上报纸)。
- 交易终端重做:首页三个入口(银行、Poinbase、Web3)加报告;左右分栏表格;买入均价;Web3 页的稳定币价格与状态;银行提取前的审查提示;金额输入框加宽。
- 入口升级价 400,开局送交易终端、矿架、RZX 60 和鉴定手册。
- 经济模拟 v7(运维库 research/sim/out_v7.md):电网电价下 RZX 70 挖矿第 30 天占总收入 18%、第 60 天 20%。
- 纯逻辑层:带种子的随机数、资产登记、地址账本(污点与成本随币走)、恒定乘积交易对、挖矿(显卡、矿机、全网算力、减半、损耗与暴毙)。
- 纯逻辑层:市场(价格锚、情绪、游戏事件冲击、NPC 大户、多交易对套利回归、当日均价),三种稳定币(CRC 脱锚与减记、CRT 脱锚与冻结概率、CRA 补贴池与死亡螺旋),合规出金与 AML(大额与累计阈值、污点比例、轻度与重度命中、只对收益计税)。
- mod 本体(第一阶段的第一部分):人脉网络"Web3 治理社区"(300,整个 mod 的入口)交付交易终端和一张 RZX 50;矿架向杰克逊博士购买;矿架是带电池槽和 4 个显卡槽的机器,每晚从电池扣电挖 PSC;显卡按型号显示算力、耗电与健康度,每晚损耗并可能烧毁;交易终端有钱包、交易(四个交易对、存入信用点、提取 CRC 过合规审查)、报告三页;夜间结算写晨间日志;PSC 交易所开业与减半上报纸;状态存进游戏的 mod 存档数据。中英双语。
Binary file not shown.
Binary file not shown.
+27 -29
View File
@@ -7,8 +7,9 @@ import random
from sprites import Canvas, C, key, MODELS
BW, BH = 192, 128
FANS = ((68, 51), (142, 51))
FR = 30 # 风扇半径
# 风扇布局(按风扇数):中心与半径;FANS / FR 保留为双风扇的值,给叠加图模块当默认
FAN_LAYOUT = {1: ([(105, 55)], 34), 2: ([(68, 51), (142, 51)], 30), 3: ([(52, 51), (96, 51), (140, 51)], 21)}
FANS, FR = FAN_LAYOUT[2][0], FAN_LAYOUT[2][1]
SH0, SH1 = (24, 14), (186, 96) # 散热罩 左上 / 右下
FING_Y0, FING_Y1 = 109, 122
NOTCH_X = 112
@@ -94,9 +95,9 @@ def bracket(c):
c.rect(9, 10, 14, 12, 'k')
def fan(c, cx, cy, accent, accent_dark):
def fan(c, cx, cy, accent, accent_dark, R=None):
"""风扇:九片弯曲扇叶(前缘高光、叶面渐变、叶尖暗边),外圈带四个固定螺丝,轴心带品牌圆标。"""
R = FR
R = R or FR
for y in range(cy - R - 1, cy + R + 2):
for x in range(cx - R - 1, cx + R + 2):
dx, dy = x - cx, y - cy
@@ -135,7 +136,10 @@ def fan(c, cx, cy, accent, accent_dark):
c.put(sx, sy, 'g')
def gpu_front(accent, accent_dark, brand_text, burnt=False):
def gpu_front(accent, accent_dark, brand_text, tier=1, burnt=False):
"""tier 决定实物特征:风扇数(1/2/3/3)与供电口(6 针 / 8 针 / 8 针 / 双 8 针)。"""
count = (1, 2, 3, 3)[tier]
fans, R = FAN_LAYOUT[count]
c = Canvas(BW, BH)
bracket(c)
x0, y0 = SH0
@@ -161,21 +165,26 @@ def gpu_front(accent, accent_dark, brand_text, burnt=False):
c.put(x, y, 'x' if (x - (x1 - 18)) % 3 == 0 else 'g' if (x - (x1 - 18)) % 3 == 1 else 'n')
c.rect(x1 - 20, y0 + 10, x1 - 19, y1 - 4, 'x')
# 风扇之间的中筋
c.rect(105, y0 + 8, 106, y1 - 5, 'k'); c.rect(107, y0 + 8, 107, y1 - 5, 'x')
for cx, cy in FANS:
fan(c, cx, cy, accent, accent_dark)
for a, b in zip(fans, fans[1:]):
mx = (a[0] + b[0]) // 2 + 1
c.rect(mx - 1, y0 + 8, mx, y1 - 5, 'k'); c.rect(mx + 1, y0 + 8, mx + 1, y1 - 5, 'x')
for cx, cy in fans:
fan(c, cx, cy, accent, accent_dark, R)
# 前沿斜切装饰条:品牌色 + 品牌字
for x in range(x0 + 4, x1 - 20):
c.rect(x, y1 - 5, x, y1 - 3, accent if x % 11 else accent_dark)
text57(c, 112, 84, brand_text, 'h')
text57(c, 30 if count == 1 else 112, 84, brand_text, 'h') # 单风扇的大风扇占了右边,品牌字改放左下
# 顶沿:四根铜热管端头与 8 针供电口
for i in range(4):
x = 38 + i * 9
c.rect(x, 7, x + 6, y0 - 1, 'p'); c.rect(x, 7, x + 1, y0 - 1, 'h' if i == 0 else 'p')
c.rect(x + 5, 7, x + 6, y0 - 1, 'q'); c.rect(x, 7, x + 6, 7, 'q')
c.rect(112, 5, 154, y0 - 1, 'n'); c.rect(112, 5, 154, 6, 'z')
for i in range(8):
c.rect(114 + i * 5, 7, 116 + i * 5, 9, 'k'); c.put(115 + i * 5, 8, 'Y')
# 供电口:低端 6 针;中端、高端 8 针;旗舰两个 8 针
for left, pins in ((((118, 6),) if tier == 0 else ((112, 8),) if tier < 3 else ((78, 8), (126, 8)))):
right = left + pins * 5 + 2
c.rect(left, 5, right, y0 - 1, 'n'); c.rect(left, 5, right, 6, 'z')
for i in range(pins):
c.rect(left + 2 + i * 5, 7, left + 4 + i * 5, 9, 'k'); c.put(left + 3 + i * 5, 8, 'Y')
# PCB:阻焊绿、边缘高光、元件与走线
for y in range(y1 + 1, FING_Y0):
for x in range(x0, x1 + 1):
@@ -248,8 +257,6 @@ def sticker(c, seed):
w = 1 + ((x * 7 + seed) % 3)
if (x * 5 + seed) % 4 != 0:
c.rect(x, 70, x, 84, 'n')
text57(c, 118, 88, "0X5A70", 'n')
c.rect(150, 88, 166, 94, 'm')
def seal(c):
@@ -407,19 +414,12 @@ def ov_sticker(kind):
BIG = {}
for _b, (_a, _ad, _t) in {'probidia': ('G', 'g2', 'PROBIDIA'), 'pmd': ('R', 'r2', 'PMD')}.items():
BIG['gpu_big_%s_f' % _b] = (lambda a=_a, ad=_ad, t=_t: gpu_front(a, ad, t))
for _tier in range(4):
BIG['gpu_big_%s_f_t%d' % (_b, _tier)] = (lambda a=_a, ad=_ad, t=_t, k=_tier: gpu_front(a, ad, t, k))
BIG['gpu_big_%s_b' % _b] = (lambda a=_a, ad=_ad: gpu_back(a, ad))
BIG['gpu_big_burnt_f'] = lambda: gpu_front('e', 'c', 'PROBIDIA', burnt=True)
BIG['gpu_big_burnt_f'] = lambda: gpu_front('e', 'c', 'PROBIDIA', 1, burnt=True)
BIG['gpu_big_burnt_b'] = lambda: gpu_back('e', 'c', burnt=True)
BIG_OV = {
'gpu_big_ov_fan1': lambda: ov_fan(1), 'gpu_big_ov_fan2': lambda: ov_fan(2),
'gpu_big_ov_fin1': lambda: ov_fingers(1), 'gpu_big_ov_fin2': lambda: ov_fingers(2),
'gpu_big_ov_brk1': lambda: ov_bracket(1), 'gpu_big_ov_brk2': lambda: ov_bracket(2),
'gpu_big_ov_stk1': lambda: ov_sticker(1), 'gpu_big_ov_stk2': lambda: ov_sticker(2),
}
for _m in MODELS:
BIG_OV['gpu_big_ov_txt_' + key(_m)] = (lambda m=_m: ov_text(m, False))
BIG_OV['gpu_big_ov_txs_' + key(_m)] = (lambda m=_m: ov_text(m, True))
BIG_OV = {}
if __name__ == '__main__':
import sys, os
@@ -435,7 +435,5 @@ if __name__ == '__main__':
b.px[y][x] = oc.px[y][x]
return b
bg = (0x6A, 0x72, 0x70)
stack('gpu_big_probidia_f').png(os.path.join(d, 'big_f.png'), scale=5, bg=bg)
stack('gpu_big_pmd_f', 'gpu_big_ov_fan1', 'gpu_big_ov_fin2', 'gpu_big_ov_brk2').png(os.path.join(d, 'big_f_fake.png'), scale=5, bg=bg)
stack('gpu_big_pmd_b', 'gpu_big_ov_txs_rz6800', 'gpu_big_ov_stk1').png(os.path.join(d, 'big_b_fake.png'), scale=5, bg=bg)
stack('gpu_big_probidia_b', 'gpu_big_ov_txt_rzx80ti').png(os.path.join(d, 'big_b.png'), scale=5, bg=bg)
for t in range(4):
stack('gpu_big_probidia_f_t%d' % t).png(os.path.join(d, 'big_f_t%d.png' % t), scale=3, bg=bg)
+153 -34
View File
@@ -30,6 +30,11 @@ C = {
'Y': (0xC8, 0xA4, 0x4A), # 金手指
'c': (0x2E, 0x2A, 0x2A), # 焦黑
'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), # 铜
'q': (0x8A, 0x4F, 0x34), # 暗铜
'w': (0xE6, 0xE2, 0xD0), # 白
@@ -137,13 +142,14 @@ def bracket(c):
c.put(2, 27, 'h'); c.put(4, 27, 'd')
def fan(c, cx, cy, accent, burnt=False):
def fan(c, cx, cy, accent, burnt=False, R=7):
"""风扇:深色风道里六片弯曲扇叶(前缘高光、叶面按受光渐变),外圈细边,轴心带品牌色点;光从左上来。"""
import math
for y in range(cy - 7, cy + 8):
for x in range(cx - 7, cx + 8):
k = 7.0 / R # 按半径折算到 R=7 的画法
for y in range(cy - R, cy + R + 1):
for x in range(cx - R, cx + R + 1):
dx, dy = x - cx, y - cy
r = math.hypot(dx, dy)
r = math.hypot(dx, dy) * k
if r > 7.4:
continue
ang = (math.atan2(dy, dx) / (2 * math.pi)) % 1.0
@@ -164,7 +170,10 @@ def fan(c, cx, cy, accent, burnt=False):
c.put(cx - 1, cy - 1, 'w')
def gpu_front(accent, accent_dark, burnt=False):
FAN_LAYOUT_SMALL = {1: ([(26, 13)], 9), 2: ([(17, 13), (34, 13)], 7), 3: ([(14, 13), (26, 13), (38, 13)], 5)}
def gpu_front(accent, accent_dark, burnt=False, tier=1):
"""显卡正面(48×32,3×2 格),略微俯视:石墨色散热罩带斜切角与品牌色斜条,两个七叶风扇,
右端露出鳍片,顶沿露出铜热管和 8 针供电口,前沿是带元件的 PCB 和逐根画出的金手指。每格中心都不透明。"""
c = Canvas(W, H)
@@ -182,26 +191,33 @@ def gpu_front(accent, accent_dark, burnt=False):
for y in range(TOP0 + 2, TOP1):
c.put(x, y, 'g' if (x - (R - 4)) % 2 == 0 else 'k')
c.rect(R - 5, TOP0 + 2, R - 5, TOP1 - 1, 'x')
# 中间的筋条(两个风扇之间)与上下沿的槽
for y in range(TOP0 + 3, TOP1 - 2):
c.put(25, y, 'k'); c.put(26, y, 'x')
count = (1, 2, 3, 3)[tier]
fans, fr = FAN_LAYOUT_SMALL[count]
# 风扇之间的筋条
for a, b in zip(fans, fans[1:]):
mx = (a[0] + b[0]) // 2
for y in range(TOP0 + 3, TOP1 - 2):
c.put(mx, y, 'k'); c.put(mx + 1, y, 'x')
# 品牌色斜条(从前沿向左上延伸)与前沿色条
for x in range(L + 1, R - 5):
c.put(x, TOP1 - 1, accent if x % 9 else accent_dark)
c.put(x, TOP1, accent_dark)
for cx, cy in FANS:
fan(c, cx, cy, accent)
for dx, dy in ((-7, -7), (7, -7), (-7, 7), (7, 7)): # 风扇四角的固定孔
c.put(cx + dx, cy + dy, 'n' if dx + dy else 'k')
for cx, cy in fans:
fan(c, cx, cy, accent, R=fr)
if count < 3:
for dx, dy in ((-7, -7), (7, -7), (-7, 7), (7, 7)): # 风扇四角的固定孔
c.put(cx + dx, cy + dy, 'n' if dx + dy else 'k')
# 顶沿:铜热管端头 + 8 针供电口(带针脚)
for x0 in (L + 6, L + 8, L + 10, L + 12):
c.put(x0, TOP0 - 1, 'p'); c.put(x0 + 1, TOP0 - 1, 'q')
for x in range(L + 18, L + 28):
c.put(x, TOP0 - 1, 'n')
for x in range(L + 18, L + 28, 2):
c.put(x, TOP0 - 2, 'k')
for x in range(L + 19, L + 28, 2):
c.put(x, TOP0 - 2, 'Y')
# 供电口:低端 6 针;中端、高端 8 针;旗舰两个 8 针(每针占两像素)
for left, pins in (((L + 22, 6),) if tier == 0 else ((L + 18, 8),) if tier < 3 else ((L + 8, 8), (L + 25, 8))):
for x in range(left, left + pins * 2):
c.put(x, TOP0 - 1, 'n')
for x in range(left, left + pins * 2, 2):
c.put(x, TOP0 - 2, 'k')
for x in range(left + 1, left + pins * 2, 2):
c.put(x, TOP0 - 2, 'Y')
# 前沿:PCB(绿)带元件,之后金手指
for x in range(L, R + 1):
c.put(x, TOP1 + 1, 'G' if x % 7 else 'g2'); c.put(x, TOP1 + 2, 'g2')
@@ -221,7 +237,7 @@ def gpu_front(accent, accent_dark, burnt=False):
c.put(x, y, 'c')
for x in range(L + 1, R - 5):
c.put(x, TOP1 - 1, 'e')
for cx, cy in FANS:
for cx, cy in fans:
for dx, dy in ((0, 0), (1, 1), (-1, 1), (2, -2), (-2, -2)):
c.put(cx + dx, cy + dy, 'c')
outline(c)
@@ -298,10 +314,10 @@ def overlay():
return Canvas(W, H)
def ov_fan(kind):
def ov_fan(kind, count=2):
"""风扇叠加:1 积灰(叶片上一层灰),2 叶片缺角(边缘缺一块并露出黑底)。"""
c = overlay()
for cx, cy in FANS:
for cx, cy in FAN_LAYOUT_SMALL[count][0]:
if kind == 1:
for dx, dy in ((-4, -2), (-2, -4), (3, -3), (4, 1), (-3, 3), (2, 4), (-5, 0), (0, 5), (1, -5), (-1, 2), (5, -1), (3, 2)):
c.put(cx + dx, cy + dy, 'l')
@@ -363,7 +379,7 @@ def ov_sticker(kind):
return c
MODELS = ['RZX 50', 'RZX 60', 'RZX 70', 'RZX 80 Ti', 'RZX 90', 'RZ 470', 'RZ 580', 'RZ 6800', 'RZ 7900']
MODELS = ['RZX 60', 'RZX 70', 'RZX 80', 'RZX 90', 'RZ 70', 'RZ 80', 'RZ 800', 'RZ 900']
def rig():
@@ -503,24 +519,115 @@ def key(model):
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():
"""鉴定手册(16×16,一格):竖放的小册子,封面带品牌色条,书脊在左。"""
c = Canvas(16, 16)
c.box(2, 1, 13, 14, 'b')
c.rect(3, 2, 4, 13, 'd') # 书脊
c.rect(5, 2, 12, 2, 'h')
c.rect(6, 4, 11, 5, 'G') # 封面色块
c.rect(6, 4, 11, 4, 'g2')
for y in (7, 9, 11): # 文字行
c.rect(6, y, 11 if y != 11 else 9, y, 'm')
c.put(12, 13, 'd')
outline(c)
return c
SPRITES = {
'gpu_probidia_f': lambda: gpu_front('G', 'g2'),
'gpu_probidia_b': lambda: gpu_back('G', 'g2'),
'gpu_pmd_f': lambda: gpu_front('R', 'r2'),
'gpu_pmd_b': lambda: gpu_back('R', 'r2'),
'gpu_guide': guide,
'gpu_burnt_f': lambda: gpu_front('e', 'c', burnt=True),
'gpu_burnt_b': lambda: gpu_back('e', 'c', burnt=True),
'gpu_probidia_b': lambda: gpu_back('G', 'g2'),
'gpu_pmd_b': lambda: gpu_back('R', 'r2'),
'rig': rig,
'terminal': terminal,
}
for _tier in range(4):
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))
UI_ICONS = {'ui_bank': ui_bank, 'ui_poinbase': ui_poinbase, 'ui_web3': ui_web3}
# 叠加图:和底图同尺寸,位置 (0,0) 对齐,只画变化的像素,不参与判格
OVERLAYS = {
'gpu_ov_fan1': lambda: ov_fan(1), 'gpu_ov_fan2': lambda: ov_fan(2),
'gpu_ov_fin1': lambda: ov_fingers(1), 'gpu_ov_fin2': lambda: ov_fingers(2),
'gpu_ov_brk1': lambda: ov_bracket(1), 'gpu_ov_brk2': lambda: ov_bracket(2),
'gpu_ov_stk1': lambda: ov_sticker(1), 'gpu_ov_stk2': lambda: ov_sticker(2),
'gpu_ov_brk1': lambda: ov_bracket(1),
'gpu_ov_stk2': lambda: ov_sticker(2),
}
for _n in (1, 2, 3):
OVERLAYS['gpu_ov_fan1_%d' % _n] = (lambda n=_n: ov_fan(1, n))
OVERLAYS['gpu_ov_fan2_%d' % _n] = (lambda n=_n: ov_fan(2, n))
for _m in MODELS:
OVERLAYS['gpu_ov_txt_' + key(_m)] = (lambda m=_m: ov_text(m, False))
OVERLAYS['gpu_ov_txs_' + key(_m)] = (lambda m=_m: ov_text(m, True))
@@ -551,25 +658,37 @@ if __name__ == '__main__':
if oc.px[y][x] is not None:
b.px[y][x] = oc.px[y][x]
return b
stack('gpu_probidia_f', 'gpu_ov_fan1', '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_pmd_f', 'gpu_ov_fan2', 'gpu_ov_fin2', 'gpu_ov_brk2').png(os.path.join(sys.argv[2], 'prev_front_fake.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
stack('gpu_probidia_b', 'gpu_ov_txt_rzx80ti').png(os.path.join(sys.argv[2], 'prev_back_ok.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
stack('gpu_pmd_b', 'gpu_ov_txs_rz6800', 'gpu_ov_stk1', 'gpu_ov_stk2').png(os.path.join(sys.argv[2], 'prev_back_fake.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
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))
print(sys.argv[2])
else:
out = os.path.normpath(os.path.join(here, '..', 'src', 'Decentralized', 'ModFiles', 'Textures', 'Items'))
for f in os.listdir(out): # 清掉旧的显卡图,避免留下不再使用的文件
if f.startswith('gpu_'):
os.remove(os.path.join(out, f))
for name, make in SPRITES.items():
canvas = make()
check_cells(canvas, name)
canvas.png(os.path.join(out, name + '.png'))
for name, make in OVERLAYS.items():
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
for name, make in big.BIG.items():
make().png(os.path.join(out, name + '.png'))
for name, make in big_cases.CASES.items():
if not name.startswith('gpu_big_ov_print_'):
if name.startswith('gpu_big_ov_fan_'):
continue
if not name.startswith('gpu_big_ov_print_') and name in ('gpu_big_ov_fin_dim', 'gpu_big_ov_fin_oxide', 'gpu_big_ov_brk_scratch', 'gpu_big_ov_seal_missing'):
make().png(os.path.join(out, name + '.png'))
# 风扇叠加按风扇数各出一套:叠加模块里的风扇位置是模块级变量,换成对应布局后再画
for count, (centres, radius) in big.FAN_LAYOUT.items():
big_cases.FANS = list(centres) if len(centres) > 1 else [centres[0], centres[0]]
big_cases.FR = radius
for fname, short in (('fan_dust1', 'fan_dust1'), ('fan_dust2', 'fan_dust2'), ('fan_chip', 'fan_chip')):
getattr(big_cases, fname)().png(os.path.join(out, 'gpu_big_ov_%s_%d.png' % (short, count)))
for m in MODELS:
for style in ('normal', 'skew', 'stamp'):
big_cases.print_overlay(m, style).png(os.path.join(out, 'gpu_big_ov_print_%s_%s.png' % (style, key(m))))
+78 -32
View File
@@ -3,52 +3,59 @@ using System.Linq;
namespace Decentralized.Domain
{
/// <summary>卖家的说法:每个标签一项。型号是文字,其余五项取值与 <see cref="CardLook"/> 的取值一致。</summary>
/// <summary>卖家的说法:每个标签一项。型号是文字,其余取值与 <see cref="CardLook"/> 一致;「供电与风扇」由说法里的型号决定档次。</summary>
public struct Claim
{
public string Model;
public int Fan, Fingers, Bracket, Print, Sticker;
public int Fan, Fingers, Bracket, Print, Sticker, Serial;
public int Get(int row) => row == CardLabels.RowFan ? Fan : row == CardLabels.RowFingers ? Fingers : row == CardLabels.RowBracket ? Bracket : row == CardLabels.RowPrint ? Print : Sticker;
public int Tier => Hardware.Specs[Model].Tier;
public int Get(int row) => row == CardLabels.RowBuild ? Tier : row == CardLabels.RowFan ? Fan : row == CardLabels.RowFingers ? Fingers
: row == CardLabels.RowBracket ? Bracket : row == CardLabels.RowPrint ? Print : row == CardLabels.RowSerial ? Serial : Sticker;
public Claim With(int row, int value)
{
var c = this;
if (row == CardLabels.RowFan) c.Fan = value; else if (row == CardLabels.RowFingers) c.Fingers = value;
else if (row == CardLabels.RowBracket) c.Bracket = value; else if (row == CardLabels.RowPrint) c.Print = value; else c.Sticker = value;
else if (row == CardLabels.RowBracket) c.Bracket = value; else if (row == CardLabels.RowPrint) c.Print = value;
else if (row == CardLabels.RowSerial) c.Serial = value; else if (row == CardLabels.RowSeal) c.Sticker = value;
return c;
}
public string Encode() => $"{Model}:{Fan}{Fingers}{Bracket}{Print}{Sticker}";
public string Encode() => $"{Model}:{Fan}{Fingers}{Bracket}{Print}{Sticker}{Serial}";
public static Claim Decode(string s)
{
var p = (s ?? "").Split(':');
if (p.Length != 2 || p[1].Length != 5 || !Hardware.Specs.ContainsKey(p[0])) return default;
if (p.Length != 2 || p[1].Length != 6 || !Hardware.Specs.ContainsKey(p[0])) return default;
int D(int i) => Math.Max(0, Math.Min(2, p[1][i] - '0'));
return new Claim { Model = p[0], Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
return new Claim { Model = p[0], Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4), Serial = D(5) };
}
/// <summary>按真实情况写出的说法(没有隐瞒)。</summary>
public static Claim Truth(Unit u)
{
var l = u.Appearance;
return new Claim { Model = u.FakeOf ?? u.Model, Fan = l.Fan, Fingers = l.Fingers, Bracket = l.Bracket, Print = l.Print, Sticker = l.Sticker };
return new Claim { Model = u.FakeOf ?? u.Model, Fan = l.Fan, Fingers = l.Fingers, Bracket = l.Bracket, Print = l.Print, Sticker = l.Sticker, Serial = l.Serial };
}
}
/// <summary>
/// 标签栏的数据:六个标签(型号、风扇、金手指、挡板、丝印、贴纸),每个标签有卖家的说法和真实情况。
/// 标签栏的数据:五个标签(型号真伪、风扇叶片、金手指、挡板、封条),每个标签有卖家的说法和真实情况;
/// 供电与风扇、背面丝印、序列号三项是型号真伪的依据,只存状态,不单独成行。
/// 一个标签被指出(拆穿)之后,说法改回真实情况;价格按「已指出的标签取真实情况,其余取说法」算,
/// 所以卖家隐瞒的问题要玩家一项项指出来才能把价格拉回真实价值。
/// 所以卖家隐瞒的问题要玩家一项项指出来才能把价格拉回真实价值。指出型号有问题,型号自动改回真实型号。
/// </summary>
public static class CardLabels
{
public const int RowSpec = 1, RowFan = 2, RowFingers = 4, RowBracket = 8, RowPrint = 16, RowSticker = 32, AllRows = 63;
public static readonly int[] Rows = { RowSpec, RowFan, RowFingers, RowBracket, RowPrint, RowSticker };
public const int RowSpec = 1, RowBuild = 2, RowFan = 4, RowFingers = 8, RowBracket = 16, RowPrint = 32, RowSerial = 64, RowSeal = 128, AllRows = 255;
/// <summary>标签栏的顺序:第一行鉴定真伪(型号;依据是供电口与风扇数、序列号、背面丝印,玩家对着大图自己看),后四行鉴定成色。</summary>
public static readonly int[] Rows = { RowSpec, RowFan, RowFingers, RowBracket, RowSeal };
public const int AuthRows = 1;
/// <summary>各标签各取值对价格的影响(百分比,相加):风扇积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失 −6%。
/// 接口数不对、丝印、手写序列号本身不扣价,它们是假货的证据。</summary>
/// <summary>各标签各取值对价格的影响(百分比,相加):风扇叶片积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失 −6%。
/// 型号、供电与风扇、丝印、序列号本身不扣价,它们决定的是这块卡是什么芯片。</summary>
public static double Pct(int row, int value)
{
switch (row)
@@ -56,14 +63,22 @@ namespace Decentralized.Domain
case RowFan: return value == 1 ? -4 : value == 2 ? -12 : 0;
case RowFingers: return value == 1 ? -4 : value == 2 ? -12 : 0;
case RowBracket: return value == 1 ? -2 : 0;
case RowSticker: return value == 2 ? -6 : 0;
case RowSeal: return value == 2 ? -6 : 0;
}
return 0;
}
/// <summary>这一项的真实取值是不是假货的证据。</summary>
public static bool IsFakeSign(int row, int realValue) =>
(row == RowBracket && realValue == 2) || (row == RowPrint && realValue != 0) || (row == RowSticker && realValue == 1);
/// <summary>这一行是不是假货的证据:说法与真实情况不同,并且真实情况本身不该出现在真卡上。</summary>
public static bool IsEvidence(int row, Claim claim, Claim real)
{
switch (row)
{
case RowBuild: return claim.Tier != real.Tier;
case RowPrint: return real.Print != 0 && claim.Print != real.Print;
case RowSerial: return real.Serial != 0 && claim.Serial != real.Serial;
}
return false;
}
/// <summary>
/// 价值:claim 为空表示没有人说过什么,一律按真实情况;exposed 里的位表示已指出的标签(取真实情况)。
@@ -74,9 +89,8 @@ namespace Decentralized.Domain
var real = Claim.Truth(u);
Claim believed = claim == null ? real : claim.Value;
double sum = 0;
foreach (int r in Rows)
foreach (int r in new[] { RowFan, RowFingers, RowBracket, RowSeal })
{
if (r == RowSpec) continue;
int v = claim == null || (exposed & r) != 0 ? real.Get(r) : believed.Get(r);
sum += Pct(r, v);
}
@@ -85,24 +99,23 @@ namespace Decentralized.Domain
return Math.Max(1, (long)Math.Round(value));
}
/// <summary>指出一个标签:返回 0 没问题(说法属实)、1 隐瞒了对价格不利的情况、2 拆穿了假货。
/// 指出假货的证据(接口数、丝印、手写序列号)时,型号标签一并拆穿。</summary>
/// <summary>指出一个标签:返回 0 没问题(说法属实)、1 隐瞒了对价格不利的情况、2 拆穿了假货(型号一并改回真实型号)。</summary>
public static int Point(Unit u, Claim claim, int row, ref int exposed)
{
var real = Claim.Truth(u);
bool wasExposed = (exposed & row) != 0;
exposed |= row;
if (row == RowSpec) return claim.Model != real.Model ? 2 : 0;
if (IsFakeSign(row, real.Get(row)) && claim.Get(row) != real.Get(row))
if (IsEvidence(row, claim, real))
{
exposed |= RowSpec;
exposed |= RowSpec | RowBuild;
return 2;
}
if (wasExposed) return 0;
return claim.Get(row) != real.Get(row) ? 1 : 0;
}
/// <summary>说法里隐瞒了什么:说法比真实情况价值高的标签,以及型号不符。</summary>
/// <summary>说法里隐瞒了什么:说法比真实情况价值高的标签,型号不符,以及假货的证据。</summary>
public static int Lies(Unit u, Claim claim, int exposed)
{
var real = Claim.Truth(u);
@@ -111,24 +124,57 @@ namespace Decentralized.Domain
{
if ((exposed & r) != 0) continue;
if (r == RowSpec) { if (claim.Model != real.Model) lies |= r; }
else if (claim.Get(r) != real.Get(r) && (Pct(r, claim.Get(r)) > Pct(r, real.Get(r)) || IsFakeSign(r, real.Get(r)))) lies |= r;
else if (claim.Get(r) != real.Get(r) && Pct(r, claim.Get(r)) > Pct(r, real.Get(r))) lies |= r;
}
return lies;
}
/// <summary>卖家怎么说:老实人如实说;中间商每项有三成概率隐瞒;骗子全部说成没问题。假卡的型号一律说成标称型号。</summary>
/// <summary>卖家怎么说:老实人如实说;中间商每项有七成概率说成没问题;骗子全部说成没问题。假卡的型号一律说成标称型号。</summary>
public static Claim DealerClaim(Unit u, DealerKind kind, Rng rng)
{
var c = Claim.Truth(u);
if (kind == DealerKind.Honest) return c;
c.Model = u.Model;
if (kind == DealerKind.Honest) { c.Model = u.FakeOf ?? u.Model; return c; }
double hide = kind == DealerKind.Scammer ? 1.0 : 0.7;
foreach (int r in Rows)
{
if (r == RowSpec) continue;
foreach (int r in new[] { RowFan, RowFingers, RowBracket, RowPrint, RowSerial, RowSeal })
if (rng.Chance(hide)) c = c.With(r, 0);
}
return c;
}
// ---------- 序列号 ----------
/// <summary>各牌子的序列号前缀与合法工厂码。序列号格式:前缀 + 档次码(1 到 4)+ "-" + 工厂码(两位)+ "-" + 四位流水。</summary>
public static string BrandCode(string brand) => brand == Hardware.Probidia ? "PB" : "MD";
public static readonly int[] ProbidiaFactories = { 11, 12, 21, 34, 47 }, PmdFactories = { 13, 22, 35, 41, 58 };
public static int[] Factories(string brand) => brand == Hardware.Probidia ? ProbidiaFactories : PmdFactories;
private static uint Mix(uint seed, int salt)
{
uint x = seed * 2654435761u + (uint)salt * 40503u + 777u;
x ^= x >> 15; x *= 2246822519u; x ^= x >> 13; x *= 3266489917u; x ^= x >> 16;
return x;
}
/// <summary>
/// 卡上贴纸的序列号。合规:前缀(牌子)+ 标称型号的档次码 + 合法工厂码 + 四位流水。
/// 不合规(贴纸是后补的):工厂码不在合法范围,或流水只有三位。档次码不符:贴的是原芯片的序列号,档次码是真实芯片的档次。
/// </summary>
public static string SerialText(Unit u)
{
var label = Hardware.Specs[u.Model];
var chip = Hardware.Specs[u.FakeOf ?? u.Model];
uint seed = u.Look.Seed == 0 ? 12345u : u.Look.Seed;
int kind = u.Appearance.Serial;
int[] ok = Factories(label.Brand);
int tier = (kind == 2 ? chip.Tier : label.Tier) + 1;
int factory = ok[(int)(Mix(seed, 1) % (uint)ok.Length)];
int number = 1000 + (int)(Mix(seed, 2) % 9000);
if (kind == 1)
{
if (Mix(seed, 3) % 2 == 0) { int[] bad = { 0, 9, 66, 77, 88, 99 }; factory = bad[(int)(Mix(seed, 4) % (uint)bad.Length)]; }
else number = 100 + (int)(Mix(seed, 5) % 900);
}
return $"{BrandCode(label.Brand)}{tier}-{factory:00}-{number}";
}
}
}
+14 -25
View File
@@ -13,12 +13,15 @@ namespace Decentralized.Domain
public int Fan;
/// <summary>金手指:0 光亮,1 发暗,2 氧化发绿(老化)。</summary>
public int Fingers;
/// <summary>挡板:0 完好,1 划痕(老化),2 接口数量不对(假卡)。</summary>
/// <summary>挡板:0 完好,1 划痕(老化)。</summary>
public int Bracket;
/// <summary>背面丝印:0 与标称型号一致,1 印的是真实芯片的型号(假卡),2 重新印过、字歪(假卡)。</summary>
public int Print;
/// <summary>背面贴纸与封条:0 印刷序列号、封条完好,1 手写序列号(假卡),2 封条缺失(旧卡或翻新卡)。</summary>
/// <summary>封条:0 完好,2 缺失(开过封的旧卡或翻新卡)。</summary>
public int Sticker;
/// <summary>序列号:0 合规(格式对、工厂码在合法范围、档次码与型号一致),1 不合规(工厂码不在合法范围或位数不对,贴纸是后补的),
/// 2 档次码与标称型号不符(贴的是原芯片的序列号)。后两种只出现在假卡上。</summary>
public int Serial;
public const int Variants = 3;
@@ -43,7 +46,7 @@ namespace Decentralized.Domain
double fan = worn + (U(Seed, 1) - 0.5) * 0.3, fin = worn + (U(Seed, 2) - 0.5) * 0.3, brk = worn + (U(Seed, 3) - 0.5) * 0.3;
l.Fan = fan >= 0.55 ? 2 : fan >= 0.25 ? 1 : 0;
l.Fingers = fin >= 0.60 ? 2 : fin >= 0.30 ? 1 : 0;
if (Bracket != 2) l.Bracket = brk >= 0.45 ? 1 : 0;
l.Bracket = brk >= 0.45 ? 1 : 0;
return l;
}
@@ -52,14 +55,14 @@ namespace Decentralized.Domain
/// <summary>算力倍数:风扇积灰 ×0.97、缺角 ×0.92(降频);金手指发暗 ×0.98、氧化 ×0.94(接触不良掉速)。</summary>
public double HashFactor => FanHash[Fan] * FingerHash[Fingers];
/// <summary>五位数字,存在物品标签上:风扇、金手指、挡板、丝印、贴纸。</summary>
public string Encode() => $"{Fan}{Fingers}{Bracket}{Print}{Sticker}";
/// <summary>六位数字,存在物品标签上:风扇、金手指、挡板、丝印、封条、序列号。</summary>
public string Encode() => $"{Fan}{Fingers}{Bracket}{Print}{Sticker}{Serial}";
public static CardLook Decode(string s)
{
if (s == null || s.Length != 5) return default;
if (s == null || (s.Length != 5 && s.Length != 6)) return default;
int D(int i) => Math.Max(0, Math.Min(Variants - 1, s[i] - '0'));
return new CardLook { Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
return new CardLook { Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4), Serial = s.Length > 5 ? D(5) : 0 };
}
// 外观维度对应卡的真实状态:损耗、暴毙概率随之变化,价值走标签(见 CardLabels)
@@ -73,7 +76,7 @@ namespace Decentralized.Domain
public double DeathFactor => FanDeath[Fan] * FingerDeath[Fingers];
/// <summary>只在假卡上出现的取值:玩家一旦看到,就能肯定是假的。</summary>
public bool HasFakeSign => Bracket == 2 || Print != 0 || Sticker == 1;
public bool HasFakeSign => Print != 0 || Serial != 0;
/// <summary>
/// 生成外观。真卡:老化维度按健康度出现,封条缺失在二手卡上很常见,不会出现破绽类取值。
@@ -84,26 +87,12 @@ namespace Decentralized.Domain
var look = new CardLook { Seed = rng.NextUInt() | 1, Sticker = rng.Chance(fake ? 0.35 : 0.15 + 0.30 * (1 - Math.Max(0, Math.Min(100, health)) / 100.0)) ? 2 : 0 };
if (fake)
{
int signs = rng.Chance(0.55) ? 1 : rng.Chance(0.6) ? 2 : 3;
var order = new[] { 0, 1, 2 };
for (int i = order.Length - 1; i > 0; i--)
{
int j = rng.Range(0, i);
(order[i], order[j]) = (order[j], order[i]);
}
for (int k = 0; k < signs; k++)
{
switch (order[k])
{
case 0: look.Bracket = 2; break;
case 1: look.Print = rng.Chance(0.6) ? 1 : 2; break;
default: look.Sticker = 1; break;
}
}
// 假卡本身就带着实物上的破绽(低档次的板子:供电口和风扇数对不上),此外骗子还可能重印丝印、补贴序列号
if (rng.Chance(0.55)) look.Print = rng.Chance(0.6) ? 1 : 2;
if (rng.Chance(0.65)) look.Serial = rng.Chance(0.55) ? 2 : 1;
}
return look.Appearance(health);
}
}
/// <summary>
+3 -3
View File
@@ -9,9 +9,9 @@ namespace Decentralized.Domain
{
public const string Rig = "RIG";
public static readonly string[] Basic = { "RZX50", "RZX60", "RZ470", "RZ580" };
public static readonly string[] Advanced = { "RZX70", "RZX80Ti", "RZ6800" };
public static readonly string[] Flagship = { "RZX90", "RZ7900" };
public static readonly string[] Basic = { "RZX60", "RZ70" };
public static readonly string[] Advanced = { "RZX70", "RZ80", "RZX80", "RZ800" };
public static readonly string[] Flagship = { "RZX90", "RZ900" };
public const int AdvancedFromDay = 10, FlagshipFromDay = 30, SpecialFlagshipFromDay = 20;
public const double RigRestockChance = 0.30, AdvancedChance = 0.60, AdvancedSecondChance = 0.30, FlagshipChance = 0.25;
+14 -1
View File
@@ -16,6 +16,8 @@ namespace Decentralized.Domain
public sealed class Market
{
public double Sentiment = 1.0;
/// <summary>供给因子:出块奖励减半抬高挖矿成本,价格锚随之上移;减半前一周有预期行情。每晚向目标靠拢。</summary>
public double Supply = 1.0;
public List<Pool> Pools = new List<Pool>();
/// <summary>开门时的参考价,夜间用它和收盘价算当日均价(合约的标记价)。</summary>
public double OpenPrice = Tuning.PscStartPrice;
@@ -42,9 +44,20 @@ namespace Decentralized.Domain
/// <summary>PSC 的参考价(信用点)。</summary>
public double Price => Main.Price;
/// <summary>供给因子的目标:奖励比起始奖励每少一半,锚上移 2^传导指数;减半前 7 天内再逐日加预期。</summary>
public static double SupplyTarget(int day, double reward)
{
double t = Math.Pow(Tuning.BlockReward / Math.Max(1e-9, reward), Tuning.HalvingPassThrough);
int toHalving = Tuning.HalvingDays - day % Tuning.HalvingDays; // 减半当晚 day % HalvingDays == 0,那天之后又是一个完整周期
if (toHalving <= Tuning.HalvingRampDays) t *= 1 + Tuning.HalvingRamp * (Tuning.HalvingRampDays - toHalving + 1) / Tuning.HalvingRampDays;
return t;
}
public void UpdateSupply(int day, double reward) => Supply += (SupplyTarget(day, reward) - Supply) * Tuning.SupplyAdjust;
public double Anchor(ICollection<MarketEvent> active)
{
double a = Tuning.PscStartPrice * Sentiment;
double a = Tuning.PscStartPrice * Sentiment * Supply;
if (active.Contains(MarketEvent.Blackout)) a *= 1.10;
if (active.Contains(MarketEvent.Crackdown)) a *= 0.92;
return a;
+13 -6
View File
@@ -13,6 +13,8 @@ namespace Decentralized.Domain
public double Kj;
public int Price;
/// <summary>暴毙概率的品牌系数。</summary>
/// <summary>档次:0 低端、1 中端、2 高端、3 旗舰(显卡)。实物特征(供电口、风扇数)只看档次。</summary>
public int Tier;
public double DeathFactor;
/// <summary>每天损耗的系数。</summary>
public double WearFactor;
@@ -25,20 +27,25 @@ namespace Decentralized.Domain
public static class Hardware
{
/// <summary>各档次的实物特征:供电口针数(旗舰是两个 8 针)与风扇数量。</summary>
public static readonly string[] PowerPins = { "6", "8", "8", "8+8" };
public static readonly int[] FanCount = { 1, 2, 3, 3 };
public const string Probidia = "Probidia", Pmd = "PMD", RatDigg = "RatDigg";
public static readonly IReadOnlyDictionary<string, HardwareSpec> Specs = new[]
{
Gpu("RZX50", Probidia, 1.0, 4, 220), Gpu("RZX60", Probidia, 1.5, 5, 330), Gpu("RZX70", Probidia, 5.0, 12, 800),
Gpu("RZX80Ti", Probidia, 8.0, 18, 1300), Gpu("RZX90", Probidia, 20.0, 30, 5000),
Gpu("RZ470", Pmd, 0.8, 2.5, 170), Gpu("RZ580", Pmd, 1.2, 3, 250), Gpu("RZ6800", Pmd, 4.0, 8, 680),
Gpu("RZ7900", Pmd, 15.0, 20, 4000),
// 四个档次(低端、中端、高端、旗舰),两个牌子通用:型号只是显示名,性能和价格由「牌子 + 档次」决定
Gpu("RZX60", Probidia, 0, 1.0, 4, 220), Gpu("RZX70", Probidia, 1, 5.0, 12, 800),
Gpu("RZX80", Probidia, 2, 8.0, 18, 1300), Gpu("RZX90", Probidia, 3, 20.0, 30, 5000),
Gpu("RZ70", Pmd, 0, 0.8, 2.5, 170), Gpu("RZ80", Pmd, 1, 4.0, 8, 680),
Gpu("RZ800", Pmd, 2, 8.0, 12, 1500), Gpu("RZ900", Pmd, 3, 15.0, 20, 4000),
Asic("M1", 50, 60, 11000), Asic("M2", 120, 110, 27000), Asic("M3", 300, 220, 72000),
}.ToDictionary(s => s.Model);
private static HardwareSpec Gpu(string model, string brand, double h, double kj, int price) => new HardwareSpec
private static HardwareSpec Gpu(string model, string brand, int tier, double h, double kj, int price) => new HardwareSpec
{
Model = model, Brand = brand, Hash = h, Kj = kj, Price = price,
Model = model, Brand = brand, Tier = tier, Hash = h, Kj = kj, Price = price,
DeathFactor = brand == Probidia ? 0.7 : 1.0, WearFactor = brand == Probidia ? 1.0 : 1.2,
};
+7
View File
@@ -9,6 +9,13 @@ namespace Decentralized.Domain
public const double PscStartPrice = 100;
public const double BlockReward = 90;
public const int HalvingDays = 90;
/// <summary>减半对币价的传导:奖励减半时,价格锚上移 2^传导指数 倍(0.6 即约 +52%),市场已部分预期到,所以不是翻倍。</summary>
public const double HalvingPassThrough = 0.6;
/// <summary>减半前一周的预期行情:锚逐日抬高,到减半当天为 +8%。</summary>
public const int HalvingRampDays = 7;
public const double HalvingRamp = 0.08;
/// <summary>价格锚向目标的逐晚靠拢比例(约 7 天完成大半)。</summary>
public const double SupplyAdjust = 0.15;
public const double NpcHash0 = 360;
public const double NpcKjPerH = 2.4;
public const double ElecPrice = 1.5;
+19
View File
@@ -19,11 +19,30 @@ namespace Decentralized.Domain
/// <summary>被执法标记。</summary>
public bool Flagged;
/// <summary>买入记录:每种币买入的数量与花费(信用点等值),用来显示买入均价。卖出或转出时按比例减少。</summary>
public Dictionary<string, double> BuyQty = new Dictionary<string, double>(), BuyCost = new Dictionary<string, double>();
public void RecordBuy(string asset, double qty, double costCredits)
{
if (qty <= 0) return;
BuyQty[asset] = (BuyQty.TryGetValue(asset, out var q) ? q : 0) + qty;
BuyCost[asset] = (BuyCost.TryGetValue(asset, out var c) ? c : 0) + Math.Max(0, costCredits);
}
/// <summary>买入均价(信用点);没有买入记录(例如全是挖出来的)返回 null。</summary>
public double? AvgBuy(string asset) => BuyQty.TryGetValue(asset, out var q) && q > 1e-9 ? BuyCost[asset] / q : (double?)null;
public double Get(string asset) => Balances.TryGetValue(asset, out var v) ? v : 0;
public void Add(string asset, double amount)
{
if (amount == 0) return;
if (amount < 0 && BuyQty.TryGetValue(asset, out var bq))
{
double before = Get(asset), keep = before > 1e-12 ? Math.Max(0, 1 + amount / before) : 0;
BuyQty[asset] = bq * keep; BuyCost[asset] *= keep;
if (BuyQty[asset] < 1e-9) { BuyQty.Remove(asset); BuyCost.Remove(asset); }
}
double v = Get(asset) + amount;
if (v < -1e-9) throw new InvalidOperationException($"{Address}: {asset} 余额不足");
if (Math.Abs(v) < 1e-12) Balances.Remove(asset);
+31 -4
View File
@@ -19,10 +19,10 @@ namespace Decentralized
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Used cards x4 (test)", () =>
{
var rng = Session.Data.Rng;
Items.Give(Items.NewCard("RZX80Ti", 40, null, rng));
Items.Give(Items.NewCard("RZX80Ti", 70, "RZX60", rng));
Items.Give(Items.NewCard("RZ6800", 30, "RZ580", rng));
Items.Give(Items.NewCard("RZ7900", 90, "RZ470", rng));
Items.Give(Items.NewCard("RZX80", 40, null, rng));
Items.Give(Items.NewCard("RZX80", 70, "RZX60", rng));
Items.Give(Items.NewCard("RZ800", 30, "RZ70", rng));
Items.Give(Items.NewCard("RZ900", 90, "RZ70", rng));
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dump inspection UI (test)", () => Log.Guard("检视界面结构", () =>
{
@@ -41,6 +41,33 @@ namespace Decentralized
Walk(m.panel.transform, 0);
Log.Info(sb.ToString());
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Edit claim + offer to buyer (test)", () => Log.Guard("卖出侧测试", () =>
{
var rng = Session.Data.Rng;
// 自己的一张真卡:标签机改成旗舰型号并把成色说成没问题,再摆给一位买家
var item = Items.NewCard("RZX70", 55, null, rng);
Items.Give(item);
var u = Items.ToUnit(item);
var c = Claim.Truth(u); c.Model = "RZX90"; c.Fan = 0; c.Fingers = 0; c.Bracket = 0; c.Sticker = 0;
item.EnableTag(Items.EditedTag);
Items.SetClaim(item, c, 0);
var client = new StoreClient { identifier = "pd_test_buyer", displayName = "买家", spriteName = SpriteDict.GetRandomScavSpriteName(), clientIntent = StoreClient.ClientIntent.BUY };
client.CompleteClientCreation();
PlayerStore.Instance.currentClientInstance = StoreClientInstance.CreateClientInstance(client);
Log.Info($"测试:卡 #{item.uniqueId} 说法 {c.Encode()},真实 {Claim.Truth(u).Encode()},价格 {item.unitValue},信誉前");
Items.OfferForTest(item);
Log.Info($"测试:验货后 Exposed={Items.Exposed(item)} 价格 {item.unitValue}");
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Buy scam card (test)", () => Log.Guard("买入侧测试", () =>
{
var rng = Session.Data.Rng;
var item = Items.NewCard("RZX90", 60, "RZX60", rng, DealerKind.Scammer, 0);
long before = item.unitValue;
GeneralHelper.SetItemOwned(item, true);
Items.Refresh(item);
Items.Give(item);
Log.Info($"买入测试:卡 #{item.uniqueId} 买前报价 {before},买下后价值 {item.unitValue},真实 {CardValue.True(Items.ToUnit(item))},说法 {(Items.GetClaim(item)?.Encode() ?? "无")}");
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dr. Jackson stock (test)", () =>
{
EmporiumEntry.Instance.ClearFrontInv();
+72 -38
View File
@@ -16,7 +16,9 @@ namespace Decentralized
/// </summary>
public sealed class GpuInspectView : MonoBehaviour
{
private const float RowH = 30f, CardScale = 3f;
private const float RowH = 30f, CardScale = 3f, PanelTop = -112f, DetailH = 26f, GroupGap = 8f;
private const int RowCount = 5;
private TextMeshProUGUI detailText;
private static GpuInspectView instance;
private RectTransform root, cardRoot;
private Button flip;
@@ -26,9 +28,10 @@ namespace Decentralized
private bool hidGlass;
private Transform buttonTemplate;
private TMP_FontAsset font;
private readonly TextMeshProUGUI[] rowName = new TextMeshProUGUI[6], rowText = new TextMeshProUGUI[6];
private readonly Button[] rowButton = new Button[6];
private readonly TextMeshProUGUI[] rowButtonText = new TextMeshProUGUI[6];
private readonly TextMeshProUGUI[] rowName = new TextMeshProUGUI[RowCount], rowText = new TextMeshProUGUI[RowCount];
private readonly Button[] rowButton = new Button[RowCount];
private readonly TextMeshProUGUI[] rowButtonText = new TextMeshProUGUI[RowCount];
private TextMeshProUGUI serialText;
public static void Ensure()
{
@@ -83,18 +86,17 @@ namespace Decentralized
string P = "gpu_big_ov_";
if (!Items.IsBack(item))
{
if (l.Fan == 1) o.Add(P + "fan_dust2");
if (l.Fan == 2) { o.Add(P + "fan_dust1"); o.Add(P + "fan_chip"); }
int n = Items.FansOf(u);
if (l.Fan == 1) o.Add(P + "fan_dust2_" + n);
if (l.Fan == 2) { o.Add(P + "fan_dust1_" + n); o.Add(P + "fan_chip_" + n); }
if (l.Fingers == 1) o.Add(P + "fin_dim");
if (l.Fingers == 2) o.Add(P + "fin_oxide");
if (l.Bracket == 1) o.Add(P + "brk_scratch");
if (l.Bracket == 2) o.Add(P + "brk_port");
}
else
{
string model = (l.Print == 1 && u.FakeOf != null ? u.FakeOf : u.Model).ToLowerInvariant();
o.Add(P + "print_" + (l.Print == 2 ? "skew_" : l.Print == 1 ? "stamp_" : "normal_") + model);
if (l.Sticker == 1) o.Add(P + "sticker_hand");
if (l.Sticker == 2) o.Add(P + "seal_missing");
}
return o;
@@ -162,28 +164,40 @@ namespace Decentralized
cardRoot.SetParent(root, false);
cardRoot.localPosition = new Vector3(0, 85, 0);
buttonTemplate = m.appraisalPanel.transform.Find("buttonSend");
// 贴纸上的序列号:随卡变化,运行时用文字写在贴纸的空白处
serialText = MakeText(root, "Serial", Vector2.zero, new Vector2(48 * CardScale, 8 * CardScale), TextAlignmentOptions.Center, 20);
serialText.color = new Color(0.1f, 0.1f, 0.13f);
serialText.fontStyle = FontStyles.Bold;
serialText.overflowMode = TextOverflowModes.Overflow;
serialText.rectTransform.sizeDelta = new Vector2(48 * CardScale, 14 * CardScale);
if (buttonTemplate == null) return;
font = buttonTemplate.GetComponentInChildren<TextMeshProUGUI>()?.font;
if (font != null) serialText.font = font;
// 标签区:深色底加浅色边框,和上面的大图分开
MakeRect(root, "LabelFrame", new Vector2(0, -201), new Vector2(648, 194), new Color(0.78f, 0.68f, 0.45f, 1f));
MakeRect(root, "LabelBack", new Vector2(0, -201), new Vector2(640, 186), new Color(0.10f, 0.11f, 0.14f, 0.96f));
flip = MakeButton(root, "Flip", new Vector2(0, -326), new Vector2(200, 34), out flipText);
float panelH = RowCount * RowH + 10f + DetailH + GroupGap, panelY = PanelTop - panelH / 2;
MakeRect(root, "LabelFrame", new Vector2(0, panelY), new Vector2(648, panelH + 8), new Color(0.78f, 0.68f, 0.45f, 1f));
MakeRect(root, "LabelBack", new Vector2(0, panelY), new Vector2(640, panelH), new Color(0.10f, 0.11f, 0.14f, 0.96f));
MakeRect(root, "LabelDivider", new Vector2(0, PanelTop - 5 - RowH * CardLabels.AuthRows - DetailH - GroupGap / 2), new Vector2(620, 2), new Color(0.78f, 0.68f, 0.45f, 0.8f));
detailText = MakeText(root, "Detail", new Vector2(-15, PanelTop - 5 - RowH * CardLabels.AuthRows - DetailH / 2), new Vector2(600, DetailH), TextAlignmentOptions.Left, 17);
flip = MakeButton(root, "Flip", new Vector2(0, PanelTop - panelH - 24), new Vector2(200, 30), out flipText);
flip.onClick.AddListener(() => { if (shown != null) Items.Flip(shown); });
for (int i = 0; i < 6; i++)
for (int i = 0; i < RowCount; i++)
{
int row = CardLabels.Rows[i];
float y = -126 - i * RowH;
rowName[i] = MakeText(root, "RowName" + i, new Vector2(-255, y), new Vector2(110, RowH), TextAlignmentOptions.Left, 24);
rowText[i] = MakeText(root, "RowText" + i, new Vector2(-30, y), new Vector2(330, RowH), TextAlignmentOptions.Left, 24);
rowButton[i] = MakeButton(root, "RowButton" + i, new Vector2(245, y), new Vector2(110, RowH - 4), out rowButtonText[i]);
float y = PanelTop - 5 - RowH * (i + 0.5f) - (i >= CardLabels.AuthRows ? DetailH + GroupGap : 0f);
rowName[i] = MakeText(root, "RowName" + i, new Vector2(-255, y), new Vector2(110, RowH), TextAlignmentOptions.Left, 22);
rowText[i] = MakeText(root, "RowText" + i, new Vector2(-30, y), new Vector2(330, RowH), TextAlignmentOptions.Left, 22);
rowButton[i] = MakeButton(root, "RowButton" + i, new Vector2(245, y), new Vector2(110, RowH - 3), out rowButtonText[i]);
rowButton[i].onClick.AddListener(() => Log.Guard("标签栏", () => OnRowButton(row)));
}
}
private static string ValueText(int row, int value) =>
row == CardLabels.RowSpec ? "" : L.T("lbl." + (row == CardLabels.RowFan ? "fan" : row == CardLabels.RowFingers ? "fingers" : row == CardLabels.RowBracket ? "bracket" : row == CardLabels.RowPrint ? "print" : "sticker") + "." + value);
private static string RowKey(int row) => row == CardLabels.RowSpec ? "spec" : row == CardLabels.RowBuild ? "build" : row == CardLabels.RowFan ? "fan" : row == CardLabels.RowFingers ? "fingers"
: row == CardLabels.RowBracket ? "bracket" : row == CardLabels.RowPrint ? "print" : row == CardLabels.RowSerial ? "serial" : "seal";
private static string RowName(int row) => L.T("lbl.row." + (row == CardLabels.RowSpec ? "spec" : row == CardLabels.RowFan ? "fan" : row == CardLabels.RowFingers ? "fingers" : row == CardLabels.RowBracket ? "bracket" : row == CardLabels.RowPrint ? "print" : "sticker"));
private static string ValueText(int row, int value) => row == CardLabels.RowSpec ? "" : L.T("lbl." + RowKey(row) + "." + (row == CardLabels.RowSeal && value == 1 ? 0 : value));
private static string RowName(int row) => L.T("lbl.row." + RowKey(row));
/// <summary>现场有没有卖家 / 鉴定客户在:决定"指出"按钮是否出现。</summary>
private static bool CanPoint(GameItem item)
@@ -203,37 +217,57 @@ namespace Decentralized
int exposed = Items.Exposed(item);
bool owned = GeneralHelper.IsItemOwned(item);
bool canPoint = CanPoint(item), canEdit = owned && HasLabeler();
for (int i = 0; i < 6; i++)
for (int i = 0; i < RowCount; i++)
{
int row = CardLabels.Rows[i];
rowName[i].text = RowName(row);
rowName[i].color = new Color(1f, 0.85f, 0.45f);
rowName[i].color = i < CardLabels.AuthRows ? new Color(1f, 0.85f, 0.45f) : new Color(0.6f, 0.85f, 1f); // 真伪行金黄,成色行浅蓝
rowName[i].fontSize = 22;
string text;
Color color = new Color(0.96f, 0.98f, 1f);
if (claim == null)
{
text = row == CardLabels.RowSpec ? DisplayModel(real.Model) : ValueText(row, real.Get(row));
}
string Show(Claim c) => row == CardLabels.RowSpec ? DisplayModel(c.Model) : ValueText(row, c.Get(row));
if (claim == null) text = Show(real);
else if ((exposed & row) != 0)
{
int r = row == CardLabels.RowSpec ? 0 : real.Get(row);
text = row == CardLabels.RowSpec ? DisplayModel(real.Model) : ValueText(row, r);
bool lied = row == CardLabels.RowSpec ? claim.Value.Model != real.Model : claim.Value.Get(row) != r;
if (lied) { color = new Color(1f, 0.45f, 0.4f); text += L.T("lbl.said", row == CardLabels.RowSpec ? DisplayModel(claim.Value.Model) : ValueText(row, claim.Value.Get(row))); }
text = Show(real);
bool lied = row == CardLabels.RowSpec ? claim.Value.Model != real.Model : claim.Value.Get(row) != real.Get(row);
if (lied) { color = new Color(1f, 0.45f, 0.4f); text += L.T("lbl.said", row == CardLabels.RowBuild ? L.T("lbl.tier." + claim.Value.Tier) : Show(claim.Value)); }
else color = new Color(0.5f, 1f, 0.55f);
}
else
{
text = row == CardLabels.RowSpec ? DisplayModel(claim.Value.Model) : ValueText(row, claim.Value.Get(row));
}
else text = Show(claim.Value);
rowText[i].text = text;
rowText[i].color = color;
bool show = row != CardLabels.RowSpec && ((canPoint && claim != null && (exposed & row) == 0) || canEdit || (canPoint && claim == null));
bool point = canPoint && !canEdit;
rowButton[i].gameObject.SetActive(row != CardLabels.RowSpec ? ((canPoint && (exposed & row) == 0) || canEdit) : canEdit);
bool editable = row != CardLabels.RowBuild;
rowButton[i].gameObject.SetActive(canEdit ? editable : canPoint && (exposed & row) == 0);
rowButtonText[i].text = L.T(canEdit ? "lbl.edit" : "lbl.point");
}
// 型号真伪的依据:没拆穿之前提示去看哪里;拆穿之后列出实际查到的问题
if (detailText != null)
{
bool spec = (exposed & CardLabels.RowSpec) != 0;
string detail = claim == null ? "" : !spec ? L.T("lbl.hint") : Evidence(u, claim.Value, real);
detailText.text = detail;
detailText.color = spec ? new Color(1f, 0.7f, 0.6f) : new Color(0.75f, 0.78f, 0.85f);
}
if (flipText != null) flipText.text = L.T(Items.IsBack(item) ? "inspect.flip.front" : "inspect.flip.back");
// 贴纸上的序列号:只在背面显示
if (serialText != null)
{
serialText.gameObject.SetActive(Items.IsBack(item) && !u.Dead);
serialText.text = CardLabels.SerialText(u);
// 贴纸空白处中心在大图 (142, 91):卡片坐标以图中心为原点,y 向上
serialText.rectTransform.localPosition = new Vector3((142 - 96) * CardScale, 85 + (64 - 91) * CardScale, 0);
}
}
/// <summary>型号真伪的依据:实物与卖家说法对不上的地方(供电口与风扇数、序列号、背面丝印)。</summary>
private static string Evidence(Unit u, Claim claim, Claim real)
{
var parts = new List<string>();
if (claim.Tier != real.Tier) parts.Add(L.T("lbl.ev.build", L.T("lbl.tier." + real.Tier), Hardware.PowerPins[real.Tier], Hardware.FanCount[real.Tier]));
if (real.Serial != 0) parts.Add(L.T("lbl.ev.serial." + real.Serial));
if (real.Print != 0) parts.Add(L.T("lbl.ev.print." + real.Print));
return parts.Count == 0 ? L.T("lbl.ev.none") : L.T("lbl.ev.prefix") + string.Join(";", parts);
}
private static string DisplayModel(string model) => Items.DisplayModel(model);
@@ -244,7 +278,7 @@ namespace Decentralized
var item = shown;
if (GeneralHelper.IsItemOwned(item))
{
// 标签机:改说法,每按一次换下一个取值(型号是在九个型号里轮换)
// 标签机:改说法,每按一次换下一个取值(型号是在八个型号里轮换)
var u = Items.ToUnit(item);
var cur = Items.GetClaim(item) ?? Claim.Truth(u);
if (row == CardLabels.RowSpec)
@@ -252,7 +286,7 @@ namespace Decentralized
var models = Hardware.Specs.Values.Where(s => !s.Asic).Select(s => s.Model).ToList();
Items.EditClaim(item, row, 0, models[(models.IndexOf(cur.Model) + 1) % models.Count]);
}
else Items.EditClaim(item, row, (cur.Get(row) + 1) % 3);
else Items.EditClaim(item, row, row == CardLabels.RowSeal ? (cur.Get(row) == 0 ? 2 : 0) : row == CardLabels.RowBracket ? (cur.Get(row) + 1) % 2 : (cur.Get(row) + 1) % 3);
EmporiumAudioPlayer.Instance?.audioLabeler?.Play();
return;
}
+62 -17
View File
@@ -15,13 +15,14 @@ namespace Decentralized
{
public const string Terminal = "pd_terminal";
public const string Rig = "pd_rig";
public const string Guide = "pd_gpu_guide";
public const string GpuPrefix = "pd_gpu_";
public const string HealthTag = "PD_HEALTH";
public const string FakeTag = "PD_FAKE_OF";
public const string RefurbTag = "PD_REFURBISHED";
public const string DeadTag = "PD_DEAD";
public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", SeedTag = "PD_SEED", ClaimTag = "PD_CLAIM", ExposedTag = "PD_EXPOSED", MarkupTag = "PD_MARKUP";
public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", SeedTag = "PD_SEED", ClaimTag = "PD_CLAIM", ExposedTag = "PD_EXPOSED", MarkupTag = "PD_MARKUP", EditedTag = "PD_EDITED";
public static Action OpenTerminal;
@@ -36,6 +37,7 @@ namespace Decentralized
{
dir.Add(Terminal, CreateTerminal);
dir.Add(Rig, CreateRig);
dir.Add(Guide, CreateGuide);
foreach (var spec in Hardware.Specs.Values.Where(s => !s.Asic))
{
var s = spec;
@@ -61,9 +63,23 @@ namespace Decentralized
return item;
}
/// <summary>显卡鉴定手册:档次对照、序列号规则、外观折扣。和游戏里的鉴定手册一样,是一件物品,说明里就是内容。</summary>
private static GameItem CreateGuide()
{
var item = ItemDirectory.CreateEmptyItem(Guide);
item.SetSpriteAndShapeFromMod(ModInfo.Id, "gpu_guide");
item.name = L.T("item.guide.name");
item.shortDescription = L.T("item.guide.desc");
item.unitValue = 0;
item.EnableTag(GeneralHelper.IMPORTANT_TAG);
item.EnableTag(TypeHelper.NOT_FOR_RESALE);
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
return item;
}
// ---------- 显卡 ----------
/// <summary>RZX70 → "RZX 70",RZX80Ti → "RZX 80 Ti",RZ6800 → "RZ 6800"。</summary>
/// <summary>RZX70 → "RZX 70",RZ800 → "RZ 800"。</summary>
public static string DisplayModel(string model) => Regex.Replace(Regex.Replace(model, @"(?<=[A-Za-z])(?=\d)", " "), @"(?<=\d)(?=[A-Z])", " ");
private static GameItem CreateGpu(HardwareSpec spec)
@@ -95,7 +111,11 @@ namespace Decentralized
var item = ModHelper.SpawnItem(GpuId(model));
var u = new Unit { Model = model, FakeOf = fakeOf, Health = health, Look = CardLook.Generate(rng, health, fakeOf != null) };
Store(item, u);
if (seller != null) SetClaim(item, CardLabels.DealerClaim(u, seller.Value, rng), 0, markupPct);
if (seller != null)
{
GeneralHelper.SetItemOwned(item, false); // 卖家的货:买下之前不属于玩家,说法才有效
SetClaim(item, CardLabels.DealerClaim(u, seller.Value, rng), 0, markupPct);
}
return item;
}
@@ -103,15 +123,21 @@ namespace Decentralized
private static object checkedClient;
private static readonly HashSet<int> checkedItems = new HashSet<int>();
public const double CustomerSpotChance = 0.35;
public const double CustomerSpotChance = 0.50;
public const int RepLossMax = 25;
/// <summary>被顾客看穿时的信誉损失:骗的价差每满一周房租扣 1 点,最少 1,最多 25。</summary>
public static int RepLoss(long gap) => (int)Math.Max(1, Math.Min(RepLossMax, Math.Round(gap / (double)Math.Max(1, PlayerStore.Instance.rentValue))));
/// <summary>
/// 自己的卡摆给想买的顾客:说法里隐瞒的每一项,顾客有 35% 的概率看出来;看出来就拆穿这一项(价格回到真实情况),
/// 自己的卡摆给想买的顾客:说法里隐瞒的每一项,顾客有 50% 的概率看出来;看出来就拆穿这一项(价格回到真实情况),
/// 并且掉一点信誉。每位顾客对每张卡只看一次。说法比实际更差(把好卡说差了)顾客不会去指。
/// </summary>
private static void OnOffered(GameItem item)
public static void OfferForTest(GameItem item) { checkedItems.Clear(); OnOffered(item, true); }
private static void OnOffered(GameItem item, bool force = false)
{
if (item.IsTag(DeadTag) || !GeneralHelper.IsItemOwned(item) || !item.IsRootedInBackInv()) return;
if (item.IsTag(DeadTag) || !GeneralHelper.IsItemOwned(item) || (!force && !item.IsRootedInBackInv())) return;
var ci = PlayerStore.Instance.currentClientInstance;
var client = ci?.GetClientBlueprint();
if (client == null || (client.clientIntent != StoreClient.ClientIntent.BUY && client.clientIntent != StoreClient.ClientIntent.SELLNBUY)) return;
@@ -129,15 +155,17 @@ namespace Decentralized
if ((lies & r) != 0 && rng.Chance(CustomerSpotChance))
{
found |= r;
if (r != CardLabels.RowSpec && CardLabels.IsFakeSign(r, Claim.Truth(u).Get(r))) found |= CardLabels.RowSpec;
if (r != CardLabels.RowSpec && CardLabels.IsEvidence(r, claim.Value, Claim.Truth(u))) found |= CardLabels.RowSpec | CardLabels.RowBuild;
}
if (found == 0) return;
long before = item.unitValue;
item.ModifyTag(ExposedTag, t => t.Enable().SetInt(exposed | found));
Refresh(item);
int rep = RepLoss(before - item.unitValue);
string faction = string.IsNullOrEmpty(client.clientFaction) || StoreReputation.GetStoreReputation(client.clientFaction) == null ? "FACTION_LOWER_LEVEL" : client.clientFaction;
StoreReputation.ModReputation(faction, -2);
StoreReputation.ModReputation(faction, -rep);
EmporiumAudioPlayer.Instance?.audioFail?.Play();
Log.Info($"顾客看出标签不实:{found},价格 {item.unitValue},信誉 −2");
Log.Info($"顾客看出标签不实:{found},价格 {item.unitValue},信誉 −{rep}");
DialogUIManager.Instance.StartDialogue(new Dialogue().SetText("CLIENT", L.T((found & CardLabels.RowSpec) != 0 ? "cust.fake." + rng.Range(1, 2) : "cust.worn." + rng.Range(1, 2))));
}
@@ -149,7 +177,13 @@ namespace Decentralized
return string.IsNullOrEmpty(c.Model) ? (Claim?)null : c;
}
public static int Exposed(GameItem item) => item.IsTag(ExposedTag) ? item.GetTagReadonly(ExposedTag).GetInt() : 0;
/// <summary>已指出的标签。型号一旦被拆穿,四个真伪行(型号、供电与风扇、序列号、丝印)一起亮出真实情况。</summary>
public static int Exposed(GameItem item)
{
int e = item.IsTag(ExposedTag) ? item.GetTagReadonly(ExposedTag).GetInt() : 0;
if ((e & CardLabels.RowSpec) != 0) e |= CardLabels.RowSpec | CardLabels.RowBuild | CardLabels.RowSerial | CardLabels.RowPrint;
return e;
}
public static void SetClaim(GameItem item, Claim claim, int exposed, int markupPct = 0)
{
@@ -188,6 +222,7 @@ namespace Decentralized
var claim = GetClaim(item) ?? Claim.Truth(u);
claim = row == CardLabels.RowSpec ? new Claim { Model = model ?? claim.Model, Fan = claim.Fan, Fingers = claim.Fingers, Bracket = claim.Bracket, Print = claim.Print, Sticker = claim.Sticker } : claim.With(row, value);
int exposed = Exposed(item) & ~row;
item.EnableTag(EditedTag);
SetClaim(item, claim, exposed);
}
@@ -204,6 +239,13 @@ namespace Decentralized
private static void SyncValue(GameItem item)
{
if (item.IsTag(DeadTag)) return;
// 买下之后卖家的说法作废:卡回到真实情况,除非玩家用标签机自己改过
if (GeneralHelper.IsItemOwned(item) && item.IsTag(ClaimTag) && !item.IsTag(EditedTag))
{
item.ModifyTag(ClaimTag, t => t.Disable());
item.ModifyTag(ExposedTag, t => t.Disable());
item.ModifyTag(MarkupTag, t => t.Disable());
}
var u = ToUnit(item);
string desc = Desc(item, u);
if (item.shortDescription != desc) item.shortDescription = desc;
@@ -217,7 +259,7 @@ namespace Decentralized
public static Unit ToUnit(GameItem item)
{
string model = item.identifier.Substring(GpuPrefix.Length);
model = Hardware.Specs.Keys.First(k => k.ToLowerInvariant() == model);
model = Hardware.Specs.Keys.FirstOrDefault(k => k.ToLowerInvariant() == model) ?? "RZX60";
var u = new Unit { Model = model, Health = 100 };
if (item.IsTag(HealthTag)) u.Health = item.GetTagReadonly(HealthTag).GetDouble();
if (item.IsTag(FakeTag)) u.FakeOf = item.GetTagReadonly(FakeTag).GetString();
@@ -253,7 +295,7 @@ namespace Decentralized
item.SetSpriteAndShapeFromMod(ModInfo.Id, item.IsTag(BackTag) ? "gpu_burnt_b" : "gpu_burnt_f");
return;
}
item.SetSpriteAndShapeFromMod(ModInfo.Id, (label.Brand == Hardware.Probidia ? "gpu_probidia" : "gpu_pmd") + (item.IsTag(BackTag) ? "_b" : "_f"));
item.SetSpriteAndShapeFromMod(ModInfo.Id, LookKeys(item, new List<string>()));
SyncValue(item);
}
@@ -266,6 +308,9 @@ namespace Decentralized
}
/// <summary>当前面的底图名与叠加图名:正面三个维度,背面丝印与贴纸;烧毁的卡不叠。</summary>
/// <summary>实物上的风扇数:由真实芯片的档次决定(低档次的板子套高档次的壳,风扇数露馅)。</summary>
public static int FansOf(Unit u) => Hardware.FanCount[Hardware.Specs[u.FakeOf ?? u.Model].Tier];
public static string LookKeys(GameItem item, List<string> overlays)
{
var u = ToUnit(item);
@@ -275,18 +320,18 @@ namespace Decentralized
if (u.Dead) return back ? "gpu_burnt_b" : "gpu_burnt_f";
if (!back)
{
if (l.Fan > 0) overlays.Add("gpu_ov_fan" + l.Fan);
if (l.Fan > 0) overlays.Add($"gpu_ov_fan{l.Fan}_{FansOf(u)}");
if (l.Fingers > 0) overlays.Add("gpu_ov_fin" + l.Fingers);
if (l.Bracket > 0) overlays.Add("gpu_ov_brk" + l.Bracket);
if (l.Bracket > 0) overlays.Add("gpu_ov_brk1");
}
else
{
// 丝印:0 标称型号;1 印的是真实芯片型号;2 标称型号但字歪(重印过)
string model = l.Print == 1 && u.FakeOf != null ? u.FakeOf : u.Model;
overlays.Add((l.Print == 2 ? "gpu_ov_txs_" : "gpu_ov_txt_") + model.ToLowerInvariant());
if (l.Sticker > 0) overlays.Add("gpu_ov_stk" + l.Sticker);
if (l.Sticker == 2) overlays.Add("gpu_ov_stk2");
}
return brand + (back ? "_b" : "_f");
return brand + (back ? "_b" : "_f_t" + Hardware.Specs[u.FakeOf ?? u.Model].Tier);
}
public static Sprite ModSprite(string key)
+81 -15
View File
@@ -18,15 +18,12 @@ 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.desc,This card no longer works.
term.title,Trading Terminal
tab.wallet,Wallet
tab.trade,Trade
tab.report,Report
wallet.line,"{0}: {1} (about {2} credits)"
wallet.empty,The wallet is empty.
wallet.total,Total value: {0} credits
wallet.taint,Tainted value: {0} credits
trade.amount,Amount
trade.hint,Buy spends the amount in the quote coin. Sell sells the amount of the traded coin.
trade.deposit,Deposit credits as CRC
trade.withdraw,Withdraw CRC as credits
trade.pair,"{0}/{1} ({2})"
@@ -78,27 +75,15 @@ point.worn.1,"...Fine, it has that. I'll knock something off."
point.worn.2,"Sharp eyes. All right, this price."
point.fake.1,"...You caught it. It's a reflashed one. I'll price it for what it is."
point.fake.2,"Tch, not the original chip. Fine, the real chip's price."
lbl.row.spec,"Model"
lbl.row.fan,"Fans"
lbl.row.fingers,"Contacts"
lbl.row.bracket,"Bracket"
lbl.row.print,"Back print"
lbl.row.sticker,"Sticker"
lbl.fan.0,"Blades clean"
lbl.fan.1,"Blades dusty"
lbl.fan.2,"Blade chipped"
lbl.fingers.0,"Bright"
lbl.fingers.1,"Dull"
lbl.fingers.2,"Oxidised"
lbl.bracket.0,"Intact, three ports"
lbl.bracket.1,"Scratched"
lbl.bracket.2,"Wrong port count"
lbl.print.0,"Matches the model"
lbl.print.1,"Another model printed"
lbl.print.2,"Reprinted, crooked"
lbl.sticker.0,"Printed serial, seal intact"
lbl.sticker.1,"Handwritten serial"
lbl.sticker.2,"Seal missing"
lbl.said," (seller said: {0})"
lbl.point,"Point out"
lbl.edit,"Edit"
@@ -106,3 +91,84 @@ cust.fake.1,"That's not what the label says. You trying to pass off a fake?"
cust.fake.2,"The model doesn't match. That isn't the chip it claims."
cust.worn.1,"That's not what you wrote. You left out the flaws."
cust.worn.2,"The condition on the label is false. Not at this price."
lbl.row.build,"Power & fans"
lbl.row.fan,"Blades"
lbl.row.fingers,"Contacts"
lbl.row.bracket,"Bracket"
lbl.row.print,"Back print"
lbl.row.serial,"Serial"
lbl.row.seal,"Seal"
lbl.build.0,"Entry: 6-pin, single fan"
lbl.build.1,"Mid: 8-pin, two fans"
lbl.build.2,"High: 8-pin, three fans"
lbl.build.3,"Flagship: dual 8-pin, three fans"
lbl.bracket.0,"Intact"
lbl.bracket.1,"Scratched"
lbl.serial.0,"Valid"
lbl.serial.1,"Bad factory code or length"
lbl.serial.2,"Tier code does not match"
lbl.seal.0,"Intact"
lbl.seal.2,"Missing"
lbl.tier.0,"Entry"
lbl.tier.1,"Mid"
lbl.tier.2,"High"
lbl.tier.3,"Flagship"
item.guide.name,"Graphics card appraisal guide"
item.guide.desc,"Tiers: entry has a 6-pin connector and one fan; mid 8-pin and two fans; high 8-pin and three fans; flagship dual 8-pin and three fans. Model names are display only: Probidia RZX 60 / 70 / 80 / 90, PMD RZ 70 / 80 / 800 / 900, lowest to highest tier. Serial: prefix PB (Probidia) or MD (PMD), tier code 1 to 4, a dash, a two-digit factory code, a dash, four digits. Valid factory codes: PB 11, 12, 21, 34, 47; MD 13, 22, 35, 41, 58. A tier code that does not match the model, or a bad factory code or length, means a fake. Appearance discounts: dusty blades -4%, chipped -12%; dull contacts -4%, oxidised -12%; scratched bracket -2%; missing seal -6%."
lbl.row.spec,"Model authenticity"
lbl.hint,"Check: power connector and fan count, serial, back print"
lbl.ev.prefix,"Evidence: "
lbl.ev.none,"Everything matches the model"
lbl.ev.build,"Hardware is {0} ({1}-pin, {2} fans)"
lbl.ev.serial.1,"Serial: bad factory code or length"
lbl.ev.serial.2,"Serial: tier code does not match"
lbl.ev.print.1,"Print shows another model"
lbl.ev.print.2,"Print was redone"
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."
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}"
poin.account,"Poinbase account: CRC {0}, PSC {1}"
avg.line,"{0} average buy price: {1} credits"
avg.short,"avg. buy {0}"
avg.none,"—"
col.asset,"Asset"
col.amount,"Amount"
col.value,"Value"
col.avg,"Avg. buy"
bank.cash.k,"Credits"
bank.rent.k,"Rent"
bank.rent.v,"{0}, due in {1} days"
bank.crc.k,"Poinbase account (CRC)"
bank.ops,"Deposit / withdraw"
poin.account.h,"Poinbase account"
trade.h,"Trade"
web3.wallet.h,"On-chain wallet"
trade.hint,"Buying pays in the quote coin; |selling sells the traded coin."
bank.hint,"Deposit turns credits into CRC; |withdrawing turns CRC back into credits |and passes compliance review."
col.peg,"Price"
col.status,"Status"
stable.ok,"Pegged"
stable.depeg,"Depegged"
stable.warn,"Warning: staking subsidy pool low"
stable.spiral,"Collapsing"
aml.window,"Withdrawn in the last {0} days: {1} credits (cumulative line {2})"
aml.taint,"Wallet taint share: {0}%"
aml.chance,"At this amount, estimated review chance: {0}%"
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
24 wallet.total
25 wallet.taint
26 trade.amount
trade.hint
27 trade.deposit
28 trade.withdraw
29 trade.pair
75 point.worn.2
76 point.fake.1
77 point.fake.2
lbl.row.spec
lbl.row.fan
lbl.row.fingers
lbl.row.bracket
lbl.row.print
lbl.row.sticker
78 lbl.fan.0
79 lbl.fan.1
80 lbl.fan.2
81 lbl.fingers.0
82 lbl.fingers.1
83 lbl.fingers.2
lbl.bracket.0
lbl.bracket.1
lbl.bracket.2
84 lbl.print.0
85 lbl.print.1
86 lbl.print.2
lbl.sticker.0
lbl.sticker.1
lbl.sticker.2
87 lbl.said
88 lbl.point
89 lbl.edit
91 cust.fake.2
92 cust.worn.1
93 cust.worn.2
94 lbl.row.build
95 lbl.row.fan
96 lbl.row.fingers
97 lbl.row.bracket
98 lbl.row.print
99 lbl.row.serial
100 lbl.row.seal
101 lbl.build.0
102 lbl.build.1
103 lbl.build.2
104 lbl.build.3
105 lbl.bracket.0
106 lbl.bracket.1
107 lbl.serial.0
108 lbl.serial.1
109 lbl.serial.2
110 lbl.seal.0
111 lbl.seal.2
112 lbl.tier.0
113 lbl.tier.1
114 lbl.tier.2
115 lbl.tier.3
116 item.guide.name
117 item.guide.desc
118 lbl.row.spec
119 lbl.hint
120 lbl.ev.prefix
121 lbl.ev.none
122 lbl.ev.build
123 lbl.ev.serial.1
124 lbl.ev.serial.2
125 lbl.ev.print.1
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 poin.account
149 avg.line
150 avg.short
151 avg.none
152 col.asset
153 col.amount
154 col.value
155 col.avg
156 bank.cash.k
157 bank.rent.k
158 bank.rent.v
159 bank.crc.k
160 bank.ops
161 poin.account.h
162 trade.h
163 web3.wallet.h
164 trade.hint
165 bank.hint
166 col.peg
167 col.status
168 stable.ok
169 stable.depeg
170 stable.warn
171 stable.spiral
172 aml.window
173 aml.taint
174 aml.chance
+81 -15
View File
@@ -17,15 +17,12 @@ item.gpu.desc,标称算力 {0} MH/s。标称耗电 {1} kJ/天。成色 {2}。
item.gpu.burnt.name,烧毁的 {0} {1}
item.gpu.burnt.desc,此显卡已无法工作。
term.title,交易终端
tab.wallet,钱包
tab.trade,交易
tab.report,报告
wallet.line,{0}:{1}(约 {2} 信用点)
wallet.empty,钱包为空。
wallet.total,总值:{0} 信用点
wallet.taint,污点:{0} 信用点
trade.amount,数量
trade.hint,买入按计价币的数量支付;卖出按所交易币的数量卖出。
trade.deposit,存入信用点(换成 CRC)
trade.withdraw,提取 CRC(换成信用点)
trade.pair,{0}/{1}({2})
@@ -77,27 +74,15 @@ point.worn.1,"……行,这点毛病是有,算你便宜点。"
point.worn.2,"眼睛挺毒。好吧,这个价。"
point.fake.1,"……被你看出来了。是翻新的货,按真的价给你。"
point.fake.2,"啧,不是原装的。那就按芯片本来的价吧。"
lbl.row.spec,"型号"
lbl.row.fan,"风扇"
lbl.row.fingers,"金手指"
lbl.row.bracket,"挡板"
lbl.row.print,"背面丝印"
lbl.row.sticker,"贴纸封条"
lbl.fan.0,"叶片干净"
lbl.fan.1,"叶片积灰"
lbl.fan.2,"叶片缺角"
lbl.fingers.0,"光亮"
lbl.fingers.1,"发暗"
lbl.fingers.2,"氧化发绿"
lbl.bracket.0,"完好,接口三个"
lbl.bracket.1,"有划痕"
lbl.bracket.2,"接口数不对"
lbl.print.0,"与型号一致"
lbl.print.1,"印着另一个型号"
lbl.print.2,"重印过,字歪"
lbl.sticker.0,"印刷序列号,封条完好"
lbl.sticker.1,"手写序列号"
lbl.sticker.2,"封条已缺失"
lbl.said,"(卖家说:{0})"
lbl.point,"指出"
lbl.edit,"改"
@@ -105,3 +90,84 @@ cust.fake.1,"这不是它标的那块。你拿假货糊弄我?"
cust.fake.2,"型号对不上,里面的芯片根本不是这个。"
cust.worn.1,"跟你写的不一样,毛病你没说。"
cust.worn.2,"标签上的成色是假的吧,这个价我不要。"
lbl.row.build,"供电与风扇"
lbl.row.fan,"叶片"
lbl.row.fingers,"金手指"
lbl.row.bracket,"挡板"
lbl.row.print,"背面丝印"
lbl.row.serial,"序列号"
lbl.row.seal,"封条"
lbl.build.0,"低端:6针,单风扇"
lbl.build.1,"中端:8针,双风扇"
lbl.build.2,"高端:8针,三风扇"
lbl.build.3,"旗舰:双8针,三风扇"
lbl.bracket.0,"完好"
lbl.bracket.1,"有划痕"
lbl.serial.0,"合规"
lbl.serial.1,"工厂码或位数不合规"
lbl.serial.2,"档次码与型号不符"
lbl.seal.0,"完好"
lbl.seal.2,"已缺失"
lbl.tier.0,"低端"
lbl.tier.1,"中端"
lbl.tier.2,"高端"
lbl.tier.3,"旗舰"
item.guide.name,"显卡鉴定手册"
item.guide.desc,"档次:低端 6 针供电、单风扇;中端 8 针、双风扇;高端 8 针、三风扇;旗舰双 8 针、三风扇。型号只是显示名:Probidia 为 RZX 60 / 70 / 80 / 90,PMD 为 RZ 70 / 80 / 800 / 900,按档次从低到高。序列号:前缀 PB(Probidia)或 MD(PMD),接档次码 1 至 4,一道横杠,两位工厂码,一道横杠,四位流水。合法工厂码:PB 为 11、12、21、34、47;MD 为 13、22、35、41、58。档次码与型号不符,或工厂码、位数不对,都是假的。外观折扣:叶片积灰 -4%,缺角 -12%;金手指发暗 -4%,氧化 -12%;挡板划痕 -2%;封条缺失 -6%。"
lbl.row.spec,"型号真伪"
lbl.hint,"依据:供电口与风扇数、序列号、背面丝印"
lbl.ev.prefix,"依据:"
lbl.ev.none,"各项依据都与型号相符"
lbl.ev.build,"实物是{0}({1} 针,{2} 风扇)"
lbl.ev.serial.1,"序列号工厂码或位数不合规"
lbl.ev.serial.2,"序列号档次码与型号不符"
lbl.ev.print.1,"丝印印着另一个型号"
lbl.ev.print.2,"丝印重印过"
news.prehalving.title,"PSC 即将减半"
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}"
poin.account,"Poinbase 账户:CRC {0},PSC {1}"
avg.line,"{0} 买入均价:{1} 信用点"
avg.short,"买入均价 {0}"
avg.none,"—"
col.asset,"币种"
col.amount,"数量"
col.value,"估值"
col.avg,"买入均价"
bank.cash.k,"信用点"
bank.rent.k,"房租"
bank.rent.v,"{0},{1} 天后支付"
bank.crc.k,"Poinbase 账户(CRC)"
bank.ops,"存取"
poin.account.h,"Poinbase 账户"
trade.h,"交易"
web3.wallet.h,"链上钱包"
trade.hint,"买入按计价币的数量支付;|卖出按所交易币的数量卖出。"
bank.hint,"存入把信用点换成 CRC;|提取把 CRC 换回信用点,|提取要过合规审查。"
col.peg,"价格"
col.status,"状态"
stable.ok,"锚定正常"
stable.depeg,"脱锚"
stable.warn,"预兆:质押补贴池告急"
stable.spiral,"崩盘中"
aml.window,"近 {0} 天已提取 {1} 信用点(累计线 {2})"
aml.taint,"钱包污点比例:{0}%"
aml.chance,"按这个数量,预计审查概率:{0}%"
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
23 wallet.total
24 wallet.taint
25 trade.amount
trade.hint
26 trade.deposit
27 trade.withdraw
28 trade.pair
74 point.worn.2
75 point.fake.1
76 point.fake.2
lbl.row.spec
lbl.row.fan
lbl.row.fingers
lbl.row.bracket
lbl.row.print
lbl.row.sticker
77 lbl.fan.0
78 lbl.fan.1
79 lbl.fan.2
80 lbl.fingers.0
81 lbl.fingers.1
82 lbl.fingers.2
lbl.bracket.0
lbl.bracket.1
lbl.bracket.2
83 lbl.print.0
84 lbl.print.1
85 lbl.print.2
lbl.sticker.0
lbl.sticker.1
lbl.sticker.2
86 lbl.said
87 lbl.point
88 lbl.edit
90 cust.fake.2
91 cust.worn.1
92 cust.worn.2
93 lbl.row.build
94 lbl.row.fan
95 lbl.row.fingers
96 lbl.row.bracket
97 lbl.row.print
98 lbl.row.serial
99 lbl.row.seal
100 lbl.build.0
101 lbl.build.1
102 lbl.build.2
103 lbl.build.3
104 lbl.bracket.0
105 lbl.bracket.1
106 lbl.serial.0
107 lbl.serial.1
108 lbl.serial.2
109 lbl.seal.0
110 lbl.seal.2
111 lbl.tier.0
112 lbl.tier.1
113 lbl.tier.2
114 lbl.tier.3
115 item.guide.name
116 item.guide.desc
117 lbl.row.spec
118 lbl.hint
119 lbl.ev.prefix
120 lbl.ev.none
121 lbl.ev.build
122 lbl.ev.serial.1
123 lbl.ev.serial.2
124 lbl.ev.print.1
125 lbl.ev.print.2
126 news.prehalving.title
127 news.prehalving.desc
128 tab.home
129 tab.bank
130 tab.poinbase
131 tab.web3
132 home.enter
133 home.summary
134 home.lastnight
135 entry.bank
136 entry.bank.l1
137 entry.bank.l2
138 entry.poinbase
139 entry.poinbase.l1
140 entry.poinbase.l2
141 entry.web3
142 entry.web3.l1
143 entry.web3.l2
144 bank.cash
145 bank.rent
146 bank.crc
147 poin.account
148 avg.line
149 avg.short
150 avg.none
151 col.asset
152 col.amount
153 col.value
154 col.avg
155 bank.cash.k
156 bank.rent.k
157 bank.rent.v
158 bank.crc.k
159 bank.ops
160 poin.account.h
161 trade.h
162 web3.wallet.h
163 trade.hint
164 bank.hint
165 col.peg
166 col.status
167 stable.ok
168 stable.depeg
169 stable.warn
170 stable.spiral
171 aml.window
172 aml.taint
173 aml.chance
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 B

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Loaded 100 of 128 files, more files were not shown because too many files have changed in this diff. Show more