@maka/maka-cli 5.51.0 → 5.53.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.
Files changed (91) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/augmentations.d.ts +61 -0
  3. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js +222 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js.map +1 -1
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +4 -0
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +35 -0
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +32 -2
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +11 -0
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +183 -13
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/catalog.js +39 -11
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/catalog.js.map +1 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/docwagon.js +4 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/docwagon.js.map +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +18 -2
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +2 -2
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js +45 -9
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.d.ts +0 -6
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +61 -4
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/note.d.ts +20 -5
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/note.js +34 -10
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/note.js.map +1 -1
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +2 -2
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +17 -5
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.d.ts +21 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js +94 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js.map +1 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +52 -30
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +1 -1
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +0 -8
  51. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +41 -0
  52. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +9 -0
  54. package/bundle/typescript/src/commands/game/sideQuest/game.js +26 -5
  55. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +20 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +95 -6
  58. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  59. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +24 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +63 -15
  61. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  62. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +18 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +7 -1
  65. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.d.ts +2 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js +9 -0
  67. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js.map +1 -1
  68. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.d.ts +14 -4
  69. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js +239 -87
  70. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js.map +1 -1
  71. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js +19 -5
  72. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js.map +1 -1
  73. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js +1 -0
  74. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +14 -4
  76. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +18 -0
  78. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +23 -0
  79. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  80. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.d.ts +9 -0
  81. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +11 -0
  82. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js.map +1 -1
  83. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +4 -2
  84. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js.map +1 -1
  85. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.d.ts +13 -0
  86. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js +19 -2
  87. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js.map +1 -1
  88. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +20 -0
  89. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +141 -5
  90. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  91. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * THE STREET CATALOG -- what the swag network can source (see
3
3
  * commands/catalog.ts and commands/source.ts). Harvested from the SR5e
4
- * gear tables via the rules archive: rows with a `page` are cited
5
- * straight from the book; rows marked `approx` carry canon-approximate
6
- * figures the excerpts couldn't confirm.
4
+ * gear tables: rows with a `page` are cited straight from the book
5
+ * (price, availability, legality, and the canon stat block), rows
6
+ * marked `approx` carry canon-approximate figures the book doesn't
7
+ * price.
7
8
  *
8
9
  * SOURCE OF TRUTH: maka-cli.com's GameCatalog collection (player
9
10
  * direction: the site is the game's backend). This file is the SEED and
@@ -11,8 +12,15 @@
11
12
  * (maka-cli-com-2: app/imports/api/v1/game-catalog/game-catalog-seed.ts;
12
13
  * keep the two in sync, bump CATALOG_VERSION on any change), and the
13
14
  * CLI overrides this list with the server's whenever it can reach it.
15
+ *
16
+ * STATS ARE REFERENCE, NOT ENGINE INPUT: `stats` is the book's own line
17
+ * for the row (Acc/DV/AP/mode/ammo, armor rating, deck array...). The
18
+ * engine keeps deriving its own compressed combat numbers in
19
+ * models/item.ts so that generated street gear behaves like catalog
20
+ * gear -- these strings are what the player reads, and what an NPC's
21
+ * swag briefing quotes.
14
22
  */
15
- export const CATALOG_VERSION = 4;
23
+ export const CATALOG_VERSION = 6;
16
24
  const row = (slug, name, category, price, availability, description, extra = {}) => ({
17
25
  slug, name, category, price, availability, description,
18
26
  rating: extra.rating ?? (price >= 5000 ? 'Rare' : price >= 500 ? 'Standard' : 'Common'),
@@ -25,93 +33,237 @@ const row = (slug, name, category, price, availability, description, extra = {})
25
33
  });
