@maka/maka-cli 5.380.2 → 5.381.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +13 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +4 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/rep.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/store.js +7 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +22 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +44 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +3 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +45 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/reputation-ending.js +39 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.381.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|
|
@@ -20,6 +20,14 @@ export class DropCommand extends Command {
|
|
|
20
20
|
if (bill)
|
|
21
21
|
return bill;
|
|
22
22
|
const itemName = args.join(' ').toLowerCase(); // Assuming the item name may contain spaces
|
|
23
|
+
// HELD COUNTS AS CARRIED (user ruling 2026-10-04): the thing in your
|
|
24
|
+
// hand or on your back drops too -- back to the bag first, which is
|
|
25
|
+
// what the path below reads.
|
|
26
|
+
if (!this.actor.inventory.hasItem(itemName)) {
|
|
27
|
+
const worn = this.actor.findEquipped(itemName);
|
|
28
|
+
if (worn)
|
|
29
|
+
this.actor.bagEquipped(worn.name);
|
|
30
|
+
}
|
|
23
31
|
// Check if the item exists in the player's inventory
|
|
24
32
|
if (!this.actor.inventory.hasItem(itemName)) {
|
|
25
33
|
this.logger.write(`Item "${itemName}" not found in ${this.actor.name}'s inventory during 'Drop' command.`);
|
|
@@ -76,6 +76,17 @@ export class GiveCommand extends Command {
|
|
|
76
76
|
}
|
|
77
77
|
// === Try to give an item ===
|
|
78
78
|
let item = this.actor.inventory.getItem(itemOrAmount);
|
|
79
|
+
// HELD COUNTS AS CARRIED (user ruling 2026-10-04; qW2HZD5HQCX42FN29: a
|
|
80
|
+
// Johnson with the cargo case in his hand could not give it). Bagged at
|
|
81
|
+
// the handover below, not here, so a refused give leaves it held.
|
|
82
|
+
let fromSlot = false;
|
|
83
|
+
if (!item) {
|
|
84
|
+
const worn = this.actor.findEquipped(itemOrAmount);
|
|
85
|
+
if (worn) {
|
|
86
|
+
item = worn;
|
|
87
|
+
fromSlot = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
// DATA ON A DEVICE IS IN YOUR HANDS TOO (digital inventory, user ruling
|
|
80
91
|
// 2026-10-04): a file in your commlink's library is beamed across the
|
|
81
92
|
// table, no chip required. Pack first -- a chip by that name wins --
|
|
@@ -225,7 +236,8 @@ export class GiveCommand extends Command {
|
|
|
225
236
|
receiveData(recipient, item);
|
|
226
237
|
}
|
|
227
238
|
else {
|
|
228
|
-
didTransfer = this.actor.
|
|
239
|
+
didTransfer = (!fromSlot || this.actor.bagEquipped(item.name))
|
|
240
|
+
&& this.actor.inventory.transferItemTo(item.name, recipient.inventory);
|
|
229
241
|
}
|
|
230
242
|
if (!didTransfer) {
|
|
231
243
|
this.logger.write(`Item "${item.name}" is not transferable.`);
|
|
@@ -60,7 +60,7 @@ export class LockCommand extends Command {
|
|
|
60
60
|
return `${crossNote}The door leading to "${door.getOtherSideToName(room)}" is already locked.`;
|
|
61
61
|
}
|
|
62
62
|
const requiredKeyOrSolution = door.unlockItemOrSolution?.toLowerCase();
|
|
63
|
-
if (requiredKeyOrSolution && this.actor.
|
|
63
|
+
if (requiredKeyOrSolution && this.actor.carries(requiredKeyOrSolution) && this.actor.bagEquipped(requiredKeyOrSolution)) {
|
|
64
64
|
const key = this.actor.inventory.getItem(requiredKeyOrSolution);
|
|
65
65
|
if (key) {
|
|
66
66
|
door.isLocked = true;
|
|
@@ -78,7 +78,7 @@ export class LockCommand extends Command {
|
|
|
78
78
|
return `The door "${label}" is already locked.`;
|
|
79
79
|
}
|
|
80
80
|
const requiredKeyOrSolution = door.unlockItemOrSolution?.toLowerCase();
|
|
81
|
-
if (requiredKeyOrSolution && this.actor.
|
|
81
|
+
if (requiredKeyOrSolution && this.actor.carries(requiredKeyOrSolution) && this.actor.bagEquipped(requiredKeyOrSolution)) {
|
|
82
82
|
door.isLocked = true;
|
|
83
83
|
this.logger.write(`LockCommand: ${this.actor.name} locked the door: ${label}`);
|
|
84
84
|
this.actor.performAction('lock', label);
|
|
@@ -153,8 +153,10 @@ export class MoveCommand extends Command {
|
|
|
153
153
|
// was buyable and inert, the same shape as the hostility flag:
|
|
154
154
|
// wired at one end only.
|
|
155
155
|
//
|
|
156
|
-
// Carried
|
|
157
|
-
|
|
156
|
+
// Carried OR worn (user ruling 2026-10-04: an equipped item is a
|
|
157
|
+
// carried item): a kit in the pack is a kit you can rig, and a
|
|
158
|
+
// harness already on you more so.
|
|
159
|
+
const assisted = [...actor.inventory.getAllItems(), ...actor.equippedItems()].some(i => /climbing gear|climbing kit|\brope\b|harness|carabiner|crampon|grapple gun/i.test(`${i.name} ${i.description ?? ''}`));
|
|
158
160
|
// SURFACE AND CONDITION (p.134), read off the room's own prose and
|
|
159
161
|
// the level's name -- see climbSurfaceModifier. Zero unless the
|
|
160
162
|
// fiction says otherwise, which is why adding this could not
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
2
|
import { BULLET } from '../utilities/log-style.js';
|
|
3
|
+
import { reputationWarning, NOTORIETY_GAME_OVER, AWARENESS_GAME_OVER } from '../utilities/reputation-ending.js';
|
|
3
4
|
/**
|
|
4
5
|
* THE LEDGER BEHIND THE THREE NUMBERS (uEQtkJczFtDE7r3Gn: "I'd like a way
|
|
5
6
|
* to view the ledger or my 3 stats for cred, notoriety, and awareness").
|
|
@@ -49,7 +50,10 @@ export class RepCommand extends Command {
|
|
|
49
50
|
static description = 'Your reputation, itemised (SR5 p.368): Street Cred and the career karma behind it, every stain on your Notoriety, and what put your face on the feeds. The same three numbers the sheet shows, with the reasons.';
|
|
50
51
|
async execute() {
|
|
51
52
|
const a = this.actor;
|
|
52
|
-
|
|
53
|
+
const warning = reputationWarning(a);
|
|
54
|
+
return ['REPUTATION (p.368)', ...credSection(a), ...notorietySection(a), ...awarenessSection(a),
|
|
55
|
+
` Notoriety ${NOTORIETY_GAME_OVER} or Public Awareness ${AWARENESS_GAME_OVER} ends a career (house rule).`,
|
|
56
|
+
...(warning ? [` ${warning}`] : [])].join('\n');
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
//# sourceMappingURL=rep.js.map
|
|
@@ -44,7 +44,7 @@ export class SellCommand extends Command {
|
|
|
44
44
|
const pending = this.game?.pendingSale;
|
|
45
45
|
// Bare "sell": close the standing quote, or show what's fenceable.
|
|
46
46
|
if (!wanted) {
|
|
47
|
-
if (pending && pending.fence === fence.name && (pending.lot || actor.
|
|
47
|
+
if (pending && pending.fence === fence.name && (pending.lot || (actor.carries(pending.itemName) && actor.bagEquipped(pending.itemName)))) {
|
|
48
48
|
return pending.lot ? this.closeLot(fence) : this.close(fence);
|
|
49
49
|
}
|
|
50
50
|
return this.listSellables(fence);
|
|
@@ -10,6 +10,13 @@ export class StoreCommand extends Command {
|
|
|
10
10
|
}
|
|
11
11
|
store(args) {
|
|
12
12
|
const itemName = args.join(' ').toLowerCase(); // Handle multi-word item names
|
|
13
|
+
// HELD COUNTS AS CARRIED (user ruling 2026-10-04): what you hold or
|
|
14
|
+
// wear stores too -- back to the bag first, which the path below reads.
|
|
15
|
+
if (!this.actor.inventory.hasItem(itemName)) {
|
|
16
|
+
const worn = this.actor.findEquipped(itemName);
|
|
17
|
+
if (worn)
|
|
18
|
+
this.actor.bagEquipped(worn.name);
|
|
19
|
+
}
|
|
13
20
|
// Check if the item exists in the player's inventory
|
|
14
21
|
if (!this.actor.inventory.hasItem(itemName)) {
|
|
15
22
|
this.logger.write(`Item "${itemName}" not found in ${this.actor.name}'s inventory during 'Store' command.`);
|
|
@@ -69,7 +69,7 @@ export class UnlockCommand extends Command {
|
|
|
69
69
|
return reach.refusal;
|
|
70
70
|
const crossNote = reach.line ? `${reach.line}\n` : '';
|
|
71
71
|
if (holder) {
|
|
72
|
-
if (holder.keyItem && this.actor.
|
|
72
|
+
if (holder.keyItem && this.actor.carries(holder.keyItem.name) && this.actor.bagEquipped(holder.keyItem.name)) {
|
|
73
73
|
const result = holder.offer('', this.actor.inventory);
|
|
74
74
|
this.logger.write(`UnlockCommand: ${this.actor.name} turned ${holder.keyItem.name} in ${holder.name}: ${result.success}`);
|
|
75
75
|
this.actor.performAction('unlocked', result.message);
|
|
@@ -86,7 +86,7 @@ export class UnlockCommand extends Command {
|
|
|
86
86
|
return crossNote + `${holder.name} holds it. ${takes} Or force it: ${ways}.`;
|
|
87
87
|
}
|
|
88
88
|
const requiredKeyOrSolution = door.unlockItemOrSolution?.toLowerCase();
|
|
89
|
-
if (requiredKeyOrSolution && this.actor.
|
|
89
|
+
if (requiredKeyOrSolution && this.actor.carries(requiredKeyOrSolution) && this.actor.bagEquipped(requiredKeyOrSolution)) {
|
|
90
90
|
const key = this.actor.inventory.getItem(requiredKeyOrSolution);
|
|
91
91
|
if (key) {
|
|
92
92
|
const unlockMessage = room.attemptUnlockExit(direction, key.name);
|
|
@@ -249,7 +249,7 @@ export class UseCommand extends Command {
|
|
|
249
249
|
// NOTHING TYPED AFTER THE NAME. Say what this particular thing
|
|
250
250
|
// wants, which depends on what it IS.
|
|
251
251
|
if (!rest) {
|
|
252
|
-
if (device.keyItem && this.actor.
|
|
252
|
+
if (device.keyItem && this.actor.carries(device.keyItem.name) && this.actor.bagEquipped(device.keyItem.name)) {
|
|
253
253
|
// They are carrying what opens it -- that IS the offer.
|
|
254
254
|
return crossNote + this.openWith(device, '', 'use');
|
|
255
255
|
}
|
|
@@ -24,7 +24,7 @@ export class WorkCommand extends Command {
|
|
|
24
24
|
}
|
|
25
25
|
const gig = this.game.activeGig;
|
|
26
26
|
if (gig) {
|
|
27
|
-
return actor.
|
|
27
|
+
return actor.carries(gig.itemName)
|
|
28
28
|
? `You've got work: get the ${gig.itemName} to ${gig.to}. ${gig.pay} nuyen on delivery.`
|
|
29
29
|
: `You took ${gig.from}'s gig but the ${gig.itemName} isn't on you anymore -- find it, or that bridge stays burned this cycle.`;
|
|
30
30
|
}
|
|
@@ -3282,5 +3282,26 @@
|
|
|
3282
3282
|
// walked toward a runner was narrated stepping while the map, melee
|
|
3283
3283
|
// reach and ranges all read the post. A picket with an exact cell is
|
|
3284
3284
|
// now seated from it.
|
|
3285
|
-
|
|
3285
|
+
// 1.192.3 (2026-10-04): AN EQUIPPED ITEM IS A CARRIED ITEM, for every
|
|
3286
|
+
// actor (user ruling; qW2HZD5HQCX42FN29). Scene build slotted Mr.
|
|
3287
|
+
// Vellum's delivery case, the meet asked his bag for it, and the cargo
|
|
3288
|
+
// never changed hands -- his prompt never named it the runner's, and his
|
|
3289
|
+
// own "give" could not move it either. Player.carries()/bagEquipped():
|
|
3290
|
+
// the meet (give and upload cargo), the job-item prompt line, give,
|
|
3291
|
+
// drop, store, sell, keys (lock/unlock/use), the gig check and climbing
|
|
3292
|
+
// gear now see what is held or worn.
|
|
3293
|
+
// 1.193.0 (2026-10-04): NOTORIETY OR PUBLIC AWARENESS AT 5 IS GAME OVER
|
|
3294
|
+
// (rfJGmF42kdnRw5AMT; user ruling, a HOUSE RULE -- SR5 sets no such
|
|
3295
|
+
// number, p.368 / Run Faster p.202).
|
|
3296
|
+
// - Read after every landed command for every standing human
|
|
3297
|
+
// (Game.settleReputationEndings): permadeath by the astral cord's road
|
|
3298
|
+
// -- the primary's save burns, a rider's goes to onPlayerDeath. No
|
|
3299
|
+
// brink, no DocWagon: neither fixes a name.
|
|
3300
|
+
// - "rep" names the line and warns one step short of it.
|
|
3301
|
+
// - Chargen refuses a build whose qualities start it at Notoriety 5.
|
|
3302
|
+
// - p.368's quality lists are counted whole: Addiction (once, any
|
|
3303
|
+
// severity), Gremlins, Scorched, Uncouth, Uneducated and Weak Immune
|
|
3304
|
+
// System add one, Blandness takes one off. Elf Poser, Ork Poser and
|
|
3305
|
+
// Spirit Bane have no catalog row yet.
|
|
3306
|
+
export const ENGINE_VERSION = '1.193.0';
|
|
3286
3307
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -18,6 +18,7 @@ import { canonicalLifestyleTier, LIFESTYLE_TIERS, canonicalLifestyleOption, life
|
|
|
18
18
|
import { SAVE_VERSION } from '../types/save-file.js';
|
|
19
19
|
import { hubSeedRefOf } from '../utilities/hub-seed.js';
|
|
20
20
|
import { DOCWAGON_TIERS, canonicalDocwagonTier, sealWristband, newContract } from '../utilities/docwagon.js';
|
|
21
|
+
import { reputationEnding, NOTORIETY_GAME_OVER } from '../utilities/reputation-ending.js';
|
|
21
22
|
/**
|
|
22
23
|
* THE ENGINE-SIDE CHARACTER FACTORY -- one payload in, one bootable save
|
|
23
24
|
* out, so the maka-cli.com web generator and the CLI cannot produce
|
|
@@ -426,6 +427,13 @@ export function buildFromChargen(payload) {
|
|
|
426
427
|
}
|
|
427
428
|
player.addQuality(key, payload.qualityRatings?.[q.key] ?? payload.qualityRatings?.[key]);
|
|
428
429
|
}
|
|
430
|
+
// BORN ON THE LINE (utilities/reputation-ending.ts): Bad Rep's 3 and
|
|
431
|
+
// p.368's quality stains can reach 5 at creation, and the first landed
|
|
432
|
+
// command would end the career. Refused here, where every build lands,
|
|
433
|
+
// rather than handed a runner who is over before they start.
|
|
434
|
+
if (reputationEnding(player)) {
|
|
435
|
+
throw new ChargenPayloadError(`Those qualities start this runner at Notoriety ${player.notoriety} -- ${NOTORIETY_GAME_OVER} ends a career. Drop a negative one (p.368's list, or Bad Rep).`);
|
|
436
|
+
}
|
|
429
437
|
// ---- THE PICK, applied (2026-09-04). Without this the whole `choice`
|
|
430
438
|
// contract stopped at the row: the shape was declared, the resolver
|
|
431
439
|
// could read it, and nothing on any path ever put an answer in.
|
|
@@ -8,6 +8,8 @@ import { hostLabel } from './utilities/grid-names.js';
|
|
|
8
8
|
import { marksPlaced, marksOnYou } from './utilities/marks.js';
|
|
9
9
|
import { settleUnresolvedHarm } from './utilities/harm-settlement.js';
|
|
10
10
|
import { answerBrink } from './utilities/not-dead-yet.js';
|
|
11
|
+
import { reputationEnding } from './utilities/reputation-ending.js';
|
|
12
|
+
import { announceGameOver } from './utilities/game-over.js';
|
|
11
13
|
import { settleNanoware } from './utilities/implant-matrix.js';
|
|
12
14
|
import { hostInteriorPanel } from './utilities/grid-view.js';
|
|
13
15
|
import { DEFAULT_GRID_PROVIDER, PUBLIC_GRID, globalGrid } from './utilities/grids.js';
|
|
@@ -4517,7 +4519,10 @@ export default class Game {
|
|
|
4517
4519
|
// exactly the job it is for.
|
|
4518
4520
|
const wc = this.scene.getWinCondition();
|
|
4519
4521
|
const johnson = this.scene.getActor(client.name);
|
|
4520
|
-
|
|
4522
|
+
// HELD COUNTS (qW2HZD5HQCX42FN29): scene build may have put the
|
|
4523
|
+
// cargo in the client's hand -- carries() asks the slots too, and
|
|
4524
|
+
// bagEquipped() brings it back to the bag the transfer reads.
|
|
4525
|
+
if (wc?.type === 'give' && wc.item && johnson && johnson.carries(wc.item) && johnson.bagEquipped(wc.item)) {
|
|
4521
4526
|
const handed = johnson.inventory.transferItemTo(wc.item, this.player.inventory);
|
|
4522
4527
|
if (handed) {
|
|
4523
4528
|
this.scene.updateInventory(this.player.inventory);
|
|
@@ -4540,7 +4545,7 @@ export default class Game {
|
|
|
4540
4545
|
// device (data-library receiveData; a chip only for a runner who
|
|
4541
4546
|
// carries no device at all). The drop is a HOST, and the meet says
|
|
4542
4547
|
// which, on its own line for the same terse() reason as above.
|
|
4543
|
-
if (wc?.type === 'upload' && wc.item && johnson && johnson.
|
|
4548
|
+
if (wc?.type === 'upload' && wc.item && johnson && johnson.carries(wc.item) && johnson.bagEquipped(wc.item)) {
|
|
4544
4549
|
const cargo = johnson.inventory.getItem(wc.item);
|
|
4545
4550
|
johnson.inventory.removeItem(cargo.name);
|
|
4546
4551
|
const { device } = receiveData(this.player, cargo);
|
|
@@ -9854,8 +9859,45 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
9854
9859
|
// After the command has fully landed, so the game master judges the
|
|
9855
9860
|
// world the table is actually looking at.
|
|
9856
9861
|
this.gameMaster?.afterCommand();
|
|
9862
|
+
// A quit (null) has already sealed the save; nothing ends after it.
|
|
9863
|
+
if (res.text !== null)
|
|
9864
|
+
this.settleReputationEndings();
|
|
9857
9865
|
return res;
|
|
9858
9866
|
}
|
|
9867
|
+
/**
|
|
9868
|
+
* NOTORIETY OR PUBLIC AWARENESS AT 5 IS GAME OVER (utilities/
|
|
9869
|
+
* reputation-ending.ts, user ruling 2026-10-04). Every standing human
|
|
9870
|
+
* at the table is read after every landed command, so a stain from any
|
|
9871
|
+
* source -- an incident, the tab's debt roll, a quality trade, earned
|
|
9872
|
+
* cred lifting awareness -- ends the career on the beat it lands.
|
|
9873
|
+
* Permadeath, by the same road the astral cord takes (planes.ts): the
|
|
9874
|
+
* primary's death runs Scene.endGame (the save burns, or the host
|
|
9875
|
+
* takes it in a crew space); a rider's goes to onPlayerDeath while
|
|
9876
|
+
* the run goes on. Not a body on the floor, so no brink and no
|
|
9877
|
+
* DocWagon -- neither one fixes a name.
|
|
9878
|
+
*/
|
|
9879
|
+
settleReputationEndings() {
|
|
9880
|
+
const scene = this.scene;
|
|
9881
|
+
if (!scene)
|
|
9882
|
+
return;
|
|
9883
|
+
for (const p of scene.getPlayers()) {
|
|
9884
|
+
if (scene.deadPlayers.has(p.name))
|
|
9885
|
+
continue;
|
|
9886
|
+
const line = reputationEnding(p);
|
|
9887
|
+
if (!line)
|
|
9888
|
+
continue;
|
|
9889
|
+
Logger.getInstance().write(`Reputation game over: ${p.name} (Notoriety ${p.notoriety}, Public Awareness ${p.publicAwareness}).`);
|
|
9890
|
+
scene.addWorldEvent(`${p.name}'s reputation ended their career.`);
|
|
9891
|
+
scene.markPlayerDead(p);
|
|
9892
|
+
if (p === scene.getPlayer()) {
|
|
9893
|
+
void scene.endGame(line).then(() => announceGameOver(Logger.getInstance(), line, p.name));
|
|
9894
|
+
}
|
|
9895
|
+
else {
|
|
9896
|
+
this.onPlayerDeath?.(p);
|
|
9897
|
+
announceGameOver(Logger.getInstance(), line, p.name);
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9859
9901
|
async runInputAs(actorName, input) {
|
|
9860
9902
|
const actor = this.scene.getPlayerByName(actorName);
|
|
9861
9903
|
if (!actor) {
|
|
@@ -2116,7 +2116,9 @@ ${worldEventsSummary}
|
|
|
2116
2116
|
// delivery the thing in its pocket actually was, so "carry it to
|
|
2117
2117
|
// the Stitch yourself" came out as a refusal instead of a handover.
|
|
2118
2118
|
const wc = this._scene?.getWinCondition?.();
|
|
2119
|
-
|
|
2119
|
+
// Held or worn counts as carried (qW2HZD5HQCX42FN29: Vellum's case was
|
|
2120
|
+
// in his hand, so this line never told him it was the runner's cargo).
|
|
2121
|
+
const carriesPrize = !!wc?.item && this.carries(wc.item);
|
|
2120
2122
|
// THE EXTRACTION, FROM THE INSIDE (commands/bring.ts, p.336-337):
|
|
2121
2123
|
// the person the job is about is told which of the book's two shapes
|
|
2122
2124
|
// they are, and their security is told whose security it is.
|
|
@@ -670,6 +670,42 @@ export class Player extends AbstractPlayer {
|
|
|
670
670
|
}
|
|
671
671
|
return out;
|
|
672
672
|
}
|
|
673
|
+
/**
|
|
674
|
+
* AN EQUIPPED ITEM IS A CARRIED ITEM (user ruling 2026-10-04). Equipping
|
|
675
|
+
* moves an item out of the bag into a slot, so `inventory.hasItem` alone
|
|
676
|
+
* says a hand holding the thing is empty. The reported session
|
|
677
|
+
* (qW2HZD5HQCX42FN29): scene build auto-equipped Mr. Vellum's Hardshell
|
|
678
|
+
* Heirloom Case, the meet asked his BAG for the cargo, found nothing,
|
|
679
|
+
* and never handed it over -- while the runner was data-spiking the
|
|
680
|
+
* very case "smoking in his hand". Exact name, any case.
|
|
681
|
+
*/
|
|
682
|
+
carries(name) {
|
|
683
|
+
if (this._inventory.hasItem(name))
|
|
684
|
+
return true;
|
|
685
|
+
const wanted = name.toLowerCase();
|
|
686
|
+
return this.equippedItems().some(i => i.name.toLowerCase() === wanted);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Put a held or worn item (exact name) back in the bag, so the bag's
|
|
690
|
+
* transfer can move it to someone else. True when it is in the bag
|
|
691
|
+
* afterwards -- already there counts. A housed device never comes out.
|
|
692
|
+
*/
|
|
693
|
+
bagEquipped(name) {
|
|
694
|
+
if (this._inventory.hasItem(name))
|
|
695
|
+
return true;
|
|
696
|
+
const wanted = name.toLowerCase();
|
|
697
|
+
for (const [groupName, group] of Object.entries(this._equipment)) {
|
|
698
|
+
if (!group)
|
|
699
|
+
continue;
|
|
700
|
+
for (const [slotName, item] of Object.entries(group)) {
|
|
701
|
+
if (item?.name.toLowerCase() !== wanted)
|
|
702
|
+
continue;
|
|
703
|
+
this.unequipBySlot(groupName, slotName);
|
|
704
|
+
return this._inventory.hasItem(item.name);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
673
709
|
/**
|
|
674
710
|
* THE WORN OR HELD ITEM A PLAYER MEANS (user ask 2026-09-24: "I'd like
|
|
675
711
|
* to be able to look my equipped gear"). Equipping moves an item out of
|
|
@@ -2842,10 +2878,16 @@ export class Player extends AbstractPlayer {
|
|
|
2842
2878
|
}
|
|
2843
2879
|
/** The p.368 quality list, restricted to the qualities this game ships. */
|
|
2844
2880
|
get qualityNotoriety() {
|
|
2845
|
-
|
|
2881
|
+
// p.368's lists, verbatim and whole (2026-10-04: the engine counted
|
|
2882
|
+
// four of thirteen negatives and two of three positives). Elf Poser,
|
|
2883
|
+
// Ork Poser and Spirit Bane are on the book's list but have no
|
|
2884
|
+
// catalog row, so nobody can hold them yet. Addiction is one entry
|
|
2885
|
+
// whatever its severity row -- one point, not one per row.
|
|
2886
|
+
const plus = ['bad-luck', 'combat-paralysis', 'sinner-criminal', 'incompetent', 'gremlins',
|
|
2887
|
+
'scorched', 'uncouth', 'uneducated', 'weak-immune-system']
|
|
2846
2888
|
.filter(k => this.qualities.has(k)).length
|
|
2847
|
-
+ (this.qualities.
|
|
2848
|
-
const minus = ['first-impression', 'lucky'].filter(k => this.qualities.has(k)).length;
|
|
2889
|
+
+ ([...this.qualities.keys()].some(k => k.startsWith('addiction')) ? 1 : 0);
|
|
2890
|
+
const minus = ['blandness', 'first-impression', 'lucky'].filter(k => this.qualities.has(k)).length;
|
|
2849
2891
|
// BAD REP CARRIES ITS OWN NUMBER (p.79): "starts play with 3 points
|
|
2850
2892
|
// of Notoriety" -- not one entry on p.368's list, which is why it is
|
|
2851
2893
|
// added here rather than pushed into `plus`. The quality does not
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE NAME THAT ENDS A CAREER (user ruling 2026-10-04, rfJGmF42kdnRw5AMT:
|
|
3
|
+
* "If either gets to 5, it's game over").
|
|
4
|
+
*
|
|
5
|
+
* A HOUSE RULE, AND SAID SO. SR5 gives no Notoriety or Public Awareness
|
|
6
|
+
* score at which a career ends: p.368 only says high Notoriety makes
|
|
7
|
+
* work "increasingly difficult as contacts and others become less
|
|
8
|
+
* willing to help", and Run Faster p.202 reserves "it's all over for
|
|
9
|
+
* you" for killing a Johnson who paid you -- an act, not a number. The
|
|
10
|
+
* number is the table's ruling; the fiction below borrows the book's
|
|
11
|
+
* two reasons a runner stops being hireable: nobody will work with the
|
|
12
|
+
* name, or everybody knows the face.
|
|
13
|
+
*
|
|
14
|
+
* Read once per landed command (Game.settleReputationEndings), not at
|
|
15
|
+
* each of the places that raise either score -- incidents, the tab's
|
|
16
|
+
* debt roll, quality trades and earned cred all move them, and a check
|
|
17
|
+
* at each would be the family swept one site at a time.
|
|
18
|
+
*/
|
|
19
|
+
export const NOTORIETY_GAME_OVER = 5;
|
|
20
|
+
export const AWARENESS_GAME_OVER = 5;
|
|
21
|
+
/** The GAME OVER banner when this runner's reputation has ended them, else undefined. */
|
|
22
|
+
export function reputationEnding(p) {
|
|
23
|
+
if (p.notoriety >= NOTORIETY_GAME_OVER) {
|
|
24
|
+
return `💀 GAME OVER: ${p.name}'s name is poison -- Notoriety ${p.notoriety}. No fixer takes the call and no Johnson sits down across from them. The career is over.`;
|
|
25
|
+
}
|
|
26
|
+
if (p.publicAwareness >= AWARENESS_GAME_OVER) {
|
|
27
|
+
return `💀 GAME OVER: ${p.name}'s face is on every feed -- Public Awareness ${p.publicAwareness}. A runner the whole city knows is no use in the shadows. The career is over.`;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
/** The line `rep` and the sheet show under a score that is one step from the end. */
|
|
32
|
+
export function reputationWarning(p) {
|
|
33
|
+
if (p.notoriety === NOTORIETY_GAME_OVER - 1)
|
|
34
|
+
return `One more stain and the name is finished (Notoriety ${NOTORIETY_GAME_OVER} ends a career).`;
|
|
35
|
+
if (p.publicAwareness === AWARENESS_GAME_OVER - 1)
|
|
36
|
+
return `One more headline and the face is finished (Public Awareness ${AWARENESS_GAME_OVER} ends a career).`;
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=reputation-ending.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.381.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|