Bugs found by playing the game over the bus: - hotspots now have positions: 'walk to precinct door' pathfinds there, and touch verbs walk over to hotspots like they do to props/NPCs - GameState::settle(): the MCP surface resolves walk-then-act in one call instead of returning zero events on a distant verb - typed 'use X on Y' walks over like a clicked use (was unlimited range) - taking/deleting a solid prop rebakes — no more lingering invisible walls - sergeant dialogue no longer loops back to its greeting - serde-facing maps are BTreeMap: room JSON and --sample are byte-stable Engine: - sprite-sheet actor views: <name>-sheet<C>x<R>.png slices into C frames x R direction loops (vend-bot now rolls on animated treads) - pixel-accurate click targeting with 1px slop; typed commands click mid-sprite (a feet click lands between an actor's legs) - parser: compass walking (go east / n), 'bye' ends conversations, 'look' lists exits GUI: - hover hot-text: cursor reads the sentence a click would say - death banner + '+N' score toast Tests: full-playthrough regression suite (win path, death rewind, walk-over dialogue, solid-prop footprint) + sheet slicing unit test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
846 lines
15 KiB
JSON
846 lines
15 KiB
JSON
{
|
|
"name": "41st Precinct Lobby",
|
|
"enter_text": "Fluorescents flicker over the duty desk. The evidence locker waits along the east wall.",
|
|
"background": "",
|
|
"ops": [
|
|
{
|
|
"VGradient": {
|
|
"x": 0,
|
|
"y": 0,
|
|
"w": 320,
|
|
"h": 96,
|
|
"ramp": [
|
|
119,
|
|
76
|
|
],
|
|
"ink": {
|
|
"color": 0,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 0,
|
|
"y": 96,
|
|
"w": 320,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 153,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"VGradient": {
|
|
"x": 0,
|
|
"y": 100,
|
|
"w": 320,
|
|
"h": 90,
|
|
"ramp": [
|
|
23,
|
|
21
|
|
],
|
|
"ink": {
|
|
"color": 0,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 0,
|
|
"y": 100,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 64,
|
|
"y": 100,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 128,
|
|
"y": 100,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 192,
|
|
"y": 100,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 256,
|
|
"y": 100,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 32,
|
|
"y": 115,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 96,
|
|
"y": 115,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 160,
|
|
"y": 115,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 224,
|
|
"y": 115,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 288,
|
|
"y": 115,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 0,
|
|
"y": 130,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 64,
|
|
"y": 130,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 128,
|
|
"y": 130,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 192,
|
|
"y": 130,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 256,
|
|
"y": 130,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 32,
|
|
"y": 145,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 96,
|
|
"y": 145,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 160,
|
|
"y": 145,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 224,
|
|
"y": 145,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 288,
|
|
"y": 145,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 0,
|
|
"y": 160,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 64,
|
|
"y": 160,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 128,
|
|
"y": 160,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 192,
|
|
"y": 160,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 256,
|
|
"y": 160,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 32,
|
|
"y": 175,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 96,
|
|
"y": 175,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 160,
|
|
"y": 175,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 224,
|
|
"y": 175,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 288,
|
|
"y": 175,
|
|
"w": 32,
|
|
"h": 15,
|
|
"ink": {
|
|
"color": 118,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 118,
|
|
"y": 96,
|
|
"w": 90,
|
|
"h": 26,
|
|
"ink": {
|
|
"color": 110,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 118,
|
|
"y": 96,
|
|
"w": 90,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 153,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 30,
|
|
"y": 30,
|
|
"w": 20,
|
|
"h": 26,
|
|
"ink": {
|
|
"color": 196,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 56,
|
|
"y": 30,
|
|
"w": 20,
|
|
"h": 26,
|
|
"ink": {
|
|
"color": 167,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 140,
|
|
"y": 182,
|
|
"w": 40,
|
|
"h": 8,
|
|
"ink": {
|
|
"color": 147,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"palette": null,
|
|
"cycles": [],
|
|
"scale": {
|
|
"horizon_y": 88,
|
|
"min_scale": 0.7,
|
|
"full_y": 185
|
|
},
|
|
"spawn": [
|
|
160,
|
|
176
|
|
],
|
|
"exits": [
|
|
null,
|
|
null,
|
|
null,
|
|
null
|
|
],
|
|
"exit_flags": [
|
|
"",
|
|
"",
|
|
"",
|
|
""
|
|
],
|
|
"exit_blocked": [
|
|
"",
|
|
"",
|
|
"",
|
|
""
|
|
],
|
|
"hotspots": [
|
|
{
|
|
"name": "street door",
|
|
"rect": [
|
|
136,
|
|
184,
|
|
48,
|
|
6
|
|
],
|
|
"poly": [],
|
|
"msgs": {
|
|
"look": "Back out to Neon Row."
|
|
},
|
|
"exit_to": 0,
|
|
"arrive": [
|
|
226,
|
|
120
|
|
],
|
|
"requires_flag": "",
|
|
"blocked_text": "",
|
|
"trigger": false,
|
|
"blocks": false
|
|
},
|
|
{
|
|
"name": "duty desk",
|
|
"rect": [
|
|
118,
|
|
96,
|
|
90,
|
|
30
|
|
],
|
|
"poly": [],
|
|
"msgs": {
|
|
"do": "You straighten a stack of incident forms. Order is restored, briefly.",
|
|
"look": "The duty desk. Coffee rings dating back three administrations."
|
|
},
|
|
"exit_to": null,
|
|
"arrive": null,
|
|
"requires_flag": "",
|
|
"blocked_text": "",
|
|
"trigger": false,
|
|
"blocks": false
|
|
},
|
|
{
|
|
"name": "wanted posters",
|
|
"rect": [
|
|
28,
|
|
28,
|
|
52,
|
|
30
|
|
],
|
|
"poly": [],
|
|
"msgs": {
|
|
"look": "WANTED: the Compost Bandit. Last seen fleeing Sickbay with a datacard."
|
|
},
|
|
"exit_to": null,
|
|
"arrive": null,
|
|
"requires_flag": "",
|
|
"blocked_text": "",
|
|
"trigger": false,
|
|
"blocks": false
|
|
}
|
|
],
|
|
"props": [
|
|
{
|
|
"name": "locker",
|
|
"sprite": "locker",
|
|
"x": 286,
|
|
"y": 138,
|
|
"msgs": {
|
|
"do": "Locked tight. The reader-slot blinks at you, unimpressed.",
|
|
"look": "Evidence locker 47. The reader-slot blinks, wanting a keycard."
|
|
},
|
|
"takeable": false,
|
|
"synonyms": "",
|
|
"use_text": "The keycard chirps. Locker 47 clunks open: one data-slate, sealed in an evidence bag.",
|
|
"needs": "keycard",
|
|
"wins": false,
|
|
"requires_flag": "",
|
|
"sets_flag": "locker_open",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 10,
|
|
"consumes": false,
|
|
"kills": false,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": true,
|
|
"fixed_priority": null,
|
|
"fixed_scale": null
|
|
},
|
|
{
|
|
"name": "terminal",
|
|
"sprite": "terminal",
|
|
"x": 36,
|
|
"y": 130,
|
|
"msgs": {
|
|
"do": "It wants the evidence, not your fingerprints.",
|
|
"look": "The case terminal. Slot: DATA-SLATE. It has been hungry for weeks."
|
|
},
|
|
"takeable": false,
|
|
"synonyms": "",
|
|
"use_text": "The slate slides home. Records unspool. Warrants bloom. Somewhere, the Compost Bandit sneezes.",
|
|
"needs": "evidence",
|
|
"wins": true,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 10,
|
|
"consumes": true,
|
|
"kills": false,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": true,
|
|
"fixed_priority": null,
|
|
"fixed_scale": null
|
|
}
|
|
],
|
|
"npcs": [
|
|
{
|
|
"name": "sergeant",
|
|
"sprite": "sergeant",
|
|
"x": 162,
|
|
"y": 132,
|
|
"msgs": {
|
|
"look": "Sergeant Brasso. Chrome polished, patience not."
|
|
},
|
|
"dialogue": [
|
|
{
|
|
"says": "Morp-9. The data-slate case won't close itself.",
|
|
"choices": [
|
|
{
|
|
"text": "Where do I start?",
|
|
"goto": 1,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"points": 0,
|
|
"kills": false
|
|
},
|
|
{
|
|
"text": "What do I do with evidence?",
|
|
"goto": 2,
|
|
"requires_flag": "locker_open",
|
|
"sets_flag": "",
|
|
"points": 0,
|
|
"kills": false
|
|
},
|
|
{
|
|
"text": "On it, Sarge.",
|
|
"goto": -1,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"points": 0,
|
|
"kills": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"says": "Perp dumped a keycard in Rain Alley, east off Neon Row. It opens evidence locker 47.",
|
|
"choices": [
|
|
{
|
|
"text": "Got it.",
|
|
"goto": -1,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"points": 0,
|
|
"kills": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"says": "Slot the slate into the case terminal, west wall. Then we both go home.",
|
|
"choices": [
|
|
{
|
|
"text": "Copy.",
|
|
"goto": -1,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"points": 0,
|
|
"kills": false
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"portrait": "sergeant-face",
|
|
"wander": 0,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"fixed_scale": null
|
|
}
|
|
],
|
|
"music": 1,
|
|
"weather": 0,
|
|
"defaults": {},
|
|
"script": "room1.rhai"
|
|
} |