26
34
  export const CATALOG_ITEMS = [
27
35
  // ---- Melee (p.423) ----
28
- row('combat-knife', 'Combat Knife', 'MeleeWeapon', 300, 4, 'Mil-spec steel that holds an edge through anything.', { page: 'p.423', shape: 'blade' }),
29
- row('sword', 'Sword', 'MeleeWeapon', 500, 5, 'A proper length of sharpened argument.', { page: 'p.423', shape: 'blade', size: 'Medium', weight: 1.5 }),
30
- row('katana', 'Katana', 'MeleeWeapon', 1000, 9, 'Folded steel and folded history. Restricted, naturally.', { page: 'p.423', shape: 'blade', size: 'Medium', weight: 1.2, rating: 'Rare' }),
31
- row('club', 'Club', 'MeleeWeapon', 30, 2, 'The oldest tool in the world, still in production.', { page: 'p.423', shape: 'club', size: 'Medium', weight: 2 }),
32
- row('stun-baton', 'Stun Baton', 'MeleeWeapon', 750, 6, 'Collapsible shock stick -- the polite end of crowd control.', { page: 'p.423', shape: 'baton' }),
33
- row('shock-gloves', 'Shock Gloves', 'MeleeWeapon', 550, 6, 'Capacitor plates across the knuckles. A crackling non-lethal argument.', { approx: true, shape: 'gloves', weight: 0.5, texture: 'insulated weave' }),
34
- row('staff', 'Staff', 'MeleeWeapon', 100, 3, 'Two meters of reach and no questions at checkpoints.', { approx: true, shape: 'staff', size: 'Large', weight: 2 }),
35
- // ---- Sidearms & long guns (p.426, p.428) ----
36
- row('colt-america-l36', 'Colt America L36', 'RangedWeapon', 320, 4, 'A light pistol as common as rain and twice as honest.', { page: 'p.426', shape: 'pistol' }),
37
- row('ares-predator-v', 'Ares Predator V', 'RangedWeapon', 725, 5, 'THE heavy pistol. Smartgun system built in, reputation included.', { page: 'p.426', shape: 'pistol', weight: 1.2 }),
38
- row('ruger-super-warhawk', 'Ruger Super Warhawk', 'RangedWeapon', 400, 4, 'A revolver with opinions. Loud ones.', { page: 'p.426', shape: 'revolver', weight: 1.4 }),
39
- row('remington-roomsweeper', 'Remington Roomsweeper', 'RangedWeapon', 250, 6, 'A heavy pistol that thinks it is a shotgun. It is not wrong.', { page: 'p.426', shape: 'pistol', weight: 1.5 }),
40
- row('ares-alpha', 'Ares Alpha', 'RangedWeapon', 2650, 11, 'The assault rifle with the grenade launcher. Forbidden, and worth it.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4.5, rating: 'MilitaryGrade' }),
41
- row('ak-97', 'AK-97', 'RangedWeapon', 950, 8, 'A century of design maturity: it always, always fires.', { approx: true, shape: 'rifle', size: 'Large', weight: 4 }),
42
- row('defiance-t-250', 'Defiance T-250', 'RangedWeapon', 450, 4, 'A pump shotgun with all the subtlety the name implies.', { approx: true, shape: 'shotgun', size: 'Large', weight: 3.5 }),
43
- row('ares-light-fire-75', 'Ares Light Fire 75', 'RangedWeapon', 1250, 6, 'A whisper-quiet light pistol built around its silencer.', { approx: true, shape: 'pistol' }),
44
- // ---- Ammunition ----
45
- row('regular-ammo', 'Regular Ammo (box)', 'Ammunition', 20, 2, 'Ten clean rounds, serial numbers politely absent.', { approx: true, shape: 'box', size: 'Tiny', weight: 0.5, color: 'brass', texture: 'metal' }),
46
- row('stick-n-shock', 'Stick-n-Shock Rounds', 'Ammunition', 80, 6, 'Taser slugs -- for when you want them down, not gone.', { approx: true, shape: 'box', size: 'Tiny', weight: 0.5 }),
47
- row('apds', 'APDS Rounds', 'Ammunition', 120, 12, 'Armor-piercing discarding sabot. Very forbidden. Very effective.', { approx: true, shape: 'box', size: 'Tiny', weight: 0.5, rating: 'MilitaryGrade' }),
48
- // ---- Armor (p.436) ----
49
- row('armor-jacket', 'Armor Jacket', 'BodyArmor', 1000, 2, 'The runner standard: looks like a jacket, works like a wall.', { page: 'p.436', shape: 'jacket', size: 'Medium', weight: 3, texture: 'ballistic weave' }),
50
- row('chameleon-suit', 'Chameleon Suit', 'ArmorClothing', 1700, 10, 'Ruthenium weave that drinks the light around it.', { page: 'p.436', shape: 'suit', size: 'Medium', weight: 2, color: 'shifting gray', rating: 'Rare' }),
51
- row('urban-explorer', 'Urban Explorer Jumpsuit', 'ArmorClothing', 650, 8, 'Armored athletic wear for the parkour-inclined professional.', { page: 'p.436', shape: 'jumpsuit', size: 'Medium', weight: 2 }),
52
- row('armor-vest', 'Armor Vest', 'BodyArmor', 500, 4, 'Concealable ballistic weave. The difference between a bad night and a last one.', { approx: true, shape: 'vest', size: 'Medium', weight: 3 }),
53
- row('lined-coat', 'Lined Coat', 'BodyArmor', 900, 4, 'Duster-length armor with room for everything underneath.', { approx: true, shape: 'coat', size: 'Medium', weight: 3 }),
54
- row('armor-clothing', 'Armor Clothing', 'ArmorClothing', 450, 2, 'Boardroom-passable protection.', { approx: true, shape: 'clothing', size: 'Medium', weight: 2 }),
55
- row('helmet', 'Helmet', 'Helmet', 100, 2, 'A hard shell for the soft parts that matter most.', { approx: true, shape: 'helmet', weight: 1 }),
56
- row('ballistic-shield', 'Ballistic Shield', 'Shield', 1200, 12, 'A door you carry into rooms that need one.', { approx: true, shape: 'shield', size: 'Large', weight: 7, rating: 'Rare' }),
57
- // ---- Commlinks & decks (p.439) ----
58
- row('meta-link', 'Meta Link', 'Commlink', 100, 2, 'The squatter special. It makes calls. Usually.', { page: 'p.439', shape: 'slab', size: 'Tiny', weight: 0.2 }),
59
- row('sony-emperor', 'Sony Emperor', 'Commlink', 700, 4, 'A solid mid-shelf commlink with a name bigger than its specs.', { approx: true, shape: 'slab', size: 'Tiny', weight: 0.2 }),
60
- row('renraku-sensei', 'Renraku Sensei', 'Commlink', 1000, 6, 'A businessman\'s link: discreet, capable, forgettable on purpose.', { page: 'p.439', shape: 'slab', size: 'Tiny', weight: 0.2 }),
61
- row('hermes-ikon', 'Hermes Ikon', 'Commlink', 3000, 10, 'Serious hardware for people whose calls matter.', { page: 'p.439', shape: 'slab', size: 'Tiny', weight: 0.3, rating: 'Rare' }),
62
- row('fairlight-caliban', 'Fairlight Caliban', 'Commlink', 8000, 14, 'The commlink executives get assassinated for.', { page: 'p.439', shape: 'slab', size: 'Tiny', weight: 0.3, rating: 'Legendary' }),
63
- // Decks (p.439/p.227) -- the aspirational end of the catalog: a
36
+ row('combat-knife', 'Combat Knife', 'MeleeWeapon', 300, 4, 'Mil-spec steel that holds an edge through anything.', { page: 'p.423', shape: 'blade', stats: 'Acc 6, DV (STR+2)P, AP -3' }),
37
+ row('sword', 'Sword', 'MeleeWeapon', 500, 5, 'A proper length of sharpened argument.', { page: 'p.423', shape: 'blade', size: 'Medium', weight: 1.5, legality: 'R', stats: 'Acc 6, Reach 1, DV (STR+3)P, AP -2' }),
38
+ row('katana', 'Katana', 'MeleeWeapon', 1000, 9, 'Folded steel and folded history. Restricted, naturally.', { page: 'p.423', shape: 'blade', size: 'Medium', weight: 1.2, rating: 'Rare', legality: 'R', stats: 'Acc 7, Reach 1, DV (STR+3)P, AP -3' }),
39
+ row('pole-arm', 'Pole Arm', 'MeleeWeapon', 1000, 6, 'Three meters of reach and a very old idea about crowd control.', { page: 'p.423', shape: 'polearm', size: 'Large', weight: 3, rating: 'Standard', legality: 'R', stats: 'Acc 5, Reach 3, DV (STR+3)P, AP -2' }),
40
+ row('club', 'Club', 'MeleeWeapon', 30, 0, 'The oldest tool in the world, still in production.', { page: 'p.423', shape: 'club', size: 'Medium', weight: 2, stats: 'Acc 4, Reach 1, DV (STR+3)P, AP --' }),
41
+ row('sap', 'Sap', 'MeleeWeapon', 30, 2, 'Lead shot in a leather sock. Quiet, deniable, effective.', { page: 'p.423', shape: 'sap', size: 'Tiny', weight: 0.5, texture: 'leather', stats: 'Acc 5, DV (STR+2)P, AP --' }),
42
+ row('stun-baton', 'Stun Baton', 'MeleeWeapon', 750, 6, 'Collapsible shock stick -- the polite end of crowd control.', { page: 'p.423', shape: 'baton', legality: 'R', stats: 'Acc 4, Reach 1, DV 9S(e), AP -5' }),
43
+ row('shock-gloves', 'Shock Gloves', 'MeleeWeapon', 550, 6, 'Capacitor plates across the knuckles. A crackling non-lethal argument.', { page: 'p.423', shape: 'gloves', weight: 0.5, texture: 'insulated weave', legality: 'R', stats: 'DV 8S(e), AP -5 (Unarmed Combat)' }),
44
+ row('monofilament-whip', 'Monofilament Whip', 'MeleeWeapon', 10000, 12, 'A thread that cuts bone like warm butter -- yours included, on a bad roll.', { page: 'p.423', shape: 'whip', size: 'Tiny', weight: 0.5, rating: 'Legendary', legality: 'F', stats: 'Acc 5 (7), Reach 2, DV 12P, AP -8 (Exotic Melee)' }),
45
+ row('staff', 'Staff', 'MeleeWeapon', 100, 3, 'Two meters of reach and no questions at checkpoints.', { approx: true, shape: 'staff', size: 'Large', weight: 2, stats: 'Acc 4, Reach 2, DV (STR+3)P, AP --' }),
46
+ // ---- Projectile (p.424) ----
47
+ row('medium-crossbow', 'Medium Crossbow', 'RangedWeapon', 500, 4, 'Silent, reusable, and legal in more places than it should be.', { page: 'p.424', shape: 'crossbow', size: 'Medium', weight: 3, legality: 'R', stats: 'Acc 6, DV 7P, AP -2, 4 (m)' }),
48
+ row('throwing-knives', 'Throwing Knives (set)', 'MeleeWeapon', 25, 4, 'Balanced steel that solves problems across a room.', { page: 'p.424', shape: 'blades', size: 'Tiny', weight: 0.4, legality: 'R', stats: 'DV (STR+1)P, AP -1 (Throwing Weapons)' }),
49
+ // ---- Tasers & hold-outs (p.426) ----
50
+ row('yamaha-pulsar', 'Yamaha Pulsar', 'RangedWeapon', 180, 0, 'A taser that ends arguments without ending anyone.', { page: 'p.426', shape: 'taser', weight: 0.7, stats: 'Acc 5, DV 7S(e), AP -5, SA, 4 (m)' }),
51
+ row('walther-palm-pistol', 'Walther Palm Pistol', 'RangedWeapon', 180, 4, 'Two shots that fit in a closed fist. Ask no more of it.', { page: 'p.426', shape: 'holdout', size: 'Tiny', weight: 0.4, legality: 'R', stats: 'Acc 4, DV 7P, AP --, SS/BF, 2 (b)' }),
52
+ // ---- Sidearms & long guns (p.426-p.430) ----
53
+ row('ares-light-fire-70', 'Ares Light Fire 70', 'RangedWeapon', 200, 3, 'The quiet one: a light pistol built around the silencer everyone bolts onto it.', { page: 'p.426', shape: 'pistol', legality: 'R', stats: 'Acc 7, DV 6P, AP --, SA, 16 (c)' }),
54
+ row('beretta-201t', 'Beretta 201T', 'RangedWeapon', 210, 7, 'A burst-capable light pistol with a folding trigger and a temper.', { page: 'p.426', shape: 'pistol', legality: 'R', stats: 'Acc 6, DV 6P, AP --, SA/BF, RC (1), 21 (c)' }),
55
+ row('colt-america-l36', 'Colt America L36', 'RangedWeapon', 320, 4, 'A light pistol as common as rain and twice as honest.', { page: 'p.426', shape: 'pistol', legality: 'R', stats: 'Acc 7, DV 7P, AP --, SA, 11 (c)' }),
56
+ row('taurus-omni-6', 'Taurus Omni-6', 'RangedWeapon', 300, 3, 'A revolver that swaps cylinders: light rounds or heavy, your night, your call.', { page: 'p.426', shape: 'revolver', weight: 1.2, legality: 'R', stats: 'Acc 5 (6), DV 6P/7P, AP 0/-1, SA/SS, 6 (cy)' }),
57
+ row('ares-predator-v', 'Ares Predator V', 'RangedWeapon', 725, 5, 'THE heavy pistol. Smartgun system built in, reputation included.', { page: 'p.426', shape: 'pistol', weight: 1.2, legality: 'R', stats: 'Acc 5 (7), DV 8P, AP -1, SA, 15 (c)' }),
58
+ row('browning-ultra-power', 'Browning Ultra-Power', 'RangedWeapon', 640, 4, 'A working professional\'s heavy pistol, with an integral laser sight.', { page: 'p.426', shape: 'pistol', weight: 1.2, legality: 'R', stats: 'Acc 5 (6), DV 8P, AP -1, SA, 10 (c)' }),
59
+ row('ruger-super-warhawk', 'Ruger Super Warhawk', 'RangedWeapon', 400, 4, 'A revolver with opinions. Loud ones.', { page: 'p.426', shape: 'revolver', weight: 1.4, legality: 'R', stats: 'Acc 5, DV 9P, AP -2, SS, 6 (cy)' }),
60
+ row('remington-roomsweeper', 'Remington Roomsweeper', 'RangedWeapon', 250, 6, 'A heavy pistol that thinks it is a shotgun. It is not wrong.', { page: 'p.426', shape: 'pistol', weight: 1.5, legality: 'R', stats: 'Acc 4, DV 7P, AP -1, SA, 8 (m)' }),
61
+ row('ceska-black-scorpion', 'Ceska Black Scorpion', 'RangedWeapon', 270, 6, 'A machine pistol with a folding stock and no manners at all.', { page: 'p.427', shape: 'machine pistol', weight: 1.4, legality: 'R', stats: 'Acc 5, DV 6P, AP --, SA/BF, RC (1), 35 (c)' }),
62
+ row('uzi-iv', 'Uzi IV', 'RangedWeapon', 450, 4, 'An old name that still writes checks in three-round bursts.', { page: 'p.427', shape: 'smg', size: 'Medium', weight: 2.5, legality: 'R', stats: 'Acc 4 (5), DV 7P, AP --, BF, RC (1), 24 (c)' }),
63
+ row('ingram-smartgun-x', 'Ingram Smartgun X', 'RangedWeapon', 800, 6, 'The runner\'s SMG: smartlink, gas vent, and a silencer already fitted.', { page: 'p.427', shape: 'smg', size: 'Medium', weight: 2.5, legality: 'R', stats: 'Acc 4 (6), DV 8P, AP --, BF/FA, RC 2, 32 (c)' }),
64
+ row('fn-p93-praetor', 'FN P93 Praetor', 'RangedWeapon', 900, 11, 'A bullpup SMG built for corp security. Forbidden, and it shows.', { page: 'p.427', shape: 'smg', size: 'Medium', weight: 3, legality: 'F', stats: 'Acc 6, DV 8P, AP --, SA/BF/FA, RC 1 (2), 50 (c)' }),
65
+ row('ak-97', 'AK-97', 'RangedWeapon', 950, 4, 'A century of design maturity: it always, always fires.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4, legality: 'R', stats: 'Acc 5, DV 10P, AP -2, SA/BF/FA, 38 (c)' }),
66
+ row('colt-m23', 'Colt M23', 'RangedWeapon', 550, 4, 'Cheap, mass-produced, no frills -- the gangbanger\'s rifle and the gunsmith\'s canvas.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4, legality: 'R', stats: 'Acc 4, DV 9P, AP -2, SA/BF/FA, 40 (c)' }),
67
+ row('fn-har', 'FN HAR', 'RangedWeapon', 1500, 8, 'Corporate threat-response hardware: laser sight, gas-vent 2, no apologies.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4.2, rating: 'Standard', legality: 'R', stats: 'Acc 5 (6), DV 10P, AP -2, SA/BF/FA, RC 2, 35 (c)' }),
68
+ row('ares-alpha', 'Ares Alpha', 'RangedWeapon', 2650, 11, 'The assault rifle with the grenade launcher. Forbidden, and worth it.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4.5, rating: 'MilitaryGrade', legality: 'F', stats: 'Acc 5 (7), DV 11P, AP -2, SA/BF/FA, RC 2, 42 (c) + grenade launcher' }),
69
+ row('yamaha-raiden', 'Yamaha Raiden', 'RangedWeapon', 2600, 14, 'Electronic firing, state of the art, and utterly illegal to be caught with.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4.5, rating: 'MilitaryGrade', legality: 'F', stats: 'Acc 6 (8), DV 11P, AP -2, BF/FA, RC 1, 60 (c)' }),
70
+ row('ruger-100', 'Ruger 100', 'RangedWeapon', 1300, 4, 'A hunting rifle that does not care what you are hunting.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 4.5, rating: 'Standard', legality: 'R', stats: 'Acc 6, DV 11P, AP -3, SA, RC (1), 8 (m)' }),
71
+ row('ares-desert-strike', 'Ares Desert Strike', 'RangedWeapon', 17500, 10, 'A sniper rifle built to work after a week in the sand. Forbidden everywhere.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 6, rating: 'Legendary', legality: 'F', stats: 'Acc 7, DV 13P, AP -4, SA, RC (1), 14 (c)' }),
72
+ row('ranger-arms-sm-5', 'Ranger Arms SM-5', 'RangedWeapon', 28000, 16, 'The assassin\'s rifle: silenced, scoped, and it breaks down into a briefcase.', { page: 'p.428', shape: 'rifle', size: 'Large', weight: 6, rating: 'Legendary', legality: 'F', stats: 'Acc 8, DV 14P, AP -5, SA, RC (1), 15 (c)' }),
73
+ row('defiance-t-250', 'Defiance T-250', 'RangedWeapon', 450, 4, 'A pump shotgun with all the subtlety the name implies.', { page: 'p.429', shape: 'shotgun', size: 'Large', weight: 3.5, legality: 'R', stats: 'Acc 4, DV 10P, AP -1, SS/SA, 5 (m)' }),
74
+ row('enfield-as-7', 'Enfield AS-7', 'RangedWeapon', 1100, 12, 'A military assault shotgun. Universally feared, for excellent reasons.', { page: 'p.429', shape: 'shotgun', size: 'Large', weight: 4.5, rating: 'Standard', legality: 'F', stats: 'Acc 4 (5), DV 13P, AP -1, SA/BF, 10 (c) or 24 (d)' }),
75
+ row('stoner-ares-m202', 'Stoner-Ares M202', 'RangedWeapon', 7000, 12, 'A medium machine gun. If you are buying this, the plan already failed.', { page: 'p.430', shape: 'machine gun', size: 'Large', weight: 9, rating: 'MilitaryGrade', legality: 'F', stats: 'Acc 5, DV 10P, AP -3, FA, 50 (c) or 100 (belt)' }),
76
+ // ---- Firearm accessories (p.432) ----
77
+ row('silencer', 'Silencer/Suppressor', 'Electronics', 500, 9, 'The difference between a gunshot and a rumor.', { page: 'p.432', shape: 'tube', size: 'Tiny', weight: 0.4, legality: 'F', stats: 'Barrel mount' }),
78
+ row('laser-sight', 'Laser Sight', 'Electronics', 125, 2, 'A red dot that argues with your shaking hands.', { page: 'p.432', shape: 'sight', size: 'Tiny', weight: 0.2, stats: 'Top or under mount, +1 Accuracy' }),
79
+ row('quick-draw-holster', 'Quick-Draw Holster', 'Electronics', 175, 4, 'Spring-loaded rig for people whose first shot has to be first.', { page: 'p.432', shape: 'holster', size: 'Tiny', weight: 0.3, texture: 'synthleather' }),
80
+ row('hidden-arm-slide', 'Hidden Arm Slide', 'Electronics', 350, 4, 'A sleeve rig that puts a pistol in your palm mid-handshake.', { page: 'p.432', shape: 'slide', size: 'Tiny', weight: 0.3, legality: 'R' }),
81
+ row('spare-clip', 'Spare Clip', 'Ammunition', 5, 4, 'Because reloading from a box is how people die.', { page: 'p.432', shape: 'clip', size: 'Tiny', weight: 0.2, color: 'gunmetal' }),
82
+ // ---- Ammunition, per 10 rounds (p.433) ----
83
+ row('regular-ammo', 'Regular Ammo (box)', 'Ammunition', 20, 2, 'Ten clean rounds, serial numbers politely absent.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, color: 'brass', texture: 'metal', legality: 'R', stats: 'DV --, AP --' }),
84
+ row('gel-rounds', 'Gel Rounds', 'Ammunition', 25, 2, 'Riot-control jelly slugs: they drop people who have to wake up later.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'R', stats: 'DV +0S, AP +1, -2 target Physical limit vs knockdown' }),
85
+ row('tracer-rounds', 'Tracer Rounds', 'Ammunition', 60, 6, 'Burning tails that walk your fire onto the target -- and back to you.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'R', stats: 'DV --, AP --, +1 die on FA bursts' }),
86
+ row('flechette-rounds', 'Flechette Rounds', 'Ammunition', 65, 6, 'A cloud of steel needles. Murder on flesh, useless on plate.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'R', stats: 'DV +2, AP +5' }),
87
+ row('hollow-points', 'Hollow Point Rounds', 'Ammunition', 70, 4, 'Rounds that open on impact. Forbidden for the obvious reason.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'F', stats: 'DV +1, AP +2' }),
88
+ row('injection-darts', 'Injection Darts', 'Ammunition', 75, 4, 'Delivery system in search of a payload. Bring your own chemistry.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.4, legality: 'R', stats: 'Payload: one dose, injection vector' }),
89
+ row('stick-n-shock', 'Stick-n-Shock Rounds', 'Ammunition', 80, 6, 'Taser slugs -- for when you want them down, not gone.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'R', stats: 'DV -2S(e), AP -5' }),
90
+ row('explosive-rounds', 'Explosive Rounds', 'Ammunition', 80, 9, 'Shaped charges in a casing. They misfire on a critical glitch. Enjoy.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, legality: 'F', stats: 'DV +1, AP -1' }),
91
+ row('apds', 'APDS Rounds', 'Ammunition', 120, 12, 'Armor-piercing discarding sabot. Very forbidden. Very effective.', { page: 'p.433', shape: 'box', size: 'Tiny', weight: 0.5, rating: 'MilitaryGrade', legality: 'F', stats: 'DV --, AP -4' }),
92
+ // ---- Grenades (p.435) ----
93
+ row('smoke-grenade', 'Smoke Grenade', 'Explosive', 40, 4, 'Portable weather for tactical withdrawals.', { page: 'p.435', shape: 'grenade', size: 'Tiny', weight: 0.3, legality: 'R', stats: '10m radius smoke' }),
94
+ row('thermal-smoke-grenade', 'Thermal Smoke Grenade', 'Explosive', 60, 6, 'Smoke that blinds thermographic eyes too. Worth the extra twenty.', { page: 'p.435', shape: 'grenade', size: 'Tiny', weight: 0.3, legality: 'R', stats: '10m radius, blocks thermographic' }),
95
+ row('flash-bang', 'Flash-Bang Grenade', 'Explosive', 100, 6, 'A very loud argument for leaving.', { page: 'p.435', shape: 'grenade', size: 'Tiny', weight: 0.3, legality: 'R', stats: 'DV 10S, AP -4, 10m radius' }),
96
+ row('flash-pak', 'Flash-Pak', 'Explosive', 125, 4, 'A strobe that ruins low-light and thermo optics without hurting a soul.', { page: 'p.435', shape: 'strobe', size: 'Tiny', weight: 0.3, stats: 'Special: blinds vision enhancements' }),
97
+ row('frag-grenade', 'Fragmentation Grenade', 'Explosive', 100, 11, 'Steel rain in a can. Nothing subtle survives it, including you.', { page: 'p.435', shape: 'grenade', size: 'Tiny', weight: 0.4, legality: 'F', stats: 'DV 18P(f), AP +5, -1/m blast' }),
98
+ row('he-grenade', 'High-Explosive Grenade', 'Explosive', 100, 11, 'The one that takes the wall with it.', { page: 'p.435', shape: 'grenade', size: 'Tiny', weight: 0.4, legality: 'F', stats: 'DV 16P, AP -2, -2/m blast' }),
99
+ row('plastic-explosive', 'Plastic Explosive (Rating 10)', 'Explosive', 1000, 16, 'Grey putty that opens doors, vaults, and criminal careers.', { page: 'p.436', shape: 'brick', size: 'Tiny', weight: 1, rating: 'Rare', legality: 'F', stats: 'Rating 10, DV by rating' }),
100
+ row('detonator-cap', 'Detonator Cap', 'Explosive', 75, 8, 'The small piece that makes the big piece matter.', { page: 'p.436', shape: 'cap', size: 'Tiny', weight: 0.1, legality: 'R' }),
101
+ // ---- Clothing (p.436) -- what fills the torso's `clothing` slot and
102
+ // the mundane-wear slots around it. Equipping keys on CATEGORY alone
103
+ // (Player.determineEquipmentSlot), so a jacket filed under Electronics
104
+ // is a jacket nobody can put on. The book prices mundane wear as ONE
105
+ // line -- Clothing, 20-100,000 nuyen, no availability test -- so the
106
+ // specific street pieces below cite p.436 and carry `approx` for the
107
+ // price point chosen within that range. None of them add soak: boots,
108
+ // gloves, and limb trim are deliberately 0 armor (getArmorValue).
109
+ row('street-clothing', 'Street Clothing', 'Clothing', 50, 0, 'Layers the sprawl stops looking at. The most useful thing you can wear.', { page: 'p.436', shape: 'clothing', size: 'Medium', weight: 1, color: 'faded black', texture: 'synthweave', stats: 'Armor 0' }),
110
+ row('synthleather-jacket', 'Synthleather Jacket', 'ArmorClothing', 200, 0, 'A jacket with enough hide in the weave to argue with a knife.', { page: 'p.436', shape: 'jacket', size: 'Medium', weight: 1.5, color: 'oxblood', texture: 'synthleather', stats: 'Armor 4' }),
111
+ row('electrochromic-clothing', 'Electrochromic Clothing', 'Clothing', 500, 2, 'Colour and pattern on command -- a different person leaves than the one who walked in.', { page: 'p.436', shape: 'clothing', size: 'Medium', weight: 1, color: 'whatever you tell it', rating: 'Standard', stats: 'Clothing modification, +2 avail' }),
112
+ row('feedback-clothing', 'Feedback Clothing', 'Clothing', 500, 8, 'Haptic weave that lets AR touch you back. Riggers and simsense junkies swear by it.', { page: 'p.436', shape: 'clothing', size: 'Medium', weight: 1, rating: 'Standard', stats: 'Clothing modification' }),
113
+ row('synthleather-boots', 'Synthleather Boots', 'Boots', 100, 0, 'Resoled twice, waterproof on the third try. They will outlast the job.', { page: 'p.436', approx: true, shape: 'boots', weight: 1, color: 'brown-black', texture: 'synthleather' }),
114
+ row('combat-boots', 'Combat Boots', 'Boots', 250, 4, 'Steel toes, ankle support, and tread that holds on wet rooftop gravel.', { page: 'p.436', approx: true, shape: 'boots', weight: 1.5, texture: 'ballistic nylon' }),
115
+ row('fingerless-gloves', 'Fingerless Gloves', 'Gloves', 40, 0, 'Grip where you need it, bare skin where the sensors want it.', { page: 'p.436', approx: true, shape: 'gloves', size: 'Tiny', weight: 0.2, texture: 'synthleather' }),
116
+ row('knit-cap', 'Knit Cap', 'Hat', 25, 0, 'Pulled low, it takes half your face out of every camera in the district.', { page: 'p.436', approx: true, shape: 'cap', size: 'Tiny', weight: 0.2, color: 'charcoal', texture: 'wool' }),
117
+ row('armored-pants', 'Armored Pants', 'LegArmor', 300, 4, 'Reinforced at the knee and thigh for people who take cover the hard way.', { page: 'p.436', approx: true, shape: 'pants', size: 'Medium', weight: 1.5, texture: 'ballistic weave' }),
118
+ row('forearm-guards', 'Forearm Guards', 'ArmArmor', 200, 4, 'Bracers under the sleeves -- what you raise when the blade is already moving.', { page: 'p.436', approx: true, shape: 'bracers', size: 'Small', weight: 1 }),
119
+ row('smartwatch', 'Smartwatch', 'Wristband', 200, 0, 'Wrist-worn commlink peripheral: time, vitals, and a very small screen of bad news.', { approx: true, shape: 'watch', size: 'Tiny', weight: 0.1, stats: 'Device Rating 2' }),
120
+ // ---- Armor (p.436-p.438) ----
121
+ row('armor-clothing', 'Armor Clothing', 'ArmorClothing', 450, 2, 'Boardroom-passable protection.', { page: 'p.436', shape: 'clothing', size: 'Medium', weight: 2, stats: 'Armor 6' }),
122
+ row('armor-vest', 'Armor Vest', 'BodyArmor', 500, 4, 'Concealable ballistic weave. The difference between a bad night and a last one.', { page: 'p.437', shape: 'vest', size: 'Medium', weight: 3, stats: 'Armor 9' }),
123
+ row('urban-explorer', 'Urban Explorer Jumpsuit', 'ArmorClothing', 650, 8, 'Armored athletic wear for the parkour-inclined professional.', { page: 'p.436', shape: 'jumpsuit', size: 'Medium', weight: 2, stats: 'Armor 9' }),
124
+ row('lined-coat', 'Lined Coat', 'BodyArmor', 900, 4, 'Duster-length armor with room for everything underneath.', { page: 'p.437', shape: 'coat', size: 'Medium', weight: 3, stats: 'Armor 9, -2 Concealability for what is under it' }),
125
+ row('armor-jacket', 'Armor Jacket', 'BodyArmor', 1000, 2, 'The runner standard: looks like a jacket, works like a wall.', { page: 'p.436', shape: 'jacket', size: 'Medium', weight: 3, texture: 'ballistic weave', stats: 'Armor 12' }),
126
+ row('actioneer-business-clothes', 'Actioneer Business Clothes', 'ArmorClothing', 1500, 8, 'A power suit with a concealable holster and a lawyer\'s alibi.', { page: 'p.437', shape: 'suit', size: 'Medium', weight: 2, color: 'charcoal', rating: 'Standard', stats: 'Armor 8, concealable holster' }),
127
+ row('chameleon-suit', 'Chameleon Suit', 'ArmorClothing', 1700, 10, 'Ruthenium weave that drinks the light around it.', { page: 'p.436', shape: 'suit', size: 'Medium', weight: 2, color: 'shifting gray', rating: 'Rare', legality: 'R', stats: 'Armor 9, +ruthenium polymer' }),
128
+ row('full-body-armor', 'Full Body Armor', 'BodyArmor', 2000, 14, 'Impossible to conceal, styled to intimidate, built for heavy ops.', { page: 'p.437', shape: 'hardsuit', size: 'Large', weight: 8, rating: 'Rare', legality: 'R', stats: 'Armor 15' }),
129
+ row('helmet', 'Helmet', 'Helmet', 100, 0, 'A hard shell for the soft parts that matter most.', { page: 'p.437', shape: 'helmet', weight: 1, stats: 'Armor +2, Capacity 6' }),
130
+ row('full-helmet', 'Full Helmet', 'Helmet', 500, 0, 'Sealed faceplate. Nobody reads your expression, nobody reads your face.', { page: 'p.436', shape: 'helmet', weight: 1.5, rating: 'Standard', stats: 'Armor +3 (full body armor)' }),
131
+ row('ballistic-shield', 'Ballistic Shield', 'Shield', 1200, 12, 'A door you carry into rooms that need one.', { page: 'p.438', shape: 'shield', size: 'Large', weight: 7, rating: 'Rare', legality: 'R', stats: 'Armor +6' }),
132
+ // ---- Commlinks (p.438) ----
133
+ row('meta-link', 'Meta Link', 'Commlink', 100, 2, 'The squatter special. It makes calls. Usually.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.2, stats: 'Device Rating 1' }),
134
+ row('sony-emperor', 'Sony Emperor', 'Commlink', 700, 4, 'A solid mid-shelf commlink with a name bigger than its specs.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.2, stats: 'Device Rating 2' }),
135
+ row('renraku-sensei', 'Renraku Sensei', 'Commlink', 1000, 6, 'A businessman\'s link: discreet, capable, forgettable on purpose.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.2, stats: 'Device Rating 3' }),
136
+ row('erika-elite', 'Erika Elite', 'Commlink', 2500, 8, 'The decker\'s daily driver when the deck stays home.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.2, rating: 'Standard', stats: 'Device Rating 4' }),
137
+ row('hermes-ikon', 'Hermes Ikon', 'Commlink', 3000, 10, 'Serious hardware for people whose calls matter.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.3, rating: 'Rare', stats: 'Device Rating 5' }),
138
+ row('transys-avalon', 'Transys Avalon', 'Commlink', 5000, 12, 'Executive-grade signal discipline in a slab of brushed ceramic.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.3, rating: 'Rare', stats: 'Device Rating 6' }),
139
+ row('fairlight-caliban', 'Fairlight Caliban', 'Commlink', 8000, 14, 'The commlink executives get assassinated for.', { page: 'p.438', shape: 'slab', size: 'Tiny', weight: 0.3, rating: 'Legendary', stats: 'Device Rating 7' }),
140
+ row('sim-module', 'Sim Module (hot-sim modified)', 'Electronics', 100, 0, 'The chip that turns a commlink into a doorway. Modified, of course.', { page: 'p.438', shape: 'chip', size: 'Tiny', weight: 0.1, stats: '+100 nuyen add-on; hot sim is the illegal part' }),
141
+ // ---- Cyberdecks (p.439) -- the aspirational end of the catalog: a
64
142
  // Cyber-5 costs more than a Super-Platinum DocWagon year. Rating maps
65
143
  // the book's Device Rating onto the engine's quality tiers (deck
66
144
  // boxes and pools ride qualityBonus).
67
- row('erika-mcd-1', 'Erika MCD-1', 'Cyberdeck', 49500, 3, 'The learner\'s cyberdeck -- "entry-level" meaning a year of rent.', { approx: true, shape: 'deck', size: 'Small', weight: 2, rating: 'Common' }),
68
- row('microdeck-summit', 'Microdeck Summit', 'Cyberdeck', 58000, 3, 'Device Rating 1: a real deck, barely. Every decker\'s first love.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Common' }),
69
- row('hermes-chariot', 'Hermes Chariot', 'Cyberdeck', 123000, 6, 'Device Rating 2: the working professional\'s ride through the grid.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Standard' }),
70
- row('renraku-tsurugi', 'Renraku Tsurugi', 'Cyberdeck', 214125, 9, 'Device Rating 3: a sword, as the name says -- corp-grade ice-cutting.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2.5, rating: 'Rare' }),
71
- row('shiawase-cyber-5', 'Shiawase Cyber-5', 'Cyberdeck', 549375, 15, 'Device Rating 5: the deck legends retire on. Or die holding.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 3, rating: 'Legendary' }),
72
- // ---- Deck software (p.245-246) -- "load <program>" runs a carried
73
- // chip; hacking-program pricing is cited, common-program pricing is
74
- // canon-approximate. Agents: rating x 2,000, avail rating x 3 (cited).
75
- row('browse-program', 'Browse Program', 'Software', 80, 2, 'Purpose-built search routines: +2 dice on Matrix Search.', { approx: true, shape: 'chip', size: 'Tiny', weight: 0.1 }),
76
- row('toolbox-program', 'Toolbox Program', 'Software', 80, 2, 'Utility suite that keeps your persona thinking: +1 Data Processing.', { approx: true, shape: 'chip', size: 'Tiny', weight: 0.1 }),
77
- row('encryption-program', 'Encryption Program', 'Software', 80, 2, 'Wraps your traffic in ciphertext: +1 Firewall.', { approx: true, shape: 'chip', size: 'Tiny', weight: 0.1 }),
78
- row('signal-scrub-program', 'Signal Scrub Program', 'Software', 80, 2, 'Cleans your noise floor: +1 die defending your PAN.', { approx: true, shape: 'chip', size: 'Tiny', weight: 0.1 }),
79
- row('armor-program', 'Armor Program', 'Software', 250, 4, 'Structural integrity for your icon: +2 dice to resist Matrix damage.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
80
- row('biofeedback-filter-program', 'Biofeedback Filter Program', 'Software', 250, 4, 'A firewall for your sim module: +2 dice against biofeedback and dumpshock.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
81
- row('exploit-program', 'Exploit Program', 'Software', 250, 4, 'Scans targets for Firewall weaknesses: +2 Sleaze when cracking.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
82
- row('sneak-program', 'Sneak Program', 'Software', 250, 4, 'Misroutes anyone hunting your icon: +2 dice defending a silent PAN.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
83
- row('hammer-program', 'Hammer Program', 'Software', 250, 4, 'Brute-force payload: +2 Matrix damage on your attack programs.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
84
- row('decryption-program', 'Decryption Program', 'Software', 250, 4, 'Rips ciphers apart in realtime: +2 Attack in Matrix combat.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1 }),
85
- row('agent-rating-4', 'Agent Rating 4', 'Software', 8000, 12, 'Autonomous pilot program, rating 4 -- a sharper mind for your deck than the bundled dog-brain ("agent" deploys it).', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Rare' }),
86
- row('agent-rating-6', 'Agent Rating 6', 'Software', 12000, 18, 'Top-shelf autonomous agent, rating 6 -- near-sapient, and priced like it knows.', { page: 'p.246', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Legendary' }),
87
- row('microtransceiver', 'Microtransceiver', 'Electronics', 100, 2, 'Ear-canal comms for jobs where commlinks are too loud.', { approx: true, shape: 'earpiece', size: 'Tiny', weight: 0.1 }),
88
- row('white-noise-generator', 'White Noise Generator', 'Electronics', 300, 6, 'A bubble of privacy in a city of ears.', { approx: true, shape: 'puck', size: 'Tiny', weight: 0.3 }),
89
- row('area-jammer', 'Area Jammer', 'Electronics', 1200, 8, 'Kills every wireless signal in the room, yours included.', { approx: true, shape: 'box', weight: 1 }),
90
- // ---- Survival & entry (p.449) ----
91
- row('climbing-gear', 'Climbing Gear', 'Electronics', 200, 2, 'Rope, harness, and hardware rated for your bad decisions.', { page: 'p.449', shape: 'kit', size: 'Medium', weight: 5 }),
92
- row('flashlight', 'Flashlight', 'Electronics', 25, 2, 'Light where the city forgot to put any.', { page: 'p.449', shape: 'tube', size: 'Tiny', weight: 0.3 }),
93
- row('gecko-tape-gloves', 'Gecko Tape Gloves', 'Electronics', 250, 12, 'Walk up the wall. Forbidden for exactly that reason.', { page: 'p.449', shape: 'gloves', size: 'Tiny', weight: 0.3, rating: 'Rare' }),
145
+ row('erika-mcd-1', 'Erika MCD-1', 'Cyberdeck', 49500, 3, 'The learner\'s cyberdeck -- "entry-level" meaning a year of rent.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Common', legality: 'R', stats: 'DR 1, A/S/D/F 4 3 2 1, 1 program' }),
146
+ row('microdeck-summit', 'Microdeck Summit', 'Cyberdeck', 58000, 3, 'Device Rating 1: a real deck, barely. Every decker\'s first love.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Common', legality: 'R', stats: 'DR 1, A/S/D/F 4 3 3 1, 1 program' }),
147
+ row('microtronica-azteca-200', 'Microtronica Azteca 200', 'Cyberdeck', 110250, 6, 'Aztechnology\'s starter deck. It works. Somebody is always watching it work.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Standard', legality: 'R', stats: 'DR 2, A/S/D/F 5 4 3 2, 2 programs' }),
148
+ row('hermes-chariot', 'Hermes Chariot', 'Cyberdeck', 123000, 6, 'Device Rating 2: the working professional\'s ride through the grid.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2, rating: 'Standard', legality: 'R', stats: 'DR 2, A/S/D/F 5 4 4 2, 2 programs' }),
149
+ row('novatech-navigator', 'Novatech Navigator', 'Cyberdeck', 205750, 9, 'A deck that finds the seams in a host and remembers them.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2.5, rating: 'Rare', legality: 'R', stats: 'DR 3, A/S/D/F 6 5 4 3, 3 programs' }),
150
+ row('renraku-tsurugi', 'Renraku Tsurugi', 'Cyberdeck', 214125, 9, 'Device Rating 3: a sword, as the name says -- corp-grade ice-cutting.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 2.5, rating: 'Rare', legality: 'R', stats: 'DR 3, A/S/D/F 6 5 5 3, 3 programs' }),
151
+ row('sony-ciy-720', 'Sony CIY-720', 'Cyberdeck', 345000, 12, 'The deck you buy when the job pays in corporate secrets.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 3, rating: 'Legendary', legality: 'R', stats: 'DR 4, A/S/D/F 7 6 5 4, 4 programs' }),
152
+ row('shiawase-cyber-5', 'Shiawase Cyber-5', 'Cyberdeck', 549375, 15, 'Device Rating 5: the deck legends retire on. Or die holding.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 3, rating: 'Legendary', legality: 'R', stats: 'DR 5, A/S/D/F 8 7 6 5, 5 programs' }),
153
+ row('fairlight-excalibur', 'Fairlight Excalibur', 'Cyberdeck', 823250, 18, 'Device Rating 6. There is no rung above this one, only rumors.', { page: 'p.439', shape: 'deck', size: 'Small', weight: 3, rating: 'Legendary', legality: 'R', stats: 'DR 6, A/S/D/F 9 8 7 6, 6 programs' }),
154
+ // ---- Deck software (p.442) -- "load <program>" runs a carried
155
+ // chip. Common programs are unrestricted at 80 nuyen; hacking
156
+ // programs are 250 nuyen at 6R; agents are rating x 2,000, avail
157
+ // rating x 3.
158
+ row('browse-program', 'Browse Program', 'Software', 80, 0, 'Purpose-built search routines: +2 dice on Matrix Search.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, stats: 'Common program' }),
159
+ row('toolbox-program', 'Toolbox Program', 'Software', 80, 0, 'Utility suite that keeps your persona thinking: +1 Data Processing.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, stats: 'Common program' }),
160
+ row('encryption-program', 'Encryption Program', 'Software', 80, 0, 'Wraps your traffic in ciphertext: +1 Firewall.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, stats: 'Common program' }),
161
+ row('signal-scrub-program', 'Signal Scrub Program', 'Software', 80, 0, 'Cleans your noise floor: +1 die defending your PAN.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, stats: 'Common program' }),
162
+ row('armor-program', 'Armor Program', 'Software', 250, 6, 'Structural integrity for your icon: +2 dice to resist Matrix damage.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
163
+ row('biofeedback-filter-program', 'Biofeedback Filter Program', 'Software', 250, 6, 'A firewall for your sim module: +2 dice against biofeedback and dumpshock.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
164
+ row('exploit-program', 'Exploit Program', 'Software', 250, 6, 'Scans targets for Firewall weaknesses: +2 Sleaze when cracking.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
165
+ row('sneak-program', 'Sneak Program', 'Software', 250, 6, 'Misroutes anyone hunting your icon: +2 dice defending a silent PAN.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
166
+ row('hammer-program', 'Hammer Program', 'Software', 250, 6, 'Brute-force payload: +2 Matrix damage on your attack programs.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
167
+ row('decryption-program', 'Decryption Program', 'Software', 250, 6, 'Rips ciphers apart in realtime: +2 Attack in Matrix combat.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Hacking program' }),
168
+ row('agent-rating-4', 'Agent Rating 4', 'Software', 8000, 12, 'Autonomous pilot program, rating 4 -- a sharper mind for your deck than the bundled dog-brain ("agent" deploys it).', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Rare', stats: 'Agent 4: rating x 2,000 nuyen, avail rating x 3' }),
169
+ row('agent-rating-6', 'Agent Rating 6', 'Software', 12000, 18, 'Top-shelf autonomous agent, rating 6 -- near-sapient, and priced like it knows.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Legendary', stats: 'Agent 6: rating x 2,000 nuyen, avail rating x 3' }),
170
+ row('mapsoft', 'Mapsoft', 'Software', 100, 4, 'Every alley, service tunnel, and dead end in the sprawl. Mostly current.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1 }),
171
+ row('shopsoft', 'Shopsoft', 'Software', 150, 4, 'Knows what a thing is worth before the fixer opens his mouth: +1 to haggle.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1 }),
172
+ row('datasoft', 'Datasoft', 'Software', 120, 4, 'A packaged library on one subject. Trid, text, and someone\'s opinions.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1 }),
173
+ row('linguasoft-3', 'Linguasoft (Rating 3)', 'Software', 3000, 2, 'A language you never learned, arriving a half-second late.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Standard' }),
174
+ row('knowsoft-3', 'Knowsoft (Rating 3)', 'Software', 6000, 4, 'Somebody else\'s expertise, rented by the evening.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Rare' }),
175
+ row('activesoft-3', 'Activesoft (Rating 3)', 'Software', 15000, 8, 'A skill your hands have never practiced. Skillwires sold separately.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Rare' }),
176
+ row('autosoft-3', 'Autosoft (Rating 3)', 'Software', 1500, 6, 'Drone brains in a chip: targeting, evasion, or clearsight, pick one.', { page: 'p.442', shape: 'chip', size: 'Tiny', weight: 0.1, rating: 'Standard' }),
177
+ // ---- Comms & countermeasures (p.441) ----
178
+ row('microtransceiver', 'Microtransceiver', 'Electronics', 100, 2, 'Ear-canal comms for jobs where commlinks are too loud.', { page: 'p.441', shape: 'earpiece', size: 'Tiny', weight: 0.1 }),
179
+ row('subvocal-mic', 'Subvocal Microphone', 'Electronics', 50, 4, 'Stage-whisper at any range: -4 to anyone trying to overhear you.', { page: 'p.440', shape: 'patch', size: 'Tiny', weight: 0.1, stats: 'Device Rating 3' }),
180
+ row('white-noise-generator', 'White Noise Generator (Rating 6)', 'Electronics', 300, 6, 'A bubble of privacy in a city of ears.', { page: 'p.441', shape: 'puck', size: 'Tiny', weight: 0.3, stats: 'Rating 6: rating x 50 nuyen, avail rating' }),
181
+ row('data-tap', 'Data Tap', 'Electronics', 300, 6, 'Clamps a hardline and copies everything through it, quietly.', { page: 'p.441', shape: 'clamp', size: 'Tiny', weight: 0.2, legality: 'R' }),
182
+ row('tag-eraser', 'Tag Eraser', 'Electronics', 450, 6, 'Kills RFID at contact range: ten boxes of Matrix damage, one charge.', { page: 'p.441', shape: 'wand', size: 'Tiny', weight: 0.3, legality: 'R' }),
183
+ row('directional-jammer', 'Directional Jammer (Rating 6)', 'Electronics', 1200, 12, 'A cone of silence you can aim. Everything in front goes deaf.', { page: 'p.441', shape: 'horn', weight: 1, rating: 'Rare', legality: 'F', stats: 'Rating 6: rating x 200 nuyen, avail (rating x 2)F' }),
184
+ row('area-jammer', 'Area Jammer (Rating 6)', 'Electronics', 1200, 18, 'Kills every wireless signal in the room, yours included.', { page: 'p.441', shape: 'box', weight: 1, rating: 'Rare', legality: 'F', stats: 'Rating 6: rating x 200 nuyen, avail (rating x 3)F' }),
185
+ row('stealth-tags', 'Stealth RFID Tags (per 10)', 'Electronics', 10, 7, 'Tags that do not answer when the wrong people ask.', { page: 'p.440', shape: 'tags', size: 'Tiny', weight: 0.1, legality: 'R', stats: 'Device Rating 3' }),
186
+ row('biometric-reader', 'Biometric Reader', 'Electronics', 200, 4, 'Prints, retinas, voice, tongue -- everything but DNA.', { page: 'p.440', shape: 'pad', size: 'Tiny', weight: 0.3, stats: 'Device Rating 3' }),
187
+ row('trodes', 'Trodes', 'Electronics', 70, 0, 'A cheap headband doorway to VR. Everybody\'s first ride.', { page: 'p.440', shape: 'headband', size: 'Tiny', weight: 0.1, stats: 'Device Rating 3' }),
188
+ // ---- Optics & audio (p.444-445) ----
189
+ row('goggles', 'Goggles (Capacity 4)', 'Glasses', 200, 0, 'Four slots of whatever you need your eyes to do tonight.', { page: 'p.444', shape: 'goggles', size: 'Tiny', weight: 0.3, stats: 'Capacity 4: capacity x 50 nuyen' }),
190
+ row('glasses', 'Glasses (Capacity 3)', 'Glasses', 300, 0, 'Ordinary frames, extraordinary optics. Nobody looks twice.', { page: 'p.444', shape: 'glasses', size: 'Tiny', weight: 0.1, stats: 'Capacity 3: capacity x 100 nuyen' }),
191
+ row('binoculars', 'Binoculars', 'Electronics', 50, 0, 'Distance, made someone else\'s problem.', { page: 'p.444', shape: 'binoculars', size: 'Tiny', weight: 0.5, stats: 'Built-in vision magnification' }),
192
+ row('micro-camera', 'Micro-Camera', 'Electronics', 100, 0, 'A lens the size of a coat button, and it never blinks.', { page: 'p.444', shape: 'lens', size: 'Tiny', weight: 0.1, stats: 'Capacity 1' }),
193
+ row('mage-sight-goggles', 'Mage Sight Goggles', 'Glasses', 3000, 12, 'Borrowed astral vision -- a mage sees what the lenses see.', { page: 'p.444', shape: 'goggles', size: 'Tiny', weight: 0.4, rating: 'Rare', legality: 'R' }),
194
+ row('smartlink', 'Smartlink (external)', 'Electronics', 2000, 4, 'The optics half of a smartgun system: +2 dice, and the gun knows it.', { page: 'p.444', shape: 'module', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R', stats: 'Vision enhancement, Capacity [1]' }),
195
+ row('low-light-vision-mod', 'Low-Light Vision (mod)', 'Electronics', 500, 4, 'Turns a dark alley into a gray one.', { page: 'p.444', shape: 'module', size: 'Tiny', weight: 0.1, stats: 'Vision enhancement, Capacity [1]' }),
196
+ row('thermographic-vision-mod', 'Thermographic Vision (mod)', 'Electronics', 500, 6, 'Heat as a picture: bodies behind doors, engines still warm.', { page: 'p.444', shape: 'module', size: 'Tiny', weight: 0.1, stats: 'Vision enhancement, Capacity [1]' }),
197
+ row('ear-buds', 'Ear Buds (Capacity 3)', 'Electronics', 150, 0, 'Hard to spot, harder to tell from the music kind.', { page: 'p.445', shape: 'earbuds', size: 'Tiny', weight: 0.1, stats: 'Capacity 3: capacity x 50 nuyen' }),
198
+ row('laser-mic', 'Laser Microphone (Capacity 3)', 'Electronics', 300, 6, 'Reads a conversation off the windowpane it happened behind.', { page: 'p.445', shape: 'mic', size: 'Tiny', weight: 0.4, legality: 'R', stats: 'Capacity 3: capacity x 100 nuyen' }),
199
+ // ---- Security, B&E, and rope (p.447-p.450) ----
200
+ row('plasteel-restraints', 'Plasteel Restraints', 'Electronics', 50, 6, 'Cuffs that do not care how strong you think you are.', { page: 'p.447', shape: 'cuffs', size: 'Tiny', weight: 0.3, legality: 'R' }),
201
+ row('containment-manacles', 'Containment Manacles', 'Electronics', 250, 6, 'For prisoners with chrome, magic, or ideas.', { page: 'p.447', shape: 'manacles', size: 'Small', weight: 1.5, legality: 'R' }),
202
+ row('lockpick-set', 'Lockpick Set', 'Electronics', 250, 4, 'Manual picks and a shim set, wrapped like surgeon\'s tools.', { page: 'p.448', shape: 'roll', size: 'Tiny', weight: 0.3, legality: 'R' }),
203
+ row('sequencer-4', 'Sequencer (Rating 4)', 'Electronics', 1000, 12, 'Brute-forces a maglock\'s passcode while you stand there looking bored.', { page: 'p.448', shape: 'dongle', size: 'Tiny', weight: 0.2, rating: 'Standard', legality: 'F', stats: 'Rating 4: rating x 250 nuyen, avail (rating x 3)F' }),
204
+ row('maglock-passkey-4', 'Maglock Passkey (Rating 4)', 'Electronics', 8000, 12, 'A skeleton key for cardreaders. Expensive because it works.', { page: 'p.448', shape: 'card', size: 'Tiny', weight: 0.1, rating: 'Rare', legality: 'F', stats: 'Rating 4: rating x 2,000 nuyen, avail (rating x 3)F' }),
205
+ row('keycard-copier-4', 'Keycard Copier (Rating 4)', 'Electronics', 2400, 8, 'Swipe once, own the door. The forged card rolls Rating x 2.', { page: 'p.448', shape: 'reader', size: 'Tiny', weight: 0.3, rating: 'Standard', legality: 'F', stats: 'Rating 4: rating x 600 nuyen, avail 8F' }),
206
+ row('miniwelder', 'Miniwelder', 'Electronics', 250, 2, 'Thirty minutes of arc: through a hinge, a bar, or a padlock.', { page: 'p.448', shape: 'torch', size: 'Small', weight: 1.5 }),
207
+ row('thermite-burning-bar', 'Thermite Burning Bar', 'Electronics', 500, 16, 'Burns through a vault door and everything behind it. Bring a hood.', { page: 'p.448', shape: 'bar', size: 'Medium', weight: 3, rating: 'Rare', legality: 'R' }),
208
+ row('monofilament-chainsaw', 'Monofilament Chainsaw', 'Electronics', 500, 8, 'A loop of monowire on a motor. It does not know what a wall is.', { page: 'p.448', shape: 'saw', size: 'Medium', weight: 3, rating: 'Standard', stats: 'Acc 3, Reach 1, DV 8P, AP -6 (Exotic Melee)' }),
209
+ row('standard-rope', 'Standard Rope (100 m)', 'Electronics', 50, 0, 'A hundred meters of the least glamorous life insurance made.', { page: 'p.450', shape: 'coil', size: 'Medium', weight: 4, texture: 'braided' }),
210
+ row('stealth-rope', 'Stealth Rope (100 m)', 'Electronics', 85, 8, 'Rope that stiffens on command and goes limp when you leave it.', { page: 'p.450', shape: 'coil', size: 'Medium', weight: 3, legality: 'F' }),
211
+ row('myomeric-rope', 'Myomeric Rope (10 m)', 'Electronics', 200, 10, 'Rope that climbs for you. Ten meters of muscle in a coil.', { page: 'p.450', shape: 'coil', size: 'Small', weight: 2, rating: 'Standard' }),
212
+ // ---- Survival & entry (p.449-p.450) ----
213
+ row('climbing-gear', 'Climbing Gear', 'Electronics', 200, 0, 'Rope, harness, and hardware rated for your bad decisions.', { page: 'p.449', shape: 'kit', size: 'Medium', weight: 5 }),
214
+ row('flashlight', 'Flashlight', 'Electronics', 25, 0, 'Light where the city forgot to put any.', { page: 'p.449', shape: 'tube', size: 'Tiny', weight: 0.3 }),
215
+ row('light-stick', 'Light Stick', 'Electronics', 25, 0, 'Crack it, drop it, mark the way out.', { page: 'p.449', shape: 'stick', size: 'Tiny', weight: 0.1, color: 'chem green' }),
216
+ row('gas-mask', 'Gas Mask', 'Mask', 200, 0, 'Filters the air the sprawl insists on selling you.', { page: 'p.449', shape: 'mask', size: 'Small', weight: 1 }),
217
+ row('respirator-4', 'Respirator (Rating 4)', 'Mask', 200, 0, 'A slim filter for smoke, dust, and whatever the plant is venting.', { page: 'p.449', shape: 'mask', size: 'Tiny', weight: 0.4, stats: 'Rating 4: rating x 50 nuyen' }),
218
+ row('chemsuit-4', 'Chemsuit (Rating 4)', 'Clothing', 600, 8, 'A coverall that argues with chemistry for as long as it lasts.', { page: 'p.449', shape: 'coverall', size: 'Medium', weight: 2, rating: 'Standard', stats: 'Rating 4: rating x 150 nuyen, avail rating x 2' }),
219
+ row('gecko-tape-gloves', 'Gecko Tape Gloves', 'Gloves', 250, 12, 'Walk up the wall. Forbidden for exactly that reason.', { page: 'p.449', shape: 'gloves', size: 'Tiny', weight: 0.3, rating: 'Rare' }),
220
+ row('rappelling-gloves', 'Rappelling Gloves', 'Gloves', 50, 0, 'Palms that let you go down fast without leaving skin behind.', { page: 'p.449', shape: 'gloves', size: 'Tiny', weight: 0.2, texture: 'reinforced leather' }),
94
221
  row('survival-kit', 'Survival Kit', 'Electronics', 200, 4, 'Everything the barrens forgot to provide.', { page: 'p.449', shape: 'kit', size: 'Medium', weight: 3 }),
95
- row('grapple-gun', 'Grapple Gun', 'Electronics', 500, 8, 'A rooftop in one pull of a trigger. Bring the descent kit.', { approx: true, shape: 'launcher', size: 'Medium', weight: 2 }),
96
- row('lockpick-set', 'Lockpick Set', 'Electronics', 250, 6, 'Manual picks and a shim set, wrapped like surgeon\'s tools.', { approx: true, shape: 'roll', size: 'Tiny', weight: 0.3 }),
97
- // ---- Medical (p.450, p.96) ----
98
- row('medkit-3', 'Medkit (Rating 3)', 'Medical', 750, 3, 'Pocket trauma kit: sealant, stims, a diagnostic patch.', { approx: true, shape: 'case', color: 'red' }),
99
- row('medkit-6', 'Medkit (Rating 6)', 'Medical', 1500, 6, 'A clinic in a handheld case -- the good one.', { page: 'p.96', shape: 'case', size: 'Medium', weight: 2, color: 'red' }),
100
- row('stim-patch', 'Stim Patch (Rating 4)', 'StimPatch', 100, 4, 'Slap-on chemical courage. The crash comes later.', { approx: true, shape: 'patch', size: 'Tiny', weight: 0.1, color: 'yellow' }),
101
- row('trauma-patch', 'Trauma Patch', 'Medical', 500, 12, 'The patch that argues with death itself. Once.', { approx: true, shape: 'patch', size: 'Tiny', weight: 0.1, color: 'red', rating: 'Rare' }),
222
+ row('grapple-gun', 'Grapple Gun', 'Electronics', 500, 8, 'A rooftop in one pull of a trigger. Bring the descent kit.', { page: 'p.450', shape: 'launcher', size: 'Medium', weight: 2, legality: 'R', stats: 'Acc 3, DV 7S, AP -2, SS, 1 (ml)' }),
223
+ row('tool-kit', 'Tool Kit', 'Tool', 500, 0, 'One trade\'s worth of tools in a case. Buy the trade separately.', { page: 'p.443', shape: 'case', size: 'Medium', weight: 4 }),
224
+ // ---- Medical (p.450-p.451) ----
225
+ row('disposable-syringe', 'Disposable Syringe', 'Medical', 10, 3, 'One dose, one use, no questions afterward.', { page: 'p.450', shape: 'syringe', size: 'Tiny', weight: 0.1, color: 'clear' }),
226
+ row('biomonitor', 'Biomonitor', 'Medical', 300, 3, 'Reads your vitals and tells the world when they stop.', { page: 'p.450', shape: 'band', size: 'Tiny', weight: 0.2 }),
227
+ row('medkit-supplies', 'Medkit Supplies', 'Medical', 100, 0, 'Refills: sealant, patches, and the drugs that ran out last run.', { page: 'p.450', shape: 'pouch', size: 'Tiny', weight: 0.5, color: 'red' }),
228
+ row('medkit-3', 'Medkit (Rating 3)', 'Medical', 750, 3, 'Pocket trauma kit: sealant, stims, a diagnostic patch.', { page: 'p.450', shape: 'case', color: 'red', stats: 'Rating 3: rating x 250 nuyen, avail rating' }),
229
+ row('medkit-6', 'Medkit (Rating 6)', 'Medical', 1500, 6, 'A clinic in a handheld case -- the good one.', { page: 'p.450', shape: 'case', size: 'Medium', weight: 2, color: 'red', stats: 'Rating 6: rating x 250 nuyen, avail rating' }),
230
+ row('stim-patch', 'Stim Patch (Rating 4)', 'StimPatch', 100, 8, 'Slap-on chemical courage. The crash comes later.', { page: 'p.451', shape: 'patch', size: 'Tiny', weight: 0.1, color: 'yellow', stats: 'Rating 4: rating x 25 nuyen, avail rating x 2' }),
231
+ row('antidote-patch-4', 'Antidote Patch (Rating 4)', 'SlapPatch', 200, 4, 'Argues with a toxin already in your blood. Sometimes it wins.', { page: 'p.451', shape: 'patch', size: 'Tiny', weight: 0.1, color: 'green', stats: 'Rating 4: rating x 50 nuyen, avail rating' }),
232
+ row('tranq-patch-6', 'Tranq Patch (Rating 6)', 'SlapPatch', 120, 12, 'Puts someone under without putting them down.', { page: 'p.451', shape: 'patch', size: 'Tiny', weight: 0.1, color: 'blue', stats: 'Rating 6: rating x 20 nuyen, avail rating x 2' }),
233
+ row('trauma-patch', 'Trauma Patch', 'Medical', 500, 6, 'The patch that argues with death itself. Once.', { page: 'p.451', shape: 'patch', size: 'Tiny', weight: 0.1, color: 'red', rating: 'Standard', stats: 'Stabilizes a dying patient on Body' }),
102
234
  // ---- Magical goods (p.461) ----
103
- row('power-focus-1', 'Power Focus (Force 1)', 'Focus', 18000, 4, 'A bound anchor for living mana. The real investment.', { page: 'p.461', shape: 'amulet', size: 'Tiny', weight: 0.2, rating: 'Rare' }),
104
- row('combat-spell-formula', 'Combat Spell Formula', 'SpellFormula', 2000, 8, 'A combat spell, notated for study. Bring talent.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1 }),
105
- row('reagents', 'Reagents (10 drams)', 'Focus', 200, 2, 'Alchemically potent odds and ends, jarred and labeled.', { approx: true, shape: 'jar', size: 'Tiny', weight: 0.5 }),
106
- // ---- Explosives ----
107
- row('flash-bang', 'Flash-Bang Grenade', 'Explosive', 100, 6, 'A very loud argument for leaving.', { approx: true, shape: 'grenade', size: 'Tiny', weight: 0.3 }),
108
- row('smoke-grenade', 'Smoke Grenade', 'Explosive', 40, 4, 'Portable weather for tactical withdrawals.', { approx: true, shape: 'grenade', size: 'Tiny', weight: 0.3 }),
109
- // ---- Drones ----
110
- row('fly-spy', 'MCT Fly-Spy', 'Drone', 2000, 8, 'A palm-sized dragonfly with a camera where its soul should be.', { approx: true, shape: 'insect', size: 'Tiny', weight: 0.4, color: 'iridescent green' }),
111
- // ---- Identity (p.367: 2,500x per rating; a KE checkpoint's scanner
112
- // beats the rating to BURN the fake -- see game.ts checkpointOnRideHome) ----
113
- row('fake-sin-2', 'Fake SIN (Rating 2)', 'Software', 5000, 6, 'A life that holds up to a doorman scanner -- not to anyone paid to look twice.', { page: 'p.367', shape: 'credential', size: 'Tiny', weight: 0 }),
114
- row('fake-sin-4', 'Fake SIN (Rating 4)', 'Software', 10000, 12, 'A properly built identity: employment history, a tax trail, a face that checks out.', { page: 'p.367', shape: 'credential', size: 'Tiny', weight: 0 }),
115
- row('fake-sin-6', 'Fake SIN (Rating 6)', 'Software', 15000, 18, 'Artisan work -- a person who has always existed, right up until the day you burn them.', { page: 'p.367', shape: 'credential', size: 'Tiny', weight: 0, rating: 'Rare' }),
235
+ row('reagents', 'Reagents (10 drams)', 'Focus', 200, 0, 'Alchemically potent odds and ends, jarred and labeled.', { page: 'p.461', shape: 'jar', size: 'Tiny', weight: 0.5, stats: '20 nuyen per dram' }),
236
+ row('lodge-materials-3', 'Magical Lodge Materials (Force 3)', 'Focus', 1500, 6, 'The candles, chalk, and silence a lodge is actually made of.', { page: 'p.461', shape: 'crate', size: 'Medium', weight: 6, rating: 'Standard', stats: 'Force 3: force x 500 nuyen, avail force x 2' }),
237
+ row('qi-focus-1', 'Qi Focus (Force 1)', 'Focus', 3000, 3, 'Bound to the body, not the hand -- a tattoo that hums when you do.', { page: 'p.461', shape: 'tattoo', size: 'Tiny', weight: 0, rating: 'Standard', legality: 'R', stats: 'Force 1: force x 3,000 nuyen, avail (force x 3)R' }),
238
+ row('spell-focus-1', 'Spell Focus (Force 1)', 'Focus', 4000, 3, 'A lens for one category of spell. Bond it or it is jewelry.', { page: 'p.461', shape: 'ring', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R', stats: 'Force 1: force x 4,000 nuyen, avail (force x 3)R' }),
239
+ row('spirit-focus-1', 'Spirit Focus (Force 1)', 'Focus', 4000, 3, 'Where a summoner keeps the leash.', { page: 'p.461', shape: 'talisman', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R', stats: 'Force 1: force x 4,000 nuyen, avail (force x 3)R' }),
240
+ row('weapon-focus-1', 'Weapon Focus (Force 1)', 'Focus', 7000, 4, 'A blade that cuts things that have never had a body.', { page: 'p.461', shape: 'blade', size: 'Medium', weight: 1.2, rating: 'Rare', legality: 'R', stats: 'Force 1: force x 7,000 nuyen, avail (force x 4)R' }),
241
+ row('power-focus-1', 'Power Focus (Force 1)', 'Focus', 18000, 4, 'A bound anchor for living mana. The real investment.', { page: 'p.461', shape: 'amulet', size: 'Tiny', weight: 0.2, rating: 'Rare', legality: 'R', stats: 'Force 1: force x 18,000 nuyen, avail (force x 4)R' }),
242
+ row('detection-spell-formula', 'Detection Spell Formula', 'SpellFormula', 500, 4, 'The notation for seeing what is not on offer. Bring talent.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1, legality: 'R' }),
243
+ row('health-spell-formula', 'Health Spell Formula', 'SpellFormula', 500, 4, 'Mending, written down. Cheaper than a street doc, harder to learn.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1, legality: 'R' }),
244
+ row('illusion-spell-formula', 'Illusion Spell Formula', 'SpellFormula', 1000, 8, 'How to lie to eyes, ears, and sensors at the same time.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R' }),
245
+ row('manipulation-spell-formula', 'Manipulation Spell Formula', 'SpellFormula', 1500, 8, 'Moving the world with the mind, notated in someone else\'s hand.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R' }),
246
+ row('combat-spell-formula', 'Combat Spell Formula', 'SpellFormula', 2000, 8, 'A combat spell, notated for study. Bring talent.', { page: 'p.461', shape: 'scroll', size: 'Tiny', weight: 0.1, rating: 'Standard', legality: 'R' }),
247
+ // ---- Drones (p.466) ----
248
+ row('fly-spy', 'MCT Fly-Spy', 'Drone', 2000, 8, 'A palm-sized dragonfly with a camera where its soul should be.', { page: 'p.466', shape: 'insect', size: 'Tiny', weight: 0.4, color: 'iridescent green', stats: 'Handl 4, Spd 3, Acc 2, Bod 1, Arm 0, Pilot 3, Sen 3' }),
249
+ row('horizon-flying-eye', 'Horizon Flying Eye', 'Drone', 2000, 8, 'A news drone that never asks whose story it is filming.', { page: 'p.466', shape: 'disc', size: 'Tiny', weight: 0.5, color: 'white', stats: 'Handl 4, Spd 3, Acc 2, Bod 1, Arm 0, Pilot 3, Sen 3' }),
250
+ row('aztechnology-crawler', 'Aztechnology Crawler', 'Drone', 4000, 4, 'A walker that goes where wheels give up and stairs begin.', { page: 'p.466', shape: 'walker', size: 'Small', weight: 8, rating: 'Standard', stats: 'Handl 4, Spd 3, Acc 1, Bod 3, Arm 3, Pilot 3, Sen 3' }),
251
+ row('ares-duelist', 'Ares Duelist', 'Drone', 4500, 5, 'A bodyguard drone with a monofilament arm and no sense of humor.', { page: 'p.466', shape: 'walker', size: 'Small', weight: 10, rating: 'Standard', legality: 'R', stats: 'Handl 3, Spd 3, Acc 1, Bod 4, Arm 4, Pilot 3, Sen 3' }),
252
+ row('gm-nissan-doberman', 'GM-Nissan Doberman', 'Drone', 5000, 4, 'A tracked weapons platform the size of a dog, with a dog\'s loyalty.', { page: 'p.466', shape: 'tracked', size: 'Medium', weight: 20, rating: 'Rare', legality: 'R', stats: 'Handl 5, Spd 3, Acc 1, Bod 4, Arm 4, Pilot 3, Sen 3, weapon mount' }),
253
+ row('mct-nissan-roto-drone', 'MCT-Nissan Roto-Drone', 'Drone', 5000, 6, 'The workhorse quadrotor: cargo, cameras, or something with a barrel.', { page: 'p.466', shape: 'rotary', size: 'Medium', weight: 15, rating: 'Rare', stats: 'Handl 4, Spd 4, Acc 2, Bod 4, Arm 4, Pilot 3, Sen 3, weapon mount' }),
254
+ row('cd-dalmatian', 'Cyberspace Designs Dalmatian', 'Drone', 10000, 6, 'A VTOL that carries a body -- yours, ideally still breathing.', { page: 'p.466', shape: 'vtol', size: 'Medium', weight: 25, rating: 'Rare', legality: 'R', stats: 'Handl 5, Spd 5, Acc 3, Bod 5, Arm 5, Pilot 3, Sen 3' }),
255
+ row('lockheed-optic-x2', 'Lockheed Optic-X2', 'Drone', 21000, 10, 'A high-altitude eye that watches a whole district and never blinks.', { page: 'p.466', shape: 'vstol', size: 'Medium', weight: 12, rating: 'Legendary', stats: 'Handl 4, Spd 4, Acc 3, Bod 2, Arm 2, Pilot 3, Sen 3' }),
256
+ row('steel-lynx', 'Steel Lynx', 'Drone', 25000, 10, 'A rolling gun emplacement. When it arrives, the run is already loud.', { page: 'p.466', shape: 'wheeled', size: 'Large', weight: 120, rating: 'Legendary', legality: 'R', stats: 'Handl 5, Spd 4, Acc 2, Bod 6, Arm 12, Pilot 3, Sen 3, heavy weapon mount' }),
257
+ // ---- Identity, paper, and credit (p.443: fake SIN 2,500x per rating;
258
+ // a KE checkpoint's scanner beats the rating to BURN the fake -- see
259
+ // game.ts checkpointOnRideHome) ----
260
+ row('fake-sin-2', 'Fake SIN (Rating 2)', 'FakeSIN', 5000, 6, 'A life that holds up to a doorman scanner -- not to anyone paid to look twice.', { page: 'p.443', shape: 'credential', size: 'Tiny', weight: 0, legality: 'F', stats: 'Rating 2: rating x 2,500 nuyen, avail (rating x 3)F' }),
261
+ row('fake-sin-4', 'Fake SIN (Rating 4)', 'FakeSIN', 10000, 12, 'A properly built identity: employment history, a tax trail, a face that checks out.', { page: 'p.443', shape: 'credential', size: 'Tiny', weight: 0, legality: 'F', stats: 'Rating 4: rating x 2,500 nuyen, avail (rating x 3)F' }),
262
+ row('fake-sin-6', 'Fake SIN (Rating 6)', 'FakeSIN', 15000, 18, 'Artisan work -- a person who has always existed, right up until the day you burn them.', { page: 'p.443', shape: 'credential', size: 'Tiny', weight: 0, rating: 'Rare', legality: 'F', stats: 'Rating 6: rating x 2,500 nuyen, avail (rating x 3)F' }),
263
+ row('fake-license-4', 'Fake License (Rating 4)', 'License', 800, 12, 'Paper that says the gun on your hip is a hobby.', { page: 'p.443', shape: 'credential', size: 'Tiny', weight: 0, legality: 'F', stats: 'Rating 4: rating x 200 nuyen, avail (rating x 3)F' }),
264
+ row('credstick-silver', 'Certified Credstick (Silver)', 'Credstick', 20, 0, 'Anonymous money in a stick. Holds 20,000 and asks nothing.', { page: 'p.443', shape: 'stick', size: 'Tiny', weight: 0.1, color: 'silver', stats: 'Max 20,000 nuyen' }),
265
+ row('credstick-gold', 'Certified Credstick (Gold)', 'Credstick', 100, 5, 'Holds a hundred thousand. Carrying one says something about your night.', { page: 'p.443', shape: 'stick', size: 'Tiny', weight: 0.1, color: 'gold', stats: 'Max 100,000 nuyen' }),
266
+ row('credstick-platinum', 'Certified Credstick (Platinum)', 'Credstick', 500, 10, 'Half a million, certified. People have died for the empty ones.', { page: 'p.443', shape: 'stick', size: 'Tiny', weight: 0.1, color: 'platinum', rating: 'Standard', stats: 'Max 500,000 nuyen' }),
267
+ row('credstick-ebony', 'Certified Credstick (Ebony)', 'Credstick', 1000, 20, 'A million nuyen and a very short list of people who own one.', { page: 'p.443', shape: 'stick', size: 'Tiny', weight: 0.1, color: 'ebony', rating: 'Rare', stats: 'Max 1,000,000 nuyen' }),
116
268
  ];
117
269
  //# sourceMappingURL=catalog-data.js.map