A 30-45 minute turn-based RPG built overnight as an engine showcase: six recruitable characters (party of three), four recurring NPCs, choice-driven dialogue with visible-but-locked skill checks, shops, loot, levels, two bosses with in-battle Talk options, the reverted grey-town beat, and an epilogue assembled from the player's actual choices. 33 dialogue scenes / 171 nodes. Terrain is ColorLayers only; kenney_tinydungeon supplies characters, items, and props. Creative direction, story, dialogue, maps, and systems design by Fable; implementation by four Opus subagents (framework, battle, overworld, integration). Verified by tests/playthrough.py: a scripted headless run of all three acts, 20/20 beats passing, plus per-system unit tests (script integrity, UI kit, 225-battle balance sim, overworld). Run: cd build && ./mcrogueface --exec ../games/unwritten/main.py Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014vxWJ6GY384SmZiMVXgrF4
38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
"""UNWRITTEN - shared assets. Owner: Agent A.
|
|
|
|
The ONE Texture for the whole game and the speaker->portrait sprite map.
|
|
Constructed once here; import TEX everywhere else (never re-create the Texture).
|
|
Asset path is relative to the build/ working directory (see ARCHITECTURE section 0).
|
|
"""
|
|
import mcrfpy
|
|
|
|
TEX = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
|
|
|
# Speaker id -> sprite index (BIBLE sections 2/3/4). NARRATOR has no portrait.
|
|
PORTRAITS = {
|
|
"PIP": 85,
|
|
"BRAMBLE": 96,
|
|
"MOTH": 84,
|
|
"VERA": 111,
|
|
"CANTOR": 109,
|
|
"NYX": 121,
|
|
"QUILL": 100,
|
|
"GRISELDA": 88,
|
|
"ODD": 87,
|
|
"BELL": 110,
|
|
"GATEKEEPER": 20,
|
|
"CUSTODIAN": 41,
|
|
"NARRATOR": None,
|
|
}
|
|
|
|
|
|
def portrait_index(speaker):
|
|
"""Return the sprite index for a speaker id, or None for NARRATOR.
|
|
|
|
Fail early: an unknown speaker id is a content/authoring error, not a
|
|
thing to paper over with a placeholder sprite.
|
|
"""
|
|
if speaker not in PORTRAITS:
|
|
raise KeyError(
|
|
"unknown speaker id %r - add it to core.assets.PORTRAITS" % (speaker,))
|
|
return PORTRAITS[speaker]
|