From 15e12ce0488e1b23b7ddf817d89f0d95eb50e5f4 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 22 Aug 2026 18:50:39 +0200 Subject: [PATCH 01/10] =?UTF-8?q?Seed=20The=20Barricelli=20Files=20mystery?= =?UTF-8?q?=20(Scenes=205=E2=80=9310,=20narrative-only)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A narrative-only manifest wrapping the existing barricelli-inventor-proof level template: Dobby intro/tasks (branching, awards dobby.knows_barricelli*), a phone node bound to Glitch Hunter, the gated Glitch-Hunter interrogation (options require the Dobby flags, converge via a terminal), Scene 7 level, Scene 8 debrief, the barricelli_luggage merit ceremony, and the Scene 10 deep-web branch. Phone dialing still stubbed — the phone node is authored structure off the walkable path (Dobby → Hunter is direct for now). Co-Authored-By: Claude Opus 4.8 --- mysteries/barricelli-files/mystery.json | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 mysteries/barricelli-files/mystery.json diff --git a/mysteries/barricelli-files/mystery.json b/mysteries/barricelli-files/mystery.json new file mode 100644 index 0000000..b6e31a1 --- /dev/null +++ b/mysteries/barricelli-files/mystery.json @@ -0,0 +1,96 @@ +{ + "slug": "barricelli-files", + "title": "The Barricelli Files", + "narrative": { + "cast": [ + { "key": "dobby", "name": "Dobby", "role": "Glitch University · Student Counsellor", "defaultPose": "neutral" }, + { "key": "glitch-hunter", "name": "Glitch Hunter", "role": "Glitch University · Cosmotologist", "defaultPose": "neutral", "phoneNumber": "5550100", "email": "hunter@glitch.university" } + ], + "graph": { + "entry": "dobby-intro", + "nodes": [ + { + "key": "dobby-intro", "type": "dialogue", "label": "Scene 5 · Dobby", "x": 200, "y": 60, + "terminals": [{ "key": "continue", "label": "Continue", "to": "dobby-tasks" }], + "utterances": [ + { "npc": "dobby", "text": "Oh — you're the new Principal Investigator. I'm Dobby, student counsellor. Welcome to the Glitch University PI programme." }, + { "npc": "dobby", "text": "You'll want the GUPI course over at glitch.university — Open Source Intelligence, basics for citizen scientists. If you can't be bothered, there's a video that covers the absolute minimum." }, + { "npc": "dobby", "text": "It's remarkably simple, provided you have sufficient intelligence. Find the evidence on the internet, screenshot it, paste it in here. Fill out the source, date and URL so it can be verified — then connect it to the claim with a red thread and submit the report to the professor. You'll hear back within five earth-seconds." } + ] + }, + { + "key": "dobby-tasks", "type": "dialogue", "label": "Scene 6.1 · Dobby tasks", "x": 200, "y": 240, + "terminals": [{ "key": "continue", "label": "Call him", "to": "hunter-intro" }], + "utterances": [ + { "key": "d0", "npc": "dobby", "text": "Back already? Well — we do have something. Our Cosmotologist is investigating somebody. Some old mathematician, it seems. You should call him up." }, + { "key": "d-name", "parent": "d0", "utterer": "player", "text": "Who's the mathematician?", "awardsFlag": "dobby.knows_barricelli_name" }, + { "key": "d-name-a", "parent": "d-name", "npc": "dobby", "text": "Niels Aall Barricelli. There — don't say I never give you anything." }, + { "key": "d-more", "parent": "d-name-a", "utterer": "player", "text": "Tell me more about him.", "awardsFlag": "dobby.knows_barricelli" }, + { "key": "d-more-a", "parent": "d-more", "npc": "dobby", "text": "Italian-Norwegian mathematician. Niels — spelled with an 'e'. That's all you're getting from me." }, + { "key": "d-call2", "parent": "d-more-a", "utterer": "player", "text": "Right. I'll call him.", "terminal": "continue" }, + { "key": "d-call1", "parent": "d-name-a", "utterer": "player", "text": "I'll call him.", "terminal": "continue" }, + { "key": "d-reach", "parent": "d0", "utterer": "player", "text": "How do I reach him?" }, + { "key": "d-reach-a", "parent": "d-reach", "npc": "dobby", "text": "There's a number floating about. Find a phone. You're an investigator — investigate." }, + { "key": "d-reach-go", "parent": "d-reach-a", "utterer": "player", "text": "On it.", "terminal": "continue" } + ] + }, + { + "key": "phone", "type": "phone", "label": "Phone (dial Glitch Hunter)", "x": 470, "y": 240, + "terminals": [{ "key": "call-hunter", "label": "Glitch Hunter", "npc": "glitch-hunter", "to": "hunter-intro" }] + }, + { + "key": "hunter-intro", "type": "dialogue", "label": "Scene 6.2 · Glitch Hunter", "x": 200, "y": 420, + "terminals": [ + { "key": "proceed", "label": "Take the task", "to": "hunter-correct" }, + { "key": "decline", "label": "Back", "to": "dobby-tasks" } + ], + "utterances": [ + { "key": "h0", "npc": "glitch-hunter", "text": "Right — Dobby said you were up for some research tasks. I want a junior investigator. Interesting stuff, piling up everywhere. Question is: do you want in, or do you want out?" }, + { "key": "h-out", "parent": "h0", "utterer": "player", "text": "Out.", "terminal": "decline" }, + { "key": "h-in", "parent": "h0", "utterer": "player", "text": "I'm in." }, + { "key": "h-in-a", "parent": "h-in", "npc": "glitch-hunter", "text": "Good. I think we just found the entrance to a rabbit hole. I'll give you the name: Niels Aall Barricelli. Does it ring a bell?" }, + { "key": "h-noidea", "parent": "h-in-a", "utterer": "player", "text": "No — no idea who that is.", "terminal": "proceed" }, + { "key": "h-knows", "parent": "h-in-a", "utterer": "player", "text": "Niels Aall Barricelli — a Norwegian-Italian mathematician.", "requiresFlag": "dobby.knows_barricelli", "terminal": "proceed" }, + { "key": "h-name", "parent": "h-in-a", "utterer": "player", "text": "Niels Aall Barricelli.", "requiresFlag": "dobby.knows_barricelli_name", "terminal": "proceed" } + ] + }, + { + "key": "hunter-correct", "type": "dialogue", "label": "Scene 6.2 · The task", "x": 200, "y": 600, + "terminals": [{ "key": "continue", "label": "To the board", "to": "scene7" }], + "utterances": [ + { "npc": "glitch-hunter", "text": "Ah — impressive. He's not very well known. But you got the first name wrong there. It's Nils, not Niels. Easy mistake to make. Might come in handy to remember that." }, + { "npc": "glitch-hunter", "text": "Anyway — the task is simple. Nils Aall Barricelli. He wasn't just a brilliant mathematician; he was an inventor. Prove that to me, using open sources, and I'll take you on board." } + ] + }, + { + "key": "scene7", "type": "level", "label": "Scene 7 · Prove he was an inventor", "x": 200, "y": 780, + "templateSlug": "barricelli-inventor-proof", + "terminals": [{ "key": "report_back", "label": "Report back", "to": "hunter-drawer" }] + }, + { + "key": "hunter-drawer", "type": "dialogue", "label": "Scene 8 · Glitch Hunter", "x": 200, "y": 960, + "terminals": [{ "key": "continue", "label": "Continue", "to": "luggage" }], + "utterances": [ + { "npc": "glitch-hunter", "text": "Ha — there it is. Using a suitcase as drawers. You can just take the drawer with you. Told you he was a genius." } + ] + }, + { + "key": "luggage", "type": "merit", "label": "Scene 9 · The Barricelli Luggage", "x": 200, "y": 1140, + "awardsFlag": "barricelli_luggage", + "terminals": [{ "key": "continue", "label": "Continue", "to": "hunter-deepweb" }] + }, + { + "key": "hunter-deepweb", "type": "dialogue", "label": "Scene 10 · The deep web", "x": 200, "y": 1320, + "terminals": [{ "key": "done", "label": "End", "to": null }], + "utterances": [ + { "key": "w0", "npc": "glitch-hunter", "text": "Well done — you've mastered Open Source Intelligence. Barricelli was an unusual man. Not only an inventor and a mathematician. A genius. He saved his family too, you know." }, + { "key": "w-who", "parent": "w0", "utterer": "player", "text": "Who are you, exactly?" }, + { "key": "w-who-a", "parent": "w-who", "npc": "glitch-hunter", "text": "Me? I study Philosophical Cosmology. There's a video series and a book over at Glitch University, if you're curious.", "terminal": "done" }, + { "key": "w-family", "parent": "w0", "utterer": "player", "text": "Saved his family?" }, + { "key": "w-family-a", "parent": "w-family", "npc": "glitch-hunter", "text": "A remarkable character. Yes, he did. But perhaps I shouldn't tell you — it's a fitting task, one that would prove some real skill. If you can tell me how old he was when he saved his mother and father, and had a newspaper write about it — you'll need the deep web. Historical archives, national libraries. Figure that out, and you're ready for something deeper.", "terminal": "done" } + ] + } + ] + } + } +} From 2271a7b02c3d994713ebd92d53cd29d9831362c4 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 22 Aug 2026 19:24:11 +0200 Subject: [PATCH 02/10] Barricelli: add Scene 6 note-board between Dobby and Glitch Hunter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dobby now sends the player to a one-note level ("PHONE FOR GLITCH HUNTER · 5550100") whose report-back reaches Glitch Hunter — the intended note→phone beat, with the note-board's report-back standing in for dialing until the phone runtime lands. The note-board is a frozen barricelli-phone-note level template (single note exhibit). Co-Authored-By: Claude Opus 4.8 --- mysteries/barricelli-files/mystery.json | 384 ++++++++++++++++++++---- 1 file changed, 321 insertions(+), 63 deletions(-) diff --git a/mysteries/barricelli-files/mystery.json b/mysteries/barricelli-files/mystery.json index b6e31a1..c22130d 100644 --- a/mysteries/barricelli-files/mystery.json +++ b/mysteries/barricelli-files/mystery.json @@ -3,91 +3,349 @@ "title": "The Barricelli Files", "narrative": { "cast": [ - { "key": "dobby", "name": "Dobby", "role": "Glitch University · Student Counsellor", "defaultPose": "neutral" }, - { "key": "glitch-hunter", "name": "Glitch Hunter", "role": "Glitch University · Cosmotologist", "defaultPose": "neutral", "phoneNumber": "5550100", "email": "hunter@glitch.university" } + { + "key": "dobby", + "name": "Dobby", + "role": "Glitch University · Student Counsellor", + "defaultPose": "neutral" + }, + { + "key": "glitch-hunter", + "name": "Glitch Hunter", + "role": "Glitch University · Cosmotologist", + "defaultPose": "neutral", + "phoneNumber": "5550100", + "email": "hunter@glitch.university" + } ], "graph": { "entry": "dobby-intro", "nodes": [ { - "key": "dobby-intro", "type": "dialogue", "label": "Scene 5 · Dobby", "x": 200, "y": 60, - "terminals": [{ "key": "continue", "label": "Continue", "to": "dobby-tasks" }], - "utterances": [ - { "npc": "dobby", "text": "Oh — you're the new Principal Investigator. I'm Dobby, student counsellor. Welcome to the Glitch University PI programme." }, - { "npc": "dobby", "text": "You'll want the GUPI course over at glitch.university — Open Source Intelligence, basics for citizen scientists. If you can't be bothered, there's a video that covers the absolute minimum." }, - { "npc": "dobby", "text": "It's remarkably simple, provided you have sufficient intelligence. Find the evidence on the internet, screenshot it, paste it in here. Fill out the source, date and URL so it can be verified — then connect it to the claim with a red thread and submit the report to the professor. You'll hear back within five earth-seconds." } - ] - }, - { - "key": "dobby-tasks", "type": "dialogue", "label": "Scene 6.1 · Dobby tasks", "x": 200, "y": 240, - "terminals": [{ "key": "continue", "label": "Call him", "to": "hunter-intro" }], - "utterances": [ - { "key": "d0", "npc": "dobby", "text": "Back already? Well — we do have something. Our Cosmotologist is investigating somebody. Some old mathematician, it seems. You should call him up." }, - { "key": "d-name", "parent": "d0", "utterer": "player", "text": "Who's the mathematician?", "awardsFlag": "dobby.knows_barricelli_name" }, - { "key": "d-name-a", "parent": "d-name", "npc": "dobby", "text": "Niels Aall Barricelli. There — don't say I never give you anything." }, - { "key": "d-more", "parent": "d-name-a", "utterer": "player", "text": "Tell me more about him.", "awardsFlag": "dobby.knows_barricelli" }, - { "key": "d-more-a", "parent": "d-more", "npc": "dobby", "text": "Italian-Norwegian mathematician. Niels — spelled with an 'e'. That's all you're getting from me." }, - { "key": "d-call2", "parent": "d-more-a", "utterer": "player", "text": "Right. I'll call him.", "terminal": "continue" }, - { "key": "d-call1", "parent": "d-name-a", "utterer": "player", "text": "I'll call him.", "terminal": "continue" }, - { "key": "d-reach", "parent": "d0", "utterer": "player", "text": "How do I reach him?" }, - { "key": "d-reach-a", "parent": "d-reach", "npc": "dobby", "text": "There's a number floating about. Find a phone. You're an investigator — investigate." }, - { "key": "d-reach-go", "parent": "d-reach-a", "utterer": "player", "text": "On it.", "terminal": "continue" } - ] - }, - { - "key": "phone", "type": "phone", "label": "Phone (dial Glitch Hunter)", "x": 470, "y": 240, - "terminals": [{ "key": "call-hunter", "label": "Glitch Hunter", "npc": "glitch-hunter", "to": "hunter-intro" }] - }, - { - "key": "hunter-intro", "type": "dialogue", "label": "Scene 6.2 · Glitch Hunter", "x": 200, "y": 420, + "key": "dobby-intro", + "type": "dialogue", + "label": "Scene 5 · Dobby", + "x": 200, + "y": 60, "terminals": [ - { "key": "proceed", "label": "Take the task", "to": "hunter-correct" }, - { "key": "decline", "label": "Back", "to": "dobby-tasks" } + { + "key": "continue", + "label": "Continue", + "to": "dobby-tasks" + } ], "utterances": [ - { "key": "h0", "npc": "glitch-hunter", "text": "Right — Dobby said you were up for some research tasks. I want a junior investigator. Interesting stuff, piling up everywhere. Question is: do you want in, or do you want out?" }, - { "key": "h-out", "parent": "h0", "utterer": "player", "text": "Out.", "terminal": "decline" }, - { "key": "h-in", "parent": "h0", "utterer": "player", "text": "I'm in." }, - { "key": "h-in-a", "parent": "h-in", "npc": "glitch-hunter", "text": "Good. I think we just found the entrance to a rabbit hole. I'll give you the name: Niels Aall Barricelli. Does it ring a bell?" }, - { "key": "h-noidea", "parent": "h-in-a", "utterer": "player", "text": "No — no idea who that is.", "terminal": "proceed" }, - { "key": "h-knows", "parent": "h-in-a", "utterer": "player", "text": "Niels Aall Barricelli — a Norwegian-Italian mathematician.", "requiresFlag": "dobby.knows_barricelli", "terminal": "proceed" }, - { "key": "h-name", "parent": "h-in-a", "utterer": "player", "text": "Niels Aall Barricelli.", "requiresFlag": "dobby.knows_barricelli_name", "terminal": "proceed" } + { + "npc": "dobby", + "text": "Oh — you're the new Principal Investigator. I'm Dobby, student counsellor. Welcome to the Glitch University PI programme." + }, + { + "npc": "dobby", + "text": "You'll want the GUPI course over at glitch.university — Open Source Intelligence, basics for citizen scientists. If you can't be bothered, there's a video that covers the absolute minimum." + }, + { + "npc": "dobby", + "text": "It's remarkably simple, provided you have sufficient intelligence. Find the evidence on the internet, screenshot it, paste it in here. Fill out the source, date and URL so it can be verified — then connect it to the claim with a red thread and submit the report to the professor. You'll hear back within five earth-seconds." + } ] }, { - "key": "hunter-correct", "type": "dialogue", "label": "Scene 6.2 · The task", "x": 200, "y": 600, - "terminals": [{ "key": "continue", "label": "To the board", "to": "scene7" }], + "key": "dobby-tasks", + "type": "dialogue", + "label": "Scene 6.1 · Dobby tasks", + "x": 200, + "y": 240, + "terminals": [ + { + "key": "continue", + "label": "Find a phone", + "to": "note-board" + } + ], "utterances": [ - { "npc": "glitch-hunter", "text": "Ah — impressive. He's not very well known. But you got the first name wrong there. It's Nils, not Niels. Easy mistake to make. Might come in handy to remember that." }, - { "npc": "glitch-hunter", "text": "Anyway — the task is simple. Nils Aall Barricelli. He wasn't just a brilliant mathematician; he was an inventor. Prove that to me, using open sources, and I'll take you on board." } + { + "key": "d0", + "npc": "dobby", + "text": "Back already? Well — we do have something. Our Cosmotologist is investigating somebody. Some old mathematician, it seems. You should call him up." + }, + { + "key": "d-name", + "parent": "d0", + "utterer": "player", + "text": "Who's the mathematician?", + "awardsFlag": "dobby.knows_barricelli_name" + }, + { + "key": "d-name-a", + "parent": "d-name", + "npc": "dobby", + "text": "Niels Aall Barricelli. There — don't say I never give you anything." + }, + { + "key": "d-more", + "parent": "d-name-a", + "utterer": "player", + "text": "Tell me more about him.", + "awardsFlag": "dobby.knows_barricelli" + }, + { + "key": "d-more-a", + "parent": "d-more", + "npc": "dobby", + "text": "Italian-Norwegian mathematician. Niels — spelled with an 'e'. That's all you're getting from me." + }, + { + "key": "d-call2", + "parent": "d-more-a", + "utterer": "player", + "text": "Right. I'll call him.", + "terminal": "continue" + }, + { + "key": "d-call1", + "parent": "d-name-a", + "utterer": "player", + "text": "I'll call him.", + "terminal": "continue" + }, + { + "key": "d-reach", + "parent": "d0", + "utterer": "player", + "text": "How do I reach him?" + }, + { + "key": "d-reach-a", + "parent": "d-reach", + "npc": "dobby", + "text": "There's a number floating about. Find a phone. You're an investigator — investigate." + }, + { + "key": "d-reach-go", + "parent": "d-reach-a", + "utterer": "player", + "text": "On it.", + "terminal": "continue" + } ] }, { - "key": "scene7", "type": "level", "label": "Scene 7 · Prove he was an inventor", "x": 200, "y": 780, + "key": "note-board", + "type": "level", + "label": "Scene 6 · Note board", + "x": 200, + "y": 420, + "templateSlug": "barricelli-phone-note", + "terminals": [ + { + "key": "report_back", + "label": "Call Glitch Hunter", + "to": "hunter-intro" + } + ] + }, + { + "key": "phone", + "type": "phone", + "label": "Phone (dial Glitch Hunter)", + "x": 470, + "y": 420, + "terminals": [ + { + "key": "call-hunter", + "label": "Glitch Hunter", + "npc": "glitch-hunter", + "to": "hunter-intro" + } + ] + }, + { + "key": "hunter-intro", + "type": "dialogue", + "label": "Scene 6.2 · Glitch Hunter", + "x": 200, + "y": 600, + "terminals": [ + { + "key": "proceed", + "label": "Take the task", + "to": "hunter-correct" + }, + { + "key": "decline", + "label": "Back", + "to": "dobby-tasks" + } + ], + "utterances": [ + { + "key": "h0", + "npc": "glitch-hunter", + "text": "Right — Dobby said you were up for some research tasks. I want a junior investigator. Interesting stuff, piling up everywhere. Question is: do you want in, or do you want out?" + }, + { + "key": "h-out", + "parent": "h0", + "utterer": "player", + "text": "Out.", + "terminal": "decline" + }, + { + "key": "h-in", + "parent": "h0", + "utterer": "player", + "text": "I'm in." + }, + { + "key": "h-in-a", + "parent": "h-in", + "npc": "glitch-hunter", + "text": "Good. I think we just found the entrance to a rabbit hole. I'll give you the name: Niels Aall Barricelli. Does it ring a bell?" + }, + { + "key": "h-noidea", + "parent": "h-in-a", + "utterer": "player", + "text": "No — no idea who that is.", + "terminal": "proceed" + }, + { + "key": "h-knows", + "parent": "h-in-a", + "utterer": "player", + "text": "Niels Aall Barricelli — a Norwegian-Italian mathematician.", + "requiresFlag": "dobby.knows_barricelli", + "terminal": "proceed" + }, + { + "key": "h-name", + "parent": "h-in-a", + "utterer": "player", + "text": "Niels Aall Barricelli.", + "requiresFlag": "dobby.knows_barricelli_name", + "terminal": "proceed" + } + ] + }, + { + "key": "hunter-correct", + "type": "dialogue", + "label": "Scene 6.2 · The task", + "x": 200, + "y": 780, + "terminals": [ + { + "key": "continue", + "label": "To the board", + "to": "scene7" + } + ], + "utterances": [ + { + "npc": "glitch-hunter", + "text": "Ah — impressive. He's not very well known. But you got the first name wrong there. It's Nils, not Niels. Easy mistake to make. Might come in handy to remember that." + }, + { + "npc": "glitch-hunter", + "text": "Anyway — the task is simple. Nils Aall Barricelli. He wasn't just a brilliant mathematician; he was an inventor. Prove that to me, using open sources, and I'll take you on board." + } + ] + }, + { + "key": "scene7", + "type": "level", + "label": "Scene 7 · Prove he was an inventor", + "x": 200, + "y": 960, "templateSlug": "barricelli-inventor-proof", - "terminals": [{ "key": "report_back", "label": "Report back", "to": "hunter-drawer" }] - }, - { - "key": "hunter-drawer", "type": "dialogue", "label": "Scene 8 · Glitch Hunter", "x": 200, "y": 960, - "terminals": [{ "key": "continue", "label": "Continue", "to": "luggage" }], - "utterances": [ - { "npc": "glitch-hunter", "text": "Ha — there it is. Using a suitcase as drawers. You can just take the drawer with you. Told you he was a genius." } + "terminals": [ + { + "key": "report_back", + "label": "Report back", + "to": "hunter-drawer" + } ] }, { - "key": "luggage", "type": "merit", "label": "Scene 9 · The Barricelli Luggage", "x": 200, "y": 1140, - "awardsFlag": "barricelli_luggage", - "terminals": [{ "key": "continue", "label": "Continue", "to": "hunter-deepweb" }] + "key": "hunter-drawer", + "type": "dialogue", + "label": "Scene 8 · Glitch Hunter", + "x": 200, + "y": 1140, + "terminals": [ + { + "key": "continue", + "label": "Continue", + "to": "luggage" + } + ], + "utterances": [ + { + "npc": "glitch-hunter", + "text": "Ha — there it is. Using a suitcase as drawers. You can just take the drawer with you. Told you he was a genius." + } + ] }, { - "key": "hunter-deepweb", "type": "dialogue", "label": "Scene 10 · The deep web", "x": 200, "y": 1320, - "terminals": [{ "key": "done", "label": "End", "to": null }], + "key": "luggage", + "type": "merit", + "label": "Scene 9 · The Barricelli Luggage", + "x": 200, + "y": 1320, + "awardsFlag": "barricelli_luggage", + "terminals": [ + { + "key": "continue", + "label": "Continue", + "to": "hunter-deepweb" + } + ] + }, + { + "key": "hunter-deepweb", + "type": "dialogue", + "label": "Scene 10 · The deep web", + "x": 200, + "y": 1500, + "terminals": [ + { + "key": "done", + "label": "End", + "to": null + } + ], "utterances": [ - { "key": "w0", "npc": "glitch-hunter", "text": "Well done — you've mastered Open Source Intelligence. Barricelli was an unusual man. Not only an inventor and a mathematician. A genius. He saved his family too, you know." }, - { "key": "w-who", "parent": "w0", "utterer": "player", "text": "Who are you, exactly?" }, - { "key": "w-who-a", "parent": "w-who", "npc": "glitch-hunter", "text": "Me? I study Philosophical Cosmology. There's a video series and a book over at Glitch University, if you're curious.", "terminal": "done" }, - { "key": "w-family", "parent": "w0", "utterer": "player", "text": "Saved his family?" }, - { "key": "w-family-a", "parent": "w-family", "npc": "glitch-hunter", "text": "A remarkable character. Yes, he did. But perhaps I shouldn't tell you — it's a fitting task, one that would prove some real skill. If you can tell me how old he was when he saved his mother and father, and had a newspaper write about it — you'll need the deep web. Historical archives, national libraries. Figure that out, and you're ready for something deeper.", "terminal": "done" } + { + "key": "w0", + "npc": "glitch-hunter", + "text": "Well done — you've mastered Open Source Intelligence. Barricelli was an unusual man. Not only an inventor and a mathematician. A genius. He saved his family too, you know." + }, + { + "key": "w-who", + "parent": "w0", + "utterer": "player", + "text": "Who are you, exactly?" + }, + { + "key": "w-who-a", + "parent": "w-who", + "npc": "glitch-hunter", + "text": "Me? I study Philosophical Cosmology. There's a video series and a book over at Glitch University, if you're curious.", + "terminal": "done" + }, + { + "key": "w-family", + "parent": "w0", + "utterer": "player", + "text": "Saved his family?" + }, + { + "key": "w-family-a", + "parent": "w-family", + "npc": "glitch-hunter", + "text": "A remarkable character. Yes, he did. But perhaps I shouldn't tell you — it's a fitting task, one that would prove some real skill. If you can tell me how old he was when he saved his mother and father, and had a newspaper write about it — you'll need the deep web. Historical archives, national libraries. Figure that out, and you're ready for something deeper.", + "terminal": "done" + } ] } ] From 56bf2f97d0fe3ac188aeca0e24bdca4caf89ffbb Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 22 Aug 2026 19:38:46 +0200 Subject: [PATCH 03/10] Phone runtime (2a): connected directory + dial resolution GET /playthroughs/:id/phone returns the directory of the phone node the current node is wired to; POST /playthroughs/:id/dial resolves a number to connect (advance to the wired dialogue), voicemail (known contact, no line here), or not-in-service. Barricelli's note-board is wired to the phone node and Glitch Hunter's decline returns to it. Frontend (inventory on the board) is 2b. Co-Authored-By: Claude Opus 4.8 --- mysteries/barricelli-files/mystery.json | 7 ++++- server/index.ts | 9 ++++++ server/narrativeRepository.ts | 42 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/mysteries/barricelli-files/mystery.json b/mysteries/barricelli-files/mystery.json index c22130d..235e63e 100644 --- a/mysteries/barricelli-files/mystery.json +++ b/mysteries/barricelli-files/mystery.json @@ -141,6 +141,11 @@ "key": "report_back", "label": "Call Glitch Hunter", "to": "hunter-intro" + }, + { + "key": "phone", + "label": "Phone", + "to": "phone" } ] }, @@ -174,7 +179,7 @@ { "key": "decline", "label": "Back", - "to": "dobby-tasks" + "to": "note-board" } ], "utterances": [ diff --git a/server/index.ts b/server/index.ts index 670b186..912f442 100644 --- a/server/index.ts +++ b/server/index.ts @@ -516,6 +516,15 @@ app.post('/api/playthroughs/:id/utterances/:uid/reach', async (req, res, next) = result.ok ? res.json({ earned: result.earned ?? false }) : res.status(404).json({ error: 'Not found' }) } catch (error) { next(error) } }) +// The phone tool: the directory available on the current node, and dialing a number. +app.get('/api/playthroughs/:id/phone', async (req, res, next) => { + try { res.json(await narrative.phoneDirectory(String(req.params.id))) } + catch (error) { next(error) } +}) +app.post('/api/playthroughs/:id/dial', async (req, res, next) => { + try { res.json(await narrative.dial(String(req.params.id), String(req.body?.number || ''))) } + catch (error) { next(error) } +}) // Dev teleport: jump the playthrough to an explicit story node. Powers /node/:id. app.post('/api/playthroughs/:id/goto', async (req, res, next) => { try { diff --git a/server/narrativeRepository.ts b/server/narrativeRepository.ts index e21d9a1..bb4315b 100644 --- a/server/narrativeRepository.ts +++ b/server/narrativeRepository.ts @@ -59,6 +59,8 @@ export interface NarrativeRepository { listAchievements(playthroughId: string): Promise awardAchievement(playthroughId: string, flagKey: string, nodeId?: string | null): Promise<{ ok: boolean; earned: boolean; error?: string }> reachUtterance(playthroughId: string, utteranceId: string): Promise<{ ok: boolean; earned?: boolean }> + phoneDirectory(playthroughId: string): Promise<{ available: boolean; numbers: { number: string; name: string }[] }> + dial(playthroughId: string, number: string): Promise<{ outcome: 'connect' | 'voicemail' | 'unknown'; name?: string; state?: PlaythroughState }> gotoNode(userId: string, playthroughId: string, nodeId: string): Promise<{ ok: boolean; state?: PlaythroughState; error?: string }> listMysteries(): Promise listPlayableMysteries(): Promise<{ slug: string; title: string }[]> @@ -280,6 +282,46 @@ export function createNarrativeRepository(pool: Pool, objectStorage: ObjectStora return { ok: true, earned: (result.rowCount || 0) > 0 } }, + // The phone directory available on the player's current node: the terminals of a + // phone node the current node is wired to. No connected phone node => nobody's listed. + async phoneDirectory(playthroughId) { + const pt = (await pool.query<{ current_node_id: string | null }>('SELECT current_node_id FROM osint.playthroughs WHERE id=$1', [playthroughId])).rows[0] + if (!pt?.current_node_id) return { available: false, numbers: [] } + const phoneNode = (await pool.query<{ id: string }>( + `SELECT pn.id FROM osint.story_node_terminals t JOIN osint.story_nodes pn ON pn.id=t.to_node_id + WHERE t.parent_node_id=$1 AND pn.node_type='phone' LIMIT 1`, [pt.current_node_id])).rows[0] + if (!phoneNode) return { available: true, numbers: [] } + const dir = (await pool.query<{ number: string; name: string }>( + `SELECT npc.phone_number AS number, npc.name FROM osint.story_node_terminals t JOIN osint.npcs npc ON npc.id=t.npc_id + WHERE t.parent_node_id=$1 AND npc.phone_number IS NOT NULL ORDER BY t.sort_order`, [phoneNode.id])).rows + return { available: true, numbers: dir } + }, + + // Resolve a dialed number: connect (advance to the wired dialogue), voicemail (a + // known contact with no line here), or not-in-service (no such number). + async dial(playthroughId, rawNumber) { + const number = rawNumber.replace(/\D/g, '') + if (!number) return { outcome: 'unknown' } + const pt = (await pool.query<{ current_node_id: string | null }>('SELECT current_node_id FROM osint.playthroughs WHERE id=$1', [playthroughId])).rows[0] + if (!pt?.current_node_id) return { outcome: 'unknown' } + const phoneNode = (await pool.query<{ id: string }>( + `SELECT pn.id FROM osint.story_node_terminals t JOIN osint.story_nodes pn ON pn.id=t.to_node_id + WHERE t.parent_node_id=$1 AND pn.node_type='phone' LIMIT 1`, [pt.current_node_id])).rows[0] + if (phoneNode) { + const term = (await pool.query<{ to_node_id: string | null; name: string }>( + `SELECT t.to_node_id, npc.name FROM osint.story_node_terminals t JOIN osint.npcs npc ON npc.id=t.npc_id + WHERE t.parent_node_id=$1 AND regexp_replace(npc.phone_number,'\\D','','g')=$2 LIMIT 1`, [phoneNode.id, number])).rows[0] + if (term?.to_node_id) { + await pool.query('UPDATE osint.playthroughs SET current_node_id=$2,current_level_id=NULL,updated_at=NOW() WHERE id=$1', [playthroughId, term.to_node_id]) + const state = await stateForPlaythrough(playthroughId) + return { outcome: 'connect', name: term.name, state: state ?? undefined } + } + } + const npc = (await pool.query<{ name: string }>( + `SELECT name FROM osint.npcs WHERE regexp_replace(phone_number,'\\D','','g')=$1 AND mystery_id IS NULL LIMIT 1`, [number])).rows[0] + return npc ? { outcome: 'voicemail', name: npc.name } : { outcome: 'unknown' } + }, + async listAchievements(playthroughId) { if (!(await pool.query('SELECT 1 FROM osint.playthroughs WHERE id=$1', [playthroughId])).rowCount) return null const rows = (await pool.query<{ flag_key: string }>('SELECT flag_key FROM osint.achievements WHERE playthrough_id=$1 ORDER BY flag_key', [playthroughId])).rows From 80bc9b21a7fbc2586e2249aaab5fd3f93f52a019 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 22 Aug 2026 19:49:00 +0200 Subject: [PATCH 04/10] Phone runtime (2b): inventory phone on the board dials for real The phone gains a session mode that reads the live directory (/phone) and dials (/dial); the inventory threads it through. On a campaign level the board shows an INVENTORY nav button that opens the tool rack, and connecting a call hands back to the campaign via /?resume=1 (a fast-path that resumes the current node instead of the splash). On Barricelli's note-board: open inventory -> phone -> dial 5550100 -> Glitch Hunter. Co-Authored-By: Claude Opus 4.8 --- src/App.tsx | 12 +++++++++-- src/inventory.tsx | 6 +++--- src/phone.tsx | 53 +++++++++++++++++++++++++++++++++-------------- src/play.tsx | 7 +++++++ 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a568c0c..97277d3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' +import { lazy, Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { BookOpen, Building2, CalendarClock, Camera, Check, ChevronRight, CircleHelp, ClipboardCheck, FileText, FolderOpen, Hand, Image as ImageIcon, Images, Info, Link2, Minus, MousePointer2, Network, Newspaper, NotebookPen, Pencil, Plus, RotateCcw, Search, Trash2, Upload, UserRound, X, ZoomIn, ZoomOut } from 'lucide-react' import type { BriefConcept, CaseDocument, CaseReport, CaseReportSubmissionInput, CaseState, Connection, DocumentCaptureKind, DocumentSemanticAnalysis, EventExhibit, Evidence, EvidenceMatchRuleDefinition, Exhibit, ExhibitRelation, FolderExhibit, LevelBrief, LevelFlag, LevelGoal, OrganizationKind, PartyExhibit, PartyKind, SourceFileType, TimelineRange, TimelineView, UploadedCaseDocument } from './types' import { AdminPanel } from './admin' @@ -6,6 +6,9 @@ import { clampBoardZoom, containedIds, dateValue, discardExhibit, folderIsOpen, import { defaultConnectionLabel, documentCapture, documentCaptureRegistry, documentExhibits, documentWidget, evidenceExhibits, exhibitWidget, type ExhibitWidgetContext, type WidgetCommand } from './exhibitRegistry' import type { PlaythroughState } from './narrative' +// three.js stays out of the board bundle until the player opens the inventory. +const Inventory = lazy(() => import('./inventory').then(m => ({ default: m.Inventory }))) + const BOARD_W = 2400 const BOARD_H = 1500 const SOURCE_FILE_TYPES: { value: SourceFileType; label: string }[] = [ @@ -71,6 +74,7 @@ export function App() { const [recentlyCreatedConnectionId, setRecentlyCreatedConnectionId] = useState(null) const [threadDraft, setThreadDraft] = useState(null) const [flagsOpen, setFlagsOpen] = useState(false) + const [inventoryOpen, setInventoryOpen] = useState(false) const [matchRulesOpen, setMatchRulesOpen] = useState(false) const [arrivingExhibitIds, setArrivingExhibitIds] = useState([]) const [activePlaythroughId, setActivePlaythroughId] = useState(null) @@ -537,6 +541,9 @@ export function App() { }) const timelineView = caseState.views.find((view): view is TimelineView => view.type === 'timeline') return
+ {inventoryOpen && activePlaythroughId && + window.location.assign('/?resume=1') }} onClose={() => setInventoryOpen(false)} /> + }
GUOSINT BOARD / {requestedEditMode && caseState.editingAllowed ? 'LEVEL EDITOR' : 'CASE TERMINAL'}