@maka/maka-cli 5.8.0 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +4 -2
  3. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +3 -2
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +14 -6
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +3 -2
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +24 -1
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +2 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +31 -4
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +16 -2
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -0
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +86 -15
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +5 -4
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +2 -1
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +5 -4
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +2 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -1
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +5 -0
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +5 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +94 -16
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.d.ts +38 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js +178 -0
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js.map +1 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +2 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +7 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.d.ts +10 -0
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js +24 -0
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js.map +1 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +2 -1
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -1
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +2 -1
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +2 -1
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +2 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -1
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +48 -4
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +3 -2
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/move.d.ts +41 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +160 -0
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js +2 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +5 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +2 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -1
  65. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +2 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -1
  67. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +3 -2
  68. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  71. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +15 -4
  72. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  73. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js +7 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +2 -1
  76. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +3 -2
  78. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +11 -1
  80. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +1 -1
  81. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +24 -13
  82. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
  83. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +2 -1
  84. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js.map +1 -1
  85. package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +4 -0
  86. package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js.map +1 -1
  87. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +37 -4
  88. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  89. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +6 -0
  90. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js.map +1 -1
  91. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js +5 -0
  92. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js.map +1 -1
  93. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +9 -2
  94. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  95. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +2 -1
  96. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js.map +1 -1
  97. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js +2 -1
  98. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js.map +1 -1
  99. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +2 -1
  100. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +1 -1
  101. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js +5 -0
  102. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
  103. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +11 -0
  104. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +138 -23
  105. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  106. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +12 -0
  107. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  108. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +10 -0
  109. package/bundle/typescript/src/commands/game/sideQuest/game.js +144 -24
  110. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  111. package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +2 -0
  112. package/bundle/typescript/src/commands/game/sideQuest/models/item.js +3 -0
  113. package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
  114. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +11 -0
  115. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +187 -6
  116. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  117. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +9 -2
  118. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +27 -5
  119. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  120. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +2 -0
  121. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +9 -1
  122. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
  123. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +12 -0
  124. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +51 -4
  125. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  126. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +28 -0
  127. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +165 -4
  128. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  129. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +76 -20
  130. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +157 -34
  131. package/bundle/typescript/src/commands/game/sideQuest/types/seed/exit-seed.d.ts +1 -0
  132. package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
  133. package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +1 -0
  134. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +1 -0
  135. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +6 -0
  136. package/bundle/typescript/src/commands/game/sideQuest/ui.js +52 -3
  137. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  138. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +18 -4
  139. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
  140. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +10 -0
  141. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +17 -0
  142. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  143. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.d.ts +36 -0
  144. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js +57 -0
  145. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js.map +1 -0
  146. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.d.ts +4 -0
  147. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js +55 -0
  148. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js.map +1 -0
  149. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +8 -7
  150. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  151. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +121 -0
  152. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +315 -0
  153. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -0
  154. package/package.json +1 -1
@@ -178,60 +178,107 @@
178
178
  },
179
179
  {
180
180
  "name": "Purple Haze Bar",
181
- "description": "The district's namesake: a smoky dive where the house special glows faintly and the booths have heard every job in Tacoma get planned. Runners drink here between runs.",
181
+ "description": "The district's namesake: a smoky dive with a long bar down one side and high-backed booths along the walls -- they've heard every job in Tacoma get planned. The house special glows faintly, a back corner sits where the light gives up, and the door lets the neon bleed in behind every arrival.",
182
182
  "roomType": "Bar",
183
+ "size": "large",
184
+ "spots": [
185
+ { "name": "door", "description": "The street door, where the neon bleeds in." },
186
+ { "name": "bar", "description": "The long counter, sticky with decades." },
187
+ { "name": "booths", "description": "High-backed booths built for quiet deals.", "tags": ["cover-partial"] },
188
+ { "name": "back corner", "description": "The dim table nobody chooses by accident." }
189
+ ],
183
190
  "exits": [
184
- { "direction": "north", "targetRoom": "Bar Backroom" }
191
+ { "direction": "north", "targetRoom": "Bar Backroom", "atSpot": "back corner" }
185
192
  ]
186
193
  },
