@maka/maka-cli 5.5.0 → 5.7.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 +17 -1
- 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 +79 -0
- 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 +193 -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 +10 -3
- 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 +12 -4
- 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 +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +63 -7
- 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 +277 -44
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
- 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 +4 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +15 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +3 -3
- 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 +62 -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/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 +5 -2
- 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 +6 -2
- 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/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 +131 -4
- 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 +115 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +464 -25
- 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.js +70 -7
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +109 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +423 -39
- 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/scene.d.ts +9 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +41 -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 +50 -3
- 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 +25 -14
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +100 -10
- 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/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 +84 -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/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/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/package.json +2 -2
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import blessed from 'blessed';
|
|
2
2
|
import { capitalCase } from 'change-case';
|
|
3
|
+
import { Category } from './types/shared/item-enum.js';
|
|
4
|
+
import { Item } from './models/item.js';
|
|
3
5
|
import { Room } from './models/_index.js';
|
|
6
|
+
import { isFence, LIFESTYLE_TIERS } from './utilities/commerce.js';
|
|
7
|
+
import { rollPool, formatRoll } from './utilities/dice.js';
|
|
4
8
|
import { Direction } from './types/shared/direction-enum.js';
|
|
5
9
|
import { Logger } from './utilities/logger.js';
|
|
6
10
|
import { LookCommand } from './commands/look.js';
|
|
@@ -47,6 +51,20 @@ import { PanCommand } from './commands/pan.js';
|
|
|
47
51
|
import { RebootCommand } from './commands/reboot.js';
|
|
48
52
|
import { BrandishCommand, HolsterCommand } from './commands/brandish.js';
|
|
49
53
|
import { AdvanceCommand } from './commands/advance.js';
|
|
54
|
+
import { QualityTradeCommand } from './commands/quality-trade.js';
|
|
55
|
+
import { AskCommand } from './commands/ask.js';
|
|
56
|
+
import { ContactsCommand } from './commands/contacts.js';
|
|
57
|
+
import { JumpCommand } from './commands/jump.js';
|
|
58
|
+
import { InitiateCommand } from './commands/initiate.js';
|
|
59
|
+
import { InstallCommand } from './commands/install.js';
|
|
60
|
+
import { BuyCommand } from './commands/buy.js';
|
|
61
|
+
import { SellCommand } from './commands/sell.js';
|
|
62
|
+
import { PayCommand } from './commands/pay.js';
|
|
63
|
+
import { LifestyleCommand } from './commands/lifestyle.js';
|
|
64
|
+
import { TreatCommand } from './commands/treat.js';
|
|
65
|
+
import { WorkCommand } from './commands/work.js';
|
|
66
|
+
import { EatCommand } from './commands/eat.js';
|
|
67
|
+
import { DrinkCommand } from './commands/drink.js';
|
|
50
68
|
import { equipIntoEmptySlots } from './archetypes.js';
|
|
51
69
|
import { NPC } from './models/npc.js';
|
|
52
70
|
import { HaggleCommand } from './commands/haggle.js';
|
|
@@ -99,6 +117,255 @@ export default class Game {
|
|
|
99
117
|
get hasHub() {
|
|
100
118
|
return this._hubScene !== undefined;
|
|
101
119
|
}
|
|
120
|
+
/** The active job's name (seed name) -- notoriety incidents cite it. */
|
|
121
|
+
get currentJobName() {
|
|
122
|
+
return this.sceneSeed?.name;
|
|
123
|
+
}
|
|
124
|
+
// ==================== CONTACTS (SR5e p.386-389) ====================
|
|
125
|
+
// Earned, never bought: a hub NPC becomes a contact through a real
|
|
126
|
+
// transaction (a sale, a bowl served, stitches, a fenced score), and
|
|
127
|
+
// the relationship deepens through gigs (to Loyalty 6) or patronage
|
|
128
|
+
// (money alone caps at Loyalty 3 -- cred buys familiarity, not
|
|
129
|
+
// friendship). CONNECTION is the contact's reach (role heuristic,
|
|
130
|
+
// same duck-typing school as commerce.isFence); LOYALTY is yours to
|
|
131
|
+
// grow. "ask" (commands/ask.ts) is what the roster is FOR: legwork
|
|
132
|
+
// against the real pending-run seed. One pull per contact per
|
|
133
|
+
// homecoming (asked resets in returnToHub).
|
|
134
|
+
contacts = new Map();
|
|
135
|
+
/** How much juice the name carries, read off the role. */
|
|
136
|
+
static connectionFor(npc) {
|
|
137
|
+
const hay = `${npc.description ?? ''} ${npc.name}`.toLowerCase();
|
|
138
|
+
if (npc.name === FIXER_NAME || /fixer|johnson/.test(hay))
|
|
139
|
+
return 5;
|
|
140
|
+
if (/fence|pawn|launder|smuggl|no questions/.test(hay))
|
|
141
|
+
return 4;
|
|
142
|
+
if (/talismonger|street doc|clinic|surgeon|decker|information/.test(hay))
|
|
143
|
+
return 3;
|
|
144
|
+
if (/bartender|barkeep|vendor|noodle|shop|stall|merchant|dealer|market/.test(hay))
|
|
145
|
+
return 2;
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* HOME TURF: the hub is where the runner LIVES -- everyone in the
|
|
150
|
+
* district already knows them (player request: "all NPCs there should
|
|
151
|
+
* already know them"). Called at hub capture: the whole meat-plane
|
|
152
|
+
* cast opens at Loyalty 1 ("knows your name"), so legwork works from
|
|
153
|
+
* day one and only run-scene faces are ever strangers. Depth is still
|
|
154
|
+
* earned -- trade, gigs, patronage -- exactly as before. Ice and
|
|
155
|
+
* spirits don't do noodles: non-meat actors stay off the roster.
|
|
156
|
+
*/
|
|
157
|
+
seedHomeTurfContacts() {
|
|
158
|
+
for (const actor of this._hubScene?.allActors ?? []) {
|
|
159
|
+
if (actor === this.player || actor.plane !== 'meat')
|
|
160
|
+
continue;
|
|
161
|
+
if (this.contacts.has(actor.name))
|
|
162
|
+
continue;
|
|
163
|
+
this.contacts.set(actor.name, { connection: Game.connectionFor(actor), loyalty: 1, spent: 0, gigs: 0, asked: false });
|
|
164
|
+
}
|
|
165
|
+
Logger.getInstance().write(`Home turf: ${this.contacts.size} contacts seeded from the hub cast.`);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Records a real transaction with a hub NPC: first contact opens the
|
|
169
|
+
* relationship at Loyalty 1 (a safeguard -- the hub cast is pre-seeded
|
|
170
|
+
* by seedHomeTurfContacts); gigs raise it a point each (cap 6);
|
|
171
|
+
* cumulative spend raises it at each full 1,000 nuyen (cap 3).
|
|
172
|
+
* Returns a player-facing line when something changed, else null.
|
|
173
|
+
*/
|
|
174
|
+
touchContact(name, kind, spent = 0) {
|
|
175
|
+
if (!this.hasHub)
|
|
176
|
+
return null;
|
|
177
|
+
const npc = this._hubScene?.allActors?.find(a => a.name === name && a !== this.player);
|
|
178
|
+
if (!npc)
|
|
179
|
+
return null;
|
|
180
|
+
let c = this.contacts.get(name);
|
|
181
|
+
if (!c) {
|
|
182
|
+
c = { connection: Game.connectionFor(npc), loyalty: 1, spent: 0, gigs: 0, asked: false };
|
|
183
|
+
this.contacts.set(name, c);
|
|
184
|
+
if (kind === 'trade')
|
|
185
|
+
c.spent += spent;
|
|
186
|
+
if (kind === 'gig')
|
|
187
|
+
c.gigs += 1;
|
|
188
|
+
Logger.getInstance().write(`Contact opened: ${name} (connection ${c.connection}).`);
|
|
189
|
+
return `${name} knows your name now -- a CONTACT (Connection ${c.connection}, Loyalty 1). ("contacts" for the roster, "ask ${name} about ..." for legwork.)`;
|
|
190
|
+
}
|
|
191
|
+
if (kind === 'gig') {
|
|
192
|
+
c.gigs += 1;
|
|
193
|
+
if (c.loyalty < 6) {
|
|
194
|
+
c.loyalty += 1;
|
|
195
|
+
return `${name} remembers who made that hand-off happen. (Loyalty ${c.loyalty}.)`;
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const thousandsBefore = Math.floor(c.spent / 1000);
|
|
200
|
+
c.spent += spent;
|
|
201
|
+
if (Math.floor(c.spent / 1000) > thousandsBefore && c.loyalty < 3) {
|
|
202
|
+
c.loyalty += 1;
|
|
203
|
+
return `${name} has started keeping your usual ready. (Loyalty ${c.loyalty} -- patronage tops out at 3; run their errands to go deeper.)`;
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
// ==================== Downtime economy (hub life) ====================
|
|
208
|
+
// A fence's standing quote (see commands/sell.ts / haggle.ts): one at a
|
|
209
|
+
// time, replaced by any new quote, dead the moment the fence isn't at
|
|
210
|
+
// the counter. Lives here because sell and haggle are separate commands
|
|
211
|
+
// negotiating the same number.
|
|
212
|
+
pendingSale;
|
|
213
|
+
// LIFESTYLE (SR5e p.369, monthly costs scaled to per-job-cycle): the
|
|
214
|
+
// hideout has a tier, upkeep is charged on every homecoming
|
|
215
|
+
// (returnToHub), and the tier buys real mechanics -- rest quality at
|
|
216
|
+
// home (rest.ts) and, higher up, a bonus edge every job. Can't cover
|
|
217
|
+
// the upkeep? You slide DOWN a tier and the shortfall becomes debt --
|
|
218
|
+
// and while the tab stands, rest at the hideout recovers half (rest.ts;
|
|
219
|
+
// "pay" squares it, pay.ts; "lifestyle" views and moves tiers).
|
|
220
|
+
lifestyleTier = 'Squatter';
|
|
221
|
+
rentDebt = 0;
|
|
222
|
+
/** The active lifestyle tier's row (falls back to Squatter). */
|
|
223
|
+
get lifestyle() {
|
|
224
|
+
return LIFESTYLE_TIERS.find(t => t.name === this.lifestyleTier) ?? LIFESTYLE_TIERS[1];
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Makes the hideout LOOK like what the rent buys: the room's description
|
|
228
|
+
* follows the active tier (player request -- an upgraded lifestyle read
|
|
229
|
+
* as the same windowless squat). Called wherever the tier moves: the
|
|
230
|
+
* lifestyle command, and the shortfall slide in returnToHub.
|
|
231
|
+
*/
|
|
232
|
+
syncHomeToLifestyle() {
|
|
233
|
+
if (this._homeRoom)
|
|
234
|
+
this._homeRoom.description = this.lifestyle.homeDescription;
|
|
235
|
+
}
|
|
236
|
+
// RESTOCK (see returnToHub): consumable shop stock -- food, drink,
|
|
237
|
+
// ammo, patches -- snapshotted at hub capture and restored to any shelf
|
|
238
|
+
// found empty on each homecoming. Durable one-offs are deliberately NOT
|
|
239
|
+
// in the category set: the pawnshop's lockpicks, once sold, stay sold.
|
|
240
|
+
static RESTOCK_CATEGORIES = new Set([
|
|
241
|
+
Category.Food, Category.Drink, Category.Ammunition, Category.StimPatch, Category.Medical, Category.SlapPatch,
|
|
242
|
+
]);
|
|
243
|
+
_restockSnapshot = [];
|
|
244
|
+
/** Captures every restockable shelf item's blueprint (hub capture time). */
|
|
245
|
+
snapshotVendorStock() {
|
|
246
|
+
this._restockSnapshot = [];
|
|
247
|
+
for (const room of Object.values(this.scene.getRooms())) {
|
|
248
|
+
for (const item of room.inventory.getAllItems()) {
|
|
249
|
+
if (!item.owner || !Game.RESTOCK_CATEGORIES.has(item.category))
|
|
250
|
+
continue;
|
|
251
|
+
this._restockSnapshot.push({
|
|
252
|
+
room: room.name,
|
|
253
|
+
owner: item.owner,
|
|
254
|
+
config: {
|
|
255
|
+
name: item.name, description: item.description, size: item.size,
|
|
256
|
+
category: item.category, shape: item.shape, color: item.color,
|
|
257
|
+
texture: item.texture, rating: item.rating, weight: item.weight,
|
|
258
|
+
details: item.details, price: item.price,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/** Refills empty shelves from the snapshot; returns how many came back. */
|
|
265
|
+
restockVendorShelves() {
|
|
266
|
+
let restocked = 0;
|
|
267
|
+
const rooms = this.scene.getRooms();
|
|
268
|
+
for (const entry of this._restockSnapshot) {
|
|
269
|
+
const room = Object.values(rooms).find(r => r.name === entry.room);
|
|
270
|
+
if (!room || room.inventory.hasItem(entry.config.name))
|
|
271
|
+
continue;
|
|
272
|
+
const fresh = new Item(entry.config);
|
|
273
|
+
fresh.owner = entry.owner;
|
|
274
|
+
room.inventory.addItem(fresh);
|
|
275
|
+
restocked += 1;
|
|
276
|
+
}
|
|
277
|
+
return restocked;
|
|
278
|
+
}
|
|
279
|
+
/** The fence moves merchandise: anything sold to a fence is sold ON. */
|
|
280
|
+
fenceSellsOn() {
|
|
281
|
+
for (const actor of this.scene.allActors) {
|
|
282
|
+
if (actor === this.player || !isFence(actor, actor.currentLocation))
|
|
283
|
+
continue;
|
|
284
|
+
for (const item of [...actor.inventory.getAllItems()]) {
|
|
285
|
+
if (item.transferable && item.category !== Category.Commlink) {
|
|
286
|
+
actor.inventory.removeItem(item.name);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// SIDE GIGS (see commands/work.ts / give.ts): one small favor on offer
|
|
292
|
+
// at a time, rotating each homecoming like the rumor. Take it with
|
|
293
|
+
// "work" in front of the giver; deliver the package with "give"; the
|
|
294
|
+
// pay lands on delivery.
|
|
295
|
+
gigAvailable;
|
|
296
|
+
activeGig;
|
|
297
|
+
_gigPool = [];
|
|
298
|
+
_gigIndex = 0;
|
|
299
|
+
/** Rotates the gig on offer (skipped while one is already in hand). */
|
|
300
|
+
advanceGig() {
|
|
301
|
+
if (this._gigPool.length === 0 || this.activeGig)
|
|
302
|
+
return;
|
|
303
|
+
this.gigAvailable = this._gigPool[this._gigIndex % this._gigPool.length];
|
|
304
|
+
this._gigIndex += 1;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* WORK FROM A RELATIONSHIP (see commands/ask.ts): a contact puts THEIR
|
|
308
|
+
* errand on the table. Their seeded gigs get first claim -- spliced
|
|
309
|
+
* out of the rotating pool, so work handed to you personally never
|
|
310
|
+
* reappears on the ambient board -- and when the pool holds nothing of
|
|
311
|
+
* theirs, the relationship synthesizes one: a sealed parcel to another
|
|
312
|
+
* hub local, priced off Connection (their reach IS the stakes) with a
|
|
313
|
+
* Loyalty kicker. Loyalty 5+ pays the family rate (+25%). The offer
|
|
314
|
+
* lands as gigAvailable: the existing "work" pickup and "give"
|
|
315
|
+
* delivery handle the rest unchanged.
|
|
316
|
+
*/
|
|
317
|
+
offerGigFrom(contactName) {
|
|
318
|
+
const contact = this.contacts.get(contactName);
|
|
319
|
+
if (!contact || !this.hasHub || this.activeGig)
|
|
320
|
+
return null;
|
|
321
|
+
let gig = undefined;
|
|
322
|
+
const seeded = this._gigPool.findIndex(g => g.from === contactName);
|
|
323
|
+
if (seeded >= 0) {
|
|
324
|
+
gig = this._gigPool.splice(seeded, 1)[0];
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const locals = (this._hubScene?.allActors ?? [])
|
|
328
|
+
.filter(a => a !== this.player && a.name !== contactName && a.plane === 'meat' && !a.isIncapacitated())
|
|
329
|
+
.map(a => a.name);
|
|
330
|
+
if (locals.length === 0)
|
|
331
|
+
return null;
|
|
332
|
+
const to = locals[Math.floor(Math.random() * locals.length)];
|
|
333
|
+
const pay = 60 + contact.connection * 40 + contact.loyalty * 10;
|
|
334
|
+
gig = {
|
|
335
|
+
from: contactName,
|
|
336
|
+
to,
|
|
337
|
+
itemName: `${contactName}'s Sealed Parcel`,
|
|
338
|
+
itemDescription: `A tight-wrapped parcel, taped past curiosity. Whatever it is, ${contactName} trusts you to not ask.`,
|
|
339
|
+
pay,
|
|
340
|
+
brief: `Quiet job, no questions asked: this needs to reach ${to}, today, unopened. ${pay} nuyen on delivery.`,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const familyRate = contact.loyalty >= 5;
|
|
344
|
+
if (familyRate)
|
|
345
|
+
gig = { ...gig, pay: Math.round(gig.pay * 1.25) };
|
|
346
|
+
this.gigAvailable = gig;
|
|
347
|
+
Logger.getInstance().write(`Contact gig offered by ${contactName}: "${gig.itemName}" -> ${gig.to}, ${gig.pay}${familyRate ? ' (family rate)' : ''}.`);
|
|
348
|
+
return { ...gig, familyRate };
|
|
349
|
+
}
|
|
350
|
+
// FIRST-OF-ITS-KIND bounty (see commands/sell.ts): the fence pays a
|
|
351
|
+
// 50% premium on the first item of each category to cross her counter.
|
|
352
|
+
fencedCategories = new Set();
|
|
353
|
+
// Street rumors: the hub seed ships a pool (scene2.json "rumors"); one
|
|
354
|
+
// is ACTIVE at a time, rotating each time a run wraps. The active rumor
|
|
355
|
+
// is planted in the world-event mesh (every hub NPC's prompt reads it,
|
|
356
|
+
// so Mama Wu can actually gossip) and handed to the next job's skeleton
|
|
357
|
+
// prompt as its backdrop -- the street foreshadows the work.
|
|
358
|
+
_rumorPool = [];
|
|
359
|
+
_rumorIndex = 0;
|
|
360
|
+
activeRumor;
|
|
361
|
+
/** Rotates to the next rumor and plants it where NPCs will hear it. */
|
|
362
|
+
advanceRumor() {
|
|
363
|
+
if (this._rumorPool.length === 0)
|
|
364
|
+
return;
|
|
365
|
+
this.activeRumor = this._rumorPool[this._rumorIndex % this._rumorPool.length];
|
|
366
|
+
this._rumorIndex += 1;
|
|
367
|
+
this._hubScene?.addWorldEvent(`Word around the district: ${this.activeRumor}`);
|
|
368
|
+
}
|
|
102
369
|
/** The persistent hideout room -- go.ts's walk-home trigger checks it. */
|
|
103
370
|
get homeRoom() {
|
|
104
371
|
return this._homeRoom;
|
|
@@ -242,6 +509,26 @@ export default class Game {
|
|
|
242
509
|
CommandFactory.registerCommand('hangup', EndCallCommand);
|
|
243
510
|
CommandFactory.registerCommand('advance', AdvanceCommand);
|
|
244
511
|
CommandFactory.registerCommand('train', AdvanceCommand);
|
|
512
|
+
CommandFactory.registerCommand('qualities', QualityTradeCommand);
|
|
513
|
+
CommandFactory.registerCommand('quality', QualityTradeCommand);
|
|
514
|
+
CommandFactory.registerCommand('ask', AskCommand);
|
|
515
|
+
CommandFactory.registerCommand('contacts', ContactsCommand);
|
|
516
|
+
CommandFactory.registerCommand('jump', JumpCommand);
|
|
517
|
+
CommandFactory.registerCommand('rig', JumpCommand);
|
|
518
|
+
CommandFactory.registerCommand('initiate', InitiateCommand);
|
|
519
|
+
CommandFactory.registerCommand('submerge', InitiateCommand);
|
|
520
|
+
CommandFactory.registerCommand('install', InstallCommand);
|
|
521
|
+
CommandFactory.registerCommand('chrome', InstallCommand);
|
|
522
|
+
CommandFactory.registerCommand('buy', BuyCommand);
|
|
523
|
+
CommandFactory.registerCommand('browse', BuyCommand);
|
|
524
|
+
CommandFactory.registerCommand('sell', SellCommand);
|
|
525
|
+
CommandFactory.registerCommand('pay', PayCommand);
|
|
526
|
+
CommandFactory.registerCommand('eat', EatCommand);
|
|
527
|
+
CommandFactory.registerCommand('drink', DrinkCommand);
|
|
528
|
+
CommandFactory.registerCommand('lifestyle', LifestyleCommand);
|
|
529
|
+
CommandFactory.registerCommand('treat', TreatCommand);
|
|
530
|
+
CommandFactory.registerCommand('work', WorkCommand);
|
|
531
|
+
CommandFactory.registerCommand('gig', WorkCommand);
|
|
245
532
|
CommandFactory.registerCommand('haggle', HaggleCommand);
|
|
246
533
|
CommandFactory.registerCommand('negotiate', HaggleCommand);
|
|
247
534
|
CommandFactory.registerCommand('sneak', SneakCommand);
|
|
@@ -307,9 +594,12 @@ export default class Game {
|
|
|
307
594
|
this.player.currentLocation = this.determineStartRoom();
|
|
308
595
|
this.registerCommands();
|
|
309
596
|
this._screen = screen;
|
|
597
|
+
// The description follows the rent from day one (see
|
|
598
|
+
// syncHomeToLifestyle) -- a fresh session at the default Squatter tier
|
|
599
|
+
// reads exactly as the hideout always has.
|
|
310
600
|
this._homeRoom = new Room({
|
|
311
601
|
name: 'Your Hideout',
|
|
312
|
-
description:
|
|
602
|
+
description: this.lifestyle.homeDescription,
|
|
313
603
|
roomType: 'Residence',
|
|
314
604
|
});
|
|
315
605
|
this.scene.addRoom(this._homeRoom);
|
|
@@ -325,6 +615,17 @@ export default class Game {
|
|
|
325
615
|
if (this.scene.isHub) {
|
|
326
616
|
this._hubScene = this.scene;
|
|
327
617
|
this._hubName = this.sceneSeed?.name ?? 'home';
|
|
618
|
+
// The street starts talking from minute one.
|
|
619
|
+
this._rumorPool = this.sceneSeed?.rumors ?? [];
|
|
620
|
+
this.advanceRumor();
|
|
621
|
+
// The economy wakes up with it: shelf blueprints for restocking,
|
|
622
|
+
// the first odd job on offer, and the lifestyle's edge perk.
|
|
623
|
+
this.snapshotVendorStock();
|
|
624
|
+
this._gigPool = this.sceneSeed?.gigs ?? [];
|
|
625
|
+
this.advanceGig();
|
|
626
|
+
this.player.lifestyleEdgeBonus = this.lifestyle.edgeBonus;
|
|
627
|
+
// Home turf knows its own (see seedHomeTurfContacts).
|
|
628
|
+
this.seedHomeTurfContacts();
|
|
328
629
|
}
|
|
329
630
|
// Gear up automatically: wearables (and the commlink) slot into any
|
|
330
631
|
// EMPTY slots at load -- no "equip all" ritual to start a session
|
|
@@ -394,11 +695,13 @@ export default class Game {
|
|
|
394
695
|
* instant feedback, but this stays safe to call directly too).
|
|
395
696
|
*/
|
|
396
697
|
async continueToNextScene() {
|
|
397
|
-
// Hub sessions: a
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
|
|
698
|
+
// Hub sessions: a RUN doesn't chain into the next draft -- going
|
|
699
|
+
// home is going home, and the next run is commissioned from there
|
|
700
|
+
// (call the fixer). Deliberately NOT gated on completion: the taxi
|
|
701
|
+
// will carry a runner away from a job gone sideways (see call.ts) --
|
|
702
|
+
// walking away just means the payout never landed. Classic sessions
|
|
703
|
+
// skip this and keep the old continue->draft loop.
|
|
704
|
+
if (this._hubScene && this.scene !== this._hubScene) {
|
|
402
705
|
return this.returnToHub();
|
|
403
706
|
}
|
|
404
707
|
const gate = this.canDraftNextScene();
|
|
@@ -425,6 +728,9 @@ export default class Game {
|
|
|
425
728
|
const hub = this._hubScene;
|
|
426
729
|
if (!hub)
|
|
427
730
|
return `There's no home to go back to.`;
|
|
731
|
+
// Read the run's fate BEFORE the scene swap below discards it -- a
|
|
732
|
+
// walked-away job gets a different homecoming line than a wrapped one.
|
|
733
|
+
const wrappedRun = this.scene !== hub && this.scene.isCompleted();
|
|
428
734
|
// Settle cross-plane and combat state that must not leak into the hub
|
|
429
735
|
// (the run's rooms -- including a bodyRoom the player left behind --
|
|
430
736
|
// are about to be orphaned wholesale).
|
|
@@ -433,6 +739,7 @@ export default class Game {
|
|
|
433
739
|
this.player.simMode = undefined;
|
|
434
740
|
this.player.astralTicks = 0;
|
|
435
741
|
this.player.activeDeck = undefined;
|
|
742
|
+
this.player.riggedDrone = undefined;
|
|
436
743
|
this.player.astralPerceiving = false;
|
|
437
744
|
this.player.sneaking = false;
|
|
438
745
|
this.player.inExchange = false;
|
|
@@ -444,12 +751,60 @@ export default class Game {
|
|
|
444
751
|
this.connectHomeRoom(hub.determineStartRoom());
|
|
445
752
|
this.player.currentLocation = this._homeRoom;
|
|
446
753
|
this.player.cameFrom = undefined;
|
|
754
|
+
this.pendingSale = undefined;
|
|
755
|
+
// Contacts have had time to work their networks again -- one legwork
|
|
756
|
+
// pull each per homecoming (see commands/ask.ts).
|
|
757
|
+
for (const contact of this.contacts.values())
|
|
758
|
+
contact.asked = false;
|
|
759
|
+
// LIFESTYLE upkeep comes due with the homecoming: this tier's share
|
|
760
|
+
// plus any standing debt, paid from carried cred as far as it goes.
|
|
761
|
+
// Coming up short SLIDES YOU DOWN a tier (the district notices) and
|
|
762
|
+
// the shortfall becomes the tab -- rest.ts halves recovery while you
|
|
763
|
+
// owe; "pay" squares it; "lifestyle" climbs back up.
|
|
764
|
+
const tier = this.lifestyle;
|
|
765
|
+
const due = tier.upkeep + this.rentDebt;
|
|
766
|
+
const paid = Math.min(this.player.currency, due);
|
|
767
|
+
if (paid > 0)
|
|
768
|
+
this.player.adjustCurrency(-paid);
|
|
769
|
+
this.rentDebt = due - paid;
|
|
770
|
+
let rentLine;
|
|
771
|
+
if (this.rentDebt > 0) {
|
|
772
|
+
const tierIndex = LIFESTYLE_TIERS.findIndex(t => t.name === tier.name);
|
|
773
|
+
if (tierIndex > 0) {
|
|
774
|
+
this.lifestyleTier = LIFESTYLE_TIERS[tierIndex - 1].name;
|
|
775
|
+
// The downgrade is visible the moment you look around.
|
|
776
|
+
this.syncHomeToLifestyle();
|
|
777
|
+
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. ("pay" the tab, "lifestyle" to climb back.)`;
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
rentLine = `Even the doorway costs something: ${this.rentDebt} nuyen on the tab. ("pay" when you're flush.)`;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
rentLine = `The landlord's drone takes its ${paid} nuyen (${tier.name} upkeep) at the door and buzzes off satisfied.`;
|
|
785
|
+
}
|
|
786
|
+
this.player.lifestyleEdgeBonus = this.lifestyle.edgeBonus;
|
|
787
|
+
// A High lifestyle erases the run: hot shower, real bed, full reset.
|
|
788
|
+
let comfortLine = '';
|
|
789
|
+
if (this.lifestyle.clearStunOnReturn && this.player.stunTaken > 0) {
|
|
790
|
+
const cleared = this.player.healStun(this.player.stunTaken);
|
|
791
|
+
comfortLine = `\nThe shower runs hot until the run is a rumor -- ${cleared} stun gone before your head hits the pillow.`;
|
|
792
|
+
}
|
|
793
|
+
// Fresh streets, fresh gossip, restocked shelves, new work.
|
|
794
|
+
this.advanceRumor();
|
|
795
|
+
this.fenceSellsOn();
|
|
796
|
+
const restocked = this.restockVendorShelves();
|
|
797
|
+
const restockLine = restocked > 0 ? `\nThe district has restocked around you -- the wok steams, the bar's shelf is full, the market stalls are loud again.` : '';
|
|
798
|
+
this.advanceGig();
|
|
447
799
|
this.updateEquipment(this.player.equipment);
|
|
448
800
|
this.updateInventory(this.player.inventory, capitalCase(this.scene.getCurrencyType()));
|
|
449
801
|
this.updateExits(this.player.currentLocation);
|
|
450
802
|
this.updateStatus();
|
|
451
|
-
Logger.getInstance().write(`Returned to hub "${this._hubName}"
|
|
452
|
-
|
|
803
|
+
Logger.getInstance().write(`Returned to hub "${this._hubName}". Lifestyle ${this.lifestyleTier}: ${paid} paid, ${this.rentDebt} owed. Restocked ${restocked}. Rumor: ${this.activeRumor ?? '(none)'}`);
|
|
804
|
+
const fateLine = wrappedRun
|
|
805
|
+
? `The run's wrapped and the street swallows you back up.`
|
|
806
|
+
: `The run's behind you -- unfinished, unpaid, already someone else's problem. The street doesn't ask.`;
|
|
807
|
+
return `\n=== ${this._hubName.toUpperCase()} ===\n${fateLine} You're home -- your hideout, one door off ${hub.determineStartRoom().name}. Call ${FIXER_NAME} when you're hungry for the next job.\n${rentLine}${comfortLine}${restockLine}`;
|
|
453
808
|
}
|
|
454
809
|
/**
|
|
455
810
|
* The actual drafting work, split out from continueToNextScene() purely
|
|
@@ -481,7 +836,7 @@ export default class Game {
|
|
|
481
836
|
//
|
|
482
837
|
// The player object rides along so the job is drafted FOR this runner
|
|
483
838
|
// -- their archetype, stats, and toolkit (see buildPlayerDossier).
|
|
484
|
-
const nextSeed = await SceneSeedGenerator.generate(this._logFilePath, nextTier, previousFixerName, this.player);
|
|
839
|
+
const nextSeed = await SceneSeedGenerator.generate(this._logFilePath, nextTier, previousFixerName, this.player, this.activeRumor);
|
|
485
840
|
if (!nextSeed) {
|
|
486
841
|
return `Incoming call -- ${FIXER_NAME}: "Job fell through, chummer. Client got cold feet before the ink dried. Give me another call in a bit -- I'll shake something loose."`;
|
|
487
842
|
}
|
|
@@ -519,14 +874,21 @@ export default class Game {
|
|
|
519
874
|
this.scene = await SceneSynthesizer.synthesizeFromJson(seed, this.player, false, false);
|
|
520
875
|
this.scene.initialize(this);
|
|
521
876
|
this.player.currentLocation = this.determineStartRoom();
|
|
522
|
-
// New job
|
|
523
|
-
|
|
524
|
-
//
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
877
|
+
// New job -- and the needs come due at the door (see
|
|
878
|
+
// applyDepartureNeeds): edge only refills fed-and-slept, and ignored
|
|
879
|
+
// needs land as locked fatigue. Lines ride the JOB banner below.
|
|
880
|
+
const needsLines = this.applyDepartureNeeds();
|
|
881
|
+
// The home base is owned by Game, not any one Scene. HUB sessions
|
|
882
|
+
// leave it OUT of run scenes (player request): your cot is across
|
|
883
|
+
// town, not stapled to the jobsite -- the taxi ("call taxi", fare on
|
|
884
|
+
// the tab if you're broke) is the road home. Classic sessions keep
|
|
885
|
+
// the hideout adjacent as always: it's the only home they have.
|
|
886
|
+
if (!this.hasHub) {
|
|
887
|
+
this.scene.addRoom(this._homeRoom);
|
|
888
|
+
this.connectHomeRoom(this.player.currentLocation);
|
|
889
|
+
if (opts.startInHideout) {
|
|
890
|
+
this.player.currentLocation = this._homeRoom;
|
|
891
|
+
}
|
|
530
892
|
}
|
|
531
893
|
this.updateEquipment(this.player.equipment);
|
|
532
894
|
this.updateInventory(this.player.inventory, capitalCase(this.scene.getCurrencyType()));
|
|
@@ -535,7 +897,67 @@ export default class Game {
|
|
|
535
897
|
// Fresh streets, no trail yet (see Player.cameFrom / the HUD crumb).
|
|
536
898
|
this.player.cameFrom = undefined;
|
|
537
899
|
this.warmNpcAppearancesInBackground();
|
|
538
|
-
return `\n=== JOB #${this._tier}: ${seed.name} ===\n${this.scene.story}`;
|
|
900
|
+
return `\n=== JOB #${this._tier}: ${seed.name} ===\n${this.scene.story}${needsLines.length > 0 ? `\n${needsLines.join('\n')}` : ''}`;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* The needs check at every job departure (SR5e p.56 / p.172, scaled to
|
|
904
|
+
* job-cycle "days"). Fed AND slept since the last departure: edge
|
|
905
|
+
* refills and you head out sharp. Skip either: edge stays at whatever's
|
|
906
|
+
* left. Keep skipping: escalating fatigue (1S, 2S, 3S... per cycle),
|
|
907
|
+
* resisted Body + Willpower, landing as LOCKED stun no recovery touches
|
|
908
|
+
* until the need is met (Player.healStun / markFed / markSlept). A
|
|
909
|
+
* Sleep Regulator (augmentations.ts) halves the effective sleep debt.
|
|
910
|
+
* Fatigue is capped one box short of a knockout -- RAW would drop you,
|
|
911
|
+
* but collapsing INSIDE the departure transition would strand the
|
|
912
|
+
* session between scenes; "running on fumes" carries the spirit.
|
|
913
|
+
*/
|
|
914
|
+
applyDepartureNeeds() {
|
|
915
|
+
const p = this.player;
|
|
916
|
+
const fed = p.ateThisCycle;
|
|
917
|
+
const slept = p.sleptThisCycle;
|
|
918
|
+
const lines = [];
|
|
919
|
+
p.hungerStreak = fed ? 0 : p.hungerStreak + 1;
|
|
920
|
+
p.sleepStreak = slept ? 0 : p.sleepStreak + 1;
|
|
921
|
+
p.refreshEdge(fed && slept);
|
|
922
|
+
if (fed && slept) {
|
|
923
|
+
lines.push(`Fed and slept -- you head out SHARP. (Edge ${p.edgeRemaining}/${p.edge + p.lifestyleEdgeBonus}.)`);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
const missing = !fed && !slept ? 'meal and no sleep' : !fed ? 'meal' : 'sleep';
|
|
927
|
+
lines.push(`No ${missing} since the last job -- your edge doesn't come back (${p.edgeRemaining} left). A hot meal and a night in your own bed reset it.`);
|
|
928
|
+
}
|
|
929
|
+
// Escalating, resisted, LOCKED fatigue per ignored need.
|
|
930
|
+
const events = [];
|
|
931
|
+
if (p.hungerStreak > 0) {
|
|
932
|
+
events.push({ kind: 'hunger', label: 'Hunger', dmg: p.hungerStreak });
|
|
933
|
+
}
|
|
934
|
+
const effectiveSleepDebt = p.hasAug('sleep-regulator') ? Math.floor(p.sleepStreak / 2) : p.sleepStreak;
|
|
935
|
+
if (effectiveSleepDebt > 0) {
|
|
936
|
+
events.push({ kind: 'sleep', label: 'Sleep deprivation', dmg: effectiveSleepDebt });
|
|
937
|
+
}
|
|
938
|
+
for (const event of events) {
|
|
939
|
+
const resist = rollPool(Math.max(1, p.body + p.willpower));
|
|
940
|
+
const taken = Math.max(0, event.dmg - resist.hits);
|
|
941
|
+
const headroom = Math.max(0, p.maxStunBoxes - p.stunTaken - 1);
|
|
942
|
+
const applied = Math.min(taken, headroom);
|
|
943
|
+
if (applied > 0) {
|
|
944
|
+
p.takeStun(applied);
|
|
945
|
+
p.addLockedFatigue(event.kind, applied);
|
|
946
|
+
lines.push(`${event.label} (${event.dmg}S): resist ${formatRoll(resist)} -- ${applied} stun, and it will NOT heal until you ${event.kind === 'hunger' ? 'eat' : 'sleep'}: ${p.stunSummary()}`);
|
|
947
|
+
}
|
|
948
|
+
else if (taken > 0) {
|
|
949
|
+
lines.push(`${event.label} (${event.dmg}S): resist ${formatRoll(resist)} -- you're running on fumes; there's barely anything left to take.`);
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
lines.push(`${event.label} (${event.dmg}S): resist ${formatRoll(resist)} -- you shake it off. For now.`);
|
|
953
|
+
}
|
|
954
|
+
Logger.getInstance().write(`Departure needs: ${event.label} ${event.dmg}S, resisted ${resist.hits}, applied ${applied} (locked).`);
|
|
955
|
+
}
|
|
956
|
+
// The new cycle starts unfed and unslept -- Wu's counter and your own
|
|
957
|
+
// cot are how these flip back (use.ts eat / rest.ts at a Residence).
|
|
958
|
+
p.ateThisCycle = false;
|
|
959
|
+
p.sleptThisCycle = false;
|
|
960
|
+
return lines;
|
|
539
961
|
}
|
|
540
962
|
/**
|
|
541
963
|
* "travel": takes the arranged transport to the pending job (see
|
|
@@ -588,8 +1010,13 @@ export default class Game {
|
|
|
588
1010
|
border: { type: 'line' },
|
|
589
1011
|
style: { fg: 'white', border: { fg: 'cyan' } },
|
|
590
1012
|
keys: true, // Enable keyboard input
|
|
591
|
-
mouse
|
|
592
|
-
|
|
1013
|
+
// NO mouse -- deliberately. Any widget with mouse:true makes
|
|
1014
|
+
// blessed arm xterm mouse tracking (?1000h/?1002h/?1003h/?1005h),
|
|
1015
|
+
// and three real sessions ended with the whole process tree dying
|
|
1016
|
+
// hard (no JS cleanup possible) and the shell flooded by raw mouse
|
|
1017
|
+
// reports on every cursor move. The input box is auto-focused and
|
|
1018
|
+
// keyboard-driven; click-to-focus bought nothing worth that risk.
|
|
1019
|
+
inputOnFocus: true,
|
|
593
1020
|
vi: true
|
|
594
1021
|
});
|
|
595
1022
|
this._inputBox = inputBox;
|
|
@@ -639,11 +1066,13 @@ export default class Game {
|
|
|
639
1066
|
}
|
|
640
1067
|
},
|
|
641
1068
|
keys: true, // Enable keyboard input
|
|
642
|
-
mouse:
|
|
1069
|
+
// NO mouse (see initInputBox): never arm mouse tracking. The log
|
|
1070
|
+
// auto-scrolls (alwaysScroll) and keys/vi still scroll it when
|
|
1071
|
+
// focused -- wheel-scroll wasn't worth shells full of coordinate
|
|
1072
|
+
// bytes every time a session died hard.
|
|
643
1073
|
tags: true // Intercept tags
|
|
644
1074
|
});
|
|
645
1075
|
this._logBox = logBox;
|
|
646
|
-
logBox.removeAllListeners('mousemove');
|
|
647
1076
|
screen.append(logBox);
|
|
648
1077
|
}
|
|
649
1078
|
/**
|
|
@@ -776,6 +1205,15 @@ export default class Game {
|
|
|
776
1205
|
else if (player.plane === 'astral') {
|
|
777
1206
|
lines.push(`{magenta-fg}✧ ASTRAL -- ${player.astralTicks} move${player.astralTicks === 1 ? '' : 's'} out -- body at ${player.bodyRoom?.name ?? '???'}{/magenta-fg}`);
|
|
778
1207
|
}
|
|
1208
|
+
else if (player.plane === 'drone') {
|
|
1209
|
+
// Jumped-in: the HULL is the track that matters (attacks land there,
|
|
1210
|
+
// half bleeding through the link), so it lives on the HUD alongside
|
|
1211
|
+
// where the empty body was left.
|
|
1212
|
+
const drone = player.riggedDrone;
|
|
1213
|
+
const hull = drone ? ` Hull ${drone.droneSummary()}` : '';
|
|
1214
|
+
const mount = player.droneArmed ? ' ⌖ armed' : ' (recon -- no mounts)';
|
|
1215
|
+
lines.push(`{yellow-fg}✈ JUMPED IN: ${drone?.name ?? 'drone'} ${(player.simMode ?? 'cold').toUpperCase()}-SIM${mount}${hull} -- body at ${player.bodyRoom?.name ?? '???'}{/yellow-fg}`);
|
|
1216
|
+
}
|
|
779
1217
|
this._statusBox.setContent(lines.join('\n'));
|
|
780
1218
|
this._screen.render();
|
|
781
1219
|
}
|
|
@@ -878,10 +1316,11 @@ export default class Game {
|
|
|
878
1316
|
{ title: 'gear', entries: [['inv', 'inventory'], ['equipment', 'eq'], ['take'], ['drop'], ['give'], ['store'], ['equip'], ['unequip'], ['brandish', 'draw'], ['holster', 'stow'], ['reload'], ['use'], ['read']] },
|
|
879
1317
|
{ title: 'combat', entries: [['attack', 'kill'], ['subdue'], ['stance'], ['surrender'], ['edge'], ['rest']] },
|
|
880
1318
|
{ title: 'magic', entries: [['spells'], ['cast'], ['project'], ['return'], ['assense']] },
|
|
881
|
-
{ title: 'matrix', entries: [['jack'], ['hack'], ['pan'], ['silent'], ['reboot'], ['drone']] },
|
|
882
|
-
{ title: 'social', entries: [['say'], ['talk-to'], ['call'], ['end-call', 'end', 'hangup'], ['haggle', 'negotiate'], ['persuade', 'convince']] },
|
|
1319
|
+
{ title: 'matrix', entries: [['jack'], ['hack'], ['pan'], ['silent'], ['reboot'], ['drone'], ['jump', 'rig']] },
|
|
1320
|
+
{ title: 'social', entries: [['say'], ['talk-to'], ['call'], ['end-call', 'end', 'hangup'], ['haggle', 'negotiate'], ['persuade', 'convince'], ['ask'], ['contacts']] },
|
|
1321
|
+
{ title: 'street', entries: [['browse', 'buy'], ['sell'], ['work', 'gig'], ['treat'], ['eat'], ['drink'], ['lifestyle'], ['pay']] },
|
|
883
1322
|
{ title: 'barriers', entries: [['solve'], ['pick'], ['bluff'], ['unlock'], ['lock']] },
|
|
884
|
-
{ title: 'character', entries: [['sheet', 'stats'], ['advance', 'train']] },
|
|
1323
|
+
{ title: 'character', entries: [['sheet', 'stats'], ['advance', 'train'], ['qualities', 'quality'], ['initiate', 'submerge'], ['install', 'chrome']] },
|
|
885
1324
|
{ title: 'session', entries: [['help'], ['clear'], ['quit', 'exit']] },
|
|
886
1325
|
];
|
|
887
1326
|
/** Descriptions for the verbs handled inline in handleInput() rather than registered. */
|