From 95b0d1564d4321e7619d96b16146abaf19c0d65b Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 6 Jun 2026 21:09:45 +0000 Subject: [PATCH] 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 --- docs/economy-system.md | 177 +++++++++++++++++++++++++++++------------ 1 file changed, 126 insertions(+), 51 deletions(-) diff --git a/docs/economy-system.md b/docs/economy-system.md index 1a62898..acf832c 100644 --- a/docs/economy-system.md +++ b/docs/economy-system.md @@ -1,6 +1,6 @@ # Brass and Sigil — Economy & Quest System Design -> Status: design draft (v0.1). Not yet implemented. +> Status: architecture locked (v0.2). Implementation ready to begin. > Owner: matt > Last updated: 2026-06-06 @@ -120,27 +120,31 @@ The same accept → complete → return pattern works for every guild: | **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 tracking & cancellation +### Player-facing tracking & cancellation (locked) -A quest engine that players can't inspect is worthless. The design ships with three layers of visibility: +Three surfaces, **all shipping together in Phase 3.** Not iterated, not optional. -#### V1 — command-driven (Phase 3, with the engine) +| Surface | Role | Mechanism | +|---|---|---| +| **Bossbars** | Always-visible HUD — one bossbar per active quest, shows name + N/M progress bar | Vanilla `/bossbar` commands driven by KubeJS. Native per-player display (scoreboards are global; bossbars aren't). Up to 3 stacked at top of screen. | +| **`/quests` commands** | Management — list, inspect, cancel | KubeJS `ServerEvents.commandRegistry`. Works in chat and over the Telegram bridge. Clickable chat components for `[cancel]` and `[details]`. | +| **Easy NPC dialog** | Accept + turn-in at guild NPCs | Dialog action runs `/bns_quest accept ` (KubeJS-registered command). Dialog branching uses scoreboard objectives that KubeJS mirrors quest state into. | | Command | Effect | |---|---| -| `/quests` | Lists all of your ACTIVE and READY quests with progress, source guild, and reward | -| `/quests info ` | Detail view for one quest — description, objective, progress, reward | -| `/quests cancel ` | Drops an active quest, freeing your slot. The quest goes on cooldown as if completed (prevents accept-cancel-accept RNG farming) | +| `/quests` | Lists your ACTIVE / READY quests with progress, source guild, reward | +| `/quests info ` | Detail view | +| `/quests cancel ` | Drops an active quest, frees the slot. Applies the same cooldown as if completed (prevents accept-cancel-accept RNG-shopping). | -Cheap to ship. Works on mobile via the Telegram bridge. Doesn't require any extra mods. +### Why NOT FTB Quests for bounties -#### V2 — Quest Log book item (Phase 3.5) +FTB Quests is the right tool for **progression** (Phase 5+), not for daily bounties. Confirmed via source review: -Every new player joins with a **Quest Log** book in their inventory (KubeJS `PlayerEvents.loggedIn` grants it once). Right-click to open. Dynamic Patchouli-style pages list active/ready quests, with a "Cancel" button per quest. Same backend as the `/quests` commands — just a nicer surface. +- **No abandon/cancel event** — players can `Ctrl+Shift+R` to reset quest progress, but nothing fires that KubeJS can hook → can't enforce cancellation cooldown. +- **Cooldown is per-team, not per-player** — in a 4-player team, one member completing puts it on cooldown for all four. +- **No per-player quest visibility** — gating "5 of 20 active today" requires `CustomTask` gates rather than first-class per-player visibility. -#### V3 — Sidebar HUD (later) - -Optional scoreboard sidebar showing 2-3 most recent quests + progress, always-on. Implemented via vanilla scoreboard objectives driven by KubeJS. Players can toggle on/off via `/quests sidebar`. +For long-form progression questing, all three are non-issues. So: FTB Quests for the campaign, custom KubeJS system for daily bounties. ### Quest slot limit @@ -152,9 +156,9 @@ Players have a **maximum of 3 active quests at a time.** This is the friction th A quest moving to READY frees the slot for accepting a new one even before turn-in. -### KubeJS implementation sketch +### KubeJS implementation — locked specifics -- **State storage:** per-player JSON in `world/data/bns_quests/.json`, written via KubeJS persistent-data hooks. Schema: +- **State storage:** per-player JSON in `world/data/bns_quests/.json`. Schema: ```json { "active": [ @@ -164,7 +168,8 @@ A quest moving to READY frees the slot for accepting a new one even before turn- "progress": 7, "target": 10, "rewardSpurs": 50, - "source": "adventurers_guild" + "source": "adventurers_guild", + "bossbarId": "bns:quest_advg_plunderer_10_" } ], "completed": { @@ -172,10 +177,41 @@ A quest moving to READY frees the slot for accepting a new one even before turn- } } ``` -- **Event tracking:** standard KubeJS event handlers (`EntityEvents.death`, `ItemEvents.pickedUp`, `BlockEvents.broken`) read the player's active quests and increment. -- **Dialog branching:** Easy NPC dialog with conditions that call KubeJS to check quest state and route the dialog appropriately. -- **Reward delivery:** call Numismatics' wallet API to deposit spurs. -- **Commands:** registered via `ServerEvents.commandRegistry` in KubeJS, scoped to the player who runs them. + +- **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 ')`, 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.` that KubeJS mirrors quest state into. Dialog `Command` action triggers `/bns_quest accept ` 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')`. --- @@ -243,18 +279,34 @@ A quest moving to READY frees the slot for accepting a new one even before turn- --- -## 8. Mod stack (we have what we need) +## 8. Mod stack (locked) -| Need | Mod | Status | +### Already in pack +| Need | Mod | Version | |---|---|---| -| Currency + wallets + shop blocks | Numismatics | ✅ In pack | -| NPC quest-givers + dialog | Easy NPC | ✅ In pack | -| Quest engine (progression) | FTB Quests | ⏳ Planned, not added | -| Plot claims | FTB Chunks | ✅ In pack | -| Tier system | FTB Ranks | ❓ Not in pack — add when we get to tiers | -| Glue & quest state | KubeJS | ✅ In pack | +| 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 | -**Not adding:** CustomNPCs (Easy NPC covers it), Citizens (Bukkit-only), PMMO (out of scope), generic "quest giver" mods (FTB Quests + KubeJS do it). +### 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 (Phase 6). CurseForge only, no Modrinth listing. | + +### Custom mod +**Not needed for V1.** Re-evaluate only if implementation hits a hard wall on a specific feature. + +### 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) --- @@ -262,18 +314,18 @@ A quest moving to READY frees the slot for accepting a new one even before turn- Each phase is independently shippable. Earlier phases unlock value without depending on later ones. -| Phase | Deliverable | Why this order | +| Phase | Deliverable | Spawn build required? | |---|---|---| -| **0** | Spawn skeleton built — 7 guild buildings placed (rough). | Locations have to exist before NPCs can stand in them. | -| **1** | **Merchant's Bazaar V1:** Numismatics sell shop (fixed prices, no DR yet) for diamonds & netherite. | Cheapest to ship, immediate economy unlock. | -| **2** | **Civic Office V1:** Plot claim cost via KubeJS `/ftbchunks claim` override. | Real money sink. Tests FTB Chunks integration depth. | -| **3** | **Adventurer's Guild V1:** Bounty board engine. 5 daily quests, accept→complete→return flow, `/quests` commands, 3-slot cap. | The biggest gameplay feature. Validates the universal quest pattern. | -| **3.5** | Quest Log book item (V2 of tracking surface). | Nicer UX surface on top of the same engine — non-blocking. | -| **4** | **Villager rebalance:** strip vanilla, ship the spur-priced trade table. | Closes the exploit. Big JSON pass. | -| **5** | **Library / Academy:** FTB Quests anchor + first chapter of progression quests. | Long-form content begins. | -| **6** | **Civic Office V2:** FTB Ranks integration + tier upgrades. | Endgame money sink. | -| **7** | **Dynamic exchange rates** (Bazaar V2). | Most engineering, biggest anti-farm payoff, ship last. | -| **8+** | Foundry, Tavern, deeper Library quests, dungeons-with-Lootr. | Beyond V1. | +| **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 for everything else. | Yes (user) | +| **1** | **Sell shop:** Numismatics shop blocks for diamonds (10 spurs) + netherite ingots (100 spurs). Lifetime-sale tracker for diminishing returns. | No — test anywhere, drop into Bazaar later | +| **2** | **Plot claim cost:** `FTBChunksEvents.before('claim')` deducts spurs. Free first 9 chunks, 50/chunk to 25, 200/chunk beyond. | No | +| **3** | **Bounty engine:** state machine + `/quests` commands + bossbar HUD + 3-slot cap. Daily quest pool with KubeJS-rotated dailies. NPC dialog hooks ready but inert until guild NPCs exist at spawn. | No (NPC layer activates once spawn is built) | +| **4** | **Villager rebalance:** strip vanilla, ship spur-priced trade table via `ServerEvents.villagerTrades`. | No | +| **5** | **Progression quest line:** FTB Quests anchor + first chapter. KubeJS rewards dispense spurs via Numismatics API. | No (anchor NPC at Library once spawn exists) | +| **6** | **Tier upgrades:** FTB Ranks integrated. `/tier upgrade` deducts spurs and promotes via `ftbranks add`. Rank perms gate FTB Chunks claim limits. | No | +| **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 | --- @@ -290,24 +342,47 @@ Each phase is independently shippable. Earlier phases unlock value without depen --- -## 11. Open decisions +## 11. Decisions made -These need an actual call before implementation: +Resolved before implementation: -1. **Keep emeralds as a secondary collectible?** Or fully retire them now that villagers don't use them? -2. **Sethomes / warps as paid services?** Or free baseline? -3. **PvP bounties** (players posting bounties on other players)? Probably no for V1, revisit later. -4. **Lootr** — add for shared dungeon loot, or skip until structured dungeons exist? -5. **Daily quest count** — start with 5? 8? Refresh window — true daily (00:00 UTC) or per-player 24h? +| Question | Decision | +|---|---| +| Quest UI for bounties | Custom (bossbars + commands + Easy NPC) — NOT FTB Quests | +| Quest UI for progression | FTB Quests | +| State storage | Per-player JSON in `world/data/bns_economy/` | +| Numismatics access | Direct Java via `Java.loadClass` | +| Chunk claim refusal | `FTBChunksEvents.before('claim')` with `event.setResult(...)` | +| Tier system | FTB Ranks + permission-driven chunk limits | +| Custom mod for V1 | No — only if a hard wall appears | +| Sethomes / warps | Paid services (`/sethome` 50 spurs, `/warp` 25 spurs, `/back` 10 spurs) | +| Daily quest count | 5 active at a time, refresh per-player 24h after last accept | +| Emeralds | Retired as currency, retain as a trade material for villager-redirect trades | + +### 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** — to be set with the first FTB Ranks config pass --- ## 12. Implementation notes -- Document each phase's KubeJS scripts under `pack/overrides/kubejs/server_scripts/economy/.js` -- State data under `world/data/bns_economy/` — readable JSON, easy to debug -- All NPC dialog trees authored in Easy NPC's editor, exported as preset JSONs into `pack/overrides/world/datapacks/bns-fuel/data/easy_npc/` -- Trade table for villager rebalance lives in a single `villager_trades.js` so one file is the source of truth +- KubeJS scripts organized under `pack/overrides/kubejs/server_scripts/economy/`: + - `01_numismatics_api.js` — Java.loadClass wrapper helpers (read/deposit/deduct) + - `02_quest_state.js` — load/save `world/data/bns_economy/.json` + - `03_bossbar_hud.js` — per-player bossbar lifecycle + - `04_commands.js` — `/quests`, `/bns_quest`, `/tier` registrations + - `05_bounty_engine.js` — Phase 3 quest engine + - `06_sell_shop.js` — Phase 1 shop block tracking + DR + - `07_plot_costs.js` — Phase 2 FTB Chunks event handler + - `08_villager_trades.js` — Phase 4 rebalance + - `09_tier_upgrades.js` — Phase 6 rank upgrades + - `99_dynamic_exchange.js` — Phase 7 +- State data under `world/data/bns_economy/.json` per player. Readable, easy to debug. Migration to SQLite considered if dataset grows beyond ~100 players. +- All NPC dialog trees 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 into `/bns_quest` namespace which routes to KubeJS handlers. +- Trade table for villager rebalance: single source file `08_villager_trades.js`. Each profession × tier has its own block. One file = one diff to review during balance passes. --- -- 2.52.0