187
194
  {
188
195
  "name": "Bar Backroom",
189
- "description": "Crates of unlabeled bottles, a card table with initials knifed into it, and a wall of photographs of runners who didn't come home. Quiet enough to actually talk.",
190
- "roomType": "Bar"
196
+ "description": "Crates of unlabeled bottles by the door, a card table with initials knifed into it, and a wall of photographs of runners who didn't come home. Quiet enough to actually talk.",
197
+ "roomType": "Bar",
198
+ "size": "small",
199
+ "spots": [
200
+ { "name": "door" },
201
+ { "name": "card table" },
202
+ { "name": "crates" }
203
+ ]
191
204
  },
192
205
  {
193
206
  "name": "Mama Wu's Noodles",
194
- "description": "Steam, chili oil, and the best laksa in the Barrens-adjacent world. Mama Wu feeds everyone and forgets nothing.",
195
- "roomType": "Restaurant"
207
+ "description": "Steam, chili oil, and the best laksa in the Barrens-adjacent world -- a bead-curtain door opening onto six stools at the counter. Mama Wu feeds everyone and forgets nothing.",
208
+ "roomType": "Restaurant",
209
+ "size": "small",
210
+ "spots": [
211
+ { "name": "door", "description": "A bead curtain and a blast of chili-scented steam." },
212
+ { "name": "counter", "description": "Six stools and the pot that never empties." }
213
+ ]
196
214
  },
197
215
  {
198
216
  "name": "Night Market",
199
- "description": "Stalls of counterfeit everything under strings of dying festival lights. The real shops open off the market's edges -- iron, circuits, medicine, and things with no barcode at all.",
217
+ "description": "From the entrance, stalls of counterfeit everything under strings of dying festival lights, and a shadowed edge where the light gives out. The real shops open off the stalls -- iron, circuits, medicine, and things with no barcode at all.",
200
218
  "roomType": "Market",
219
+ "size": "large",
220
+ "spots": [
221
+ { "name": "entrance" },
222
+ { "name": "stalls" },
223
+ { "name": "shadowed edge", "tags": ["cover-partial"] }
224
+ ],
201
225
  "exits": [
202
- { "direction": "north", "targetRoom": "Ironmonger's Arms" },
203
- { "direction": "east", "targetRoom": "Circuit Bazaar" },
204
- { "direction": "south", "targetRoom": "Doc Halloran's Clinic" }
226
+ { "direction": "north", "targetRoom": "Ironmonger's Arms", "atSpot": "stalls" },
227
+ { "direction": "east", "targetRoom": "Circuit Bazaar", "atSpot": "stalls" },
228
+ { "direction": "south", "targetRoom": "Doc Halloran's Clinic", "atSpot": "stalls" }
205
229
  ]
206
230
  },
207
231
  {
208
232
  "name": "Ironmonger's Arms",
209
- "description": "Racked steel and gun oil: Torque's shop sells serious hardware to serious people. Everything on the wall has a price and a history.",
233
+ "description": "Racked steel and gun oil: from the door, Torque's counter fronts wall racks of serious hardware for serious people. Everything on the racks has a price and a history.",
210
234
  "roomType": "Shop",
211
- "owner": "Torque"
235
+ "owner": "Torque",
236
+ "size": "small",
237
+ "spots": [
238
+ { "name": "door" },
239
+ { "name": "counter" },
240
+ { "name": "racks" }
241
+ ]
212
242
  },
213
243
  {
214
244
  "name": "Circuit Bazaar",
215
- "description": "Ratchet's electronics den -- benches of half-gutted decks, coils of optical cable, and a public host humming over the shopfront where locals trade files and gossip.",
245
+ "description": "Ratchet's electronics den -- a counter by the door, workbenches of half-gutted decks and optical cable behind it, and a public host humming over the shopfront where locals trade files and gossip.",
216
246
  "roomType": "Shop",
217
247
  "owner": "Ratchet",
218
248
  "hasNode": true,
249
+ "size": "small",
250
+ "spots": [
251
+ { "name": "door" },
252
+ { "name": "counter" },
253
+ { "name": "workbenches" }
254
+ ],
219
255
  "exits": [
220
256
  { "direction": "east", "targetRoom": "Talismonger's Hut" }
221
257
  ]
222
258
  },
