@maka/maka-cli 5.77.0 → 5.79.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 (48) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +8 -1
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +8 -1
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/climb.js +9 -2
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/climb.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js +14 -2
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.d.ts +19 -0
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +68 -0
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +9 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/time.js +9 -2
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/time.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  18. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +11 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +20 -0
  21. package/bundle/typescript/src/commands/game/sideQuest/game.js +87 -3
  22. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/headless.js +0 -3
  24. package/bundle/typescript/src/commands/game/sideQuest/headless.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +33 -0
  26. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +42 -0
  27. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +18 -7
  29. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +37 -7
  30. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.d.ts +180 -0
  32. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js +534 -0
  33. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js.map +1 -0
  34. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +34 -4
  35. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +78 -47
  36. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.js +30 -3
  38. package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.d.ts +31 -28
  40. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +59 -32
  41. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js.map +1 -1
  42. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +12 -0
  43. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +14 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  45. package/bundle/typescript/src/tools/sdk-loader/runtime-fetch.d.ts +24 -2
  46. package/bundle/typescript/src/tools/sdk-loader/runtime-fetch.js +87 -4
  47. package/bundle/typescript/src/tools/sdk-loader/runtime-fetch.js.map +1 -1
  48. package/package.json +1 -1
@@ -0,0 +1,534 @@
1
+ import { rollPool, formatRoll } from './dice.js';
2
+ import { MAX_MARKS } from './marks.js';
3
+ /**
4
+ * INTRUSION COUNTERMEASURES -- the host's side of the roll (SR5 p.247-248).
5
+ *
6
+ * This closes the standing note on Room.hostAttributes(): "the only
7
+ * mechanical use SR5 gives a Host Rating is IC rolling Host Rating x 2
8
+ * (p.247-248), and this engine has no IC -- `hostAlert` is a flag and
9
+ * 'ice hunting' is prose; nothing rolls on the host's side anywhere."
10
+ * A host now rolls. hostRating is no longer read by nothing.
11
+ *
12
+ * PHASE 1 SHIPS TWO TYPES: Patrol and Probe. The other ten citable types
13
+ * (Killer, Sparky, Black, Blaster, Acid, Binder, Jammer, Marker,
14
+ * Scramble, Track) land in later phases, and are deliberately ABSENT
15
+ * from the table below rather than present-and-inert -- a host must not
16
+ * advertise ice it cannot run, which is the same rule
17
+ * commands/advertised-verbs.test.ts enforces for verbs.
18
+ *
19
+ * TAR BABY and CRASH IC ARE LEFT OUT ON PURPOSE, and this is a canon
20
+ * statement rather than a backlog note: they appear in published host
21
+ * lists (False Flag p.31-32, p.37-38; Data Trails p.100) but the
22
+ * rulebooks give no mechanics for either. Three differently-phrased
23
+ * retrievals found none. Data Trails p.56-57 describes a "Tarball"
24
+ * CYBERPROGRAM -- a decker's program, not the IC -- and reaching for it
25
+ * would be inventing an ability, which CLAUDE.md rates worse than a
26
+ * missing one. If a page turns up, they are a table row each.
27
+ */
28
+ /** What the host rolls, for every IC that attacks: Host Rating x 2,
29
+ * LIMITED by the host's Attack (p.247-248). The bracket is a cap on
30
+ * hits and never a die in the pool -- see dice.ts. */
31
+ export function icAttackPool(room) {
32
+ return { pool: room.hostRating * 2, limit: room.hostAttributes().attack };
33
+ }
34
+ /**
35
+ * The target's defence against an attacking IC: INTUITION + FIREWALL
36
+ * (p.248, named identically on Black, Killer and Probe).
37
+ *
38
+ * Built from Player.panDefence()'s components rather than its total, on
39
+ * purpose. That method's `silent`/`scrub`/`sneak` terms exist because a
40
+ * hidden PAN must be FOUND before it can be hit; IC inside the host has
41
+ * already found you -- it is attacking, not searching -- and canon
42
+ * writes this defence as the two attributes flat. Taking the total would
43
+ * hand the defender up to +5 dice the book does not give them.
44
+ *
45
+ * Reusing the components keeps the technomancer rule (a living persona's
46
+ * Firewall is Willpower, p.251) and the correction that Firewall is a
47
+ * WHOLE attribute here, both of which panDefence already carries.
48
+ */
49
+ export function icDefencePool(actor) {
50
+ const d = actor.panDefence('sleaze');
51
+ return Math.max(0, d.attribute + d.firewall);
52
+ }
53
+ /**
54
+ * HOW OFTEN PATROL LOOKS, before it suspects anyone (Data Trails p.86).
55
+ * The interval is in Combat Turns and is rolled fresh each time.
56
+ *
57
+ * CANON'S TABLE STARTS AT RATING 3. Ratings 1-2 exist (p.247 rates hosts
58
+ * 1-12) and the interval table simply does not cover them. Rather than
59
+ * invent a sixth band this takes the nearest published one, and says so:
60
+ * an ENGINE DECISION extending 3-4 downward, not a citation. The
61
+ * alternative -- a rating-1 host that never patrols at all -- would be
62
+ * inventing a rule too, and a more surprising one.
63
+ */
64
+ const PATROL_INTERVAL = [
65
+ { maxRating: 2, dice: 1, plus: 0 }, // engine decision -- see above
66
+ { maxRating: 4, dice: 1, plus: 0 },
67
+ { maxRating: 6, dice: 1, plus: 2 },
68
+ { maxRating: 8, dice: 2, plus: 0 },
69
+ { maxRating: 10, dice: 2, plus: 2 },
70
+ { maxRating: 12, dice: 3, plus: 0 },
71
+ ];
72
+ /** A fresh interval in Combat Turns for this host's next sweep. */
73
+ export function rollPatrolInterval(hostRating) {
74
+ const band = PATROL_INTERVAL.find(b => hostRating <= b.maxRating)
75
+ ?? PATROL_INTERVAL[PATROL_INTERVAL.length - 1];
76
+ // rollPool's faces, so the audited roller stays the only place dice
77
+ // are made. Hits are irrelevant here; the pips are the interval.
78
+ const faces = rollPool(band.dice).rolls.reduce((a, b) => a + b, 0);
79
+ return Math.max(1, faces + band.plus);
80
+ }
81
+ /** How the reducers name the attribute they are eating, for the log. */
82
+ const ATTR_LABEL = {
83
+ attack: 'Attack', sleaze: 'Sleaze', dataProcessing: 'Data Processing', firewall: 'Firewall',
84
+ };
85
+ /**
86
+ * LAUNCH ORDER. Canon fixes the RATE (one per Combat Turn) and the CAPS
87
+ * (up to Host Rating running; never two of one type) but says nothing
88
+ * about which program a host reaches for first. Patrol leads here for a
89
+ * reason canon does give: "most hosts have Patrol IC running at all
90
+ * times" (p.248), so on any host that has noticed you, Patrol is the one
91
+ * already up rather than one it had to launch.
92
+ */
93
+ export const IC_TYPES = [
94
+ { name: 'Patrol IC', kind: 'patrol', attacks: false, hurtByFailure: false },
95
+ { name: 'Probe IC', kind: 'probe', attacks: true, hurtByFailure: true },
96
+ { name: 'Killer IC', kind: 'damage', attacks: true, hurtByFailure: true },
97
+ // "Psycho Killer" -- an upgraded Killer, same damage line plus
98
+ // biofeedback (p.248).
99
+ { name: 'Sparky IC', kind: 'damage', attacks: true, hurtByFailure: true, biofeedback: true },
100
+ // "Also known as Grey IC" -- biofeedback that can only ever be Stun,
101
+ // plus a link-lock (p.248).
102
+ { name: 'Blaster IC', kind: 'damage', attacks: true, hurtByFailure: true, biofeedback: true, stunOnly: true, linkLock: true },
103
+ // The nastiest thing on the table: full biofeedback AND a link-lock,
104
+ // so it hurts you and holds you there to keep doing it (p.248).
105
+ { name: 'Black IC', kind: 'damage', attacks: true, hurtByFailure: true, biofeedback: true, linkLock: true },
106
+ // THE FOUR REDUCERS -- one mechanic, four rows. Each eats a different
107
+ // Matrix attribute by 1 per successful attack, cumulatively, until you
108
+ // reboot; once that attribute is at 0 they deal 1 DV per net hit
109
+ // instead (p.248).
110
+ { name: 'Acid IC', kind: 'reducer', attacks: true, hurtByFailure: true, reduces: 'firewall' },
111
+ { name: 'Binder IC', kind: 'reducer', attacks: true, hurtByFailure: true, reduces: 'dataProcessing' },
112
+ { name: 'Jammer IC', kind: 'reducer', attacks: true, hurtByFailure: true, reduces: 'attack' },
113
+ { name: 'Marker IC', kind: 'reducer', attacks: true, hurtByFailure: true, reduces: 'sleaze' },
114
+ // THE TWO CONDITIONALS -- they do nothing at all until the host has
115
+ // collected enough marks, which is what Probe IC is for.
116
+ { name: 'Track IC', kind: 'track', attacks: true, hurtByFailure: true, marksNeeded: 2 },
117
+ { name: 'Scramble IC', kind: 'scramble', attacks: true, hurtByFailure: true, marksNeeded: 3 },
118
+ ];
119
+ const empty = () => ({ lines: [], meta: [], world: [] });
120
+ /**
121
+ * Does this host act at all this turn?
122
+ *
123
+ * `hostAlert` IS canon's launch condition, already set in the right
124
+ * place: hack.ts sets it exactly when a blown sleaze gets the intruder
125
+ * MADE, which is p.247's "the host launches IC when it notices someone
126
+ * doing something unauthorized". No new trigger is invented here -- the
127
+ * world event hack.ts already prints promises "ice hunting", and this is
128
+ * the code that finally pays that promise.
129
+ *
130
+ * A CRACKED HOST STOPS: room.ts documents cracking as mooting the alert,
131
+ * and a host whose security "hangs in shreds" fielding fresh ice would
132
+ * contradict the line the player was already given.
133
+ */
134
+ export function hostIsHunting(room, actor) {
135
+ return actor.plane === 'matrix'
136
+ && room.hasNode
137
+ && room.hostAlert
138
+ && !room.hostCracked;
139
+ }
140
+ /**
141
+ * IS THIS PERSONA A LEGITIMATE USER? (p.248, Patrol.)
142
+ *
143
+ * Marks are what tell a host you belong -- Patrol's own entry is where
144
+ * the rules tie marks to being a legitimate user, and hack.ts already
145
+ * says the same thing from the other side ("enough marks and the host
146
+ * thinks they belong"). So a persona holding at least one mark on this
147
+ * host is not what Patrol is hunting for, and the `mark` verb finally
148
+ * buys something defensive rather than only softening a later crack.
149
+ */
150
+ export function looksLegitimate(room, actor) {
151
+ return (room.hostMarksBy.get(actor.name) ?? 0) > 0;
152
+ }
153
+ /**
154
+ * AMBIENT PATROL -- the host's own eyes, before anyone has been made.
155
+ *
156
+ * This is the half of Patrol IC that Phase 1 could not reach, and
157
+ * without it the ice was very nearly unreachable: the only way to wake
158
+ * it was to fail a sleaze, and against a flat threshold of 2 a competent
159
+ * decker essentially never does. Measured on the Phase 1 build: twelve
160
+ * runs, twelve cracked hosts, zero sightings. Canon puts the detection
161
+ * on the host (p.248: "most hosts have Patrol IC running at all times",
162
+ * and it is the program that hunts illegal activity), so a Patrol that
163
+ * only ran AFTER you were spotted was a partial implementation of the
164
+ * one program whose whole job is spotting.
165
+ *
166
+ * WHAT IT ROLLS. Canon does not print Patrol's dice pool, and this says
167
+ * so rather than pretending: the pool is the general IC rule (Host
168
+ * Rating x 2, p.247) and the bracket is the one the Matrix Perception
169
+ * action carries ([Data Processing], p.241). Both halves are cited; the
170
+ * combination is the engine's, because the book never spells it out.
171
+ *
172
+ * WHAT IT TAKES TO BE SEEN, which canon IS explicit about (p.235): an
173
+ * icon that is not running silent is spotted AUTOMATICALLY, with no test
174
+ * at all. Running silent turns it into an Opposed test against Logic +
175
+ * Sleaze (p.236/241), and a tie goes to the hider.
176
+ */
177
+ export function ambientPatrol(room, actor) {
178
+ const out = empty();
179
+ if (actor.plane !== 'matrix' || !room.hasNode)
180
+ return out;
181
+ // Already made, or already open: Phase 1's turn owns those.
182
+ if (room.hostAlert || room.hostCracked)
183
+ return out;
184
+ if (looksLegitimate(room, actor))
185
+ return out;
186
+ // The sweep is on a clock (Data Trails p.86) -- it is not looking
187
+ // every single moment until it has reason to.
188
+ const due = (room.patrolCountdown ?? rollPatrolInterval(room.hostRating)) - 1;
189
+ if (due > 0) {
190
+ room.patrolCountdown = due;
191
+ return out;
192
+ }
193
+ room.patrolCountdown = rollPatrolInterval(room.hostRating);
194
+ let spotted;
195
+ if (!actor.runningSilent) {
196
+ // p.235: no test. A visible icon in the host is simply seen.
197
+ spotted = true;
198
+ out.meta.push(`Patrol IC -- sweep: icon not running silent, spotted automatically (no test, p.235)`);
199
+ }
200
+ else {
201
+ const look = rollPool(room.hostRating * 2, room.hostAttributes().dataProcessing);
202
+ const hide = rollPool(actor.logic + actor.matrixAttribute('sleaze'));
203
+ // p.236: a tie goes to the hider.
204
+ spotted = look.hits > hide.hits;
205
+ out.meta.push(`Patrol IC -- Matrix Perception (Host Rating x2 [Data Processing]): ${formatRoll(look)} v. Logic + Sleaze: ${formatRoll(hide)}`);
206
+ }
207
+ if (!spotted) {
208
+ out.lines.push(` Something quarters the datastream around you and moves on. Your silence held.`);
209
+ return out;
210
+ }
211
+ room.hostAlert = true;
212
+ actor.sneaking = false;
213
+ out.lines.push(` {red-fg}Patrol IC finds you -- an unmarked icon where none should be. The host knows it has an intruder.{/red-fg}`);
214
+ out.world.push(`${room.name}'s Patrol IC made ${actor.name} on a routine sweep -- alarm flagged, ice hunting.`);
215
+ return out;
216
+ }
217
+ /**
218
+ * THE HOST'S WHOLE TURN: its eyes if it has not made you, its ice if it
219
+ * has. One entry point, so the caller never has to know which half of
220
+ * the host is awake.
221
+ */
222
+ export function runHostTurn(room, actor) {
223
+ if (hostIsHunting(room, actor))
224
+ return runICTurn(room, actor);
225
+ return ambientPatrol(room, actor);
226
+ }
227
+ /**
228
+ * ONE COMBAT TURN of the host's ice.
229
+ *
230
+ * THE TURN MAPPING IS AN ENGINE MAPPING, NOT A RULE, and is labelled so
231
+ * deliberately. Canon paces IC in Combat Turns -- one program launched
232
+ * per turn, at the beginning of it. sideQuest has no Matrix initiative
233
+ * structure to hang that on, so ONE PLAYER ACTION TAKEN INSIDE AN
234
+ * ALERTED HOST counts as one Combat Turn. That is the closest structural
235
+ * analogue available; it is not something the book says, and it is not
236
+ * dressed up as a citation.
237
+ */
238
+ export function runICTurn(room, actor) {
239
+ if (!hostIsHunting(room, actor))
240
+ return empty();
241
+ const out = empty();
242
+ // LAUNCH: one program per Combat Turn (p.247), up to (Host Rating)
243
+ // running at once, and never more than one of each type.
244
+ if (room.runningIC.length < room.hostRating) {
245
+ const next = IC_TYPES.find(t => !room.runningIC.includes(t.name));
246
+ if (next) {
247
+ room.runningIC.push(next.name);
248
+ out.lines.push(next.kind === 'patrol'
249
+ ? ` {red-fg}${next.name} is already walking the host -- it never had to be launched.{/red-fg}`
250
+ : ` {red-fg}The host launches ${next.name}.{/red-fg}`);
251
+ }
252
+ }
253
+ // Every running program acts. An IC attack is a Complex Action
254
+ // (p.247), which is one program's whole turn.
255
+ for (const name of room.runningIC) {
256
+ const type = IC_TYPES.find(t => t.name === name);
257
+ if (!type)
258
+ continue;
259
+ if (type.kind === 'patrol')
260
+ patrolActs(room, actor, out);
261
+ else if (type.kind === 'probe')
262
+ probeActs(room, actor, out);
263
+ else if (type.kind === 'damage')
264
+ damageActs(room, actor, type, out);
265
+ else
266
+ reducerOrConditionalActs(room, actor, type, out);
267
+ }
268
+ return out;
269
+ }
270
+ /**
271
+ * The opposed attack every attacking IC makes: Host Rating x 2 [Attack]
272
+ * v. Intuition + Firewall (p.248). Returns the net hits; 0 or less is a
273
+ * miss. Shared so the four reducers, Track and Scramble cannot drift
274
+ * from Killer's version of the same roll.
275
+ */
276
+ function icAttack(room, actor, type, out) {
277
+ const { pool, limit } = icAttackPool(room);
278
+ const attack = rollPool(pool, limit);
279
+ const defence = rollPool(icDefencePool(actor));
280
+ const net = attack.hits - defence.hits;
281
+ out.meta.push(`${type.name} -- Host Rating x2 [Attack ${limit}]: ${formatRoll(attack)} v. Intuition + Firewall: ${formatRoll(defence)} (net ${net})`);
282
+ return net;
283
+ }
284
+ /**
285
+ * ACID / BINDER / JAMMER / MARKER, and the two mark-gated programs.
286
+ *
287
+ * THE FOUR REDUCERS ARE ONE MECHANIC (p.248), written four times in the
288
+ * book with a different attribute each: "when it gets 1 or more net hits
289
+ * on its attack, it reduces your (attribute) by 1. If your (attribute)
290
+ * has been reduced to 0 already, it causes 1 DV Matrix damage per net
291
+ * hit on the attack. The reduction is cumulative and lasts until you
292
+ * reboot the targeted device." So this is a table with four rows rather
293
+ * than four functions -- and a uniform approximation would have been a
294
+ * deviation, but a uniform RULE is just the rule.
295
+ *
296
+ * The reduction bites through Player.matrixAttribute, which is the one
297
+ * place all four are read -- so Acid really does thin your defence pool
298
+ * against the next IC, and Jammer really does lower the ceiling on your
299
+ * own Brute Force.
300
+ */
301
+ function reducerOrConditionalActs(room, actor, type, out) {
302
+ const net = icAttack(room, actor, type, out);
303
+ if (net <= 0) {
304
+ out.lines.push(` ${type.name} probes at your icon and finds no purchase.`);
305
+ return;
306
+ }
307
+ if (type.kind === 'reducer' && type.reduces) {
308
+ const attr = type.reduces;
309
+ const label = ATTR_LABEL[attr];
310
+ if (actor.matrixAttribute(attr) > 0) {
311
+ actor.matrixAttrLoss[attr] += 1;
312
+ out.lines.push(` {red-fg}${type.name} eats into your ${label} -- down to ${actor.matrixAttribute(attr)} until you reboot.{/red-fg}`);
313
+ return;
314
+ }
315
+ // AT ZERO IT STOPS REDUCING AND STARTS BURNING: 1 DV per net hit.
316
+ const soak = rollPool(actor.getSoakPool());
317
+ const dealt = Math.max(0, net - soak.hits);
318
+ out.meta.push(`${type.name} -- ${label} already 0, ${net} DV v. Device Rating + Firewall: ${formatRoll(soak)} -> ${dealt}`);
319
+ if (dealt <= 0) {
320
+ out.lines.push(` {red-fg}${type.name} claws at a ${label} that is already gone, and finds nothing left to take.{/red-fg}`);
321
+ return;
322
+ }
323
+ applyMatrixDamage(actor, dealt, type, out);
324
+ return;
325
+ }
326
+ // MARK-GATED. Both check what the HOST holds on you -- the counter
327
+ // Probe IC spends its turns building, which is why a host fields Probe
328
+ // alongside these rather than instead of them.
329
+ const marks = room.hostMarksOn.get(actor.name) ?? 0;
330
+ const needed = type.marksNeeded ?? 0;
331
+ if (marks < needed) {
332
+ out.lines.push(` ${type.name} closes on you and cannot get a grip -- it needs ${needed} mark${needed === 1 ? '' : 's'} on you and the host has ${marks}.`);
333
+ return;
334
+ }
335
+ if (type.kind === 'track') {
336
+ // p.249: "the host (and its owners) discover your physical location,
337
+ // which is usually reported to real-world authorities immediately."
338
+ out.lines.push(` {red-fg}${type.name} rides your datastream back to the meat. They have your PHYSICAL LOCATION -- and that goes straight to the authorities.{/red-fg}`);
339
+ out.world.push(`${room.name}'s Track IC pinned ${actor.name}'s real-world location and called it in.`);
340
+ out.traced = true;
341
+ return;
342
+ }
343
+ // SCRAMBLE (p.248): "if the host has three marks on you when this IC
344
+ // hits you, you reboot immediately, taking dumpshock if you were in
345
+ // VR." Reported, not performed -- leaveMatrix needs the Scene.
346
+ out.lines.push(` {red-fg}${type.name} nukes your connection outright --{/red-fg}`);
347
+ out.world.push(`${room.name}'s Scramble IC force-rebooted ${actor.name}.`);
348
+ out.scrambled = true;
349
+ }
350
+ /** Matrix damage onto the right track: the deck, or a technomancer's
351
+ * own head (p.229). Shared by the damage IC and the reducers-at-zero. */
352
+ function applyMatrixDamage(actor, dealt, type, out) {
353
+ if (actor.isTechnomancer()) {
354
+ actor.takeStun(dealt);
355
+ out.lines.push(` {red-fg}${type.name} tears through your living persona -- ${dealt} stun: ${actor.stunSummary()}{/red-fg}`);
356
+ return;
357
+ }
358
+ actor.takeMatrixDamage(dealt);
359
+ out.lines.push(` {red-fg}${type.name} burns ${dealt} box${dealt === 1 ? '' : 'es'} off your deck: ${actor.matrixSummary()}{/red-fg}`);
360
+ if (actor.matrixCrashed) {
361
+ out.lines.push(` {red-fg}The deck BRICKS under it --{/red-fg}`);
362
+ out.bricked = true;
363
+ }
364
+ }
365
+ /**
366
+ * KILLER IC and SPARKY IC (p.248).
367
+ *
368
+ * Both roll Host Rating x 2 [Attack] v. Intuition + Firewall and deal
369
+ * "(Attack) DV Matrix damage, +1 DV per net hit and +2 DV per mark on
370
+ * the target". Sparky -- "Psycho Killer", an upgraded Killer -- adds
371
+ * biofeedback; Killer does not.
372
+ *
373
+ * THE MARKS IN THE DV ARE THE HOST'S, not the player's: "per mark on the
374
+ * target" is what the host holds ON you, which is hostMarksOn and which
375
+ * Probe IC spends its turns building. That coupling is canon's own, and
376
+ * it is why Probe is worth a slot on a host that also fields a Killer.
377
+ *
378
+ * A FAILED IC ATTACK SHOULD DAMAGE THE IC (p.247) AND DOES NOT YET,
379
+ * stated here rather than left for the next reader to discover. Modelling
380
+ * it needs a condition monitor per program AND a way for the player to
381
+ * attack ice, and neither exists: there is no cybercombat path against
382
+ * IC in this engine at all. Half of it -- ice that hurts itself while
383
+ * the player still cannot swing back -- would be worse than neither
384
+ * half. It lands with the verb.
385
+ */
386
+ function damageActs(room, actor, type, out) {
387
+ const { limit } = icAttackPool(room);
388
+ const net = icAttack(room, actor, type, out);
389
+ if (net <= 0) {
390
+ out.lines.push(` ${type.name} lunges at your icon and closes on nothing.`);
391
+ return;
392
+ }
393
+ // Before the damage, so a clean soak is not an escape -- see the note
394
+ // on applyLinkLock.
395
+ applyLinkLock(actor, type, out);
396
+ const marks = room.hostMarksOn.get(actor.name) ?? 0;
397
+ const dv = limit + net + (2 * marks);
398
+ // MATRIX DAMAGE IS RESISTED WITH DEVICE RATING + FIREWALL (p.228), and
399
+ // getSoakPool() is already exactly that on the matrix plane -- whole
400
+ // Firewall, technomancer living-persona rule and Armor program
401
+ // included. Not recomputed here; one formula, one place.
402
+ const soak = rollPool(actor.getSoakPool());
403
+ const dealt = Math.max(0, dv - soak.hits);
404
+ out.meta.push(`${type.name} -- ${dv} DV (Attack ${limit} +${net} net${marks > 0 ? ` +${2 * marks} for ${marks} mark${marks === 1 ? '' : 's'}` : ''}) v. Device Rating + Firewall: ${formatRoll(soak)} -> ${dealt}`);
405
+ if (dealt <= 0) {
406
+ out.lines.push(` {red-fg}${type.name} bites and your firewall holds -- barely.{/red-fg}`);
407
+ return;
408
+ }
409
+ // p.229: a technomancer has no Matrix condition monitor and takes
410
+ // Matrix damage as Stun. Shared with the reducers-at-zero path.
411
+ applyMatrixDamage(actor, dealt, type, out);
412
+ if (type.biofeedback)
413
+ dealBiofeedback(actor, dealt, type, out);
414
+ }
415
+ /**
416
+ * THE LINK-LOCK LANDS ON A SUCCESSFUL ATTACK (p.248): "a single
417
+ * successful attack also link-locks the target".
418
+ *
419
+ * Tied to the attack CONNECTING, not to how much damage got through --
420
+ * so a hit soaked down to zero boxes still holds you. That is the whole
421
+ * horror of Black IC: it does not have to hurt you this turn to make
422
+ * sure you are still here next turn. Called before the damage is
423
+ * resolved for exactly that reason; putting it after the "soaked to
424
+ * nothing" early return would have quietly made a clean soak an escape.
425
+ */
426
+ function applyLinkLock(actor, type, out) {
427
+ if (!type.linkLock || actor.linkLockedBy.has(type.name))
428
+ return;
429
+ actor.linkLockedBy.add(type.name);
430
+ out.lines.push(` {red-fg}${type.name} sinks keep-alive hooks into your connection -- you are LINK-LOCKED. "jack out" is a fight now, not a decision.{/red-fg}`);
431
+ out.world.push(`${actor.name}'s persona is link-locked by ${type.name}.`);
432
+ }
433
+ /**
434
+ * BIOFEEDBACK (p.229) -- a SECOND, SEPARATE hit on the user, applied
435
+ * after the Matrix damage is resolved, not instead of it.
436
+ *
437
+ * The worked example at p.245 sets the amount: a spider's 8 DV becomes 4
438
+ * boxes after the deck resists, and the decker then resists **4P** of
439
+ * biofeedback. So the biofeedback DV is the Matrix damage that actually
440
+ * LANDED, not the DV that was thrown. Sparky's own entry says only "with
441
+ * biofeedback damage" and does not quantify it; Black IC's says "an
442
+ * equal amount", which agrees with the worked example. That is the
443
+ * reading used here, and it is an inference from two cited passages
444
+ * rather than a line anybody printed about Sparky.
445
+ *
446
+ * Resisted with WILLPOWER + FIREWALL (p.229). Hot-sim makes it PHYSICAL,
447
+ * cold-sim makes it STUN (p.230) -- which is the whole price of hot-sim,
448
+ * and the game already tells players that when they jack in.
449
+ */
450
+ function dealBiofeedback(actor, dv, type, out) {
451
+ const resist = rollPool(actor.willpower + actor.matrixAttribute('firewall'));
452
+ const dealt = Math.max(0, dv - resist.hits);
453
+ // BLASTER IS THE EXCEPTION (p.248): "the biofeedback from a Blaster
454
+ // program can only cause Stun damage" -- even in hot-sim, where every
455
+ // other source turns Physical. It is the whole difference between Grey
456
+ // IC and Black, and the reason a Blaster is survivable.
457
+ const hot = actor.simMode === 'hot' && !type.stunOnly;
458
+ out.meta.push(`${type.name} -- biofeedback ${dv} DV v. Willpower + Firewall: ${formatRoll(resist)} -> ${dealt}${hot ? ' PHYSICAL (hot-sim)' : ' stun'}`);
459
+ if (dealt <= 0) {
460
+ out.lines.push(` The feedback surge breaks against you and dies.`);
461
+ return;
462
+ }
463
+ if (hot) {
464
+ actor.takeDamage(dealt);
465
+ out.lines.push(` {red-fg}Hot-sim carries it into the MEAT -- ${dealt} physical: ${actor.conditionSummary()}{/red-fg}`);
466
+ }
467
+ else {
468
+ actor.takeStun(dealt);
469
+ out.lines.push(` {red-fg}Feedback burns -- ${dealt} stun: ${actor.stunSummary()}{/red-fg}`);
470
+ }
471
+ }
472
+ /**
473
+ * PATROL IC (p.248): makes NO attack test. It runs Matrix Perception on
474
+ * the personas in the host hunting illegal activity, has no Attack
475
+ * rating, shares what it finds with the host, and takes no Matrix damage
476
+ * when it fails.
477
+ *
478
+ * CADENCE: Data Trails p.86 spaces Patrol's perception tests by Host
479
+ * Rating (3-4: 1D6 Combat Turns, up to 11-12: 3D6) -- BUT that is the
480
+ * unsuspecting case, and the same passage says that once the Patrol IC
481
+ * or a spider suspects an intruder it runs Matrix Perception EVERY
482
+ * ACTION. This engine only reaches this code once the host has already
483
+ * made someone (hostAlert), so every-action is the branch that applies
484
+ * and the dice interval is deliberately not modelled. When ambient
485
+ * pre-alert patrolling exists, the interval table is what it needs.
486
+ */
487
+ function patrolActs(room, actor, out) {
488
+ // No Attack rating means no bracket to cap the hits with.
489
+ const roll = rollPool(room.hostRating * 2);
490
+ out.meta.push(`Patrol IC -- Matrix Perception (Host Rating x2): ${formatRoll(roll)}`);
491
+ if (roll.hits > 0) {
492
+ out.lines.push(` {red-fg}Patrol IC sweeps your icon and hands what it sees to the host.{/red-fg}`);
493
+ }
494
+ else {
495
+ // "Does not take Matrix damage when it fails" -- a whiff costs it
496
+ // nothing, which is why Patrol is the one thing always running.
497
+ out.lines.push(` Patrol IC quarters the datastream around you and comes up empty -- this pass.`);
498
+ }
499
+ }
500
+ /**
501
+ * PROBE IC (p.248): Host Rating x 2 [Attack] v. Intuition + Firewall.
502
+ * "Each successful attack gives the host and its IC one mark on the
503
+ * target, up to a maximum of three marks."
504
+ *
505
+ * MARKS RUN THE OTHER WAY FROM Room.hostMarksBy, and the distinction is
506
+ * the whole reason hostMarksOn exists. hostMarksBy counts marks the
507
+ * PLAYER holds on the HOST (they soften a crack). Probe places marks the
508
+ * HOST holds on the PLAYER, which is a different quantity pointing the
509
+ * other direction -- and the one Scramble (3 marks) and Track (2 marks)
510
+ * read in Phase 4. The engine previously modelled the host's side as the
511
+ * single boolean `hostAlert`, which cannot answer "how many".
512
+ */
513
+ function probeActs(room, actor, out) {
514
+ const { pool, limit } = icAttackPool(room);
515
+ const attack = rollPool(pool, limit);
516
+ const defence = rollPool(icDefencePool(actor));
517
+ const net = attack.hits - defence.hits;
518
+ out.meta.push(`Probe IC -- Host Rating x2 [Attack ${limit}]: ${formatRoll(attack)} v. Intuition + Firewall: ${formatRoll(defence)} (net ${net})`);
519
+ const held = room.hostMarksOn.get(actor.name) ?? 0;
520
+ if (net <= 0) {
521
+ out.lines.push(` Probe IC tests your edges and finds nothing to hold onto.`);
522
+ return;
523
+ }
524
+ if (held >= MAX_MARKS) {
525
+ // Canon caps Probe's marks at three; a fourth is not a thing.
526
+ out.lines.push(` {red-fg}Probe IC crawls over marks it already owns -- it can hold no more of you.{/red-fg}`);
527
+ return;
528
+ }
529
+ const now = held + 1;
530
+ room.hostMarksOn.set(actor.name, now);
531
+ out.lines.push(` {red-fg}Probe IC gets a grip -- the host holds ${now} mark${now === 1 ? '' : 's'} on you now${now >= MAX_MARKS ? ', all it can carry' : ''}.{/red-fg}`);
532
+ out.world.push(`${room.name}'s ice tightened its hold on ${actor.name}.`);
533
+ }
534
+ //# sourceMappingURL=ic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ic.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/ic.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,34 @@ export declare function planeLabel(actor: Player): string;
25
25
  /** Every off-plane actor whose empty body lies in this room. */
