Feature Narrative Lectern - bh679/dungeon-train-mc GitHub Wiki
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.
-
Loader + registry. Stories parse from JSON under
data/dungeontrain/narratives/stories/, loaded via vanilla'sResourceManagerdatapack 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 overworldSavedData) tracks read letter indices perstoryBasenameโ 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 inDataComponents.CUSTOM_DATAso 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_lecternblock always renders with a book on top (HAS_BOOK=trueforced at placement). On the first right-click,BookFactory.buildOrRandomForLecternresolves the next-unread letter for the world, places the book on the vanillaLecternBlockEntityat 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:onRemoveclears 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 vanillaLecternBlockEntityvia NeoForge'sBlockEntityTypeAddBlocksEvent, registered inNarrativeLecternHooks. 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.
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
ItemEntitycarrying 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'sLecternBlockEntityis never anItemEntity, so it's outside the drop-burn hook entirely.StartingBookClientEvents(the client-side close detector) explicitly excludesLecternScreenโ aBookViewScreensubclass โ 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.
-
Carriages. A
narrativecontent cell ships in theblack,cracked,pen, andwindowedtemplate pools (weight 3). Sub-variantsdungeonnarrativeandseatingnarrativedecorate the surrounding interior to match. - Manual placement. Creative tab โ Functional Blocks โ Narrative Lectern. Same hitbox and rotation as vanilla lectern.
| 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. |
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.
-
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
LecternBlockEntityand 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 legacyplayersdata 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.