@maka/maka-cli 5.68.0 → 5.70.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 (116) hide show
  1. package/bundle/typescript/package.json +13 -2
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/abandon.d.ts +30 -0
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/abandon.js +54 -0
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/abandon.js.map +1 -0
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +5 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +12 -4
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +29 -0
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +109 -2
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +6 -0
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/climb.d.ts +48 -0
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/climb.js +107 -0
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/climb.js.map +1 -0
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +7 -3
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js.map +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +2 -2
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js +12 -2
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +22 -18
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +68 -30
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js +17 -4
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +12 -2
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +12 -1
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js +8 -6
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js.map +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/move.d.ts +11 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +107 -14
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.js +5 -4
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.js.map +1 -1
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js +14 -1
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +33 -2
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/standby.d.ts +40 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/standby.js +91 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/standby.js.map +1 -0
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js +10 -11
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js.map +1 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +4 -2
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +12 -5
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.js +41 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.js.map +1 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js +5 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
  58. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +6 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +112 -8
  60. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +6 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +85 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/game.js +214 -3
  65. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +52 -0
  67. package/bundle/typescript/src/commands/game/sideQuest/models/item.js +101 -0
  68. package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +67 -0
  70. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +167 -66
  71. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  72. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +161 -5
  73. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +245 -24
  74. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +97 -0
  76. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +134 -5
  77. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +1 -0
  79. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +2 -0
  80. package/bundle/typescript/src/commands/game/sideQuest/types/repro.d.ts +42 -0
  81. package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +12 -0
  82. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +6 -0
  83. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +7 -0
  84. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +19 -1
  85. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  86. package/bundle/typescript/src/commands/game/sideQuest/utilities/crash-evidence.d.ts +53 -0
  87. package/bundle/typescript/src/commands/game/sideQuest/utilities/crash-evidence.js +106 -0
  88. package/bundle/typescript/src/commands/game/sideQuest/utilities/crash-evidence.js.map +1 -0
  89. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +41 -1
  90. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +52 -4
  91. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -1
  92. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.d.ts +1 -1
  93. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +45 -7
  94. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js.map +1 -1
  95. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.d.ts +26 -1
  96. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js +45 -1
  97. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js.map +1 -1
  98. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +9 -1
  99. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  100. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.d.ts +46 -0
  101. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +90 -5
  102. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js.map +1 -1
  103. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +70 -0
  104. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +168 -22
  105. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  106. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.d.ts +33 -0
  107. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +46 -6
  108. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  109. package/bundle/typescript/src/tools/ai/ai.class.d.ts +53 -0
  110. package/bundle/typescript/src/tools/ai/ai.class.js +152 -7
  111. package/bundle/typescript/src/tools/ai/ai.class.js.map +1 -1
  112. package/bundle/typescript/src/tools/https/https.class.d.ts +1 -1
  113. package/bundle/typescript/src/tools/https/https.class.js +29 -4
  114. package/bundle/typescript/src/tools/https/https.class.js.map +1 -1
  115. package/bundle/typescript/src/tools/https/https.interface.d.ts +8 -0
  116. package/package.json +13 -2
