@maka/maka-cli 5.3.16 → 5.4.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/create.js +1 -1
- package/bundle/typescript/src/commands/create.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.d.ts +43 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +131 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +79 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.d.ts +42 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +224 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +28 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +101 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +149 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +58 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +378 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.d.ts +11 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.js +8 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js +60 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js +31 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.js +27 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.d.ts +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +40 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +43 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +7 -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 +11 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +115 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +25 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +176 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +39 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +64 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.d.ts +10 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.js +11 -33
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +35 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +46 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js +55 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.d.ts +9 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +18 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +54 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/read.js +6 -5
- package/bundle/typescript/src/commands/game/sideQuest/commands/read.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js +52 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js +40 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +59 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +23 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +20 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +49 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +50 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +32 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +65 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js +41 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.js +43 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.d.ts +20 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +45 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.d.ts +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +80 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +1 -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 +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +101 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
- 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/npc-factory.js +7 -2
- package/bundle/typescript/src/commands/game/sideQuest/factories/npc-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.d.ts +8 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +138 -21
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +119 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +816 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +70 -9
- package/bundle/typescript/src/commands/game/sideQuest/game.js +403 -164
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.js +101 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.js +95 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/door.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js +15 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js +54 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +41 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +106 -3
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +25 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +220 -47
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +213 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +525 -9
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +75 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +174 -6
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +20 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +39 -18
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +69 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +175 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +80 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +7 -2
- package/bundle/typescript/src/commands/game/sideQuest/types/player-equipment.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/exit-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/npc-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +34 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +15 -1
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/ui.d.ts +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +95 -5
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.d.ts +60 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +138 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +143 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +328 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.d.ts +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.js +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +22 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +34 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.d.ts +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js +92 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +44 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +193 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.js +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +54 -7
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/set/global.sub.cmd.js +59 -5
- package/bundle/typescript/src/commands/set/global.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/tools/ai/ai.class.d.ts +35 -0
- package/bundle/typescript/src/tools/ai/ai.class.js +112 -0
- package/bundle/typescript/src/tools/ai/ai.class.js.map +1 -0
- package/bundle/typescript/src/tools/fsi/fsi.interfaces.d.ts +1 -0
- package/bundle/typescript/src/tools/fsi/fsi.interfaces.js.map +1 -1
- package/bundle/typescript/src/tools/https/https.class.d.ts +2 -1
- package/bundle/typescript/src/tools/https/https.class.js +43 -0
- package/bundle/typescript/src/tools/https/https.class.js.map +1 -1
- package/bundle/typescript/src/tools/https/https.interface.d.ts +8 -0
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import blessed from 'blessed';
|
|
2
2
|
import { capitalCase } from 'change-case';
|
|
3
|
-
import {
|
|
3
|
+
import { Room } from './models/_index.js';
|
|
4
|
+
import { Direction } from './types/shared/direction-enum.js';
|
|
5
|
+
import { Logger } from './utilities/logger.js';
|
|
4
6
|
import { LookCommand } from './commands/look.js';
|
|
5
7
|
import { TakeCommand } from './commands/take.js';
|
|
6
8
|
import { GoCommand } from './commands/go.js';
|
|
@@ -18,20 +20,72 @@ import { StoreCommand } from './commands/store.js';
|
|
|
18
20
|
import { GiveCommand } from './commands/give.js';
|
|
19
21
|
import { ReadCommand } from './commands/read.js';
|
|
20
22
|
import { KillCommand } from './commands/kill.js';
|
|
23
|
+
import { AttackCommand } from './commands/attack.js';
|
|
24
|
+
import { SpellsCommand } from './commands/spells.js';
|
|
25
|
+
import { InventoryCommand } from './commands/inv.js';
|
|
26
|
+
import { EquipmentCommand } from './commands/equipment.js';
|
|
27
|
+
import { MapCommand } from './commands/map.js';
|
|
28
|
+
import { SilentCommand } from './commands/silent.js';
|
|
29
|
+
import { RestCommand } from './commands/rest.js';
|
|
30
|
+
import { ReloadCommand } from './commands/reload.js';
|
|
31
|
+
import { SubdueCommand } from './commands/subdue.js';
|
|
32
|
+
import { EdgeCommand } from './commands/edge.js';
|
|
33
|
+
import { SheetCommand } from './commands/sheet.js';
|
|
34
|
+
import { CallCommand } from './commands/call.js';
|
|
35
|
+
import { EndCallCommand } from './commands/end-call.js';
|
|
36
|
+
import { PickCommand } from './commands/pick.js';
|
|
37
|
+
import { HackCommand } from './commands/hack.js';
|
|
38
|
+
import { CastCommand } from './commands/cast.js';
|
|
39
|
+
import { StanceCommand } from './commands/stance.js';
|
|
40
|
+
import { SurrenderCommand } from './commands/surrender.js';
|
|
41
|
+
import { JackCommand } from './commands/jack.js';
|
|
42
|
+
import { ProjectCommand, ReturnCommand } from './commands/project.js';
|
|
43
|
+
import { AssenseCommand } from './commands/assense.js';
|
|
44
|
+
import { DroneCommand } from './commands/drone.js';
|
|
45
|
+
import { PanCommand } from './commands/pan.js';
|
|
46
|
+
import { RebootCommand } from './commands/reboot.js';
|
|
47
|
+
import { AutoFight } from './utilities/auto-fight.js';
|
|
21
48
|
import { CommandFactory } from './factories/command-factory.js';
|
|
22
49
|
import { SceneSynthesizer } from './factories/scene-factory.js';
|
|
50
|
+
import { SceneSeedGenerator, FIXER_NAME } from './factories/scene-seed-generator.js';
|
|
51
|
+
import { CALL_ICON, CALL_COLOR } from './utilities/comm-style.js';
|
|
23
52
|
export default class Game {
|
|
24
53
|
initialized;
|
|
25
54
|
static instance;
|
|
26
55
|
sceneSeed;
|
|
27
56
|
scene;
|
|
28
57
|
player;
|
|
58
|
+
// The fight autopilot (Tab / "stance" command) -- created once, reads
|
|
59
|
+
// live game state at fire time so it survives scene transitions.
|
|
60
|
+
autoFight = new AutoFight(this);
|
|
61
|
+
// Just three surfaces: a full-width log, a HUD strip, and the input box.
|
|
62
|
+
// The old always-on Inventory/Equipment/Map sidebar boxes became the
|
|
63
|
+
// on-demand inv/equipment/map commands (see commands/inv.ts et al.) to
|
|
64
|
+
// give the log the whole screen.
|
|
29
65
|
_screen;
|
|
30
|
-
_exitsBox;
|
|
31
|
-
_inventoryBox;
|
|
32
66
|
_logBox;
|
|
67
|
+
_statusBox;
|
|
33
68
|
_inputBox;
|
|
34
|
-
|
|
69
|
+
_useAi = false;
|
|
70
|
+
_logFilePath = '';
|
|
71
|
+
_tier = 1;
|
|
72
|
+
// Guards against two overlapping continueToNextScene() calls -- found via
|
|
73
|
+
// a real session where calling the fixer twice in a row (nothing stopped
|
|
74
|
+
// it, and a 20-40+ second draft invites a second, impatient call) fired
|
|
75
|
+
// two independent drafts at once. Both read the same starting _tier
|
|
76
|
+
// before either finished, both generated a "tier 2" job, and whichever
|
|
77
|
+
// one finished last silently overwrote the first one's scene, room, and
|
|
78
|
+
// player-location state out from under whatever the player was already
|
|
79
|
+
// doing in it.
|
|
80
|
+
_draftInProgress = false;
|
|
81
|
+
// A single fixed room, created once and never torn down across job
|
|
82
|
+
// transitions -- the player's own place, unlike every job's generated
|
|
83
|
+
// rooms which get discarded wholesale when the next job replaces them.
|
|
84
|
+
_homeRoom;
|
|
85
|
+
_homeRoomExitDirection;
|
|
86
|
+
static HOME_DIRECTION_PRIORITY = [
|
|
87
|
+
Direction.DOWN, Direction.UP, Direction.SOUTH, Direction.NORTH, Direction.EAST, Direction.WEST,
|
|
88
|
+
];
|
|
35
89
|
constructor(player, sceneSeed) {
|
|
36
90
|
this.player = player;
|
|
37
91
|
this.sceneSeed = sceneSeed;
|
|
@@ -55,10 +109,65 @@ export default class Game {
|
|
|
55
109
|
CommandFactory.registerCommand('give', GiveCommand);
|
|
56
110
|
CommandFactory.registerCommand('read', ReadCommand);
|
|
57
111
|
CommandFactory.registerCommand('kill', KillCommand);
|
|
112
|
+
CommandFactory.registerCommand('attack', AttackCommand);
|
|
113
|
+
CommandFactory.registerCommand('spells', SpellsCommand);
|
|
114
|
+
CommandFactory.registerCommand('inv', InventoryCommand);
|
|
115
|
+
CommandFactory.registerCommand('inventory', InventoryCommand);
|
|
116
|
+
CommandFactory.registerCommand('equipment', EquipmentCommand);
|
|
117
|
+
CommandFactory.registerCommand('eq', EquipmentCommand);
|
|
118
|
+
CommandFactory.registerCommand('map', MapCommand);
|
|
119
|
+
CommandFactory.registerCommand('exits', MapCommand);
|
|
120
|
+
CommandFactory.registerCommand('silent', SilentCommand);
|
|
121
|
+
CommandFactory.registerCommand('rest', RestCommand);
|
|
122
|
+
CommandFactory.registerCommand('reload', ReloadCommand);
|
|
123
|
+
CommandFactory.registerCommand('subdue', SubdueCommand);
|
|
124
|
+
CommandFactory.registerCommand('edge', EdgeCommand);
|
|
125
|
+
CommandFactory.registerCommand('sheet', SheetCommand);
|
|
126
|
+
CommandFactory.registerCommand('stats', SheetCommand);
|
|
127
|
+
CommandFactory.registerCommand('call', CallCommand);
|
|
128
|
+
CommandFactory.registerCommand('end-call', EndCallCommand);
|
|
129
|
+
CommandFactory.registerCommand('pick', PickCommand);
|
|
130
|
+
CommandFactory.registerCommand('hack', HackCommand);
|
|
131
|
+
CommandFactory.registerCommand('cast', CastCommand);
|
|
132
|
+
CommandFactory.registerCommand('stance', StanceCommand);
|
|
133
|
+
CommandFactory.registerCommand('surrender', SurrenderCommand);
|
|
134
|
+
CommandFactory.registerCommand('jack', JackCommand);
|
|
135
|
+
CommandFactory.registerCommand('project', ProjectCommand);
|
|
136
|
+
CommandFactory.registerCommand('return', ReturnCommand);
|
|
137
|
+
CommandFactory.registerCommand('assense', AssenseCommand);
|
|
138
|
+
CommandFactory.registerCommand('drone', DroneCommand);
|
|
139
|
+
CommandFactory.registerCommand('pan', PanCommand);
|
|
140
|
+
CommandFactory.registerCommand('reboot', RebootCommand);
|
|
58
141
|
}
|
|
59
142
|
determineStartRoom() {
|
|
60
143
|
return this.scene.determineStartRoom();
|
|
61
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Wires a fresh, always-unlocked path between the persistent home base
|
|
147
|
+
* and wherever the player is starting the current job from. Clears the
|
|
148
|
+
* previous job's connection first (if any) so the home base never keeps
|
|
149
|
+
* a stale door into an abandoned scene's now-orphaned rooms -- only ever
|
|
150
|
+
* one way out at a time, pointing at whichever job is actually current.
|
|
151
|
+
*/
|
|
152
|
+
connectHomeRoom(jobStartRoom) {
|
|
153
|
+
if (this._homeRoomExitDirection) {
|
|
154
|
+
this._homeRoom.exits.delete(this._homeRoomExitDirection);
|
|
155
|
+
}
|
|
156
|
+
const direction = Game.HOME_DIRECTION_PRIORITY.find(d => !jobStartRoom.exits.has(d));
|
|
157
|
+
if (!direction) {
|
|
158
|
+
// Every direction is already spoken for on this job's start room --
|
|
159
|
+
// rare (most generated start rooms use 1-2 exits), but leave the
|
|
160
|
+
// player without a home-base link this job rather than clobbering
|
|
161
|
+
// one of the job's own exits.
|
|
162
|
+
this._homeRoomExitDirection = undefined;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
jobStartRoom.addExit(direction, this._homeRoom);
|
|
166
|
+
// Tracked from the HOME room's own side (the opposite of `direction`,
|
|
167
|
+
// which is the job room's side) -- that's the key actually stored on
|
|
168
|
+
// this._homeRoom.exits, and what gets cleared above next time around.
|
|
169
|
+
this._homeRoomExitDirection = jobStartRoom.getOppositeDirection(direction);
|
|
170
|
+
}
|
|
62
171
|
get logBox() {
|
|
63
172
|
return this._logBox;
|
|
64
173
|
}
|
|
@@ -66,11 +175,11 @@ export default class Game {
|
|
|
66
175
|
return this._inputBox;
|
|
67
176
|
}
|
|
68
177
|
async init(options, screen) {
|
|
69
|
-
this.
|
|
178
|
+
this._useAi = options.useAi;
|
|
179
|
+
this._logFilePath = options.logFilePath;
|
|
70
180
|
this.initInputBox(screen);
|
|
71
181
|
this.initLogBox(screen);
|
|
72
|
-
this.
|
|
73
|
-
this.initInventoryBox(screen);
|
|
182
|
+
this.initStatusBox(screen);
|
|
74
183
|
// Create a loading box
|
|
75
184
|
const loadingBox = blessed.box({
|
|
76
185
|
parent: screen,
|
|
@@ -101,8 +210,139 @@ export default class Game {
|
|
|
101
210
|
this.player.currentLocation = this.determineStartRoom();
|
|
102
211
|
this.registerCommands();
|
|
103
212
|
this._screen = screen;
|
|
213
|
+
this._homeRoom = new Room({
|
|
214
|
+
name: 'Your Hideout',
|
|
215
|
+
description: `A cramped, windowless room you can actually call your own -- a cot, a locker, and a door that locks from the inside. Whatever you leave here stays here, no matter where the next job takes you.`,
|
|
216
|
+
roomType: 'Residence',
|
|
217
|
+
});
|
|
218
|
+
this.scene.addRoom(this._homeRoom);
|
|
219
|
+
this.connectHomeRoom(this.player.currentLocation);
|
|
104
220
|
this.initialized = true;
|
|
105
|
-
|
|
221
|
+
// Nudge the player toward "call" up front -- the fixer convention means
|
|
222
|
+
// the story deliberately doesn't narrate a completed briefing, so
|
|
223
|
+
// without this hint there'd be no in-game indication that "call" is how
|
|
224
|
+
// you actually get your orders.
|
|
225
|
+
const wc = this.scene.getWinCondition();
|
|
226
|
+
const callHint = (wc?.type === 'give' && wc.toNpc)
|
|
227
|
+
? `\n\nYour commlink is quiet for now, but ${wc.toNpc} is expecting to hear from you -- try "call ${wc.toNpc}" when you're ready to check in.`
|
|
228
|
+
: '';
|
|
229
|
+
return `Welcome, ${this.player.name}\n ${this.scene.story}${callHint}`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Synchronous readiness check for continueToNextScene() -- lets callers
|
|
233
|
+
* give instant feedback for the "not ready yet" cases (job not done, no
|
|
234
|
+
* AI configured) without waiting on anything async.
|
|
235
|
+
*/
|
|
236
|
+
canDraftNextScene() {
|
|
237
|
+
if (this._draftInProgress) {
|
|
238
|
+
return { allowed: false, message: `Already drafting the next job -- give it a moment, he'll call back.` };
|
|
239
|
+
}
|
|
240
|
+
if (!this.scene.isCompleted()) {
|
|
241
|
+
return { allowed: false, message: `This job isn't finished yet.` };
|
|
242
|
+
}
|
|
243
|
+
if (!this._useAi) {
|
|
244
|
+
return { allowed: false, message: `Continuing needs an AI-generated job, which requires an AI provider to be configured (OPENAI_API_KEY or ANTHROPIC_API_KEY).` };
|
|
245
|
+
}
|
|
246
|
+
return { allowed: true };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Fires continueToNextScene() without waiting on it -- job generation can
|
|
250
|
+
* take 20-40+ seconds, and neither "continue" nor calling the fixer
|
|
251
|
+
* should freeze the rest of the session behind that. Self-announces the
|
|
252
|
+
* result once it resolves, styled the same as any other comms exchange,
|
|
253
|
+
* since by design nothing else is holding a reference to display it.
|
|
254
|
+
* Public: called directly by call.ts when the player calls the fixer
|
|
255
|
+
* after a job's done, not just from "continue" here in game.ts.
|
|
256
|
+
*/
|
|
257
|
+
draftNextSceneInBackground() {
|
|
258
|
+
void this.continueToNextScene()
|
|
259
|
+
.then(nextJobText => {
|
|
260
|
+
if (nextJobText) {
|
|
261
|
+
Logger.getInstance().logWithColor(`${CALL_ICON} ${nextJobText}`, CALL_COLOR);
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.catch(err => {
|
|
265
|
+
Logger.getInstance().error(`Failed to draft the next job: ${err}`);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Drafts and loads the next job once the current scene's win condition
|
|
270
|
+
* has been met. Reuses the same long-lived Player (and its inventory),
|
|
271
|
+
* so gear compounds across the session -- nothing is persisted to disk,
|
|
272
|
+
* it just carries forward for the life of this process. Re-checks
|
|
273
|
+
* canDraftNextScene() itself (callers are expected to check it first for
|
|
274
|
+
* instant feedback, but this stays safe to call directly too).
|
|
275
|
+
*/
|
|
276
|
+
async continueToNextScene() {
|
|
277
|
+
const gate = this.canDraftNextScene();
|
|
278
|
+
if (!gate.allowed) {
|
|
279
|
+
return gate.message ?? `This job isn't finished yet.`;
|
|
280
|
+
}
|
|
281
|
+
this._draftInProgress = true;
|
|
282
|
+
try {
|
|
283
|
+
return await this.draftNextScene();
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
this._draftInProgress = false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The actual drafting work, split out from continueToNextScene() purely
|
|
291
|
+
* so that method can wrap it in the try/finally that clears
|
|
292
|
+
* _draftInProgress -- every return path below (including the early
|
|
293
|
+
* "couldn't draft" one) needs that cleanup to run.
|
|
294
|
+
*/
|
|
295
|
+
async draftNextScene() {
|
|
296
|
+
const nextTier = this._tier + 1;
|
|
297
|
+
// Carry the current fixer's identity forward -- without this, each
|
|
298
|
+
// "continue" generates a scene with zero knowledge of the last one, so
|
|
299
|
+
// the AI just invents a brand new job-giver every time. Looked up by
|
|
300
|
+
// identity (is there an actor actually named FIXER_NAME?), not via the
|
|
301
|
+
// win condition's toNpc -- that's often a one-off contact the fixer
|
|
302
|
+
// pointed the runner toward, not the fixer himself (see the "broker,
|
|
303
|
+
// not always the destination" convention).
|
|
304
|
+
const fixerStillInScene = this.scene.allActors.some(a => a.name === FIXER_NAME);
|
|
305
|
+
const previousFixerName = fixerStillInScene ? FIXER_NAME : undefined;
|
|
306
|
+
// Append, don't replace -- this used to be setContent(), which wiped
|
|
307
|
+
// the whole log box (including the JOB COMPLETE banner and everything
|
|
308
|
+
// before it) for the entire generation wait, which can run 20-40+
|
|
309
|
+
// seconds for a tiered scene with AI flavor enabled. Framed as the
|
|
310
|
+
// fixer's own line when he's actually in this scene, rather than a
|
|
311
|
+
// bare system message -- he's the one drumming up the next gig.
|
|
312
|
+
const waitingLine = fixerStillInScene
|
|
313
|
+
? `\n${CALL_ICON} ${FIXER_NAME}: "I'll call you back when I've got something lined up." (drafting the next job...)`
|
|
314
|
+
: '\nDrafting the next job... please wait.';
|
|
315
|
+
this.logBox.pushLine(waitingLine);
|
|
316
|
+
this.logBox.scrollTo(this.logBox.getScrollHeight());
|
|
317
|
+
this._screen.render();
|
|
318
|
+
const nextSeed = await SceneSeedGenerator.generate(this._logFilePath, nextTier, previousFixerName);
|
|
319
|
+
if (!nextSeed) {
|
|
320
|
+
return `Couldn't draft a new job this time. Type "continue" to try again, or "quit" to stop here.`;
|
|
321
|
+
}
|
|
322
|
+
this._tier = nextTier;
|
|
323
|
+
this.sceneSeed = nextSeed;
|
|
324
|
+
// A call partner from the old scene wouldn't exist in the new one --
|
|
325
|
+
// hang up before moving on, rather than leave a dangling reference.
|
|
326
|
+
if (this.player.activeCallPartner) {
|
|
327
|
+
this.player.activeCallPartner = undefined;
|
|
328
|
+
}
|
|
329
|
+
// false: this is a continuation, not a fresh game -- the player already
|
|
330
|
+
// has gear from prior jobs, so don't dump another starter kit on top.
|
|
331
|
+
this.scene = await SceneSynthesizer.synthesizeFromJson(nextSeed, this.player, this._useAi, false);
|
|
332
|
+
this.scene.initialize(this);
|
|
333
|
+
this.player.currentLocation = this.determineStartRoom();
|
|
334
|
+
// New job, fresh luck (see commands/edge.ts).
|
|
335
|
+
this.player.refreshEdge();
|
|
336
|
+
// The home base is owned by Game, not any one Scene -- re-inject it and
|
|
337
|
+
// rewire its one exit to point at this job's start room instead of the
|
|
338
|
+
// just-abandoned one.
|
|
339
|
+
this.scene.addRoom(this._homeRoom);
|
|
340
|
+
this.connectHomeRoom(this.player.currentLocation);
|
|
341
|
+
this.updateEquipment(this.player.equipment);
|
|
342
|
+
this.updateInventory(this.player.inventory, capitalCase(this.scene.getCurrencyType()));
|
|
343
|
+
this.updateExits(this.player.currentLocation);
|
|
344
|
+
this.updateStatus();
|
|
345
|
+
return `\n=== JOB #${this._tier}: ${nextSeed.name} ===\n${this.scene.story}`;
|
|
106
346
|
}
|
|
107
347
|
async close() {
|
|
108
348
|
this._inputBox.removeAllListeners(); // removes all event listeners
|
|
@@ -113,42 +353,13 @@ export default class Game {
|
|
|
113
353
|
process.exit(2);
|
|
114
354
|
});
|
|
115
355
|
}
|
|
116
|
-
initMap(screen) {
|
|
117
|
-
// Map
|
|
118
|
-
const exitsBox = blessed.box({
|
|
119
|
-
top: '70%',
|
|
120
|
-
left: '75%',
|
|
121
|
-
width: '25%',
|
|
122
|
-
height: '20%',
|
|
123
|
-
border: {
|
|
124
|
-
type: 'line'
|
|
125
|
-
},
|
|
126
|
-
label: ' Map ',
|
|
127
|
-
content: 'Available exits will be shown here...',
|
|
128
|
-
tags: true,
|
|
129
|
-
scrollable: true,
|
|
130
|
-
scrollbar: {
|
|
131
|
-
ch: ' ',
|
|
132
|
-
track: {
|
|
133
|
-
bg: 'yellow'
|
|
134
|
-
},
|
|
135
|
-
style: {
|
|
136
|
-
inverse: true
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
keys: true,
|
|
140
|
-
mouse: true,
|
|
141
|
-
});
|
|
142
|
-
screen.append(exitsBox);
|
|
143
|
-
this._exitsBox = exitsBox;
|
|
144
|
-
}
|
|
145
356
|
initInputBox(screen) {
|
|
146
357
|
// Create an input box for user commands
|
|
147
358
|
const inputBox = blessed.textbox({
|
|
148
359
|
bottom: 0,
|
|
149
360
|
left: 0,
|
|
150
361
|
width: '100%',
|
|
151
|
-
height:
|
|
362
|
+
height: 3,
|
|
152
363
|
tags: true,
|
|
153
364
|
border: { type: 'line' },
|
|
154
365
|
style: { fg: 'white', border: { fg: 'cyan' } },
|
|
@@ -161,8 +372,21 @@ export default class Game {
|
|
|
161
372
|
screen.append(inputBox);
|
|
162
373
|
// Key bindings
|
|
163
374
|
screen.key(['escape', 'C-c'], () => process.exit(0));
|
|
164
|
-
// Focus on the input box by default
|
|
165
|
-
|
|
375
|
+
// Focus on the input box by default -- but ONLY if it isn't already
|
|
376
|
+
// focused. screen.append() auto-focuses the first keyable element
|
|
377
|
+
// (Node.insert), and since the sidebar boxes were removed this textbox
|
|
378
|
+
// IS the first one, so it arrives here already focused with its input
|
|
379
|
+
// reader mid-setup. Blessed's _focus() has no same-element guard:
|
|
380
|
+
// focusing an already-focused textbox fires a self-blur that cancels
|
|
381
|
+
// that reader BEFORE its keypress listener finishes attaching (the
|
|
382
|
+
// attach rides a delayed tick), so the cancel removes nothing, a
|
|
383
|
+
// second reader starts, both listeners land -- and every keystroke
|
|
384
|
+
// doubles, from startup ("exit" arrived as "eexxiitt" in a real
|
|
385
|
+
// session). Previously the Map box was appended first and soaked up
|
|
386
|
+
// the auto-focus, which is the only reason this never fired before.
|
|
387
|
+
if (screen.focused !== inputBox) {
|
|
388
|
+
inputBox.focus();
|
|
389
|
+
}
|
|
166
390
|
// Render the screen
|
|
167
391
|
screen.render();
|
|
168
392
|
}
|
|
@@ -172,8 +396,9 @@ export default class Game {
|
|
|
172
396
|
id: 'logBox',
|
|
173
397
|
top: '0%',
|
|
174
398
|
left: '0%',
|
|
175
|
-
width: '
|
|
176
|
-
|
|
399
|
+
width: '100%',
|
|
400
|
+
// Fixed-row footer: 8 rows of HUD (status box) + 3 rows of input.
|
|
401
|
+
height: '100%-11',
|
|
177
402
|
border: {
|
|
178
403
|
type: 'line'
|
|
179
404
|
},
|
|
@@ -197,139 +422,138 @@ export default class Game {
|
|
|
197
422
|
logBox.removeAllListeners('mousemove');
|
|
198
423
|
screen.append(logBox);
|
|
199
424
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
425
|
+
/**
|
|
426
|
+
* The HUD: a fixed strip between the log and the input box for the
|
|
427
|
+
* important real-time state -- condition tracks, armed/link status,
|
|
428
|
+
* sustained spells, active call. Anything that changes moment to moment
|
|
429
|
+
* and shouldn't need a command to check lives here; everything slower
|
|
430
|
+
* (full inventory, slot-by-slot equipment, the map) is an on-demand
|
|
431
|
+
* command instead. Future contexts (astral projection, VR hot-sim) get
|
|
432
|
+
* their own line here the same way the call line did: small explicit
|
|
433
|
+
* pieces, not a status framework built ahead of need.
|
|
434
|
+
*/
|
|
435
|
+
initStatusBox(screen) {
|
|
436
|
+
const statusBox = blessed.box({
|
|
437
|
+
id: 'statusBox',
|
|
438
|
+
bottom: 3, // sits directly on the input box
|
|
439
|
+
left: '0%',
|
|
440
|
+
width: '100%',
|
|
441
|
+
height: 8, // 6 content rows inside the border: location, tracks,
|
|
442
|
+
// readiness, and up to two transient lines (sustained, call)
|
|
206
443
|
border: {
|
|
207
444
|
type: 'line'
|
|
208
445
|
},
|
|
209
|
-
label: ' Equipment ',
|
|
210
|
-
content: 'Your equipped items will be listed here...',
|
|
211
446
|
tags: true,
|
|
212
|
-
|
|
213
|
-
scrollbar: {
|
|
214
|
-
ch: ' ',
|
|
215
|
-
track: {
|
|
216
|
-
bg: 'yellow'
|
|
217
|
-
},
|
|
218
|
-
style: {
|
|
219
|
-
inverse: true
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
keys: true,
|
|
223
|
-
mouse: true,
|
|
224
|
-
});
|
|
225
|
-
this._equipmentBox = equipmentBox;
|
|
226
|
-
// Append the equipment box to the screen
|
|
227
|
-
screen.append(equipmentBox);
|
|
228
|
-
}
|
|
229
|
-
initInventoryBox(screen) {
|
|
230
|
-
const inventoryBox = blessed.box({
|
|
231
|
-
top: '0%',
|
|
232
|
-
left: '75%',
|
|
233
|
-
width: '25%',
|
|
234
|
-
height: '45%',
|
|
235
|
-
border: {
|
|
236
|
-
type: 'line'
|
|
237
|
-
},
|
|
238
|
-
label: ' Inventory ',
|
|
239
|
-
content: 'Your items will be listed here...',
|
|
240
|
-
tags: true, // Enable formatting tags in content
|
|
241
|
-
scrollable: true, // Enable scrolling if the content overflows
|
|
242
|
-
scrollbar: {
|
|
243
|
-
ch: ' ',
|
|
244
|
-
track: {
|
|
245
|
-
bg: 'yellow'
|
|
246
|
-
},
|
|
247
|
-
style: {
|
|
248
|
-
inverse: true
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
keys: true,
|
|
252
|
-
mouse: true,
|
|
447
|
+
content: '',
|
|
253
448
|
});
|
|
254
|
-
|
|
255
|
-
screen.append(
|
|
256
|
-
this._inventoryBox = inventoryBox;
|
|
449
|
+
this._statusBox = statusBox;
|
|
450
|
+
screen.append(statusBox);
|
|
257
451
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
renderSlotGroup("Left Hand", playerEquipment.leftHand);
|
|
283
|
-
renderSlotGroup("Right Hand", playerEquipment.rightHand);
|
|
284
|
-
this._equipmentBox.setContent(sections.join('\n'));
|
|
285
|
-
this._screen.render();
|
|
286
|
-
}
|
|
287
|
-
;
|
|
288
|
-
updateExits(room) {
|
|
289
|
-
let description = `Currently in: ${room.name}\n\n`;
|
|
290
|
-
description += `----- Exits ------\n`;
|
|
291
|
-
if (room.exits.size > 0) {
|
|
292
|
-
room.exits.forEach((exit, key) => {
|
|
293
|
-
if (exit instanceof Door) {
|
|
294
|
-
const nextRoom = exit.getOtherSideToName(room);
|
|
295
|
-
description += `${capitalCase(key)}: ${nextRoom}`;
|
|
296
|
-
if (exit.checkIfLocked()) {
|
|
297
|
-
description += " (Locked)";
|
|
298
|
-
}
|
|
299
|
-
else if (exit.checkIfLockable()) {
|
|
300
|
-
description += ' (Unlocked)';
|
|
301
|
-
}
|
|
302
|
-
description += '\n';
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
description += `${capitalCase(key)}\n`; // Example: "NORTH"
|
|
306
|
-
}
|
|
307
|
-
});
|
|
452
|
+
/**
|
|
453
|
+
* Refreshes the status box from current player state. Called after any
|
|
454
|
+
* command that can change what belongs there (currently "call"/"end-call")
|
|
455
|
+
* and once on load/job transition.
|
|
456
|
+
*/
|
|
457
|
+
updateStatus() {
|
|
458
|
+
// updateInventory/updateEquipment route here now, and those hooks can
|
|
459
|
+
// fire from synthesis paths before the blessed UI exists.
|
|
460
|
+
if (!this._statusBox)
|
|
461
|
+
return;
|
|
462
|
+
const player = this.player;
|
|
463
|
+
const lines = [];
|
|
464
|
+
// Line 1: where you are, and the fight autopilot mode. The stance
|
|
465
|
+
// lives HERE -- the shortest HUD line -- because appended to the long
|
|
466
|
+
// readiness line it wrapped out of the box on narrower terminals and
|
|
467
|
+
// effectively only ever showed in the Tab announcement. Always shown,
|
|
468
|
+
// even MANUAL: the HUD is how players discover Tab cycles it at all
|
|
469
|
+
// (see utilities/auto-fight.ts). MANUAL is white, not gray -- blessed's
|
|
470
|
+
// gray is ANSI "bright black", which on a stock dark terminal palette
|
|
471
|
+
// is illegible enough that the stance looked absent entirely.
|
|
472
|
+
const stanceColor = player.autoStance === 'aggressive' ? 'red' : player.autoStance === 'guarded' ? 'yellow' : 'white';
|
|
473
|
+
const stance = `{${stanceColor}-fg}⚔ ${player.autoStance.toUpperCase()}{/${stanceColor}-fg}`;
|
|
474
|
+
if (player.currentLocation) {
|
|
475
|
+
lines.push(`{bold}📍 ${player.currentLocation.name}{/bold} | ${stance}`);
|
|
308
476
|
}
|
|
309
477
|
else {
|
|
310
|
-
|
|
478
|
+
lines.push(stance);
|
|
311
479
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
480
|
+
// Line 2: both condition monitors -- in a game where a fight can kill
|
|
481
|
+
// you and casting can knock you out, current damage on either track is
|
|
482
|
+
// never something a player should have to ask for. Colors shift as
|
|
483
|
+
// each track fills; the stacked wound modifier from both tracks shows
|
|
484
|
+
// once.
|
|
485
|
+
const trackColor = (taken, max) => taken === 0 ? 'green' : taken >= max - 3 ? 'red' : 'yellow';
|
|
486
|
+
const wound = player.woundModifier;
|
|
487
|
+
const woundNote = wound < 0 ? ` {red-fg}(${wound} dice){/red-fg}` : '';
|
|
488
|
+
const physColor = trackColor(player.damageTaken, player.maxConditionBoxes);
|
|
489
|
+
const stunColor = trackColor(player.stunTaken, player.maxStunBoxes);
|
|
490
|
+
lines.push(`{${physColor}-fg}Physical ${player.conditionSummary()}{/${physColor}-fg} ` +
|
|
491
|
+
`{${stunColor}-fg}Stun ${player.stunSummary()}{/${stunColor}-fg}${woundNote}`);
|
|
492
|
+
// Line 3: readiness -- armed state, armor, and whether the commlink is
|
|
493
|
+
// actually live (equipped), the recurring "why can't I call anyone"
|
|
494
|
+
// friction surfaced at a glance.
|
|
495
|
+
const weapon = player.getWeaponProfile();
|
|
496
|
+
const weaponItem = player.equipment.rightHand?.weapon ?? player.equipment.leftHand?.weapon;
|
|
497
|
+
const ammoNote = weaponItem?.isFirearm()
|
|
498
|
+
? (weaponItem.jammed
|
|
499
|
+
? ` {red-fg}[JAMMED -- reboot]{/red-fg}`
|
|
500
|
+
: weaponItem.ammo === 0 ? ` {red-fg}[DRY]{/red-fg}` : ` [${weaponItem.ammo} rds]`)
|
|
501
|
+
: '';
|
|
502
|
+
const armed = weapon.name === 'bare fists'
|
|
503
|
+
? `{gray-fg}Unarmed{/gray-fg}`
|
|
504
|
+
: `Armed: ${weapon.name} (DV ${weapon.damage}${weapon.poolBonus > 0 ? `, +${weapon.poolBonus} dice` : ''})${ammoNote}`;
|
|
505
|
+
const armorNote = `Armor +${player.getArmorValue()}`;
|
|
506
|
+
const hasLink = player.equipment.head?.commlink != null;
|
|
507
|
+
const link = !hasLink
|
|
508
|
+
? `{gray-fg}Link offline{/gray-fg}`
|
|
509
|
+
: player.runningSilent
|
|
510
|
+
? `{magenta-fg}Link SILENT{/magenta-fg}`
|
|
511
|
+
: player.arOffline
|
|
512
|
+
? `{red-fg}AR CUT -- reboot{/red-fg}`
|
|
513
|
+
: `{${CALL_COLOR}-fg}Link online{/${CALL_COLOR}-fg}`;
|
|
514
|
+
lines.push(`${armed} | ${armorNote} | ${link}`);
|
|
515
|
+
// Lines 4+: transient states, present only while active. Future
|
|
516
|
+
// contexts (astral projection, VR hot-sim) join here the same way.
|
|
517
|
+
if (player.sustainedArmor > 0) {
|
|
518
|
+
lines.push(`{magenta-fg}Sustained: armor +${player.sustainedArmor} soak (-2 dice to everything else){/magenta-fg}`);
|
|
519
|
+
}
|
|
520
|
+
if (player.activeCallPartner) {
|
|
521
|
+
lines.push(`{${CALL_COLOR}-fg}${CALL_ICON} On a call with ${player.activeCallPartner.name}{/${CALL_COLOR}-fg}`);
|
|
522
|
+
}
|
|
523
|
+
// Off-plane context: which reality you're in, your persona's Matrix
|
|
524
|
+
// track (cold-sim damage lands there), and where your empty body lies.
|
|
525
|
+
if (player.plane === 'matrix') {
|
|
526
|
+
// Matrix damage is hardware damage: show the DECK's track. A
|
|
527
|
+
// technomancer has no deck -- their stun track (already on line 2)
|
|
528
|
+
// is what the ice chews on.
|
|
529
|
+
const track = player.activeDeck
|
|
530
|
+
? ` Deck ${player.activeDeck.deckSummary()}${player.simMode === 'hot' ? ' +biofeedback' : ''}`
|
|
531
|
+
: ` (living persona -- damage is REAL stun)`;
|
|
532
|
+
lines.push(`{cyan-fg}▣ MATRIX ${(player.simMode ?? 'cold').toUpperCase()}-SIM${track} -- body at ${player.bodyRoom?.name ?? '???'}{/cyan-fg}`);
|
|
533
|
+
}
|
|
534
|
+
else if (player.plane === 'astral') {
|
|
535
|
+
lines.push(`{magenta-fg}✧ ASTRAL -- ${player.astralTicks} move${player.astralTicks === 1 ? '' : 's'} out -- body at ${player.bodyRoom?.name ?? '???'}{/magenta-fg}`);
|
|
327
536
|
}
|
|
328
|
-
|
|
329
|
-
this._inventoryBox.setContent(itemLines.join('\n'));
|
|
537
|
+
this._statusBox.setContent(lines.join('\n'));
|
|
330
538
|
this._screen.render();
|
|
331
539
|
}
|
|
332
|
-
|
|
540
|
+
// The three methods below were the render paths for the old always-on
|
|
541
|
+
// sidebar boxes. Those boxes are gone (the log owns the whole screen; see
|
|
542
|
+
// commands/inv.ts, equipment.ts, map.ts for the on-demand replacements),
|
|
543
|
+
// but the methods stay as no-ops: dozens of call sites across commands,
|
|
544
|
+
// scene proxies, and ui.ts still ping them after inventory/equipment/exit
|
|
545
|
+
// changes, and keeping the hooks alive means a future HUD element can tap
|
|
546
|
+
// those same signals without re-plumbing. Each refreshes the HUD, since
|
|
547
|
+
// several HUD fields (armed, armor, link) derive from equipment state.
|
|
548
|
+
updateEquipment(_playerEquipment) {
|
|
549
|
+
this.updateStatus();
|
|
550
|
+
}
|
|
551
|
+
updateExits(_room) {
|
|
552
|
+
// Nothing to render -- the "map" command reads live room state.
|
|
553
|
+
}
|
|
554
|
+
updateInventory(_playerInventory, _currencyName = 'currency') {
|
|
555
|
+
this.updateStatus();
|
|
556
|
+
}
|
|
333
557
|
// Public method to get the instance of the Game class
|
|
334
558
|
static async getInstance(player, sceneSeed) {
|
|
335
559
|
if (!Game.instance) {
|
|
@@ -341,12 +565,24 @@ export default class Game {
|
|
|
341
565
|
const parts = input.trim().split(' ');
|
|
342
566
|
const verb = parts[0].toLowerCase();
|
|
343
567
|
const args = parts.slice(1);
|
|
344
|
-
if (verb === 'quit') {
|
|
568
|
+
if (verb === 'quit' || verb === 'exit') {
|
|
345
569
|
return null;
|
|
346
570
|
}
|
|
347
571
|
if (verb === 'maka') {
|
|
348
572
|
return 'You are still in the game, type "quit" to exit';
|
|
349
573
|
}
|
|
574
|
+
if (verb === 'continue') {
|
|
575
|
+
// Instant feedback for "not ready yet" (job not done, no AI
|
|
576
|
+
// configured); otherwise kicks off drafting in the background rather
|
|
577
|
+
// than freezing this command (and everything after it) behind a
|
|
578
|
+
// 20-40+ second generation call -- same as calling the fixer does.
|
|
579
|
+
const gate = this.canDraftNextScene();
|
|
580
|
+
if (!gate.allowed) {
|
|
581
|
+
return gate.message ?? `This job isn't finished yet.`;
|
|
582
|
+
}
|
|
583
|
+
this.draftNextSceneInBackground();
|
|
584
|
+
return `Understood -- drafting the next job now. Feel free to keep exploring in the meantime.`;
|
|
585
|
+
}
|
|
350
586
|
if (verb === 'help') {
|
|
351
587
|
return this.getHelpMessage();
|
|
352
588
|
}
|
|
@@ -357,6 +593,8 @@ export default class Game {
|
|
|
357
593
|
actor: this.player,
|
|
358
594
|
rooms: this.scene.getRooms(),
|
|
359
595
|
scene: this.scene,
|
|
596
|
+
screen: this._screen,
|
|
597
|
+
game: this,
|
|
360
598
|
};
|
|
361
599
|
const command = CommandFactory.createCommand(verb, context);
|
|
362
600
|
if (command) {
|
|
@@ -375,6 +613,7 @@ export default class Game {
|
|
|
375
613
|
}
|
|
376
614
|
}
|
|
377
615
|
helpText += `- clear: Clear the screen.\n`;
|
|
616
|
+
helpText += `- continue: Take on a new, tougher job once you've completed the current one.\n`;
|
|
378
617
|
helpText += `- quit: Exit the game.\n`;
|
|
379
618
|
return helpText;
|
|
380
619
|
}
|