@maka/maka-cli 5.180.0 → 5.182.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/attack.js +17 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +7 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +8 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +10 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +69 -23
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +6 -4
- package/bundle/typescript/src/commands/game/sideQuest/game.js +47 -39
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +39 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +44 -17
- package/bundle/typescript/src/commands/game/sideQuest/utilities/alarmed-staff.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +91 -23
- package/bundle/typescript/src/commands/game/sideQuest/utilities/host-combat.js +107 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-brain.js +84 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js +84 -51
- package/bundle/typescript/src/commands/game/sideQuest/utilities/npc-combat-brain.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +5 -0
- package/package.json +1 -1
|
@@ -297,37 +297,37 @@ export function runHostTurn(room, actor, scene) {
|
|
|
297
297
|
return ambientPatrol(room, actor);
|
|
298
298
|
}
|
|
299
299
|
/**
|
|
300
|
-
*
|
|
300
|
+
* THE START OF A HOST'S COMBAT TURN (p.247): "a host can launch one IC
|
|
301
|
+
* program per Combat Turn, at the beginning of each Combat Turn", up to
|
|
302
|
+
* its rating running, never two of a kind. Crashed programs leave the
|
|
303
|
+
* list first and come back the very next turn (p.355-356, "delayed,
|
|
304
|
+
* never destroyed") -- the relaunch takes the turn's one launch.
|
|
301
305
|
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* ALERTED HOST counts as one Combat Turn. That is the closest structural
|
|
307
|
-
* analogue available; it is not something the book says, and it is not
|
|
308
|
-
* dressed up as a citation.
|
|
306
|
+
* Launches only; nothing acts here. In play the encounter's newTurn
|
|
307
|
+
* hook calls this (utilities/host-combat.ts) and each program then acts
|
|
308
|
+
* in its own Action Phase (utilities/ic-brain.ts). Returns the actor a
|
|
309
|
+
* launch produced, so the caller can seat it in the fight.
|
|
309
310
|
*/
|
|
310
|
-
export function
|
|
311
|
-
if (!hostIsHunting(room, actor))
|
|
312
|
-
return empty();
|
|
313
|
-
const out = empty();
|
|
311
|
+
export function hostLaunchStep(room, scene, out) {
|
|
314
312
|
const host = room.host;
|
|
315
|
-
|
|
316
|
-
// filled since the last turn is off the list now, and the host
|
|
317
|
-
// relaunches it the very next turn -- "delayed, never destroyed".
|
|
313
|
+
let launched;
|
|
318
314
|
if (host) {
|
|
319
|
-
//
|
|
320
|
-
//
|
|
315
|
+
// Crashes first, then the relaunch: a program whose monitor filled
|
|
316
|
+
// during the last turn is off the list at the start of this one and
|
|
317
|
+
// comes straight back -- "reactivated the very next turn" -- taking
|
|
318
|
+
// the turn's one launch.
|
|
319
|
+
for (const kind of crashedIC(host)) {
|
|
320
|
+
crashIC(scene, host, kind);
|
|
321
|
+
out.lines.push(` {red-fg}${kind} shears apart -- crashed. The host is already recompiling it.{/red-fg}`);
|
|
322
|
+
}
|
|
321
323
|
if (host.relaunchQueue.length > 0 && room.runningIC.length < room.hostRating) {
|
|
322
324
|
const kind = host.relaunchQueue.shift();
|
|
323
325
|
room.runningIC.push(kind);
|
|
324
326
|
if (scene)
|
|
325
|
-
spawnIC(scene, host, kind);
|
|
327
|
+
launched = spawnIC(scene, host, kind);
|
|
326
328
|
out.lines.push(` {red-fg}The host relaunches ${kind}.{/red-fg}`);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
crashIC(scene, host, kind);
|
|
330
|
-
out.lines.push(` {red-fg}${kind} shears apart -- crashed. The host is already recompiling it.{/red-fg}`);
|
|
329
|
+
// ONE launch per turn (p.247): the relaunch was it.
|
|
330
|
+
return launched;
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
if (room.runningIC.length < room.hostRating) {
|
|
@@ -335,26 +335,49 @@ export function runICTurn(room, actor, scene) {
|
|
|
335
335
|
if (next) {
|
|
336
336
|
room.runningIC.push(next.name);
|
|
337
337
|
if (host && scene)
|
|
338
|
-
spawnIC(scene, host, next.name);
|
|
338
|
+
launched = spawnIC(scene, host, next.name) ?? launched;
|
|
339
339
|
out.lines.push(next.kind === 'patrol'
|
|
340
340
|
? ` {red-fg}${next.name} is already walking the host -- it never had to be launched.{/red-fg}`
|
|
341
341
|
: ` {red-fg}The host launches ${next.name}.{/red-fg}`);
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
|
-
|
|
345
|
-
|
|
344
|
+
return launched;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* ONE PROGRAM'S ACTION -- an IC attack is a Complex Action (p.247), which
|
|
348
|
+
* is one program's whole Action Phase. `ice` is the program's own actor
|
|
349
|
+
* when the scene has one: it is what a failed attack damages.
|
|
350
|
+
*/
|
|
351
|
+
export function icActs(room, actor, type, out, ice) {
|
|
352
|
+
if (type.kind === 'patrol')
|
|
353
|
+
patrolActs(room, actor, out);
|
|
354
|
+
else if (type.kind === 'probe')
|
|
355
|
+
probeActs(room, actor, type, out, ice);
|
|
356
|
+
else if (type.kind === 'damage')
|
|
357
|
+
damageActs(room, actor, type, out, ice);
|
|
358
|
+
else
|
|
359
|
+
reducerOrConditionalActs(room, actor, type, out, ice);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* ONE WHOLE COMBAT TURN of the host's ice, as a single call: the launch
|
|
363
|
+
* step, then every running program acts once. This is the shape the
|
|
364
|
+
* tests drive and the shape the engine used to run after EVERY player
|
|
365
|
+
* command ("one action inside an alerted host = one Combat Turn" -- an
|
|
366
|
+
* engine mapping that was never a rule, and that had four programs
|
|
367
|
+
* hitting a persona for typing "look"). In play the Combat Turn owns the
|
|
368
|
+
* pacing now (utilities/host-combat.ts): the launch at newTurn, each
|
|
369
|
+
* program in its own phase.
|
|
370
|
+
*/
|
|
371
|
+
export function runICTurn(room, actor, scene) {
|
|
372
|
+
if (!hostIsHunting(room, actor))
|
|
373
|
+
return empty();
|
|
374
|
+
const out = empty();
|
|
375
|
+
hostLaunchStep(room, scene, out);
|
|
346
376
|
for (const name of room.runningIC) {
|
|
347
377
|
const type = IC_TYPES.find(t => t.name === name);
|
|
348
378
|
if (!type)
|
|
349
379
|
continue;
|
|
350
|
-
|
|
351
|
-
patrolActs(room, actor, out);
|
|
352
|
-
else if (type.kind === 'probe')
|
|
353
|
-
probeActs(room, actor, out);
|
|
354
|
-
else if (type.kind === 'damage')
|
|
355
|
-
damageActs(room, actor, type, out);
|
|
356
|
-
else
|
|
357
|
-
reducerOrConditionalActs(room, actor, type, out);
|
|
380
|
+
icActs(room, actor, type, out, room.host?.iceActors.get(name));
|
|
358
381
|
}
|
|
359
382
|
return out;
|
|
360
383
|
}
|
|
@@ -363,13 +386,29 @@ export function runICTurn(room, actor, scene) {
|
|
|
363
386
|
* v. Intuition + Firewall (p.248). Returns the net hits; 0 or less is a
|
|
364
387
|
* miss. Shared so the four reducers, Track and Scramble cannot drift
|
|
365
388
|
* from Killer's version of the same roll.
|
|
389
|
+
*
|
|
390
|
+
* A FAILED ATTACK ACTION HURTS THE ATTACKER (p.247 "as with all Attack
|
|
391
|
+
* actions, a failed attack causes damage to the IC"; the amount is the
|
|
392
|
+
* general rule, Data Trails p.181: one box of Matrix damage per net hit
|
|
393
|
+
* the defender got, unresisted). Patrol makes no Attack action and is
|
|
394
|
+
* never here (hurtByFailure false). Applied to the program's own actor,
|
|
395
|
+
* which is its condition monitor (ic-actors.ts); a program crashed this
|
|
396
|
+
* way leaves at the next launch step like any other.
|
|
366
397
|
*/
|
|
367
|
-
function icAttack(room, actor, type, out) {
|
|
398
|
+
function icAttack(room, actor, type, out, ice) {
|
|
368
399
|
const { pool, limit } = icAttackPool(room);
|
|
369
400
|
const attack = rollPool(pool, limit);
|
|
370
401
|
const defence = rollPool(icDefencePool(actor));
|
|
371
402
|
const net = attack.hits - defence.hits;
|
|
372
403
|
out.meta.push(`${type.name} -- Host Rating x2 [Attack ${limit}]: ${formatRoll(attack)} v. Intuition + Firewall: ${formatRoll(defence)} (net ${net})`);
|
|
404
|
+
if (net < 0 && type.hurtByFailure && ice && !ice.isDown()) {
|
|
405
|
+
const rebound = -net;
|
|
406
|
+
ice.takeDamage(rebound);
|
|
407
|
+
out.meta.push(`${type.name} -- failed Attack action: ${rebound} box${rebound === 1 ? '' : 'es'} back on the program, unresisted (p.247)`);
|
|
408
|
+
out.lines.push(ice.isDown()
|
|
409
|
+
? ` {red-fg}${ice.name}'s own attack code rejects and tears it apart -- ${rebound} box${rebound === 1 ? '' : 'es'}, and its monitor fills.{/red-fg}`
|
|
410
|
+
: ` ${ice.name} eats its own rejected code -- ${rebound} box${rebound === 1 ? '' : 'es'} (${ice.conditionSummary()}).`);
|
|
411
|
+
}
|
|
373
412
|
return net;
|
|
374
413
|
}
|
|
375
414
|
/**
|
|
@@ -389,8 +428,8 @@ function icAttack(room, actor, type, out) {
|
|
|
389
428
|
* against the next IC, and Jammer really does lower the ceiling on your
|
|
390
429
|
* own Brute Force.
|
|
391
430
|
*/
|
|
392
|
-
function reducerOrConditionalActs(room, actor, type, out) {
|
|
393
|
-
const net = icAttack(room, actor, type, out);
|
|
431
|
+
function reducerOrConditionalActs(room, actor, type, out, ice) {
|
|
432
|
+
const net = icAttack(room, actor, type, out, ice);
|
|
394
433
|
if (net <= 0) {
|
|
395
434
|
out.lines.push(` ${type.name} probes at your icon and finds no purchase.`);
|
|
396
435
|
return;
|
|
@@ -467,17 +506,13 @@ function applyMatrixDamage(actor, dealt, type, out) {
|
|
|
467
506
|
* Probe IC spends its turns building. That coupling is canon's own, and
|
|
468
507
|
* it is why Probe is worth a slot on a host that also fields a Killer.
|
|
469
508
|
*
|
|
470
|
-
* A FAILED IC ATTACK
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* attack ice, and neither exists: there is no cybercombat path against
|
|
474
|
-
* IC in this engine at all. Half of it -- ice that hurts itself while
|
|
475
|
-
* the player still cannot swing back -- would be worse than neither
|
|
476
|
-
* half. It lands with the verb.
|
|
509
|
+
* A FAILED IC ATTACK DAMAGES THE IC (p.247) -- see icAttack, where it
|
|
510
|
+
* lands for every attacking program at once. The player's side of it,
|
|
511
|
+
* cybercombat against ice, is `attack <program>` inside the host.
|
|
477
512
|
*/
|
|
478
|
-
function damageActs(room, actor, type, out) {
|
|
513
|
+
function damageActs(room, actor, type, out, ice) {
|
|
479
514
|
const { limit } = icAttackPool(room);
|
|
480
|
-
const net = icAttack(room, actor, type, out);
|
|
515
|
+
const net = icAttack(room, actor, type, out, ice);
|
|
481
516
|
if (net <= 0) {
|
|
482
517
|
out.lines.push(` ${type.name} lunges at your icon and closes on nothing.`);
|
|
483
518
|
return;
|
|
@@ -608,12 +643,10 @@ function patrolActs(room, actor, out) {
|
|
|
608
643
|
* read in Phase 4. The engine previously modelled the host's side as the
|
|
609
644
|
* single boolean `hostAlert`, which cannot answer "how many".
|
|
610
645
|
*/
|
|
611
|
-
function probeActs(room, actor, out) {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
const net = attack.hits - defence.hits;
|
|
616
|
-
out.meta.push(`Probe IC -- Host Rating x2 [Attack ${limit}]: ${formatRoll(attack)} v. Intuition + Firewall: ${formatRoll(defence)} (net ${net})`);
|
|
646
|
+
function probeActs(room, actor, type, out, ice) {
|
|
647
|
+
// The same opposed roll as every attacking program (icAttack), so a
|
|
648
|
+
// failed probe rebounds on the program like any failed Attack action.
|
|
649
|
+
const net = icAttack(room, actor, type, out, ice);
|
|
617
650
|
const held = room.hostMarksOn.get(actor.name) ?? 0;
|
|
618
651
|
if (net <= 0) {
|
|
619
652
|
out.lines.push(` Probe IC tests your edges and finds nothing to hold onto.`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Category } from '../types/shared/item-enum.js';
|
|
2
2
|
import { inMeleeReach, isInCover, coverAvailableFor, actorDistanceMeters } from './spots.js';
|
|
3
3
|
import { Logger } from './logger.js';
|
|
4
|
+
import { runIcActionPhase } from './ic-brain.js';
|
|
4
5
|
/**
|
|
5
6
|
* AN NPC'S ACTION PHASE, PLAYED BY RULE (SR5 p.163-167).
|
|
6
7
|
*
|
|
@@ -25,6 +26,10 @@ import { Logger } from './logger.js';
|
|
|
25
26
|
* Nothing here rolls a die of its own: the verbs do.
|
|
26
27
|
*/
|
|
27
28
|
export async function runNpcActionPhase(enc, npc) {
|
|
29
|
+
// A HOST'S PROGRAM HAS ITS OWN BRAIN (utilities/ic-brain.ts): no
|
|
30
|
+
// weapon to draw, no ground to close -- one test, by type.
|
|
31
|
+
if (npc.icKind !== undefined)
|
|
32
|
+
return runIcActionPhase(enc, npc);
|
|
28
33
|
const logger = Logger.getInstance();
|
|
29
34
|
if (npc.isIncapacitated() || npc.surrendered)
|
|
30
35
|
return;
|
|
@@ -209,6 +209,11 @@ export const DUMPSHOCK_DV = 6;
|
|
|
209
209
|
export function leaveMatrix(scene, actor, opts) {
|
|
210
210
|
const lines = [];
|
|
211
211
|
const body = actor.bodyRoom;
|
|
212
|
+
// OUT OF THE MATRIX IS OUT OF THE FIGHT (utilities/host-combat.ts): a
|
|
213
|
+
// persona leaving its host arena -- jack out, link-lock broken, deck
|
|
214
|
+
// bricked, Scramble, convergence -- is no longer a participant. Before
|
|
215
|
+
// endPersona clears the position the arena is read off.
|
|
216
|
+
scene.encounterFor?.(actor)?.leave(actor);
|
|
212
217
|
if (opts?.forced) {
|
|
213
218
|
lines.push(...applyDumpshock(actor, opts.reason ?? `The connection is severed from the other side.`));
|
|
214
219
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.182.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.",
|