@maka/maka-cli 5.6.0 → 5.8.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 +2 -2
- package/bundle/typescript/src/commands/_index.d.ts +4 -0
- package/bundle/typescript/src/commands/_index.js +4 -0
- package/bundle/typescript/src/commands/_index.js.map +1 -1
- package/bundle/typescript/src/commands/debug.js +1 -7
- package/bundle/typescript/src/commands/debug.js.map +1 -1
- package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js +33 -32
- package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/env/_index.d.ts +3 -0
- package/bundle/typescript/src/commands/env/_index.js +4 -0
- package/bundle/typescript/src/commands/env/_index.js.map +1 -0
- package/bundle/typescript/src/commands/env/env.command.d.ts +3 -0
- package/bundle/typescript/src/commands/env/env.command.js +22 -0
- package/bundle/typescript/src/commands/env/env.command.js.map +1 -0
- package/bundle/typescript/src/commands/env/get.sub.cmd.d.ts +1 -0
- package/bundle/typescript/src/commands/env/get.sub.cmd.js +46 -0
- package/bundle/typescript/src/commands/env/get.sub.cmd.js.map +1 -0
- package/bundle/typescript/src/commands/env/prompt-for-env.d.ts +10 -0
- package/bundle/typescript/src/commands/env/prompt-for-env.js +27 -0
- package/bundle/typescript/src/commands/env/prompt-for-env.js.map +1 -0
- package/bundle/typescript/src/commands/env/set.sub.cmd.d.ts +1 -0
- package/bundle/typescript/src/commands/env/set.sub.cmd.js +59 -0
- package/bundle/typescript/src/commands/env/set.sub.cmd.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +81 -2
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.d.ts +31 -0
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.js +125 -0
- package/bundle/typescript/src/commands/game/sideQuest/augmentations.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.d.ts +17 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +113 -15
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.d.ts +37 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +197 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +21 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +13 -5
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js +7 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +97 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +21 -10
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +41 -27
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +6 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +54 -20
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +35 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +306 -55
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.d.ts +33 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +137 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +37 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drink.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drink.js +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drink.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js +7 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/eat.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/eat.js +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/eat.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +101 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +6 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +67 -22
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +15 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +58 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.d.ts +26 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +125 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/install.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/install.js +125 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/install.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jump.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +83 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +76 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +7 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/order.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/order.js +151 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/order.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pay.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pay.js +39 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pay.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +6 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +121 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +60 -12
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +27 -10
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +15 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sell.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js +125 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +36 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +9 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +27 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +112 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/treat.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js +67 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +7 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +29 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +118 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +68 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +132 -5
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +6 -2
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +10 -6
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +196 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +828 -31
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +28 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +111 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +33 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +232 -10
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +111 -5
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +432 -41
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +6 -6
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +38 -14
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +9 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +48 -8
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/qualities.d.ts +44 -0
- package/bundle/typescript/src/commands/game/sideQuest/qualities.js +155 -0
- package/bundle/typescript/src/commands/game/sideQuest/qualities.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +274 -5
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.d.ts +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +82 -8
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.d.ts +13 -6
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +28 -15
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +15 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +207 -34
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.d.ts +56 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js +144 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +7 -4
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +8 -5
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js +49 -4
- package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +107 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.js +23 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +51 -15
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/remote/reconfig.sub.cmd.js +27 -1
- package/bundle/typescript/src/commands/remote/reconfig.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/run/run.command.js +1 -7
- package/bundle/typescript/src/commands/run/run.command.js.map +1 -1
- package/bundle/typescript/src/commands/ssl.js +10 -15
- package/bundle/typescript/src/commands/ssl.js.map +1 -1
- package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js +1 -6
- package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/generators/config.js +11 -10
- package/bundle/typescript/src/generators/config.js.map +1 -1
- package/bundle/typescript/src/templates/meteor/config/pm2.config.js +3 -3
- package/bundle/typescript/src/templates/meteor/project/gitignore +2 -1
- package/bundle/typescript/src/templates/react/kit/mui/auth/password-reset/basic-password-reset-page.js.tsx +3 -4
- package/bundle/typescript/src/tool.d.ts +2 -0
- package/bundle/typescript/src/tool.js +3 -0
- package/bundle/typescript/src/tool.js.map +1 -1
- package/bundle/typescript/src/tools/aws/aws.class.js +6 -4
- package/bundle/typescript/src/tools/aws/aws.class.js.map +1 -1
- package/bundle/typescript/src/tools/dotenvx/dotenvx.class.d.ts +24 -0
- package/bundle/typescript/src/tools/dotenvx/dotenvx.class.js +66 -0
- package/bundle/typescript/src/tools/dotenvx/dotenvx.class.js.map +1 -0
- package/bundle/typescript/src/tools/ssh/ssh.class.js +1 -1
- package/bundle/typescript/src/tools/ssh/ssh.class.js.map +1 -1
- package/bundle/typescript/src/tools/ssl/ssl.class.d.ts +8 -0
- package/bundle/typescript/src/tools/ssl/ssl.class.js +53 -0
- package/bundle/typescript/src/tools/ssl/ssl.class.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,13 +3,17 @@ import { Logger } from '../utilities/logger.js';
|
|
|
3
3
|
import { Inventory } from './inventory.js';
|
|
4
4
|
import { AbstractPlayer } from '../types/shared/abstracts.js';
|
|
5
5
|
export const STANCE_CYCLE = ['manual', 'guarded', 'aggressive'];
|
|
6
|
+
/** Meat and a jumped-in drone share the physical world. */
|
|
7
|
+
export function isPhysicalPlane(plane) {
|
|
8
|
+
return plane === 'meat' || plane === 'drone';
|
|
9
|
+
}
|
|
6
10
|
export const SKILL_NAMES = [
|
|
7
11
|
'firearms', 'blades', 'unarmed',
|
|
8
12
|
'sneaking', 'perception', 'athletics',
|
|
9
13
|
'hacking', 'locksmith',
|
|
10
14
|
'negotiation', 'con', 'intimidation',
|
|
11
15
|
'first-aid',
|
|
12
|
-
'assensing', 'spellcasting', 'piloting',
|
|
16
|
+
'assensing', 'spellcasting', 'piloting', 'conjuring',
|
|
13
17
|
];
|
|
14
18
|
/**
|
|
15
19
|
* Skills that fall back to the legacy `combatSkill` attribute when no
|
|
@@ -166,13 +170,28 @@ export class Player extends AbstractPlayer {
|
|
|
166
170
|
canPerceive(other) {
|
|
167
171
|
if (this.plane === other.plane)
|
|
168
172
|
return true;
|
|
169
|
-
|
|
173
|
+
// A drone is a physical machine in the meat world: meat sees it, its
|
|
174
|
+
// sensors see meat -- and combat between them is ordinary physics.
|
|
175
|
+
if (isPhysicalPlane(this.plane) && isPhysicalPlane(other.plane))
|
|
176
|
+
return true;
|
|
177
|
+
return this.plane === 'astral' && isPhysicalPlane(other.plane);
|
|
178
|
+
}
|
|
179
|
+
/** Whether these two can trade physical blows (see attack.ts targeting). */
|
|
180
|
+
sharesCombatPlane(other) {
|
|
181
|
+
return this.plane === other.plane
|
|
182
|
+
|| (isPhysicalPlane(this.plane) && isPhysicalPlane(other.plane));
|
|
170
183
|
}
|
|
171
184
|
// True while an AI-driven action is parked waiting for an in-flight
|
|
172
185
|
// exchange to settle (CombatExchange.waitForLull). One queued action per
|
|
173
186
|
// actor: a second colliding attempt is refused outright instead of
|
|
174
187
|
// stacking a burst of delayed attacks behind the same lull.
|
|
175
188
|
waitingForLull = false;
|
|
189
|
+
// Death-resolution claim (CombatExchange.resolveDeath): set
|
|
190
|
+
// SYNCHRONOUSLY by the first resolution to reach this actor, so a
|
|
191
|
+
// concurrent kill (autopilot + typed action racing -- a real session
|
|
192
|
+
// crashed on the second removeActor) skips cleanly instead of
|
|
193
|
+
// narrating a second death during the first one's narration beats.
|
|
194
|
+
deathClaimed = false;
|
|
176
195
|
// Combat attributes (see ICombatAttributes). Defaults make an unseeded
|
|
177
196
|
// actor a competent-but-mortal street-level runner; seeds can override
|
|
178
197
|
// per-NPC to make a hardened enforcer or a soft civilian.
|
|
@@ -225,8 +244,192 @@ export class Player extends AbstractPlayer {
|
|
|
225
244
|
this.resonance = config.combat?.resonance ?? 0;
|
|
226
245
|
this.adept = config.combat?.adept ?? false;
|
|
227
246
|
this.skills = { ...(config.combat?.skills ?? {}) };
|
|
247
|
+
// Essence bookkeeping needs the UNAUGMENTED talent to burn down from
|
|
248
|
+
// (see installAug) -- capture it before any 'ware lands.
|
|
249
|
+
this.baseMagic = this.magic;
|
|
250
|
+
this.baseResonance = this.resonance;
|
|
228
251
|
this._equipment = this.initializeEquipment();
|
|
229
252
|
}
|
|
253
|
+
// ==================== Augmentations (see augmentations.ts) ====================
|
|
254
|
+
/** Everyone is born whole. */
|
|
255
|
+
static ESSENCE_MAX = 6;
|
|
256
|
+
// What's left of the meat. Burned permanently by installAug; hitting 0
|
|
257
|
+
// is refused at the clinic -- there'd be nothing left to cut away.
|
|
258
|
+
essence = Player.ESSENCE_MAX;
|
|
259
|
+
// The unaugmented Magic/Resonance, from which essence loss burns down
|
|
260
|
+
// (SR5e: each full point of essence lost -- ROUNDED UP -- costs one).
|
|
261
|
+
// `magic`/`resonance` above stay the EFFECTIVE values every pool reads.
|
|
262
|
+
baseMagic = 0;
|
|
263
|
+
baseResonance = 0;
|
|
264
|
+
/** Installed 'ware, by catalog key (see AUGMENTATIONS). */
|
|
265
|
+
augmentations = new Set();
|
|
266
|
+
hasAug(key) {
|
|
267
|
+
return this.augmentations.has(key);
|
|
268
|
+
}
|
|
269
|
+
/** Full essence points lost, rounded up -- the toll Magic/Resonance pay. */
|
|
270
|
+
get essenceBurn() {
|
|
271
|
+
return Math.ceil(Player.ESSENCE_MAX - this.essence - 1e-9);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Burns the essence and records the 'ware. No gating here -- the clinic
|
|
275
|
+
* (commands/install.ts) owns the refusals (essence floor, conflicts,
|
|
276
|
+
* payment); archetype starting chrome installs through this too.
|
|
277
|
+
*/
|
|
278
|
+
installAug(aug) {
|
|
279
|
+
this.augmentations.add(aug.key);
|
|
280
|
+
// One-decimal arithmetic: 6 - 0.1 - 0.2 must read 5.7, not 5.6999....
|
|
281
|
+
this.essence = Math.round((this.essence - aug.essence) * 10) / 10;
|
|
282
|
+
const burn = this.essenceBurn;
|
|
283
|
+
this.magic = Math.max(0, this.baseMagic - burn);
|
|
284
|
+
this.resonance = Math.max(0, this.baseResonance - burn);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The single tap every pool reads chrome through. Wired Reflexes tiers
|
|
288
|
+
* don't stack -- the higher tier wins (II supersedes I) -- but the
|
|
289
|
+
* synaptic-vs-wired conflict is the clinic's job, not this getter's.
|
|
290
|
+
*/
|
|
291
|
+
augBonus(kind) {
|
|
292
|
+
if (this.augmentations.size === 0)
|
|
293
|
+
return 0;
|
|
294
|
+
const has = (k) => this.augmentations.has(k);
|
|
295
|
+
const reflexes = (has('wired-reflexes-2') ? 2 : has('wired-reflexes-1') ? 1 : 0) + (has('synaptic-booster') ? 1 : 0);
|
|
296
|
+
switch (kind) {
|
|
297
|
+
case 'initiative': return reflexes;
|
|
298
|
+
case 'defense': return reflexes;
|
|
299
|
+
case 'attack': return has('muscle-toner') ? 1 : 0;
|
|
300
|
+
case 'firearms': return has('smartlink') ? 2 : 0;
|
|
301
|
+
case 'soak': return (has('bone-lacing') ? 1 : 0) + (has('dermal-plating') ? 2 : 0);
|
|
302
|
+
case 'unarmed-damage': return has('bone-lacing') ? 2 : 0;
|
|
303
|
+
case 'perception': return has('cybereyes') ? 2 : 0;
|
|
304
|
+
case 'matrix': return has('datajack') ? 1 : 0;
|
|
305
|
+
case 'piloting': return has('control-rig') ? 2 : 0;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// ==================== Qualities (see qualities.ts) ====================
|
|
309
|
+
// SR5e's traits (p.71-85), baked into the archetype at creation
|
|
310
|
+
// (outfitArchetype). Every entry in the catalog has a real mechanical
|
|
311
|
+
// hook; the ones that read as flat pool nudges all tap through
|
|
312
|
+
// qualityBonus below, the same single-switch pattern as augBonus.
|
|
313
|
+
/** Installed qualities, by catalog key (see QUALITIES). */
|
|
314
|
+
qualities = new Set();
|
|
315
|
+
hasQuality(key) {
|
|
316
|
+
return this.qualities.has(key);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Records a quality and applies its one-time creation effect. Lucky is
|
|
320
|
+
* the only quality that edits an attribute (RAW raises the Edge ceiling;
|
|
321
|
+
* with no attribute point-buy here, the point lands directly).
|
|
322
|
+
*/
|
|
323
|
+
addQuality(key) {
|
|
324
|
+
if (this.qualities.has(key))
|
|
325
|
+
return;
|
|
326
|
+
this.qualities.add(key);
|
|
327
|
+
if (key === 'lucky') {
|
|
328
|
+
this.edge += 1;
|
|
329
|
+
this.edgeRemaining += 1;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/** The single tap every pool reads qualities through (cf. augBonus). */
|
|
333
|
+
qualityBonus(kind) {
|
|
334
|
+
if (this.qualities.size === 0)
|
|
335
|
+
return 0;
|
|
336
|
+
const has = (k) => this.qualities.has(k);
|
|
337
|
+
switch (kind) {
|
|
338
|
+
case 'sneak': return (has('catlike') ? 2 : 0) + (has('distinctive-style') ? -2 : 0);
|
|
339
|
+
case 'athletics': return has('natural-athlete') ? 2 : 0;
|
|
340
|
+
case 'matrix': return has('simsense-vertigo') ? -2 : 0;
|
|
341
|
+
case 'hacking': return (has('analytical-mind') ? 2 : 0) + this.qualityBonus('matrix');
|
|
342
|
+
case 'piloting': return has('juryrigger') ? 2 : 0;
|
|
343
|
+
case 'soak': return has('toughness') ? 1 : 0;
|
|
344
|
+
case 'healing': return has('quick-healer') ? 2 : 0;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// First Impression (p.74): the first social play of each job rolls +2.
|
|
348
|
+
// Consumed like the edge boost and the liquid-courage die; re-armed
|
|
349
|
+
// with the per-job resets in refreshEdge.
|
|
350
|
+
_firstImpressionUsed = false;
|
|
351
|
+
/** Spends the first impression: +2 for the social roll happening NOW. */
|
|
352
|
+
consumeFirstImpression() {
|
|
353
|
+
if (!this.qualities.has('first-impression') || this._firstImpressionUsed)
|
|
354
|
+
return 0;
|
|
355
|
+
this._firstImpressionUsed = true;
|
|
356
|
+
return 2;
|
|
357
|
+
}
|
|
358
|
+
/** SINner tax (p.84-85): the cut every payout loses to having a file. */
|
|
359
|
+
get sinTaxRate() {
|
|
360
|
+
if (this.qualities.has('sinner-corp'))
|
|
361
|
+
return 0.20;
|
|
362
|
+
if (this.qualities.has('sinner-criminal') || this.qualities.has('sinner-national'))
|
|
363
|
+
return 0.15;
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
// Combat Paralysis (p.80): armed by beginExchange when a FRESH fight
|
|
367
|
+
// opens, consumed by the initiative roll-off -- pool halved, then the
|
|
368
|
+
// freeze passes (subsequent exchanges of the same fight roll normally).
|
|
369
|
+
paralysisFreeze = false;
|
|
370
|
+
// ==================== Initiation & Submersion (SR5e p.324-326 / p.257-258) ====================
|
|
371
|
+
// The Awakened (and technomancer) endgame: karma-priced grades (10 +
|
|
372
|
+
// grade x3, see commands/initiate.ts) that lift the talent ceiling to
|
|
373
|
+
// 6 + grade and grant one METAMAGIC (or echo) each. Grade can never
|
|
374
|
+
// exceed the BASE talent (RAW). The picks live here as a Set so every
|
|
375
|
+
// pool can read them the same way it reads qualities and chrome.
|
|
376
|
+
initiationGrade = 0;
|
|
377
|
+
/** Metamagics (casters/adepts) and echoes (technomancers), by key. */
|
|
378
|
+
metamagics = new Set();
|
|
379
|
+
hasMetamagic(key) {
|
|
380
|
+
return this.metamagics.has(key);
|
|
381
|
+
}
|
|
382
|
+
// ==================== Reputation (SR5e p.367-368) ====================
|
|
383
|
+
// Three tracked scores. STREET CRED is earned karma made visible: one
|
|
384
|
+
// point per full 10 career karma (earned on the street -- creation
|
|
385
|
+
// karma doesn't count; the name starts unknown). NOTORIETY starts from
|
|
386
|
+
// the qualities (the book's own list) and grows from incidents --
|
|
387
|
+
// walking out on a job, cutting down someone mid-surrender -- each
|
|
388
|
+
// counted ONCE (RAW: never twice for the same incident). PUBLIC
|
|
389
|
+
// AWARENESS derives from both, plus a Distinctive Style everyone can
|
|
390
|
+
// describe to a Knight Errant sketch artist.
|
|
391
|
+
/** Total karma EARNED in play -- spending it doesn't touch this. */
|
|
392
|
+
careerKarma = 0;
|
|
393
|
+
// Each stain on the record, in the player's own fiction ("walked out
|
|
394
|
+
// on 'Tacoma Rain'"). The strings ARE the dedupe keys.
|
|
395
|
+
_notorietyReasons = [];
|
|
396
|
+
/** Records an incident once; false means this exact stain already stands. */
|
|
397
|
+
addNotoriety(reason) {
|
|
398
|
+
if (this._notorietyReasons.includes(reason))
|
|
399
|
+
return false;
|
|
400
|
+
this._notorietyReasons.push(reason);
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
get notorietyReasons() {
|
|
404
|
+
return this._notorietyReasons;
|
|
405
|
+
}
|
|
406
|
+
get streetCred() {
|
|
407
|
+
return Math.floor(this.careerKarma / 10);
|
|
408
|
+
}
|
|
409
|
+
/** The p.368 quality list, restricted to the qualities this game ships. */
|
|
410
|
+
get qualityNotoriety() {
|
|
411
|
+
const plus = ['bad-luck', 'combat-paralysis', 'sinner-criminal']
|
|
412
|
+
.filter(k => this.qualities.has(k)).length
|
|
413
|
+
+ [...this.qualities].filter(k => k.startsWith('incompetent-')).length;
|
|
414
|
+
const minus = ['first-impression', 'lucky'].filter(k => this.qualities.has(k)).length;
|
|
415
|
+
return plus - minus;
|
|
416
|
+
}
|
|
417
|
+
get notoriety() {
|
|
418
|
+
return Math.max(0, this.qualityNotoriety + this._notorietyReasons.length);
|
|
419
|
+
}
|
|
420
|
+
get publicAwareness() {
|
|
421
|
+
return Math.floor((this.streetCred + this.notoriety) / 3)
|
|
422
|
+
+ (this.qualities.has('distinctive-style') ? 1 : 0);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* The dice a NAME is worth in shadow-side social work (haggle,
|
|
426
|
+
* persuade): cred minus the stories that undercut it. RAW makes cred a
|
|
427
|
+
* social LIMIT bonus; with no limit system here, net-rep dice carry
|
|
428
|
+
* the same idea -- and notoriety eating cred keeps it self-balancing.
|
|
429
|
+
*/
|
|
430
|
+
get socialRep() {
|
|
431
|
+
return Math.max(0, this.streetCred - this.notoriety);
|
|
432
|
+
}
|
|
230
433
|
// ======================== Skills (see SkillName) ========================
|
|
231
434
|
/** Explicit per-skill ratings; anything absent resolves via skillRating(). */
|
|
232
435
|
skills = {};
|
|
@@ -241,6 +444,10 @@ export class Player extends AbstractPlayer {
|
|
|
241
444
|
* everything else is 0 (unskilled).
|
|
242
445
|
*/
|
|
243
446
|
skillRating(name) {
|
|
447
|
+
// Incompetent (p.81): the skill isn't low, it's ABSENT -- pinned to 0
|
|
448
|
+
// no matter what the sheet or the fallback would say.
|
|
449
|
+
if (this.qualities.has(`incompetent-${name}`))
|
|
450
|
+
return 0;
|
|
244
451
|
const explicit = this.skills[name];
|
|
245
452
|
if (typeof explicit === 'number')
|
|
246
453
|
return Math.max(0, explicit);
|
|
@@ -334,14 +541,21 @@ export class Player extends AbstractPlayer {
|
|
|
334
541
|
set equipment(newEquipment) {
|
|
335
542
|
this._equipment = newEquipment;
|
|
336
543
|
}
|
|
544
|
+
// An explicit cap set through the setter wins over the strength-derived
|
|
545
|
+
// default -- companion shells use this (a palm-sized drone slings a
|
|
546
|
+
// credstick, not a strength-1 humanoid's 30kg). The setter used to
|
|
547
|
+
// write a field the getter never read; it was dead code.
|
|
548
|
+
_carryCapOverrideKg;
|
|
337
549
|
get maxCarryingWeight() {
|
|
550
|
+
if (this._carryCapOverrideKg !== undefined)
|
|
551
|
+
return this._carryCapOverrideKg;
|
|
338
552
|
// Strength-derived: 20 + 10/point. The old flat default (50kg) is
|
|
339
553
|
// exactly what a strength-3 runner gets, so pre-sheet balance holds.
|
|
340
554
|
return 20 + this.strength * 10;
|
|
341
555
|
}
|
|
342
556
|
set maxCarryingWeight(weight) {
|
|
343
557
|
if (weight > 0) {
|
|
344
|
-
this.
|
|
558
|
+
this._carryCapOverrideKg = weight;
|
|
345
559
|
}
|
|
346
560
|
else {
|
|
347
561
|
console.error('Max carrying weight must be positive.');
|
|
@@ -628,7 +842,22 @@ export class Player extends AbstractPlayer {
|
|
|
628
842
|
* at everything, including not getting shot.
|
|
629
843
|
*/
|
|
630
844
|
get woundModifier() {
|
|
631
|
-
|
|
845
|
+
const painEditor = this.augmentations.has('pain-editor');
|
|
846
|
+
// High Pain Tolerance (p.74): one box simply doesn't count toward
|
|
847
|
+
// modifiers -- taken off the physical track first, spilling to stun if
|
|
848
|
+
// there's nothing physical to ignore. RAW forbids stacking it with a
|
|
849
|
+
// pain editor, so the chrome wins when both are present.
|
|
850
|
+
const ignore = !painEditor && this.qualities.has('high-pain-tolerance') ? 1 : 0;
|
|
851
|
+
const dmg = Math.max(0, this._damageTaken - ignore);
|
|
852
|
+
const spill = Math.max(0, ignore - this._damageTaken);
|
|
853
|
+
const stun = Math.max(0, this._stunTaken - spill);
|
|
854
|
+
// Low Pain Tolerance (p.82): the -1 arrives every 2 boxes, not 3.
|
|
855
|
+
const step = this.qualities.has('low-pain-tolerance') ? 2 : 3;
|
|
856
|
+
const raw = Math.floor(dmg / step) + Math.floor(stun / step);
|
|
857
|
+
// A pain editor files the first wound tier under "later": the -1 never
|
|
858
|
+
// arrives, though everything past it still does.
|
|
859
|
+
const eased = painEditor ? Math.max(0, raw - 1) : raw;
|
|
860
|
+
return -eased;
|
|
632
861
|
}
|
|
633
862
|
/** Dead: the physical track is full. */
|
|
634
863
|
isDown() {
|
|
@@ -680,10 +909,17 @@ export class Player extends AbstractPlayer {
|
|
|
680
909
|
const filled = Math.min(max, this._damageTaken);
|
|
681
910
|
return `${'■'.repeat(filled)}${'□'.repeat(max - filled)} (${filled}/${max})`;
|
|
682
911
|
}
|
|
683
|
-
/**
|
|
912
|
+
/**
|
|
913
|
+
* Clears stun boxes; returns how many were actually recovered. FATIGUE
|
|
914
|
+
* boxes (see lockedFatigue) are untouchable until their need is met --
|
|
915
|
+
* SR5e p.172: fatigue damage cannot be healed while the condition
|
|
916
|
+
* causing it still exists. Eating/sleeping unlocks them (markFed /
|
|
917
|
+
* markSlept), after which they heal like any stun.
|
|
918
|
+
*/
|
|
684
919
|
healStun(boxes) {
|
|
685
920
|
const before = this._stunTaken;
|
|
686
|
-
|
|
921
|
+
const floor = Math.min(before, this.lockedFatigue);
|
|
922
|
+
this._stunTaken = Math.max(floor, before - Math.max(0, Math.floor(boxes)));
|
|
687
923
|
return before - this._stunTaken;
|
|
688
924
|
}
|
|
689
925
|
/** The stun track, same rendering as conditionSummary(). */
|
|
@@ -702,7 +938,14 @@ export class Player extends AbstractPlayer {
|
|
|
702
938
|
// Off the meat plane your gun is just a memory: Matrix combat runs on
|
|
703
939
|
// attack programs (Logic-scaled), astral combat on raw will given
|
|
704
940
|
// form (Magic-scaled). Hot-sim's deeper immersion sharpens the
|
|
705
|
-
// programs' bite.
|
|
941
|
+
// programs' bite. A jumped-in drone fights with its MOUNT (armed
|
|
942
|
+
// airframes only -- attack.ts refuses an unarmed recon frame).
|
|
943
|
+
if (this.plane === 'drone') {
|
|
944
|
+
const quality = this.riggedDrone?.qualityBonus ?? 0;
|
|
945
|
+
return this.droneArmed
|
|
946
|
+
? { name: `${this.riggedDrone?.name ?? 'drone'} weapon mount`, damage: 2 + quality, poolBonus: 0 }
|
|
947
|
+
: { name: 'no weapon mounts', damage: 0, poolBonus: 0 };
|
|
948
|
+
}
|
|
706
949
|
if (this.plane === 'matrix') {
|
|
707
950
|
const hot = this.simMode === 'hot' ? 1 : 0;
|
|
708
951
|
// The deck IS the arsenal: its quality feeds program damage the way
|
|
@@ -717,19 +960,21 @@ export class Player extends AbstractPlayer {
|
|
|
717
960
|
if (this.plane === 'astral') {
|
|
718
961
|
return { name: 'astral strike', damage: 2 + Math.floor(Math.max(1, this.magic) / 2), poolBonus: 0 };
|
|
719
962
|
}
|
|
720
|
-
// Muscle drives melee
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
//
|
|
963
|
+
// Muscle drives melee, and the weapon CLASS drives everything else
|
|
964
|
+
// now (RAW gear pass, see Item.rangedBase/meleeBase/armorPen): a
|
|
965
|
+
// holdout no longer hits like a shotgun, and a knife's bite comes
|
|
966
|
+
// partly from ignoring armor. Quality still rides on top. A HOLSTERED
|
|
967
|
+
// weapon counts for nothing -- until it's drawn (brandish, or the
|
|
968
|
+
// auto-draw when an exchange opens), your hands are empty.
|
|
725
969
|
const strengthBase = Math.floor(this.strength / 2);
|
|
726
970
|
const weapon = this.weaponDrawn ? this.getCarriedWeapon() : null;
|
|
727
971
|
if (!weapon) {
|
|
728
|
-
|
|
972
|
+
// Bone lacing turns the fists themselves into weapons.
|
|
973
|
+
return { name: 'bare fists', damage: 1 + strengthBase + this.augBonus('unarmed-damage'), poolBonus: 0, armorPen: 0 };
|
|
729
974
|
}
|
|
730
975
|
const bonus = weapon.qualityBonus;
|
|
731
|
-
const base = weapon.category === Category.MeleeWeapon ?
|
|
732
|
-
return { name: weapon.name, damage: base + bonus, poolBonus: bonus };
|
|
976
|
+
const base = weapon.category === Category.MeleeWeapon ? weapon.meleeBase + strengthBase : weapon.rangedBase;
|
|
977
|
+
return { name: weapon.name, damage: base + bonus, poolBonus: bonus, armorPen: weapon.armorPen };
|
|
733
978
|
}
|
|
734
979
|
/** The weapon riding in a hand slot, drawn or holstered. */
|
|
735
980
|
getCarriedWeapon() {
|
|
@@ -754,14 +999,28 @@ export class Player extends AbstractPlayer {
|
|
|
754
999
|
}
|
|
755
1000
|
/** Total soak dice from every equipped armor-category item. */
|
|
756
1001
|
getArmorValue() {
|
|
1002
|
+
// RAW armor pass (p.436-437, compressed): the BODY piece carries the
|
|
1003
|
+
// real protection (1 + quality, +1 for jacket/plate-class weight --
|
|
1004
|
+
// RAW clothing 6 vs jacket 12), a helmet or shield adds its flat
|
|
1005
|
+
// point, and trim (boots, gloves, arm/leg pieces) is flavor, not
|
|
1006
|
+
// dice -- stacking four rated accessories used to out-armor the
|
|
1007
|
+
// jacket they hung off.
|
|
757
1008
|
let total = 0;
|
|
758
1009
|
for (const group of Object.values(this._equipment)) {
|
|
759
1010
|
if (!group)
|
|
760
1011
|
continue;
|
|
761
1012
|
for (const item of Object.values(group)) {
|
|
762
|
-
if (item
|
|
763
|
-
|
|
764
|
-
|
|
1013
|
+
if (!item || !Player.ARMOR_CATEGORIES.has(item.category))
|
|
1014
|
+
continue;
|
|
1015
|
+
if (item.category === Category.Helmet)
|
|
1016
|
+
total += 1;
|
|
1017
|
+
else if (item.category === Category.Shield)
|
|
1018
|
+
total += 2;
|
|
1019
|
+
else if (item.category === Category.Boots || item.category === Category.Gloves
|
|
1020
|
+
|| item.category === Category.ArmArmor || item.category === Category.LegArmor)
|
|
1021
|
+
total += 0;
|
|
1022
|
+
else
|
|
1023
|
+
total += 1 + item.qualityBonus + (item.isHeavyArmor ? 1 : 0);
|
|
765
1024
|
}
|
|
766
1025
|
}
|
|
767
1026
|
return total;
|
|
@@ -769,21 +1028,33 @@ export class Player extends AbstractPlayer {
|
|
|
769
1028
|
getAttackPool() {
|
|
770
1029
|
// Plane decides which attribute drives the fight: meat is muscle
|
|
771
1030
|
// (Agility), the Matrix is mind (Logic, hot-sim +2), the astral is
|
|
772
|
-
// will (Willpower + half Magic)
|
|
1031
|
+
// will (Willpower + half Magic), and a jumped-in drone is GUNNERY --
|
|
1032
|
+
// Logic + piloting through the control rig (its +2 rides
|
|
1033
|
+
// augBonus('piloting')), hot-sim +1 (p.266: matrix bonuses apply to
|
|
1034
|
+
// vehicle actions). The SKILL is per-armament now (see
|
|
773
1035
|
// activeCombatSkill / skillRating): firearms, blades, or unarmed in
|
|
774
1036
|
// the meat; hacking is cybercombat; astral strikes ride unarmed (will
|
|
775
1037
|
// given form). Wounds and sustaining drag everywhere.
|
|
1038
|
+
if (this.plane === 'drone') {
|
|
1039
|
+
const hot = this.simMode === 'hot' ? 1 : 0;
|
|
1040
|
+
return Math.max(0, this.logic + this.skillRating('piloting') + this.augBonus('piloting') + this.qualityBonus('piloting') + hot + this.woundModifier - this.sustainingPenalty);
|
|
1041
|
+
}
|
|
776
1042
|
if (this.plane === 'matrix') {
|
|
777
1043
|
const hot = this.simMode === 'hot' ? 2 : 0;
|
|
778
|
-
|
|
1044
|
+
// Attack Upgrade echo (p.258): +1 on the offensive matrix pool.
|
|
1045
|
+
return Math.max(0, this.logic + this.skillRating('hacking') + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('hacking') + (this.hasMetamagic('attack-upgrade') ? 1 : 0) + this.woundModifier - this.sustainingPenalty);
|
|
779
1046
|
}
|
|
780
1047
|
if (this.plane === 'astral') {
|
|
781
1048
|
return Math.max(0, this.willpower + Math.floor(this.magic / 2) + this.skillRating('unarmed') + this.woundModifier - this.sustainingPenalty);
|
|
782
1049
|
}
|
|
783
1050
|
// An adept's magic lives in their hands: half of it (rounded down)
|
|
784
1051
|
// rides every meat-plane strike -- killing hands, improved reflexes.
|
|
785
|
-
|
|
786
|
-
|
|
1052
|
+
// Chrome rides here too: muscle toner on every swing, the smartlink
|
|
1053
|
+
// only when the drawn armament is actually a firearm.
|
|
1054
|
+
// Power Point (initiation, adepts): one more point of magic-as-muscle.
|
|
1055
|
+
const adeptBonus = this.adept ? Math.floor(this.magic / 2) + (this.hasMetamagic('power-point') ? 1 : 0) : 0;
|
|
1056
|
+
const smartlink = this.activeCombatSkill() === 'firearms' && this.weaponDrawn ? this.augBonus('firearms') : 0;
|
|
1057
|
+
return Math.max(0, this.agility + this.skillRating(this.activeCombatSkill()) + adeptBonus + this.getWeaponProfile().poolBonus + this.augBonus('attack') + smartlink + this.woundModifier - this.sustainingPenalty);
|
|
787
1058
|
}
|
|
788
1059
|
/**
|
|
789
1060
|
* The subdual pool: bare-hands work regardless of what's in the other
|
|
@@ -791,34 +1062,44 @@ export class Player extends AbstractPlayer {
|
|
|
791
1062
|
* See subdue.ts.
|
|
792
1063
|
*/
|
|
793
1064
|
getUnarmedAttackPool() {
|
|
794
|
-
const adeptBonus = this.adept ? Math.floor(this.magic / 2) : 0;
|
|
795
|
-
return Math.max(0, this.agility + this.skillRating('unarmed') + adeptBonus + this.woundModifier - this.sustainingPenalty);
|
|
1065
|
+
const adeptBonus = this.adept ? Math.floor(this.magic / 2) + (this.hasMetamagic('power-point') ? 1 : 0) : 0;
|
|
1066
|
+
return Math.max(0, this.agility + this.skillRating('unarmed') + adeptBonus + this.augBonus('attack') + this.woundModifier - this.sustainingPenalty);
|
|
796
1067
|
}
|
|
797
1068
|
getDefensePool() {
|
|
798
1069
|
// Reaction plus HALF intuition (rounded down) -- full SR
|
|
799
1070
|
// Reaction+Intuition turned text-paced exchanges into whiff-fests, so
|
|
800
1071
|
// gut feel contributes at half weight. Intuition 3 (the default) adds
|
|
801
1072
|
// +1 over the old reaction-only pool. Off-plane variants swap the
|
|
802
|
-
// reflex attribute but keep the same shape.
|
|
1073
|
+
// reflex attribute but keep the same shape. Jumped-in: the rigger's
|
|
1074
|
+
// own reflexes drive the airframe, control rig sharpening handling.
|
|
1075
|
+
if (this.plane === 'drone') {
|
|
1076
|
+
return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + this.augBonus('piloting') + this.qualityBonus('piloting') + this.woundModifier - this.sustainingPenalty);
|
|
1077
|
+
}
|
|
803
1078
|
if (this.plane === 'matrix') {
|
|
804
1079
|
const hot = this.simMode === 'hot' ? 1 : 0;
|
|
805
|
-
return Math.max(0, this.intuition + Math.floor(this.logic / 2) + Math.floor(this.resonance / 2) + hot + this.woundModifier - this.sustainingPenalty);
|
|
1080
|
+
return Math.max(0, this.intuition + Math.floor(this.logic / 2) + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('matrix') + this.woundModifier - this.sustainingPenalty);
|
|
806
1081
|
}
|
|
807
1082
|
if (this.plane === 'astral') {
|
|
808
1083
|
return Math.max(0, this.intuition + Math.floor(this.magic / 2) + this.woundModifier - this.sustainingPenalty);
|
|
809
1084
|
}
|
|
810
1085
|
// Combat sense: the adept's magic reads the fight before it happens.
|
|
1086
|
+
// Wired reflexes read it faster still.
|
|
811
1087
|
const adeptBonus = this.adept ? Math.floor(this.magic / 2) : 0;
|
|
812
|
-
return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + adeptBonus + this.woundModifier - this.sustainingPenalty);
|
|
1088
|
+
return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + adeptBonus + this.augBonus('defense') + this.woundModifier - this.sustainingPenalty);
|
|
813
1089
|
}
|
|
814
1090
|
getSoakPool() {
|
|
815
1091
|
// Matrix: firewall -- Logic plus the DECK's quality (your deck is your
|
|
816
1092
|
// armor); a technomancer's firewall is Willpower plus half resonance
|
|
817
1093
|
// (their mind guards itself). Astral: pure Willpower; worn armor is
|
|
818
|
-
// meat left behind.
|
|
1094
|
+
// meat left behind. Drone: the HULL soaks -- airframe body scaled by
|
|
1095
|
+
// build quality; the rider's armor is streets away with their body.
|
|
1096
|
+
if (this.plane === 'drone') {
|
|
1097
|
+
return Math.max(0, 4 + 2 * (this.riggedDrone?.qualityBonus ?? 0));
|
|
1098
|
+
}
|
|
819
1099
|
if (this.plane === 'matrix') {
|
|
820
1100
|
if (this.isTechnomancer()) {
|
|
821
|
-
|
|
1101
|
+
// Firewall Upgrade echo (p.258): the living persona hardens.
|
|
1102
|
+
return Math.max(0, this.willpower + Math.floor(this.resonance / 2) + (this.hasMetamagic('firewall-upgrade') ? 1 : 0));
|
|
822
1103
|
}
|
|
823
1104
|
const deck = this.activeDeck ?? this.getCyberdeck();
|
|
824
1105
|
return Math.max(0, this.logic + (deck?.qualityBonus ?? 0));
|
|
@@ -826,7 +1107,7 @@ export class Player extends AbstractPlayer {
|
|
|
826
1107
|
if (this.plane === 'astral') {
|
|
827
1108
|
return Math.max(0, this.willpower);
|
|
828
1109
|
}
|
|
829
|
-
return Math.max(0, this.body + this.getArmorValue() + this._sustainedArmor);
|
|
1110
|
+
return Math.max(0, this.body + this.getArmorValue() + this._sustainedArmor + this.augBonus('soak') + this.qualityBonus('soak'));
|
|
830
1111
|
}
|
|
831
1112
|
/**
|
|
832
1113
|
* Initiative: Reaction + Intuition (straight SR), plus the AR edge from
|
|
@@ -838,18 +1119,34 @@ export class Player extends AbstractPlayer {
|
|
|
838
1119
|
getInitiativePool() {
|
|
839
1120
|
// Matrix speed is thought-speed: Logic + Intuition, hot-sim +2 (the
|
|
840
1121
|
// whole point of running hot). Astral forms move at the speed of
|
|
841
|
-
// intention: Intuition doubled.
|
|
1122
|
+
// intention: Intuition doubled. Jumped-in rides VR reflexes (p.266:
|
|
1123
|
+
// cold +2, hot +3 -- the engine's flattened take on the extra dice).
|
|
1124
|
+
//
|
|
1125
|
+
// NO sustaining penalty here (RAW spell pass): initiative is a score,
|
|
1126
|
+
// not a TEST -- holding a spell slows what you DO, never when you get
|
|
1127
|
+
// to do it. (This also lets Increase Reflexes actually work: its +2
|
|
1128
|
+
// used to be eaten whole by its own -2 sustaining burden.) Wounds
|
|
1129
|
+
// still slow the draw -- pain is not a spell.
|
|
1130
|
+
if (this.plane === 'drone') {
|
|
1131
|
+
const vr = this.simMode === 'hot' ? 3 : 2;
|
|
1132
|
+
return Math.max(0, this.reaction + this.intuition + vr + this.woundModifier);
|
|
1133
|
+
}
|
|
842
1134
|
if (this.plane === 'matrix') {
|
|
843
|
-
|
|
844
|
-
|
|
1135
|
+
// Overclocking echo (p.258): +1 while running hot -- the living
|
|
1136
|
+
// persona accelerated past spec.
|
|
1137
|
+
const hot = this.simMode === 'hot' ? 2 + (this.hasMetamagic('overclocking') ? 1 : 0) : 0;
|
|
1138
|
+
return Math.max(0, this.logic + this.intuition + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('matrix') + this.woundModifier);
|
|
845
1139
|
}
|
|
846
1140
|
if (this.plane === 'astral') {
|
|
847
|
-
return Math.max(0, this.intuition * 2 + this.woundModifier
|
|
1141
|
+
return Math.max(0, this.intuition * 2 + this.woundModifier);
|
|
848
1142
|
}
|
|
849
1143
|
const link = this._equipment.head?.commlink;
|
|
850
1144
|
// AR needs a link that's on you, broadcasting, and not hacked dark.
|
|
851
1145
|
const arBonus = link && !this.runningSilent && !this.arOffline ? link.qualityBonus : 0;
|
|
852
|
-
|
|
1146
|
+
// Increase Reflexes (sustained, cast.ts): the spellwork equivalent of
|
|
1147
|
+
// wired reflexes, for as long as the caster holds it.
|
|
1148
|
+
const spellReflexes = this.sustainedReflexes ? 2 : 0;
|
|
1149
|
+
return Math.max(0, this.reaction + this.intuition + arBonus + this.augBonus('initiative') + spellReflexes + this.woundModifier);
|
|
853
1150
|
}
|
|
854
1151
|
/**
|
|
855
1152
|
* If an edge boost is armed (see commands/edge.ts), consume it: returns
|
|
@@ -863,11 +1160,60 @@ export class Player extends AbstractPlayer {
|
|
|
863
1160
|
this.edgeBoostArmed = false;
|
|
864
1161
|
return this.edge;
|
|
865
1162
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1163
|
+
// A comfortable home sharpens the luck: Middle+ lifestyles bank a bonus
|
|
1164
|
+
// edge each job (set by Game from the active tier; 0 for NPCs).
|
|
1165
|
+
lifestyleEdgeBonus = 0;
|
|
1166
|
+
// ==================== Needs (SR5e p.56 / p.172) ====================
|
|
1167
|
+
// Edge comes back from "a fulfilling meal and a good night's sleep" --
|
|
1168
|
+
// so eating (use.ts) and sleeping (rest.ts, at a Residence) are tracked
|
|
1169
|
+
// per JOB-CYCLE and cashed in at departure (Game.applyDepartureNeeds).
|
|
1170
|
+
// Ignore a need across multiple cycles and RAW fatigue stacks: 1S, 2S,
|
|
1171
|
+
// 3S... resisted Body + Willpower -- and fatigue stun is LOCKED (won't
|
|
1172
|
+
// heal by rest, stim, food's own recovery, or the doc) until the need
|
|
1173
|
+
// that caused it is finally met.
|
|
1174
|
+
// Sessions start fresh off your own cot: fed, slept, sharp.
|
|
1175
|
+
ateThisCycle = true;
|
|
1176
|
+
sleptThisCycle = true;
|
|
1177
|
+
hungerStreak = 0;
|
|
1178
|
+
sleepStreak = 0;
|
|
1179
|
+
_hungerFatigue = 0;
|
|
1180
|
+
_sleepFatigue = 0;
|
|
1181
|
+
/** Stun boxes no recovery can touch until their need is met. */
|
|
1182
|
+
get lockedFatigue() {
|
|
1183
|
+
return this._hungerFatigue + this._sleepFatigue;
|
|
1184
|
+
}
|
|
1185
|
+
/** A real meal: hunger resets and hunger-fatigue unlocks (heals normally now). */
|
|
1186
|
+
markFed() {
|
|
1187
|
+
this.ateThisCycle = true;
|
|
1188
|
+
this.hungerStreak = 0;
|
|
1189
|
+
this._hungerFatigue = 0;
|
|
1190
|
+
}
|
|
1191
|
+
/** A real sleep (a bed, not a jobsite corner): sleep debt resets and unlocks. */
|
|
1192
|
+
markSlept() {
|
|
1193
|
+
this.sleptThisCycle = true;
|
|
1194
|
+
this.sleepStreak = 0;
|
|
1195
|
+
this._sleepFatigue = 0;
|
|
1196
|
+
}
|
|
1197
|
+
/** Records freshly-taken stun as need-locked fatigue. */
|
|
1198
|
+
addLockedFatigue(kind, boxes) {
|
|
1199
|
+
if (kind === 'hunger')
|
|
1200
|
+
this._hungerFatigue += Math.max(0, boxes);
|
|
1201
|
+
else
|
|
1202
|
+
this._sleepFatigue += Math.max(0, boxes);
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* New job bookkeeping -- the boost and the once-per-job haggle always
|
|
1206
|
+
* reset, but the edge POOL only refills when the runner left fed and
|
|
1207
|
+
* slept (SR5e p.56: a fulfilling meal and a good night's sleep).
|
|
1208
|
+
*/
|
|
1209
|
+
refreshEdge(wellRested = true) {
|
|
1210
|
+
if (wellRested) {
|
|
1211
|
+
this.edgeRemaining = this.edge + this.lifestyleEdgeBonus;
|
|
1212
|
+
}
|
|
869
1213
|
this.edgeBoostArmed = false;
|
|
870
1214
|
this.haggledThisJob = false;
|
|
1215
|
+
// A new job is a new room to read: First Impression re-arms (p.74).
|
|
1216
|
+
this._firstImpressionUsed = false;
|
|
871
1217
|
}
|
|
872
1218
|
// ======================== Magic (see commands/cast.ts) ========================
|
|
873
1219
|
// Two roads to casting. GEAR-DRIVEN: what a mundane can cast comes from
|
|
@@ -900,11 +1246,25 @@ export class Player extends AbstractPlayer {
|
|
|
900
1246
|
setSustainedArmor(bonus) {
|
|
901
1247
|
this._sustainedArmor = Math.max(0, Math.floor(bonus));
|
|
902
1248
|
}
|
|
1249
|
+
// The other sustained spells (RAW gear/spell pass): invisibility bends
|
|
1250
|
+
// light around the bearer (+3 on sneak contests, npc.ts), increase
|
|
1251
|
+
// reflexes runs the nerves hot (+2 initiative). Each holds the standard
|
|
1252
|
+
// -2 sustaining burden; cast again to release (cast.ts).
|
|
1253
|
+
sustainedInvisibility = false;
|
|
1254
|
+
sustainedReflexes = false;
|
|
903
1255
|
get sustainingPenalty() {
|
|
904
1256
|
// Each sustained effect costs -2 dice across the board: the armor
|
|
905
|
-
// spell, and open astral senses (see
|
|
906
|
-
// -- holding
|
|
907
|
-
|
|
1257
|
+
// spell, invisibility, hot reflexes, and open astral senses (see
|
|
1258
|
+
// commands/assense.ts). They stack -- holding two is walking around
|
|
1259
|
+
// half-blind. Dumpshock disorientation (p.229) rides the same rail.
|
|
1260
|
+
// QUICKENING (metamagic, p.326 adapted): the armor spell anchors into
|
|
1261
|
+
// the world and sustains itself -- its -2 simply never bills.
|
|
1262
|
+
const armorCost = this._sustainedArmor > 0 && !this.hasMetamagic('quickening') ? 2 : 0;
|
|
1263
|
+
return armorCost
|
|
1264
|
+
+ (this.sustainedInvisibility ? 2 : 0)
|
|
1265
|
+
+ (this.sustainedReflexes ? 2 : 0)
|
|
1266
|
+
+ (this.astralPerceiving ? 2 : 0)
|
|
1267
|
+
+ (this.dumpshocked ? 2 : 0);
|
|
908
1268
|
}
|
|
909
1269
|
// Astral perception (see commands/assense.ts): senses held OPEN as a
|
|
910
1270
|
// sustained state -- passive aura reads, at -2 dice to everything else.
|
|
@@ -915,9 +1275,40 @@ export class Player extends AbstractPlayer {
|
|
|
915
1275
|
// Sneaking-vs-Perception rolls per witness (NPC.see). Broken by any
|
|
916
1276
|
// loud act -- attacking, casting, speaking, brandishing, calling.
|
|
917
1277
|
sneaking = false;
|
|
1278
|
+
// ==================== Jumped-in rigging (commands/jump.ts) ====================
|
|
1279
|
+
// The drone currently being ridden while plane === 'drone': the item
|
|
1280
|
+
// whose hull track (Item.droneDamage) eats what would hit the rider,
|
|
1281
|
+
// whose quality feeds the drone pools, and whose destruction is a
|
|
1282
|
+
// DUMPSHOCK (see planes.ts leaveDrone).
|
|
1283
|
+
riggedDrone;
|
|
1284
|
+
/** Whether the ridden drone carries a weapon mount (name/description duck-typing). */
|
|
1285
|
+
get droneArmed() {
|
|
1286
|
+
const d = this.riggedDrone;
|
|
1287
|
+
if (!d)
|
|
1288
|
+
return false;
|
|
1289
|
+
return /armed|combat|gun|roto|assault|sentry|weapon/i.test(`${d.name} ${d.description ?? ''}`);
|
|
1290
|
+
}
|
|
1291
|
+
// Dumpshock disorientation (SR5e p.229): -2 on everything for
|
|
1292
|
+
// (10 - Willpower) minutes after a forced dump, wall-clock -- the
|
|
1293
|
+
// penalty rides sustainingPenalty so every pool already feels it.
|
|
1294
|
+
dumpshockedUntil = 0;
|
|
1295
|
+
get dumpshocked() {
|
|
1296
|
+
return Date.now() < this.dumpshockedUntil;
|
|
1297
|
+
}
|
|
918
1298
|
// One haggle per job (see commands/haggle.ts); reset with the edge pool
|
|
919
1299
|
// on each new job (refreshEdge).
|
|
920
1300
|
haggledThisJob = false;
|
|
1301
|
+
// A drink in you (see use.ts): +1 die on the next social roll --
|
|
1302
|
+
// persuade, haggle, bluff -- then it's gone. A second drink before it's
|
|
1303
|
+
// spent doesn't stack, it just costs a stun box ("one too many").
|
|
1304
|
+
liquidCourage = false;
|
|
1305
|
+
/** Spends the drink: returns its bonus die for the roll happening NOW. */
|
|
1306
|
+
consumeLiquidCourage() {
|
|
1307
|
+
if (!this.liquidCourage)
|
|
1308
|
+
return 0;
|
|
1309
|
+
this.liquidCourage = false;
|
|
1310
|
+
return 1;
|
|
1311
|
+
}
|
|
921
1312
|
// Where this actor just came from and the direction BACK -- the HUD's
|
|
922
1313
|
// breadcrumb (see game.ts updateStatus / go.ts). Big scenes (the hub's
|
|
923
1314
|
// 18 rooms) made "which way did I come in" a real question. Cleared on
|