26
26
  export declare function shellsInRoom(scene: Scene, room: Room): (Player | NPC)[];
27
27
  export declare function enterMatrix(scene: Scene, actor: Player, mode: SimMode): string[];
28
+ /**
29
+ * DUMPSHOCK (SR5 p.229) -- ONE RULE, ONE PLACE.
30
+ *
31
+ * "When you're disconnected from the Matrix while in VR without
32
+ * gracefully switching to AR first... The Damage Value for dumpshock is
33
+ * 6S if you're in cold-sim and 6P if you're in hot-sim. Dumpshock is
34
+ * biofeedback damage, so you resist it with Willpower + Firewall...
35
+ * you're also disoriented and take a -2 dice pool modifier on all of
36
+ * your actions for (10 - Willpower) minutes. Remember that if you're
37
+ * being dumped because your deck just got bricked, you don't have a
38
+ * functional Firewall attribute any more, so only use your Willpower."
39
+ *
40
+ * EXTRACTED BECAUSE THE TWO COPIES HAD ALREADY DRIFTED. leaveMatrix and
41
+ * leaveDrone each rolled their own dumpshock: both used a DV of 4, which
42
+ * is not a number the book has, and both resisted on Willpower alone,
43
+ * which is missing a term -- but only the DRONE one applied the
44
+ * disorientation, so a decker dumped out of the Matrix walked away
45
+ * sharper than a rigger dumped out of a drone, for no reason anybody
46
+ * wrote down. Correcting one and not the other would have widened that
47
+ * gap instead of closing it. A rigger jumped into a drone is in VR, so
48
+ * it is the same rule and now it is the same code.
49
+ *
50
+ * The Biofeedback Filter's +2 (p.245) rides along and is simply absent
51
+ * for a rigger with no active deck, which is the right answer anyway.
52
+ */
53
+ export declare function applyDumpshock(actor: Player, reason: string): string[];
54
+ /** p.229. Was 4 in two places; the book says 6. */
55
+ export declare const DUMPSHOCK_DV = 6;
28
56
  export declare function leaveMatrix(scene: Scene, actor: Player, opts?: {
29
57
  forced?: boolean;
30
58
  reason?: string;
@@ -41,10 +69,12 @@ export declare function leaveMatrix(scene: Scene, actor: Player, opts?: {
41
69
  export declare function enterDrone(scene: Scene, actor: Player, drone: Item, mode: SimMode): string[];
42
70
  /**
43
71
  * Jumping out -- clean when voluntary; a WRECKED airframe is a forced
44
- * dump: dumpshock (4 boxes resisted with Willpower, stun cold / PHYSICAL
45
- * hot -- the engine's same softened DV as a Matrix dump) plus the p.229
46
- * disorientation: -2 on everything for (10 - Willpower) minutes,
47
- * wall-clock (Player.dumpshockedUntil rides sustainingPenalty).
72
+ * dump, and a rigger jumped into a drone is in VR, so the dump is
73
+ * ORDINARY DUMPSHOCK: see applyDumpshock, which both this and the Matrix
74
+ * exit now share. It used to be spelled out here with a DV of 4 and a
75
+ * Willpower-only resist -- the same softening the Matrix side had, minus
76
+ * the Firewall term, plus the only correct half either copy had (the
77
+ * p.229 disorientation, which the Matrix side was missing entirely).
48
78
  */
49
79
  export declare function leaveDrone(scene: Scene, actor: Player, opts?: {
50
80
  forced?: boolean;