Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 367056e2ba | |||
| 25107efcf1 |
@@ -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
|
||||
Reference in New Issue
Block a user