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>
714 lines
12 KiB
JSON
714 lines
12 KiB
JSON
{
|
|
"name": "Rain Alley",
|
|
"enter_text": "The alley smells like rust and secrets. Crates wall off the far corner. Something glints behind them.",
|
|
"background": "",
|
|
"ops": [
|
|
{
|
|
"VGradient": {
|
|
"x": 0,
|
|
"y": 0,
|
|
"w": 320,
|
|
"h": 100,
|
|
"ramp": [
|
|
33,
|
|
76
|
|
],
|
|
"ink": {
|
|
"color": 0,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Polygon": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
0
|
|
],
|
|
[
|
|
110,
|
|
0
|
|
],
|
|
[
|
|
86,
|
|
100
|
|
],
|
|
[
|
|
0,
|
|
100
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 75,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Polygon": {
|
|
"pts": [
|
|
[
|
|
210,
|
|
0
|
|
],
|
|
[
|
|
320,
|
|
0
|
|
],
|
|
[
|
|
320,
|
|
100
|
|
],
|
|
[
|
|
234,
|
|
100
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 111,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
10
|
|
],
|
|
[
|
|
101,
|
|
10
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
217,
|
|
10
|
|
],
|
|
[
|
|
320,
|
|
10
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
24
|
|
],
|
|
[
|
|
98,
|
|
24
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
220,
|
|
24
|
|
],
|
|
[
|
|
320,
|
|
24
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
38
|
|
],
|
|
[
|
|
96,
|
|
38
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
222,
|
|
38
|
|
],
|
|
[
|
|
320,
|
|
38
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
52
|
|
],
|
|
[
|
|
94,
|
|
52
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
224,
|
|
52
|
|
],
|
|
[
|
|
320,
|
|
52
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
66
|
|
],
|
|
[
|
|
91,
|
|
66
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
227,
|
|
66
|
|
],
|
|
[
|
|
320,
|
|
66
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
80
|
|
],
|
|
[
|
|
89,
|
|
80
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
229,
|
|
80
|
|
],
|
|
[
|
|
320,
|
|
80
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
0,
|
|
94
|
|
],
|
|
[
|
|
87,
|
|
94
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 33,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Line": {
|
|
"pts": [
|
|
[
|
|
231,
|
|
94
|
|
],
|
|
[
|
|
320,
|
|
94
|
|
]
|
|
],
|
|
"ink": {
|
|
"color": 68,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 250,
|
|
"y": 30,
|
|
"w": 4,
|
|
"h": 16,
|
|
"ink": {
|
|
"color": 248,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 254,
|
|
"y": 30,
|
|
"w": 6,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 250,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 254,
|
|
"y": 36,
|
|
"w": 6,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 252,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 254,
|
|
"y": 42,
|
|
"w": 6,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 254,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"VoronoiFill": {
|
|
"x": 0,
|
|
"y": 100,
|
|
"w": 320,
|
|
"h": 90,
|
|
"cell": 11,
|
|
"colors": [
|
|
20,
|
|
21,
|
|
19,
|
|
76
|
|
],
|
|
"edge_color": 17,
|
|
"edge": 1.4,
|
|
"seed": 21,
|
|
"ink": {
|
|
"color": 0,
|
|
"pri": "Band",
|
|
"ctl": {
|
|
"Set": 0
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Scatter": {
|
|
"x": 0,
|
|
"y": 100,
|
|
"w": 320,
|
|
"h": 90,
|
|
"colors": [
|
|
24,
|
|
77
|
|
],
|
|
"density": 7,
|
|
"seed": 22,
|
|
"ink": {
|
|
"color": 0,
|
|
"pri": "Keep",
|
|
"ctl": "Keep",
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Ellipse": {
|
|
"cx": 150,
|
|
"cy": 152,
|
|
"rx": 44,
|
|
"ry": 8,
|
|
"ink": {
|
|
"color": 77,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 2
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 60,
|
|
"y": 20,
|
|
"w": 6,
|
|
"h": 66,
|
|
"ink": {
|
|
"color": 24,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"Rect": {
|
|
"x": 60,
|
|
"y": 86,
|
|
"w": 6,
|
|
"h": 4,
|
|
"ink": {
|
|
"color": 26,
|
|
"pri": "Keep",
|
|
"ctl": {
|
|
"Set": 1
|
|
},
|
|
"hot": null
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"palette": null,
|
|
"cycles": [
|
|
{
|
|
"start": 248,
|
|
"len": 8,
|
|
"period": 4,
|
|
"reverse": true,
|
|
"active": true
|
|
}
|
|
],
|
|
"scale": {
|
|
"horizon_y": 95,
|
|
"min_scale": 0.6,
|
|
"full_y": 185
|
|
},
|
|
"spawn": [
|
|
24,
|
|
160
|
|
],
|
|
"exits": [
|
|
null,
|
|
null,
|
|
null,
|
|
0
|
|
],
|
|
"exit_flags": [
|
|
"",
|
|
"",
|
|
"",
|
|
""
|
|
],
|
|
"exit_blocked": [
|
|
"",
|
|
"",
|
|
"",
|
|
""
|
|
],
|
|
"hotspots": [
|
|
{
|
|
"name": "pipe",
|
|
"rect": [
|
|
58,
|
|
18,
|
|
10,
|
|
74
|
|
],
|
|
"poly": [],
|
|
"msgs": {
|
|
"listen": "Drip. Drip. Drip. It's in 7/8 time, somehow.",
|
|
"look": "A drainage pipe keeping its own beat."
|
|
},
|
|
"exit_to": null,
|
|
"arrive": null,
|
|
"requires_flag": "",
|
|
"blocked_text": "",
|
|
"trigger": false,
|
|
"blocks": false
|
|
},
|
|
{
|
|
"name": "bar sign",
|
|
"rect": [
|
|
246,
|
|
26,
|
|
20,
|
|
24
|
|
],
|
|
"poly": [],
|
|
"msgs": {
|
|
"look": "BAR, says the little neon, economically."
|
|
},
|
|
"exit_to": null,
|
|
"arrive": null,
|
|
"requires_flag": "",
|
|
"blocked_text": "",
|
|
"trigger": false,
|
|
"blocks": false
|
|
}
|
|
],
|
|
"props": [
|
|
{
|
|
"name": "crate stack",
|
|
"sprite": "crate",
|
|
"x": 226,
|
|
"y": 138,
|
|
"msgs": {
|
|
"look": "Shipping crates. Stenciled: PROPERTY OF NOBODY, HONEST."
|
|
},
|
|
"takeable": false,
|
|
"synonyms": "crates boxes",
|
|
"use_text": "",
|
|
"needs": "",
|
|
"wins": false,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 0,
|
|
"consumes": false,
|
|
"kills": false,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": true,
|
|
"fixed_priority": null,
|
|
"fixed_scale": null
|
|
},
|
|
{
|
|
"name": "more crates",
|
|
"sprite": "crate",
|
|
"x": 258,
|
|
"y": 130,
|
|
"msgs": {
|
|
"look": "More crates. The alley is basically municipal Tetris."
|
|
},
|
|
"takeable": false,
|
|
"synonyms": "",
|
|
"use_text": "",
|
|
"needs": "",
|
|
"wins": false,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 0,
|
|
"consumes": false,
|
|
"kills": false,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": true,
|
|
"fixed_priority": null,
|
|
"fixed_scale": null
|
|
},
|
|
{
|
|
"name": "keycard",
|
|
"sprite": "keycard",
|
|
"x": 268,
|
|
"y": 112,
|
|
"msgs": {
|
|
"look": "A precinct keycard, dropped in a hurry. Evidence locker 47, if you had to guess."
|
|
},
|
|
"takeable": true,
|
|
"synonyms": "",
|
|
"use_text": "",
|
|
"needs": "",
|
|
"wins": false,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 5,
|
|
"consumes": false,
|
|
"kills": false,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": false,
|
|
"fixed_priority": null,
|
|
"fixed_scale": null
|
|
},
|
|
{
|
|
"name": "fusebox",
|
|
"sprite": "fusebox",
|
|
"x": 96,
|
|
"y": 96,
|
|
"msgs": {
|
|
"look": "A junction box, sparking gently. Every instinct you have files a hazard report."
|
|
},
|
|
"takeable": false,
|
|
"synonyms": "",
|
|
"use_text": "You touch the sparking fusebox. For 0.4 glorious seconds you are the brightest thing on Neon Row.",
|
|
"needs": "",
|
|
"wins": false,
|
|
"requires_flag": "",
|
|
"sets_flag": "",
|
|
"dialogue": [],
|
|
"portrait": "",
|
|
"points": 0,
|
|
"consumes": false,
|
|
"kills": true,
|
|
"visible_flag": "",
|
|
"hidden_by_flag": "",
|
|
"solid": false,
|
|
"fixed_priority": 15,
|
|
"fixed_scale": null
|
|
}
|
|
],
|
|
"npcs": [],
|
|
"music": 2,
|
|
"weather": 1,
|
|
"defaults": {},
|
|
"script": "room2.rhai"
|
|
} |