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_name=Brass and Sigil Toolkit
mod_license=All Rights Reserved
mod_version=0.8.0
mod_version=0.8.1
mod_group_id=uk.sijbers.bnstoolkit
@@ -25,9 +25,8 @@ public final class OpenScreenDispatcher {
case "tier" -> { NetSend.requestSnapshot("TIER"); new TierUpgradeScreen().openGui(); }
case "bounty" -> { NetSend.requestSnapshot("BOUNTY"); new BountyBoardScreen().openGui(); }
case "plot" -> { NetSend.requestSnapshot("PLOT"); new PlotPurchaseScreen().openGui(); }
case "counthouse", "shop"
-> { NetSend.requestSnapshot("SELL"); new CountingHouseScreen().openGui(); }
case "questlog" -> { NetSend.requestSnapshot("ALL"); new QuestLogScreen().openGui(); }
case "counthouse" -> { NetSend.requestSnapshot("SELL"); new CountingHouseScreen().openGui(); }
case "journal" -> { NetSend.requestSnapshot("ALL"); new QuestLogScreen().openGui(); }
case "hub" -> { NetSend.requestSnapshot("ALL"); new HubScreen().openGui(); }
default -> BnsToolkit.LOG.warn("[bnstoolkit] unknown screen requested: {}", which);
}
@@ -36,25 +36,22 @@ public final class BountyBoardScreen extends BaseBnsScreen {
public void addWidgets() {
BountyList list = new BountyList(this);
list.setPos(8, contentTop());
list.setSize(170, getHeight() - 30);
list.setSize(170, getHeight() - 50);
add(list);
BountyDetail detail = new BountyDetail(this);
detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30);
detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail);
add(SimpleTextButton.create(this,
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("BOUNTY")));
add(BaseBnsScreen.makeBackOrClose(this));
}
@Override
public void alignWidgets() {
Widget rf = getWidgets().get(2);
rf.setSize(60, 16);
rf.setPos(getWidth() - 68, getHeight() - 22);
Widget close = getWidgets().get(2);
close.setSize(72, 16);
close.setPos(getWidth() - 80, getHeight() - 22);
}
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.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.SimpleTextButton;
import dev.ftb.mods.ftblibrary.ui.Theme;
import dev.ftb.mods.ftblibrary.ui.Widget;
import dev.ftb.mods.ftblibrary.ui.input.MouseButton;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
@@ -17,26 +16,23 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import uk.sijbers.bnstoolkit.client.ClientBnsState;
import uk.sijbers.bnstoolkit.data.SellCatalogue;
import uk.sijbers.bnstoolkit.data.SpurBalance;
import uk.sijbers.bnstoolkit.network.NetSend;
import java.util.List;
/**
* The Counting House — wealth exchange screen.
*
* Not a general shop. Players bring diamonds / netherite / ancient
* debris and convert them to spurs, credited straight to their bank
* account. Iron / gold / coal / etc. are NOT here — they belong on
* player-run Numismatics Vendor blocks at market prices.
* Players bring diamonds / netherite / ancient debris and convert them
* to spurs credited directly to their bank. Reached via the Counting
* House Clerk NPC at spawn (not openable remotely by default).
*
* Right pane shows current effective price for the selected wealth
* token after the player's tier fee + DR. Quick-exchange buttons cover
* 1 / 16 / 64.
*
* Reached via the Counting House Clerk NPC at spawn — not openable from
* anywhere via keybind. The sell action sends a {@code RunBnsCommand}
* packet which calls Java-side {@code /bns sell}; that handler pulls
* items, computes payout, and deposits to the Numismatics bank.
* <p>Left grid: 4 wealth-token icons. Click to select.
* <p>Right pane: selected item's name, list price, lifetime sales,
* effective fee%, payout per unit, AND how many of that
* item the player has in their inventory right now.
* <p>Quick-exchange buttons: 1 / 16 / 64.
* <p>Bottom-right: Back (returns to Hub if opened from there) /
* Close (NPC-opened path).
*/
public final class CountingHouseScreen extends BaseBnsScreen {
public CountingHouseScreen() {
@@ -47,27 +43,27 @@ public final class CountingHouseScreen extends BaseBnsScreen {
@Override
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);
grid.setPos(8, contentTop());
grid.setSize(170, getHeight() - 30);
grid.setSize(170, getHeight() - 50);
add(grid);
SellDetail detail = new SellDetail(this);
detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30);
detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail);
add(SimpleTextButton.create(this,
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("SELL")));
add(BaseBnsScreen.makeBackOrClose(this));
}
@Override
public void alignWidgets() {
Widget rf = getWidgets().get(2);
rf.setSize(60, 16);
rf.setPos(getWidth() - 68, getHeight() - 22);
Widget close = getWidgets().get(2);
close.setSize(72, 16);
close.setPos(getWidth() - 80, getHeight() - 22);
}
// ─── Item grid ───────────────────────────────────────────────────
@@ -78,7 +74,7 @@ public final class CountingHouseScreen extends BaseBnsScreen {
for (ResourceLocation id : SellCatalogue.PRICES.keySet()) add(new ItemCell(this, id));
}
@Override public void alignWidgets() {
int cols = 5, cell = 32, gap = 2;
int cols = 4, cell = 36, gap = 4;
int i = 0;
for (Widget w : getWidgets()) {
int col = i % cols;
@@ -104,10 +100,15 @@ public final class CountingHouseScreen extends BaseBnsScreen {
Color4I bg = isSelected ? Color4I.rgba(0x80FFD700) :
isMouseOver ? Color4I.rgba(0x40FFFFFF) : Color4I.rgba(0x40000000);
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()) {
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) {
@@ -130,21 +131,30 @@ public final class CountingHouseScreen extends BaseBnsScreen {
long sold = ClientBnsState.lifetimeSoldOf(selected);
int feePct = ClientBnsState.effectiveFeePct;
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, "On hand: §f" + onHand + "§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, "You get: §a" + perUnit + " spurs / unit§r"));
add(new LabelRow(this, "You get: §a" + perUnit + " sp/unit§r"));
add(new LabelRow(this, ""));
for (int n : new int[]{1, 16, 64}) {
final int count = n;
boolean haveEnough = onHand >= count;
Component label = Component.literal((haveEnough ? "§a" : "§8") + "Sell " + count);
add(SimpleTextButton.create(this,
Component.literal("Sell " + count),
label,
Icon.empty(),
btn -> {
if (!haveEnough) {
TierUpgradeScreen.playClick();
return;
}
NetSend.runBns("sell " + selected + " " + count);
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);
}
}
// ─── 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() {
PlotList list = new PlotList(this);
list.setPos(8, contentTop());
list.setSize(170, getHeight() - 30);
list.setSize(170, getHeight() - 50);
add(list);
PlotDetail detail = new PlotDetail(this);
detail.setPos(186, contentTop());
detail.setSize(getWidth() - 194, getHeight() - 30);
detail.setSize(getWidth() - 194, getHeight() - 50);
add(detail);
add(SimpleTextButton.create(this,
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("PLOT")));
add(BaseBnsScreen.makeBackOrClose(this));
}
@Override
public void alignWidgets() {
Widget rf = getWidgets().get(2);
rf.setSize(60, 16);
rf.setPos(getWidth() - 68, getHeight() - 22);
Widget close = getWidgets().get(2);
close.setSize(72, 16);
close.setPos(getWidth() - 80, getHeight() - 22);
}
private static final class PlotList extends Panel {
@@ -44,25 +44,19 @@ public final class QuestLogScreen extends BaseBnsScreen {
@Override
public void addWidgets() {
// Pre-size the content panel so its children get correct
// dimensions during the recursive refreshWidgets.
ContentPanel content = new ContentPanel(this);
content.setPos(8, contentTop());
content.setSize(getWidth() - 16, getHeight() - 32);
content.setSize(getWidth() - 16, getHeight() - 50);
add(content);
add(SimpleTextButton.create(this, Component.literal("Refresh"), Icons.REFRESH,
btn -> NetSend.requestSnapshot("ALL")));
add(BaseBnsScreen.makeBackOrClose(this));
}
@Override
public void alignWidgets() {
int btnY = getHeight() - 22;
Widget refresh = getWidgets().get(1);
refresh.setSize(60, 16); refresh.setPos(8, btnY);
Widget close = getWidgets().get(2);
close.setSize(60, 16); close.setPos(getWidth() - 68, btnY);
Widget close = getWidgets().get(1);
close.setSize(72, 16);
close.setPos(getWidth() - 80, getHeight() - 22);
}
/** Three-section journal body. */
@@ -37,12 +37,9 @@ public final class TierUpgradeScreen extends BaseBnsScreen {
@Override
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);
ladder.setPos(8, contentTop());
ladder.setSize(180, getHeight() - 28);
ladder.setSize(180, getHeight() - 50);
add(ladder);
DetailPanel detail = new DetailPanel(this);
@@ -50,18 +47,14 @@ public final class TierUpgradeScreen extends BaseBnsScreen {
detail.setSize(getWidth() - 204, getHeight() - 50);
add(detail);
add(SimpleTextButton.create(this,
Component.literal("Refresh"),
Icons.REFRESH,
btn -> NetSend.requestSnapshot("TIER")));
add(BaseBnsScreen.makeBackOrClose(this));
}
@Override
public void alignWidgets() {
// Sub-panels are sized in addWidgets; only position the refresh button here.
Widget rf = getWidgets().get(2);
rf.setSize(60, 16);
rf.setPos(getWidth() - 68, getHeight() - 22);
Widget close = getWidgets().get(2);
close.setSize(72, 16);
close.setPos(getWidth() - 80, getHeight() - 22);
}
// ─── Sub-panels ──────────────────────────────────────────────────
@@ -35,7 +35,7 @@ public final class BnsServerCommands {
private BnsServerCommands() {}
private static final String[] SCREEN_NAMES = {
"hub", "tier", "bounty", "plot", "counthouse", "shop", "questlog"
"hub", "tier", "bounty", "plot", "counthouse", "journal"
};
@SubscribeEvent
@@ -74,7 +74,7 @@ public final class BnsServerCommands {
for (String s : SCREEN_NAMES) if (s.equals(which)) { ok = true; break; }
if (!ok) {
src.sendFailure(Component.literal("Unknown screen: " + which
+ ". Options: hub, tier, bounty, plot, shop, questlog"));
+ ". Options: hub, tier, bounty, plot, counthouse, journal"));
return 0;
}
ServerEconomyBridge.openScreen(sp, which);
@@ -5,6 +5,7 @@
"key.bnstoolkit.bounties": "Open Bounty Board (direct)",
"key.bnstoolkit.plots": "Open Plot Office (direct)",
"key.bnstoolkit.shop": "Open Counting House (direct)",
"key.bnstoolkit.journal": "Open Adventurer's Journal (direct)",
"key.bnstoolkit.questlog": "Open Quest Log (direct)",
"screen.bnstoolkit.hub": "Brass and Sigil",
@@ -12,6 +13,5 @@
"screen.bnstoolkit.bounty_board": "Bounty Board",
"screen.bnstoolkit.quest_log": "Adventurer's Journal",
"screen.bnstoolkit.plot_purchase": "Plot Office",
"screen.bnstoolkit.shop_browser": "Counting House",
"screen.bnstoolkit.counting_house": "Counting House"
}