Compare commits

..

11 Commits

Author SHA1 Message Date
Matt 25107efcf1 docs: bnstoolkit architecture + detailed perk specifications
New companion-mod design doc covering:

Architecture
- Purpose & scope (what's in the mod vs what stays in KubeJS)
- High-level diagram showing client/server/KubeJS/FTB-stack roles
- Full Java package layout under uk.sijbers.bnstoolkit
- Six GUI screens specified individually with layouts + network calls
  (Quest Log, Bounty Board, Tier Upgrade, Plot Purchase, Shop Browser,
   Quest Progress HUD)
- Three mixin targets into FTB Chunks' ChunkScreenPanel (drawBackground,
  ChunkButton.addMouseOverText, ChunkButton.onClicked) -- no FTB fork
- Network packet contract (7 S2C + 5 C2S packets, versioned)
- Data persistence model (SavedData for plots + sethomes, in-memory
  for death locations, KubeJS for quest state)
- Two config files documented (tiers.json + plots.json) with example
  JSON5 schemas
- KubeJS integration surface (8 /bnstoolkit_internal commands)
- Build & release setup (separate Gitea repo, standard NeoForge
  ModDevGradle, semver)
- 6 mod implementation phases (M0-M6), each independently shippable

Detailed perk specifications
- Wilderness chunks: FTB Ranks permission node, native FTB Chunks
  enforcement, no mod-side work
- Plot slots: SavedData-backed ownership tracking, 50% refund on
  release
- Sethomes: full sethome system built in bnstoolkit (no other sethome
  mod exists in pack). Per-tier cap, 30s cooldown, /sethome /home
  /delhome /listhomes commands
- Daily bounty slots: enforced in KubeJS engine, UI greys Accept when
  at cap
- Shop fee discount: base 25% Bazaar fee reduced by tier discount.
  Only applies to NPC Bazaar shop, not player-to-player Numismatics.
  Worked example showing -25% Sovereign = 0% fee = full 10 spurs/diamond
- Chat colour: full colour ladder per tier (grey to rainbow at
  Sovereign with character-by-character cycling)
- Join broadcast: 6 levels documented (none, simple, fancy, epic,
  epic+particles, epic+spectacular) with example ASCII
- /back after death: in-memory storage, 60s cooldown, dimension-aware
- Future perk dimensions: 7 candidates the schema supports without
  code changes (particle trails, custom join sounds, custom title
  prefix, priority bounty access, tier-gated vendors, daily login
  bonus, keep-inventory chance)

