diff --git a/docs/bnstoolkit-architecture.md b/docs/bnstoolkit-architecture.md new file mode 100644 index 0000000..25df0f1 --- /dev/null +++ b/docs/bnstoolkit-architecture.md @@ -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 ` 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 ` which + charges spurs and runs `ftbranks add ` + +### 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: " 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` | On Quest Log open, on any quest-state change | +| `S2CBountyBoardSnapshot` | `List` (5-10 entries) | On Bounty Board open | +| `S2CTierSnapshot` | `(currentTier, balance, perksByTier)` | On Tier Upgrade Screen open | +| `S2CShopBrowserSnapshot` | `List` | 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 ` (KubeJS) | +| `C2SCancelQuest` | `questId` | Call `/bnstoolkit_internal cancel_quest ` (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/.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 ` | Mark quest as ACTIVE in KubeJS state | +| `/bnstoolkit_internal cancel_quest ` | Cancel quest, apply cooldown | +| `/bnstoolkit_internal complete_quest ` | Mark quest READY (for testing/manual) | +| `/bnstoolkit_internal turn_in_quest ` | Pay reward via Numismatics | +| `/bnstoolkit_internal debit ` | Charge spurs (used by plot purchase, tier upgrade) | +| `/bnstoolkit_internal credit ` | Pay spurs | +| `/bnstoolkit_internal upgrade_rank ` | Promote via FTB Ranks | +| `/bnstoolkit_internal get_balance ` | 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 ` locations a player can +save. `/home ` teleports there; `/listhomes` lists; `/delhome ` +removes. + +**Implementation:** the pack currently has no sethome mod. We build +it in bnstoolkit: +- `/sethome ` saves current player position + world + name +- `/home ` teleports (validates dimension load) +- `/listhomes` shows owned homes +- `/delhome ` 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 ` 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