@maka/maka-cli 5.382.0 → 5.383.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/commands/command-registry.js +9 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.js +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/follow.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +4 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +8 -2
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +16 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +3 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/earshot.js +26 -4
- package/bundle/typescript/src/commands/game/sideQuest/utilities/npc-authorization.js +27 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +38 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.383.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/commands/command-registry.ts
|
|
2
2
|
import { CommandFactory } from '../factories/command-factory.js';
|
|
3
3
|
import { Logger } from '../utilities/logger.js';
|
|
4
|
-
import { authorizeNpcAction } from '../utilities/npc-authorization.js';
|
|
4
|
+
import { authorizeNpcAction, clientHoldsTheMeet } from '../utilities/npc-authorization.js';
|
|
5
5
|
export class CommandRegistry {
|
|
6
6
|
scene;
|
|
7
7
|
constructor(scene) {
|
|
@@ -74,6 +74,14 @@ export class CommandRegistry {
|
|
|
74
74
|
return { args, refusal: verdict.reason ?? `That is not yours to do.` };
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
// The meet's Johnson keeps his seat (npc-authorization.ts).
|
|
78
|
+
if (declaring.isMovement() && !this.scene.isHumanControlled(actor)) {
|
|
79
|
+
const held = clientHoldsTheMeet(this.scene, actor);
|
|
80
|
+
if (held) {
|
|
81
|
+
Logger.getInstance().write(`Refused "${commandString.trim()}" from ${actor.name}: the client keeps his seat.`);
|
|
82
|
+
return { args, refusal: held };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
77
85
|
return { command, args };
|
|
78
86
|
}
|
|
79
87
|
}
|
|
@@ -46,6 +46,16 @@ export class Command {
|
|
|
46
46
|
// whether or not it also remembered to say so here.
|
|
47
47
|
return this.humanOnly ? 'human-only' : this.npcPolicy;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* THIS VERB MOVES THE ACTOR'S OWN BODY -- across the room (move, run,
|
|
51
|
+
* sprint, fly), out of it (go) or after someone (follow). Read by
|
|
52
|
+
* CommandRegistry.parse for AI-driven actors only: a Johnson holding a
|
|
53
|
+
* meet keeps his seat (npc-authorization.ts clientHoldsTheMeet).
|
|
54
|
+
*/
|
|
55
|
+
static movesActor = false;
|
|
56
|
+
static isMovement() {
|
|
57
|
+
return this.movesActor;
|
|
58
|
+
}
|
|
49
59
|
static verb;
|
|
50
60
|
static description; // Make this static
|
|
51
61
|
actor;
|
|
@@ -124,6 +124,7 @@ export function dragFollowers(scene, mover, origin, logger) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
export class FollowCommand extends Command {
|
|
127
|
+
static movesActor = true;
|
|
127
128
|
static verb = 'follow';
|
|
128
129
|
static description = 'Tail another runner: "follow <name>" and their room changes take you with them. Bare "follow" (or "unfollow") peels off. It breaks on its own if you get caught in a fight, fall behind, or they leave the run. Same room, same plane, real players only -- NPC companions already heel by nature ("order" sends them).';
|
|
129
130
|
/** The scene's other humans -- the only legal targets. */
|
|
@@ -96,6 +96,7 @@ export class GoCommand extends Command {
|
|
|
96
96
|
return false;
|
|
97
97
|
return GoCommand.HEELS_ON[c.kind](plane);
|
|
98
98
|
}
|
|
99
|
+
static movesActor = true;
|
|
99
100
|
static verb = 'go';
|
|
100
101
|
static description = 'Move in the specified direction ("go back" retraces your last move). Fleeing someone you just fought hands them one free parting shot. Jacked in, the grid sees no distance: "go <any room>" jumps your persona straight there ("map" lights the icons). Inside a host there is no distance to cross at all -- "exit" first.';
|
|
101
102
|
/** See MoveCommand.execute: going through a door is a walk too, and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
2
|
import { hint } from '../utilities/hints.js';
|
|
3
3
|
import { capitalCase } from 'change-case';
|
|
4
|
-
import { planeTintItemName,
|
|
4
|
+
import { planeTintItemName, jobMark } from '../utilities/planes.js';
|
|
5
5
|
import { dataDevices } from '../utilities/data-library.js';
|
|
6
6
|
/**
|
|
7
7
|
* On-demand inventory panel, replacing the always-on blessed sidebar box
|
|
@@ -76,7 +76,7 @@ export class InventoryCommand extends Command {
|
|
|
76
76
|
const tinted = item.plane === 'meat'
|
|
77
77
|
? rarityTint(item.rating, planeTintItemName(item, 'meat'))
|
|
78
78
|
: planeTintItemName(item, 'meat');
|
|
79
|
-
let line = ` ${CARRIED_MARK}${
|
|
79
|
+
let line = ` ${CARRIED_MARK}${jobMark(this.scene, item)}${tinted}`;
|
|
80
80
|
// x N for a stack, the same shape the currency tag below uses.
|
|
81
81
|
if (item.quantity > 1)
|
|
82
82
|
line += ` �${item.quantity}`;
|
|
@@ -130,7 +130,7 @@ export class InventoryCommand extends Command {
|
|
|
130
130
|
if (libraries.length > 0) {
|
|
131
131
|
lines.push('', `{light-blue-fg}On your devices:{/light-blue-fg}`);
|
|
132
132
|
for (const device of libraries) {
|
|
133
|
-
lines.push(` ${device.name}: ${device.files.map(f => `${
|
|
133
|
+
lines.push(` ${device.name}: ${device.files.map(f => `${jobMark(this.scene, f)}${planeTintItemName(f, 'meat')}`).join(', ')}`);
|
|
134
134
|
}
|
|
135
135
|
const legend = hint(`("transfer <file> to <device>" moves one; "transfer <file> to chip" writes it to physical media.)`);
|
|
136
136
|
if (legend)
|
|
@@ -45,6 +45,7 @@ import { onTheWall, midClimbRefusal } from '../utilities/climb-state.js';
|
|
|
45
45
|
* verb that leaves a room.
|
|
46
46
|
*/
|
|
47
47
|
export class MoveCommand extends Command {
|
|
48
|
+
static movesActor = true;
|
|
48
49
|
static verb = 'walk';
|
|
49
50
|
static description = 'Cross the room to a named spot or person: "move to bar", "move to Brick" (also "walk"/"approach"; "run to"/"sprint to" are the fast crossings). "move south 2" paces you across the room by cells instead -- use "go" to leave the room entirely. Bare "move" lists the room\'s spots. You must share a spot with someone to talk or trade with them.';
|
|
50
51
|
/** Which movement mode this registration answers to (see subclasses). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
2
|
import { hint } from '../utilities/hints.js';
|
|
3
|
-
import { planeTintItemName, emphasizeFind,
|
|
3
|
+
import { planeTintItemName, emphasizeFind, jobMark, fileReach } from '../utilities/planes.js';
|
|
4
4
|
import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
5
5
|
import { perceptionTest, matrixSearchTest, matrixSearchSeconds, MATRIX_SEARCH } from '../utilities/perception.js';
|
|
6
6
|
import { gridVicinity } from '../utilities/grid-reach.js';
|
|
@@ -194,7 +194,7 @@ export class SearchCommand extends Command {
|
|
|
194
194
|
if (files.length === 0) {
|
|
195
195
|
return `The archive is indexed and EMPTY -- ${hostLabel(where)} keeps nothing worth the trip. ${clock}`;
|
|
196
196
|
}
|
|
197
|
-
const list = files.map(i => `${
|
|
197
|
+
const list = files.map(i => `${jobMark(this.scene, i)}${emphasizeFind(planeTintItemName(i, actor.plane))}`).join(', ');
|
|
198
198
|
const seal = where.hostCracked
|
|
199
199
|
? hint(` ("take" them -- the seal is already broken.)`)
|
|
200
200
|
: hint(` (sealed in the host's data vault: "hack" the node to break the seal, then "take".)`);
|
|
@@ -206,7 +206,7 @@ export class SearchCommand extends Command {
|
|
|
206
206
|
const sealedElsewhere = files.length > 0 && where.hasNode;
|
|
207
207
|
return `Nothing loose on ${where.name}'s grid. ${clock}${sealedElsewhere ? hint(` (${hostLabel(where)} keeps its files INSIDE -- "enter" it and search from in there.)`) : ''}`;
|
|
208
208
|
}
|
|
209
|
-
const list = loose.map(i => `${
|
|
209
|
+
const list = loose.map(i => `${jobMark(this.scene, i)}${emphasizeFind(planeTintItemName(i, actor.plane))}`).join(', ');
|
|
210
210
|
return `Loose on ${where.name}'s grid: ${list}. ${clock}${hint(` ("take" rides them out on your deck.)`)}`;
|
|
211
211
|
}
|
|
212
212
|
async execute(_args) {
|
|
@@ -303,7 +303,7 @@ export class SearchCommand extends Command {
|
|
|
303
303
|
// Plain lowercase for the log/AI history; the SCREEN list is tinted
|
|
304
304
|
// per plane (see planeTintItemName) so paydata reads as data.
|
|
305
305
|
const itemsList = found.map(i => i.name.toLowerCase()).join(', ');
|
|
306
|
-
const displayList = found.map(i => `${
|
|
306
|
+
const displayList = found.map(i => `${jobMark(this.scene, i)}${emphasizeFind(planeTintItemName(i, this.actor.plane))}`).join(', ');
|
|
307
307
|
this.logger.write(`SearchCommand: ${this.actor.name} found the following items: ${itemsList}`);
|
|
308
308
|
this.actor.performAction('search', itemsList);
|
|
309
309
|
description += `You search the area and find: ${displayList}`;
|
|
@@ -153,7 +153,8 @@ export class TalkCommand extends Command {
|
|
|
153
153
|
if (insideWithMe.includes(target)) {
|
|
154
154
|
await speaker.hear(speaker, `(to ${target.name}) ${message}`);
|
|
155
155
|
await target.hear(speaker, `(privately) ${message}`);
|
|
156
|
-
|
|
156
|
+
// The echo above IS the confirmation (see the meat branch below).
|
|
157
|
+
return '';
|
|
157
158
|
}
|
|
158
159
|
// "At" spots: conversation needs shared ground (see
|
|
159
160
|
// utilities/spots.ts) -- the bartender doesn't lean across the room.
|
|
@@ -174,7 +175,12 @@ export class TalkCommand extends Command {
|
|
|
174
175
|
// Face time with the run's CLIENT is THE MEET (or the wrap-up) --
|
|
175
176
|
// see Game.onClientInteraction (terms / lump-sum settlement).
|
|
176
177
|
const clientBeat = this.game?.onClientInteraction(target.name);
|
|
177
|
-
|
|
178
|
+
// ONE LINE PER THING SAID (86LjwvK7xSbXhWXGi: "this repeated text can
|
|
179
|
+
// be removed: You talk to Mr. Vellum privately: ..."). The speaker's
|
|
180
|
+
// own hear() above already printed "Maka: (to Mr. Vellum) ...", so a
|
|
181
|
+
// reply quoting the same words back was the same sentence twice. The
|
|
182
|
+
// reply carries only what the echo cannot: the meet's beat.
|
|
183
|
+
return clientBeat ?? '';
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
//# sourceMappingURL=talk.js.map
|
|
@@ -3319,5 +3319,20 @@
|
|
|
3319
3319
|
// hub boot (reaching a save that already slid) and after a shared
|
|
3320
3320
|
// run's homecoming. A move that cannot land now is retried next time;
|
|
3321
3321
|
// a session with no districtSeedLoader never moves.
|
|
3322
|
-
|
|
3322
|
+
// 1.195.0 (2026-10-04): THE MEET, CLEANED UP (four live-table reports).
|
|
3323
|
+
// - The run's client holds his seat until the job is settled: his AI's
|
|
3324
|
+
// move/approach/run/sprint/fly/go/follow are refused with a reason
|
|
3325
|
+
// (npc-authorization.ts clientHoldsTheMeet; Command.movesActor), except
|
|
3326
|
+
// inside a fight (CWMrB2aNyL4hhrjyG).
|
|
3327
|
+
// - A body nudged onto an exact cell (shoulderAside, yieldDoorway) still
|
|
3328
|
+
// hears and is heard: earshot reads an exact cell as a place, so the
|
|
3329
|
+
// shoved Johnson is no longer deaf to `say` (xurFSiqNtRNbXA88A).
|
|
3330
|
+
// - talk-to/tell no longer quote the words back under the speaker's own
|
|
3331
|
+
// echo (86LjwvK7xSbXhWXGi).
|
|
3332
|
+
// - `inv`/`search` mark job leads with ☆ -- a page spelling a scene
|
|
3333
|
+
// device's code, or a device's own key -- beside the objective's ★
|
|
3334
|
+
// (nfrBW27eRRxstJ8uC).
|
|
3335
|
+
// - The HUD's holstered line drops its `-- "brandish"` tip
|
|
3336
|
+
// (MkxamPxmsSyonR3wC).
|
|
3337
|
+
export const ENGINE_VERSION = '1.195.0';
|
|
3323
3338
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -9273,8 +9273,12 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
9273
9273
|
? ` {red-fg}[JAMMED -- reboot]{/red-fg}`
|
|
9274
9274
|
: weaponItem.ammo === 0 ? ` {red-fg}[DRY]{/red-fg}` : ` [${weaponItem.ammo} rds]`)
|
|
9275
9275
|
: '';
|
|
9276
|
+
// NO VERB COACHING IN THE HUD (MkxamPxmsSyonR3wC: "get rid of this
|
|
9277
|
+
// line in the status bar: -- "brandish""). The status bar is state;
|
|
9278
|
+
// the `brandish` hint already rides the equip/holster replies and
|
|
9279
|
+
// `equipment`, where it answers the moment the question comes up.
|
|
9276
9280
|
if (player.plane === 'meat' && weaponItem && !player.weaponDrawn) {
|
|
9277
|
-
return `{white-fg}Holstered: ${weaponItem.name}${ammoNote}
|
|
9281
|
+
return `{white-fg}Holstered: ${weaponItem.name}${ammoNote}{/white-fg}`;
|
|
9278
9282
|
}
|
|
9279
9283
|
// White, not gray: gray is ANSI bright-black and illegible on
|
|
9280
9284
|
// stock dark palettes -- the same ruling that moved the HUD
|
|
@@ -2613,7 +2613,9 @@ staff, so codes, passphrases, and lock combinations are exactly the thing you we
|
|
|
2613
2613
|
THEM to solve -- say so plainly when they ask, and send them to find it rather than
|
|
2614
2614
|
handing them a number you'd be making up. If you genuinely don't know a thing, say so plainly and point at who might. You
|
|
2615
2615
|
front half the pay at the meet and settle the rest on delivery (the engine moves the money
|
|
2616
|
-
-- never haggle it yourself unless they invoke a negotiation)
|
|
2616
|
+
-- never haggle it yourself unless they invoke a negotiation).
|
|
2617
|
+
You chose this venue and you HOLD YOUR SEAT in it: never move, walk, approach, follow or
|
|
2618
|
+
leave -- the runner comes to you. Turn, lean, gesture or speak instead.`
|
|
2617
2619
|
: '';
|
|
2618
2620
|
// ---------- prompt ----------
|
|
2619
2621
|
const prompt = `
|
|
@@ -3180,6 +3180,10 @@ export class Player extends AbstractPlayer {
|
|
|
3180
3180
|
* rather than leaving you drawn at the old one. NPCs keep re-deriving --
|
|
3181
3181
|
* a picket or a crowd is placed by spot on purpose (NPC overrides).
|
|
3182
3182
|
*/
|
|
3183
|
+
/** An exact cell somebody PUT this body on -- a step, a shove, a
|
|
3184
|
+
* flight -- as opposed to a drift seat pinned only so it is drawn in
|
|
3185
|
+
* one place (pinDerivedSeat). earshot.ts reads the first as a place. */
|
|
3186
|
+
get placedOnCell() { return this._atCell !== undefined && !this._cellPinnedFromSeat; }
|
|
3183
3187
|
get pinsDerivedSeat() { return true; }
|
|
3184
3188
|
pinDerivedSeat(cell) {
|
|
3185
3189
|
this.atCell = cell;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sameSpot, spotOf, spotsActive, isPerchSpot, OPEN_FLOOR, CONVERSATION_M, actorDistanceMeters } from './spots.js';
|
|
1
|
+
import { sameSpot, spotOf, spotsActive, isPerchSpot, OPEN_FLOOR, CONVERSATION_M, actorDistanceMeters, onTheFloor } from './spots.js';
|
|
2
2
|
/**
|
|
3
3
|
* WHO CAN OVERHEAR A MURMUR (playtest, Deditri: "a private place for
|
|
4
4
|
* calls"). A call is audible to the room, and that is correct and
|
|
@@ -123,6 +123,28 @@ export function hearingRangeMeters(listener) {
|
|
|
123
123
|
const bonus = listener.augBonus('hearing') + listener.qualityBonus('perception');
|
|
124
124
|
return Math.max(SPEECH_METERS, SPEECH_METERS + bonus);
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* HAS A PLACE IN THE ROOM: a named spot, OR an exact cell on the floor.
|
|
128
|
+
*
|
|
129
|
+
* THE SECOND HALF IS THE FIX (xurFSiqNtRNbXA88A: "I'm getting 'Nobody's
|
|
130
|
+
* close enough to catch that', but I'm right next to Mr. Vellum"). The
|
|
131
|
+
* runner stepped into Vellum's square; the excuse-me rule (spots.ts
|
|
132
|
+
* shoulderAside) moved him one cell over and, as it and yieldDoorway both
|
|
133
|
+
* do on purpose, left him on that EXACT cell with no spot name. This
|
|
134
|
+
* module read "no spot name" as "circulating", and a circulating listener
|
|
135
|
+
* is passing through and hears no murmur -- so a Johnson drawn one square
|
|
136
|
+
* from the runner was deaf to everything said to him for the rest of the
|
|
137
|
+
* meet. An exact cell is the most precise position the engine has
|
|
138
|
+
* (seatingIn seats it first), and distance is measured from it.
|
|
139
|
+
*
|
|
140
|
+
* A DRIFT SEAT IS STILL NOT A PLACE: a circulating runner's derived seat
|
|
141
|
+
* is pinned to atCell only so it is drawn in one spot (Player.
|
|
142
|
+
* pinDerivedSeat), and placedOnCell says no to it -- the Kessler rule
|
|
143
|
+
* below stands (speech-range.test.ts).
|
|
144
|
+
*/
|
|
145
|
+
function standsSomewhere(actor) {
|
|
146
|
+
return spotOf(actor) !== undefined || (onTheFloor(actor) && actor.placedOnCell);
|
|
147
|
+
}
|
|
126
148
|
/**
|
|
127
149
|
* CAN THIS LISTENER HEAR ORDINARY SPEECH FROM THERE -- the whole of the
|
|
128
150
|
* `say` rule, in one place so the verb, the NPC fan-out and any future
|
|
@@ -176,7 +198,7 @@ export function canHearShout(speaker, listener) {
|
|
|
176
198
|
const room = speaker.currentLocation;
|
|
177
199
|
if (!spotsActive(room))
|
|
178
200
|
return true;
|
|
179
|
-
if (
|
|
201
|
+
if (!standsSomewhere(speaker) || !standsSomewhere(listener))
|
|
180
202
|
return true;
|
|
181
203
|
const reach = SHOUT_METERS + (hearingRangeMeters(listener) - SPEECH_METERS);
|
|
182
204
|
return actorDistanceMeters(room, speaker, listener) <= reach;
|
|
@@ -186,10 +208,10 @@ export function canHearSpeech(speaker, listener) {
|
|
|
186
208
|
if (!spotsActive(room))
|
|
187
209
|
return true;
|
|
188
210
|
// The speaker is unplaced: they are moving among everyone.
|
|
189
|
-
if (
|
|
211
|
+
if (!standsSomewhere(speaker))
|
|
190
212
|
return true;
|
|
191
213
|
// The listener is unplaced: they are passing through, not standing here.
|
|
192
|
-
if (
|
|
214
|
+
if (!standsSomewhere(listener))
|
|
193
215
|
return false;
|
|
194
216
|
return actorDistanceMeters(room, speaker, listener) <= hearingRangeMeters(listener);
|
|
195
217
|
}
|
|
@@ -126,6 +126,33 @@ export function authorizeNpcAction(scene, actor, verb, args) {
|
|
|
126
126
|
}
|
|
127
127
|
return ALLOWED;
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* THE JOHNSON HOLDS THE MEET (CWMrB2aNyL4hhrjyG: "when at the meet, the
|
|
131
|
+
* johnson shouldn't just keep moving around the room").
|
|
132
|
+
*
|
|
133
|
+
* The run's client picks the venue and waits in it (Run Faster's meet,
|
|
134
|
+
* p.197-198: the Johnson sets the place, the runners come to them). His
|
|
135
|
+
* AI read every beat as a chance to stage himself -- "Mr. Lacquer moves
|
|
136
|
+
* to the open floor", "moves to the counter", prod 2026-10-04 -- so the
|
|
137
|
+
* man the runner came to see wandered the room for the whole briefing.
|
|
138
|
+
* Prose can still have him turn, lean, or look at the door; the engine
|
|
139
|
+
* keeps his body where the meet put it until the job is settled, and his
|
|
140
|
+
* leaving after the settlement is the engine's own beat (game.ts
|
|
141
|
+
* "Johnsons don't linger"), not his AI's.
|
|
142
|
+
*
|
|
143
|
+
* A FIGHT IS NOT A MEET: inside a Combat Turn the turn loop moves him,
|
|
144
|
+
* and a Johnson ducking for cover is not this rule's business.
|
|
145
|
+
*
|
|
146
|
+
* Returns a refusal reason, or undefined when he may move.
|
|
147
|
+
*/
|
|
148
|
+
export function clientHoldsTheMeet(scene, actor) {
|
|
149
|
+
const client = scene.ownerGame?.currentClient;
|
|
150
|
+
if (!client || client.name !== actor.name || client.reported || client.dead)
|
|
151
|
+
return undefined;
|
|
152
|
+
if (actor.inExchange || scene.encounterIn?.(actor.currentLocation))
|
|
153
|
+
return undefined;
|
|
154
|
+
return `You hold your seat for the meet -- the runner came to you, and you stay where you are until the job is settled. Turn, gesture, or speak instead of walking.`;
|
|
155
|
+
}
|
|
129
156
|
/**
|
|
130
157
|
* Is this actor home? A decker jacked into the Matrix, a magician
|
|
131
158
|
* projecting astrally and a rigger jumped into a drone all leave a body
|
|
@@ -7,6 +7,7 @@ import { fuzzyPickName } from './fuzzy-match.js';
|
|
|
7
7
|
import { resetOverwatch } from './overwatch.js';
|
|
8
8
|
import { rebootWipe } from './marks.js';
|
|
9
9
|
import { hint } from './hints.js';
|
|
10
|
+
import { codesOnPage } from './clue-reading.js';
|
|
10
11
|
import { actorCell } from './spots.js';
|
|
11
12
|
import { announceGameOver } from './game-over.js';
|
|
12
13
|
import { holdAtBrink } from './not-dead-yet.js';
|
|
@@ -624,6 +625,43 @@ export function isObjectiveItem(scene, itemName) {
|
|
|
624
625
|
export function objectiveMark(scene, itemName) {
|
|
625
626
|
return isObjectiveItem(scene, itemName) ? `{yellow-fg}${OBJECTIVE_GLYPH}{/yellow-fg} ` : '';
|
|
626
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* PART OF THE JOB, NOT THE PRIZE (nfrBW27eRRxstJ8uC: "the 'Lot Catalogue
|
|
630
|
+
* Proof Sheet' isn't marked in my inventory as quest specific").
|
|
631
|
+
*
|
|
632
|
+
* The star is the objective alone, so a page the job's route runs
|
|
633
|
+
* through -- the proof sheet with the vault shutter's code pencilled in
|
|
634
|
+
* its margin -- sat unmarked among the loot, and the runner could not
|
|
635
|
+
* tell it from a souvenir. A LEAD is an item this scene's barriers
|
|
636
|
+
* answer to, read off the same facts the engine itself opens them with:
|
|
637
|
+
* - a page that spells one of the scene's device codes (clue-reading.ts
|
|
638
|
+
* codesOnPage -- the fact ensureClueDetails guarantees is written), or
|
|
639
|
+
* - the key a device declares as its own (Device.keyItem).
|
|
640
|
+
* Nothing else: a lead the engine cannot point to would be a guess.
|
|
641
|
+
*
|
|
642
|
+
* It marks the item, never the code -- whether the runner can READ the
|
|
643
|
+
* number off the page is still the Perception Test (clue-reading.ts).
|
|
644
|
+
* Hub scenes have no win condition and mark nothing, like the star.
|
|
645
|
+
*/
|
|
646
|
+
export const LEAD_GLYPH = '☆';
|
|
647
|
+
export function isJobLead(scene, item) {
|
|
648
|
+
if (!scene?.getWinCondition?.())
|
|
649
|
+
return false;
|
|
650
|
+
const name = item.name.trim().toLowerCase();
|
|
651
|
+
for (const room of Object.values(scene.getRooms())) {
|
|
652
|
+
for (const device of room.devices ?? []) {
|
|
653
|
+
if (device.keyItem?.name.trim().toLowerCase() === name)
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return codesOnPage(scene, item).length > 0;
|
|
658
|
+
}
|
|
659
|
+
/** `★ ` for the objective, `☆ ` for a lead (isJobLead), '' otherwise. */
|
|
660
|
+
export function jobMark(scene, item) {
|
|
661
|
+
if (isObjectiveItem(scene, item.name))
|
|
662
|
+
return objectiveMark(scene, item.name);
|
|
663
|
+
return isJobLead(scene, item) ? `{yellow-fg}${LEAD_GLYPH}{/yellow-fg} ` : '';
|
|
664
|
+
}
|
|
627
665
|
/**
|
|
628
666
|
* The "you can see them, but you can't touch them" refusal. When a target
|
|
629
667
|
* name matches someone in the room on a plane the actor perceives but
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.383.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|