Magnifier inspection view for graphics cards
Cards are inspectable. While the magnifier inspection tray shows a card, a mod-owned panel under the tray draws the card enlarged with its appearance overlays and a turn-over button. No Harmony patches: a resident component watches the inspection manager. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e643332a41
commit
c05fe41ca9
6 files changed
+169
-17
No files matched your search
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Decentralized.Domain;
|
||||
|
||||
namespace Decentralized
|
||||
@@ -23,6 +24,20 @@ namespace Decentralized
|
||||
Items.Give(Items.NewCard("RZ6800", 30, "RZ580", rng));
|
||||
Items.Give(Items.NewCard("RZ7900", 90, "RZ470", rng));
|
||||
});
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dump inspection UI (test)", () => Log.Guard("检视界面结构", () =>
|
||||
{
|
||||
var sb = new System.Text.StringBuilder("\n");
|
||||
void Walk(Transform t, int depth)
|
||||
{
|
||||
var rt = t as RectTransform;
|
||||
var img = t.GetComponent<UnityEngine.UI.Image>();
|
||||
sb.AppendLine($"{new string(' ', depth * 2)}{t.name} active={t.gameObject.activeSelf} pos={t.localPosition} size={(rt != null ? rt.sizeDelta.ToString() : "-")} scale={t.localScale}{(img != null ? " img=" + (img.sprite != null ? img.sprite.name : "null") : "")}");
|
||||
if (depth < 5) foreach (Transform c in t) Walk(c, depth + 1);
|
||||
}
|
||||
var m = InspectionUIManager.Instance;
|
||||
Walk(m.panel.transform, 0);
|
||||
Log.Info(sb.ToString());
|
||||
}));
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dr. Jackson stock (test)", () =>
|
||||
{
|
||||
EmporiumEntry.Instance.ClearFrontInv();
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Decentralized
|
||||
{
|
||||
/// <summary>
|
||||
/// 放大镜检视显卡时的大图:和香烟一样把物品放大摆在检视台上,正反面各一张,看外观自己判断真假。
|
||||
/// 游戏没有给 mod 开放检视界面,这里不打补丁,只在检视台(InspectionUIManager.panel)下面挂一个自己的界面,
|
||||
/// 用一个常驻的 MonoBehaviour 每帧看一眼是否正在检视显卡。
|
||||
/// </summary>
|
||||
public sealed class GpuInspectView : MonoBehaviour
|
||||
{
|
||||
private static GpuInspectView instance;
|
||||
private RectTransform root, cardRoot;
|
||||
private Button flip;
|
||||
private TextMeshProUGUI flipText;
|
||||
private GameItem shown;
|
||||
private string shownSig;
|
||||
|
||||
public static void Ensure()
|
||||
{
|
||||
if (instance != null) return;
|
||||
var go = new GameObject("pd_gpu_inspect_view");
|
||||
DontDestroyOnLoad(go);
|
||||
instance = go.AddComponent<GpuInspectView>();
|
||||
}
|
||||
|
||||
public static void Remove()
|
||||
{
|
||||
if (instance != null) Destroy(instance.gameObject);
|
||||
instance = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var m = InspectionUIManager.Instance;
|
||||
var item = m != null && m.panel != null && m.panel.activeSelf ? m.currentInspectingItem : null;
|
||||
if (item == null || !Items.IsGpu(item))
|
||||
{
|
||||
if (root != null && root.gameObject.activeSelf) root.gameObject.SetActive(false);
|
||||
shown = null;
|
||||
return;
|
||||
}
|
||||
Log.Guard("显卡检视大图", () =>
|
||||
{
|
||||
if (root == null) Build(m);
|
||||
if (!root.gameObject.activeSelf) root.gameObject.SetActive(true);
|
||||
var keys = new List<string>();
|
||||
string sig = Items.LookKeys(item, keys) + "|" + string.Join(",", keys);
|
||||
if (item != shown || sig != shownSig)
|
||||
{
|
||||
shown = item;
|
||||
shownSig = sig;
|
||||
Redraw(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Build(InspectionUIManager m)
|
||||
{
|
||||
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||
root.SetParent(m.panel.transform, false);
|
||||
root.sizeDelta = new Vector2(1920, 1080);
|
||||
root.localPosition = Vector3.zero;
|
||||
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||
cardRoot.SetParent(root, false);
|
||||
cardRoot.localPosition = new Vector3(0, 40, 0);
|
||||
// 翻面按钮:复制鉴定面板里"发送"按钮的外观
|
||||
var template = m.appraisalPanel.transform.Find("buttonSend");
|
||||
if (template != null)
|
||||
{
|
||||
var go = Instantiate(template.gameObject, root, false);
|
||||
go.name = "Flip";
|
||||
go.transform.localPosition = new Vector3(0, -300, 0);
|
||||
flip = go.GetComponent<Button>();
|
||||
flip.onClick = new Button.ButtonClickedEvent();
|
||||
flip.onClick.AddListener(() => { if (shown != null) Items.Flip(shown); });
|
||||
flipText = go.GetComponentInChildren<TextMeshProUGUI>();
|
||||
// 去掉复制来的按钮上驱动文字的组件(本地化 / 提交逻辑),文字由这里设置
|
||||
foreach (var mb in go.GetComponentsInChildren<MonoBehaviour>(true))
|
||||
{
|
||||
string n = mb.GetType().Name;
|
||||
if (mb is Button || mb is Image || mb is TextMeshProUGUI || mb is CanvasRenderer) continue;
|
||||
if (n.Contains("Local") || n.Contains("Appraisal") || n.Contains("Send")) Destroy(mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Redraw(GameItem item)
|
||||
{
|
||||
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 k in keys)
|
||||
{
|
||||
var sprite = Items.ModSprite(k);
|
||||
if (sprite == null) continue;
|
||||
var go = new GameObject(k, typeof(RectTransform), typeof(Image));
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
rt.SetParent(cardRoot, false);
|
||||
rt.sizeDelta = new Vector2(sprite.rect.width, sprite.rect.height);
|
||||
rt.localScale = Vector3.one * 14f;
|
||||
var img = go.GetComponent<Image>();
|
||||
img.sprite = sprite;
|
||||
img.raycastTarget = false;
|
||||
}
|
||||
if (flipText != null) flipText.text = L.T(Items.IsBack(item) ? "inspect.flip.front" : "inspect.flip.back");
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-17
@@ -70,6 +70,7 @@ namespace Decentralized
|
||||
{
|
||||
var item = ItemDirectory.CreateEmptyItem(GpuId(spec.Model));
|
||||
item.SetGameItemType("MATERIAL");
|
||||
InspectableHelper.InitInspectableItem(item);
|
||||
item.onLoaded = Refresh;
|
||||
// 右键"切换"翻面;外观差异用叠加图画在同一张底图上
|
||||
item.EnableTag(ToggleAppearanceHelper.TOGGLE_APPEARANCE_TAG);
|
||||
@@ -77,8 +78,7 @@ namespace Decentralized
|
||||
item.canToggleSlotItemFunc = (_, _, _) => true;
|
||||
item.onToggleSlotItemFunc = (toggled, _, _) => Log.Guard("显卡翻面", () =>
|
||||
{
|
||||
if (toggled.IsTag(BackTag)) toggled.ModifyTag(BackTag, t => t.Disable()); else toggled.EnableTag(BackTag);
|
||||
Refresh(toggled);
|
||||
Flip(toggled);
|
||||
});
|
||||
item.getItemSpriteModifierForItemFunc = Overlays;
|
||||
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
|
||||
@@ -152,33 +152,51 @@ namespace Decentralized
|
||||
SyncValue(item);
|
||||
}
|
||||
|
||||
private static Sprite ModSprite(string key)
|
||||
public static bool IsBack(GameItem item) => item.IsTag(BackTag);
|
||||
|
||||
public static void Flip(GameItem item)
|
||||
{
|
||||
var mod = ModLoader.Mods.Find(m => m.Manifest.ID.Equals(ModInfo.Id));
|
||||
return mod != null && mod.Sprites.TryGetValue(key, out var sprite) ? sprite : null;
|
||||
if (IsBack(item)) item.ModifyTag(BackTag, t => t.Disable()); else item.EnableTag(BackTag);
|
||||
Refresh(item);
|
||||
}
|
||||
|
||||
/// <summary>外观叠加图:正面三个维度,背面丝印与贴纸;烧毁的卡不叠。</summary>
|
||||
private static List<ItemSpriteModifier> Overlays(GameItem item)
|
||||
/// <summary>当前面的底图名与叠加图名:正面三个维度,背面丝印与贴纸;烧毁的卡不叠。</summary>
|
||||
public static string LookKeys(GameItem item, List<string> overlays)
|
||||
{
|
||||
var list = new List<ItemSpriteModifier>();
|
||||
SyncValue(item); // 归属变化(买下)后价格随之变回真实价值
|
||||
if (item.IsTag(DeadTag)) return list;
|
||||
var u = ToUnit(item);
|
||||
var keys = new List<string>();
|
||||
if (!item.IsTag(BackTag))
|
||||
bool back = IsBack(item);
|
||||
string brand = u.Label.Brand == Hardware.Probidia ? "gpu_probidia" : "gpu_pmd";
|
||||
if (u.Dead) return back ? "gpu_burnt_b" : "gpu_burnt_f";
|
||||
if (!back)
|
||||
{
|
||||
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);
|
||||
if (u.Look.Fan > 0) overlays.Add("gpu_ov_fan" + u.Look.Fan);
|
||||
if (u.Look.Fingers > 0) overlays.Add("gpu_ov_fin" + u.Look.Fingers);
|
||||
if (u.Look.Bracket > 0) overlays.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);
|
||||
overlays.Add((u.Look.Print == 2 ? "gpu_ov_txs_" : "gpu_ov_txt_") + model.ToLowerInvariant());
|
||||
if (u.Look.Sticker > 0) overlays.Add("gpu_ov_stk" + u.Look.Sticker);
|
||||
}
|
||||
return brand + (back ? "_b" : "_f");
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
/// <summary>物品栏里的外观叠加图。</summary>
|
||||
private static List<ItemSpriteModifier> Overlays(GameItem item)
|
||||
{
|
||||
var list = new List<ItemSpriteModifier>();
|
||||
SyncValue(item); // 归属变化(买下)后价格随之变回真实价值
|
||||
if (item.IsTag(DeadTag)) return list;
|
||||
var keys = new List<string>();
|
||||
LookKeys(item, keys);
|
||||
int priority = 0;
|
||||
foreach (var k in keys)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Decentralized
|
||||
Night.Subscribe();
|
||||
DealerStock.Subscribe();
|
||||
UsedDealerClient.Subscribe();
|
||||
GpuInspectView.Ensure();
|
||||
ModHook.OnGameLoadedLate += Session.Load;
|
||||
ModHook.OnNewGameStarted += OnNewGame;
|
||||
Log.Info($"{ModInfo.Name} {ModInfo.Version} 已启用");
|
||||
@@ -27,6 +28,7 @@ namespace Decentralized
|
||||
Night.Unsubscribe();
|
||||
DealerStock.Unsubscribe();
|
||||
UsedDealerClient.Unsubscribe();
|
||||
GpuInspectView.Remove();
|
||||
ModHook.OnGameLoadedLate -= Session.Load;
|
||||
ModHook.OnNewGameStarted -= OnNewGame;
|
||||
}
|
||||
|
||||
@@ -66,3 +66,5 @@ dealer.line1.3,"Picked these up on the road. First-hand price."
|
||||
dealer.line2.1,"See for yourself, turn them over and check the back."
|
||||
dealer.line2.2,"No warranty. Look before you take."
|
||||
dealer.line2.3,"Price is negotiable if you want them."
|
||||
inspect.flip.back,"Turn over"
|
||||
inspect.flip.front,"Front side"
|
||||
|
@@ -65,3 +65,5 @@ dealer.line1.3,"路上收来的货,一手价。"
|
||||
dealer.line2.1,"自己看,背面也翻过来看。"
|
||||
dealer.line2.2,"不保修,看好了再拿。"
|
||||
dealer.line2.3,"要的话价钱好商量。"
|
||||
inspect.flip.back,"翻到背面"
|
||||
inspect.flip.front,"翻到正面"
|
||||
|
Reference in new issue
Block a user