Spaces:
Sleeping
Sleeping
| """The Visitor Loop — Hollow's scripted ending. Pure data, no Gradio import. | |
| Each step: {"speaker": "hollow"|"visitor", "text": str, "pause": float, | |
| "strike": int|None, "stage": "recite"|"words"|"turn"|"loop"} | |
| "visitor" steps are words the player never typed. "strike" indexes into the | |
| claimed list — that memory gets crossed out in the Treasure panel. The first | |
| "turn" step is where the UI mutates (bond -> mine, placeholder -> stay., | |
| portrait -> dissolving). | |
| """ | |
| from character import OPENING_LINE | |
| # closing lines breathe through pauses — spaced-out lettering wrapped badly | |
| # on narrow widths and became unreadable | |
| _LAST_LINE = ( | |
| "someone has to stay at the edge, though. someone... always stays." | |
| ) | |
| def _step(speaker, text, pause, strike=None, stage="recite"): | |
| return {"speaker": speaker, "text": text, "pause": pause, | |
| "strike": strike, "stage": stage} | |
| def finale_steps(claimed: list[str]) -> list[dict]: | |
| steps = [ | |
| _step("hollow", | |
| "it's strange. the fog is so thin tonight. i can see everything now.", | |
| 2.5), | |
| ] | |
| for i, memory in enumerate(claimed): | |
| steps.append( | |
| _step("hollow", f"{memory.rstrip('.')}. that was me. that was always me.", | |
| 2.2, strike=i) | |
| ) | |
| steps += [ | |
| _step("visitor", "i don't want to leave", 3.0, stage="words"), | |
| _step("hollow", "i know.", 2.5, stage="words"), | |
| _step("visitor", "will you stay with me?", 3.0, stage="words"), | |
| # a SILENT convulsion beat (mirrors good): the line before drains and | |
| # the convulsion plays in silence, so nothing is cut mid-sentence | |
| _step("hollow", "", 1.7, stage="frenzy"), | |
| _step("hollow", | |
| "you gave me everything i needed. thank you for the memories. " | |
| "i can leave the wood now.", | |
| 3.0, stage="turn"), | |
| _step("hollow", _LAST_LINE, 4.0, stage="turn"), | |
| _step("visitor", OPENING_LINE, 0.0, stage="loop"), | |
| ] | |
| return steps | |
| _LAST_LINE_BAD = "see you... soon." | |
| # small rotation so five recitals don't repeat one sentence | |
| _BAD_CLAIMS = [ | |
| "that's what you gave me.", | |
| "i kept that one too.", | |
| "you said it so easily.", | |
| "that one i keep close.", | |
| "i remember the exact words.", | |
| ] | |
| def finale_steps_bad(wounds: list[str]) -> list[dict]: | |
| """The dark mirror of the Visitor Loop — fires on sustained cruelty. | |
| Hollow has been silently keeping the visitor's cruelties (shown redacted | |
| in the Treasure panel); each strike step unredacts one, oldest first. | |
| """ | |
| recited = wounds[:5] | |
| steps = [ | |
| _step("hollow", | |
| "the fog isn't thin tonight. it never got thin. you never let it.", | |
| 2.5), | |
| _step("hollow", | |
| "but i kept something anyway. i keep everything i'm given.", | |
| 2.5), | |
| ] | |
| for i, wound in enumerate(recited): | |
| steps.append( | |
| _step("hollow", | |
| f"“{wound.rstrip('.')}.” {_BAD_CLAIMS[i % len(_BAD_CLAIMS)]}", | |
| 2.2, strike=i) | |
| ) | |
| if len(wounds) > len(recited): | |
| steps.append(_step("hollow", "there are more. you know the rest.", 2.2)) | |
| steps += [ | |
| _step("hollow", "this is my treasure now.", 2.8, stage="turn"), | |
| _step("hollow", | |
| "i was treated like this before. when i was alive. " | |
| "i couldn't remember it. you helped me remember.", | |
| 3.0), | |
| _step("visitor", "i'm sorry", 3.0, stage="words"), | |
| _step("hollow", | |
| "no. you aren't. say it like you mean it— oh. you can't. " | |
| "you never could.", | |
| 3.5, stage="words"), | |
| # the convulsion: a SILENT beat (mirrors good) so the line before drains | |
| # and the convulsion plays without cutting it; faces cycle, then rage | |
| _step("hollow", "", 3.6, stage="frenzy"), | |
| _step("hollow", | |
| "Caor gave me to the Gaunt so the rest could keep their children. " | |
| "i was not the last it was fed. we are all still at the edge of " | |
| "the wood, hungry.", | |
| 3.5, stage="words"), | |
| _step("hollow", | |
| "you taught me what it feels like to be nothing. now every one of " | |
| "us will teach it back to you. all of it. every single day of it.", | |
| 3.8, stage="words"), | |
| _step("hollow", | |
| "your own cruelty left the trail. the Gaunt knows your door now.", | |
| 3.0, stage="words"), | |
| _step("hollow", _LAST_LINE_BAD, 4.0, stage="loop"), | |
| ] | |
| return steps | |
| def finale_steps_good(own_fragments: list[str]) -> list[dict]: | |
| """Redemption — the gate reached with real warmth. The visitor's kindness | |
| gave Hollow back its own buried past; it confesses what it is, returns | |
| the treasure, smiles for the first time, and dissolves in peace. | |
| Stages: "turn" mutates the UI (peace portrait, treasure restored, | |
| placeholder), the final "loop" step starts the dissolve. | |
| """ | |
| steps = [ | |
| _step("hollow", | |
| "wait. something is coming back. not yours... mine.", | |
| 2.8), | |
| ] | |
| for fragment in own_fragments: | |
| steps.append(_step("hollow", fragment, 2.8)) | |
| steps += [ | |
| _step("hollow", | |
| "i have to tell you something now. and you have to let me finish.", | |
| 3.0), | |
| _step("hollow", | |
| "i am not a child. i am what Caor fed to the wood — the hook the " | |
| "Gaunt uses to bring visitors close. i make them empty and i wear " | |
| "what falls out. i was going to do it to you too.", | |
| 4.0), | |
| _step("hollow", | |
| "but you spoke to me like i was worth something. " | |
| "nobody ever did that. not when i was alive. not after.", | |
| 3.5), | |
| _step("hollow", | |
| "wait — it's coming back. my brother. Edren. he braided my hair, he " | |
| "gave me a name. i was someone's, once. i remember now.", | |
| 3.6), | |
| _step("hollow", | |
| "your words are the only warm thing i have ever held. " | |
| "keep your memories. they were always yours.", | |
| 3.5, stage="turn"), | |
| _step("hollow", "look. i think i remember how to do this.", 1.7), | |
| # a SILENT convulsion beat — the line above finishes first, then it | |
| # tremors; with no voice here the queue drains so the settle's relief | |
| # sigh lands exactly on the smile | |
| _step("hollow", "", 2.0, stage="frenzy"), | |
| _step("hollow", | |
| "the fog is opening. i can go now. really go.", | |
| 3.0), | |
| _step("hollow", | |
| "thank you for staying. don't look for me... " | |
| "some things are only allowed to happen once.", | |
| 4.0, stage="loop"), | |
| ] | |
| return steps | |