223
259
  {
224
260
  "name": "Talismonger's Hut",
225
- "description": "Bundled herbs, etched copper, and the pressure of old rituals: Sister Verdigris's shop sits inside a working ward that hums against your teeth.",
261
+ "description": "Bundled herbs and etched copper crowd the doorway; at the altar table, Sister Verdigris works under the pressure of old rituals. The whole shop sits inside a working ward that hums against your teeth.",
226
262
  "roomType": "Shop",
227
263
  "owner": "Sister Verdigris",
228
- "warded": true
264
+ "warded": true,
265
+ "size": "small",
266
+ "spots": [
267
+ { "name": "doorway" },
268
+ { "name": "altar table" }
269
+ ]
229
270
  },
230
271
  {
231
272
  "name": "Doc Halloran's Clinic",
232
- "description": "A three-bed street clinic that smells of antiseptic and burnt coffee. Halloran patches runners with no questions and a running tab.",
273
+ "description": "A street clinic that smells of antiseptic and burnt coffee: three cots past the door, a supply shelf under lock and habit. Halloran patches runners with no questions and a running tab.",
233
274
  "roomType": "Clinic",
234
- "owner": "Doc Halloran"
275
+ "owner": "Doc Halloran",
276
+ "size": "small",
277
+ "spots": [
278
+ { "name": "door" },
279
+ { "name": "cots" },
280
+ { "name": "supply shelf" }
281
+ ]
235
282
  },
236
283
  {
237
284
  "name": "Rain-Chewed Alley",
@@ -244,17 +291,29 @@
244
291
  },
245
292
  {
246
293
  "name": "Union Pawnshop",
247
- "description": "Whisper's cage of secondhand everything -- fenced goods, forgotten heirlooms, and a ledger of favors nobody wants called in.",
294
+ "description": "Whisper's cage of secondhand everything: a caged counter facing the door, back shelves of fenced goods and forgotten heirlooms, and a ledger of favors nobody wants called in.",
248
295
  "roomType": "Shop",
249
- "owner": "Whisper"
296
+ "owner": "Whisper",
297
+ "size": "small",
298
+ "spots": [
299
+ { "name": "door" },
300
+ { "name": "counter" },
301
+ { "name": "back shelves" }
302
+ ]
250
303
  },
251
304
  {
252
305
  "name": "Flooded Undermarket",
253
- "description": "The old market level, shin-deep in black water. Vendors work from stilted stalls; things move under the surface that nobody names.",
306
+ "description": "The old market level, shin-deep in black water below the entrance stair. Vendors work from stilted stalls over the flood; dry platforms raft the edges, and things move under the surface that nobody names.",
254
307
  "roomType": "Market",
308
+ "size": "large",
309
+ "spots": [
310
+ { "name": "entrance stair" },
311
+ { "name": "stilted stalls" },
312
+ { "name": "dry platforms" }
313
+ ],
255
314
  "exits": [
256
- { "direction": "south", "targetRoom": "Smuggler's Tunnel" },
257
- { "direction": "west", "targetRoom": "Condemned Chapel" }
315
+ { "direction": "south", "targetRoom": "Smuggler's Tunnel", "atSpot": "stilted stalls" },
316
+ { "direction": "west", "targetRoom": "Condemned Chapel", "atSpot": "dry platforms" }
258
317
  ]
259
318
  },
260
319
  {
@@ -268,29 +327,51 @@
268
327
  },
269
328
  {
270
329
  "name": "Haze Dragons Turf",
271
- "description": "The Dragons' clubhouse: engine blocks for furniture, gang colors on every wall, and Brick holding court by the fire barrel. Respect gets you in; anything else gets you Brick.",
330
+ "description": "The Dragons' clubhouse: a watched gate, engine blocks for furniture, stacked crates along the wall, and Brick holding court by the fire barrel. Respect gets you in; anything else gets you Brick.",
272
331
  "roomType": "Gang Turf",
273
- "owner": "Brick"
332
+ "owner": "Brick",
333
+ "size": "medium",
334
+ "spots": [
335
+ { "name": "gate" },
336
+ { "name": "fire barrel" },
337
+ { "name": "stacked crates", "tags": ["cover-partial"] }
338
+ ]
274
339
  },
