0.8.2: Counting House → Exchange + fix click-doesn't-refresh-detail bug
Build / build (push) Has been cancelled

Rename
------
Counting House → Exchange everywhere. "Exchange" reads as intuitive
("an exchange where I trade items for spurs") where Counting House
was historically accurate but obscure. /bns open counthouse →
/bns open exchange. Class CountingHouseScreen → ExchangeScreen.
Lang key counting_house → exchange. NPC name suggestion in docs:
"Exchange Clerk".

Click-doesn't-refresh bug
-------------------------
ItemCell/BountyRow/PlotRow's mousePressed updated the static
`selected` field, but the detail sub-panel was already built with
the old value at refreshWidgets time. Result: clicking a different
item updated the highlight but the right-side detail pane never
changed.

Fix: call getGui().refreshWidgets() after updating selection. FTB
Library's recursive refresh re-runs SellDetail.addWidgets/
BountyDetail.addWidgets/PlotDetail.addWidgets with the new selected
value. Standard FTB Library pattern for "selection changed → redraw".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-08 18:22:27 +00:00
parent 3ecb23ecdd
commit 14b845e9a8
13 changed files with 36 additions and 29 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.1 mod_version=0.8.2
mod_group_id=uk.sijbers.bnstoolkit mod_group_id=uk.sijbers.bnstoolkit
@@ -10,7 +10,7 @@ import net.neoforged.neoforge.client.settings.KeyConflictContext;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import uk.sijbers.bnstoolkit.BnsToolkit; import uk.sijbers.bnstoolkit.BnsToolkit;
import uk.sijbers.bnstoolkit.client.screens.BountyBoardScreen; import uk.sijbers.bnstoolkit.client.screens.BountyBoardScreen;
import uk.sijbers.bnstoolkit.client.screens.CountingHouseScreen; import uk.sijbers.bnstoolkit.client.screens.ExchangeScreen;
import uk.sijbers.bnstoolkit.client.screens.HubScreen; import uk.sijbers.bnstoolkit.client.screens.HubScreen;
import uk.sijbers.bnstoolkit.client.screens.PlotPurchaseScreen; import uk.sijbers.bnstoolkit.client.screens.PlotPurchaseScreen;
import uk.sijbers.bnstoolkit.client.screens.QuestLogScreen; import uk.sijbers.bnstoolkit.client.screens.QuestLogScreen;
@@ -114,7 +114,7 @@ public final class BnsKeyBindings {
} }
if (OPEN_SHOP.consumeClick()) { if (OPEN_SHOP.consumeClick()) {
NetSend.requestSnapshot("SELL"); NetSend.requestSnapshot("SELL");
new CountingHouseScreen().openGui(); new ExchangeScreen().openGui();
} }
if (OPEN_QUESTLOG.consumeClick()) { if (OPEN_QUESTLOG.consumeClick()) {
NetSend.requestSnapshot("ALL"); NetSend.requestSnapshot("ALL");
@@ -2,7 +2,7 @@ package uk.sijbers.bnstoolkit.client;
import uk.sijbers.bnstoolkit.BnsToolkit; import uk.sijbers.bnstoolkit.BnsToolkit;
import uk.sijbers.bnstoolkit.client.screens.BountyBoardScreen; import uk.sijbers.bnstoolkit.client.screens.BountyBoardScreen;
import uk.sijbers.bnstoolkit.client.screens.CountingHouseScreen; import uk.sijbers.bnstoolkit.client.screens.ExchangeScreen;
import uk.sijbers.bnstoolkit.client.screens.HubScreen; import uk.sijbers.bnstoolkit.client.screens.HubScreen;
import uk.sijbers.bnstoolkit.client.screens.PlotPurchaseScreen; import uk.sijbers.bnstoolkit.client.screens.PlotPurchaseScreen;
import uk.sijbers.bnstoolkit.client.screens.QuestLogScreen; import uk.sijbers.bnstoolkit.client.screens.QuestLogScreen;
@@ -25,7 +25,7 @@ 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" -> { NetSend.requestSnapshot("SELL"); new CountingHouseScreen().openGui(); } case "exchange" -> { NetSend.requestSnapshot("SELL"); new ExchangeScreen().openGui(); }
case "journal" -> { NetSend.requestSnapshot("ALL"); new QuestLogScreen().openGui(); } case "journal" -> { 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);
@@ -120,6 +120,7 @@ public final class BountyBoardScreen extends BaseBnsScreen {
if (isMouseOver) { if (isMouseOver) {
selected = b.id(); selected = b.id();
TierUpgradeScreen.playClick(); TierUpgradeScreen.playClick();
getGui().refreshWidgets(); // re-init so detail pane reads new selection
return true; return true;
} }
return false; return false;
@@ -20,7 +20,7 @@ import uk.sijbers.bnstoolkit.data.SpurBalance;
import uk.sijbers.bnstoolkit.network.NetSend; import uk.sijbers.bnstoolkit.network.NetSend;
/** /**
* The Counting House wealth exchange screen. * The Exchange wealth exchange screen.
* *
* Players bring diamonds / netherite / ancient debris and convert them * Players bring diamonds / netherite / ancient debris and convert them
* to spurs credited directly to their bank. Reached via the Counting * to spurs credited directly to their bank. Reached via the Counting
@@ -34,9 +34,9 @@ import uk.sijbers.bnstoolkit.network.NetSend;
* <p>Bottom-right: Back (returns to Hub if opened from there) / * <p>Bottom-right: Back (returns to Hub if opened from there) /
* Close (NPC-opened path). * Close (NPC-opened path).
*/ */
public final class CountingHouseScreen extends BaseBnsScreen { public final class ExchangeScreen extends BaseBnsScreen {
public CountingHouseScreen() { public ExchangeScreen() {
super(Component.translatable("screen.bnstoolkit.counting_house")); super(Component.translatable("screen.bnstoolkit.exchange"));
} }
private static ResourceLocation selected = ResourceLocation.parse("minecraft:diamond"); private static ResourceLocation selected = ResourceLocation.parse("minecraft:diamond");
@@ -115,6 +115,11 @@ public final class CountingHouseScreen extends BaseBnsScreen {
if (isMouseOver) { if (isMouseOver) {
selected = itemId; selected = itemId;
TierUpgradeScreen.playClick(); TierUpgradeScreen.playClick();
// Detail pane was built with the old selected value re-init
// the whole screen so SellDetail.addWidgets reads the new
// selection. FTB Library's refreshWidgets clears + re-adds
// + recurses into sub-panels, which is what we need.
getGui().refreshWidgets();
return true; return true;
} }
return false; return false;
@@ -21,7 +21,7 @@ import uk.sijbers.bnstoolkit.network.NetSend;
* wealth, current shop fee, slot caps, active bounties count, owned * wealth, current shop fee, slot caps, active bounties count, owned
* plots count. The Quest Log button opens the read-only quest overview. * plots count. The Quest Log button opens the read-only quest overview.
* *
* <p>Transactional features (Counting House exchange, Bounty Board, * <p>Transactional features (Exchange exchange, Bounty Board,
* Tier Upgrade, Plot Office) are not reachable from the hub by design. * Tier Upgrade, Plot Office) are not reachable from the hub by design.
* Players visit the corresponding NPC at spawn — that's what makes spawn * Players visit the corresponding NPC at spawn — that's what makes spawn
* worth caring about. NPCs invoke {@code /bns open <screen>} on the * worth caring about. NPCs invoke {@code /bns open <screen>} on the
@@ -92,6 +92,7 @@ public final class PlotPurchaseScreen extends BaseBnsScreen {
if (isMouseOver) { if (isMouseOver) {
selected = p.id(); selected = p.id();
TierUpgradeScreen.playClick(); TierUpgradeScreen.playClick();
getGui().refreshWidgets();
return true; return true;
} }
return false; return false;
@@ -26,10 +26,10 @@ import java.util.Map;
* <ul> * <ul>
* <li><b>Active Bounties</b> — kills in progress, with progress bars * <li><b>Active Bounties</b> — kills in progress, with progress bars
* and turn-in rewards. Primary use case.</li> * and turn-in rewards. Primary use case.</li>
* <li><b>Counting House Rates</b> — current per-unit spur payout for * <li><b>Exchange Rates</b> — current per-unit spur payout for
* each wealth-token after the player's tier fee + lifetime DR. * each wealth-token after the player's tier fee + lifetime DR.
* Lets players check rates from the field without travelling to * Lets players check rates from the field without travelling to
* the Counting House.</li> * the Exchange.</li>
* <li><b>Owned Plots</b> — what you own + your slot cap.</li> * <li><b>Owned Plots</b> — what you own + your slot cap.</li>
* </ul> * </ul>
* *
@@ -85,8 +85,8 @@ public final class QuestLogScreen extends BaseBnsScreen {
add(new Header(this,"")); add(new Header(this,""));
// Section 2: Counting House rates // Section 2: Exchange rates
add(new Header(this,"§e=== Counting House Rates ===")); add(new Header(this,"§e=== Exchange Rates ==="));
int fee = ClientBnsState.effectiveFeePct; int fee = ClientBnsState.effectiveFeePct;
add(new Header(this,"§7 Your tier fee: §c" + fee + "%§r")); add(new Header(this,"§7 Your tier fee: §c" + fee + "%§r"));
for (Map.Entry<ResourceLocation, Long> entry : SellCatalogue.PRICES.entrySet()) { for (Map.Entry<ResourceLocation, Long> entry : SellCatalogue.PRICES.entrySet()) {
@@ -6,11 +6,11 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
* Counting House exchange rates. * Exchange rates — the four wealth-tokens the Exchange will buy.
* *
* <p>The Counting House is the realm's wealth exchange — not a general * <p>The Exchange is the realm's wealth exchange — not a general
* shop. It accepts the universal wealth-store items (diamond, netherite, * shop. It accepts diamond, diamond_block, netherite_ingot, and
* ancient debris) and credits the player's bank account in spurs. * ancient_debris, crediting the player's bank account in spurs.
* *
* <p>Iron, gold, coal, redstone, mob drops etc. are <em>not</em> here. * <p>Iron, gold, coal, redstone, mob drops etc. are <em>not</em> here.
* Players who want to sell those operate their own shops with * Players who want to sell those operate their own shops with
@@ -11,7 +11,7 @@ import uk.sijbers.bnstoolkit.client.ClientBnsState;
/** /**
* S2C: civic tier (1..13), cash on hand (inventory coins), bank balance * S2C: civic tier (1..13), cash on hand (inventory coins), bank balance
* (Numismatics personal account), and the effective Counting House fee%. * (Numismatics personal account), and the effective Exchange fee%.
* *
* Cash + bank are sent separately so the hub UI can break them out * Cash + bank are sent separately so the hub UI can break them out
* ("on hand" vs "in bank") while the HUD overlay sums them for the * ("on hand" vs "in bank") while the HUD overlay sums them for the
@@ -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", "journal" "hub", "tier", "bounty", "plot", "exchange", "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, counthouse, journal")); + ". Options: hub, tier, bounty, plot, exchange, journal"));
return 0; return 0;
} }
ServerEconomyBridge.openScreen(sp, which); ServerEconomyBridge.openScreen(sp, which);
@@ -22,13 +22,13 @@ import uk.sijbers.bnstoolkit.economy.NumismaticsBridge;
import uk.sijbers.bnstoolkit.network.ServerEconomyBridge; import uk.sijbers.bnstoolkit.network.ServerEconomyBridge;
/** /**
* Counting House commands — the wealth-exchange + wallet inspection * Exchange commands — the wealth-exchange + wallet inspection
* verbs that used to live in KubeJS. * verbs that used to live in KubeJS.
* *
* <ul> * <ul>
* <li>{@code /bns sell <item> <count>} — exchange wealth-token items * <li>{@code /bns sell <item> <count>} — exchange wealth-token items
* for spurs, credited straight to the player's Numismatics bank * for spurs, credited straight to the player's Numismatics bank
* account. NPC dialogue at the Counting House Clerk fires this on * account. NPC dialogue at the Exchange Clerk fires this on
* the clicking player.</li> * the clicking player.</li>
* <li>{@code /bns wallet} — show inventory cash, bank balance, total.</li> * <li>{@code /bns wallet} — show inventory cash, bank balance, total.</li>
* <li>{@code /bns wallet deposit <amount>} — move coins from inventory * <li>{@code /bns wallet deposit <amount>} — move coins from inventory
@@ -67,7 +67,7 @@ public final class CountingHouseCommands {
ctx.getSource(), ctx.getSource(),
IntegerArgumentType.getInteger(ctx, "amount")))))); IntegerArgumentType.getInteger(ctx, "amount"))))));
event.getDispatcher().register(bns); event.getDispatcher().register(bns);
BnsToolkit.LOG.info("[bnstoolkit] registered Counting House + wallet commands"); BnsToolkit.LOG.info("[bnstoolkit] registered Exchange + wallet commands");
} }
// ─── /bns sell ─────────────────────────────────────────────────── // ─── /bns sell ───────────────────────────────────────────────────
@@ -86,8 +86,8 @@ public final class CountingHouseCommands {
return 0; return 0;
} }
if (!SellCatalogue.PRICES.containsKey(item)) { if (!SellCatalogue.PRICES.containsKey(item)) {
sp.sendSystemMessage(Component.literal("§cThe Counting House doesn't exchange '" + itemId sp.sendSystemMessage(Component.literal("§cThe Exchange doesn't accept '" + itemId
+ "'. Try diamonds, netherite, or ancient debris.")); + "'. Bring diamonds, netherite, or ancient debris."));
return 0; return 0;
} }
@@ -130,7 +130,7 @@ public final class CountingHouseCommands {
sp.sendSystemMessage(Component.literal("§6Sold " + count + "x " + itemId sp.sendSystemMessage(Component.literal("§6Sold " + count + "x " + itemId
+ " for §a" + payout + " spurs§r§6 → bank.")); + " for §a" + payout + " spurs§r§6 → bank."));
BnsToolkit.LOG.info("[bns/counthouse] {} sold {}x {} for {} spurs (lifetime now {})", BnsToolkit.LOG.info("[bns/exchange] {} sold {}x {} for {} spurs (lifetime now {})",
sp.getName().getString(), count, itemId, payout, lifetimeSold + count); sp.getName().getString(), count, itemId, payout, lifetimeSold + count);
// Push fresh snapshots so the HUD updates instantly // Push fresh snapshots so the HUD updates instantly
@@ -4,7 +4,7 @@
"key.bnstoolkit.tier": "Open Tier Ladder (direct)", "key.bnstoolkit.tier": "Open Tier Ladder (direct)",
"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 Exchange (direct)",
"key.bnstoolkit.journal": "Open Adventurer's Journal (direct)", "key.bnstoolkit.journal": "Open Adventurer's Journal (direct)",
"key.bnstoolkit.questlog": "Open Quest Log (direct)", "key.bnstoolkit.questlog": "Open Quest Log (direct)",
@@ -13,5 +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.counting_house": "Counting House" "screen.bnstoolkit.exchange": "Exchange"
} }