界面与按钮
基本信息
| 属性 | 值 |
|---|---|
| GUI ID | 仅 1(GuiHandler 中 if (id == 1),其他 ID 返回 null) |
| 注册方式 | NetworkRegistry.INSTANCE.registerGuiHandler(this, new GuiHandler()) |
| 客户端 GUI | lain.mods.cos.client.GuiCosArmorInventory extends InventoryEffectRenderer |
| 服务端容器 | lain.mods.cos.inventory.ContainerCosArmor |
| 界面底图 | cosmeticarmorreworked:textures/gui/cosarmorinventory.png |
| 界面尺寸 | 176 × 166(GuiEvents 硬编码用于按钮定位) |
| 注入方式 | Forge GuiScreenEvent.InitGuiEvent.Post / ActionPerformedEvent.Post / DrawScreenEvent.Post |
| 权限 | 无。ContainerCosArmor.canInteractWith() 恒为 true |
GuiHandler.getServerGuiElement() 用服务端的 getCosArmorInventory(uuid),getClientGuiElement() 用客户端的 getCosArmorInventoryClient(uuid)——两侧容器结构完全一致(见 时装护甲物品栏)。
按钮 ID 总表
本 mod 共注入 4 类、7 个按钮实例,全部由 GuiEvents.guiPostInit() 在原版背包界面和成品界面同时创建:
| 按钮 ID | 类 | 尺寸 | 定位(相对 guiLeft/guiTop) |
作用 |
|---|---|---|---|---|
| 76 | GuiCosArmorButton |
10×10 | +66, +67 |
在原版背包 ↔ 成品界面之间切换 |
| 77 | GuiCosArmorToggleButton |
5×5 | +60, +72 |
全局开关 客户端护甲渲染替换 |
| 80 | GuiCosArmorToggleButton |
5×5 | +97, +61(+7 + 18×3) |
头盔槽(存储下标 0)皮肤模式开关 |
| 81 | GuiCosArmorToggleButton |
5×5 | +97, +43 |
胸甲槽(存储下标 1) |
| 82 | GuiCosArmorToggleButton |
5×5 | +97, +25 |
护腿槽(存储下标 2) |
| 83 | GuiCosArmorToggleButton |
5×5 | +97, +7 |
靴子槽(存储下标 3) |
按钮 80~83 只在成品界面生效:
initGui()是GuiCosArmorInventory的覆写方法,在原版背包界面里不会被调用;GuiEvents只创建 76 和 77 两个按钮。按钮 76~83 也会在原版背包界面上出现(ID 76/77),其行为分支靠event.gui instanceof GuiCosArmorInventory区分。
按钮 76:界面切换
在 GuiEvents.guiPostAction() 中处理,按所在界面分两种路径:
| 点击时所在界面 | 行为 | 发出的封包 |
|---|---|---|
原版背包(GuiInventory) |
保持界面不变,交给服务端 openGui(..., 1, ...) 切换 |
PacketOpenCosArmorInventory(判别字 3) |
成品界面(GuiCosArmorInventory) |
客户端先 displayGuiScreen(new GuiInventory(...)),服务端再把 openContainer 改回 inventoryContainer |
PacketOpenNormalInventory(判别字 4) |
两条路径都是客户端先本地切界面、服务端封包后到,因为容器状态无法跨端直接同步。详见 网络封包。
按钮图标取自同一张贴图:普通态 drawTexturedModalRect(x, y, 10, 166, 10, 10),悬停态 (x, y, 0, 166, 10, 10),用混合打开。悬停提示由 GuiEvents.guiPostDraw() 在 DrawScreenEvent.Post 阶段绘制,文本来自 I18n.format(displayString):原版背包界面为 cos.gui.buttonCos,成品界面为 cos.gui.buttonNormal。
按钮 77 / 80~83 的状态绘制
GuiCosArmorToggleButton.drawButton() 贴图取 (0 + 5 × state, 176) 处的 5×5 像素,所以 state 字段直接决定贴图列偏移:state = 0 取第 0 列,state = 1 取第 5 列。初始化时 GuiEvents 用 isRenderActive() ? 0 : 1、成品界面用 isSkinArmor(j) ? 1 : 0 预置,因此渲染开启 = 状态 0,渲染关闭 = 状态 1(按钮 77 的语义与 80~83 相反)。
GuiScreenTooltipHelper 的作用
GuiEvents.guiPostDraw() 需要遍历 event.gui.buttonList 并调用 GuiScreen.func_146283_a(...)(原版 drawHoveringText,SRG 名)。这两个成员在原版里是 protected,模组自己的类无法访问。net.minecraft.client.gui.GuiScreenTooltipHelper 通过把自己放进 net.minecraft.client.gui 包来解决——Java 的 protected 成员在同包内可直接访问,因此无需 Mixin、无需 Access Transformer、无需 coremod:
| 方法 | 实现 | 对应原版成员 |
|---|---|---|
static void drawTooltip(GuiScreen, List<String>, int, int) |
screen.func_146283_a(textLines, mouseX, mouseY) |
GuiScreen#func_146283_a(即 drawHoveringText) |
static List<GuiButton> getButtonList(GuiScreen) |
return screen.buttonList; |
GuiScreen#buttonList 字段 |
该类是 final + 私有构造函数,两个方法都是纯转发,不含任何逻辑。它只服务于「给按钮加悬停提示」这一件事,并且提示只会在按钮可见且被悬停时绘制(cosButton.visible && cosButton.isHovered() && tooltip != null),且遍历中 break,因此同一时刻最多显示一个按钮提示。
界面内嵌功能
- 成品界面继承
InventoryEffectRenderer,因此药水附魔的装备在界面内会渲染光效。 - 2×2 合成网格可用,产物槽禁止 Shift 取货(见 时装护甲物品栏)。
- 界面中会绘制可自由转动的玩家模型:
drawPlayerModel()复制自原版GuiInventory,通过鼠标 X/Y 偏移(xSizeFloat/ySizeFloat,在drawScreen()末尾记录)驱动rotationYaw/rotationPitch。 - 按钮 0/1 被复用为原版「成就」/「统计」界面(
GuiAchievements/GuiStats),但GuiCosArmorInventory.initGui()先buttonList.clear()再super.initGui(),因此重建的是GuiContainer的默认按钮。