@maka/maka-cli 5.134.0 → 5.135.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/aim.js +60 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +132 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js +12 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +102 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js +24 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/defend.js +74 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/delay.js +34 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.js +11 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-turn.js +58 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/heal.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiative.js +20 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +42 -59
- package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js +23 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js +31 -10
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/unequip.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +11 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.js +24 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +21 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +44 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +33 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +98 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/action-budget.js +87 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/action-cost.js +66 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +58 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +99 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +568 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +20 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/movement-cost.js +80 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/npc-combat-brain.js +122 -0
- package/package.json +1 -1
|
@@ -308,6 +308,8 @@ export class CombatExchange {
|
|
|
308
308
|
if (lines.length > 0)
|
|
309
309
|
this.logger.log(`\n${lines.join('\n')}`, fightScope);
|
|
310
310
|
this.scene.updateStatus();
|
|
311
|
+
if (process.env.MAKA_NO_BEATS === '1')
|
|
312
|
+
return;
|
|
311
313
|
await new Promise(resolve => setTimeout(resolve, CombatExchange.BEAT_MS));
|
|
312
314
|
}
|
|
313
315
|
/**
|
|
@@ -454,6 +456,94 @@ export class CombatExchange {
|
|
|
454
456
|
* rebuild AI-history reports as [...world, ...meta] so an NPC's
|
|
455
457
|
* memory of the fight keeps the dice.
|
|
456
458
|
*/
|
|
459
|
+
/**
|
|
460
|
+
* WHAT THE COMBAT TURN ADDS TO ONE STRIKE (RAG-checked 2026-09-06):
|
|
461
|
+
* - TAKE AIM (p.166): +1 per aim on the shot it was for, capped at
|
|
462
|
+
* half Willpower; consumed here whether or not it was for this
|
|
463
|
+
* target (aiming at one person then shooting another wastes it).
|
|
464
|
+
* - CHARGING (p.186): a running attacker closing into melee gets +4
|
|
465
|
+
* -- net +2 with the -2 running penalty already in the pool.
|
|
466
|
+
* - DEFENDER RUNNING (p.189 Defense Modifiers): +2 to defend
|
|
467
|
+
* against a ranged attack.
|
|
468
|
+
* - SURPRISE (p.192): a surprised defender gets no Defense Test at
|
|
469
|
+
* all against someone who is not.
|
|
470
|
+
* - FULL DEFENSE (p.191): +Willpower for the rest of the turn.
|
|
471
|
+
* - DODGE / BLOCK / PARRY (p.168): -5 Initiative each, +Gymnastics /
|
|
472
|
+
* +Unarmed / +weapon skill on this one test, by the defender's
|
|
473
|
+
* standing policy (commands/defend.ts), and only when the score
|
|
474
|
+
* can pay.
|
|
475
|
+
* Outside an encounter only the aim, charge and running lines apply --
|
|
476
|
+
* they are properties of the actors, not of the turn structure.
|
|
477
|
+
*/
|
|
478
|
+
turnModifiers(attacker, defender, isMelee, strike, meta) {
|
|
479
|
+
let attack = 0;
|
|
480
|
+
let defense = 0;
|
|
481
|
+
if (attacker.aimBonus > 0 && !isMelee && !strike.isSpell) {
|
|
482
|
+
if (!attacker.aimTarget || attacker.aimTarget === defender.name) {
|
|
483
|
+
const aimed = Math.min(attacker.aimBonus, attacker.aimCap);
|
|
484
|
+
attack += aimed;
|
|
485
|
+
meta.push(` Aimed: +${aimed} (Take Aim, p.166)`);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
meta.push(` The aim on ${attacker.aimTarget} is wasted on ${defender.name}.`);
|
|
489
|
+
}
|
|
490
|
+
attacker.clearAim();
|
|
491
|
+
}
|
|
492
|
+
if (isMelee && !strike.isSpell && attacker.isRunningThisTurn) {
|
|
493
|
+
attack += 4;
|
|
494
|
+
meta.push(` Charging: +4 (p.186; the -2 for running is already in the pool)`);
|
|
495
|
+
}
|
|
496
|
+
if (!isMelee && defender.isRunningThisTurn) {
|
|
497
|
+
defense += 2;
|
|
498
|
+
meta.push(` ${defender.name} is running: +2 to defend (p.189)`);
|
|
499
|
+
}
|
|
500
|
+
const enc = this.scene.encounterFor?.(defender) ?? this.scene.encounterFor?.(attacker);
|
|
501
|
+
if (!enc)
|
|
502
|
+
return { attack, defense, noDefense: false };
|
|
503
|
+
if (enc.isSurprised(defender) && !enc.isSurprised(attacker)) {
|
|
504
|
+
meta.push(` ${defender.name} is SURPRISED -- no Defense Test (p.192).`);
|
|
505
|
+
return { attack, defense, noDefense: true };
|
|
506
|
+
}
|
|
507
|
+
if (enc.fullDefenseActive(defender)) {
|
|
508
|
+
defense += defender.willpower;
|
|
509
|
+
meta.push(` Full Defense: +${defender.willpower} Willpower (p.191)`);
|
|
510
|
+
}
|
|
511
|
+
const policy = defender.defensePolicy;
|
|
512
|
+
if (policy !== 'none' && (defender.plane === 'meat' || defender.plane === 'drone')) {
|
|
513
|
+
let skill = 0;
|
|
514
|
+
let label = '';
|
|
515
|
+
let source = '';
|
|
516
|
+
if (policy === 'dodge') {
|
|
517
|
+
skill = defender.skillRating('gymnastics');
|
|
518
|
+
label = 'Dodge';
|
|
519
|
+
source = 'Gymnastics';
|
|
520
|
+
}
|
|
521
|
+
else if (policy === 'block' && isMelee) {
|
|
522
|
+
skill = defender.skillRating('unarmed');
|
|
523
|
+
label = 'Block';
|
|
524
|
+
source = 'Unarmed Combat';
|
|
525
|
+
}
|
|
526
|
+
else if (policy === 'parry' && isMelee) {
|
|
527
|
+
const held = defender.weaponDrawn ? defender.getCarriedWeapon() : null;
|
|
528
|
+
if (held && !held.isFirearm()) {
|
|
529
|
+
skill = defender.skillRating(defender.activeCombatSkill());
|
|
530
|
+
label = 'Parry';
|
|
531
|
+
source = 'weapon skill';
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (label && skill > 0) {
|
|
535
|
+
const refusal = enc.spendInterrupt(defender, 5, label);
|
|
536
|
+
if (refusal) {
|
|
537
|
+
meta.push(` ${label} not taken: ${refusal}`);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
defense += skill;
|
|
541
|
+
meta.push(` ${label}: +${skill} (${source}, -5 Initiative, p.168)`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return { attack, defense, noDefense: false };
|
|
546
|
+
}
|
|
457
547
|
resolveStrike(attacker, defender, profile) {
|
|
458
548
|
const strike = profile ?? this.weaponProfile(attacker, defender);
|
|
459
549
|
const world = [];
|
|
@@ -564,10 +654,16 @@ export class CombatExchange {
|
|
|
564
654
|
const attackLimit = strike.isSpell
|
|
565
655
|
? strike.limit
|
|
566
656
|
: (strike.limit ?? attacker.getAttackLimit(strike.weaponItem));
|
|
567
|
-
const attack = rollPool(strike.pool + directed + rangeMod + attackerFloor + targetDown, attackLimit);
|
|
568
|
-
const defense = rollPool(defender.getDefensePool() + inspired + counterspell + coverDice + proneDefence);
|
|
569
657
|
const header = `${attacker.name} attacks ${defender.name} ${strike.label}`;
|
|
570
658
|
meta.push(`${header}:`);
|
|
659
|
+
// THE COMBAT TURN'S MODIFIERS (utilities/combat-turn.ts): Take Aim,
|
|
660
|
+
// charging, a running defender, surprise, Full Defense and the
|
|
661
|
+
// dodge/block/parry interrupts -- see turnModifiers below.
|
|
662
|
+
const turn = this.turnModifiers(attacker, defender, isMelee, strike, meta);
|
|
663
|
+
const attack = rollPool(strike.pool + directed + rangeMod + attackerFloor + targetDown + turn.attack, attackLimit);
|
|
664
|
+
const defense = turn.noDefense
|
|
665
|
+
? rollPool(0)
|
|
666
|
+
: rollPool(defender.getDefensePool() + inspired + counterspell + coverDice + proneDefence + turn.defense);
|
|
571
667
|
const rangeNote = weaponClass && spatial.meters > 0
|
|
572
668
|
? ` (${rangeMod !== 0 ? `${rangeMod} range` : `${rangeBracketName(spatial.meters, weaponClass)} range`} -- ~${Math.round(spatial.meters)} m)`
|
|
573
669
|
: '';
|
|
@@ -908,7 +1004,7 @@ export class CombatExchange {
|
|
|
908
1004
|
if (this.scene.isHumanControlled(this.actor)) {
|
|
909
1005
|
return '';
|
|
910
1006
|
}
|
|
911
|
-
if (this.playerWitnesses()) {
|
|
1007
|
+
if (this.playerWitnesses() && process.env.MAKA_NO_BEATS !== '1') {
|
|
912
1008
|
await new Promise(resolve => setTimeout(resolve, CombatExchange.NPC_FOLLOWUP_PAUSE_MS));
|
|
913
1009
|
}
|
|
914
1010
|
return report.join('\n');
|