Inspection overlays designed by agy; appearance dimensions tied to value, wear and death
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
No files matched your search
@@ -568,7 +568,13 @@ if __name__ == '__main__':
|
||||
canvas.png(os.path.join(out, name + '.png'))
|
||||
for name, make in OVERLAYS.items():
|
||||
make().png(os.path.join(out, name + '.png'))
|
||||
import big
|
||||
for name, make in {**big.BIG, **big.BIG_OV}.items():
|
||||
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_'):
|
||||
make().png(os.path.join(out, name + '.png'))
|
||||
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))))
|
||||
print(out)
|
||||
@@ -32,6 +32,19 @@ namespace Decentralized.Domain
|
||||
return new CardLook { Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
|
||||
}
|
||||
|
||||
// 外观维度对应卡的真实状态:价格、损耗、暴毙概率都随之变化,所以看外观就是在估价
|
||||
private static readonly double[] FanValue = { 1.00, 0.96, 0.88 }, FanWear = { 1.00, 1.10, 1.30 }, FanDeath = { 1.00, 1.00, 1.20 };
|
||||
private static readonly double[] FingerValue = { 1.00, 0.96, 0.88 }, FingerDeath = { 1.00, 1.10, 1.30 };
|
||||
|
||||
/// <summary>外观对价值的折扣:风扇积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失(开过封)−6%。</summary>
|
||||
public double ValueFactor => FanValue[Fan] * FingerValue[Fingers] * (Bracket == 1 ? 0.98 : 1.0) * (Sticker == 2 ? 0.94 : 1.0);
|
||||
|
||||
/// <summary>每晚损耗倍数:风扇积灰散热差 ×1.1,缺角 ×1.3。</summary>
|
||||
public double WearFactor => FanWear[Fan];
|
||||
|
||||
/// <summary>暴毙概率倍数:风扇缺角 ×1.2;金手指发暗 ×1.1、氧化 ×1.3(接触不良)。</summary>
|
||||
public double DeathFactor => FanDeath[Fan] * FingerDeath[Fingers];
|
||||
|
||||
/// <summary>只在假卡上出现的取值:玩家一旦看到,就能肯定是假的。</summary>
|
||||
public bool HasFakeSign => Bracket == 2 || Print != 0 || Sticker == 1;
|
||||
|
||||
@@ -87,8 +100,8 @@ namespace Decentralized.Domain
|
||||
return Math.Max(1, (long)Math.Round(spec.Price * f));
|
||||
}
|
||||
|
||||
/// <summary>真实价值(芯片型号 + 健康度)。</summary>
|
||||
public static long True(Unit u) => Of(u.FakeOf ?? u.Model, u.Health);
|
||||
/// <summary>真实价值(芯片型号 + 健康度 + 外观折扣)。</summary>
|
||||
public static long True(Unit u) => Math.Max(1, (long)Math.Round(Of(u.FakeOf ?? u.Model, u.Health) * u.Look.ValueFactor));
|
||||
|
||||
/// <summary>标称价:贩子报的价,按标称型号算;二手卡也折旧,但只按贩子"自称"的健康度。</summary>
|
||||
public static long Quoted(Unit u, double claimedHealth) => Of(u.Model, claimedHealth);
|
||||
|
||||
@@ -103,14 +103,14 @@ namespace Decentralized.Domain
|
||||
public double DeathChance(ModuleBonus b)
|
||||
{
|
||||
double p = Tuning.DeathBase + Tuning.DeathWorn * Math.Pow(1 - Health / 100, 2);
|
||||
return p * Label.DeathFactor * (FakeOf != null ? Tuning.FakeDeathFactor : 1) * b.Death;
|
||||
return p * Label.DeathFactor * (FakeOf != null ? Tuning.FakeDeathFactor : 1) * b.Death * Look.DeathFactor;
|
||||
}
|
||||
|
||||
/// <summary>一夜的损耗与暴毙判定;返回是否在今晚暴毙。</summary>
|
||||
public bool Night(ModuleBonus b, Rng rng)
|
||||
{
|
||||
if (Dead) return false;
|
||||
Health = Math.Max(0, Health - Label.WearFactor * b.Wear);
|
||||
Health = Math.Max(0, Health - Label.WearFactor * b.Wear * Look.WearFactor);
|
||||
if (rng.Chance(DeathChance(b)))
|
||||
{
|
||||
Dead = true;
|
||||
|
||||
@@ -58,6 +58,34 @@ namespace Decentralized
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>检视大图的叠加层。五个维度各自对应一种或几种细节,组合时再加几处同源的小细节(磕碰、开过壳的螺丝、重新粘过的封条)。</summary>
|
||||
public static List<string> BigOverlays(GameItem item)
|
||||
{
|
||||
var o = new List<string>();
|
||||
var u = Items.ToUnit(item);
|
||||
if (u.Dead) return o;
|
||||
var l = u.Look;
|
||||
string P = "gpu_big_ov_";
|
||||
if (!Items.IsBack(item))
|
||||
{
|
||||
if (l.Fan == 1) o.Add(P + (l.Fingers > 0 ? "fan_dust2" : "fan_dust1"));
|
||||
if (l.Fan == 2) { o.Add(P + "fan_chip"); if (l.Fingers == 2) o.Add(P + "fan_oil"); }
|
||||
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.Fan > 0) o.Add(P + "shroud_chip"); }
|
||||
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.Print == 2) o.Add(P + "screw_opened"); // 重印过,说明拆开过
|
||||
if (l.Sticker == 1) o.Add(P + "sticker_hand");
|
||||
if (l.Sticker == 2) o.Add(P + (l.Print != 0 ? "seal_reglued" : "seal_missing"));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
private void Build(InspectionUIManager m)
|
||||
{
|
||||
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||
@@ -92,12 +120,11 @@ namespace Decentralized
|
||||
{
|
||||
foreach (Transform c in cardRoot) Destroy(c.gameObject);
|
||||
var keys = new List<string>();
|
||||
string baseKey = Items.LookKeys(item, keys);
|
||||
keys.Insert(0, baseKey);
|
||||
foreach (var small in keys)
|
||||
string baseKey = Items.LookKeys(item, keys); // 小图键名只用来取底图(品牌与正反面)
|
||||
var layers = new List<string> { "gpu_big_" + baseKey.Substring("gpu_".Length) };
|
||||
layers.AddRange(BigOverlays(item));
|
||||
foreach (var k in layers)
|
||||
{
|
||||
// 检视用大图:键名对应小图(gpu_probidia_f → gpu_big_probidia_f,gpu_ov_fan1 → gpu_big_ov_fan1)
|
||||
string k = small.StartsWith("gpu_ov_") ? "gpu_big_ov_" + small.Substring("gpu_ov_".Length) : "gpu_big_" + small.Substring("gpu_".Length);
|
||||
var sprite = Items.ModSprite(k);
|
||||
if (sprite == null) continue;
|
||||
var go = new GameObject(k, typeof(RectTransform), typeof(Image));
|
||||
|
||||
|
Before Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 238 B |
|
After Width: | Height: | Size: 409 B |
|
After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 1011 B |
|
Before Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 380 B |
|
After Width: | Height: | Size: 509 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 285 B |
|
Before Width: | Height: | Size: 983 B |
|
After Width: | Height: | Size: 417 B |
|
After Width: | Height: | Size: 706 B |
|
After Width: | Height: | Size: 437 B |
|
After Width: | Height: | Size: 431 B |
|
After Width: | Height: | Size: 428 B |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 488 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 569 B |
|
After Width: | Height: | Size: 574 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 626 B |
|
After Width: | Height: | Size: 592 B |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 586 B |
|
After Width: | Height: | Size: 689 B |
|
After Width: | Height: | Size: 604 B |
|
After Width: | Height: | Size: 865 B |
|
After Width: | Height: | Size: 876 B |
|
After Width: | Height: | Size: 929 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 859 B |
|
After Width: | Height: | Size: 855 B |
|
After Width: | Height: | Size: 883 B |
|
After Width: | Height: | Size: 966 B |
|
After Width: | Height: | Size: 874 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 492 B |
|
After Width: | Height: | Size: 419 B |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 1011 B |
|
Before Width: | Height: | Size: 534 B |
|
Before Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 300 B |
|
Before Width: | Height: | Size: 303 B |
|
Before Width: | Height: | Size: 323 B |
|
Before Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 301 B |
|
Before Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 301 B |
|
Before Width: | Height: | Size: 330 B |
|
Before Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 293 B |
|
Before Width: | Height: | Size: 298 B |
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 292 B |
|
Before Width: | Height: | Size: 288 B |
|
Before Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 291 B |
@@ -156,6 +156,11 @@ internal static class Program
|
||||
var u = new Unit { Model = "RZX90", FakeOf = "RZX60", Health = 80 };
|
||||
Check(CardValue.True(u) == CardValue.Of("RZX60", 80) && CardValue.Quoted(u, 80) == CardValue.Of("RZX90", 80), "真实价值按芯片型号,标称价按标称型号");
|
||||
Check(CardValue.Quoted(u, 80) > CardValue.True(u) * 8, $"假 RZX 90 的标称价 {CardValue.Quoted(u, 80)} 远高于真值 {CardValue.True(u)}");
|
||||
var worn = new Unit { Model = "RZX70", Health = 100, Look = new CardLook { Fan = 2, Fingers = 2, Bracket = 1, Sticker = 2 } };
|
||||
Check(Near(CardLook.Decode("22102").ValueFactor, 0.88 * 0.88 * 0.98 * 0.94, 1e-9) && CardValue.True(worn) == (long)Math.Round(800 * 0.88 * 0.88 * 0.98 * 0.94), "外观折扣乘进真实价值:风扇缺角 + 金手指氧化 + 划痕 + 封条缺失");
|
||||
var clean = new Unit { Model = "RZX70", Health = 80 };
|
||||
var dusty = new Unit { Model = "RZX70", Health = 80, Look = new CardLook { Fan = 2, Fingers = 2 } };
|
||||
Check(dusty.DeathChance(ModuleBonus.None) > clean.DeathChance(ModuleBonus.None) * 1.5 && Near(CardLook.Decode("20000").WearFactor, 1.3, 1e-9), "风扇缺角、金手指氧化抬高暴毙概率与损耗");
|
||||
Check(CardValue.Of("RZX70", 100) == 800 && CardValue.Of("RZX70", 0) == 200 && CardValue.Of("RZX70", 50) == 500, "健康 100 满价、0 剩 25%、50 为 62.5%");
|
||||
});
|
||||
|
||||
|
||||