@maka/maka-cli 5.8.0 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +4 -2
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +14 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +24 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +31 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +16 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +86 -15
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +5 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +5 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +94 -16
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.d.ts +38 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js +178 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +7 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hints.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +48 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.d.ts +41 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +160 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/order.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/order.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +1 -1
- 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.js +7 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +11 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +24 -13
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +37 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +9 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.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/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 +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +11 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +138 -23
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +144 -24
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +3 -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 +11 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +187 -6
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +9 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +27 -5
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +9 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +51 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +28 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +165 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +76 -20
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +157 -34
- 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/player-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +52 -3
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +18 -4
- 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 +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.d.ts +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js +57 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.d.ts +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js +55 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +8 -7
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +121 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +315 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { capitalCase } from 'change-case';
|
|
|
3
3
|
import { Category } from './types/shared/item-enum.js';
|
|
4
4
|
import { Item } from './models/item.js';
|
|
5
5
|
import { Room } from './models/_index.js';
|
|
6
|
+
import { Door } from './models/door.js';
|
|
6
7
|
import { isFence, LIFESTYLE_TIERS } from './utilities/commerce.js';
|
|
7
8
|
import { rollPool, formatRoll } from './utilities/dice.js';
|
|
8
9
|
import { Direction } from './types/shared/direction-enum.js';
|
|
@@ -33,6 +34,7 @@ import { SilentCommand } from './commands/silent.js';
|
|
|
33
34
|
import { RestCommand } from './commands/rest.js';
|
|
34
35
|
import { ReloadCommand } from './commands/reload.js';
|
|
35
36
|
import { SubdueCommand } from './commands/subdue.js';
|
|
37
|
+
import { GrappleCommand, ReleaseCommand, StruggleCommand } from './commands/grapple.js';
|
|
36
38
|
import { EdgeCommand } from './commands/edge.js';
|
|
37
39
|
import { SheetCommand } from './commands/sheet.js';
|
|
38
40
|
import { CallCommand } from './commands/call.js';
|
|
@@ -59,6 +61,9 @@ import { InitiateCommand } from './commands/initiate.js';
|
|
|
59
61
|
import { SummonCommand } from './commands/summon.js';
|
|
60
62
|
import { OrderCommand } from './commands/order.js';
|
|
61
63
|
import { DeployCommand, AgentCommand, RecallCommand } from './commands/companions.js';
|
|
64
|
+
import { MoveCommand, RunCommand, SprintCommand } from './commands/move.js';
|
|
65
|
+
import { HintsCommand } from './commands/hints.js';
|
|
66
|
+
import { hint } from './utilities/hints.js';
|
|
62
67
|
import { InstallCommand } from './commands/install.js';
|
|
63
68
|
import { BuyCommand } from './commands/buy.js';
|
|
64
69
|
import { SellCommand } from './commands/sell.js';
|
|
@@ -82,6 +87,7 @@ import { SceneSeedGenerator, FIXER_NAME } from './factories/scene-seed-generator
|
|
|
82
87
|
import { CALL_ICON, CALL_COLOR } from './utilities/comm-style.js';
|
|
83
88
|
import { AI } from '../../../tools/ai/ai.class.js';
|
|
84
89
|
import { BULLET } from './utilities/log-style.js';
|
|
90
|
+
import { spotOf } from './utilities/spots.js';
|
|
85
91
|
export default class Game {
|
|
86
92
|
initialized;
|
|
87
93
|
static instance;
|
|
@@ -98,6 +104,10 @@ export default class Game {
|
|
|
98
104
|
_screen;
|
|
99
105
|
_logBox;
|
|
100
106
|
_metaBox;
|
|
107
|
+
// Quit confirmation state (see handleInput / initInputBox): armed by
|
|
108
|
+
// the first quit/exit or ESC, cleared by anything that isn't a yes.
|
|
109
|
+
_quitArmed = false;
|
|
110
|
+
_escArmedAt = 0;
|
|
101
111
|
_statusBox;
|
|
102
112
|
_inputBox;
|
|
103
113
|
_useAi = false;
|
|
@@ -414,7 +424,7 @@ export default class Game {
|
|
|
414
424
|
if (kind === 'gig')
|
|
415
425
|
c.gigs += 1;
|
|
416
426
|
Logger.getInstance().write(`Contact opened: ${name} (connection ${c.connection}).`);
|
|
417
|
-
return `${name} knows your name now -- a CONTACT (Connection ${c.connection}, Loyalty 1)
|
|
427
|
+
return `${name} knows your name now -- a CONTACT (Connection ${c.connection}, Loyalty 1).${hint(` ("contacts" for the roster, "ask ${name} about ..." for legwork.)`)}`;
|
|
418
428
|
}
|
|
419
429
|
if (kind === 'gig') {
|
|
420
430
|
c.gigs += 1;
|
|
@@ -428,7 +438,7 @@ export default class Game {
|
|
|
428
438
|
c.spent += spent;
|
|
429
439
|
if (Math.floor(c.spent / 1000) > thousandsBefore && c.loyalty < 3) {
|
|
430
440
|
c.loyalty += 1;
|
|
431
|
-
return `${name} has started keeping your usual ready
|
|
441
|
+
return `${name} has started keeping your usual ready.${hint(` (Loyalty ${c.loyalty} -- patronage tops out at 3; run their errands to go deeper.)`)}`;
|
|
432
442
|
}
|
|
433
443
|
return null;
|
|
434
444
|
}
|
|
@@ -710,6 +720,12 @@ export default class Game {
|
|
|
710
720
|
CommandFactory.registerCommand('rest', RestCommand);
|
|
711
721
|
CommandFactory.registerCommand('reload', ReloadCommand);
|
|
712
722
|
CommandFactory.registerCommand('subdue', SubdueCommand);
|
|
723
|
+
// SUBDUING p.195 (see commands/grapple.ts): restrain without damage.
|
|
724
|
+
CommandFactory.registerCommand('grapple', GrappleCommand);
|
|
725
|
+
CommandFactory.registerCommand('restrain', GrappleCommand);
|
|
726
|
+
CommandFactory.registerCommand('clinch', GrappleCommand);
|
|
727
|
+
CommandFactory.registerCommand('release', ReleaseCommand);
|
|
728
|
+
CommandFactory.registerCommand('struggle', StruggleCommand);
|
|
713
729
|
CommandFactory.registerCommand('edge', EdgeCommand);
|
|
714
730
|
CommandFactory.registerCommand('sheet', SheetCommand);
|
|
715
731
|
CommandFactory.registerCommand('stats', SheetCommand);
|
|
@@ -747,6 +763,15 @@ export default class Game {
|
|
|
747
763
|
CommandFactory.registerCommand('submerge', InitiateCommand);
|
|
748
764
|
CommandFactory.registerCommand('summon', SummonCommand);
|
|
749
765
|
CommandFactory.registerCommand('conjure', SummonCommand);
|
|
766
|
+
// "At" spots (utilities/spots.ts): walk/move/approach are one
|
|
767
|
+
// crossing; run and sprint are the fast modes (combat semantics in
|
|
768
|
+
// the spot-move phase).
|
|
769
|
+
CommandFactory.registerCommand('walk', MoveCommand);
|
|
770
|
+
CommandFactory.registerCommand('move', MoveCommand);
|
|
771
|
+
CommandFactory.registerCommand('approach', MoveCommand);
|
|
772
|
+
CommandFactory.registerCommand('run', RunCommand);
|
|
773
|
+
CommandFactory.registerCommand('sprint', SprintCommand);
|
|
774
|
+
CommandFactory.registerCommand('hints', HintsCommand);
|
|
750
775
|
CommandFactory.registerCommand('order', OrderCommand);
|
|
751
776
|
CommandFactory.registerCommand('deploy', DeployCommand);
|
|
752
777
|
CommandFactory.registerCommand('recall', RecallCommand);
|
|
@@ -784,6 +809,19 @@ export default class Game {
|
|
|
784
809
|
if (this._homeRoomExitDirection) {
|
|
785
810
|
this._homeRoom.exits.delete(this._homeRoomExitDirection);
|
|
786
811
|
}
|
|
812
|
+
// The OTHER half of the previous link: sweep any exit on the anchor
|
|
813
|
+
// room that already leads home before choosing a direction. Without
|
|
814
|
+
// this, every homecoming re-ran the search against a room still
|
|
815
|
+
// holding last time's hideout door and stacked a second one on the
|
|
816
|
+
// next free direction (a real session's Neon Strip ended up with
|
|
817
|
+
// "up" AND "down" both reading "To Your Hideout").
|
|
818
|
+
for (const [dir, ex] of [...jobStartRoom.exits.entries()]) {
|
|
819
|
+
const leadsHome = ex instanceof Door
|
|
820
|
+
? ex.getOtherSide(jobStartRoom) === this._homeRoom
|
|
821
|
+
: ex === this._homeRoom.name;
|
|
822
|
+
if (leadsHome)
|
|
823
|
+
jobStartRoom.exits.delete(dir);
|
|
824
|
+
}
|
|
787
825
|
const direction = Game.HOME_DIRECTION_PRIORITY.find(d => !jobStartRoom.exits.has(d));
|
|
788
826
|
if (!direction) {
|
|
789
827
|
// Every direction is already spoken for on this job's start room --
|
|
@@ -864,6 +902,15 @@ export default class Game {
|
|
|
864
902
|
this.player.lifestyleEdgeBonus = this.lifestyle.edgeBonus;
|
|
865
903
|
// Home turf knows its own (see seedHomeTurfContacts).
|
|
866
904
|
this.seedHomeTurfContacts();
|
|
905
|
+
// The next run starts cooking the moment you're home (player
|
|
906
|
+
// request): generation takes 20-40s, and waiting for the player
|
|
907
|
+
// to CALL first meant "give me a bit" every time. Draft now --
|
|
908
|
+
// Mr. Johnson's "1 new message" ping lands mid-session, and the
|
|
909
|
+
// call finds work already waiting. The gate keeps this a no-op
|
|
910
|
+
// with AI off or a draft/pending seed somehow already in flight.
|
|
911
|
+
if (this.canDraftNextScene().allowed) {
|
|
912
|
+
this.draftNextSceneInBackground();
|
|
913
|
+
}
|
|
867
914
|
}
|
|
868
915
|
// Gear up automatically: wearables (and the commlink) slot into any
|
|
869
916
|
// EMPTY slots at load -- no "equip all" ritual to start a session
|
|
@@ -872,6 +919,9 @@ export default class Game {
|
|
|
872
919
|
equipIntoEmptySlots(this.player);
|
|
873
920
|
this.warmNpcAppearancesInBackground();
|
|
874
921
|
this.startPursuitHeartbeat();
|
|
922
|
+
// The banner learns where we are only after the hub/scene bookkeeping
|
|
923
|
+
// above settles.
|
|
924
|
+
this.refreshLogLabel();
|
|
875
925
|
this.initialized = true;
|
|
876
926
|
// Nudge the player toward "call" up front -- the fixer convention means
|
|
877
927
|
// the story deliberately doesn't narrate a completed briefing, so
|
|
@@ -879,7 +929,7 @@ export default class Game {
|
|
|
879
929
|
// you actually get your orders.
|
|
880
930
|
const wc = this.scene.getWinCondition();
|
|
881
931
|
const callHint = (wc?.type === 'give' && wc.toNpc)
|
|
882
|
-
? `\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.`
|
|
932
|
+
? hint(`\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.`)
|
|
883
933
|
: '';
|
|
884
934
|
return `Welcome, ${this.player.name}\n ${this.scene.story}${callHint}`;
|
|
885
935
|
}
|
|
@@ -1015,10 +1065,10 @@ export default class Game {
|
|
|
1015
1065
|
this.lifestyleTier = LIFESTYLE_TIERS[tierIndex - 1].name;
|
|
1016
1066
|
// The downgrade is visible the moment you look around.
|
|
1017
1067
|
this.syncHomeToLifestyle();
|
|
1018
|
-
rentLine = `The landlord's drone pings before the door even shuts: ${paid > 0 ? `${paid} paid, ` : ''}${this.rentDebt} nuyen still owed. Your ${tier.name} arrangement lapses -- you're living ${this.lifestyleTier} now
|
|
1068
|
+
rentLine = `The landlord's drone pings before the door even shuts: ${paid > 0 ? `${paid} paid, ` : ''}${this.rentDebt} nuyen still owed. Your ${tier.name} arrangement lapses -- you're living ${this.lifestyleTier} now.${hint(` ("pay" the tab, "lifestyle" to climb back.)`)}`;
|
|
1019
1069
|
}
|
|
1020
1070
|
else {
|
|
1021
|
-
rentLine = `Even the doorway costs something: ${this.rentDebt} nuyen on the tab
|
|
1071
|
+
rentLine = `Even the doorway costs something: ${this.rentDebt} nuyen on the tab.${hint(` ("pay" when you're flush.)`)}`;
|
|
1022
1072
|
}
|
|
1023
1073
|
}
|
|
1024
1074
|
else {
|
|
@@ -1041,6 +1091,7 @@ export default class Game {
|
|
|
1041
1091
|
this.updateInventory(this.player.inventory, capitalCase(this.scene.getCurrencyType()));
|
|
1042
1092
|
this.updateExits(this.player.currentLocation);
|
|
1043
1093
|
this.updateStatus();
|
|
1094
|
+
this.refreshLogLabel();
|
|
1044
1095
|
Logger.getInstance().write(`Returned to hub "${this._hubName}". Lifestyle ${this.lifestyleTier}: ${paid} paid, ${this.rentDebt} owed. Restocked ${restocked}. Rumor: ${this.activeRumor ?? '(none)'}`);
|
|
1045
1096
|
// Drones spin back up beside the cot.
|
|
1046
1097
|
const companionArrivalLines = this.unfoldCompanionsOnArrival();
|
|
@@ -1048,7 +1099,7 @@ export default class Game {
|
|
|
1048
1099
|
const fateLine = wrappedRun
|
|
1049
1100
|
? `The run's wrapped and the street swallows you back up.`
|
|
1050
1101
|
: `The run's behind you -- unfinished, unpaid, already someone else's problem. The street doesn't ask.`;
|
|
1051
|
-
return `\n=== ${this._hubName.toUpperCase()} ===\n${fateLine} You're home -- your hideout, one door off ${hub.determineStartRoom().name}
|
|
1102
|
+
return `\n=== ${this._hubName.toUpperCase()} ===\n${fateLine} You're home -- your hideout, one door off ${hub.determineStartRoom().name}.${hint(` Call ${FIXER_NAME} when you're hungry for the next job.`)}\n${rentLine}${comfortLine}${restockLine}${companionBlock.length > 0 ? `\n${companionBlock.join('\n')}` : ''}`;
|
|
1052
1103
|
}
|
|
1053
1104
|
/**
|
|
1054
1105
|
* The actual drafting work, split out from continueToNextScene() purely
|
|
@@ -1093,7 +1144,7 @@ export default class Game {
|
|
|
1093
1144
|
this._pendingRunSeed = nextSeed;
|
|
1094
1145
|
this._pendingRunTier = nextTier;
|
|
1095
1146
|
this._pendingRide = Game.RIDE_POOL[Math.floor(Math.random() * Game.RIDE_POOL.length)];
|
|
1096
|
-
return `Your link pings -- 1 new message: ${FIXER_NAME}
|
|
1147
|
+
return `Your link pings -- 1 new message: ${FIXER_NAME}.${hint(` ("call ${FIXER_NAME}" to hear the job.)`)}`;
|
|
1097
1148
|
}
|
|
1098
1149
|
// Classic sessions transition immediately, hideout-first, as always.
|
|
1099
1150
|
return await this.activateRunScene(nextSeed, nextTier, { startInHideout: true });
|
|
@@ -1147,8 +1198,11 @@ export default class Game {
|
|
|
1147
1198
|
// warming so re-shelled companions get warmed with the cast.
|
|
1148
1199
|
const arrivalLines = this.unfoldCompanionsOnArrival();
|
|
1149
1200
|
this.warmNpcAppearancesInBackground();
|
|
1201
|
+
this.refreshLogLabel();
|
|
1202
|
+
// No "=== JOB #n ===" banner on entry (player request) -- the Game
|
|
1203
|
+
// Log window's own title carries the job name now.
|
|
1150
1204
|
const companionBlock = [...companionLines, ...arrivalLines];
|
|
1151
|
-
return `\n
|
|
1205
|
+
return `\n${this.scene.story}${needsLines.length > 0 ? `\n${needsLines.join('\n')}` : ''}${companionBlock.length > 0 ? `\n${companionBlock.join('\n')}` : ''}`;
|
|
1152
1206
|
}
|
|
1153
1207
|
/**
|
|
1154
1208
|
* The needs check at every job departure (SR5e p.56 / p.172, scaled to
|
|
@@ -1171,11 +1225,15 @@ export default class Game {
|
|
|
1171
1225
|
p.sleepStreak = slept ? 0 : p.sleepStreak + 1;
|
|
1172
1226
|
p.refreshEdge(fed && slept);
|
|
1173
1227
|
if (fed && slept) {
|
|
1174
|
-
|
|
1228
|
+
// Good-news status with a rules lesson inside -- hint-gated
|
|
1229
|
+
// (player request); the HUD's edge count carries the number anyway.
|
|
1230
|
+
const note = hint(`Fed and slept -- you head out SHARP. (Edge ${p.edgeRemaining}/${p.edge + p.lifestyleEdgeBonus}.)`);
|
|
1231
|
+
if (note)
|
|
1232
|
+
lines.push(note);
|
|
1175
1233
|
}
|
|
1176
1234
|
else {
|
|
1177
1235
|
const missing = !fed && !slept ? 'meal and no sleep' : !fed ? 'meal' : 'sleep';
|
|
1178
|
-
lines.push(`No ${missing} since the last job -- your edge doesn't come back (${p.edgeRemaining} left)
|
|
1236
|
+
lines.push(`No ${missing} since the last job -- your edge doesn't come back (${p.edgeRemaining} left).${hint(` A hot meal and a night in your own bed reset it.`)}`);
|
|
1179
1237
|
}
|
|
1180
1238
|
// Escalating, resisted, LOCKED fatigue per ignored need.
|
|
1181
1239
|
const events = [];
|
|
@@ -1273,8 +1331,18 @@ export default class Game {
|
|
|
1273
1331
|
});
|
|
1274
1332
|
this._inputBox = inputBox;
|
|
1275
1333
|
screen.append(inputBox);
|
|
1276
|
-
// Key bindings
|
|
1277
|
-
|
|
1334
|
+
// Key bindings. ESC/Ctrl-C take a SECOND press within 3s to quit
|
|
1335
|
+
// (player request: "I just exited by mistake") -- the first press
|
|
1336
|
+
// warns; typing anything disarms nothing (the window just expires).
|
|
1337
|
+
screen.key(['escape', 'C-c'], () => {
|
|
1338
|
+
const now = Date.now();
|
|
1339
|
+
if (now - this._escArmedAt < 3000) {
|
|
1340
|
+
process.exit(0);
|
|
1341
|
+
}
|
|
1342
|
+
this._escArmedAt = now;
|
|
1343
|
+
Logger.getInstance().logWithColor(`Press again within 3s to quit. (Or "quit" + "y".)`, 'yellow');
|
|
1344
|
+
this._screen?.render();
|
|
1345
|
+
});
|
|
1278
1346
|
// Focus on the input box by default -- but ONLY if it isn't already
|
|
1279
1347
|
// focused. screen.append() auto-focuses the first keyable element
|
|
1280
1348
|
// (Node.insert), and since the sidebar boxes were removed this textbox
|
|
@@ -1309,7 +1377,7 @@ export default class Game {
|
|
|
1309
1377
|
border: {
|
|
1310
1378
|
type: 'line'
|
|
1311
1379
|
},
|
|
1312
|
-
label:
|
|
1380
|
+
label: this.buildLogLabel(false),
|
|
1313
1381
|
scrollable: true, // Enable scrolling
|
|
1314
1382
|
alwaysScroll: true,
|
|
1315
1383
|
scrollbar: {
|
|
@@ -1337,10 +1405,17 @@ export default class Game {
|
|
|
1337
1405
|
// yanking the view back down (Logger.log and ui.ts honor it).
|
|
1338
1406
|
// Paging back to the bottom -- or End -- re-engages the follow.
|
|
1339
1407
|
logBox.followBottom = true;
|
|
1408
|
+
// CRITICAL: the input box is ALWAYS reading (focus never leaves it),
|
|
1409
|
+
// and blessed's textarea.readInput sets screen.grabKeys = true --
|
|
1410
|
+
// which mutes every screen-level key handler EXCEPT keys listed in
|
|
1411
|
+
// screen.ignoreLocked (screen.js:591). Without this, the bindings
|
|
1412
|
+
// below simply never fire (a real session: "PgUp/Dn don't work").
|
|
1413
|
+
// The textbox itself ignores these keys, so nothing is stolen.
|
|
1414
|
+
screen.ignoreLocked.push('pageup', 'pagedown', 'end');
|
|
1340
1415
|
const page = () => Math.max(1, (typeof logBox.height === 'number' ? logBox.height : 20) - 3);
|
|
1341
1416
|
const setFollow = (on) => {
|
|
1342
1417
|
logBox.followBottom = on;
|
|
1343
|
-
logBox.setLabel(on
|
|
1418
|
+
logBox.setLabel(this.buildLogLabel(!on));
|
|
1344
1419
|
};
|
|
1345
1420
|
const atBottom = () => logBox.getScrollPerc() >= 100 || logBox.getScrollHeight() <= page() + 3;
|
|
1346
1421
|
screen.key(['pageup'], () => {
|
|
@@ -1361,6 +1436,29 @@ export default class Game {
|
|
|
1361
1436
|
});
|
|
1362
1437
|
screen.append(logBox);
|
|
1363
1438
|
}
|
|
1439
|
+
/**
|
|
1440
|
+
* The Game Log banner names WHERE you are in the campaign (player
|
|
1441
|
+
* request): the hub by name at home, "JOB #n: <name>" on a run --
|
|
1442
|
+
* plus the scroll state the label already carried.
|
|
1443
|
+
*/
|
|
1444
|
+
buildLogLabel(scrolled) {
|
|
1445
|
+
const onHub = this._hubScene !== undefined && this.scene === this._hubScene;
|
|
1446
|
+
const name = onHub ? this._hubName : this.sceneSeed?.name;
|
|
1447
|
+
const where = name ? ` -- ${name}` : '';
|
|
1448
|
+
// The key coaching is hint-gated (player request); the SCROLLED
|
|
1449
|
+
// state itself stays -- it's status, not teaching.
|
|
1450
|
+
return scrolled
|
|
1451
|
+
? ` Game Log${where} -- SCROLLED${hint(' (End to follow)')} `
|
|
1452
|
+
: ` Game Log${where}${hint(' (PgUp/PgDn to scroll)')} `;
|
|
1453
|
+
}
|
|
1454
|
+
/** Re-stamps the banner after a scene change (init, run start, home). */
|
|
1455
|
+
refreshLogLabel() {
|
|
1456
|
+
const box = this._logBox;
|
|
1457
|
+
if (!box)
|
|
1458
|
+
return;
|
|
1459
|
+
box.setLabel(this.buildLogLabel(box.followBottom === false));
|
|
1460
|
+
this._screen?.render();
|
|
1461
|
+
}
|
|
1364
1462
|
/**
|
|
1365
1463
|
* Scroll the log to its tail -- unless the player has paged up to read
|
|
1366
1464
|
* history (followBottom off), in which case their place is left alone.
|
|
@@ -1461,7 +1559,10 @@ export default class Game {
|
|
|
1461
1559
|
// a real question.
|
|
1462
1560
|
const crumb = player.cameFrom ? `${player.cameFrom.room} (${player.cameFrom.back}) > ` : '';
|
|
1463
1561
|
if (player.currentLocation) {
|
|
1464
|
-
|
|
1562
|
+
// "At" spots: where you stand WITHIN the room, when it has places
|
|
1563
|
+
// to stand (see utilities/spots.ts).
|
|
1564
|
+
const atNote = spotOf(player) ? ` @ ${player.atSpot}` : '';
|
|
1565
|
+
lines.push(`${crumb}{bold}📍 ${player.currentLocation.name}${atNote}{/bold} | ${stance}`);
|
|
1465
1566
|
}
|
|
1466
1567
|
else {
|
|
1467
1568
|
lines.push(stance);
|
|
@@ -1544,7 +1645,7 @@ export default class Game {
|
|
|
1544
1645
|
// works the grid while you're back in the meat (the AR-drop exit).
|
|
1545
1646
|
const remoteAgent = this.companions.find(c => c.kind === 'agent');
|
|
1546
1647
|
if (remoteAgent && player.plane !== 'matrix') {
|
|
1547
|
-
lines.push(`{cyan-fg}▣ DECK IN AR -- ${remoteAgent.npc.name} on the grid${remoteAgent.npc.objective ? ', working its directive' : ''} ("jack in" to rejoin it, "recall" to fold it home){/cyan-fg}`);
|
|
1648
|
+
lines.push(`{cyan-fg}▣ DECK IN AR -- ${remoteAgent.npc.name} on the grid${remoteAgent.npc.objective ? ', working its directive' : ''}${hint(' ("jack in" to rejoin it, "recall" to fold it home)')}{/cyan-fg}`);
|
|
1548
1649
|
}
|
|
1549
1650
|
// Off-plane context: which reality you're in, your persona's Matrix
|
|
1550
1651
|
// track (cold-sim damage lands there), and where your empty body lies.
|
|
@@ -1603,8 +1704,25 @@ export default class Game {
|
|
|
1603
1704
|
const parts = normalized.trim().split(' ');
|
|
1604
1705
|
const verb = parts[0].toLowerCase();
|
|
1605
1706
|
const args = parts.slice(1);
|
|
1606
|
-
|
|
1607
|
-
|
|
1707
|
+
// Quitting takes a CONFIRM (player request: "I just exited by
|
|
1708
|
+
// mistake"): the first "quit" arms it, "y"/"yes" (or quit again)
|
|
1709
|
+
// seals it, anything else disarms and runs as the command it is.
|
|
1710
|
+
if (this._quitArmed) {
|
|
1711
|
+
this._quitArmed = false;
|
|
1712
|
+
if (/^(y|yes|quit|exit)$/i.test(verb)) {
|
|
1713
|
+
return null;
|
|
1714
|
+
}
|
|
1715
|
+
// fall through -- the answer was a real command; play on.
|
|
1716
|
+
}
|
|
1717
|
+
else if (verb === 'quit' || verb === 'exit') {
|
|
1718
|
+
this._quitArmed = true;
|
|
1719
|
+
return `Really quit? ("y" to confirm -- anything else keeps playing.)`;
|
|
1720
|
+
}
|
|
1721
|
+
// Out cold with the fight settling around you (see
|
|
1722
|
+
// Scene.beginPlayerKnockout): unconsciousness is not a prompt. Only
|
|
1723
|
+
// quit (handled above) still answers.
|
|
1724
|
+
if (this.player.isUnconscious() && this.scene.playerKnockout) {
|
|
1725
|
+
return `You're out cold -- the world moves without you.`;
|
|
1608
1726
|
}
|
|
1609
1727
|
if (verb === 'maka') {
|
|
1610
1728
|
return 'You are still in the game, type "quit" to exit';
|
|
@@ -1653,10 +1771,12 @@ export default class Game {
|
|
|
1653
1771
|
return `I don't understand "${verb}" -- did you mean "${guess}"?`;
|
|
1654
1772
|
}
|
|
1655
1773
|
const corrected = await this.handleInput([guess, ...args].join(' '));
|
|
1656
|
-
// The correction note
|
|
1657
|
-
//
|
|
1658
|
-
|
|
1659
|
-
|
|
1774
|
+
// The correction note stays INLINE and always-on (player ruling:
|
|
1775
|
+
// it briefly moved to the Mechanics ticker, and a conversational
|
|
1776
|
+
// "well I'm here..." silently became a SELL with nothing in the
|
|
1777
|
+
// world log explaining the refusal that came back). It's not dice
|
|
1778
|
+
// noise -- it's the only receipt for a command you didn't type.
|
|
1779
|
+
return corrected === null ? null : `(read "${verb}" as "${guess}")\n${corrected ?? ''}`;
|
|
1660
1780
|
}
|
|
1661
1781
|
return `I don't understand the command "${verb}".`;
|
|
1662
1782
|
}
|
|
@@ -1670,7 +1790,7 @@ export default class Game {
|
|
|
1670
1790
|
* future command can't silently fall out of help.
|
|
1671
1791
|
*/
|
|
1672
1792
|
static HELP_CATEGORIES = [
|
|
1673
|
-
{ title: 'moving', entries: [['look'], ['go'], ['map', 'exits'], ['search'], ['sneak']] },
|
|
1793
|
+
{ title: 'moving', entries: [['look'], ['go'], ['move', 'walk', 'approach', 'run', 'sprint'], ['map', 'exits'], ['search'], ['sneak']] },
|
|
1674
1794
|
{ title: 'gear', entries: [['inv', 'inventory'], ['equipment', 'eq'], ['take'], ['drop'], ['give'], ['store'], ['equip'], ['unequip'], ['brandish', 'draw'], ['holster', 'stow'], ['reload'], ['use'], ['read']] },
|
|
1675
1795
|
{ title: 'combat', entries: [['attack', 'kill'], ['subdue'], ['order'], ['stance'], ['surrender'], ['edge'], ['rest']] },
|
|
1676
1796
|
{ title: 'magic', entries: [['spells'], ['cast'], ['summon', 'conjure'], ['project'], ['return'], ['assense']] },
|
|
@@ -1679,7 +1799,7 @@ export default class Game {
|
|
|
1679
1799
|
{ title: 'street', entries: [['browse', 'buy'], ['sell'], ['work', 'gig'], ['treat'], ['eat'], ['drink'], ['lifestyle'], ['pay']] },
|
|
1680
1800
|
{ title: 'barriers', entries: [['solve'], ['pick'], ['bluff'], ['unlock'], ['lock']] },
|
|
1681
1801
|
{ title: 'character', entries: [['sheet', 'stats'], ['advance', 'train'], ['qualities', 'quality'], ['initiate', 'submerge'], ['install', 'chrome']] },
|
|
1682
|
-
{ title: 'session', entries: [['help'], ['clear'], ['quit', 'exit']] },
|
|
1802
|
+
{ title: 'session', entries: [['help'], ['hints'], ['clear'], ['quit', 'exit']] },
|
|
1683
1803
|
];
|
|
1684
1804
|
/** Descriptions for the verbs handled inline in handleInput() rather than registered. */
|
|
1685
1805
|
static BUILTIN_DESCRIPTIONS = {
|