@maka/maka-cli 5.91.0 → 5.93.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.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +9 -9
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.d.ts +54 -3
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.js +987 -53
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.d.ts +0 -14
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +22 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +14 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +7 -55
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +10 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +8 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +13 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +7 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +69 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js +90 -16
- package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +26 -3
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +95 -3
- package/bundle/typescript/src/commands/game/sideQuest/game.js +191 -13
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js +45 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +65 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +83 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +126 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +148 -15
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/qualities.d.ts +33 -1
- package/bundle/typescript/src/commands/game/sideQuest/qualities.js +12 -2
- package/bundle/typescript/src/commands/game/sideQuest/qualities.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +4 -4
- package/bundle/typescript/src/commands/game/sideQuest/skill-groups.d.ts +120 -0
- package/bundle/typescript/src/commands/game/sideQuest/skill-groups.js +135 -0
- package/bundle/typescript/src/commands/game/sideQuest/skill-groups.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/spells.d.ts +64 -0
- package/bundle/typescript/src/commands/game/sideQuest/spells.js +68 -0
- package/bundle/typescript/src/commands/game/sideQuest/spells.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +25 -2
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/alarmed-staff.d.ts +116 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/alarmed-staff.js +127 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/alarmed-staff.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js +123 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.d.ts +131 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js +170 -12
- package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/cloud-saves.d.ts +139 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/cloud-saves.js +307 -5
- package/bundle/typescript/src/commands/game/sideQuest/utilities/cloud-saves.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.d.ts +38 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +106 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/session-lock.js +19 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/session-lock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/web-character-generator.d.ts +22 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/web-character-generator.js +27 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/web-character-generator.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +63 -30
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,14 +7,15 @@ import { Item } from './models/item.js';
|
|
|
7
7
|
import { Room } from './models/_index.js';
|
|
8
8
|
import { Door } from './models/door.js';
|
|
9
9
|
import { SAVE_VERSION } from './types/save-file.js';
|
|
10
|
-
import { serializePlayer, restorePlayer, captureHubOverlay, applyHubOverlay, writeSaveAtomic, deleteSave, restoreItem, readSave, serializeItem, saveSlug, } from './utilities/persistence.js';
|
|
10
|
+
import { serializePlayer, restorePlayer, captureHubOverlay, applyHubOverlay, writeSaveAtomic, deleteSave, restoreItem, readSave, serializeItem, saveSlug, spillPathFor, } from './utilities/persistence.js';
|
|
11
11
|
import { heartbeatPresence, heartbeatPresenceNow } from './utilities/social.js';
|
|
12
12
|
import { presenceBeatAllowed } from './utilities/presence-gate.js';
|
|
13
|
-
import { pushSave, burnCloudSaveFor } from './utilities/cloud-saves.js';
|
|
13
|
+
import { pushSave, burnCloudSaveFor, enqueueBoundPush, flushPushQueue, writeSpill, clearSpill, hasPendingPush, } from './utilities/cloud-saves.js';
|
|
14
14
|
import { DOCWAGON_TIERS, RESUSC_FEE, DEATH_COMP } from './utilities/docwagon.js';
|
|
15
15
|
import { isFence, LIFESTYLE_TIERS } from './utilities/commerce.js';
|
|
16
16
|
import { bestFakeSin, wandRead, processArrest } from './utilities/sin.js';
|
|
17
17
|
import { tickEphemerals } from './utilities/ephemeral.js';
|
|
18
|
+
import { requestAlarmBeat, newAlarmBeatLatch, ALARM_STIMULUS } from './utilities/alarmed-staff.js';
|
|
18
19
|
import { renderMapViewport, mapCaption } from './utilities/map-view.js';
|
|
19
20
|
import { tallestRoomRows } from './utilities/room-grid.js';
|
|
20
21
|
import { renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
@@ -179,6 +180,16 @@ export default class Game {
|
|
|
179
180
|
// Quit confirmation state (see handleInput / initInputBox): armed by
|
|
180
181
|
// the first quit/exit or ESC, cleared by anything that isn't a yes.
|
|
181
182
|
_quitArmed = false;
|
|
183
|
+
/**
|
|
184
|
+
* The quit verb already sealed this runner to the grid, so the
|
|
185
|
+
* process-exit hook has nothing left to do. Set ONLY after an awaited
|
|
186
|
+
* flush -- an optimistic set would make the exit hook skip the spill
|
|
187
|
+
* write on a push that never landed, which is the one way this design
|
|
188
|
+
* can silently lose a beat. (Mutation-checked: never setting it is
|
|
189
|
+
* caught, and so is setting it without the flush.)
|
|
190
|
+
*/
|
|
191
|
+
_quitSealed = false;
|
|
192
|
+
get quitSealed() { return this._quitSealed; }
|
|
182
193
|
_escArmedAt = 0;
|
|
183
194
|
_statusBox;
|
|
184
195
|
/** The clock gutter left of the prompt, its ticker, and the face it
|
|
@@ -2872,13 +2883,45 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
2872
2883
|
this.refreshLogLabel();
|
|
2873
2884
|
Logger.getInstance().write(`Resumed save for ${save.playerName} (saved ${save.savedAt}, reason ${save.reason}).`);
|
|
2874
2885
|
}
|
|
2886
|
+
/**
|
|
2887
|
+
* IS THIS RUNNER'S LIFE THE SERVER'S? (server-only storage.)
|
|
2888
|
+
*
|
|
2889
|
+
* A bound runner has an account behind them; an anonymous one never
|
|
2890
|
+
* leaves this machine, and a bench is a scratch session with nothing
|
|
2891
|
+
* worth keeping. Every new branch in the save path gates on this, so
|
|
2892
|
+
* the anonymous route is the code that was already here -- which is
|
|
2893
|
+
* what the unmodified tripwire suites prove.
|
|
2894
|
+
*/
|
|
2895
|
+
get boundRunner() {
|
|
2896
|
+
return !this._anonymous && !this._bench && !!this._owner;
|
|
2897
|
+
}
|
|
2898
|
+
/** Where this runner's crash journal lives, when they are bound. */
|
|
2899
|
+
get spillPath() {
|
|
2900
|
+
if (!this._saveFilePath || !this.boundRunner)
|
|
2901
|
+
return undefined;
|
|
2902
|
+
return spillPathFor(dirname(this._saveFilePath), this.player.name);
|
|
2903
|
+
}
|
|
2875
2904
|
/**
|
|
2876
2905
|
* THE single save routine -- every safe beat funnels here. Guards make
|
|
2877
2906
|
* it a silent no-op anywhere a save would lie: mid-run (the "runs are
|
|
2878
2907
|
* disposable" ruling lives in the scene === _hubScene check), downed
|
|
2879
|
-
* or KO'd, mid-exchange, dead, or before init finishes.
|
|
2880
|
-
*
|
|
2881
|
-
*
|
|
2908
|
+
* or KO'd, mid-exchange, dead, or before init finishes.
|
|
2909
|
+
*
|
|
2910
|
+
* SYNCHRONOUS BY DESIGN, AND FOR TWO DIFFERENT REASONS NOW. It always
|
|
2911
|
+
* had to be, because `process.on('exit')` can run nothing
|
|
2912
|
+
* asynchronous. For a BOUND runner that same constraint is what makes
|
|
2913
|
+
* the spill the durability guarantee rather than the push: the beat
|
|
2914
|
+
* writes `<slug>.spill` synchronously and returns, and the upload
|
|
2915
|
+
* happens behind it on a queue that may never get the chance to
|
|
2916
|
+
* finish. Nothing here awaits the grid, ever -- a save beat that could
|
|
2917
|
+
* block on a network is a turn that can hang.
|
|
2918
|
+
*
|
|
2919
|
+
* PR1 KEEPS WRITING `<slug>.json` FOR BOUND RUNNERS TOO. That is
|
|
2920
|
+
* deliberate and temporary: this PR is a superset of today's
|
|
2921
|
+
* behaviour, so reverting it strands no data and loses no character.
|
|
2922
|
+
* PR2 is where the bound branch stops writing the local slot, once
|
|
2923
|
+
* the server contract is deployed and the launcher can source the menu
|
|
2924
|
+
* from it.
|
|
2882
2925
|
*/
|
|
2883
2926
|
requestSave(reason) {
|
|
2884
2927
|
const logger = Logger.getInstance();
|
|
@@ -2903,17 +2946,74 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
2903
2946
|
save.sync = prev.save.sync;
|
|
2904
2947
|
writeSaveAtomic(this._saveFilePath, save);
|
|
2905
2948
|
logger.write(`Saved (${reason}) -> ${this._saveFilePath}`);
|
|
2906
|
-
//
|
|
2907
|
-
//
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2949
|
+
// An ANONYMOUS runner's life never leaves this machine, and this
|
|
2950
|
+
// is where that stays true: no spill, no queue, no request.
|
|
2951
|
+
if (this._anonymous)
|
|
2952
|
+
return;
|
|
2953
|
+
const spill = this.spillPath;
|
|
2954
|
+
if (spill) {
|
|
2955
|
+
// THE JOURNAL, WRITTEN BEFORE THE PUSH IS EVEN ATTEMPTED. If the
|
|
2956
|
+
// grid is down -- or the process dies in the next millisecond --
|
|
2957
|
+
// this file is the beat. Ordering matters: enqueueing first and
|
|
2958
|
+
// spilling second would leave a window where a crash loses a
|
|
2959
|
+
// beat that a confirmed push had not yet covered.
|
|
2960
|
+
writeSpill(spill, save);
|
|
2961
|
+
enqueueBoundPush(this._saveFilePath, save, spill);
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
// Legacy/unbound-but-not-anonymous: the pre-existing mirror, still
|
|
2965
|
+
// fire-and-forget (cloud-saves.ts -- logged-out, offline and
|
|
2966
|
+
// expired all degrade to a silent no-op).
|
|
2967
|
+
void pushSave(this._saveFilePath, save);
|
|
2912
2968
|
}
|
|
2913
2969
|
catch (err) {
|
|
2914
2970
|
logger.error(`requestSave(${reason}) failed: ${err}`);
|
|
2915
2971
|
}
|
|
2916
2972
|
}
|
|
2973
|
+
/**
|
|
2974
|
+
* SEAL A BOUND RUNNER TO THE GRID -- the awaited counterpart of
|
|
2975
|
+
* requestSave, for the two beats where "eventually" is a bug.
|
|
2976
|
+
*
|
|
2977
|
+
* QUIT: nothing runs after this process. An unpushed beat is not
|
|
2978
|
+
* "late", it is gone until the next launch sweeps the spill -- and
|
|
2979
|
+
* the player was told their game was saved.
|
|
2980
|
+
*
|
|
2981
|
+
* JOB ACCEPT: the shared run loads this character FROM THE SERVER.
|
|
2982
|
+
* An unpushed beat means the table plays a stale snapshot of them,
|
|
2983
|
+
* which is a real bug today (the unawaited push before shared runs)
|
|
2984
|
+
* and closes here for free.
|
|
2985
|
+
*
|
|
2986
|
+
* Returns true when the server has the newest save. FALSE IS NOT A
|
|
2987
|
+
* FAILURE PATH -- it means the spill stands and the next launch
|
|
2988
|
+
* uploads it, which is ruling 2 working exactly as designed. Callers
|
|
2989
|
+
* report, they do not block the player.
|
|
2990
|
+
*
|
|
2991
|
+
* Anonymous and bench runners return true immediately: they have
|
|
2992
|
+
* nothing to seal and must never be made to wait on a network.
|
|
2993
|
+
*/
|
|
2994
|
+
async flushBoundSave(reason) {
|
|
2995
|
+
if (!this.boundRunner)
|
|
2996
|
+
return true;
|
|
2997
|
+
// Take a final beat first, so the thing we seal is the state the
|
|
2998
|
+
// player is quitting FROM rather than whatever the last autosave
|
|
2999
|
+
// happened to catch.
|
|
3000
|
+
this.requestSave(reason);
|
|
3001
|
+
if (!hasPendingPush())
|
|
3002
|
+
return true;
|
|
3003
|
+
const sealed = await flushPushQueue();
|
|
3004
|
+
if (sealed) {
|
|
3005
|
+
const spill = this.spillPath;
|
|
3006
|
+
// The queue clears the spill on a confirmed push; this is the
|
|
3007
|
+
// belt for the case where the confirmation raced the final beat.
|
|
3008
|
+
if (spill) {
|
|
3009
|
+
const current = readSave(this._saveFilePath);
|
|
3010
|
+
if (current.ok)
|
|
3011
|
+
clearSpill(spill, current.save.savedAt);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
Logger.getInstance().write(`flushBoundSave(${reason}): ${sealed ? 'sealed' : 'left in the spill'}.`);
|
|
3015
|
+
return sealed;
|
|
3016
|
+
}
|
|
2917
3017
|
buildSaveFile(reason) {
|
|
2918
3018
|
return {
|
|
2919
3019
|
version: SAVE_VERSION,
|
|
@@ -3865,7 +3965,12 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
3865
3965
|
// session's homecoming hands back to it.
|
|
3866
3966
|
if (this.pendingSharedRun) {
|
|
3867
3967
|
const { sessionID } = this.pendingSharedRun;
|
|
3868
|
-
|
|
3968
|
+
// AWAITED, unlike the solo departure below, because the shared
|
|
3969
|
+
// session loads this character FROM THE SERVER. A fire-and-forget
|
|
3970
|
+
// push here is the known stale-snapshot race: the table starts
|
|
3971
|
+
// playing a version of you that predates the beat you just took.
|
|
3972
|
+
// For an anonymous or unbound runner this returns immediately.
|
|
3973
|
+
await this.flushBoundSave('job-accept');
|
|
3869
3974
|
try {
|
|
3870
3975
|
const { SharedRunSession } = await import('./utilities/shared-run.js');
|
|
3871
3976
|
const banner = await SharedRunSession.startAsLeader(this, sessionID);
|
|
@@ -3888,6 +3993,12 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
3888
3993
|
}
|
|
3889
3994
|
// The departure save: hub state with the job still on offer -- what
|
|
3890
3995
|
// a mid-run quit resumes to (the "runs are disposable" ruling).
|
|
3996
|
+
//
|
|
3997
|
+
// NOT awaited, unlike the shared-run branch above, and the asymmetry
|
|
3998
|
+
// is deliberate: nothing loads this save from the server on a solo
|
|
3999
|
+
// run, so there is no stale-snapshot race to close -- and awaiting a
|
|
4000
|
+
// push here would put a network round trip (up to TIMEOUT_MS) in
|
|
4001
|
+
// front of every departure for no gain. The spill covers the crash.
|
|
3891
4002
|
this.requestSave('job-accept');
|
|
3892
4003
|
// CARRY THE OFFER'S HAGGLE ACROSS THE TRAVEL, BUT ONLY THIS
|
|
3893
4004
|
// OFFER'S (d4yoYPcRAtWpLtm9y: "I got a notice that I've already
|
|
@@ -4992,6 +5103,29 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
4992
5103
|
if (this._quitArmed) {
|
|
4993
5104
|
this._quitArmed = false;
|
|
4994
5105
|
if (/^(y|yes|quit|exit)$/i.test(verb)) {
|
|
5106
|
+
// THE SEAL. This is the last moment anything asynchronous can
|
|
5107
|
+
// run for this character -- ui.ts turns a null response into
|
|
5108
|
+
// process.exit(0), and the exit hook that follows can only write
|
|
5109
|
+
// the spill. So a bound runner's final beat is pushed HERE,
|
|
5110
|
+
// awaited, or it does not get pushed at all.
|
|
5111
|
+
//
|
|
5112
|
+
// The player is told, because a quit that pauses for a network
|
|
5113
|
+
// round trip with no explanation reads as a hang.
|
|
5114
|
+
if (this.boundRunner) {
|
|
5115
|
+
const logger = Logger.getInstance();
|
|
5116
|
+
logger.logWithColor(`Sealing your save to the grid...`, 'cyan');
|
|
5117
|
+
this._screen?.render();
|
|
5118
|
+
const sealed = await this.flushBoundSave('quit');
|
|
5119
|
+
if (!sealed) {
|
|
5120
|
+
// NOT an error, and worded so it does not read as one: the
|
|
5121
|
+
// spill is on disk and the next launch uploads it. Saying
|
|
5122
|
+
// nothing here would be the actual failure -- the player
|
|
5123
|
+
// would believe their evening reached the server.
|
|
5124
|
+
logger.logWithColor(`The grid didn't answer -- your progress is saved on this machine and will upload next launch.`, 'yellow');
|
|
5125
|
+
this._screen?.render();
|
|
5126
|
+
}
|
|
5127
|
+
this._quitSealed = true;
|
|
5128
|
+
}
|
|
4995
5129
|
return out(null);
|
|
4996
5130
|
}
|
|
4997
5131
|
// fall through -- the answer was a real command; play on.
|
|
@@ -5042,7 +5176,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
5042
5176
|
const command = CommandFactory.createCommand(verb, context);
|
|
5043
5177
|
if (command) {
|
|
5044
5178
|
const result = await command.execute(args);
|
|
5045
|
-
return out(this.withEphemeralTurn(actor, this.withICTurn(actor, result)));
|
|
5179
|
+
return out(this.withEphemeralTurn(actor, this.withAlarmTurn(actor, this.withICTurn(actor, result))));
|
|
5046
5180
|
}
|
|
5047
5181
|
// Typo tolerance: two straight sessions bounced on slips like "searh"
|
|
5048
5182
|
// and "maop". Reuse the same fuzzy matcher item/NPC names go through
|
|
@@ -5101,6 +5235,50 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
5101
5235
|
* utilities/ephemeral.ts decides what each kind does with its beat;
|
|
5102
5236
|
* this only carries the narration back. Only a mob ever produces any.
|
|
5103
5237
|
*/
|
|
5238
|
+
/** One beat in flight at a time (see utilities/alarmed-staff.ts). */
|
|
5239
|
+
_alarmBeat = newAlarmBeatLatch();
|
|
5240
|
+
/**
|
|
5241
|
+
* THE ALARMED SITE'S TURN, hung off the same single dispatch the host's
|
|
5242
|
+
* and the ephemerals' are -- one player action is one beat for the
|
|
5243
|
+
* people who work here.
|
|
5244
|
+
*
|
|
5245
|
+
* AN ENGINE MAPPING, NOT A RULE, in exactly the sense withICTurn says
|
|
5246
|
+
* it: SR5 writes no general mechanics for how guards react to an alarm
|
|
5247
|
+
* (asked twice, two wordings, same answer), and p.352's "delay the
|
|
5248
|
+
* runner team" is conditioned on an HTR team being en route, which in
|
|
5249
|
+
* a Zone 0 barrens it never is. Nothing here invents a response table,
|
|
5250
|
+
* a convergence rate or an aggression dial. What changed is only this:
|
|
5251
|
+
* NPC.act()'s two callers both live inside respondTo(), so a scene NPC
|
|
5252
|
+
* was mute unless the player addressed it. The engine stops muzzling
|
|
5253
|
+
* them while a site is alarmed. What any of them DOES with a beat is
|
|
5254
|
+
* the judgment it already exercises when spoken to.
|
|
5255
|
+
*
|
|
5256
|
+
* IT APPENDS NOTHING, and that is worth saying because it makes the
|
|
5257
|
+
* one thing this wrapper's POSITION was chosen for moot. The seam
|
|
5258
|
+
* agreement put this inner to withEphemeralTurn so an alarmed guard
|
|
5259
|
+
* would print above somebody eating noodles -- sound reasoning for an
|
|
5260
|
+
* appending wrapper, and irrelevant to a fire-and-forget one that
|
|
5261
|
+
* returns `result` untouched. The position is kept as agreed so the
|
|
5262
|
+
* slot exists if this ever narrates inline; the ordering argument is
|
|
5263
|
+
* recorded as not currently biting rather than quietly inherited.
|
|
5264
|
+
*
|
|
5265
|
+
* FIRE AND FORGET on the player's own ruling, offered three shapes:
|
|
5266
|
+
* reactions "arrive a moment later" while play continues at full
|
|
5267
|
+
* speed, one in flight so they never pile up. The precedent is
|
|
5268
|
+
* draftNextSceneInBackground -- never make the player wait on a model
|
|
5269
|
+
* call.
|
|
5270
|
+
*/
|
|
5271
|
+
withAlarmTurn(actor, result) {
|
|
5272
|
+
if (result === null)
|
|
5273
|
+
return null;
|
|
5274
|
+
// The primary's actions drive the site. A second human's commands
|
|
5275
|
+
// arrive through the same dispatch, and two humans each triggering a
|
|
5276
|
+
// beat would double the site's voice without doubling the site.
|
|
5277
|
+
if (actor !== this.player)
|
|
5278
|
+
return result;
|
|
5279
|
+
requestAlarmBeat(this._alarmBeat, this.scene, actor, npc => npc.respondTo(actor, ALARM_STIMULUS));
|
|
5280
|
+
return result;
|
|
5281
|
+
}
|
|
5104
5282
|
withEphemeralTurn(actor, result) {
|
|
5105
5283
|
if (result === null)
|
|
5106
5284
|
return null;
|