Feature Narrative Lectern - bh679/dungeon-train-mc GitHub Wiki

Narrative lectern + book progression

Since: v0.177.0 (PR #223) ยท latest: v0.429.0 (PR #707)

The first custom block in the codebase. A vanilla-lectern look-alike that loads ~50 KB of long-form story content from the original itch.io game into Minecraft as signed-book + lectern primitives, with world-scoped progression that persists across reloads and is locked to the lectern on first read. The narrative lectern is also the project's first light-emitting block, glowing at light level 12 (since v0.180.0) so it's visible from a distance in dark carriages and reads as "interactive" at a glance.

How it works

  • Loader + registry. Stories parse from JSON under data/dungeontrain/narratives/stories/, loaded via vanilla's ResourceManager datapack channel (as of PR #735 โ€” see Narrative loader). Each story is a numbered list of letters; a translation datapack can override any story file to localize its text.
  • World-scoped progression (v0.184.0). NarrativeProgressData (an overworld SavedData) tracks read letter indices per storyBasename โ€” every player in the world shares a single cursor. Each new world starts fresh. Read events fire on lectern right-click and inventory book right-click; books carry their (story, letter) identity in DataComponents.CUSTOM_DATA so the system can mark the right letter as read. Idempotent set semantics โ€” repeat reads no-op.
  • Lock-on-first-read (v0.184.0). The narrative_lectern block always renders with a book on top (HAS_BOOK=true forced at placement). On the first right-click, BookFactory.buildOrRandomForLectern resolves the next-unread letter for the world, places the book on the vanilla LecternBlockEntity at that position, marks it read, and the lectern locks to that letter. Subsequent right-clicks see the BE's non-empty book and skip resolution โ€” the locked content opens unchanged. The lock dies with the lectern: onRemove clears the BE so block break doesn't drop the transient book. Vanilla BE serialization handles save/load persistence automatically. When every story is complete, the first-click no-ops with an action-bar "All narratives complete." message.
  • Block-entity reuse. No custom BlockEntityType โ€” the narrative lectern piggybacks on vanilla LecternBlockEntity via NeoForge's BlockEntityTypeAddBlocksEvent, registered in NarrativeLecternHooks. No custom renderer is needed; the BE's book is what gets locked.
  • Light emission (v0.180.0). BlockBehaviour.Properties.ofFullCopy(Blocks.LECTERN).lightLevel(s -> 12) โ€” between glowstone (15) and a soul torch (10). Lights up surrounding blocks in dark carriages, makes the lectern visually stand out from vanilla furniture.

Burn outside the lectern (v0.429.0)

Since PR #707, reading in place at a lectern is the only way to keep a narrative book. A copy taken off the lectern (into a hand slot) now joins the mod's existing burnable-book framework โ€” the same one that already covers Starting Books โ€” via BurnableBookTag.isBurnable:

  • Drop โ€” Q-throw, death-drop, hopper-eject, or even a broken narrative lectern popping its locked book: any ItemEntity carrying a narrative-tagged stack ignites and burns away over ~3 s (see Starting Books ยง Burn lifecycle for the shared flame/smoke/fire-visual mechanics โ€” identical here, no new visual code).
  • Read by hand, then close โ€” right-clicking a narrative book held in hand opens a BookViewScreen; closing it drops and burns the book exactly like the drop path.
  • Read at a lectern โ€” never burns. The book resolved into a narrative_lectern's LecternBlockEntity is never an ItemEntity, so it's outside the drop-burn hook entirely. StartingBookClientEvents (the client-side close detector) explicitly excludes LecternScreen โ€” a BookViewScreen subclass โ€” from the held-book burn trigger, so opening/closing the lectern's own reading screen can never be mistaken for a hand-read.

This makes the lectern the one guaranteed-safe place to keep a story: take a book with you and it becomes disposable, exactly like every other burnable book type in the mod.

Where to find one in-game

  • Carriages. A narrative content cell ships in the black, cracked, pen, and windowed template pools (weight 3). Sub-variants dungeonnarrative and seatingnarrative decorate the surrounding interior to match.
  • Manual placement. Creative tab โ†’ Functional Blocks โ†’ Narrative Lectern. Same hitbox and rotation as vanilla lectern.

Commands (/dungeontrain narrative โ€ฆ)

Command What it does
list Print the loaded stories to chat.
book [story] [letter] Give the player a narrative book. Defaults to the next-unread letter for the world.
lectern [story] [letter] Place a vanilla lectern with a mounted book.
spawnlectern Place a narrative_lectern block at the player's targeted position.
progress Chat-print the world's read counts and next pointer.
reset Wipe the world's narrative progress. (Locked lecterns keep their books โ€” the lock is at BE-level and independent of the progress store.)
reload Re-scan the bundled narrative JSON.

Authoring new narratives

Stories live in src/main/resources/data/dungeontrain/narratives/stories/<basename>.json as ordered JSON arrays of letter strings. A Python conversion tool at scripts/narrative/txt_to_json.py re-converts from the original itch.io plaintext under originals/ if narratives need to be edited at the source level.

Compatibility notes

  • Sable. The narrative lectern is a vanilla-lectern look-alike from Sable's perspective โ€” same collision shape, same block-entity type. Glow follows the lectern when it rides on a Sable ship. The lectern's locked book is stored in the standard LecternBlockEntity and rides cleanly with the ship. The lighting engine updates ship-attached block lighting frame-by-frame; some pipeline-dependent visual artifacts may appear on adjacent ship blocks while the ship is moving, but that's inherent to Sable's render path, not a regression.
  • Multiplayer (v0.184.0). Progression is world-scoped โ€” every player on a shared server reads from the same cursor. The first player to right-click an unlocked lectern advances the world's read state and locks that lectern; subsequent visitors see the same book on that lectern. Locked lecterns are stable across all players. Light emission is server-side and replicated to clients via vanilla chunk packets; no special multiplayer handling needed.
  • Legacy worlds (v0.184.0). Worlds saved before v0.184.0 used per-player progression in dungeontrain_narratives.dat. On first load with the new code, that legacy players data is silently discarded and the world starts fresh per the design. Existing narrative lecterns whose BE happens to hold a stale book treat that book as their lock โ€” open them once to see what was last placed, and the world cursor advances normally from there.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