diff --git a/art/sprites.py b/art/sprites.py
index 02747c1..1277bd0 100644
--- a/art/sprites.py
+++ b/art/sprites.py
@@ -77,79 +77,208 @@ class Canvas:
f.write(data)
-def gpu(accent, accent_dark, burnt=False):
- """显卡(32×32,2×2 格),略微俯视:顶面是带两个风扇的散热罩,左端金属挡板(带接口),
- 前沿露出 PCB 和金手指,右端一道暗侧面,顶上一排供电口。每格中心都不透明。"""
- c = Canvas(32, 32)
- TOP0, TOP1 = 4, 22 # 顶面行范围
- L, R = 6, 29 # 顶面左右(左边是挡板)
- # 金属 I/O 挡板(左端竖条,带两个接口孔)
- c.rect(1, 2, 5, 27, 'b')
- c.rect(1, 2, 1, 27, 'h'); c.rect(5, 2, 5, 27, 'd')
- for y in (6, 12): # 两个视频接口(深色矩形孔,带高光边)
- c.rect(2, y, 4, y + 3, 'k')
- c.rect(2, y, 4, y, 'g')
- c.put(3, 20, 'k'); c.put(3, 22, 'k'); c.put(3, 24, 'k') # 下部散热孔
- # 散热罩顶面
- for y in range(TOP0, TOP1 + 1):
- for x in range(L, R + 1):
- c.put(x, y, 'm')
- c.put(R, y, 'd'); c.put(R - 1, y, 'd')
- for x in range(L, R + 1):
- c.put(x, TOP0, 'l'); c.put(x, TOP0 + 1, 'b')
- # 品牌色条(沿罩顶前沿)
- for x in range(L + 1, R - 1):
- c.put(x, TOP1 - 1, accent)
- c.put(L + 1, TOP1, accent_dark); c.put(R - 2, TOP1, accent_dark)
- # 两个风扇(圆形,叶片用十字 + 斜线)
- for cx in (11, 22):
- cy = 13
- for y in range(cy - 6, cy + 7):
- for x in range(cx - 6, cx + 7):
- d2 = (x - cx) ** 2 + (y - cy) ** 2
- if d2 <= 36:
- c.put(x, y, 'k')
- if 20 <= d2 <= 30:
- c.put(x, y, 'g')
- for k in range(-4, 5):
- if abs(k) >= 1:
- c.put(cx + k, cy, 'g'); c.put(cx, cy + k, 'g')
- c.put(cx - 2, cy - 2, 'g'); c.put(cx + 2, cy + 2, 'g'); c.put(cx + 2, cy - 2, 'g'); c.put(cx - 2, cy + 2, 'g')
- c.rect(cx - 1, cy - 1, cx + 1, cy + 1, 'b')
- c.put(cx, cy, 'h')
- # 前沿:PCB 绿色薄边 + 金手指
- for x in range(L, R + 1):
- c.put(x, TOP1 + 1, 'g2')
- c.put(x, TOP1 + 2, 'g2')
- for x in range(L, R + 1): # 金手指:连续金色带,中间一道缺口(PCIe 键位),带深色触点线
- c.put(x, TOP1 + 3, 'Y'); c.put(x, TOP1 + 4, 'Y' if x % 2 == 0 else 'e')
- for y in (TOP1 + 3, TOP1 + 4):
- c.put(L + 13, y, 'k'); c.put(L + 14, y, 'k')
- # 顶上一排供电口(两个黑色插口)
- for x0 in (L + 3, L + 9):
- c.rect(x0, TOP0 - 1, x0 + 4, TOP0 - 1, 'k')
- # 保证每格中心不透明:下半部(第二行格子,y 16..31 的中心 y=24)落在金手指/PCB 上
- for x in range(L, R + 1):
- for y in range(TOP1 + 1, 28):
- if c.px[y][x] is None:
- c.put(x, y, 'k')
- if burnt:
- for x, y in [(9, 8), (10, 9), (13, 17), (24, 8), (25, 9), (27, 18), (17, 12), (18, 11)]:
- c.put(x, y, 'c')
- for x in range(L + 1, R - 1):
- c.put(x, TOP1 - 1, 'e')
- for x, y in [(15, 5), (16, 5), (20, 21), (26, 14)]:
- c.put(x, y, 'c')
- # 轮廓
+FONT = { # 3×5 像素字(丝印用)
+ 'R': '110101110101101', 'Z': '111001010100111', 'X': '101101010101101', 'P': '110101110100100',
+ 'M': '101111111101101', 'D': '110101101101110', 'T': '111010010010010', 'i': '010000010010010',
+ '0': '111101101101111', '1': '010110010010111', '2': '110001010100111', '3': '110001010001110',
+ '4': '101101111001001', '5': '111100110001110', '6': '011100111101111', '7': '111001010010010',
+ '8': '111101111101111', '9': '111101111001110',
+}
+
+
+def text(c, x, y, s, color, skew=False):
+ """在画布上写 3×5 像素字;skew 为重新印过的效果:字一高一低、间距不匀。"""
+ i = 0
+ for ch in s:
+ if ch == ' ':
+ x += 2
+ continue
+ dy = (i % 2) if skew else 0
+ for k, bit in enumerate(FONT[ch]):
+ if bit == '1':
+ c.put(x + k % 3, y + k // 3 + dy, color)
+ x += 4 + (1 if skew and i % 3 == 2 else 0)
+ i += 1
+
+
+W, H = 48, 32
+TOP0, TOP1 = 4, 22 # 顶面行范围
+L, R = 6, 45 # 顶面左右(左边是挡板)
+FANS = ((17, 13), (34, 13)) # 两个风扇中心
+
+
+def outline(c):
solid = lambda v: v is not None
get = lambda x, y: c.px[y][x] if 0 <= x < c.w and 0 <= y < c.h else None
edge = [(x, y) for y in range(c.h) for x in range(c.w)
if solid(get(x, y)) and any(not solid(get(x + dx, y + dy)) for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)))]
for x, y in edge:
c.put(x, y, 'O')
+
+
+def bracket(c):
+ """左端金属挡板,三个接口孔。"""
+ c.rect(1, 2, 5, 27, 'b')
+ c.rect(1, 2, 1, 27, 'h'); c.rect(5, 2, 5, 27, 'd')
+ for y in (5, 11, 17):
+ c.rect(2, y, 4, y + 3, 'k')
+ c.rect(2, y, 4, y, 'g')
+ c.put(3, 22, 'k'); c.put(3, 24, 'k')
+
+
+def gpu_front(accent, accent_dark, burnt=False):
+ """显卡正面(48×32,3×2 格),略微俯视:顶面是带两个风扇的散热罩,左端金属挡板(三个接口),
+ 前沿露出 PCB 和金手指,右端一道暗侧面。每格中心都不透明。"""
+ c = Canvas(W, H)
+ bracket(c)
+ for y in range(TOP0, TOP1 + 1):
+ for x in range(L, R + 1):
+ c.put(x, y, 'm')
+ c.put(R, y, 'd'); c.put(R - 1, y, 'd')
+ for x in range(L, R + 1):
+ c.put(x, TOP0, 'l'); c.put(x, TOP0 + 1, 'b')
+ for x in range(L + 1, R - 1):
+ c.put(x, TOP1 - 1, accent)
+ c.put(L + 1, TOP1, accent_dark); c.put(R - 2, TOP1, accent_dark)
+ for cx, cy in FANS:
+ for y in range(cy - 7, cy + 8):
+ for x in range(cx - 7, cx + 8):
+ d2 = (x - cx) ** 2 + (y - cy) ** 2
+ if d2 <= 49:
+ c.put(x, y, 'k')
+ if 26 <= d2 <= 38:
+ c.put(x, y, 'g')
+ for k in range(-5, 6):
+ if abs(k) >= 1:
+ c.put(cx + k, cy, 'g'); c.put(cx, cy + k, 'g')
+ for dx, dy in ((-3, -3), (3, 3), (3, -3), (-3, 3)):
+ c.put(cx + dx, cy + dy, 'g')
+ c.rect(cx - 1, cy - 1, cx + 1, cy + 1, 'b')
+ c.put(cx, cy, 'h')
+ for x in range(L, R + 1):
+ c.put(x, TOP1 + 1, 'g2'); c.put(x, TOP1 + 2, 'g2')
+ for x in range(L, R + 1):
+ c.put(x, TOP1 + 3, 'Y'); c.put(x, TOP1 + 4, 'Y' if x % 2 == 0 else 'e')
+ for y in (TOP1 + 3, TOP1 + 4):
+ c.put(L + 17, y, 'k'); c.put(L + 18, y, 'k')
+ for x0 in (L + 4, L + 12): # 顶上供电口
+ c.rect(x0, TOP0 - 1, x0 + 4, TOP0 - 1, 'k')
+ for x in range(L, R + 1):
+ for y in range(TOP1 + 1, 28):
+ if c.px[y][x] is None:
+ c.put(x, y, 'k')
+ if burnt:
+ for x, y in [(12, 8), (13, 9), (18, 17), (35, 8), (36, 9), (40, 18), (24, 12), (25, 11)]:
+ c.put(x, y, 'c')
+ for x in range(L + 1, R - 1):
+ c.put(x, TOP1 - 1, 'e')
+ outline(c)
return c
+def gpu_back(accent, accent_dark, burnt=False):
+ """显卡背面(翻面后):左端仍是挡板;黑色背板,上面一行丝印(型号用叠加图画),
+ 右下一张序列号贴纸,贴纸边上一道封条。"""
+ c = Canvas(W, H)
+ bracket(c)
+ for y in range(TOP0, 28):
+ for x in range(L, R + 1):
+ c.put(x, y, 'd')
+ c.put(R, y, 'k'); c.put(R - 1, y, 'k')
+ for x in range(L, R + 1):
+ c.put(x, TOP0, 'm'); c.put(x, TOP0 + 1, 'm')
+ for x in range(L + 1, R - 1):
+ c.put(x, 16, accent)
+ c.put(L + 1, 17, accent_dark); c.put(R - 2, 17, accent_dark)
+ for x in range(L + 2, R - 2, 5): # 背板螺丝
+ for y in (22, 25):
+ c.put(x, y, 'b'); c.put(x + 1, y, 'k')
+ c.rect(26, 18, 41, 21, 'h') # 序列号贴纸(印刷条码)
+ for x in range(27, 41, 2):
+ c.rect(x, 19, x, 20, 'k')
+ c.rect(44, 17, 44, 22, 'R') # 封条(红色)
+ c.rect(43, 17, 43, 22, 'r2')
+ for x in range(L, R + 1):
+ for y in range(28, 28):
+ pass
+ if burnt:
+ for x, y in [(14, 9), (15, 10), (30, 11), (36, 13), (20, 14)]:
+ c.put(x, y, 'c')
+ outline(c)
+ return c
+
+
+def overlay():
+ return Canvas(W, H)
+
+
+def ov_fan(kind):
+ """风扇叠加:1 积灰(叶片上一层灰),2 叶片缺角(边缘缺一块并露出黑底)。"""
+ c = overlay()
+ for cx, cy in FANS:
+ 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')
+ for dx, dy in ((-4, -1), (-1, -4), (4, -2), (2, 3), (-3, 4)):
+ c.put(cx + dx, cy + dy, 'h')
+ else:
+ for dx, dy in ((4, -4), (5, -4), (5, -3), (4, -3), (3, -4), (5, -5), (6, -3)):
+ c.put(cx + dx, cy + dy, 'k')
+ c.put(cx - 5, cy + 4, 'k'); c.put(cx - 4, cy + 5, 'k'); c.put(cx - 5, cy + 5, 'k')
+ return c
+
+
+def ov_fingers(kind):
+ """金手指叠加:1 发暗(触点变褐),2 氧化发绿。"""
+ c = overlay()
+ for x in range(L, R + 1):
+ for y in (TOP1 + 3, TOP1 + 4):
+ if x in (L + 17, L + 18):
+ continue
+ if kind == 1:
+ c.put(x, y, 'e' if (x + y) % 3 else 'Y')
+ else:
+ c.put(x, y, 'g2' if (x + y) % 2 else 'G')
+ return c
+
+
+def ov_bracket(kind):
+ """挡板叠加:1 划痕;2 接口数量不对(第三个接口被封成一块平板,只剩两个)。"""
+ c = overlay()
+ if kind == 1:
+ for x, y in ((2, 9), (3, 10), (4, 11), (2, 15), (3, 15), (3, 16), (2, 20), (3, 21), (4, 22), (4, 25), (3, 25)):
+ c.put(x, y, 'l' if (x + y) % 2 else 'd')
+ else:
+ c.rect(2, 17, 4, 20, 'b')
+ c.rect(2, 17, 4, 17, 'h')
+ return c
+
+
+def ov_text(model, skew):
+ """背面丝印叠加:写型号。skew 为重新印过的版本(字高低不齐)。"""
+ c = overlay()
+ text(c, 10, 8, model, 'l' if skew else 'h', skew)
+ return c
+
+
+def ov_sticker(kind):
+ """贴纸叠加:1 手写序列号(条码被盖住、写了歪歪扭扭的字);2 封条缺失(只剩撕过的残胶)。"""
+ c = overlay()
+ if kind == 1:
+ c.rect(26, 18, 41, 21, 'h')
+ for x, y in ((27, 20), (28, 19), (29, 20), (30, 19), (32, 20), (33, 19), (33, 21), (35, 19), (36, 20), (37, 19), (38, 21), (39, 20), (40, 19)):
+ c.put(x, y, 'O')
+ else:
+ c.rect(43, 17, 44, 22, 'd')
+ for y in (17, 19, 21, 22):
+ c.put(44, y, 'm')
+ c.put(43, 18, 'm'); c.put(43, 20, 'm')
+ return c
+
+
+MODELS = ['RZX 50', 'RZX 60', 'RZX 70', 'RZX 80 Ti', 'RZX 90', 'RZ 470', 'RZ 580', 'RZ 6800', 'RZ 7900']
+
+
def rig():
"""矿架(48×48,3×3 格):和游戏里其他机器一样的略微俯视(3/4 视角)——顶面是上窄下宽的梯形,
下面是正面,正面里四层显卡位,左下角电源;光从左上来,左亮右暗;轮廓统一深紫、削掉尖角、底下两只脚和一圈阴影。"""
@@ -283,14 +412,33 @@ def terminal():
return c
+def key(model):
+ return model.replace(' ', '').lower()
+
+
SPRITES = {
- 'gpu_probidia': lambda: gpu('G', 'g2'),
- 'gpu_pmd': lambda: gpu('R', 'r2'),
- 'gpu_burnt': lambda: gpu('e', 'c', burnt=True),
+ '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_burnt_f': lambda: gpu_front('e', 'c', burnt=True),
+ 'gpu_burnt_b': lambda: gpu_back('e', 'c', burnt=True),
'rig': rig,
'terminal': terminal,
}
+# 叠加图:和底图同尺寸,位置 (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),
+}
+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))
+
+
def check_cells(canvas, name):
"""游戏按 mod 物品图每格的中心像素是否不透明来判断占格(行从图的下沿往上数)。有空格就报错。"""
cols, rows = canvas.w // 16, canvas.h // 16
@@ -306,6 +454,20 @@ if __name__ == '__main__':
canvas = make()
check_cells(canvas, name)
canvas.png(os.path.join(sys.argv[2], name + '.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
+ # 预览:把各种叠加图叠在底图上
+ def stack(base, *ovs):
+ b = SPRITES[base]()
+ for o in ovs:
+ oc = OVERLAYS[o]()
+ for y in range(oc.h):
+ for x in range(oc.w):
+ 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))
print(sys.argv[2])
else:
out = os.path.normpath(os.path.join(here, '..', 'src', 'Decentralized', 'ModFiles', 'Textures', 'Items'))
@@ -313,4 +475,6 @@ if __name__ == '__main__':
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'))
print(out)
diff --git a/src/Decentralized.Domain/CardLook.cs b/src/Decentralized.Domain/CardLook.cs
index ebce2e1..d05f80d 100644
--- a/src/Decentralized.Domain/CardLook.cs
+++ b/src/Decentralized.Domain/CardLook.cs
@@ -22,6 +22,16 @@ namespace Decentralized.Domain
public const int Variants = 3;
+ /// 五位数字,存在物品标签上:风扇、金手指、挡板、丝印、贴纸。
+ public string Encode() => $"{Fan}{Fingers}{Bracket}{Print}{Sticker}";
+
+ public static CardLook Decode(string s)
+ {
+ if (s == null || s.Length != 5) 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) };
+ }
+
/// 只在假卡上出现的取值:玩家一旦看到,就能肯定是假的。
public bool HasFakeSign => Bracket == 2 || Print != 0 || Sticker == 1;
diff --git a/src/Decentralized.Domain/Mining.cs b/src/Decentralized.Domain/Mining.cs
index 5360761..50b3a82 100644
--- a/src/Decentralized.Domain/Mining.cs
+++ b/src/Decentralized.Domain/Mining.cs
@@ -84,6 +84,8 @@ namespace Decentralized.Domain
public double Health = 100;
public bool Dead;
public bool Refurbished;
+ /// 外观;新卡(正规渠道)没有记录,按全新画。
+ public CardLook Look;
public HardwareSpec Label => Hardware.Specs[Model];
public HardwareSpec Chip => Hardware.Specs[FakeOf ?? Model];
diff --git a/src/Decentralized.Domain/RigSlots.cs b/src/Decentralized.Domain/RigSlots.cs
index 8f7a9a2..0e459f6 100644
--- a/src/Decentralized.Domain/RigSlots.cs
+++ b/src/Decentralized.Domain/RigSlots.cs
@@ -9,7 +9,7 @@ namespace Decentralized.Domain
public static class RigSlots
{
public const int Base = 2, Max = 5;
- public const int CellWidth = 2, CellsPerSlot = 2;
+ public const int CellWidth = 3, CellsPerSlot = 2;
/// 升级到第 n 个槽需要的电子元件(物品 id,件数)。n 从 3 开始。
public static (string Item, int Count) Cost(int slot)
@@ -45,7 +45,7 @@ namespace Decentralized.Domain
return take;
}
- /// 显卡格子的形状字符串:宽 2,高 2 × 最大槽数;前 slots × 2 行可放(0),其余不可放(1)。
+ /// 显卡格子的形状字符串:宽 3(一张卡 3×2 格),高 2 × 最大槽数;前 slots × 2 行可放(0),其余不可放(1)。
public static string Shape(int slots)
{
var sb = new System.Text.StringBuilder();
diff --git a/src/Decentralized/DealerStock.cs b/src/Decentralized/DealerStock.cs
index 1099abd..c2f687c 100644
--- a/src/Decentralized/DealerStock.cs
+++ b/src/Decentralized/DealerStock.cs
@@ -15,6 +15,14 @@ namespace Decentralized
ModHook.OnPlaceInventorInventoryItemLate += OnPlace;
// 开发者模式(F9)里的测试入口:像到了杰克逊店里一样,清空并扩大柜台后上货
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Give rig x3 (test)", () => { for (int i = 0; i < 3; i++) Items.Give(ModHelper.SpawnItem(Items.Rig)); });
+ 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));
+ });
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dr. Jackson stock (test)", () =>
{
EmporiumEntry.Instance.ClearFrontInv();
diff --git a/src/Decentralized/Items.cs b/src/Decentralized/Items.cs
index 01b50e4..7a354e0 100644
--- a/src/Decentralized/Items.cs
+++ b/src/Decentralized/Items.cs
@@ -21,6 +21,7 @@ namespace Decentralized
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";
public static Action OpenTerminal;
@@ -68,14 +69,32 @@ namespace Decentralized
private static GameItem CreateGpu(HardwareSpec spec)
{
var item = ItemDirectory.CreateEmptyItem(GpuId(spec.Model));
- item.SetSpriteAndShapeFromMod(ModInfo.Id, spec.Brand == Hardware.Probidia ? "gpu_probidia" : "gpu_pmd");
item.SetGameItemType("ELECTRONIC");
item.onLoaded = Refresh;
+ // 右键"切换"翻面;外观差异用叠加图画在同一张底图上
+ item.EnableTag(ToggleAppearanceHelper.TOGGLE_APPEARANCE_TAG);
+ item.mayToggleSlotItemFunc = (_, _, _) => true;
+ item.canToggleSlotItemFunc = (_, _, _) => true;
+ item.onToggleSlotItemFunc = (toggled, _, _) => Log.Guard("显卡翻面", () =>
+ {
+ if (toggled.IsTag(BackTag)) toggled.ModifyTag(BackTag, t => t.Disable()); else toggled.EnableTag(BackTag);
+ Refresh(toggled);
+ });
+ item.getItemSpriteModifierForItemFunc = Overlays;
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
Refresh(item);
return item;
}
+ /// 按给定的状态造一张卡(二手卡、假卡):写入标签并生成外观。
+ public static GameItem NewCard(string model, double health, string fakeOf, Rng rng)
+ {
+ 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);
+ return item;
+ }
+
public static Unit ToUnit(GameItem item)
{
string model = item.identifier.Substring(GpuPrefix.Length);
@@ -85,6 +104,7 @@ namespace Decentralized
if (item.IsTag(FakeTag)) u.FakeOf = item.GetTagReadonly(FakeTag).GetString();
u.Refurbished = item.IsTag(RefurbTag);
u.Dead = item.IsTag(DeadTag);
+ if (item.IsTag(LookTag)) u.Look = CardLook.Decode(item.GetTagReadonly(LookTag).GetString());
return u;
}
@@ -94,6 +114,7 @@ namespace Decentralized
if (u.FakeOf != null) item.ModifyTag(FakeTag, s => s.Enable().SetString(u.FakeOf));
if (u.Refurbished) item.EnableTag(RefurbTag);
if (u.Dead) item.EnableTag(DeadTag);
+ item.ModifyTag(LookTag, s => s.Enable().SetString(u.Look.Encode()));
Refresh(item);
}
@@ -107,12 +128,48 @@ namespace Decentralized
item.name = L.T("item.gpu.burnt.name", label.Brand, DisplayModel(label.Model));
item.shortDescription = L.T("item.gpu.burnt.desc");
item.unitValue = Math.Max(1, label.Price / 20);
- item.SetSpriteAndShapeFromMod(ModInfo.Id, "gpu_burnt");
+ 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.name = L.T("item.gpu.name", label.Brand, DisplayModel(label.Model));
item.shortDescription = L.T("item.gpu.desc", Fmt.Num(label.DisplayHash), Fmt.Num(label.Kj), Mathf.RoundToInt((float)u.Health));
- item.unitValue = Math.Max(1, (long)Math.Round(label.Price * u.Health / 100));
+ item.unitValue = CardValue.True(u);
+ }
+
+ private static Sprite ModSprite(string key)
+ {
+ var mod = ModLoader.Mods.Find(m => m.Manifest.ID.Equals(ModInfo.Id));
+ return mod != null && mod.Sprites.TryGetValue(key, out var sprite) ? sprite : null;
+ }
+
+ /// 外观叠加图:正面三个维度,背面丝印与贴纸;烧毁的卡不叠。
+ private static List Overlays(GameItem item)
+ {
+ var list = new List();
+ if (item.IsTag(DeadTag)) return list;
+ var u = ToUnit(item);
+ var keys = new List();
+ if (!item.IsTag(BackTag))
+ {
+ if (u.Look.Fan > 0) keys.Add("gpu_ov_fan" + u.Look.Fan);
+ if (u.Look.Fingers > 0) keys.Add("gpu_ov_fin" + u.Look.Fingers);
+ if (u.Look.Bracket > 0) keys.Add("gpu_ov_brk" + u.Look.Bracket);
+ }
+ else
+ {
+ // 丝印:0 标称型号;1 印的是真实芯片型号;2 标称型号但字歪(重印过)
+ string model = u.Look.Print == 1 && u.FakeOf != null ? u.FakeOf : u.Model;
+ keys.Add((u.Look.Print == 2 ? "gpu_ov_txs_" : "gpu_ov_txt_") + model.ToLowerInvariant());
+ if (u.Look.Sticker > 0) keys.Add("gpu_ov_stk" + u.Look.Sticker);
+ }
+ int priority = 0;
+ foreach (var k in keys)
+ {
+ var sprite = ModSprite(k);
+ if (sprite != null) list.Add(new ItemSpriteModifier(sprite, Vector2.zero, 0, false, priority++, false, Color.white));
+ }
+ return list;
}
// ---------- 矿架 ----------
@@ -126,7 +183,7 @@ namespace Decentralized
var battery = new GameSlotInventory();
var upgrade = new GameSlotInventory();
var modules = new GameGridInventory(MachineHelper.DEFAULT_MODULE_BAY_CONFIG, 7);
- // 显卡每张 2×2 格:一列 2 格宽的格子,每个槽两行;未解锁的行不可放
+ // 显卡每张 3×2 格:3 格宽的格子,每张卡 3×2 格,每个槽两行;未解锁的行不可放
var cards = new GameGridInventory(RigSlots.Shape(RigSlots.Base), RigSlots.CellWidth);
var window = MachineHelper.CreateMachineryWindow(true,
MachineHelper.PowerPanel(battery),
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_burnt.png b/src/Decentralized/ModFiles/Textures/Items/gpu_burnt.png
deleted file mode 100644
index aeff9bf..0000000
Binary files a/src/Decentralized/ModFiles/Textures/Items/gpu_burnt.png and /dev/null differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_b.png b/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_b.png
new file mode 100644
index 0000000..7f86961
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_b.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_f.png b/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_f.png
new file mode 100644
index 0000000..e26942d
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_burnt_f.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk1.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk1.png
new file mode 100644
index 0000000..750d49d
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk1.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk2.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk2.png
new file mode 100644
index 0000000..6c07702
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_brk2.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan1.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan1.png
new file mode 100644
index 0000000..8b0254e
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan1.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan2.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan2.png
new file mode 100644
index 0000000..2a62cc0
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fan2.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin1.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin1.png
new file mode 100644
index 0000000..447e6a6
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin1.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin2.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin2.png
new file mode 100644
index 0000000..70bec06
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_fin2.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk1.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk1.png
new file mode 100644
index 0000000..d3fb04f
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk1.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk2.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk2.png
new file mode 100644
index 0000000..67d62bc
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_stk2.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz470.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz470.png
new file mode 100644
index 0000000..ebeba7f
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz470.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz580.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz580.png
new file mode 100644
index 0000000..aa15725
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz580.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz6800.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz6800.png
new file mode 100644
index 0000000..c12a0c5
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz6800.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz7900.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz7900.png
new file mode 100644
index 0000000..50dbd2f
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rz7900.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx50.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx50.png
new file mode 100644
index 0000000..100ad93
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx50.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx60.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx60.png
new file mode 100644
index 0000000..744cce5
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx60.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx70.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx70.png
new file mode 100644
index 0000000..8e4f770
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx70.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx80ti.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx80ti.png
new file mode 100644
index 0000000..616e556
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx80ti.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx90.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx90.png
new file mode 100644
index 0000000..87a86f2
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txs_rzx90.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz470.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz470.png
new file mode 100644
index 0000000..82f3015
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz470.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz580.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz580.png
new file mode 100644
index 0000000..5d91b8e
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz580.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz6800.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz6800.png
new file mode 100644
index 0000000..71f9566
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz6800.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz7900.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz7900.png
new file mode 100644
index 0000000..47e13be
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rz7900.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx50.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx50.png
new file mode 100644
index 0000000..bd414bd
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx50.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx60.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx60.png
new file mode 100644
index 0000000..a754fc9
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx60.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx70.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx70.png
new file mode 100644
index 0000000..7c4bafc
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx70.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx80ti.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx80ti.png
new file mode 100644
index 0000000..7fc2af9
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx80ti.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx90.png b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx90.png
new file mode 100644
index 0000000..326619a
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_ov_txt_rzx90.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_pmd.png b/src/Decentralized/ModFiles/Textures/Items/gpu_pmd.png
deleted file mode 100644
index 7d340b4..0000000
Binary files a/src/Decentralized/ModFiles/Textures/Items/gpu_pmd.png and /dev/null differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_b.png b/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_b.png
new file mode 100644
index 0000000..ca14969
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_b.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_f.png b/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_f.png
new file mode 100644
index 0000000..686a41a
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_pmd_f.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_probidia.png b/src/Decentralized/ModFiles/Textures/Items/gpu_probidia.png
deleted file mode 100644
index 247a3fc..0000000
Binary files a/src/Decentralized/ModFiles/Textures/Items/gpu_probidia.png and /dev/null differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_b.png b/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_b.png
new file mode 100644
index 0000000..3d714b7
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_b.png differ
diff --git a/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_f.png b/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_f.png
new file mode 100644
index 0000000..37acd0c
Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/gpu_probidia_f.png differ
diff --git a/tests/Decentralized.Domain.Tests/Program.cs b/tests/Decentralized.Domain.Tests/Program.cs
index d125ef8..0469e13 100644
--- a/tests/Decentralized.Domain.Tests/Program.cs
+++ b/tests/Decentralized.Domain.Tests/Program.cs
@@ -170,7 +170,7 @@ internal static class Program
Check(RigSlots.Next(4) == ("rare_electronic", 2) && RigSlots.Insert(ref slots, ref prog, "rare_electronic", 1) == 1 && slots == 4 && prog == 1, "第 5 槽:2 个稀有元件,先投 1 个进度 1/2");
Check(RigSlots.Insert(ref slots, ref prog, "rare_electronic", 1) == 1 && slots == 5, "投满解锁第 5 槽");
Check(RigSlots.Next(5) == null && RigSlots.Insert(ref slots, ref prog, "rare_electronic", 1) == 0, "满 5 槽后不再收");
- Check(RigSlots.Shape(2) == "0000" + "0000" + new string('1', 12) && RigSlots.Shape(5) == new string('0', 20) && RigSlots.Shape(3).Length == 20, "形状:宽 2、共 10 行,前 2 × 槽数行可放");
+ Check(RigSlots.Shape(2) == "000000" + "000000" + new string('1', 18) && RigSlots.Shape(5) == new string('0', 30) && RigSlots.Shape(3).Length == 30, "形状:宽 3、共 10 行,前 2 × 槽数行可放");
});
Test("模块:折算系数 4,算力 +20%、省电 25%、损耗 −30% 封顶;性能加成提高损耗与暴毙", () =>