@maka/maka-cli 5.83.0 → 5.85.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 (81) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +0 -28
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +1 -41
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +7 -0
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +3 -0
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +62 -4
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/heal.d.ts +53 -0
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/heal.js +158 -0
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/heal.js.map +1 -0
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js +16 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js.map +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +13 -5
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +9 -6
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/sustain.d.ts +12 -0
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/sustain.js +52 -2
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/sustain.js.map +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +17 -11
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +45 -32
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +34 -1
  26. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +84 -6
  27. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +9 -2
  29. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +12 -5
  30. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +22 -0
  32. package/bundle/typescript/src/commands/game/sideQuest/game.js +148 -7
  33. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +11 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +86 -16
  36. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +16 -0
  38. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +23 -0
  39. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +8 -0
  41. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-vocab.js +6 -0
  42. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-vocab.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/utilities/bystander.d.ts +17 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/utilities/bystander.js +123 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/utilities/bystander.js.map +1 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/utilities/ephemeral.d.ts +116 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/utilities/ephemeral.js +96 -0
  48. package/bundle/typescript/src/commands/game/sideQuest/utilities/ephemeral.js.map +1 -0
  49. package/bundle/typescript/src/commands/game/sideQuest/utilities/first-aid.d.ts +124 -0
  50. package/bundle/typescript/src/commands/game/sideQuest/utilities/first-aid.js +209 -0
  51. package/bundle/typescript/src/commands/game/sideQuest/utilities/first-aid.js.map +1 -0
  52. package/bundle/typescript/src/commands/game/sideQuest/utilities/meet-location.d.ts +37 -0
  53. package/bundle/typescript/src/commands/game/sideQuest/utilities/meet-location.js +51 -0
  54. package/bundle/typescript/src/commands/game/sideQuest/utilities/meet-location.js.map +1 -0
  55. package/bundle/typescript/src/commands/game/sideQuest/utilities/perception.d.ts +76 -0
  56. package/bundle/typescript/src/commands/game/sideQuest/utilities/perception.js +84 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/utilities/perception.js.map +1 -0
  58. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +9 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +57 -0
  61. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +121 -16
  62. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/utilities/presence-gate.d.ts +21 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/utilities/presence-gate.js +28 -0
  65. package/bundle/typescript/src/commands/game/sideQuest/utilities/presence-gate.js.map +1 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.d.ts +75 -0
  67. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.js +121 -10
  68. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.d.ts +10 -0
  70. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js +55 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js.map +1 -1
  72. package/bundle/typescript/src/commands/game/sideQuest/utilities/street-gang.d.ts +17 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/utilities/street-gang.js +139 -0
  74. package/bundle/typescript/src/commands/game/sideQuest/utilities/street-gang.js.map +1 -0
  75. package/bundle/typescript/src/commands/game/sideQuest/utilities/web-character-generator.js +9 -1
  76. package/bundle/typescript/src/commands/game/sideQuest/utilities/web-character-generator.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +3 -1
  78. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  79. package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js +46 -44
  80. package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js.map +1 -1
  81. package/package.json +1 -1
@@ -9,6 +9,7 @@ import { Door } from './models/door.js';
9
9
  import { SAVE_VERSION } from './types/save-file.js';
10
10
  import { serializePlayer, restorePlayer, captureHubOverlay, applyHubOverlay, writeSaveAtomic, deleteSave, restoreItem, readSave, serializeItem, saveSlug, } from './utilities/persistence.js';
11
11
  import { heartbeatPresence, heartbeatPresenceNow } from './utilities/social.js';
12
+ import { presenceBeatAllowed } from './utilities/presence-gate.js';
12
13
  import { pushSave, burnCloudSaveFor } from './utilities/cloud-saves.js';
13
14
  import { DOCWAGON_TIERS, RESUSC_FEE, DEATH_COMP } from './utilities/docwagon.js';
14
15
  import { isFence, LIFESTYLE_TIERS } from './utilities/commerce.js';
@@ -127,6 +128,7 @@ import { JournalCommand } from './commands/journal.js';
127
128
  import { CoverCommand } from './commands/cover.js';
128
129
  import { PostureCommand, LieDownCommand, StandCommand } from './commands/posture.js';
129
130
  import { TreatCommand } from './commands/treat.js';
131
+ import { HealCommand } from './commands/heal.js';
130
132
  import { WorkCommand } from './commands/work.js';
131
133
  import { EatCommand } from './commands/eat.js';
132
134
  import { DrinkCommand } from './commands/drink.js';
@@ -529,8 +531,36 @@ export default class Game {
529
531
  }, 500);
