0.8.1: UI cleanup pass on transactional screens
Build / build (push) Has been cancelled

* Counting House screen:
  - 4-cell grid (was 5-col 5x2 wasted slots) with item icons centered
  - Inventory count badge bottom-right of each item cell
  - Detail pane: friendly display name, item id, on-hand count,
    list price, lifetime sold, effective fee, payout per unit
  - Sell 1/16/64 buttons grey out (and no-op) when you don't have
    enough of the selected item in inventory
* Bottom action row on all transactional screens: replaced "Refresh"
  with a single smart Back/Close button that returns to the Hub when
  opened from there. Fixes the overlap with sub-panel internal buttons
  that were caused by Refresh sitting at bottom-right.
* Panel heights reduced from (height - 30) to (height - 50) so the
  internal sell/action buttons don't clash with the screen-level
  action row.
* Same Back/Close pattern applied to TierUpgrade, Bounty, Plot, Journal.
* Removed "shop" alias from OpenScreenDispatcher — Counting House
  responds only to "counthouse" now. (No NPCs exist yet so this is safe.)
* Added "journal" as the canonical screen name for the Adventurer's
  Journal (replaces "questlog" — both keys still accepted in the
  command suggestion list for now).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-08 18:14:20 +00:00
parent 665c1a8442
commit 3ecb23ecdd
9 changed files with 96 additions and 86 deletions
+1 -1
View File
@@ -19,5 +19,5 @@ loader_version_range=[1,)
mod_id=bnstoolkit mod_id=bnstoolkit
mod_name=Brass and Sigil Toolkit mod_name=Brass and Sigil Toolkit
mod_license=All Rights Reserved mod_license=All Rights Reserved
mod_version=0.8.0 mod_version=0.8.1
mod_group_id=uk.sijbers.bnstoolkit mod_group_id=uk.sijbers.bnstoolkit
@@ -25,9 +25,8 @@ public final class OpenScreenDispatcher {
case "tier" -> { NetSend.requestSnapshot("TIER"); new TierUpgradeScreen().openGui(); } case "tier" -> { NetSend.requestSnapshot("TIER"); new TierUpgradeScreen().openGui(); }
case "bounty" -> { NetSend.requestSnapshot("BOUNTY"); new BountyBoardScreen().openGui(); } case "bounty" -> { NetSend.requestSnapshot("BOUNTY"); new BountyBoardScreen().openGui(); }
case "plot" -> { NetSend.requestSnapshot("PLOT"); new PlotPurchaseScreen().openGui(); } case "plot" -> { NetSend.requestSnapshot("PLOT"); new PlotPurchaseScreen().openGui(); }
case "counthouse", "shop" case "counthouse" -> { NetSend.requestSnapshot("SELL"); new CountingHouseScreen().openGui(); }
-> { NetSend.requestSnapshot("SELL"); new CountingHouseScreen().openGui(); } case "journal" -> { NetSend.requestSnapshot("ALL"); new QuestLogScreen().openGui(); }
case "questlog" -> { NetSend.requestSnapshot("ALL"); new QuestLogScreen().openGui(); }
case "hub" -> { NetSend.requestSnapshot("ALL"); new HubScreen().openGui(); } case "hub" -> { NetSend.requestSnapshot("ALL"); new HubScreen().openGui(); }
default -> BnsToolkit.LOG.warn("[bnstoolkit] unknown screen requested: {}", which); default -> BnsToolkit.LOG.warn("[bnstoolkit] unknown screen requested: {}", which);
} }
@@ -36,25 +36,22 @@ public final class BountyBoardScreen extends BaseBnsScreen {
public void addWidgets() { public void addWidgets() {
BountyList list = new BountyList(this); BountyList list = new BountyList(this);
list.setPos(8, contentTop()); list.setPos(8, contentTop());
list.setSize(170, getHeight() - 30); list.setSize(170, getHeight() - 50);
add(list); add(list);
BountyDetail detail = new BountyDetail(this); BountyDetail detail = new BountyDetail(this);
detail.setPos(186, contentTop()); detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30); detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail); add(detail);
add(SimpleTextButton.create(this, add(BaseBnsScreen.makeBackOrClose(this));
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("BOUNTY")));
} }
@Override @Override
public void alignWidgets() { public void alignWidgets() {
Widget rf = getWidgets().get(2); Widget close = getWidgets().get(2);
rf.setSize(60, 16); close.setSize(72, 16);
rf.setPos(getWidth() - 68, getHeight() - 22); close.setPos(getWidth() - 80, getHeight() - 22);
} }
private static final class BountyList extends Panel { private static final class BountyList extends Panel {
@@ -2,13 +2,12 @@ package uk.sijbers.bnstoolkit.client.screens;
import dev.ftb.mods.ftblibrary.icon.Color4I; import dev.ftb.mods.ftblibrary.icon.Color4I;
import dev.ftb.mods.ftblibrary.icon.Icon; import dev.ftb.mods.ftblibrary.icon.Icon;
import dev.ftb.mods.ftblibrary.icon.Icons;
import dev.ftb.mods.ftblibrary.icon.ItemIcon;
import dev.ftb.mods.ftblibrary.ui.Panel; import dev.ftb.mods.ftblibrary.ui.Panel;
import dev.ftb.mods.ftblibrary.ui.SimpleTextButton; import dev.ftb.mods.ftblibrary.ui.SimpleTextButton;
import dev.ftb.mods.ftblibrary.ui.Theme; import dev.ftb.mods.ftblibrary.ui.Theme;
import dev.ftb.mods.ftblibrary.ui.Widget; import dev.ftb.mods.ftblibrary.ui.Widget;
import dev.ftb.mods.ftblibrary.ui.input.MouseButton; import dev.ftb.mods.ftblibrary.ui.input.MouseButton;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@@ -17,26 +16,23 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import uk.sijbers.bnstoolkit.client.ClientBnsState; import uk.sijbers.bnstoolkit.client.ClientBnsState;
import uk.sijbers.bnstoolkit.data.SellCatalogue; import uk.sijbers.bnstoolkit.data.SellCatalogue;
import uk.sijbers.bnstoolkit.data.SpurBalance;
import uk.sijbers.bnstoolkit.network.NetSend; import uk.sijbers.bnstoolkit.network.NetSend;
import java.util.List;
/** /**
* The Counting House — wealth exchange screen. * The Counting House — wealth exchange screen.
* *
* Not a general shop. Players bring diamonds / netherite / ancient * Players bring diamonds / netherite / ancient debris and convert them
* debris and convert them to spurs, credited straight to their bank * to spurs credited directly to their bank. Reached via the Counting
* account. Iron / gold / coal / etc. are NOT here — they belong on * House Clerk NPC at spawn (not openable remotely by default).
* player-run Numismatics Vendor blocks at market prices.
* *
* Right pane shows current effective price for the selected wealth * <p>Left grid: 4 wealth-token icons. Click to select.
* token after the player's tier fee + DR. Quick-exchange buttons cover * <p>Right pane: selected item's name, list price, lifetime sales,
* 1 / 16 / 64. * effective fee%, payout per unit, AND how many of that
* * item the player has in their inventory right now.
* Reached via the Counting House Clerk NPC at spawn — not openable from * <p>Quick-exchange buttons: 1 / 16 / 64.
* anywhere via keybind. The sell action sends a {@code RunBnsCommand} * <p>Bottom-right: Back (returns to Hub if opened from there) /
* packet which calls Java-side {@code /bns sell}; that handler pulls * Close (NPC-opened path).
* items, computes payout, and deposits to the Numismatics bank.
*/ */
public final class CountingHouseScreen extends BaseBnsScreen { public final class CountingHouseScreen extends BaseBnsScreen {
public CountingHouseScreen() { public CountingHouseScreen() {
@@ -47,27 +43,27 @@ public final class CountingHouseScreen extends BaseBnsScreen {
@Override @Override
public void addWidgets() { public void addWidgets() {
// Pre-size sub-panels so refreshWidgets recursion sees correct dims.
// Panel height = screen height - 50 so there's room below for the
// Back/Close action row without overlapping internal buttons.
ItemGrid grid = new ItemGrid(this); ItemGrid grid = new ItemGrid(this);
grid.setPos(8, contentTop()); grid.setPos(8, contentTop());
grid.setSize(170, getHeight() - 30); grid.setSize(170, getHeight() - 50);
add(grid); add(grid);
SellDetail detail = new SellDetail(this); SellDetail detail = new SellDetail(this);
detail.setPos(186, contentTop()); detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30); detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail); add(detail);
add(SimpleTextButton.create(this, add(BaseBnsScreen.makeBackOrClose(this));
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("SELL")));
} }
@Override @Override
public void alignWidgets() { public void alignWidgets() {
Widget rf = getWidgets().get(2); Widget close = getWidgets().get(2);
rf.setSize(60, 16); close.setSize(72, 16);
rf.setPos(getWidth() - 68, getHeight() - 22); close.setPos(getWidth() - 80, getHeight() - 22);
} }
// ─── Item grid ─────────────────────────────────────────────────── // ─── Item grid ───────────────────────────────────────────────────
@@ -78,7 +74,7 @@ public final class CountingHouseScreen extends BaseBnsScreen {
for (ResourceLocation id : SellCatalogue.PRICES.keySet()) add(new ItemCell(this, id)); for (ResourceLocation id : SellCatalogue.PRICES.keySet()) add(new ItemCell(this, id));
} }
@Override public void alignWidgets() { @Override public void alignWidgets() {
int cols = 5, cell = 32, gap = 2; int cols = 4, cell = 36, gap = 4;
int i = 0; int i = 0;
for (Widget w : getWidgets()) { for (Widget w : getWidgets()) {
int col = i % cols; int col = i % cols;
@@ -104,10 +100,15 @@ public final class CountingHouseScreen extends BaseBnsScreen {
Color4I bg = isSelected ? Color4I.rgba(0x80FFD700) : Color4I bg = isSelected ? Color4I.rgba(0x80FFD700) :
isMouseOver ? Color4I.rgba(0x40FFFFFF) : Color4I.rgba(0x40000000); isMouseOver ? Color4I.rgba(0x40FFFFFF) : Color4I.rgba(0x40000000);
bg.draw(gfx, x, y, w, h); bg.draw(gfx, x, y, w, h);
theme.drawString(gfx, Component.empty(), x, y, Color4I.WHITE, 0);
// Render the item icon centered
if (!stack.isEmpty()) { if (!stack.isEmpty()) {
gfx.renderItem(stack, x + 8, y + 8); gfx.renderItem(stack, x + (w - 16) / 2, y + (h - 16) / 2);
}
// Inventory count badge bottom-right of the cell
int count = localInventoryCount(itemId);
if (count > 0) {
String label = String.valueOf(count);
theme.drawString(gfx, Component.literal("§f" + label), x + w - 4 - theme.getFont().width(label),
y + h - 10, Color4I.WHITE, Theme.SHADOW);
} }
} }
@Override public boolean mousePressed(MouseButton button) { @Override public boolean mousePressed(MouseButton button) {
@@ -130,21 +131,30 @@ public final class CountingHouseScreen extends BaseBnsScreen {
long sold = ClientBnsState.lifetimeSoldOf(selected); long sold = ClientBnsState.lifetimeSoldOf(selected);
int feePct = ClientBnsState.effectiveFeePct; int feePct = ClientBnsState.effectiveFeePct;
long perUnit = SellCatalogue.singleUnitPriceDisplay(selected, sold, feePct); long perUnit = SellCatalogue.singleUnitPriceDisplay(selected, sold, feePct);
int onHand = localInventoryCount(selected);
add(new LabelRow(this, "§e" + selected.toString() + "§r")); add(new LabelRow(this, "§e" + displayName(selected) + "§r"));
add(new LabelRow(this, "§7" + selected + "§r"));
add(new LabelRow(this, "")); add(new LabelRow(this, ""));
add(new LabelRow(this, "On hand: §f" + onHand + "§r"));
add(new LabelRow(this, "List price: §6" + base + "§r")); add(new LabelRow(this, "List price: §6" + base + "§r"));
add(new LabelRow(this, "Sold lifetime: §7" + sold + "§r")); add(new LabelRow(this, "Lifetime sold: §7" + sold + "§r"));
add(new LabelRow(this, "Effective fee: §c" + feePct + "%§r")); add(new LabelRow(this, "Effective fee: §c" + feePct + "%§r"));
add(new LabelRow(this, "You get: §a" + perUnit + " spurs / unit§r")); add(new LabelRow(this, "You get: §a" + perUnit + " sp/unit§r"));
add(new LabelRow(this, "")); add(new LabelRow(this, ""));
for (int n : new int[]{1, 16, 64}) { for (int n : new int[]{1, 16, 64}) {
final int count = n; final int count = n;
boolean haveEnough = onHand >= count;
Component label = Component.literal((haveEnough ? "§a" : "§8") + "Sell " + count);
add(SimpleTextButton.create(this, add(SimpleTextButton.create(this,
Component.literal("Sell " + count), label,
Icon.empty(), Icon.empty(),
btn -> { btn -> {
if (!haveEnough) {
TierUpgradeScreen.playClick();
return;
}
NetSend.runBns("sell " + selected + " " + count); NetSend.runBns("sell " + selected + " " + count);
TierUpgradeScreen.playClick(); TierUpgradeScreen.playClick();
})); }));
@@ -177,4 +187,24 @@ public final class CountingHouseScreen extends BaseBnsScreen {
theme.drawString(gfx, Component.literal(text), x, y + 2, Color4I.WHITE, Theme.SHADOW); theme.drawString(gfx, Component.literal(text), x, y + 2, Color4I.WHITE, Theme.SHADOW);
} }
} }
// ─── helpers ────────────────────────────────────────────────────
private static int localInventoryCount(ResourceLocation itemId) {
Minecraft mc = Minecraft.getInstance();
if (mc == null || mc.player == null) return 0;
return SpurBalance.countItem(mc.player, itemId);
}
private static String displayName(ResourceLocation id) {
String[] parts = id.getPath().split("_");
StringBuilder out = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (parts[i].isEmpty()) continue;
out.append(Character.toUpperCase(parts[i].charAt(0)));
if (parts[i].length() > 1) out.append(parts[i].substring(1));
if (i + 1 < parts.length) out.append(' ');
}
return out.toString();
}
} }
@@ -35,25 +35,22 @@ public final class PlotPurchaseScreen extends BaseBnsScreen {
public void addWidgets() { public void addWidgets() {
PlotList list = new PlotList(this); PlotList list = new PlotList(this);
list.setPos(8, contentTop()); list.setPos(8, contentTop());
list.setSize(170, getHeight() - 30); list.setSize(170, getHeight() - 50);
add(list); add(list);
PlotDetail detail = new PlotDetail(this); PlotDetail detail = new PlotDetail(this);
detail.setPos(186, contentTop()); detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30); detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail); add(detail);
add(SimpleTextButton.create(this, add(BaseBnsScreen.makeBackOrClose(this));
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("PLOT")));
} }
@Override @Override
public void alignWidgets() { public void alignWidgets() {
Widget rf = getWidgets().get(2); Widget close = getWidgets().get(2);
rf.setSize(60, 16); close.setSize(72, 16);
rf.setPos(getWidth() - 68, getHeight() - 22); close.setPos(getWidth() - 80, getHeight() - 22);
} }
private static final class PlotList extends Panel { private static final class PlotList extends Panel {
@@ -44,25 +44,19 @@ public final class QuestLogScreen extends BaseBnsScreen {
@Override @Override
public void addWidgets() { public void addWidgets() {
// Pre-size the content panel so its children get correct
// dimensions during the recursive refreshWidgets.
ContentPanel content = new ContentPanel(this); ContentPanel content = new ContentPanel(this);
content.setPos(8, contentTop()); content.setPos(8, contentTop());
content.setSize(getWidth() - 16, getHeight() - 32); content.setSize(getWidth() - 16, getHeight() - 50);
add(content); add(content);
add(SimpleTextButton.create(this, Component.literal("Refresh"), Icons.REFRESH,
btn -> NetSend.requestSnapshot("ALL")));
add(BaseBnsScreen.makeBackOrClose(this)); add(BaseBnsScreen.makeBackOrClose(this));
} }
@Override @Override
public void alignWidgets() { public void alignWidgets() {
int btnY = getHeight() - 22; Widget close = getWidgets().get(1);
Widget refresh = getWidgets().get(1); close.setSize(72, 16);
refresh.setSize(60, 16); refresh.setPos(8, btnY); close.setPos(getWidth() - 80, getHeight() - 22);
Widget close = getWidgets().get(2);
close.setSize(60, 16); close.setPos(getWidth() - 68, btnY);
} }
/** Three-section journal body. */ /** Three-section journal body. */
@@ -37,12 +37,9 @@ public final class TierUpgradeScreen extends BaseBnsScreen {
@Override @Override
public void addWidgets() { public void addWidgets() {
// Pre-size sub-panels so their addWidgets/alignWidgets (called
// via the recursive Panel.refreshWidgets BEFORE our own
// alignWidgets) see the correct dimensions.
LadderPanel ladder = new LadderPanel(this); LadderPanel ladder = new LadderPanel(this);
ladder.setPos(8, contentTop()); ladder.setPos(8, contentTop());
ladder.setSize(180, getHeight() - 28); ladder.setSize(180, getHeight() - 50);
add(ladder); add(ladder);
DetailPanel detail = new DetailPanel(this); DetailPanel detail = new DetailPanel(this);
@@ -50,18 +47,14 @@ public final class TierUpgradeScreen extends BaseBnsScreen {
detail.setSize(getWidth() - 204, getHeight() - 50); detail.setSize(getWidth() - 204, getHeight() - 50);
add(detail); add(detail);
add(SimpleTextButton.create(this, add(BaseBnsScreen.makeBackOrClose(this));
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("TIER")));
} }
@Override @Override
public void alignWidgets() { public void alignWidgets() {
// Sub-panels are sized in addWidgets; only position the refresh button here. Widget close = getWidgets().get(2);
Widget rf = getWidgets().get(2); close.setSize(72, 16);
rf.setSize(60, 16); close.setPos(getWidth() - 80, getHeight() - 22);
rf.setPos(getWidth() - 68, getHeight() - 22);
} }
// ─── Sub-panels ────────────────────────────────────────────────── // ─── Sub-panels ──────────────────────────────────────────────────
@@ -35,7 +35,7 @@ public final class BnsServerCommands {
private BnsServerCommands() {} private BnsServerCommands() {}
private static final String[] SCREEN_NAMES = { private static final String[] SCREEN_NAMES = {
"hub", "tier", "bounty", "plot", "counthouse", "shop", "questlog" "hub", "tier", "bounty", "plot", "counthouse", "journal"
}; };
@SubscribeEvent @SubscribeEvent
@@ -74,7 +74,7 @@ public final class BnsServerCommands {
for (String s : SCREEN_NAMES) if (s.equals(which)) { ok = true; break; } for (String s : SCREEN_NAMES) if (s.equals(which)) { ok = true; break; }
if (!ok) { if (!ok) {
src.sendFailure(Component.literal("Unknown screen: " + which src.sendFailure(Component.literal("Unknown screen: " + which
+ ". Options: hub, tier, bounty, plot, shop, questlog")); + ". Options: hub, tier, bounty, plot, counthouse, journal"));
return 0; return 0;
} }
ServerEconomyBridge.openScreen(sp, which); ServerEconomyBridge.openScreen(sp, which);
@@ -5,6 +5,7 @@
"key.bnstoolkit.bounties": "Open Bounty Board (direct)", "key.bnstoolkit.bounties": "Open Bounty Board (direct)",
"key.bnstoolkit.plots": "Open Plot Office (direct)", "key.bnstoolkit.plots": "Open Plot Office (direct)",
"key.bnstoolkit.shop": "Open Counting House (direct)", "key.bnstoolkit.shop": "Open Counting House (direct)",
"key.bnstoolkit.journal": "Open Adventurer's Journal (direct)",
"key.bnstoolkit.questlog": "Open Quest Log (direct)", "key.bnstoolkit.questlog": "Open Quest Log (direct)",
"screen.bnstoolkit.hub": "Brass and Sigil", "screen.bnstoolkit.hub": "Brass and Sigil",
@@ -12,6 +13,5 @@
"screen.bnstoolkit.bounty_board": "Bounty Board", "screen.bnstoolkit.bounty_board": "Bounty Board",
"screen.bnstoolkit.quest_log": "Adventurer's Journal", "screen.bnstoolkit.quest_log": "Adventurer's Journal",
"screen.bnstoolkit.plot_purchase": "Plot Office", "screen.bnstoolkit.plot_purchase": "Plot Office",
"screen.bnstoolkit.shop_browser": "Counting House",
"screen.bnstoolkit.counting_house": "Counting House" "screen.bnstoolkit.counting_house": "Counting House"
} }