Six open implementation questions flagged for resolution during
build, none blocking.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 23:09:34 +00:00
Matt 56e6006424 Merge pull request 'docs: economy v0.4 — 13-tier ladder + Sovereign 10M + Founder flag' (#64) from docs/economy-v0.4-13-tier-ladder into main 2026-06-06 23:00:45 +00:00
Matt a0aee440c3 docs: economy v0.4 -- 13-tier ladder + Sovereign 10M + Founder flag
Tier ladder extended from 8 -> 13 tiers, with cleaner historical
progression. Replaces the previous Peasant/Citizen/Patron/Notable/
Baron/Noble/Lord/Sovereign ladder which mixed commoner-economic terms
with noble terms.

New 13-tier ladder (Civic + British peerage):
  1  Peasant       free
  2  Farmer        200
  3  Citizen       750
  4  Merchant      2,500
  5  Knight        6,500       (gentry threshold)
  6  Baron         15,000      (lowest noble)
  7  Viscount      35,000
  8  Earl          75,000
  9  Marquess      150,000
  10 Duke          300,000
  11 Archduke      700,000
  12 Grand Duke    1,500,000
  13 Sovereign     10,000,000  (apex, "finished the game")

Rationale:
- Yeoman -> Farmer, Burgher -> Merchant: both replaced with
  recognizable terms while keeping the period feel.
- Earl kept as the British equivalent of Count (Viscount sits below
  Earl correctly because Viscount = vice-count).
- Top tier extended above Duke with Archduke -> Grand Duke ->
  Sovereign. Avoids singular-feeling Emperor/King/Prince titles.
- Sovereign at 10M is intentionally extreme -- months of dedicated
  play to reach, properly mythical.

Full perk table updated to spread cleanly across 13 tiers:
- Plot slots: 0/0/1/1/2/2/3/3/4/5/6/7/10
- Sethomes: 1/1/2/3/4/5/6/8/10/12/16/20/unlimited
- Daily bounties: 3/3/3/4/4/5/5/6/6/7/7/8/10
- Shop fee: 0% down to -25% at Sovereign
- Chat colours: grey -> white -> yellow -> green -> cyan -> blue
  -> purple -> gold -> orange -> red -> bright red -> rainbow

Knight (tier 5) is the gentry threshold -- the first meaningful
"I'm somebody" upgrade with colored chat, join broadcast, and /back.

Founder cosmetic flag added for matt (server owner). Permanent chat
prefix [Founder] alongside the normal rank. No gameplay perks; just
identification. Founder still grinds the regular ladder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 23:00:45 +00:00
Matt 4ca2f17d9d Merge pull request 'docs: economy v0.3 lockdown (custom mod + UI + tiers + plots)' (#63) from docs/economy-v0.3-lockdown into main 2026-06-06 22:27:07 +00:00
Matt 121ae4c603 docs: economy v0.3 — custom mod + UI approach + tier ladder + spawn plots locked
Architectural revisions:

- UI approach: custom companion mod (working name 'bnstoolkit') built
  on FTB Library's UI framework. Polished GUI screens for Quest Log,
  Bounty Board, Tier upgrade, Shop browser, Plot purchase. Quest
  progress as a custom HUD render layer (no stacked vanilla bossbars).
  All player interaction through GUI + NPC dialogue, no chat commands
  to memorise. FTB Library UI chosen for visual consistency with the
  rest of the FTB stack already in the pack.

- Tier ladder locked: 8 tiers, Civic theme, starting at Peasant.
  Peasant -> Citizen -> Patron -> Notable -> Baron -> Noble -> Lord
  -> Sovereign. Replaced Burgess with Baron; reordered ascending.

- Tier perks: full table with chunks claimable, spawn plot slots,
  sethome count, daily bounty slots, shop fee, chat colour, join
  broadcast tier, /back access. JSON-configured at
  config/bnstoolkit/tiers.json, hot-reloadable. Easy to tune any
  column without code changes.

- Spawn plots clarified as distinct from wilderness chunk claims:
  predefined regions at spawn, purchased individually for spurs,
  primary use is shops, slot-limited by tier. Purchase happens
  inside the FTB Chunks map UI via custom mod mixin. Plot data
  lives in the custom mod, not in FTB Chunks.

- Wilderness chunk claims: tier-allocated, free within allowance,
  refused above. NO per-chunk pricing (dropped from v0.2). Powered
  by FTB Ranks -> FTB Chunks permission nodes.

- Phasing updated to add Phase 0c (mod scaffolding) and reorder
  Phases 1-7 to fit the mod-supplied UI work.

- Implementation notes: KubeJS script layout, custom mod package
  layout, state data locations, config file locations all
  documented.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 22:27:06 +00:00
Matt 107622fc6d Merge pull request 'docs: economy architecture lockdown (v0.2)' (#62) from docs/economy-architecture-lockdown into main 2026-06-06 21:09:45 +00:00
Matt 95b0d1564d docs: economy architecture lockdown (v0.2)
Final architecture for the economy + quest system. No more V1/V2/V3
iteration cycles. Resolved against actual mod source code and verified
APIs via research spike.

Key decisions locked:

- Bounty UI: custom (bossbar + /quests commands + Easy NPC dialog).
  NOT FTB Quests -- it lacks per-player visibility, has no abandon
  event, and cooldown is per-team. Confirmed via FTBQ source review.

- Progression UI: FTB Quests (right tool for long-form content where
  per-team cooldowns and one-shot rewards are fine).

- Numismatics: direct Java.loadClass access (no events shipped, no
  KubeJS bindings). Documented exact API surface and call patterns.

- FTB Chunks claim cost: FTBChunksEvents.before('claim') with
  event.setResult() to refuse. Requires adding FTB XMod Compat.

- Tier system: FTB Ranks (CurseForge only) + permission-driven chunk
  limits + KubeJS-mediated upgrades via runCommandSilent('ftbranks add').

- Per-player HUD: bossbars, not scoreboards. Vanilla scoreboards are
  global; bossbars natively per-player.

- Custom mod for V1: NOT NEEDED. Re-evaluate if blocked.

Mods to add in Phase 0a (one PR before any implementation):
  - FTB Quests 2101.1.25
  - FTB XMod Compat (latest)
  - FTB Ranks 2101.1.3

Phasing updated: Phase 0a (mod additions) blocks the rest. Phase 0b
(spawn build) is non-blocking and user-paced; all systems can be
built and verified before spawn exists.

KubeJS file layout documented for clean implementation:
  pack/overrides/kubejs/server_scripts/economy/{01..99}_*.js

11 open decisions resolved. 3 remain non-blocking.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:09:45 +00:00
Matt 0c591faf14 Merge pull request 'docs: quest tracking + cancel UX' (#61) from docs/economy-quest-tracking into main 2026-06-06 20:51:32 +00:00
Matt 63e9185bf8 docs: add quest tracking + cancel UX to economy design
Three-layer visibility plan:
  V1 (Phase 3): /quests, /quests info, /quests cancel commands.
                Mobile-friendly via the bridge, no extra mods.
  V2 (Phase 3.5): Quest Log book item with dynamic pages.
  V3 (later): optional sidebar HUD via vanilla scoreboards.

3-slot active-quest cap so cancellation is a real choice:
  - Prevents accept-all-then-cancel-easy-ones RNG shopping
  - Keeps state files small
  - READY frees the slot so turn-in backlogs are workable

State storage schema documented (active + completed/cooldown).

Phase 3.5 added to the roadmap for the book item.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 20:51:14 +00:00
Matt 5bd1ace6aa Merge pull request 'docs: economy & quest system design (v0.1)' (#60) from docs/economy-system-design into main 2026-06-06 20:48:13 +00:00
Matt 1af2b9a86f docs: economy & quest system design (v0.1 draft)
Full design for the unified spawn economy:

- Seven themed guild buildings (Adventurer's, Merchant's, Civic,
  Library, Tavern, Foundry, Mint)
- Universal accept → complete → return quest pattern
- Villagers moved fully to spurs with rebalance principles +
  initial price guidance
- Money flow architecture (bounded inflows, scaling sinks)
- Phasing plan (8 phases, each independently shippable)
- Mod stack confirmed (we have everything we need for V1)
- Risk register + open decisions to make before implementation

Status: design, not yet implemented. Phase 0 (spawn skeleton)
is the next concrete deliverable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 20:48:12 +00:00
2 changed files with 1173 additions and 0 deletions
+664
View File
@@ -0,0 +1,664 @@
# bnstoolkit — Architecture & Perk Specification
> Status: design draft (v0.1). Not yet implemented.
> Owner: matt
> Last updated: 2026-06-06
`bnstoolkit` is the custom companion mod for the Brass and Sigil economy
system. It provides the UI layer (GUI screens, HUD), the FTB Chunks
map mixins for spawn plot integration, and the data models the economy
needs. All gameplay logic (bounty rotation, state machine, villager
trades, Numismatics integration, etc.) lives in KubeJS and is called
into by the mod via network packets.
This doc is the source of truth for the mod design. It must be
reviewed before any Java is written.
See also: [`economy-system.md`](./economy-system.md) for the gameplay
side of the design.
---
## 1. Purpose & scope
### In scope
- GUI screens built on FTB Library's UI framework (matches FTB Quests / FTB Chunks visual language)
- Mixins into FTB Chunks' map UI for spawn-plot cost overlay, hover tooltips, and click-to-purchase popups
- Custom HUD render layer for quest progress (replaces stacked vanilla bossbars)
- Data persistence for plot ownership (server-side, saved with world)
- Sethome system (`/sethome`, `/home`, `/delhome`, `/listhomes`) since the pack has no other sethome mod
- Network packets for client↔server state sync
- Admin commands: `/bnstoolkit reload`, debugging commands
- Config-driven tier perks (`tiers.json`) and plot definitions (`plots.json`)
### Out of scope
- Bounty quest engine (lives in KubeJS)
- Villager trade rebalance (lives in KubeJS)
- Numismatics direct integration (lives in KubeJS — mod calls KubeJS commands to debit/credit)
- FTB Quests integration (FTB Quests handles its own UI for the progression campaign)
- FTB Ranks admin (FTB Ranks handles permissions directly; mod just reads them)
---
## 2. High-level architecture
```
┌──────────────────────┐
│ Player (client) │
└──────────┬───────────┘
│ keybind / NPC click / chunk click
┌───────────────────────────────────┐
│ bnstoolkit (client) │
│ • FTB Library UI screens │
│ • FTB Chunks map mixin │
│ • Quest progress HUD │
└───────────────┬───────────────────┘
│ network packets
┌───────────────────────────────────┐
│ bnstoolkit (server) │
│ • Plot ownership data │
│ • Sethome data │
│ • Network packet handlers │
│ • /bnstoolkit reload command │
└─────┬───────────────────────┬─────┘
│ │
reads │ │ calls KubeJS commands /
FTB Ranks / │ │ fires events for KubeJS
FTB Chunks / │ │ to react to
Numismatics ▼ ▼
┌───────────────┐ ┌──────────────────────┐
│ FTB / NeoForge │ │ KubeJS scripts │
│ stack │ │ • Bounty engine │
└───────────────┘ │ • Quest state │
│ • Numismatics calls │
│ • Villager trades │
│ • Tier promotion │
└──────────────────────┘
```
Key invariant: **the mod owns UI + plot data + sethomes. KubeJS owns
gameplay logic.** They communicate via network packets and KubeJS
commands. The mod never directly mutates Numismatics balances or quest
state — it asks KubeJS to do that via commands.
---
## 3. Package layout
```
bnstoolkit/
├── build.gradle NeoForge 1.21.1 + FTB Library dep
├── src/main/java/uk/sijbers/bnstoolkit/
│ ├── BnsToolkit.java Mod entry, lifecycle
│ ├── client/
│ │ ├── ClientEvents.java Keybinds, client setup
│ │ ├── screen/
│ │ │ ├── QuestLogScreen.java
│ │ │ ├── BountyBoardScreen.java
│ │ │ ├── TierUpgradeScreen.java
│ │ │ ├── PlotPurchaseScreen.java
│ │ │ ├── ShopBrowserScreen.java
│ │ │ └── widgets/ (subclass FTB Library widgets)
│ │ ├── hud/
│ │ │ └── QuestProgressHud.java IGuiOverlay
│ │ └── mixin/
│ │ ├── ChunkScreenPanelMixin.java
│ │ └── ChunkButtonMixin.java
│ ├── server/
│ │ ├── command/
│ │ │ ├── BnsToolkitCommand.java /bnstoolkit reload, debug
│ │ │ ├── SethomeCommand.java /sethome, /home, etc.
│ │ │ └── BackCommand.java /back
│ │ ├── data/
│ │ │ ├── PlotRegistry.java loads plots.json
│ │ │ ├── PlotOwnership.java SavedData, plot owner UUIDs
│ │ │ ├── SethomeData.java SavedData, per-player homes
│ │ │ └── DeathLocations.java transient, /back support
│ │ ├── event/
│ │ │ ├── PlayerJoinHandler.java tier-based join broadcast
│ │ │ ├── PlayerDeathHandler.java remember death location for /back
│ │ │ └── ChatColourHandler.java tier-based chat name colouring
│ │ └── net/
│ │ └── (packet implementations — see §6)
│ ├── common/
│ │ ├── data/
│ │ │ ├── TierConfig.java loads tiers.json
│ │ │ ├── BountyDef.java DTO mirrored from KubeJS
│ │ │ ├── QuestStateView.java snapshot for UI display
│ │ │ └── PlotDef.java plot definition (region, price)
│ │ └── net/
│ │ └── BnsPacket.java network channel + payload base
│ └── resources/
│ ├── META-INF/neoforge.mods.toml
│ ├── bnstoolkit.mixins.json
│ └── assets/bnstoolkit/lang/en_us.json
└── src/main/resources/
└── data-config/ packed defaults, copied on first run
├── tiers.json
└── plots.json
```
---
## 4. Screen specifications
All screens extend FTB Library's `BaseScreen`. They share a common
header (player name, current tier, spur balance) so navigation feels
consistent.
### 4.1 Quest Log
**Opened by:** keybind (default Q+B?) or right-clicking a Quest Log
block placed at any guild building.
**Layout:** three tabs across the top — Active / Available / Completed.
- **Active tab:** list of currently-accepted bounties with progress
bars. Each row clickable; click expands a detail panel with
description, source guild, reward, and a `Cancel quest` button.
Cancel triggers a confirmation modal warning about cooldown.
- **Available tab:** list of bounties currently offered (rotated daily
by the KubeJS engine). Each row shows the bounty source, reward,
cooldown remaining (if any). Click → detail panel with `Accept` button.
- **Completed tab:** historical log of completed bounties this week,
for satisfaction value. No interaction beyond viewing.
**Network calls:**
- On open: client requests current quest state from server (`S2CQuestSnapshot`)
- On accept/cancel click: `C2SAcceptBounty(id)` / `C2SCancelQuest(id)`
### 4.2 Bounty Board
**Opened by:** right-clicking the Adventurer's Guild Quartermaster
NPC (Easy NPC dialog action runs `/bnstoolkit openboard adventurers_guild`)
OR clicking an in-world `bnstoolkit:bounty_board` block.
**Layout:** poster-board style — the day's available bounties as
visual "cards" laid out in a grid, each with mob icon, name,
description, reward, accept button. Friendly to non-tech users.
**Network calls:**
- On open: `S2CBountyBoardSnapshot(guild_id)` returns today's bounty list
- On accept: same `C2SAcceptBounty(id)` as Quest Log
### 4.3 Tier Upgrade Screen
**Opened by:** right-clicking the Civic Office Magistrate NPC.
**Layout:** full ladder shown as a vertical or horizontal track. Your
current tier highlighted (glowing badge). Next tier shows cost +
delta perks (e.g. "+50 chunks, +1 plot slot, +2 sethomes, +1 bounty,
-2% shop fee, chat colour: yellow"). Big `Upgrade to <Tier>` button
at the bottom, greyed out if you don't have the spurs.
Hovering any future tier shows its full perk list in a side panel.
**Network calls:**
- On open: `S2CTierSnapshot` returns current tier + balance
- On upgrade click: `C2SRequestTierUpgrade` → server verifies balance,
calls KubeJS `/bnstoolkit_internal upgrade_rank <uuid>` which
charges spurs and runs `ftbranks add <player> <tier>`
### 4.4 Plot Purchase Popup
**Opened by:** clicking an unowned plot region in the FTB Chunks map
UI (via the mod's mixin into `ChunkScreenPanel`).
**Layout:** modal overlay on top of the FTB Chunks map. Shows: plot
ID, plot type (standard/premium), region size in chunks, owner (none),
price. Your current spur balance. Your remaining plot slots (e.g.
"2 of 3 used"). `Purchase` and `Cancel` buttons. If you're at your
slot cap or don't have enough spurs, `Purchase` is greyed out with
the reason shown.
**Network calls:**
- On purchase: `C2SBuyPlot(plot_id)` → server checks slot capacity,
asks KubeJS to charge spurs, on success records ownership in
`PlotOwnership` SavedData.
### 4.5 Shop Browser
**Opened by:** right-clicking the Bazaar Master NPC at the Merchant's
Bazaar (provides a curated view of all currently-listed shop blocks
+ NPC sell shop).
**Layout:** searchable item list with current prices, sort by
category. Click an item to see who's selling it / where the shop
block is located. The NPC sell shop (diamonds, netherite etc.) shows
as a special "Official Bazaar" category with fixed prices and the
current diminishing-return multiplier per item.
**Note:** individual Numismatics shop blocks keep their native UI
when right-clicked — this Bazaar Master is the *curated central
view*, not a replacement for shop block interaction.
**Network calls:**
- On open: `S2CShopBrowserSnapshot` (asks KubeJS for current listings)
- Selecting an item → `S2CShopItemDetail(item_id)` for full info
### 4.6 Quest Progress HUD
**Render layer:** `IGuiOverlay`, drawn over the vanilla HUD.
**Layout:** small panel in top-right (configurable position) showing
each active quest as a row — quest name + progress bar + N/M numeric.
Fades on inactive (no progress in last 30s) so it doesn't visually
clutter when you're not actively bounty-hunting. Re-appears the
moment any quest progresses.
**State source:** KubeJS pushes updates via `S2CHudQuestUpdate` packet
when any quest's progress changes (debounced to once per second
maximum per quest). The HUD reads from a client-side cache.
---
## 5. Mixin targets
Minimal — we don't fork FTB Chunks, we inject only what we need.
| Target | Method | Purpose | Mixin file |
|---|---|---|---|
| `ChunkScreenPanel` | `drawBackground` (TAIL) | Overlay drawing — cost preview for the currently-hovered or selected chunk, "Your plot slots: N of M" text in a corner | `ChunkScreenPanelMixin.java` |
| `ChunkScreenPanel$ChunkButton` | `addMouseOverText` (TAIL) | Append "Cost: N spurs" or "Plot: <name>" to the per-chunk hover tooltip | `ChunkButtonMixin.java` |
| `ChunkScreenPanel$ChunkButton` | `onClicked` (HEAD, with cancellation if applicable) | If the clicked chunk is a plot region, open `PlotPurchaseScreen` instead of triggering a regular claim | `ChunkButtonMixin.java` |
That's it. Three injection points across two mixin files. No
modification of FTB Library, FTB Quests, FTB Ranks, or FTB Teams.
---
## 6. Network packet contract
NeoForge `PayloadRegistrar`. All packets versioned via a single
`PROTOCOL_VERSION` constant; bump on schema change.
### Server → Client
| Packet | Payload | When sent |
|---|---|---|
| `S2CQuestSnapshot` | `List<QuestStateView>` | On Quest Log open, on any quest-state change |
| `S2CBountyBoardSnapshot` | `List<BountyDef>` (5-10 entries) | On Bounty Board open |
| `S2CTierSnapshot` | `(currentTier, balance, perksByTier)` | On Tier Upgrade Screen open |
| `S2CShopBrowserSnapshot` | `List<ShopListing>` | On Shop Browser open |
| `S2CHudQuestUpdate` | `(questId, progress, target)` | On quest progress (debounced 1s) |
| `S2CPlotPurchaseResult` | `(success, message)` | After `C2SBuyPlot` resolves |
| `S2CTierUpgradeResult` | `(success, message)` | After `C2SRequestTierUpgrade` resolves |
### Client → Server
| Packet | Payload | What server does |
|---|---|---|
| `C2SAcceptBounty` | `questId` | Call `/bnstoolkit_internal accept_bounty <uuid> <questId>` (KubeJS) |
| `C2SCancelQuest` | `questId` | Call `/bnstoolkit_internal cancel_quest <uuid> <questId>` (KubeJS) |
| `C2SBuyPlot` | `plotId` | Verify slot capacity, ask KubeJS to debit spurs, record ownership |
| `C2SRequestTierUpgrade` | (none — server knows player) | Verify balance, ask KubeJS to debit + promote |
| `C2SOpenBoard` | `guildId` (optional context) | Reply with appropriate snapshot |
All C2S packets include the player implicitly (NeoForge passes
the `ServerPlayer` in the handler).
---
## 7. Data persistence
| Data | Storage | Lifetime |
|---|---|---|
| Plot ownership | `bnstoolkit:plots` — vanilla NeoForge `SavedData` attached to overworld | Persists with world. Authoritative source. |
| Sethomes | `bnstoolkit:sethomes` — per-player attached data | Persists with world |
| Death locations (for `/back`) | In-memory only, evicted after teleport or on logout | Transient |
| Quest state | KubeJS-managed JSON at `world/data/bns_economy/quests/<uuid>.json` | Not the mod's concern — the mod only reads via packets |
| Tier configuration | `config/bnstoolkit/tiers.json` (hot-reloadable) | Until edited |
| Plot definitions | `config/bnstoolkit/plots.json` (hot-reloadable via `/bnstoolkit reload`) | Until edited |
Plot ownership and sethomes are server-authoritative. Clients only
get views via packets — they never write to these stores directly.
---
## 8. Configuration files
### `config/bnstoolkit/tiers.json`
Defines the 13-tier ladder. Hot-reloadable.
```json5
{
"tiers": [
{
"id": "peasant",
"displayName": "Peasant",
"cost": 0,
"perks": {
"wildernessChunks": 9,
"plotSlots": 0,
"sethomes": 1,
"dailyBountySlots": 3,
"shopFeeDiscount": 0,
"chatColour": "grey",
"joinBroadcast": "none",
"backAfterDeath": false
}
},
/* ... 12 more tiers ... */
]
}
```
### `config/bnstoolkit/plots.json`
Defines spawn plot regions. Each plot has a unique ID, region bounds
(chunk coordinates), type (standard/premium), and price.
```json5
{
"plots": [
{
"id": "bazaar_stall_01",
"displayName": "Bazaar Stall 1",
"type": "standard",
"price": 2000,
"chunks": [[10, 20], [10, 21]], // chunk coords, inclusive
"world": "minecraft:overworld"
},
{
"id": "bazaar_corner_premium",
"displayName": "Premium Corner Plot",
"type": "premium",
"price": 5000,
"chunks": [[15, 25], [15, 26], [16, 25], [16, 26]],
"world": "minecraft:overworld"
}
]
}
```
---
## 9. KubeJS integration surface
The mod calls into KubeJS via custom commands registered by KubeJS
(`ServerEvents.commandRegistry`). All have the `bnstoolkit_internal`
prefix to mark them as not-for-player-use:
| Command | Purpose |
|---|---|
| `/bnstoolkit_internal accept_bounty <uuid> <questId>` | Mark quest as ACTIVE in KubeJS state |
| `/bnstoolkit_internal cancel_quest <uuid> <questId>` | Cancel quest, apply cooldown |
| `/bnstoolkit_internal complete_quest <uuid> <questId>` | Mark quest READY (for testing/manual) |
| `/bnstoolkit_internal turn_in_quest <uuid> <questId>` | Pay reward via Numismatics |
| `/bnstoolkit_internal debit <uuid> <amount> <reason>` | Charge spurs (used by plot purchase, tier upgrade) |
| `/bnstoolkit_internal credit <uuid> <amount> <reason>` | Pay spurs |
| `/bnstoolkit_internal upgrade_rank <uuid> <tierId>` | Promote via FTB Ranks |
| `/bnstoolkit_internal get_balance <uuid>` | Returns current spur balance (used for screen header) |
All commands return a JSON result via the command output that the
mod parses. KubeJS handles the actual Numismatics + FTB Ranks calls
(it already has the `Java.loadClass` helpers from the economy KubeJS
scripts).
KubeJS in turn pushes events to the mod via custom payload packets
when quest state changes (so the HUD can update without polling).
---
## 10. Build & release
- **Repo:** new private Gitea repo `minecraft/bnstoolkit` (separate
from the modpack repo for clean release-versioning, but mirrored
to the modpack's `pack/overrides/mods/` on each release)
- **Build:** standard NeoForge 1.21.1 ModDevGradle template
- **Dependencies:** Architectury API (provided), FTB Library
(provided), Numismatics (compileOnly), FTB Chunks (compileOnly)
- **CI:** Gitea Actions runs `./gradlew build` on push to main,
uploads the jar as a release artifact
- **Modpack integration:** the modpack's bump-version flow includes
pulling the latest bnstoolkit release jar and updating
`pack.lock.json` / manifest like any other mod
- **Versioning:** semantic — `1.0.0` for V1 ship, `1.x.y` for
patches, `2.0.0` if we ever change the network protocol or data
format
---
## 11. Perk specifications (detailed)
Every perk listed in the 13-tier ladder, expanded with mechanics,
implementation, and edge cases.
### 11.1 Wilderness chunks
**What it does:** caps how many wilderness chunks (outside spawn)
a player can claim via the FTB Chunks map. Claiming within the cap
is free; over the cap is refused with an in-game message.
**Implementation:** FTB Ranks permission node
`ftbchunks.max_claimed_chunks` set per rank in the FTB Ranks config
(written by the mod on first launch from `tiers.json`). FTB Chunks
reads this permission natively — no mod-side enforcement needed.
**Edge case:** if a player downgrades (which they can't normally,
but might happen via op command), they keep already-claimed chunks
over the new cap, but can't claim more until they free some.
### 11.2 Plot slots
**What it does:** caps how many spawn plots (predefined regions at
spawn, see `plots.json`) a player can own concurrently. Independent
of wilderness chunks.
**Implementation:** stored in `PlotOwnership` SavedData. On purchase,
mod checks `playerOwnedPlots.size() < tier.plotSlots`. If at cap,
the Plot Purchase Popup shows the slot count and disables the
`Purchase` button.
**Releasing a plot:** players can sell their plot back via a "Release"
button in the plot-detail screen (opens when they click their own
plot in the map). Releases the slot. Refund: 50% of purchase price.
### 11.3 Sethomes
**What it does:** how many `/sethome <name>` locations a player can
save. `/home <name>` teleports there; `/listhomes` lists; `/delhome <name>`
removes.
**Implementation:** the pack currently has no sethome mod. We build
it in bnstoolkit:
- `/sethome <name>` saves current player position + world + name
- `/home <name>` teleports (validates dimension load)
- `/listhomes` shows owned homes
- `/delhome <name>` removes
- Cap enforced from the player's tier (`tiers.json``sethomes`)
- Unlimited = represented as `-1` in the JSON
**Cooldown:** 30-second cooldown between teleports to prevent
combat-evasion. Configurable in `tiers.json` per-tier (low tiers
have longer cooldown).
**Edge case:** moving a sethome out of a now-claimed plot or a
private base is allowed — sethomes are positional, not contextual.
### 11.4 Daily bounty slots
**What it does:** how many bounties a player can have ACTIVE
simultaneously. Once the slot is freed (by completion or cancel),
the player can accept another from any guild board.
**Implementation:** enforced in KubeJS bounty engine. The mod's
Bounty Board and Quest Log screens read the slot count via packet
and grey out the Accept button when at cap.
**Edge case:** a quest in READY state still counts against your
active slots until you turn it in. This is intentional — pushes
players to actually visit the NPC to claim.
### 11.5 Shop fee discount
**What it does:** reduces the "merchant cut" deducted when you sell
items at the Bazaar's NPC sell shop. The Bazaar charges a base 25%
fee on all sales; your tier provides a discount that reduces this
toward zero.
**Worked example:** you sell a diamond worth 10 spurs at the Bazaar.
- Peasant (0% discount): 25% fee → you receive 7.5 spurs (rounded
down to 7).
- Knight (-5% discount): 20% fee → 8 spurs.
- Sovereign (-25% discount): 0% fee → full 10 spurs.
**Implementation:** KubeJS sell-shop handler reads the seller's tier
and computes effective payout. Rounded down to nearest integer.
**Edge case:** player-to-player Numismatics shop blocks do NOT
apply this fee — those are direct peer transactions, no Bazaar
middleman. The fee only applies to the official Bazaar sell shop.
### 11.6 Chat colour
**What it does:** the player's name in chat appears in this colour,
visible to everyone. Visual rank marker.
**Implementation:** KubeJS `PlayerEvents.chat` event modifies the
chat component for the player's display name based on their tier
permission (read from FTB Ranks). The mod also colours the player's
nameplate above their head (via a small client packet on tier change
that other clients use to set the display name colour locally).
**Colour ladder:**
- 1-2 Peasant/Farmer: grey
- 3 Citizen: white
- 4 Merchant: yellow
- 5 Knight: green
- 6 Baron: cyan
- 7 Viscount: blue
- 8 Earl: purple
- 9 Marquess: gold
- 10 Duke: orange
- 11 Archduke: red
- 12 Grand Duke: bright red
- 13 Sovereign: rainbow (animated via colour-cycling text component)
**Edge case:** the Founder cosmetic `[Founder]` prefix sits before
the coloured name, in a distinct colour (probably bright yellow or
white-bold). Sovereign + Founder is visually busy but acceptable —
only one player on the server will have both.
### 11.7 Join broadcast
**What it does:** when the player logs in, this controls what message
goes to everyone else on the server.
**Levels:**
- **none:** standard vanilla `<player> joined the game`.
- **simple:** rank-prefixed message in their chat colour. Example:
```
[Knight] Matt has logged in.
```
- **fancy:** multi-line, with a separator. Example:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Viscount] Matt has arrived
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
- **epic:** multi-line, with a sound effect (e.g. trumpet fanfare,
configurable per-tier). Larger ASCII separator. Possibly an ASCII
art banner of the rank.
- **epic + particles:** as epic, plus a 5-second particle effect at
the player's spawn location (firework spark, end-rod sparkle, etc.)
for everyone nearby to see.
- **epic + spectacular:** as epic + particles, plus a server-wide
effect — actionbar message visible to everyone, brief light-up of
the sky if Sovereign joins.
**Implementation:** KubeJS `PlayerEvents.loggedIn` reads tier and
dispatches the appropriate message. Particle/sound effects are
server-side commands (`/particle`, `/playsound`).
### 11.8 `/back` after death
**What it does:** the `/back` command teleports the player to where
they last died. Tiers without this perk get "You don't have access
to /back yet" if they try.
**Implementation:** mod listens to `LivingDeathEvent` for players,
stores `(world, x, y, z, timestamp)` in `DeathLocations` (in-memory
only, evicted on logout or after `/back` use). `/back` command
verifies tier perm + reads the entry.
**Cooldown:** 60 seconds. Prevents combat reinsertion.
**Edge case:** dying again clears the previous death location.
You only get one `/back` per death. Logging out clears it too —
intentional, to prevent abuse.
### 11.9 Future perk dimensions (not in V1, but easy to add later)
Architecture supports adding new perk columns in `tiers.json`
without code changes (the schema is open-ended). Candidates for
later expansion:
- **Particle trail** — high tiers leave a visible particle wake when
walking
- **Custom join sound** — different sound effect per tier on login
- **Custom title prefix** — text added before chat name beyond the
colour (e.g. `⚔ Knight Matt`)
- **Priority bounty access** — higher tiers see a larger pool of
daily bounties to pick from
- **Tier-gated vendor stock** — specific Numismatics shops only
visible to Baron+ etc.
- **Daily login bonus** — small spur reward for logging in, scaling
by tier
- **Death keep-inventory chance** — RNG % to keep inventory on death
(controversial, gate carefully)
---
## 12. Open implementation questions
These can be deferred until they become blocking, but worth flagging:
1. **Sovereign tier rainbow chat name** — Minecraft text components
don't natively support gradient text. Implementation options:
(a) cycle through colours character by character, or (b) animate
via packet on a tick interval. Choose during Phase 0c scaffolding.
2. **Plot release refund percentage** — proposed 50%. Tune via
playtest.
3. **Sethome cooldown values per tier** — proposed scaling from 60s
(low tiers) to 5s (Sovereign). Confirm in `tiers.json` schema.
4. **`/back` interaction with dimension changes** — does dying in
the End and `/back`ing return you to the End or to overworld?
Default: returns to wherever you died including the dimension.
5. **Founder cosmetic prefix colour** — bright yellow? Bold white?
Animated? Settle during implementation.
6. **Sovereign spectacular join effect** — exact effects need design
pass (sky flash? Server-wide actionbar text?). Tune in
`tiers.json`.
---
## 13. Implementation phases (mod-specific)
Aligned with the economy doc's Phase 0c (mod scaffolding):
| Mod phase | Deliverable |
|---|---|
| **M0** | Gradle project, mod metadata, FTB Library dep wired, network channel registered, empty placeholder for each screen class. Builds + loads cleanly into the running pack. |
| **M1** | Tier system: TierConfig loads from `tiers.json`, `TierUpgradeScreen` works, perks applied via FTB Ranks promotion command. Founder cosmetic prefix. Chat colour. Join broadcast at all tiers. |
| **M2** | Sethome system: `/sethome`, `/home`, `/delhome`, `/listhomes`, `/back`. SavedData persistence. Per-tier cap enforcement. |
| **M3** | Plot system: `PlotRegistry` + `PlotOwnership`, FTB Chunks mixins, `PlotPurchaseScreen`. KubeJS integration for spur debit. |
| **M4** | Quest UI: `QuestLogScreen`, `BountyBoardScreen`, `QuestProgressHud`. Network packets between KubeJS bounty engine and the mod. |
| **M5** | Shop browser: `ShopBrowserScreen`. |
| **M6** | Polish — animations, sound effects, particle effects for high tiers, theming pass on all screens. |
Each phase is independently shippable as a `1.x.0` release of the
mod jar.
---
## See also
- [`economy-system.md`](./economy-system.md) — gameplay-side design (currency, quest mechanics, villager rebalance, etc.)
- `MEMORY.md` entries — KubeJS Rhino gotchas, defaultconfigs pattern
+509
View File
@@ -0,0 +1,509 @@
# Brass and Sigil — Economy & Quest System Design
> Status: 13-tier ladder locked (v0.4). Architecture + UI confirmed in v0.3.
> Owner: matt
> Last updated: 2026-06-06
---
## 1. Goals
- **One unified currency** — spurs (Numismatics).
- **Multiple ways to earn, each one bounded.** No infinite money printer.
- **Multiple sinks that scale with progression.** Money disappears as you advance, so wealth never dwarfs the cost of new ambitions.
- **Spatial design that gives spawn personality.** Themed guild buildings, each with a clear purpose, each tied to the same wallet.
- **Forward-compat with a proper quest line** via FTB Quests, while supporting daily/repeatable activities at spawn.
- **Anti-automation by design**, not by patches.
---
## 2. Core architecture
```
┌────── MONEY IN (bounded) ──────┐
│ │
Adventurer's Guild — Daily bounties (cooldowns, scaling)
Merchant's Bazaar — Sell shop (fixed + DR), dynamic exchange
Mint — Capped emerald → spur conversion *
First-time milestones — One-shot per player
FTB Quests — Progression milestone payouts
┌───────────────────┐
│ Player wallet │ ← Numismatics
│ (spurs) │
└───────────────────┘
┌────── MONEY OUT (scaling) ──────┐
│ │
Civic Office — Plot claim costs (FTB Chunks override)
Civic Office — Tier upgrades (FTB Ranks integration)
Merchant's Bazaar — Player-to-player job board (no minting)
Villagers — Re-priced trades, net sink
Custom services — Warps, sethomes, premium cosmetics
```
\* Only if we keep emeralds; see §5 — we're moving villagers to spurs, which probably retires the Mint as a currency-conversion station and repurposes it as a wallet/info hub.
---
## 3. Spawn — themed guild buildings
Each guild is a destination. The guild's NPC is both the **entry point** (accept work) and **exit point** (return to claim reward). This pattern applies universally — see §4.
| Building | Purpose | Primary mechanics inside |
|---|---|---|
| **Adventurer's Guild** | Combat & exploration | Bounty board (daily mob hunts), boss-kill quests, dungeon-delve quests (future) |
| **Merchant's Bazaar** | Trade & raw-mat economy | Numismatics sell shop, dynamic exchange counter, player-driven job board |
| **Civic Office** | Land & status | Tier upgrade screen, spawn plot registry (purchase / view your plots), sethome/warp services. NOTE: regular wilderness chunk claiming uses the FTB Chunks map and is tier-allocated, not paid per chunk — see §4. |
| **Library / Academy** | Knowledge & magic | FTB Quests anchor NPC, Ars Nouveau themed quests, lore terminals |
| **Tavern** | Social & onboarding | First-time newbie quests, casual NPCs, food/buff vendor |
| **Foundry / Workshop** | Crafting & commissions | Create-themed contracts, crafting orders, raw-material market |
| **Mint** | Currency information | Wallet info, currency tutorial, possibly emerald exchange |
### Forward-compat with a full quest line
The architecture is naturally dual-track:
- **FTB Quests** = the *progression* backbone — chapter-by-chapter pack content, one-time rewards, story arcs. The campaign.
- **Spawn-building NPCs** (Easy NPC + KubeJS) = the *daily/repeatable* layer — bounties, sell shops, services. The live activities.
Both pay into the same wallet. Players run the campaign at their pace and the daily content for income/replay value. They reinforce each other rather than compete.
---
## 4. Tier system & spawn plots
Two distinct land/status systems that often get conflated. Keeping them clean:
### 4a. Civic tier ladder
Every player has a Civic rank. Higher rank unlocks perks. Players spend spurs at the Civic Office NPC to upgrade. Powered by FTB Ranks under the hood (permission nodes drive what each rank unlocks).
The ladder is intentionally long (13 tiers) so endgame ranks feel rare and aspirational. Top tiers represent months of dedicated play; Sovereign is "you've finished the game."
| # | Title | Cost to reach | Wilderness chunks | Plot slots | Sethomes | Daily bounties | Shop fee | Chat colour | Join broadcast | `/back` |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | **Peasant** | free (starter) | 9 | 0 | 1 | 3 | 0% | grey | — | — |
| 2 | **Farmer** | 200 | 18 | 0 | 1 | 3 | 0% | grey | — | — |
| 3 | **Citizen** | 750 | 35 | 1 | 2 | 3 | 0% | white | — | — |
| 4 | **Merchant** | 2,500 | 60 | 1 | 3 | 4 | -2% | yellow | — | ✓ |
| 5 | **Knight** | 6,500 | 100 | 2 | 4 | 4 | -5% | green | simple | ✓ |
| 6 | **Baron** | 15,000 | 150 | 2 | 5 | 5 | -7% | cyan | simple | ✓ |
| 7 | **Viscount** | 35,000 | 220 | 3 | 6 | 5 | -9% | blue | fancy | ✓ |
| 8 | **Earl** | 75,000 | 300 | 3 | 8 | 6 | -11% | purple | fancy | ✓ |
| 9 | **Marquess** | 150,000 | 400 | 4 | 10 | 6 | -13% | gold | fancy | ✓ |
| 10 | **Duke** | 300,000 | 525 | 5 | 12 | 7 | -15% | orange | epic | ✓ |
| 11 | **Archduke** | 700,000 | 700 | 6 | 16 | 7 | -17% | red | epic | ✓ |
| 12 | **Grand Duke** | 1,500,000 | 900 | 7 | 20 | 8 | -19% | bright red | epic + particles | ✓ |
| 13 | **Sovereign** | 10,000,000 | 1,500 | 10 | unlimited | 10 | -25% | rainbow | epic + spectacular | ✓ |
**Notable shape choices:**
- **Knight (tier 5)** is the gentry threshold — first colored chat (green), first join broadcast, first `/back` after a meaningful jump in plot slots
- **Duke → Archduke jump** is the late-game wall (300k → 700k, ~2.3×) — Archduke is the start of "I've really committed to this server" territory
- **Sovereign is dramatic** — 6.7× the Grand Duke cost. Real "finished the game" status. -25% shop fee is a step-change leap; unlimited everything; spectacular join effects.
Numbers are first-pass calibration. The full perk table lives in a single JSON (`config/bnstoolkit/tiers.json`) that's hot-reloadable via `/bnstoolkit reload`. Easy to tune any column without code changes.
### 4a.1 Founder flag (admin cosmetic)
The server owner / admin has a permanent **`[Founder]`** cosmetic prefix in chat that sits alongside their normal rank. It carries no gameplay perks — it's just visible identification so friends know who runs the server. The Founder still grinds the regular tier ladder like everyone else (good for playtesting balance + sharing the experience). Op commands are the actual admin tool; the Founder tag is purely visual.
**Wilderness claims:** at every tier above Peasant, you get a flat allowance of N chunks you can claim anywhere outside spawn via the FTB Chunks map. Claiming is **free** within your allowance, refused above. No per-chunk pricing. Driven by FTB Ranks → FTB Chunks permission nodes (`ftbchunks.max_claimed_chunks`).
### 4b. Spawn plots
Spawn plots are a **separate system** from wilderness claims.
- **Pre-defined regions** at spawn — single chunks or small fixed clusters — designated by admins as "plots." Visible as fenced/bordered areas on the spawn map.
- **Purchased with spurs** at additional cost per plot (separate from tier upgrade cost). Indicative pricing: standard plot ~2,000 spurs, premium (corner / prime location) ~5,000 spurs.
- **Primary use:** shops. Players set up Numismatics shop blocks on their plots and sell to other players. Residence is allowed but secondary.
- **Slot-limited by tier:** your civic rank determines how many spawn plots you can own simultaneously (column above). Hit the limit, you can't buy another until you upgrade or release a plot.
- **Purchase happens inside the FTB Chunks map UI** — clicking a plot region in the map opens a confirmation screen (provided by our custom mod's mixin) showing cost + your remaining slots. Same UI you use for wilderness claims, integrated.
- **Plot data lives in the custom mod** (`bnstoolkit:plots`), not in FTB Chunks. Plots are a separate ownership system that uses the FTB Chunks map as its visual surface.
---
## 5. Quest & bounty flow (universal pattern)
**Core principle:** for any guild, the player **accepts** work from an NPC, **completes** it in the world, then **returns** to that NPC to **turn it in** and claim the reward. No instant payouts. This:
- Anchors guilds as destinations
- Creates risk/reward tension (die on the return, lose the reward)
- Encourages waypoint use and travel
- Lets the server centrally control payouts and detect cheating
### State machine
Each quest, per player, lives in one of these states:
```
AVAILABLE ─[player accepts]─► ACTIVE ─[objective met]─► READY ─[turned in]─► COMPLETED
▲ │
└──────────────────────[cooldown elapses]───────────────────────────────────────────┘
```
- **AVAILABLE** — NPC offers the quest in dialog. Player has no current copy of it.
- **ACTIVE** — Player accepted. KubeJS tracks objective progress via relevant events (`EntityEvents.death` for kills, `ItemEvents.firstRightClicked` for explorations, etc.).
- **READY** — Objective met. Reward is *promised but not yet paid.* NPC dialog now offers a turn-in option.
- **COMPLETED** — Reward paid out. Quest goes on cooldown for this player.
- **AVAILABLE** (again) — after the cooldown, the NPC will offer it again.
### Worked example — daily bounty
1. **Adventurer's Guild NPC** has 5 randomized daily quests (e.g. "Slay 10 Plunderers — 50 spurs").
2. Player right-clicks NPC → Easy NPC dialog → "Accept: Slay 10 Plunderers."
3. KubeJS writes `quest:plunderer_10` to the player's quest state with `count: 0`.
4. Player kills Plunderers in the world. `EntityEvents.death` for `supplementaries:plunderer` increments the count.
5. At count=10, KubeJS flips state to **READY** and notifies the player ("Return to the Adventurer's Guild to claim your reward.").
6. Player returns. Right-click NPC. Easy NPC dialog detects READY state and offers "Turn in: Slay 10 Plunderers."
7. KubeJS pays 50 spurs into the player's Numismatics wallet, flips state to **COMPLETED**, sets cooldown to 24h.
### Universal applicability
The same accept → complete → return pattern works for every guild:
| Guild | Accept | Complete | Return |
|---|---|---|---|
| **Adventurer's** | "Hunt 10 plunderers" | kill them | NPC pays bounty |
| **Merchant's** | "Procure 32 diamonds for our buyer" | gather them | NPC pays inflated price + completes |
| **Civic** | "Survey 5 unclaimed chunks" | walk through them | NPC pays survey fee |
| **Library** | "Recover this lost tome" | find the book in a structure | NPC pays + grants reputation |
| **Foundry** | "Craft 10 Andesite Casings" | craft them | NPC pays commission |
### Player-facing UI (locked)
Polished, integrated, no chat commands to memorise. All player interaction happens through GUIs and NPC dialogue. Custom companion mod (working name `bnstoolkit`) provides the screens; FTB Library's UI framework is the visual basis so our screens match FTB Quests, FTB Chunks, and FTB Teams natively.
| Surface | Implementation |
|---|---|
| **Quest Log screen** | Custom mod screen. Tabs: Active / Available / Completed. Click a quest to inspect, cancel from the detail panel. Opened via keybind or by clicking a Quest Log block at any guild. |
| **Bounty Board screen** | Custom mod screen, opened by right-clicking the Adventurer's Guild Quartermaster NPC OR clicking the in-world Bounty Board block. Lists today's available daily bounties with descriptions, rewards, and "Accept" buttons. |
| **Tier upgrade screen** | Custom mod screen, opened at the Civic Office NPC. Visual ladder, your current tier highlighted, next-tier cost, perks unlocked at each rank, big Upgrade button. |
| **Plot purchase popup** | Custom mod mixin into the FTB Chunks map UI. Hover a plot region → cost + slots tooltip. Click → confirmation popup. Owned plots also clickable to see plot info. |
| **Shop browser** | Numismatics shop blocks render their existing inventory UI by default; optional richer browser screen for the central Merchant's Bazaar inventory. |
| **Quest progress HUD** | Custom mod render layer — styled HUD element showing active quest names + progress bars. NOT stacked vanilla bossbars. |
| **NPC dialogue + buttons** | Easy NPC handles all NPC interactions: accept/turn-in, info, tutorials, "click for details" buttons. Players learn the system through NPC dialogue, not by reading docs. |
### Why custom mod, on FTB Library's UI framework
- **Visual consistency:** FTB Quests, FTB Chunks, FTB Teams all use FTB Library's widget framework. Our screens built on the same framework will look and feel like part of the same product.
- **Already a pack dependency:** no new mod added just for the UI layer.
- **Mixin compatibility:** our spawn plot mixin into the FTB Chunks map operates on FTB Library widgets natively — no translation layer between vanilla MC widgets and FTB Library widgets.
- **Less boilerplate than vanilla MC Screen API:** Panel, BlockButton, IconButton, Tooltip, TextField widgets already exist and behave consistently with how players already know FTB UIs to work.
- **Forking FTB is off the table:** FTB mods are "All Rights Reserved" — depending on FTB Library as a library is fine (that's what libraries are for), forking FTB Quests or FTB Chunks for redistribution is not legally clean. Custom mod alongside the FTB stack is the correct architecture.
### Why NOT FTB Quests for daily bounties
FTB Quests stays for **long-form progression** (Phase 5+), not for daily bounties. Source review confirms:
- No abandon/cancel event (Ctrl+Shift+R reset fires no event we can hook)
- Cooldown is per-team, not per-player
- Visibility is per-team, not per-player — rotating "5 of 20 daily" doesn't fit cleanly
For long-form progression where one-shot rewards + team scope are fine, FTB Quests is great. Our custom bounty engine handles the daily/repeatable pattern with its own UI.
### Quest slot limit
Players have a **maximum of 3 active quests at a time.** This is the friction that makes cancel meaningful:
- Forces players to pick which quests they actually want — no "accept all 5, see what's easiest, cancel the rest" RNG-shopping.
- Keeps the state files small.
- Makes ready-to-turn-in queues meaningful (you can hold 3 unrewarded quests in your back pocket).
A quest moving to READY frees the slot for accepting a new one even before turn-in.
### KubeJS implementation — locked specifics
- **State storage:** per-player JSON in `world/data/bns_quests/<uuid>.json`. Schema:
```json
{
"active": [
{
"id": "advg_plunderer_10",
"acceptedAt": 1717684800,
"progress": 7,
"target": 10,
"rewardSpurs": 50,
"source": "adventurers_guild",
"bossbarId": "bns:quest_advg_plunderer_10_<uuid>"
}
],
"completed": {
"advg_plunderer_10": { "cooldownUntil": 1717771200, "completions": 3 }
}
}
```
- **Event tracking:** KubeJS event handlers (`EntityEvents.death`, `ItemEvents.pickedUp`, `BlockEvents.broken`) read the player's active quests and increment progress + update bossbar value.
- **Bossbar lifecycle:** on quest accept, KubeJS creates a unique per-player bossbar via `server.runCommandSilent('bossbar add <id> <name>')`, assigns it to that player only, updates `value` and `max` on progress. On turn-in or cancel: `bossbar remove`.
- **Numismatics integration (direct Java access — no events shipped, no KubeJS bindings):**
```js
const Numismatics = Java.loadClass('dev.ithundxr.createnumismatics.Numismatics');
const ReasonHolder = Java.loadClass('dev.ithundxr.createnumismatics.content.backend.ReasonHolder');
const acct = Numismatics.BANK.getAccount(player); // ServerPlayer
const balance = acct.getBalance(); // int (spurs)
acct.deposit(50); // pay quest reward
const ok = acct.deduct(100, ReasonHolder.empty()); // returns false if broke
Numismatics.BANK.markBankDirty(); // force persistence
```
- **Dialog branching:** Easy NPC dialog branches on scoreboard objective `bns.quest.<npc_tag>` that KubeJS mirrors quest state into. Dialog `Command` action triggers `/bns_quest accept <id> <npc_tag>` which routes to KubeJS.
- **Commands:** registered via `ServerEvents.commandRegistry` in KubeJS. Scoped to the player who runs them. The custom `/bns_quest` namespace handles NPC-triggered actions; `/quests` is the player-facing tracker.
- **FTB Chunks claim cost (Phase 2):** requires `FTB XMod Compat` mod. Then:
```js
FTBChunksEvents.before('claim', event => {
const player = event.source.player;
const acct = Numismatics.BANK.getAccount(player);
const cost = computeClaimCost(player);
if (acct.getBalance() < cost) {
event.setResult(ClaimResult.YOU_DO_NOT_HAVE_ENOUGH_SPURS); // custom enum value
return;
}
acct.deduct(cost, ReasonHolder.empty());
});
```
**Note:** the `.before` may fire twice (sim + real). Guard with idempotent state, or do balance check in `.before` and deduct in `.after('claim')`.
---
## 5. Villager rebalance
**Decision:** move all villagers to **spurs** (not emeralds). Thematic consistency over preserving vanilla mental model.
**Decision principles:**
- Villagers are **net money sinks** — players spend more on villagers than they earn from them.
- **Useful trades stay** — enchanted books, tools, food, decorative blocks. Reasonable spur prices.
- **Trash trades get deleted** — no one ever wanted to sell rotten flesh or buy paper.
- **Sell-to-villager trades exist but are bad** — `64 wheat → 1 spur`, `16 leather → 1 spur`. Farms still possible, but ~10× slower than any other income source.
### Price guidance (initial calibration — tune via playtesting)
#### Buy-from-villager (player pays spurs)
| Item | Tier-1 | Tier-3 | Tier-5 |
|---|---|---|---|
| Standard enchanted book (random) | 50 spurs | 120 spurs | 250 spurs |
| Mending book | — | — | 800 spurs |
| Diamond tool (unenchanted) | — | 80 spurs | — |
| Diamond tool (enchanted) | — | — | 350 spurs |
| Bookshelf | 15 spurs | — | — |
#### Sell-to-villager (player gets spurs)
| Item | Spurs per unit |
|---|---|
| Wheat | 1 per 64 |
| Paper | 1 per 32 |
| Leather | 1 per 16 |
| Iron ingot | 2 per 1 |
| Diamond | 8 per 1 *(matches sell-shop fixed price — players use whichever is closer)* |
### Scope
~60 trades × 5 profession tiers = ~300 trade entries to redesign. One-time data-design pass, implemented via `ServerEvents.villagerTrades` in KubeJS.
---
## 6. Money inflow sources (detailed)
| Source | Cap / DR mechanism | Rough rate |
|---|---|---|
| **Daily bounties** | Per-player per-quest cooldown (24h) | ~200-500 spurs/day for an active player |
| **Sell shop (fixed price + DR)** | Lifetime sales tracker per player, 10% price drop per 64 sold | ~50-200 spurs/hr while diamond stockpile lasts |
| **Dynamic exchange** | Global supply pool — price drops with sales | Self-balancing |
| **First-time milestones** | One-shot per player (`hasFlag`) | 5-50 spurs per milestone, ~10 milestones total |
| **FTB Quest progression** | One-time per quest | Varies; cluster rewards at chapter completion |
| **Villager sell trades** | Inherently slow + bad prices | ~50 spurs/hr from a serious villager farm |
---
## 7. Money outflow sinks (detailed)
| Sink | Cost curve | Why this scales right |
|---|---|---|
| **Plot claims** | Free up to 9 chunks. 50 spurs/chunk to 25. 200 spurs/chunk beyond. | New players claim free starter areas; ambitious players pay real money |
| **Tier upgrades** | Cumulative cost: 500 → 2k → 8k → 30k spurs | Endgame sink, status display |
| **Buy-from-villager** | See §5 | Constant outflow as you build up |
| **Premium cosmetics** | 100-2k spurs each | Optional sink for cash-flush players |
| **Services** | Sethome: 50 spurs, warp: 25 spurs, /back: 10 spurs | Convenience taxes |
---
## 8. Mod stack (locked)
### Already in pack
| Need | Mod | Version |
|---|---|---|
| Currency + wallets + shop blocks | Numismatics | 1.0.20 |
| NPC dialog presentation | Easy NPC | 6.16.x (6.17.0 available, minor bump) |
| Plot claims | FTB Chunks | 2101.1.14 |
| Team management | FTB Teams | 2101.1.9 |
| Core library | FTB Library | 2101.1.31 |
| Cross-mod compat | Architectury API | 13.0.8 |
| Glue, commands, state | KubeJS | 2101.7.2 |
### Adding in Phase 0a (mod-additions PR — blocks everything else)
| Mod | Version | Reason |
|---|---|---|
| **FTB Quests** | 2101.1.25 | Progression quest UI (Phase 5+) |
| **FTB XMod Compat** | latest 1.21.1 NeoForge | KubeJS hooks for FTB Chunks claim events + FTB Quests events |
| **FTB Ranks** | 2101.1.3 | Tier system. CurseForge only, no Modrinth listing. |
### Building our own (custom companion mod)
**`bnstoolkit`** (working name) — a small companion mod in our own repo. Confirmed locked-in.
- Provides all custom GUI screens (Quest Log, Bounty Board, Tier upgrade, Shop browser)
- Provides the FTB Chunks map mixins (spawn plot cost overlay, hover tooltip, purchase popup)
- Provides custom quest-progress HUD render layer
- Provides the `bnstoolkit:plots` data storage + ownership permissions
- Provides network packets for client↔server state sync
- Provides the `/bnstoolkit reload` admin command for hot-reloading tier config
- Built on FTB Library UI framework for visual consistency
- KubeJS handles all gameplay logic (bounty rotation, state machine, villager trades, Numismatics integration) — the mod is purely the UI + integration layer
Estimated scope: ~2-3k LOC, single-developer week of focused work. Bounded.
### Explicitly NOT adding
- CustomNPCs (Easy NPC covers it)
- Citizens (Bukkit-only)
- PMMO / skill mods (out of scope)
- Generic "quest giver" mods (FTB Quests + our KubeJS bounty engine cover it)
---
## 9. Phasing
Each phase is independently shippable. Earlier phases unlock value without depending on later ones.
| Phase | Deliverable | Spawn build required? |
|---|---|---|
| **0a** | **Mod additions PR:** FTB Quests + FTB XMod Compat + FTB Ranks (+ minor Easy NPC bump). New pack version. | No |
| **0b** | Spawn skeleton — 7 guild building blockouts (rough). **User does in-game when time permits.** Non-blocking. | Yes (user) |
| **0c** | **`bnstoolkit` companion mod — scaffolding pass.** Mod project set up, FTB Library UI dependency wired, network packet plumbing, build/release scripts. No screens yet. | No |
| **1** | **Sell shop:** Numismatics shop blocks for diamonds (10 spurs) + netherite ingots (100 spurs). Lifetime-sale tracker for diminishing returns. KubeJS only, no mod work needed. | No |
| **2** | **Tier upgrade flow:** FTB Ranks integrated + KubeJS `/tier upgrade` command. **`bnstoolkit` Tier upgrade screen** wired to Civic Office NPC. Tier perks driven by `tiers.json`. | No |
| **3** | **Spawn plot system:** plot region registry in `bnstoolkit`, ownership tracking, FTB Chunks map mixin (cost overlay + purchase popup). | No |
| **4** | **Bounty engine:** state machine, KubeJS-rotated dailies, `bnstoolkit` Bounty Board screen + Quest Log screen + Quest progress HUD. NPC dialog hooks via Easy NPC. 3-slot cap. | No (NPC layer activates once spawn is built) |
| **5** | **Villager rebalance:** strip vanilla, ship spur-priced trade table via `ServerEvents.villagerTrades`. | No |
| **6** | **Progression quest line:** FTB Quests anchor + first chapter. KubeJS rewards dispense spurs via Numismatics API. | No (anchor NPC at Library once spawn exists) |
| **7** | **Dynamic exchange rates:** global supply-pool pricing for raw mats. | No |
| **8+** | Lootr dungeons, Foundry crafting commissions, deeper FTB Quests chapters, player-vs-player bounties (if wanted). | TBD |
---
## 10. Risk register
| Risk | Likelihood | Mitigation |
|---|---|---|
| Bounty engine bugs out, players lose progress | Medium | Atomic state writes; logs of every state transition |
| Villager farms still profitable after rebalance | Medium | Track player sell volumes; tune prices in patches |
| KubeJS-EasyNPC dialog branching has limits | Low-Med | Verify hooks in a test world before Phase 3; fall back to command-driven NPCs if needed |
| FTB Chunks `/claim` command override gotchas | Low | Test in dev world; FTB Chunks API is stable |
| Plot cost feels punishing | Med | First 9 chunks free is the safety valve; tune from playtest |
| Players bypass the system via creative / `/give` | Op-only | Document op responsibility; consider audit logs for op spur grants |
---
## 11. Decisions made
Resolved before implementation:
| Question | Decision |
|---|---|
| Quest UI for bounties | **Custom mod with FTB Library UI widgets** — proper GUI screens, no chat commands |
| Quest UI for progression | FTB Quests |
| Plot purchase UI | Custom mod mixin into FTB Chunks map — hover preview, click-to-buy popup |
| Tier upgrade UI | Custom mod screen at Civic Office NPC |
| State storage | KubeJS-managed per-player JSON for quest state; mod-managed for plot ownership |
| Numismatics access | Direct Java via `Java.loadClass` from KubeJS; via API directly from the mod |
| Wilderness chunk allowance | Tier-driven via FTB Ranks permission nodes — no per-chunk pricing |
| Spawn plots | Predefined regions, slot-limited by tier, individually purchased with spurs |
| Tier ladder | Civic (13 tiers): Peasant → Farmer → Citizen → Merchant → Knight → Baron → Viscount → Earl → Marquess → Duke → Archduke → Grand Duke → Sovereign |
| Founder flag | Admin/owner has `[Founder]` cosmetic prefix in chat, grinds the regular ladder otherwise |
| Tier perks | JSON-configured (`config/bnstoolkit/tiers.json`), hot-reloadable |
| Custom mod | Confirmed. Built on FTB Library UI framework. Working name `bnstoolkit`. |
| Sethomes / warps | Tier-allocated count (not paid per use) |
| Daily quest count | Tier-driven (3 at Peasant, up to 6 at Sovereign), refresh per-player 24h after last accept |
| Emeralds | Retired as currency, retain as a trade material for villager-redirect trades |
| Fork FTB | Not needed — depend on FTB Library as a UI lib, FTB Chunks/Quests stay unmodified, integration via mixins + KubeJS hooks |
### Still to decide (non-blocking — pick during implementation)
- **PvP bounties** — out of scope for V1, revisit after V1 ships
- **Lootr** — add only when structured dungeons exist (Phase 8+)
- **Default-rank starting permissions** — finalised when FTB Ranks config is written
- **Spawn plot pricing** — first-pass numbers (2,000 standard / 5,000 premium), tune in playtest
---
## 12. Implementation notes
### KubeJS scripts (gameplay logic)
Organised under `pack/overrides/kubejs/server_scripts/economy/`:
- `01_numismatics_api.js` — Java.loadClass wrapper helpers (balance / deposit / deduct)
- `02_quest_state.js` — load/save `world/data/bns_economy/quests/<uuid>.json`
- `03_commands.js` — admin commands (`/tier upgrade`, `/bnstoolkit reload`, debug)
- `04_bounty_engine.js` — daily rotation, state machine, KubeJS↔mod packet handlers
- `05_sell_shop.js` — Numismatics shop tracking + diminishing returns
- `06_villager_trades.js` — Phase 5 `ServerEvents.villagerTrades` rebalance
- `07_tier_perks.js` — FTB Ranks promote/demote, perk application
- `99_dynamic_exchange.js` — Phase 7 supply-priced market
### Custom mod codebase
Repo: separate (or sub-tree of main repo, TBD).
Modid: `bnstoolkit` (working name).
Package layout:
```
bnstoolkit/
├── client/
│ ├── screen/ QuestLog, BountyBoard, Shop, Tier, PlotConfirm (FTB Library subclasses)
│ ├── mixin/ ChunkScreenPanel (cost overlay + plot button), ChunkButton (hover tooltip)
│ ├── hud/ QuestProgressHud (custom render)
│ └── ClientEvents keybind reg, screen reg, mixin loader
├── server/
│ ├── packet/ OpenScreen, UpgradeRank, BuyPlot, ClaimBounty, etc.
│ ├── command/ /bnstoolkit reload (config refresh)
│ └── data/ PlotRegistry, PlotOwnership (saved with world data)
├── common/
│ ├── data/ TierConfig (JSON-loaded), BountyDefinition, QuestState mirror
│ └── BnsToolkit.java mod entry
└── config-defaults/
├── tiers.json copied to config/bnstoolkit/tiers.json on first run
└── plots.json spawn plot region definitions
```
Architecture doc (`docs/bnstoolkit-architecture.md`) covers package layout, screen UX sketches, network packet contract, mixin targets, KubeJS↔mod integration surface — to be written before mod work begins.
### State data
- Quest state: `world/data/bns_economy/quests/<uuid>.json`. Readable JSON, easy to debug.
- Plot ownership: persisted with world via the mod's `SavedData` (vanilla NeoForge persistence). Authoritative source of truth.
- Tier config: `config/bnstoolkit/tiers.json` shipped from `pack/overrides/defaultconfigs/bnstoolkit/tiers.json`. Hot-reloadable.
- Plot definitions: `config/bnstoolkit/plots.json` shipped similarly. Defines plot region bounds, type (standard/premium), prices.
### NPC dialog
Authored in Easy NPC's in-game editor, exported as preset JSONs into `pack/overrides/world/datapacks/bns-fuel/data/easy_npc/`. Dialog `Command` actions call `/bnstoolkit <verb> ...` which routes to either the mod (for screen-opening) or KubeJS (for state changes).
### Villager rebalance
Single source file `06_villager_trades.js`. Each profession × tier has its own block. One file = one diff per balance pass.
---
## See also
- `MEMORY.md` entries:
- `project-mc-player-economy.md` — earlier higher-level notes on Numismatics + plot system
- `feedback-kubejs-rhino-gotchas.md` — KubeJS scripting constraints
- `feedback-bns-pack-version-sync.md` — pack version + sync workflow
- Existing tasks #20-#26 — to be reshuffled to mirror the phase plan above