530
532
  this._presenceEquipTimer.unref?.();
531
533
  }
534
+ /**
535
+ * Does this runner hold a LIVE SEAT right now -- riding at someone's
536
+ * table, or leading one that a real player sits at? Both sides are
537
+ * already tracked synchronously (HubLink.syncRiding fills ridingWith;
538
+ * the leader's live seats are liveOnly crew), so this needs no new
539
+ * state and no await.
540
+ */
541
+ holdsLiveSeat() {
542
+ return this.ridingWith.length > 0 || this.crew.some(m => m.ghost?.liveOnly);
543
+ }
532
544
  beatPresence() {
533
- if (!this.hasHub || this.scene !== this._hubScene)
545
+ if (!this.hasHub)
546
+ return;
547
+ // THE SEATED RUNNER DEFENDS ITS OWN PRESENCE DOC.
548
+ //
549
+ // This used to be `scene !== hubScene -> return`, on the reasonable
550
+ // ground that the run is nobody's business. But presence is not only
551
+ // the street's player list: the server reads the doc's SLUG to
552
+ // decide whose seats are still real, and a beat carrying a different
553
+ // slug dissolves an account's seats. So the one process that most
554
+ // needs to hold the doc -- a runner sitting in a live shared run --
555
+ // was the only one not beating, for the whole run. Any other beat on
556
+ // that account (a stale second terminal at its own hub, an older
557
+ // character) won the doc uncontested, and the seat was cut at
558
+ // heartbeat cadence with nothing printed on either side. Four
559
+ // reports, both sides of one table, twice in ten minutes.
560
+ //
561
+ // The rule itself lives in utilities/presence-gate.ts so it can be
562
+ // tested without a Game, a hub, or a socket.
563
+ if (!presenceBeatAllowed(this.scene === this._hubScene, this.holdsLiveSeat()))
534
564
  return;
535
565
  void heartbeatPresence(this.presencePayload());
536
566
  // The hub link rides the same pulse: a failed connect at boot must
@@ -978,6 +1008,20 @@ export default class Game {
978
1008
  // Roster invariant: entries are LIVE SHELLS only -- dismissal and
979
1009
  // death both remove the entry; persistent state (the frame, the deck)
980
1010
  // lives on Items in the player's possession, never here.
1011
+ /**
1012
+ * EPHEMERAL BODIES, and the reason this is a plain Map on the Game and
1013
+ * not part of the save shape (7ALf6u7PvTg6TZSm7): ephemeral means
1014
+ * ephemeral. Nothing here is ever written to a save file, so nothing
1015
+ * here has to be reconciled on load -- see utilities/ephemeral.ts's
1016
+ * despawnEphemerals for why the obvious mid-fight-save worry is
1017
+ * already answered upstream.
1018
+ */
1019
+ ephemerals = new Map();
1020
+ /** Corners that have already had their encounter -- once each, and
1021
+ * never saved, on the same session-only model as sweptRooms. */
1022
+ gangedRooms = new Set();
1023
+ /** Places already populated with texture -- once each, never saved. */
1024
+ peopledRooms = new Set();
981
1025
  companions = [];
982
1026
  /** REGISTERED SPRITES WAITING IN THE RESONANCE (SR5 p.254-256):
983
1027
  * folded for travel, or staged from a resumed save, waiting to
@@ -2280,6 +2324,15 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2280
2324
  CommandFactory.registerCommand('prone', LieDownCommand);
2281
2325
  CommandFactory.registerCommand('stand', StandCommand);
2282
2326
  CommandFactory.registerCommand('treat', TreatCommand);
2327
+ // FIRST AID, and the words a player actually reaches for. Every one
2328
+ // of these died at the parser before (see heal.ts): the mechanic was
2329
+ // real but only `use <medkit>` could reach it.
2330
+ CommandFactory.registerCommand('heal', HealCommand);
2331
+ CommandFactory.registerCommand('firstaid', HealCommand);
2332
+ CommandFactory.registerCommand('first', HealCommand); // "first aid"
2333
+ CommandFactory.registerCommand('bandage', HealCommand);
2334
+ CommandFactory.registerCommand('patch', HealCommand); // "patch up"
2335
+ CommandFactory.registerCommand('apply', HealCommand); // "apply first aid"
2283
2336
  CommandFactory.registerCommand('work', WorkCommand);
2284
2337
  CommandFactory.registerCommand('gig', WorkCommand);
2285
2338
  CommandFactory.registerCommand('haggle', HaggleCommand);
@@ -4471,6 +4524,80 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4471
4524
  if (player.sustainedArmor > 0) {
4472
4525
  lines.push(`{magenta-fg}Sustained: armor +${player.sustainedArmor} soak (-2 dice to everything else){/magenta-fg}`);
4473
4526
  }
4527
+ // THE MAGE'S HALF OF THE SAME QUESTION (TNf9WrZGxCmCzqA4L, scope
4528
+ // completed by ruling).
4529
+ //
4530
+ // The item asked for threads, and threads got a line -- but
4531
+ // invisibility, increased reflexes and both barrier spells bill the
4532
+ // IDENTICAL -2 through the identical sustainingPenalty, and none of
4533
+ // them appeared anywhere. Only the armor spell had a line. So the
4534
+ // fix for one tradition left the other exactly where the report
4535
+ // found it, and the mage filing the same report later was a bill
4536
+ // already in the post.
4537
+ //
4538
+ // Armor and open astral senses keep their OWN lines, immediately
4539
+ // below: each carries something this one cannot (the soak value it
4540
+ // is buying, the verb that closes it). They are therefore excluded
4541
+ // here rather than duplicated -- no line ever counts the same -2
4542
+ // twice, which is the property that makes these totals readable at
4543
+ // all.
4544
+ //
4545
+ // DUMPSHOCK IS DELIBERATELY NOT HERE. It bills the same -2, but it
4546
+ // is damage done TO you, not a working you are holding up, and
4547
+ // "Sustained: dumpshock" would tell the player they can drop it.
4548
+ // Naming it belongs with the condition monitors if it belongs
4549
+ // anywhere; that is a separate call and not this item's.
4550
+ const heldSpells = [];
4551
+ if (player.sustainedInvisibility)
4552
+ heldSpells.push('invisibility');
4553
+ if (player.sustainedReflexes)
4554
+ heldSpells.push('increased reflexes');
4555
+ // The barriers say WHERE, because a wall you forgot you put up is
4556
+ // the one that strands the team -- and both of them seal an exit
4557
+ // that the caster cannot pass either.
4558
+ if (player.sustainedBarrier) {
4559
+ heldSpells.push(`physical barrier (${player.sustainedBarrier.roomName} ${player.sustainedBarrier.direction})`);
4560
+ }
4561
+ if (player.sustainedManaBarrier) {
4562
+ heldSpells.push(`mana barrier (${player.sustainedManaBarrier.roomName} ${player.sustainedManaBarrier.direction})`);
4563
+ }
4564
+ if (heldSpells.length > 0) {
4565
+ lines.push(`{magenta-fg}✴ Sustained: ${heldSpells.join(', ')} (-${heldSpells.length * 2} dice){/magenta-fg}`);
4566
+ }
4567
+ // ACTIVE THREADS, WITHOUT ASKING (TNf9WrZGxCmCzqA4L: "instead of
4568
+ // saying 'sustain' to see the active threads, list the threads in
4569
+ // the status area").
4570
+ //
4571
+ // A sustained form is a standing -2 on every pool the technomancer
4572
+ // rolls, which makes it exactly the kind of state the HUD exists for
4573
+ // -- the player was paying that price and had to type a command to
4574
+ // remember why. It sits with the other transient states rather than
4575
+ // on a line of its own, and only when something is actually woven.
4576
+ //
4577
+ // SPRITE-HELD FORMS RIDE ALONG, because they are half the answer the
4578
+ // `sustain` ledger gives and leaving them out would send the player
4579
+ // back to the command this replaces. They are listed apart from
4580
+ // yours on purpose: a form a sprite carries costs YOU nothing (p.256
4581
+ // -- it takes the -2 instead), so folding both into one dice total
4582
+ // would be a lie about the number that matters.
4583
+ //
4584
+ // The sprite keeps its full name, level suffix and all. It is longer
4585
+ // than the HUD would like, but it is what `sustain` prints and what
4586
+ // the player must type to take the form back -- and two sprites of
4587
+ // the same type are told apart by nothing else.
4588
+ const ownThreads = player.sustainedForms;
4589
+ const spriteHeld = player === this.player
4590
+ ? this.companions
4591
+ .filter(c => c.kind === 'sprite' && (c.npc.sustainedForms?.length ?? 0) > 0)
4592
+ .map(c => `${c.npc.name} holds ${c.npc.sustainedForms.map(f => f.key).join(', ')}`)
4593
+ : [];
4594
+ if (ownThreads.length > 0 || spriteHeld.length > 0) {
4595
+ const mine = ownThreads.length > 0
4596
+ ? `${ownThreads.map(f => f.key).join(', ')} (-${ownThreads.length * 2} dice)`
4597
+ : `none yourself`;
4598
+ const carried = spriteHeld.length > 0 ? ` | ${spriteHeld.join('; ')}` : '';
4599
+ lines.push(`{magenta-fg}✴ Threads: ${mine}${carried}{/magenta-fg}`);
4600
+ }
4474
4601
  if (player.astralPerceiving) {
4475
4602
  lines.push(`{magenta-fg}✧ ASTRAL SENSES OPEN (-2 dice sustained)${hint(' -- "assense" to close')}{/magenta-fg}`);
4476
4603
  }
@@ -4562,10 +4689,20 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4562
4689
  // this hook was kept as a no-op for exactly this -- it already
4563
4690
  // fires on movement (go/lock/unlock, move.ts, ui.ts), scene
4564
4691
  // transitions, and barrier changes (solve/bypass/lock/unlock).
4565
- // Session mode: the server's per-player kind:'map' events feed the
4566
- // district overlay instead (SharedRunSession.route); the room box
4567
- // has no server-pushed path yet, so it just stays hidden in a
4568
- // shared run, same as the district box did before that wiring.
4692
+ // Session mode: the server's per-player kind:'map' AND kind:'room'
4693
+ // events feed BOTH overlays instead (SharedRunSession.route).
4694
+ //
4695
+ // This used to say the room box "has no server-pushed path yet". It
4696
+ // has had one since 2026-08-27, and the wire tap has since recorded
4697
+ // 114 room frames arriving and rendering. A comment that describes a
4698
+ // gap which was closed months earlier is not a stale detail -- it
4699
+ // tells the next reader the panel is unimplemented, which is exactly
4700
+ // the wrong place to start when the panel goes blank.
4701
+ //
4702
+ // What the panels are NOT fed by, in a shared run, is this function:
4703
+ // it returns here, which is why entering a run blanks them
4704
+ // explicitly (utilities/shared-run.ts blankRunPanels) rather than
4705
+ // leaving the hub's paint on screen to be mistaken for live.
4569
4706
  if (!this._mapBox || this.remoteSession)
4570
4707
  return;
4571
4708
  const here = this.player.currentLocation;
@@ -4912,7 +5049,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4912
5049
  static HELP_CATEGORIES = [
4913
5050
  { title: 'moving', entries: [['look'], ['go'], ['move', 'walk', 'approach'], ['run'], ['sprint'], ['climb', 'mantle', 'scale', 'clamber'], ['descend'], ['sit', 'kneel'], ['lie', 'prone'], ['stand'], ['follow'], ['unfollow'], ['map', 'exits'], ['search'], ['sneak']] },
4914
5051
  { title: 'gear', entries: [['inv', 'inventory'], ['equipment', 'eq'], ['take'], ['loot'], ['drop'], ['give'], ['store'], ['equip'], ['unequip'], ['brandish', 'draw'], ['holster', 'stow'], ['reload'], ['use'], ['read']] },
4915
- { title: 'combat', entries: [['attack'], ['kill'], ['subdue'], ['grapple', 'restrain', 'clinch'], ['struggle'], ['release'], ['cover'], ['stance'], ['surrender'], ['edge'], ['rest']] },
5052
+ { title: 'combat', entries: [['attack'], ['kill'], ['subdue'], ['grapple', 'restrain', 'clinch'], ['struggle'], ['release'], ['cover'], ['stance'], ['surrender'], ['edge'], ['rest'], ['heal', 'firstaid', 'bandage', 'patch']] },
4916
5053
  // The party layer: everyone who walks (or flies, or manifests) at
4917
5054
  // your side answers to these.
4918
5055
  // "players" folded back under crew after playtesting ("crew
@@ -4937,7 +5074,11 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4937
5074
  * -- "crew players" is the advertised path; the bare verb still
4938
5075
  * answers). Without this, the uncategorized sweep resurrects them
4939
5076
  * in an "other" bucket. */
4940
- static HIDDEN_VERBS = ['players'];
5077
+ // 'first' and 'apply' exist ONLY to catch the two-word phrasings
5078
+ // ("first aid", "apply first aid") that used to die at the parser. As
5079
+ // bare words they mean nothing, so they are claimed but not advertised
5080
+ // -- `heal` is the verb the help screen teaches.
5081
+ static HIDDEN_VERBS = ['players', 'first', 'apply'];
4941
5082
  /** Session verbs that only a play-tester (or admin) can actually use
4942
5083
  * -- the server role-gates both and 403s everyone else. Listed in
4943
5084
  * help ONLY for a confirmed play-tester; see helpCategories. `roles`