275
340
  {
276
341
  "name": "Condemned Chapel",
277
- "description": "A drowned chapel leaning into the flood, its pews rafted together and its saints scoured faceless. Something still sings here at the edge of hearing.",
342
+ "description": "A drowned chapel leaning into the flood: past the doorway, pews rafted together into a nave, and an altar where the saints stand scoured faceless. Something still sings here at the edge of hearing.",
278
343
  "roomType": "Chapel",
344
+ "size": "medium",
345
+ "spots": [
346
+ { "name": "doorway" },
347
+ { "name": "nave" },
348
+ { "name": "altar" }
349
+ ],
279
350
  "exits": [
280
- { "direction": "down", "targetRoom": "Chapel Crypt", "puzzle": "Crypt Grate" }
351
+ { "direction": "down", "targetRoom": "Chapel Crypt", "puzzle": "Crypt Grate", "atSpot": "altar" }
281
352
  ]
282
353
  },
283
354
  {
284
355
  "name": "Chapel Crypt",
285
- "description": "A dry vault below the waterline, older than the district above it. The reliquary niches were emptied long ago -- almost all of them.",
286
- "roomType": "Crypt"
356
+ "description": "A dry vault below the waterline, older than the district above it. From the entry stairs, reliquary niches line the walls -- emptied long ago, almost all of them.",
357
+ "roomType": "Crypt",
358
+ "size": "small",
359
+ "spots": [
360
+ { "name": "entry stairs" },
361
+ { "name": "reliquary niches" }
362
+ ]
287
363
  },
288
364
  {
289
365
  "name": "Sim Den",
290
- "description": "Rows of scavenged sim-couches wired into a matte-black host with no wireless presence at all -- hard lines only, the way the paranoid like it. The couches whisper with other people's dreams.",
366
+ "description": "Past the door, rows of scavenged sim-couches wired into a matte-black host with no wireless presence at all -- hard lines only, the way the paranoid like it. The couch rows whisper with other people's dreams.",
291
367
  "roomType": "Den",
292
368
  "hasNode": true,
293
- "nodeAccess": "wired"
369
+ "nodeAccess": "wired",
370
+ "size": "small",
371
+ "spots": [
372
+ { "name": "door" },
373
+ { "name": "couch rows" }
374
+ ]
294
375
  },
