Files
probably-decentralized/src/Decentralized/GpuGuide.cs
T

321 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using Decentralized.Domain;
using UnityEngine;
namespace Decentralized
{
/// <summary>
/// 显卡鉴定指南:游戏的纸质指南(PaperUI),版式和原版鉴定指南一致——笔记本背景、委员会页眉、居中标题、
/// 插图加图注、“价格不变 / -n%”的折价标注、末页的鉴定示例。第一次打开时按当前语言生成并登记,语言变了重新生成。
/// 坐标是设计单位(314×402,屏幕上放大 2 倍)。
/// </summary>
public static class GpuGuide
{
public const string BookId = "gpu_guide";
private const float X = 35f, W = 253f, Top = 80f, Bottom = 368f;
private const string Ink = "#221C1A", Good = "#3F7D4B", Bad = "#9E1F1A";
private static string _locale;
private static LoadedMod _mod;
public static void OnAssetsLoaded(LoadedMod mod)
{
if (mod?.Manifest?.ID == ModInfo.Id) _mod = mod;
}
/// <summary>原版背景、按钮和页眉图按名字找不到时(纸张素材目录没打包),把内存里同名的原版精灵登记给本 mod。</summary>
private static void ProvideMissingPaperSprites()
{
if (_mod == null) return;
foreach (var sprite in Resources.FindObjectsOfTypeAll<Sprite>())
{
string n = sprite.name;
if (n != "notebook_manual" && n != "guide_logo" && !n.StartsWith("paper_buttons")) continue;
if (_mod.PaperSprites.ContainsKey(n) || PaperAssets.FindInCatalog(n) != null) continue;
_mod.PaperSprites[n] = sprite;
}
}
public static void Open(int page = 0)
{
try
{
if (_locale != L.Code || !ModHelper.GetPaperBookIds(ModInfo.Id).Contains(BookId)) Register();
var ui = ModHelper.OpenPaper(ModInfo.Id, BookId);
if (ui == null) { Log.Warn("[GpuGuide] 打不开鉴定指南"); return; }
ui.FlipTo(page);
RaiseAboveModWindows(ui);
}
catch (Exception ex) { Log.Error("[GpuGuide] " + ex); }
}
/// <summary>纸质画布在 mod 窗口所在的层下面:从终端里打开时会被盖住,打开期间调到上面,关上时恢复。</summary>
private static void RaiseAboveModWindows(PaperUI ui)
{
var canvas = ui.Root != null ? ui.Root.GetComponentInParent<Canvas>()?.rootCanvas : null;
if (canvas == null || canvas.sortingOrder >= 950) return;
int old = canvas.sortingOrder;
canvas.sortingOrder = 950;
void Restore(PaperUI u) { u.Closed -= Restore; if (canvas != null) canvas.sortingOrder = old; }
ui.Closed += Restore;
}
// ================= 生成 =================
private static void Register()
{
ProvideMissingPaperSprites();
ModHelper.RemovePaperBooks(ModInfo.Id);
var b = ModHelper.CreatePaper(ModInfo.Id, BookId, "notebook_manual");
b.SetSize(628f, 804f);
b.SetPosition(-450f, 0f);
b.SetChrome();
Page1(b); Page2(b); Page3(b); Page4(b); Page5(b); Page6(b); Page7(b); Page8(b); Page9(b);
ModHelper.RegisterPaperBook(ModInfo.Id, b.Data);
_locale = L.Code;
Log.Info($"[GpuGuide] 已登记显卡鉴定指南({b.Data.pages.Count} 页,{_locale})");
}
private static string T(string key, params object[] a) => TuningText.Render(L.T(key, a));
/// <summary>新的一页:原版的页眉(委员会印章字)和标题。</summary>
private static float NewPage(PaperUI b)
{
b.AddPage(null, "guide_page");
b.SetSlot("header", L.T("gg.header"));
b.SetSlot("title", L.T("item.guide.name"));
b.SetSlot("body", " ");
return Top;
}
private static string Img(string name) => ModInfo.Id + ":" + name;
private static TMPro.TextMeshProUGUI _measurer;
private static float Measure(string text, string style, float size, float width)
{
if (_measurer == null)
{
var go = new GameObject("pd_gpu_guide_measure", typeof(RectTransform));
UnityEngine.Object.DontDestroyOnLoad(go);
_measurer = go.AddComponent<TMPro.TextMeshProUGUI>();
_measurer.enableWordWrapping = true;
_measurer.richText = true;
}
var st = PaperTextStyle.Get(style);
var font = st.Font();
if (font != null) _measurer.font = font;
_measurer.fontSize = size > 0 ? size : st.size;
float h = _measurer.GetPreferredValues(text, width, 0).y;
return h > 1 ? h + 1.5f : (size > 0 ? size : st.size) * 1.5f * (1 + text.Split('\n').Length);
}
/// <summary>一段文字,返回下一块的 y。</summary>
private static float Text(PaperUI b, float y, string text, string style = "body", string color = Ink, string align = "left", float gap = 5f, float x = X, float w = W)
{
float h = Measure(text, style, 0, w);
b.AddText(x, y, w, h, text, style, align: align);
b.Last.color = color;
if (y + h > Bottom) Log.Warn($"[GpuGuide] 内容超出纸面 overflow:{y + h:0} > {Bottom}({text.Substring(0, Math.Min(18, text.Length))})");
return y + h + gap;
}
/// <summary>小节标题(原版的蓝灰色粗体小标题)。</summary>
private static float Section(PaperUI b, float y, string text)
{
b.AddText(X, y, W, 13, text, "label", fontSize: 8f);
b.Last.color = "#2E5A78";
return y + 15f;
}
/// <summary>一张图(底图加叠加层,同一个位置),居中,宽高是显示尺寸;返回图的底边。</summary>
private static float Art(PaperUI b, float x, float y, float scale, params string[] layers)
{
float w = 192f * scale, h = 128f * scale;
foreach (var l in layers)
{
b.AddImage(x, y, w, h, Img(l), "stretch");
}
return y + h;
}
private static string Front(string brand, int tier) => $"gpu_big_{brand}_f_t{tier}";
private static int Fans(int tier) => Hardware.FanCount[tier];
private static void Caption(PaperUI b, float x, float y, float w, string text, string color = null)
{
b.AddText(x, y, w, 10, text, "caption", align: "center");
if (color != null) b.Last.color = color;
}
// ---- 1 概述 ----
private static void Page1(PaperUI b)
{
float y = NewPage(b);
y = Text(b, y, L.T("gg.p1.body"), gap: 6f);
float x = X + (W - 192f * 0.9f) / 2;
float bottom = Art(b, x, y, 0.9f, Front("probidia", 1));
Caption(b, X, bottom + 1, W, L.T("gg.p1.cap"));
y = bottom + 16f;
y = Text(b, y, L.T("gg.p1.makers"), gap: 8f);
Text(b, y, T("gg.p1.note"), "body", Good);
b.Last.fontSize = 7f;
}
// ---- 2 档次 ----
private static void Page2(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.p2.h"));
y = Text(b, y, L.T("gg.p2.body"), gap: 6f);
float cw = W / 2;
for (int row = 0; row < 2; row++)
{
float rowBottom = y;
for (int col = 0; col < 2; col++)
{
int tier = row * 2 + col;
float ax = X + col * cw + (cw - 120f) / 2;
float bottom = Art(b, ax, y, 0.625f, Front("probidia", tier));
Caption(b, X + col * cw, bottom, cw, L.T("gg.tier." + tier));
rowBottom = bottom + 12f;
}
y = rowBottom + 4f;
}
Text(b, y + 2f, L.T("gg.p2.models"), "body");
}
// ---- 3 叶片 ----
private static void Page3(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.fan.h"));
for (int s = 0; s < 3; s++)
{
var layers = new List<string> { Front("probidia", 1) };
if (s == 1) layers.Add("gpu_big_ov_fan_dust2_" + Fans(1));
if (s == 2) { layers.Add("gpu_big_ov_fan_dust1_" + Fans(1)); layers.Add("gpu_big_ov_fan_chip_" + Fans(1)); }
float bottom = Art(b, X + (W - 96f) / 2, y, 0.5f, layers.ToArray());
Caption(b, X, bottom, W, T("gg.fan." + s), s == 0 ? null : Bad);
y = bottom + 15f;
}
}
// ---- 4 金手指 ----
private static void Page4(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.fin.h"));
for (int s = 0; s < 3; s++)
{
var layers = new List<string> { Front("probidia", 1) };
if (s == 1) layers.Add("gpu_big_ov_fin_dim");
if (s == 2) layers.Add("gpu_big_ov_fin_oxide");
float bottom = Art(b, X + (W - 96f) / 2, y, 0.5f, layers.ToArray());
Caption(b, X, bottom, W, T("gg.fin." + s), s == 0 ? null : Bad);
y = bottom + 15f;
}
}
// ---- 5 挡板与封条 ----
private static void Page5(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.brk.h"));
float cw = W / 2;
float bottom = y;
for (int s = 0; s < 2; s++)
{
var layers = new List<string> { Front("probidia", 1) };
if (s == 1) layers.Add("gpu_big_ov_brk_scratch");
float bx = Art(b, X + s * cw + (cw - 120f) / 2, y, 0.625f, layers.ToArray());
Caption(b, X + s * cw, bx, cw, T("gg.brk." + s), s == 0 ? null : Bad);
bottom = bx + 12f;
}
y = bottom + 10f;
y = Section(b, y, L.T("gg.seal.h"));
bottom = y;
for (int s = 0; s < 2; s++)
{
var layers = new List<string> { "gpu_big_probidia_b", "gpu_big_ov_print_normal_rtx70" };
if (s == 1) layers.Add("gpu_big_ov_seal_missing");
float bx = Art(b, X + s * cw + (cw - 120f) / 2, y, 0.625f, layers.ToArray());
Caption(b, X + s * cw, bx, cw, T(s == 0 ? "gg.seal.0" : "gg.seal.2"), s == 0 ? null : Bad);
bottom = bx + 12f;
}
Text(b, bottom + 8f, L.T("gg.wear.note"), "body");
}
// ---- 6 序列号 ----
private static void Page6(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.serial.h"));
y = Text(b, y, L.T("gg.serial.body"), gap: 6f);
float bottom = Art(b, X + (W - 150f) / 2, y, 0.78f, "gpu_big_probidia_b", "gpu_big_ov_print_normal_rtx80");
Caption(b, X, bottom + 1, W, L.T("gg.serial.cap"));
y = bottom + 16f;
y = Text(b, y, L.T("gg.serial.pb"), gap: 4f);
y = Text(b, y, L.T("gg.serial.md"), gap: 8f);
Text(b, y, L.T("gg.serial.rule"), "body");
}
// ---- 7 背面丝印 ----
private static void Page7(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.print.h"));
string[] kinds = { "normal", "stamp", "skew" };
for (int s = 0; s < 3; s++)
{
string model = s == 1 ? "rtx60" : "rtx80";
float bottom = Art(b, X + (W - 96f) / 2, y, 0.5f, "gpu_big_probidia_b", $"gpu_big_ov_print_{kinds[s]}_{model}");
Caption(b, X, bottom, W, L.T("gg.print." + s), s == 0 ? null : Bad);
y = bottom + 14f;
}
Text(b, y, L.T("gg.print.note"), "body");
}
// ---- 8 卖家与指出 ----
private static void Page8(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.seller.h"));
y = Text(b, y, T("gg.seller.body"), gap: 8f);
y = Section(b, y, L.T("gg.point.h"));
Text(b, y, T("gg.point.body"), "body");
}
// ---- 9 鉴定示例 ----
private static void Page9(PaperUI b)
{
float y = NewPage(b);
y = Section(b, y, L.T("gg.ex.h"));
const int health = 60;
var u = new Unit
{
Model = "RTX80", Health = health,
Look = new CardLook { Seed = 0, Fan = 2, Fingers = 1, Sticker = 2 },
};
long baseV = CardValue.Of("RTX80", health);
long fan = (long)Math.Round(baseV * Tuning.CardFanChipped), fin = (long)Math.Round(baseV * Tuning.CardFingersDark), seal = (long)Math.Round(baseV * Tuning.CardSealMissing);
long total = CardLabels.Value(u, null, 0);
var layers = new[] { Front("probidia", 2), "gpu_big_ov_fan_dust1_" + Fans(2), "gpu_big_ov_fan_chip_" + Fans(2), "gpu_big_ov_fin_dim" };
float bottom = Art(b, X + (W - 150f) / 2, y, 0.78f, layers);
y = bottom + 6f;
y = Text(b, y, L.T("gg.ex.card", "Probidia RTX 80", health), "body", align: "center", gap: 10f);
const float lx = X + 38f, lw = W - 76f;
y = Text(b, y, L.T("gg.ex.base", baseV), "caption", align: "right", gap: 3f, x: lx, w: lw);
y = Text(b, y, L.T("gg.ex.fan", fan), "caption", Bad, "right", 3f, lx, lw);
y = Text(b, y, L.T("gg.ex.fin", fin), "caption", Bad, "right", 3f, lx, lw);
y = Text(b, y, L.T("gg.ex.seal", seal), "caption", Bad, "right", 6f, lx, lw);
b.AddLine(lx, y, lx + lw, y, 1f, "#6A6458");
y += 6f;
y = Text(b, y, L.T("gg.ex.total", total), "label", Ink, "right", 10f, lx, lw);
Text(b, y, L.T("gg.ex.note"), "caption", Ink, "center");
}
}
}