@@ -376,6 +376,25 @@ export default class Game {
376
376
  * so this refuses it rather than quietly destroying it.
377
377
  */
378
378
  dismissSpriteToResonance(entry: (typeof this.companions)[number]): boolean;
379
+ /**
380
+ * STANDBY (SR5 p.256, a registered sprite TASK): "tells the sprite to
381
+ * return to the Resonance but come when you call it. This keeps the
382
+ * sprite from giving away your presence or getting in harm's way."
383
+ * Costs ONE task.
384
+ *
385
+ * The middle option dismissal made visible. Dismissing is free at the
386
+ * door and costs everything the sprite owes you (p.250); standby
387
+ * costs one and keeps the rest, which is the whole reason the book
388
+ * sells it. Without this the only way to park a sprite was to spend
389
+ * its entire usefulness, and a player who wanted it out of sight for
390
+ * one room paid for that with every favour it had left.
391
+ *
392
+ * Returns false when there is no task to spend with. A registered
393
+ * sprite that owes you nothing cannot pay for standby -- and does not
394
+ * need to, because dismissing it releases nothing it still had. The
395
+ * caller says that out loud rather than silently doing the other one.
396
+ */
397
+ standbySpriteToResonance(entry: (typeof this.companions)[number]): boolean;
379
398
  /**
380
399
  * CALL half of Call/Dismiss Sprite (p.250): "You call a registered
381
400
  * sprite you have waiting in the Resonance."
@@ -693,6 +712,12 @@ export default class Game {
693
712
  */
694
713
  runStartedAt?: Date;
695
714
  forfeitArmed: boolean;
715
+ /** Set by "abandon": the next taxi WALKS OUT instead of running an
716
+ * errand (mYTCzJbsdZxYsTsZo). The reversible trip is the default and
717
+ * the irreversible one is asked for -- Notoriety, a forfeited payout
718
+ * and a destroyed scene should require the deliberate word, and a
719
+ * round trip you can undo by getting back in the cab should not. */
720
+ walkOutArmed: boolean;
696
721
  /**
697
722
  * The meet and the wrap-up, triggered by the player's first/next real
698
723
  * interaction with the client (talk-to, a connected call, a give).
@@ -811,6 +836,66 @@ export default class Game {
811
836
  * state (a persona still jacked in, an astral projection, a sneak,
812
837
  * open combat bookkeeping) is settled on the way home.
813
838
  */
839
+ /**
840
+ * THE JOB YOU LEFT STANDING (mYTCzJbsdZxYsTsZo: "I'd like it to not
841
+ * fail the run -- I should be able to come and go to the job as I
842
+ * need (to get new gear)").
843
+ *
844
+ * Held in memory only, and deliberately NOT saved. Mid-run state
845
+ * never is -- "runs are disposable, a quit resumes to the hub with
846
+ * the job back on offer" -- so an errand is SESSION-SCOPED and needs
847
+ * no save-schema change. Quit mid-errand and you get exactly today's
848
+ * behaviour rather than a half-restored scene from a save that was
849
+ * never designed to hold one.
850
+ *
851
+ * The rooms survive because something finally holds them: returnToHub
852
+ * orphans a run "wholesale" only because nothing kept a reference.
853
+ */
854
+ private _openRun?;
855
+ /** Is there a job waiting for the runner to come back to it? */
856
+ get hasOpenRun(): boolean;
857
+ /** The room an open run is waiting in, for the surfaces that say so. */
858
+ get openRunSceneName(): string | undefined;
859
+ /**
860
+ * AN ERRAND IS NOT A HOMECOMING, and that is the whole design.
861
+ *
862
+ * returnToHub is a RITUAL, not a journey. Walking its full length
863
+ * twice per gear run would hand a player, free and on demand: a full
864
+ * stun heal at High lifestyle ("the shower runs hot until the run is
865
+ * a rumor"), every vendor's shelves restocked, a fresh legwork pull
866
+ * from every contact, the busking cycle reset, and a rerolled
867
+ * for-hire board -- while ALSO charging rent again and burning a
868
+ * twelfth of the DocWagon contract, whose year "ticks in homecomings".
869
+ * Wrong in both directions at once: the exploit and the punishment
870
+ * are the same line of code.
871
+ *
872
+ * So this is its own path rather than a flag on that one. Written as
873
+ * a separate method ON PURPOSE: parameterising the ritual would mean
874
+ * every future line added to it silently joins the errand too, and
875
+ * the failure would be invisible -- a restock nobody notices is not a
876
+ * crash. Here, anything an errand should do has to be written here.
877
+ *
878
+ * WHAT IT DOES: state hygiene the cab demands (a persona cannot ride
879
+ * in a taxi), companions fold and unfold as on any trip, and the
880
+ * scene swaps. Nothing else.
881
+ *
882
+ * WHAT IT DOES NOT DO, all of it deliberate: no client settlement, no
883
+ * crew settlement or disband, no jobsRun tally, no lifestyle upkeep,
884
+ * no restock, no rumor, no gig advance, no hire reroll, no DocWagon
885
+ * tick, no legwork refresh -- and NO per-job reset. A RETURN LEG IS
886
+ * NOT A NEW JOB: Edge does not refill, haggledThisJob does not clear,
887
+ * First Impression does not re-arm. You come back as you left, which
888
+ * is the difference between fetching a gun and starting over.
889
+ */
890
+ leaveRunForErrand(): string | undefined;
891
+ /** Back to the job you left standing. */
892
+ resumeOpenRun(): string | undefined;
893
+ /** Cross-plane and combat state that must not ride in a cab, shared
894
+ * by both legs of an errand. A persona has no body to put in a taxi
895
+ * and a fight does not travel. */
896
+ private settleTravelState;
897
+ /** The panel refresh both errand legs need. */
898
+ private refreshHubUi;
814
899
  private returnToHub;
815
900
  /**
816
901
  * KNIGHT ERRANT CHECKPOINT (SINs & getting caught, ruling 2026-08-24;
@@ -101,12 +101,15 @@ import { DecompileCommand } from './commands/decompile.js';
101
101
  import { ThreadCommand } from './commands/thread.js';
102
102
  import { RegisterCommand } from './commands/register.js';
103
103
  import { SpritesCommand } from './commands/sprites.js';
104
+ import { StandbyCommand } from './commands/standby.js';
105
+ import { AbandonCommand } from './commands/abandon.js';
104
106
  import { UnravelCommand } from './commands/unravel.js';
105
107
  import { OrderCommand } from './commands/order.js';
106
108
  import { DeployCommand, AgentCommand, RecallCommand } from './commands/companions.js';
107
109
  import { DeckCommand, LoadProgramCommand, UnloadProgramCommand } from './commands/deck.js';
108
110
  import { getProgram } from './utilities/programs.js';
109
111
  import { MoveCommand, RunCommand, SprintCommand } from './commands/move.js';
112
+ import { ClimbCommand, DescendCommand } from './commands/climb.js';
110
113
  import { HintsCommand } from './commands/hints.js';
111
114
  import { hint } from './utilities/hints.js';
112
115
  import { camerasLive } from './utilities/surveillance.js';
@@ -1015,6 +1018,41 @@ export default class Game {
1015
1018
  this.dismissCompanion(entry, 'dismissed to the Resonance');
1016
1019
  return true;
1017
1020
  }
1021
+ /**
1022
+ * STANDBY (SR5 p.256, a registered sprite TASK): "tells the sprite to
1023
+ * return to the Resonance but come when you call it. This keeps the
1024
+ * sprite from giving away your presence or getting in harm's way."
1025
+ * Costs ONE task.
1026
+ *
1027
+ * The middle option dismissal made visible. Dismissing is free at the
1028
+ * door and costs everything the sprite owes you (p.250); standby
1029
+ * costs one and keeps the rest, which is the whole reason the book
1030
+ * sells it. Without this the only way to park a sprite was to spend
1031
+ * its entire usefulness, and a player who wanted it out of sight for
1032
+ * one room paid for that with every favour it had left.
1033
+ *
1034
+ * Returns false when there is no task to spend with. A registered
1035
+ * sprite that owes you nothing cannot pay for standby -- and does not
1036
+ * need to, because dismissing it releases nothing it still had. The
1037
+ * caller says that out loud rather than silently doing the other one.
1038
+ */
1039
+ standbySpriteToResonance(entry) {
1040
+ if (entry.kind !== 'sprite' || !entry.registered || !entry.spriteType)
1041
+ return false;
1042
+ const owed = entry.services ?? 0;
1043
+ if (owed < 1)
1044
+ return false;
1045
+ this._pendingSpriteRedeploy.push({
1046
+ spriteType: entry.spriteType,
1047
+ level: entry.force,
1048
+ // The task it spends going quiet. The REST survive, which is the
1049
+ // entire difference between this and dismissal.
1050
+ tasks: owed - 1,
1051
+ name: entry.npc.name,
1052
+ });
1053
+ this.dismissCompanion(entry, 'sent to standby');
1054
+ return true;
1055
+ }
1018
1056
  /**
1019
1057
  * CALL half of Call/Dismiss Sprite (p.250): "You call a registered
1020
1058
  * sprite you have waiting in the Resonance."
@@ -1770,6 +1808,12 @@ export default class Game {
1770
1808
  // Taxi warn-once (player ruling): leaving with the pay unclaimed
1771
1809
  // arms this; the second "call taxi" forfeits. Mirrors _quitArmed.
1772
1810
  forfeitArmed = false;
1811
+ /** Set by "abandon": the next taxi WALKS OUT instead of running an
1812
+ * errand (mYTCzJbsdZxYsTsZo). The reversible trip is the default and
1813
+ * the irreversible one is asked for -- Notoriety, a forfeited payout
1814
+ * and a destroyed scene should require the deliberate word, and a
1815
+ * round trip you can undo by getting back in the cab should not. */
1816
+ walkOutArmed = false;
1773
1817
  /**
1774
1818
  * The meet and the wrap-up, triggered by the player's first/next real
1775
1819
  * interaction with the client (talk-to, a connected call, a give).
@@ -2078,6 +2122,10 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2078
2122
  CommandFactory.registerCommand('register', RegisterCommand);
2079
2123
  // The sprite database (p.256-259) -- reference, the way "spells" is.
2080
2124
  CommandFactory.registerCommand('sprites', SpritesCommand);
2125
+ // STANDBY (p.256): the registered-sprite task that parks one for 1.
2126
+ CommandFactory.registerCommand('standby', StandbyCommand);
2127
+ // Walk out for good -- the taxi's irreversible half (mYTCzJbs...).
2128
+ CommandFactory.registerCommand('abandon', AbandonCommand);
2081
2129
  // Kill Complex Form (p.250) -- the Resonance counterpart to dispel.
2082
2130
  CommandFactory.registerCommand('unravel', UnravelCommand);
2083
2131
  // "At" spots (utilities/spots.ts): walk/move/approach are one
@@ -2086,6 +2134,10 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2086
2134
  CommandFactory.registerCommand('walk', MoveCommand);
2087
2135
  CommandFactory.registerCommand('move', MoveCommand);
2088
2136
  CommandFactory.registerCommand('approach', MoveCommand);
2137
+ // Vertical travel by name (commands/climb.ts) -- shortcuts over
2138
+ // move, which already crosses levels.
2139
+ CommandFactory.registerCommand('climb', ClimbCommand);
2140
+ CommandFactory.registerCommand('descend', DescendCommand);
2089
2141
  CommandFactory.registerCommand('run', RunCommand);
2090
2142
  CommandFactory.registerCommand('sprint', SprintCommand);
2091
2143
  CommandFactory.registerCommand('hints', HintsCommand);
@@ -2754,6 +2806,124 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2754
2806
  * state (a persona still jacked in, an astral projection, a sneak,
2755
2807
  * open combat bookkeeping) is settled on the way home.
2756
2808
  */
2809
+ /**
2810
+ * THE JOB YOU LEFT STANDING (mYTCzJbsdZxYsTsZo: "I'd like it to not
2811
+ * fail the run -- I should be able to come and go to the job as I
2812
+ * need (to get new gear)").
2813
+ *
2814
+ * Held in memory only, and deliberately NOT saved. Mid-run state
2815
+ * never is -- "runs are disposable, a quit resumes to the hub with
2816
+ * the job back on offer" -- so an errand is SESSION-SCOPED and needs
2817
+ * no save-schema change. Quit mid-errand and you get exactly today's
2818
+ * behaviour rather than a half-restored scene from a save that was
2819
+ * never designed to hold one.
2820
+ *
2821
+ * The rooms survive because something finally holds them: returnToHub
2822
+ * orphans a run "wholesale" only because nothing kept a reference.
2823
+ */
2824
+ _openRun;
2825
+ /** Is there a job waiting for the runner to come back to it? */
2826
+ get hasOpenRun() {
2827
+ return this._openRun !== undefined;
2828
+ }
2829
+ /** The room an open run is waiting in, for the surfaces that say so. */
2830
+ get openRunSceneName() {
2831
+ return this._openRun?.room.name;
2832
+ }
2833
+ /**
2834
+ * AN ERRAND IS NOT A HOMECOMING, and that is the whole design.
2835
+ *
2836
+ * returnToHub is a RITUAL, not a journey. Walking its full length
2837
+ * twice per gear run would hand a player, free and on demand: a full
2838
+ * stun heal at High lifestyle ("the shower runs hot until the run is
2839
+ * a rumor"), every vendor's shelves restocked, a fresh legwork pull
2840
+ * from every contact, the busking cycle reset, and a rerolled
2841
+ * for-hire board -- while ALSO charging rent again and burning a
2842
+ * twelfth of the DocWagon contract, whose year "ticks in homecomings".
2843
+ * Wrong in both directions at once: the exploit and the punishment
2844
+ * are the same line of code.
2845
+ *
2846
+ * So this is its own path rather than a flag on that one. Written as
2847
+ * a separate method ON PURPOSE: parameterising the ritual would mean
2848
+ * every future line added to it silently joins the errand too, and
2849
+ * the failure would be invisible -- a restock nobody notices is not a
2850
+ * crash. Here, anything an errand should do has to be written here.
2851
+ *
2852
+ * WHAT IT DOES: state hygiene the cab demands (a persona cannot ride
2853
+ * in a taxi), companions fold and unfold as on any trip, and the
2854
+ * scene swaps. Nothing else.
2855
+ *
2856
+ * WHAT IT DOES NOT DO, all of it deliberate: no client settlement, no
2857
+ * crew settlement or disband, no jobsRun tally, no lifestyle upkeep,
2858
+ * no restock, no rumor, no gig advance, no hire reroll, no DocWagon
2859
+ * tick, no legwork refresh -- and NO per-job reset. A RETURN LEG IS
2860
+ * NOT A NEW JOB: Edge does not refill, haggledThisJob does not clear,
2861
+ * First Impression does not re-arm. You come back as you left, which
2862
+ * is the difference between fetching a gun and starting over.
2863
+ */
2864
+ leaveRunForErrand() {
2865
+ const hub = this._hubScene;
2866
+ if (!hub || this.scene === hub)
2867
+ return undefined;
2868
+ this._openRun = {
2869
+ scene: this.scene,
2870
+ room: this.player.currentLocation,
2871
+ spot: this.player.atSpot,
2872
+ };
2873
+ const foldLines = this.foldCompanionsForTravel();
2874
+ this.settleTravelState();
2875
+ this.scene = hub;
2876
+ this.scene.addRoom(this._homeRoom);
2877
+ this.connectHomeRoom(hub.determineStartRoom());
2878
+ this.player.currentLocation = this._homeRoom;
2879
+ this.player.cameFrom = undefined;
2880
+ const arrivalLines = this.unfoldCompanionsOnArrival();
2881
+ this.refreshHubUi();
2882
+ return [...foldLines, ...arrivalLines].join('\n');
2883
+ }
2884
+ /** Back to the job you left standing. */
2885
+ resumeOpenRun() {
2886
+ const open = this._openRun;
2887
+ if (!open)
2888
+ return undefined;
2889
+ const foldLines = this.foldCompanionsForTravel();
2890
+ this.settleTravelState();
2891
+ this.scene = open.scene;
2892
+ this.player.currentLocation = open.room;
2893
+ // AFTER currentLocation: its setter clears atSpot on every room
2894
+ // change, so the order here is load-bearing (same trap as
2895
+ // scene-factory's stationing).
2896
+ this.player.atSpot = open.spot;
2897
+ this.player.cameFrom = undefined;
2898
+ this._openRun = undefined;
2899
+ const arrivalLines = this.unfoldCompanionsOnArrival();
2900
+ this.refreshHubUi();
2901
+ return [...foldLines, ...arrivalLines].join('\n');
2902
+ }
2903
+ /** Cross-plane and combat state that must not ride in a cab, shared
2904
+ * by both legs of an errand. A persona has no body to put in a taxi
2905
+ * and a fight does not travel. */
2906
+ settleTravelState() {
2907
+ this.player.plane = 'meat';
2908
+ this.player.bodyRoom = undefined;
2909
+ this.player.simMode = undefined;
2910
+ this.player.astralTicks = 0;
2911
+ this.player.activeDeck = undefined;
2912
+ this.player.riggedDrone = undefined;
2913
+ this.player.astralPerceiving = false;
2914
+ this.player.sneaking = false;
2915
+ this.player.inExchange = false;
2916
+ this.player.combatOpponent = undefined;
2917
+ this.player.activeCallPartner = undefined;
2918
+ this.autoFight.cancelPending();
2919
+ }
2920
+ /** The panel refresh both errand legs need. */
2921
+ refreshHubUi() {
2922
+ this.updateEquipment(this.player.equipment);
2923
+ this.updateInventory(this.player.inventory, capitalCase(this.scene.getCurrencyType()));
2924
+ this.updateExits(this.player.currentLocation);
2925
+ this.updateStatus();
2926
+ }
2757
2927
  returnToHub() {
2758
2928
  const hub = this._hubScene;
2759
2929
  if (!hub)
@@ -2785,6 +2955,25 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2785
2955
  this.player.astralTicks = 0;
2786
2956
  this.player.activeDeck = undefined;
2787
2957
  this.player.riggedDrone = undefined;
2958
+ // THE JOB'S NEGOTIATION ENDS WITH THE JOB (d4yoYPcRAtWpLtm9y).
2959
+ //
2960
+ // haggledThisJob is per-JOB and was only ever cleared on DEPARTURE
2961
+ // (applyDepartureNeeds -> refreshEdge). Coming home cleared nothing,
2962
+ // so a runner who pushed on the number at this job's meet carried
2963
+ // the flag back to the hub -- and launchPendingRun's offer-window
2964
+ // capture then restored that stale true over the next job's fresh
2965
+ // clear, refusing their first real haggle with "you already pushed
2966
+ // on the number once this job."
2967
+ //
2968
+ // Cleared HERE rather than papering over it at the capture, because
2969
+ // the flag's meaning is "this job", and the moment the job ends is
2970
+ // when that stops being true. Anything set afterwards belongs to
2971
+ // the next offer, which is exactly what the capture wants to keep.
2972
+ //
2973
+ // Deliberately NOT refreshEdge(): Edge refills on departure, fed
2974
+ // and slept (SR5e p.56), and refilling it on arrival home would
2975
+ // hand back a resource the next run is supposed to start without.
2976
+ this.player.haggledThisJob = false;
2788
2977
  this.player.astralPerceiving = false;
2789
2978
  this.player.sneaking = false;
2790
2979
  this.player.inExchange = false;
@@ -3335,6 +3524,28 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3335
3524
  // The departure save: hub state with the job still on offer -- what
3336
3525
  // a mid-run quit resumes to (the "runs are disposable" ruling).
3337
3526
  this.requestSave('job-accept');
3527
+ // CARRY THE OFFER'S HAGGLE ACROSS THE TRAVEL, BUT ONLY THIS
3528
+ // OFFER'S (d4yoYPcRAtWpLtm9y: "I got a notice that I've already
3529
+ // pushed the number once, however I haven't").
3530
+ //
3531
+ // activateRunScene below refreshes per-job state, which clears
3532
+ // haggledThisJob -- correct, a new job is a new negotiation. This
3533
+ // capture/restore exists because the offer itself has a haggle
3534
+ // window at the hub ("call <fixer>" delivers it), and a push made
3535
+ // there must not be forgotten by the drive over.
3536
+ //
3537
+ // THE BUG WAS THAT THE CAPTURE COULD NOT TELL WHOSE HAGGLE IT WAS.
3538
+ // haggledThisJob survives a finished run -- returnToHub clears
3539
+ // neither it nor Edge -- so a runner who haggled at LAST job's meet
3540
+ // came back to the hub still flagged, accepted a new job, and this
3541
+ // line captured that stale true and faithfully restored it over the
3542
+ // fresh clear. Their first haggle of the new job was then refused
3543
+ // with "you already pushed on the number once this job", which was
3544
+ // false, and the meet had just told them the rate was negotiable.
3545
+ //
3546
+ // Fixed at the source rather than here: returnToHub now ends the
3547
+ // job's negotiation with the job, so anything still set when a ride
3548
+ // is called genuinely belongs to the offer in hand.
3338
3549
  const haggledAtOffer = this.player.haggledThisJob;
3339
3550
  try {
3340
3551
  const banner = await this.activateRunScene(seed, tier, { startInHideout: false });
@@ -4355,7 +4566,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4355
4566
  * future command can't silently fall out of help.
4356
4567
  */
4357
4568
  static HELP_CATEGORIES = [
4358
- { title: 'moving', entries: [['look'], ['go'], ['move', 'walk', 'approach', 'run', 'sprint'], ['sit', 'kneel'], ['lie', 'prone'], ['stand'], ['follow', 'unfollow'], ['map', 'exits'], ['search'], ['sneak']] },
4569
+ { title: 'moving', entries: [['look'], ['go'], ['move', 'walk', 'approach', 'run', 'sprint'], ['climb', 'descend'], ['sit', 'kneel'], ['lie', 'prone'], ['stand'], ['follow', 'unfollow'], ['map', 'exits'], ['search'], ['sneak']] },
4359
4570
  { title: 'gear', entries: [['inv', 'inventory'], ['equipment', 'eq'], ['take'], ['drop'], ['give'], ['store'], ['equip'], ['unequip'], ['brandish', 'draw'], ['holster', 'stow'], ['reload'], ['use'], ['read']] },
4360
4571
  { title: 'combat', entries: [['attack', 'kill'], ['subdue'], ['grapple', 'restrain', 'clinch'], ['struggle'], ['release'], ['cover'], ['stance'], ['surrender'], ['edge'], ['rest']] },
4361
4572
  // The party layer: everyone who walks (or flies, or manifests) at
@@ -4367,9 +4578,9 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4367
4578
  { title: 'matrix', entries: [['jack'], ['deck'], ['load'], ['unload'], ['hack'], ['mark'], ['snoop'], ['overwatch', 'os'], ['pan'], ['ar'], ['aros'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
4368
4579
  // The Emerged get their own shelf (player request: "there MUST be
4369
4580
  // resonance" -- matrix is the place, Resonance is the talent).
4370
- { title: 'resonance', entries: [['compile', 'decompile'], ['thread'], ['unravel'], ['register'], ['sprites']] },
4581
+ { title: 'resonance', entries: [['compile', 'decompile'], ['thread'], ['unravel'], ['register'], ['standby'], ['sprites']] },
4371
4582
  { title: 'social', entries: [['say'], ['emote', 'me'], ['talk-to'], ['call'], ['end-call', 'end', 'hangup'], ['accept'], ['decline', 'deny', 'refuse'], ['haggle', 'negotiate'], ['persuade', 'convince'], ['ask'], ['contacts']] },
4372
- { title: 'street', entries: [['browse', 'buy'], ['sell'], ['catalog'], ['source', 'procure'], ['work', 'gig'], ['jobs'], ['journal'], ['perform'], ['palm'], ['docwagon'], ['treat'], ['eat'], ['drink'], ['lifestyle'], ['pay']] },
4583
+ { title: 'street', entries: [['abandon'], ['browse', 'buy'], ['sell'], ['catalog'], ['source', 'procure'], ['work', 'gig'], ['jobs'], ['journal'], ['perform'], ['palm'], ['docwagon'], ['treat'], ['eat'], ['drink'], ['lifestyle'], ['pay']] },
4373
4584
  { title: 'barriers', entries: [['pick'], ['breach'], ['dispel', 'dispell'], ['bluff'], ['unlock'], ['lock']] },
4374
4585
  { title: 'character', entries: [['sheet', 'stats'], ['advance'], ['qualities', 'quality'], ['sin', 'sins', 'papers'], ['initiate', 'submerge'], ['install', 'chrome']] },
4375
4586
  { title: 'session', entries: [['help'], ['hints'], ['time', 'clock'], ['note'], ['backlog'], ['roles'], ['clear'], ['quit', 'exit']] },