295
376
  {
296
377
  "name": "Johnson's Office",
@@ -323,6 +404,7 @@
323
404
  "player": {
324
405
  "name": "Torque",
325
406
  "startLocation": "Ironmonger's Arms",
407
+ "startSpot": "counter",
326
408
  "lifestyleChoices": [],
327
409
  "combat": {
328
410
  "body": 5,
@@ -353,6 +435,7 @@
353
435
  "player": {
354
436
  "name": "Ratchet",
355
437
  "startLocation": "Circuit Bazaar",
438
+ "startSpot": "counter",
356
439
  "lifestyleChoices": [],
357
440
  "combat": {
358
441
  "body": 2,
@@ -382,6 +465,7 @@
382
465
  "player": {
383
466
  "name": "Sister Verdigris",
384
467
  "startLocation": "Talismonger's Hut",
468
+ "startSpot": "altar table",
385
469
  "lifestyleChoices": [],
386
470
  "combat": {
387
471
  "body": 3,
@@ -412,6 +496,7 @@
412
496
  "player": {
413
497
  "name": "Doc Halloran",
414
498
  "startLocation": "Doc Halloran's Clinic",
499
+ "startSpot": "cots",
415
500
  "lifestyleChoices": [],
416
501
  "combat": {
417
502
  "body": 3,
@@ -440,6 +525,7 @@
440
525
  "player": {
441
526
  "name": "Mama Wu",
442
527
  "startLocation": "Mama Wu's Noodles",
528
+ "startSpot": "counter",
443
529
  "lifestyleChoices": [],
444
530
  "combat": {
445
531
  "body": 3,
@@ -467,6 +553,7 @@
467
553
  "player": {
468
554
  "name": "Patch",
469
555
  "startLocation": "Purple Haze Bar",
556
+ "startSpot": "bar",
470
557
  "lifestyleChoices": [],
471
558
  "combat": {
472
559
  "body": 5,
@@ -498,6 +585,7 @@
498
585
  "player": {
499
586
  "name": "Old Cray",
500
587
  "startLocation": "Purple Haze Bar",
588
+ "startSpot": "booths",
501
589
  "lifestyleChoices": [],
502
590
  "combat": {
503
591
  "body": 3,
@@ -527,6 +615,7 @@
527
615
  "player": {
528
616
  "name": "Skitter",
529
617
  "startLocation": "Night Market",
618
+ "startSpot": "stalls",
530
619
  "lifestyleChoices": [],
531
620
  "combat": {
532
621
  "body": 2,
@@ -556,6 +645,7 @@
556
645
  "player": {
557
646
  "name": "Brick",
558
647
  "startLocation": "Haze Dragons Turf",
648
+ "startSpot": "fire barrel",
559
649
  "lifestyleChoices": [],
560
650
  "combat": {
561
651
  "body": 6,
@@ -586,6 +676,7 @@
586
676
  "player": {
587
677
  "name": "Whisper",
588
678
  "startLocation": "Union Pawnshop",
679
+ "startSpot": "counter",
589
680
  "lifestyleChoices": [],
590
681
  "combat": {
591
682
  "body": 2,
@@ -615,6 +706,7 @@
615
706
  "player": {
616
707
  "name": "Static Ghost",
617
708
  "startLocation": "Sim Den",
709
+ "startSpot": "couch rows",
618
710
  "lifestyleChoices": [],
619
711
  "combat": {
620
712
  "body": 3,
@@ -638,6 +730,7 @@
638
730
  "player": {
639
731
  "name": "The Drowned Choir",
640
732
  "startLocation": "Condemned Chapel",
733
+ "startSpot": "nave",
641
734
  "lifestyleChoices": [],
642
735
  "combat": {
643
736
  "body": 4,
@@ -677,6 +770,7 @@
677
770
  "rating": "Rare",
678
771
  "weight": 2,
679
772
  "room": "Ironmonger's Arms",
773
+ "atSpot": "racks",
680
774
  "owner": "Torque"
681
775
  },
682
776
  {
@@ -690,6 +784,7 @@
690
784
  "rating": "Standard",
691
785
  "weight": 2.5,
692
786
  "room": "Ironmonger's Arms",
787
+ "atSpot": "racks",
693
788
  "owner": "Torque"
694
789
  },
695
790
  {
@@ -703,6 +798,7 @@
703
798
  "rating": "Standard",
704
799
  "weight": 4,
705
800
  "room": "Ironmonger's Arms",
801
+ "atSpot": "racks",
706
802
  "owner": "Torque"
707
803
  },
708
804
  {
@@ -716,6 +812,7 @@
716
812
  "rating": "Common",
717
813
  "weight": 0.4,
718
814
  "room": "Ironmonger's Arms",
815
+ "atSpot": "racks",
719
816
  "owner": "Torque"
720
817
  },
721
818
  {
@@ -729,6 +826,7 @@
729
826
  "rating": "Standard",
730
827
  "weight": 2,
731
828
  "room": "Circuit Bazaar",
829
+ "atSpot": "counter",
732
830
  "owner": "Ratchet"
733
831
  },
734
832
  {
@@ -742,6 +840,7 @@
742
840
  "rating": "Standard",
743
841
  "weight": 0.1,
744
842
  "room": "Circuit Bazaar",
843
+ "atSpot": "counter",
745
844
  "owner": "Ratchet"
746
845
  },
747
846
  {
@@ -755,6 +854,7 @@
755
854
  "rating": "Rare",
756
855
  "weight": 6,
757
856
  "room": "Circuit Bazaar",
857
+ "atSpot": "workbenches",
758
858
  "owner": "Ratchet"
759
859
  },
760
860
  {
@@ -768,6 +868,7 @@
768
868
  "rating": "Standard",
769
869
  "weight": 0.3,
770
870
  "room": "Talismonger's Hut",
871
+ "atSpot": "altar table",
771
872
  "owner": "Sister Verdigris"
772
873
  },
773
874
  {
@@ -781,6 +882,7 @@
781
882
  "rating": "Standard",
782
883
  "weight": 0.2,
783
884
  "room": "Talismonger's Hut",
885
+ "atSpot": "altar table",
784
886
  "owner": "Sister Verdigris"
785
887
  },
786
888
  {
@@ -794,6 +896,7 @@
794
896
  "rating": "Rare",
795
897
  "weight": 1.5,
796
898
  "room": "Doc Halloran's Clinic",
899
+ "atSpot": "supply shelf",
797
900
  "owner": "Doc Halloran"
798
901
  },
799
902
  {
@@ -807,6 +910,7 @@
807
910
  "rating": "Standard",
808
911
  "weight": 1,
809
912
  "room": "Doc Halloran's Clinic",
913
+ "atSpot": "supply shelf",
810
914
  "owner": "Doc Halloran"
811
915
  },
812
916
  {
@@ -820,6 +924,7 @@
820
924
  "rating": "Standard",
821
925
  "weight": 0.05,
822
926
  "room": "Doc Halloran's Clinic",
927
+ "atSpot": "supply shelf",
823
928
  "owner": "Doc Halloran"
824
929
  },
825
930
  {
@@ -833,6 +938,7 @@
833
938
  "rating": "Standard",
834
939
  "weight": 0.5,
835
940
  "room": "Mama Wu's Noodles",
941
+ "atSpot": "counter",
836
942
  "owner": "Mama Wu"
837
943
  },
838
944
  {
@@ -846,6 +952,7 @@
846
952
  "rating": "Common",
847
953
  "weight": 0.5,
848
954
  "room": "Mama Wu's Noodles",
955
+ "atSpot": "counter",
849
956
  "owner": "Mama Wu"
850
957
  },
851
958
  {
@@ -859,6 +966,7 @@
859
966
  "rating": "Common",
860
967
  "weight": 0.4,
861
968
  "room": "Mama Wu's Noodles",
969
+ "atSpot": "counter",
862
970
  "owner": "Mama Wu"
863
971
  },
864
972
  {
@@ -872,6 +980,7 @@
872
980
  "rating": "Common",
873
981
  "weight": 0.3,
874
982
  "room": "Mama Wu's Noodles",
983
+ "atSpot": "counter",
875
984
  "owner": "Mama Wu"
876
985
  },
877
986
  {
@@ -885,6 +994,7 @@
885
994
  "rating": "Common",
886
995
  "weight": 0.3,
887
996
  "room": "Purple Haze Bar",
997
+ "atSpot": "bar",
888
998
  "owner": "Patch"
889
999
  },
890
1000
  {
@@ -898,6 +1008,7 @@
898
1008
  "rating": "Common",
899
1009
  "weight": 0.4,
900
1010
  "room": "Purple Haze Bar",
1011
+ "atSpot": "bar",
901
1012
  "owner": "Patch"
902
1013
  },
903
1014
  {
@@ -911,6 +1022,7 @@
911
1022
  "rating": "Standard",
912
1023
  "weight": 0.3,
913
1024
  "room": "Purple Haze Bar",
1025
+ "atSpot": "bar",
914
1026
  "owner": "Patch"
915
1027
  },
916
1028
  {
@@ -924,6 +1036,7 @@
924
1036
  "rating": "Common",
925
1037
  "weight": 0.8,
926
1038
  "room": "Night Market",
1039
+ "atSpot": "stalls",
927
1040
  "owner": "Skitter"
928
1041
  },
929
1042
  {
@@ -937,6 +1050,7 @@
937
1050
  "rating": "Common",
938
1051
  "weight": 0.1,
939
1052
  "room": "Night Market",
1053
+ "atSpot": "stalls",
940
1054
  "owner": "Skitter"
941
1055
  },
942
1056
  {
@@ -950,6 +1064,7 @@
950
1064
  "rating": "Common",
951
1065
  "weight": 0.1,
952
1066
  "room": "Night Market",
1067
+ "atSpot": "stalls",
953
1068
  "owner": "Skitter"
954
1069
  },
955
1070
  {
@@ -963,7 +1078,8 @@
963
1078
  "texture": "Hand-Bound",
964
1079
  "rating": "Common",
965
1080
  "weight": 0.6,
966
- "room": "Bar Backroom"
1081
+ "room": "Bar Backroom",
1082
+ "atSpot": "card table"
967
1083
  },
968
1084
  {
969
1085
  "name": "Crypt Notes",
@@ -977,6 +1093,7 @@
977
1093
  "rating": "Common",
978
1094
  "weight": 0.3,
979
1095
  "room": "Union Pawnshop",
1096
+ "atSpot": "back shelves",
980
1097
  "owner": "Whisper"
981
1098
  },
982
1099
  {
@@ -991,6 +1108,7 @@
991
1108
  "weight": 0.2,
992
1109
  "currencyAmount": 120,
993
1110
  "room": "Union Pawnshop",
1111
+ "atSpot": "counter",
994
1112
  "owner": "Whisper"
995
1113
  },
996
1114
  {
@@ -1004,6 +1122,7 @@
1004
1122
  "rating": "Standard",
1005
1123
  "weight": 0.3,
1006
1124
  "room": "Union Pawnshop",
1125
+ "atSpot": "back shelves",
1007
1126
  "owner": "Whisper"
1008
1127
  },
1009
1128
  {
@@ -1031,6 +1150,7 @@
1031
1150
  "weight": 0.2,
1032
1151
  "currencyAmount": 200,
1033
1152
  "room": "Haze Dragons Turf",
1153
+ "atSpot": "stacked crates",
1034
1154
  "owner": "Brick"
1035
1155
  },
1036
1156
  {
@@ -1059,7 +1179,8 @@
1059
1179
  "rating": "Rare",
1060
1180
  "weight": 0,
1061
1181
  "plane": "matrix",
1062
- "room": "Sim Den"
1182
+ "room": "Sim Den",
1183
+ "atSpot": "couch rows"
1063
1184
  },
1064
1185
  {
1065
1186
  "name": "Drowned Reliquary",
@@ -1071,7 +1192,8 @@
1071
1192
  "texture": "Fused Silver",
1072
1193
  "rating": "Rare",
1073
1194
  "weight": 0.4,
1074
- "room": "Chapel Crypt"
1195
+ "room": "Chapel Crypt",
1196
+ "atSpot": "reliquary niches"
1075
1197
  },
1076
1198
  {
1077
1199
  "name": "Tarnished Credstick",
@@ -1084,7 +1206,8 @@
1084
1206
  "rating": "Common",
1085
1207
  "weight": 0.2,
1086
1208
  "currencyAmount": 300,
1087
- "room": "Chapel Crypt"
1209
+ "room": "Chapel Crypt",
1210
+ "atSpot": "reliquary niches"
1088
1211
  }
1089
1212
  ],
1090
1213
  "puzzles": [
@@ -4,4 +4,5 @@ export interface IExitSeedJson {
4
4
  keyRequired?: string;
5
5
  doorName?: string;
6
6
  puzzle?: string;
7
+ atSpot?: string;
7
8
  }
@@ -22,4 +22,5 @@ export interface IItemSeedJson {
22
22
  transferable?: boolean;
23
23
  plane?: 'meat' | 'matrix' | 'astral';
24
24
  price?: number;
25
+ atSpot?: string;
25
26
  }
@@ -44,6 +44,7 @@ export interface ICombatSeedJson {
44
44
  export interface IPlayerSeedJson {
45
45
  name: string;
46
46
  startLocation: string;
47
+ startSpot?: string;
47
48
  lifestyleChoices: LifestyleChoice[];
48
49
  startingItems?: IItemSeedJson[];
49
50
  combat?: ICombatSeedJson;
@@ -22,4 +22,5 @@ export interface IPuzzleSeedJson {
22
22
  * (e.g. a purely social one, solvable only by knowing the actual answer).
23
23
  */
24
24
  bypassType?: PuzzleBypassType;
25
+ atSpot?: string;
25
26
  }
@@ -11,6 +11,12 @@ export interface IRoomSeedJson {
11
11
  hasNode?: boolean;
12
12
  nodeAccess?: 'wireless' | 'wired';
13
13
  warded?: boolean;
14
+ spots?: {
15
+ name: string;
16
+ description?: string;
17
+ tags?: ('cover-partial' | 'cover-good')[];
18
+ }[];
19
+ size?: 'small' | 'medium' | 'large';
14
20
  supportedLifestyles?: {
15
21
  residenceTiers?: string[];
16
22
  employmentTags?: string[];
@@ -7,10 +7,46 @@ import { getArchetype, outfitArchetype } from './archetypes.js';
7
7
  import { STANCE_CYCLE } from './models/player.js';
8
8
  import { AutoFight } from './utilities/auto-fight.js';
9
9
  import { TERMINAL_RESET_SEQUENCE } from './utilities/terminal-reset.js';
10
+ // Windows Terminal draws astral-plane emoji (📞 call icon, 📍 HUD pin,
11
+ // 👥 companions, 💀) TWO columns wide, but blessed's 2015-era unicode
12
+ // tables predate emoji being classified wide and count them as ONE. The
13
+ // one-cell drift on every icon row pushed box borders sideways into the
14
+ // next panel and left smartCSR's partial redraws smearing stray orphan
15
+ // characters (two player screenshots; the text-presentation ⚔, one cell
16
+ // on both sides, stayed flush). Teaching charWidth the pictograph blocks
17
+ // fixes the model: strWidth (wrapping) and the cell renderer both route
18
+ // through this same export. BMP dingbats stay untouched -- the terminal
19
+ // really does draw those narrow.
20
+ // TWO patches, and they only work as a pair (blessed's own draw comment:
21
+ // "eat the next character which should be a space due to parseContent's
22
+ // behavior"): charWidth makes wrapping count 2 and the emitter skip the
23
+ // filler cell; chars.all makes parseContent actually INSERT that \x03
24
+ // filler after each pictograph. Either alone leaves rows misaligned or
25
+ // eats a real character.
26
+ {
27
+ const uni = blessed.unicode;
28
+ const origCharWidth = uni.charWidth;
29
+ uni.charWidth = function (text, i) {
30
+ const point = typeof text === 'number' ? text : uni.codePointAt(text, i ?? 0);
31
+ if (point >= 0x1F300 && point <= 0x1FAFF)
32
+ return 2;
33
+ return origCharWidth.call(this, text, i);
34
+ };
35
+ // U+1F300-1FAFF as surrogate pairs. Wrapped in a fresh outer group so
36
+ // parseContent's "$1\x03" replacement still means "the whole match".
37
+ const pictographs = '\\ud83c[\\udf00-\\udfff]|\\ud83d[\\udc00-\\udfff]|\\ud83e[\\udc00-\\udeff]';
38
+ uni.chars.all = new RegExp(`(${uni.chars.all.source}|${pictographs})`, 'g');
39
+ }
10
40
  export async function main(playerName, sceneSeed, options) {
11
41
  // Create a screen object
12
42
  const screen = blessed.screen({
13
43
  smartCSR: true,
44
+ // Without this, blessed strips every astral-plane character (all
45
+ // modern emoji -- the 📞 call icon, companion icons) to "?" during
46
+ // content parsing, no matter what the terminal can render. BMP
47
+ // symbols like ⚔ slipped through, which is why a session showed
48
+ // swords but question-mark phones.
49
+ fullUnicode: true,
14
50
  title: 'Side Quest Adventure Game',
15
51
  });
16
52
  // TERMINAL HYGIENE: blessed arms xterm modes (mouse tracking, alt
@@ -135,10 +171,16 @@ export async function main(playerName, sceneSeed, options) {
135
171
  // asked while it works. Cyan: blessed has no dim tag and gray
136
172
  // is banned; cyan is already this game's machine voice.
137
173
  // pushLine, not Logger -- the transcript line above already
138
- // recorded it. "clear" is exempt (echo-then-wipe is noise).
139
- if (input.trim().length > 0 && input.trim().split(' ')[0] !== 'clear') {
174
+ // recorded it. "clear" is exempt (echo-then-wipe is noise),
175
+ // and "say" keeps only the blank separator -- your words
176
+ // already land as the yellow speech line, and "> say hoi"
177
+ // above "hoi" read as a stutter (playtest note).
178
+ const echoVerb = input.trim().split(' ')[0]?.toLowerCase();
179
+ if (input.trim().length > 0 && echoVerb !== 'clear') {
140
180
  game.logBox.pushLine('');
141
- game.logBox.pushLine(`{cyan-fg}> ${input.trim()}{/cyan-fg}`);
181
+ if (echoVerb !== 'say') {
182
+ game.logBox.pushLine(`{cyan-fg}> ${input.trim()}{/cyan-fg}`);
183
+ }
142
184
  game.followLog();
143
185
  screen.render();
144
186
  }
@@ -159,6 +201,13 @@ export async function main(playerName, sceneSeed, options) {
159
201
  if (input.split(' ')[0] === 'clear') {
160
202
  game.logBox.setContent('');
161
203
  game.metaBox.setContent('');
204
+ // Blessed repositions a box's LABEL on 'scroll' events --
205
+ // setContent('') resets the scroll base without firing one,
206
+ // stranding the title mid-box (screenshot bug: "Game Log"
207
+ // sitting at the bottom after "clear"). Scrolling home
208
+ // fires the event and the label snaps back to the border.
209
+ game.logBox.scrollTo(0);
210
+ game.metaBox.scrollTo(0);
162
211
  }
163
212
  if (response)
164
213
  gameLog.transcript(response);