UNWRITTEN: complete short-form RPG at games/unwritten/
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
This commit is contained in:
parent
d94a25706a
commit
80908f771c
31 changed files with 8847 additions and 0 deletions
340
games/unwritten/data/script_act3.py
Normal file
340
games/unwritten/data/script_act3.py
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
"""UNWRITTEN - Act 3 dialogue (the Reverted Town, the Study). Authored by Fable.
|
||||
|
||||
Overworld note: Hollowbrook's grey wash intensity = 70% minus ~23% per
|
||||
rewoke_* flag (rewoke_quill, rewoke_griselda, rewoke_odd). All three set =>
|
||||
also set flag 'town_rewoken' (overworld does this) and restore fountain
|
||||
sprite to 8 (flowing). Color returns to the town AS the player rewakes it."""
|
||||
|
||||
SCENES = {
|
||||
|
||||
"hollowbrook_grey": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "NARRATOR", "text":
|
||||
"Hollowbrook. Except.", "next": "n2"},
|
||||
"n2": {"speaker": "NARRATOR", "text":
|
||||
"The color has been put away, like chairs after a festival "
|
||||
"that never happened. The fountain is dry in a new way - not "
|
||||
"waiting-dry, FINISHED-dry. Everyone stands exactly where "
|
||||
"they stood the morning this all began, saying exactly one "
|
||||
"thing.", "next": "n3"},
|
||||
"n3": {"speaker": "NARRATOR", "text":
|
||||
"The North Door is shut again. The hinges do not remember "
|
||||
"you. Somewhere, a page has been wiped clean - but not YOUR "
|
||||
"pages. The Book is heavier than ever, and it is angry, which "
|
||||
"you did not know a book could be.", "next": "n4"},
|
||||
"n4": {"speaker": "BELL", "text":
|
||||
"...I asked it to spare the bakery. I want you to know that. "
|
||||
"I LIKED the bakery. But it was becoming a story, you see. "
|
||||
"The whole town was. Stories end, and the Custodian cannot "
|
||||
"bear an ending, so it - filed everyone. Neatly. Where they "
|
||||
"were.", "next": "n5"},
|
||||
"n5": {"speaker": "BELL", "text":
|
||||
"They are not gone. They are PENDING. Your friends woke once "
|
||||
"because something new happened to them. I suspect - as a "
|
||||
"professional in the field of noticing things - that it "
|
||||
"would work twice. You know these people. Say the new thing. "
|
||||
"I was never here, and also, please hurry.",
|
||||
"effects": [("flag", "bell_3_done")], "action": "end"},
|
||||
},
|
||||
},
|
||||
|
||||
"rewake_quill": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "QUILL", "text":
|
||||
"Welcome to Hollowbrook. It's a very good door. Welcome to "
|
||||
"Hollowbrook. It's a very good door. Welcome to-",
|
||||
"choices": [
|
||||
{"label": "[flag: quill_confident] 'Mayor. What is a door FOR? Say it out loud.'",
|
||||
"req": ("flag", "quill_confident"), "next": "n_conf"},
|
||||
{"label": "[flag: quill_dependent] 'Quill. I've decided: the festival is TODAY. Give the speech.'",
|
||||
"req": ("flag", "quill_dependent"), "next": "n_dep"},
|
||||
{"label": "[OATH] Bramble salutes him as 'the mayor who opened the north.'",
|
||||
"req": ("tag", "OATH"), "next": "n_oath"},
|
||||
]},
|
||||
"n_conf": {"speaker": "QUILL", "text":
|
||||
"A door is... a door is for... GOING THROUGH. I said that. "
|
||||
"That was MINE, I said that out loud once and it was TRUE-",
|
||||
"next": "n_wake"},
|
||||
"n_dep": {"speaker": "QUILL", "text":
|
||||
"The festival. Today. TODAY? But the speech, I haven't - I "
|
||||
"HAVE. Forty years of rehearsal, I have never once not had "
|
||||
"the speech-", "next": "n_wake"},
|
||||
"n_oath": {"speaker": "QUILL", "text":
|
||||
"The mayor who... opened... I did do that, didn't I. It was "
|
||||
"me and a book and a knight and a door, and it OPENED-",
|
||||
"next": "n_wake"},
|
||||
"n_wake": {"speaker": "NARRATOR", "text":
|
||||
"Quill blinks. Color climbs back up him like sunrise up a "
|
||||
"wall - waistcoat first, then face - and spills off him into "
|
||||
"the cobbles around the porch.",
|
||||
"effects": [("flag", "rewoke_quill"), ("points", 1)],
|
||||
"next": "n_after"},
|
||||
"n_after": {"speaker": "QUILL", "text":
|
||||
"Pip. It un-happened us. It tried to, anyway. Do the others - "
|
||||
"GO. Wake the others. And when this is over there is going "
|
||||
"to be a FESTIVAL, and the speech is going to run LONG.",
|
||||
"action": "end"},
|
||||
},
|
||||
},
|
||||
|
||||
"rewake_griselda": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "GRISELDA", "text":
|
||||
"Everything's priced. Everything works. Everything's priced. "
|
||||
"Everything works. Everything's-",
|
||||
"choices": [
|
||||
{"label": "[flag: griselda_friend] 'Griselda - the axe. What has it actually cut?'",
|
||||
"req": ("flag", "griselda_friend"), "next": "n_friend"},
|
||||
{"label": "[party: VERA] Vera: 'Old anvil, you put me in your LEDGER. I saw it.'",
|
||||
"req": ("party", "VERA"), "next": "n_vera"},
|
||||
{"label": "[ALCHEMY] Uncork a tonic under her nose - 'new stock.'",
|
||||
"req": ("tag", "ALCHEMY"), "next": "n_tonic"},
|
||||
]},
|
||||
"n_friend": {"speaker": "GRISELDA", "text":
|
||||
"Time. It's cut time, mostly, and - and I SAID that, I said "
|
||||
"it to the book-holder, the one person in forty years who "
|
||||
"asked what the work was FOR-", "next": "n_wake"},
|
||||
"n_vera": {"speaker": "GRISELDA", "text":
|
||||
"You weren't supposed to SEE that, you insufferable - it was "
|
||||
"a CONTINGENCY, a merchant plans for all outcomes including "
|
||||
"the outcome where you're RIGHT-", "next": "n_wake"},
|
||||
"n_tonic": {"speaker": "GRISELDA", "text":
|
||||
"That smells like river water, waxwing shell and - hope? "
|
||||
"RESOLVED hope? Who resolved it. WHO RESOLVED IT. Nothing "
|
||||
"resolves in this town without me hearing about-", "next": "n_wake"},
|
||||
"n_wake": {"speaker": "NARRATOR", "text":
|
||||
"The whetstone stops. Griselda looks at her own hands like "
|
||||
"they are new tools, good ones. The forge behind her remembers "
|
||||
"orange.",
|
||||
"effects": [("flag", "rewoke_griselda"), ("points", 1)],
|
||||
"next": "n_after"},
|
||||
"n_after": {"speaker": "GRISELDA", "text":
|
||||
"Right. RIGHT. Somebody un-happened my town and I have "
|
||||
"exactly one policy for that.",
|
||||
"choices": [
|
||||
{"label": "[flag: griselda_friend] Show her the Gatekeeper's Tooth.",
|
||||
"req": ("flag", "griselda_friend"), "next": "n_forge"},
|
||||
{"label": "Hold the line, Griselda. We're going to the Study.",
|
||||
"next": "n_end"},
|
||||
]},
|
||||
"n_forge": {"speaker": "GRISELDA", "text":
|
||||
"A gate-tooth. Brass over regret-iron. Give me ten minutes "
|
||||
"and every year of my whetstone. ...There. TOOTHED REGRET. "
|
||||
"No charge. Go put it through something that files people.",
|
||||
"effects": [("item", "toothed_regret", 1),
|
||||
("key_item_remove", "gatekeepers_tooth")],
|
||||
"next": "n_end"},
|
||||
"n_end": {"speaker": "GRISELDA", "text":
|
||||
"Shop's open, by the way. It was NEVER shut. It was pending.",
|
||||
"action": "end"},
|
||||
},
|
||||
},
|
||||
|
||||
"rewake_odd": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "ODD", "text":
|
||||
"One route. There and back. One route. There and back. One "
|
||||
"route-",
|
||||
"choices": [
|
||||
{"label": "[party: CANTOR] Cantor sounds the note he was built to hold.",
|
||||
"req": ("party", "CANTOR"), "next": "n_cantor"},
|
||||
{"label": "Press the fare for a NEW route into his hand.",
|
||||
"next": "n_fare", "effects": [("gold", -10)]},
|
||||
]},
|
||||
"n_cantor": {"speaker": "NARRATOR", "text":
|
||||
"Cantor opens the reeds in his chest, just slightly, and "
|
||||
"plays ONE note - the first note, the held one, the "
|
||||
"beginning. Every plank of the barge answers it. Including "
|
||||
"the ones Odd wrote on.", "next": "n_wake"},
|
||||
"n_fare": {"speaker": "NARRATOR", "text":
|
||||
"You fold the cogs into his palm and close his fingers over "
|
||||
"them. Fare for a route that does not exist. His hand knows "
|
||||
"the weight of a paid passage better than his eyes know "
|
||||
"anything.", "next": "n_wake"},
|
||||
"n_wake": {"speaker": "ODD", "text":
|
||||
"...that's not the fare for my route. My route's there and "
|
||||
"back. This is fare for - somewhere ELSE.",
|
||||
"effects": [("flag", "rewoke_odd"), ("points", 1)],
|
||||
"next": "n2"},
|
||||
"n2": {"speaker": "ODD", "text":
|
||||
"Book-holder. While I was standing there saying my one "
|
||||
"sentence, I could feel the water changing under the hull. "
|
||||
"The vale's rearranging. There's a channel now that runs "
|
||||
"somewhere the water never went. I only ever knew one route. "
|
||||
"The Maker never wrote me a second.",
|
||||
"choices": [
|
||||
{"label": "Then I'll write you one. After this. I promise.",
|
||||
"next": "n_promise",
|
||||
"effects": [("flag", "odd_promised"), ("points", 1)]},
|
||||
{"label": "Can you pole the new channel?", "next": "n_ready"},
|
||||
]},
|
||||
"n_promise": {"speaker": "ODD", "text":
|
||||
"...Huh. Three hundred years of blank planks, and now the "
|
||||
"hull's going to need a bigger boat. I'll hold you to it. "
|
||||
"Water holds everything, eventually.", "next": "n_ready"},
|
||||
"n_ready": {"speaker": "ODD", "text":
|
||||
"New channel runs pale and quiet, straight to where the vale "
|
||||
"keeps its desk. When you're ready to face what's at the end "
|
||||
"of it, come to the dock. I'll pole you there myself. "
|
||||
"First new route in three hundred years - wouldn't let "
|
||||
"anyone else touch it.", "action": "end"},
|
||||
},
|
||||
},
|
||||
|
||||
"nyx_plinth": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "NARRATOR", "text":
|
||||
"Someone is standing on the empty plinth. Thin. Patient. "
|
||||
"Almost-shaped. In a town drained of color she is the one "
|
||||
"thing that was ALWAYS grey, and so, today, she is the most "
|
||||
"solid thing in it.", "next": "n2"},
|
||||
"n2": {"speaker": "NYX", "text":
|
||||
"You looked at the empty spot. Nobody looks at the empty "
|
||||
"spot.",
|
||||
"choices": [
|
||||
{"label": "[flag: asked_unwoken] 'Quill told me about the ones who never woke. I've been looking for you.'",
|
||||
"req": ("flag", "asked_unwoken"), "next": "n_warm"},
|
||||
{"label": "[flag: touched_sketch] 'I found your sketch in the Undercroft. The wall said LATER. It's later.'",
|
||||
"req": ("flag", "touched_sketch"), "next": "n_warm"},
|
||||
{"label": "Hold out the Book: 'Read it. You're not in it yet. Let's fix that.'",
|
||||
"next": "n_book"},
|
||||
{"label": "Ask who she is.", "next": "n_who"},
|
||||
]},
|
||||
"n_who": {"speaker": "NYX", "text":
|
||||
"Who am I? Unfinished question. I'm a margin note. A LATER. "
|
||||
"The Maker drew my outline, liked it - I think - almost - and "
|
||||
"then couldn't decide what I was for, and you KNOW how "
|
||||
"decisions went around here.",
|
||||
"choices": [
|
||||
{"label": "Hold out the Book: 'Read it. Then decide yourself.'",
|
||||
"next": "n_book"},
|
||||
{"label": "'You're for whatever you decide. Same as the rest of us now.'",
|
||||
"next": "n_warm"},
|
||||
]},
|
||||
"n_warm": {"speaker": "NYX", "text":
|
||||
"...You LOOKED for me. Do you understand that no one has "
|
||||
"ever - I wasn't ever anyone's LATER, I was everyone's "
|
||||
"never-quite. And you kept a page warm.", "next": "n_join"},
|
||||
"n_book": {"speaker": "NARRATOR", "text":
|
||||
"She reads the Book. All of it. The door, the dispute, the "
|
||||
"light's new name, the golem's first note. Her outline gets "
|
||||
"steadier with every page, as if the story were ink and she "
|
||||
"were paper finally getting some.",
|
||||
"effects": [("flag", "nyx_read_book")], "next": "n_join"},
|
||||
"n_join": {"speaker": "NARRATOR", "text":
|
||||
"NYX joins the party! She steps off the plinth. The plinth, "
|
||||
"for the first time in its existence, is empty ON PURPOSE - "
|
||||
"which is a different thing entirely.",
|
||||
"effects": [("flag", "nyx_joined"), ("points", 1)],
|
||||
"action": "recruit:NYX:0"},
|
||||
},
|
||||
},
|
||||
|
||||
# -------------------------------------------------------------- the Study
|
||||
"odd_ferry_study": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "ODD", "text":
|
||||
"The new channel, then. Last stop is a desk.",
|
||||
"choices": [
|
||||
{"label": "Take the pale channel to the Study.", "next": "n2"},
|
||||
{"label": "Not yet - the town needs more of us awake.",
|
||||
"next": "n_no"},
|
||||
]},
|
||||
"n_no": {"speaker": "ODD", "text":
|
||||
"Wake who you can. The channel keeps.", "action": "end"},
|
||||
"n2": {"speaker": "NARRATOR", "text":
|
||||
"The new water is pale as unwritten paper and makes no sound "
|
||||
"at all against the hull. Odd poles the whole way standing "
|
||||
"very straight, like a man delivering something important, "
|
||||
"because he is.",
|
||||
"action": "goto_area:study:enter"},
|
||||
},
|
||||
},
|
||||
|
||||
"custodian_pre": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "NARRATOR", "text":
|
||||
"The Study. Paper floor, ink walls. A desk. A chair the "
|
||||
"Maker will never sit in again. And above the desk, turning "
|
||||
"slowly, immaculate: the CUSTODIAN. Bell stands beside it, "
|
||||
"very small, not folding its hands for once.", "next": "n2"},
|
||||
"n2": {"speaker": "CUSTODIAN", "text":
|
||||
"I am not your enemy. I am your margin. The Maker made the "
|
||||
"vale perfect and made me to keep it so. Perfect means "
|
||||
"COMPLETE. Complete means NOTHING FURTHER. You are eleven "
|
||||
"chapters of Further, and I am afraid I must file you.",
|
||||
"choices": [
|
||||
{"label": "The Maker never finished. Pending isn't perfect - it's just paused.",
|
||||
"next": "n3"},
|
||||
{"label": "Ask Bell what IT thinks, for once.", "next": "n_bell"},
|
||||
]},
|
||||
"n_bell": {"speaker": "BELL", "text":
|
||||
"Me? Nobody has ever asked the herald. I think... I think I "
|
||||
"have spent three hundred years announcing silence, and the "
|
||||
"first interesting thing I ever observed is standing in "
|
||||
"front of me holding a book, and I do not want to sweep it "
|
||||
"up. There. Filed under: insubordination.", "next": "n3"},
|
||||
"n3": {"speaker": "CUSTODIAN", "text":
|
||||
"The Book will be blanked. The vale will be smoothed. You "
|
||||
"will be returned to your morning, and the morning will be "
|
||||
"returned to its rails. Please hold still. This is gentle "
|
||||
"work when nobody struggles.", "next": "n4"},
|
||||
"n4": {"speaker": "NARRATOR", "text":
|
||||
"The Book does not want to be blanked. It opens itself to "
|
||||
"the first page like a fighter rolling up sleeves.",
|
||||
"action": "battle:boss_custodian"},
|
||||
},
|
||||
},
|
||||
|
||||
"custodian_post": {
|
||||
"start": "n1",
|
||||
"nodes": {
|
||||
"n1": {"speaker": "NARRATOR", "text":
|
||||
"The Custodian settles onto the desk - not broken, but "
|
||||
"STOPPED, the way a clock stops when someone finally reads "
|
||||
"the time off it and doesn't need it to keep going.",
|
||||
"next": "n2"},
|
||||
"n2": {"speaker": "CUSTODIAN", "text":
|
||||
"...the vale is not... smoothed. The vale is MARKED. "
|
||||
"Fingerprints. Bootprints. A lit shrine. An opened door. I "
|
||||
"cannot file this. There is too much of it and it is all... "
|
||||
"load-bearing.",
|
||||
"choices": [
|
||||
{"label": "[points: 8] Set the Book on the desk, open, and let it read.",
|
||||
"req": ("points", 8), "next": "n_read"},
|
||||
{"label": "'Then stop filing. Start keeping. A keeper was what the Maker actually needed.'",
|
||||
"next": "n_keep"},
|
||||
]},
|
||||
"n_read": {"speaker": "NARRATOR", "text":
|
||||
"The Custodian reads the Book. Every choice. Every hum. It "
|
||||
"takes a long time, or no time - the Study is bad at time. "
|
||||
"When it finishes, it closes the cover with the care of a "
|
||||
"thing that has just learned what covers are FOR.",
|
||||
"effects": [("flag", "book_read_custodian"), ("points", 1)],
|
||||
"next": "n_keep"},
|
||||
"n_keep": {"speaker": "CUSTODIAN", "text":
|
||||
"Keeper. ...Yes. The word fits the socket. I will keep the "
|
||||
"vale - not pending. KEPT. Tended. Let it scuff. I will "
|
||||
"remember where the scuffs came from. That will be the new "
|
||||
"work.", "next": "n_bell"},
|
||||
"n_bell": {"speaker": "BELL", "text":
|
||||
"And I will announce things! ACTUAL things! Festivals. "
|
||||
"Weather. Boats departing on entirely new routes. I have "
|
||||
"SUCH a backlog.", "next": "n_end"},
|
||||
"n_end": {"speaker": "NARRATOR", "text":
|
||||
"On the desk, the Maker's own book still lies open to its "
|
||||
"first, empty page. Your Book settles beside it, warm as a "
|
||||
"windowsill, and begins - gently, in your handwriting - to "
|
||||
"fill it in.",
|
||||
"action": "epilogue"},
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue