@maka/maka-cli 5.6.0 → 5.8.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 (243) hide show
  1. package/bundle/typescript/package.json +2 -2
  2. package/bundle/typescript/src/commands/_index.d.ts +4 -0
  3. package/bundle/typescript/src/commands/_index.js +4 -0
  4. package/bundle/typescript/src/commands/_index.js.map +1 -1
  5. package/bundle/typescript/src/commands/debug.js +1 -7
  6. package/bundle/typescript/src/commands/debug.js.map +1 -1
  7. package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js +33 -32
  8. package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js.map +1 -1
  9. package/bundle/typescript/src/commands/env/_index.d.ts +3 -0
  10. package/bundle/typescript/src/commands/env/_index.js +4 -0
  11. package/bundle/typescript/src/commands/env/_index.js.map +1 -0
  12. package/bundle/typescript/src/commands/env/env.command.d.ts +3 -0
  13. package/bundle/typescript/src/commands/env/env.command.js +22 -0
  14. package/bundle/typescript/src/commands/env/env.command.js.map +1 -0
  15. package/bundle/typescript/src/commands/env/get.sub.cmd.d.ts +1 -0
  16. package/bundle/typescript/src/commands/env/get.sub.cmd.js +46 -0
  17. package/bundle/typescript/src/commands/env/get.sub.cmd.js.map +1 -0
  18. package/bundle/typescript/src/commands/env/prompt-for-env.d.ts +10 -0
  19. package/bundle/typescript/src/commands/env/prompt-for-env.js +27 -0
  20. package/bundle/typescript/src/commands/env/prompt-for-env.js.map +1 -0
  21. package/bundle/typescript/src/commands/env/set.sub.cmd.d.ts +1 -0
  22. package/bundle/typescript/src/commands/env/set.sub.cmd.js +59 -0
  23. package/bundle/typescript/src/commands/env/set.sub.cmd.js.map +1 -0
  24. package/bundle/typescript/src/commands/game/sideQuest/archetypes.d.ts +16 -0
  25. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +81 -2
  26. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/augmentations.d.ts +31 -0
  28. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js +125 -0
  29. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js.map +1 -0
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.d.ts +17 -7
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +113 -15
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -1
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.d.ts +37 -0
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +197 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -0
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +3 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +21 -11
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +3 -2
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +13 -5
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js +7 -1
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js.map +1 -1
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +16 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +97 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -0
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +21 -10
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +41 -27
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +6 -7
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +54 -20
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +35 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +306 -55
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.d.ts +33 -0
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +137 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -0
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.d.ts +13 -0
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +37 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -0
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/drink.d.ts +14 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/commands/drink.js +15 -0
  65. package/bundle/typescript/src/commands/game/sideQuest/commands/drink.js.map +1 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js +1 -1
  67. package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js.map +1 -1
  68. package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js +7 -0
  69. package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js.map +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/commands/eat.d.ts +14 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/commands/eat.js +15 -0
  72. package/bundle/typescript/src/commands/game/sideQuest/commands/eat.js.map +1 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js +10 -0
  74. package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +3 -0
  76. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +101 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +6 -1
  80. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +67 -22
  81. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  82. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +15 -7
  83. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  84. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +58 -2
  85. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
  86. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.d.ts +26 -0
  87. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +125 -0
  88. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -0
  89. package/bundle/typescript/src/commands/game/sideQuest/commands/install.d.ts +21 -0
  90. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js +125 -0
  91. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js.map +1 -0
  92. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.d.ts +16 -0
  93. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +83 -0
  94. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -0
  95. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.d.ts +16 -0
  96. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +76 -0
  97. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -0
  98. package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js +4 -0
  99. package/bundle/typescript/src/commands/game/sideQuest/commands/lock.js.map +1 -1
  100. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +7 -6
  101. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  102. package/bundle/typescript/src/commands/game/sideQuest/commands/order.d.ts +19 -0
  103. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js +151 -0
  104. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js.map +1 -0
  105. package/bundle/typescript/src/commands/game/sideQuest/commands/pay.d.ts +13 -0
  106. package/bundle/typescript/src/commands/game/sideQuest/commands/pay.js +39 -0
  107. package/bundle/typescript/src/commands/game/sideQuest/commands/pay.js.map +1 -0
  108. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +5 -1
  109. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
  110. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +6 -3
  111. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
  112. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.d.ts +21 -0
  113. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +121 -0
  114. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -0
  115. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +60 -12
  116. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  117. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +27 -10
  118. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  119. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +15 -4
  120. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  121. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.d.ts +17 -0
  122. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js +125 -0
  123. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js.map +1 -0
  124. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +36 -3
  125. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  126. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +9 -1
  127. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -1
  128. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +27 -4
  129. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
  130. package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js +1 -1
  131. package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js.map +1 -1
  132. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.d.ts +19 -0
  133. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +112 -0
  134. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js.map +1 -0
  135. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +8 -0
  136. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  137. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.d.ts +14 -0
  138. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js +67 -0
  139. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js.map +1 -0
  140. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +7 -0
  141. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  142. package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +29 -0
  143. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +118 -11
  144. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  145. package/bundle/typescript/src/commands/game/sideQuest/commands/work.d.ts +15 -0
  146. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +68 -0
  147. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js.map +1 -0
  148. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js +2 -1
  149. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js.map +1 -1
  150. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +1 -1
  151. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +132 -5
  152. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  153. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +6 -2
  154. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +10 -6
  155. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
  156. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +196 -0
  157. package/bundle/typescript/src/commands/game/sideQuest/game.js +828 -31
  158. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  159. package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +28 -0
  160. package/bundle/typescript/src/commands/game/sideQuest/models/item.js +111 -0
  161. package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
  162. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +33 -1
  163. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +232 -10
  164. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  165. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +111 -5
  166. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +432 -41
  167. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  168. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +6 -6
  169. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +38 -14
  170. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
  171. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +1 -0
  172. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +6 -0
  173. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  174. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +9 -0
  175. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +48 -8
  176. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  177. package/bundle/typescript/src/commands/game/sideQuest/qualities.d.ts +44 -0
  178. package/bundle/typescript/src/commands/game/sideQuest/qualities.js +155 -0
  179. package/bundle/typescript/src/commands/game/sideQuest/qualities.js.map +1 -0
  180. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +274 -5
  181. package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
  182. package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +17 -0
  183. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.d.ts +2 -0
  184. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js +4 -0
  185. package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js.map +1 -1
  186. package/bundle/typescript/src/commands/game/sideQuest/ui.js +82 -8
  187. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  188. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.d.ts +13 -6
  189. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +28 -15
  190. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
  191. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +15 -3
  192. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +207 -34
  193. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  194. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.d.ts +56 -0
  195. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js +144 -0
  196. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js.map +1 -0
  197. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +7 -4
  198. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +8 -5
  199. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -1
  200. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +14 -0
  201. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +15 -0
  202. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -0
  203. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.d.ts +16 -0
  204. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js +49 -4
  205. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js.map +1 -1
  206. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.d.ts +10 -0
  207. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js +16 -0
  208. package/bundle/typescript/src/commands/game/sideQuest/utilities/outdoors.js.map +1 -0
  209. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +21 -0
  210. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +107 -0
  211. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  212. package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.d.ts +17 -0
  213. package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.js +23 -0
  214. package/bundle/typescript/src/commands/game/sideQuest/utilities/terminal-reset.js.map +1 -0
  215. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +51 -15
  216. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  217. package/bundle/typescript/src/commands/remote/reconfig.sub.cmd.js +27 -1
  218. package/bundle/typescript/src/commands/remote/reconfig.sub.cmd.js.map +1 -1
  219. package/bundle/typescript/src/commands/run/run.command.js +1 -7
  220. package/bundle/typescript/src/commands/run/run.command.js.map +1 -1
  221. package/bundle/typescript/src/commands/ssl.js +10 -15
  222. package/bundle/typescript/src/commands/ssl.js.map +1 -1
  223. package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js +1 -6
  224. package/bundle/typescript/src/commands/testCmd/mocha.sub.cmd.js.map +1 -1
  225. package/bundle/typescript/src/generators/config.js +11 -10
  226. package/bundle/typescript/src/generators/config.js.map +1 -1
  227. package/bundle/typescript/src/templates/meteor/config/pm2.config.js +3 -3
  228. package/bundle/typescript/src/templates/meteor/project/gitignore +2 -1
  229. package/bundle/typescript/src/templates/react/kit/mui/auth/password-reset/basic-password-reset-page.js.tsx +3 -4
  230. package/bundle/typescript/src/tool.d.ts +2 -0
  231. package/bundle/typescript/src/tool.js +3 -0
  232. package/bundle/typescript/src/tool.js.map +1 -1
  233. package/bundle/typescript/src/tools/aws/aws.class.js +6 -4
  234. package/bundle/typescript/src/tools/aws/aws.class.js.map +1 -1
  235. package/bundle/typescript/src/tools/dotenvx/dotenvx.class.d.ts +24 -0
  236. package/bundle/typescript/src/tools/dotenvx/dotenvx.class.js +66 -0
  237. package/bundle/typescript/src/tools/dotenvx/dotenvx.class.js.map +1 -0
  238. package/bundle/typescript/src/tools/ssh/ssh.class.js +1 -1
  239. package/bundle/typescript/src/tools/ssh/ssh.class.js.map +1 -1
  240. package/bundle/typescript/src/tools/ssl/ssl.class.d.ts +8 -0
  241. package/bundle/typescript/src/tools/ssl/ssl.class.js +53 -0
  242. package/bundle/typescript/src/tools/ssl/ssl.class.js.map +1 -1
  243. package/package.json +2 -2
@@ -3,13 +3,17 @@ import { Logger } from '../utilities/logger.js';
3
3
  import { Inventory } from './inventory.js';
4
4
  import { AbstractPlayer } from '../types/shared/abstracts.js';
5
5
  export const STANCE_CYCLE = ['manual', 'guarded', 'aggressive'];
6
+ /** Meat and a jumped-in drone share the physical world. */
7
+ export function isPhysicalPlane(plane) {
8
+ return plane === 'meat' || plane === 'drone';
9
+ }
6
10
  export const SKILL_NAMES = [
7
11
  'firearms', 'blades', 'unarmed',
8
12
  'sneaking', 'perception', 'athletics',
9
13
  'hacking', 'locksmith',
10
14
  'negotiation', 'con', 'intimidation',
11
15
  'first-aid',
12
- 'assensing', 'spellcasting', 'piloting',
16
+ 'assensing', 'spellcasting', 'piloting', 'conjuring',
13
17
  ];
14
18
  /**
15
19
  * Skills that fall back to the legacy `combatSkill` attribute when no
@@ -166,13 +170,28 @@ export class Player extends AbstractPlayer {
166
170
  canPerceive(other) {
167
171
  if (this.plane === other.plane)
168
172
  return true;
169
- return this.plane === 'astral' && other.plane === 'meat';
173
+ // A drone is a physical machine in the meat world: meat sees it, its
174
+ // sensors see meat -- and combat between them is ordinary physics.
175
+ if (isPhysicalPlane(this.plane) && isPhysicalPlane(other.plane))
176
+ return true;
177
+ return this.plane === 'astral' && isPhysicalPlane(other.plane);
178
+ }
179
+ /** Whether these two can trade physical blows (see attack.ts targeting). */
180
+ sharesCombatPlane(other) {
181
+ return this.plane === other.plane
182
+ || (isPhysicalPlane(this.plane) && isPhysicalPlane(other.plane));
170
183
  }
171
184
  // True while an AI-driven action is parked waiting for an in-flight
172
185
  // exchange to settle (CombatExchange.waitForLull). One queued action per
173
186
  // actor: a second colliding attempt is refused outright instead of
174
187
  // stacking a burst of delayed attacks behind the same lull.
175
188
  waitingForLull = false;
189
+ // Death-resolution claim (CombatExchange.resolveDeath): set
190
+ // SYNCHRONOUSLY by the first resolution to reach this actor, so a
191
+ // concurrent kill (autopilot + typed action racing -- a real session
192
+ // crashed on the second removeActor) skips cleanly instead of
193
+ // narrating a second death during the first one's narration beats.
194
+ deathClaimed = false;
176
195
  // Combat attributes (see ICombatAttributes). Defaults make an unseeded
177
196
  // actor a competent-but-mortal street-level runner; seeds can override
178
197
  // per-NPC to make a hardened enforcer or a soft civilian.
@@ -225,8 +244,192 @@ export class Player extends AbstractPlayer {
225
244
  this.resonance = config.combat?.resonance ?? 0;
226
245
  this.adept = config.combat?.adept ?? false;
227
246
  this.skills = { ...(config.combat?.skills ?? {}) };
247
+ // Essence bookkeeping needs the UNAUGMENTED talent to burn down from
248
+ // (see installAug) -- capture it before any 'ware lands.
249
+ this.baseMagic = this.magic;
250
+ this.baseResonance = this.resonance;
228
251
  this._equipment = this.initializeEquipment();
229
252
  }
253
+ // ==================== Augmentations (see augmentations.ts) ====================
254
+ /** Everyone is born whole. */
255
+ static ESSENCE_MAX = 6;
256
+ // What's left of the meat. Burned permanently by installAug; hitting 0
257
+ // is refused at the clinic -- there'd be nothing left to cut away.
258
+ essence = Player.ESSENCE_MAX;
259
+ // The unaugmented Magic/Resonance, from which essence loss burns down
260
+ // (SR5e: each full point of essence lost -- ROUNDED UP -- costs one).
261
+ // `magic`/`resonance` above stay the EFFECTIVE values every pool reads.
262
+ baseMagic = 0;
263
+ baseResonance = 0;
264
+ /** Installed 'ware, by catalog key (see AUGMENTATIONS). */
265
+ augmentations = new Set();
266
+ hasAug(key) {
267
+ return this.augmentations.has(key);
268
+ }
269
+ /** Full essence points lost, rounded up -- the toll Magic/Resonance pay. */
270
+ get essenceBurn() {
271
+ return Math.ceil(Player.ESSENCE_MAX - this.essence - 1e-9);
272
+ }
273
+ /**
274
+ * Burns the essence and records the 'ware. No gating here -- the clinic
275
+ * (commands/install.ts) owns the refusals (essence floor, conflicts,
276
+ * payment); archetype starting chrome installs through this too.
277
+ */
278
+ installAug(aug) {
279
+ this.augmentations.add(aug.key);
280
+ // One-decimal arithmetic: 6 - 0.1 - 0.2 must read 5.7, not 5.6999....
281
+ this.essence = Math.round((this.essence - aug.essence) * 10) / 10;
282
+ const burn = this.essenceBurn;
283
+ this.magic = Math.max(0, this.baseMagic - burn);
284
+ this.resonance = Math.max(0, this.baseResonance - burn);
285
+ }
286
+ /**
287
+ * The single tap every pool reads chrome through. Wired Reflexes tiers
288
+ * don't stack -- the higher tier wins (II supersedes I) -- but the
289
+ * synaptic-vs-wired conflict is the clinic's job, not this getter's.
290
+ */
291
+ augBonus(kind) {
292
+ if (this.augmentations.size === 0)
293
+ return 0;
294
+ const has = (k) => this.augmentations.has(k);
295
+ const reflexes = (has('wired-reflexes-2') ? 2 : has('wired-reflexes-1') ? 1 : 0) + (has('synaptic-booster') ? 1 : 0);
296
+ switch (kind) {
297
+ case 'initiative': return reflexes;
298
+ case 'defense': return reflexes;
299
+ case 'attack': return has('muscle-toner') ? 1 : 0;
300
+ case 'firearms': return has('smartlink') ? 2 : 0;
301
+ case 'soak': return (has('bone-lacing') ? 1 : 0) + (has('dermal-plating') ? 2 : 0);
302
+ case 'unarmed-damage': return has('bone-lacing') ? 2 : 0;
303
+ case 'perception': return has('cybereyes') ? 2 : 0;
304
+ case 'matrix': return has('datajack') ? 1 : 0;
305
+ case 'piloting': return has('control-rig') ? 2 : 0;
306
+ }
307
+ }
308
+ // ==================== Qualities (see qualities.ts) ====================
309
+ // SR5e's traits (p.71-85), baked into the archetype at creation
310
+ // (outfitArchetype). Every entry in the catalog has a real mechanical
311
+ // hook; the ones that read as flat pool nudges all tap through
312
+ // qualityBonus below, the same single-switch pattern as augBonus.
313
+ /** Installed qualities, by catalog key (see QUALITIES). */
314
+ qualities = new Set();
315
+ hasQuality(key) {
316
+ return this.qualities.has(key);
317
+ }
318
+ /**
319
+ * Records a quality and applies its one-time creation effect. Lucky is
320
+ * the only quality that edits an attribute (RAW raises the Edge ceiling;
321
+ * with no attribute point-buy here, the point lands directly).
322
+ */
323
+ addQuality(key) {
324
+ if (this.qualities.has(key))
325
+ return;
326
+ this.qualities.add(key);
327
+ if (key === 'lucky') {
328
+ this.edge += 1;
329
+ this.edgeRemaining += 1;
330
+ }
331
+ }
332
+ /** The single tap every pool reads qualities through (cf. augBonus). */
333
+ qualityBonus(kind) {
334
+ if (this.qualities.size === 0)
335
+ return 0;
336
+ const has = (k) => this.qualities.has(k);
337
+ switch (kind) {
338
+ case 'sneak': return (has('catlike') ? 2 : 0) + (has('distinctive-style') ? -2 : 0);
339
+ case 'athletics': return has('natural-athlete') ? 2 : 0;
340
+ case 'matrix': return has('simsense-vertigo') ? -2 : 0;
341
+ case 'hacking': return (has('analytical-mind') ? 2 : 0) + this.qualityBonus('matrix');
342
+ case 'piloting': return has('juryrigger') ? 2 : 0;
343
+ case 'soak': return has('toughness') ? 1 : 0;
344
+ case 'healing': return has('quick-healer') ? 2 : 0;
345
+ }
346
+ }
347
+ // First Impression (p.74): the first social play of each job rolls +2.
348
+ // Consumed like the edge boost and the liquid-courage die; re-armed
349
+ // with the per-job resets in refreshEdge.
350
+ _firstImpressionUsed = false;
351
+ /** Spends the first impression: +2 for the social roll happening NOW. */
352
+ consumeFirstImpression() {
353
+ if (!this.qualities.has('first-impression') || this._firstImpressionUsed)
354
+ return 0;
355
+ this._firstImpressionUsed = true;
356
+ return 2;
357
+ }
358
+ /** SINner tax (p.84-85): the cut every payout loses to having a file. */
359
+ get sinTaxRate() {
360
+ if (this.qualities.has('sinner-corp'))
361
+ return 0.20;
362
+ if (this.qualities.has('sinner-criminal') || this.qualities.has('sinner-national'))
363
+ return 0.15;
364
+ return 0;
365
+ }
366
+ // Combat Paralysis (p.80): armed by beginExchange when a FRESH fight
367
+ // opens, consumed by the initiative roll-off -- pool halved, then the
368
+ // freeze passes (subsequent exchanges of the same fight roll normally).
369
+ paralysisFreeze = false;
370
+ // ==================== Initiation & Submersion (SR5e p.324-326 / p.257-258) ====================
371
+ // The Awakened (and technomancer) endgame: karma-priced grades (10 +
372
+ // grade x3, see commands/initiate.ts) that lift the talent ceiling to
373
+ // 6 + grade and grant one METAMAGIC (or echo) each. Grade can never
374
+ // exceed the BASE talent (RAW). The picks live here as a Set so every
375
+ // pool can read them the same way it reads qualities and chrome.
376
+ initiationGrade = 0;
377
+ /** Metamagics (casters/adepts) and echoes (technomancers), by key. */
378
+ metamagics = new Set();
379
+ hasMetamagic(key) {
380
+ return this.metamagics.has(key);
381
+ }
382
+ // ==================== Reputation (SR5e p.367-368) ====================
383
+ // Three tracked scores. STREET CRED is earned karma made visible: one
384
+ // point per full 10 career karma (earned on the street -- creation
385
+ // karma doesn't count; the name starts unknown). NOTORIETY starts from
386
+ // the qualities (the book's own list) and grows from incidents --
387
+ // walking out on a job, cutting down someone mid-surrender -- each
388
+ // counted ONCE (RAW: never twice for the same incident). PUBLIC
389
+ // AWARENESS derives from both, plus a Distinctive Style everyone can
390
+ // describe to a Knight Errant sketch artist.
391
+ /** Total karma EARNED in play -- spending it doesn't touch this. */
392
+ careerKarma = 0;
393
+ // Each stain on the record, in the player's own fiction ("walked out
394
+ // on 'Tacoma Rain'"). The strings ARE the dedupe keys.
395
+ _notorietyReasons = [];
396
+ /** Records an incident once; false means this exact stain already stands. */
397
+ addNotoriety(reason) {
398
+ if (this._notorietyReasons.includes(reason))
399
+ return false;
400
+ this._notorietyReasons.push(reason);
401
+ return true;
402
+ }
403
+ get notorietyReasons() {
404
+ return this._notorietyReasons;
405
+ }
406
+ get streetCred() {
407
+ return Math.floor(this.careerKarma / 10);
408
+ }
409
+ /** The p.368 quality list, restricted to the qualities this game ships. */
410
+ get qualityNotoriety() {
411
+ const plus = ['bad-luck', 'combat-paralysis', 'sinner-criminal']
412
+ .filter(k => this.qualities.has(k)).length
413
+ + [...this.qualities].filter(k => k.startsWith('incompetent-')).length;
414
+ const minus = ['first-impression', 'lucky'].filter(k => this.qualities.has(k)).length;
415
+ return plus - minus;
416
+ }
417
+ get notoriety() {
418
+ return Math.max(0, this.qualityNotoriety + this._notorietyReasons.length);
419
+ }
420
+ get publicAwareness() {
421
+ return Math.floor((this.streetCred + this.notoriety) / 3)
422
+ + (this.qualities.has('distinctive-style') ? 1 : 0);
423
+ }
424
+ /**
425
+ * The dice a NAME is worth in shadow-side social work (haggle,
426
+ * persuade): cred minus the stories that undercut it. RAW makes cred a
427
+ * social LIMIT bonus; with no limit system here, net-rep dice carry
428
+ * the same idea -- and notoriety eating cred keeps it self-balancing.
429
+ */
430
+ get socialRep() {
431
+ return Math.max(0, this.streetCred - this.notoriety);
432
+ }
230
433
  // ======================== Skills (see SkillName) ========================
231
434
  /** Explicit per-skill ratings; anything absent resolves via skillRating(). */
232
435
  skills = {};
@@ -241,6 +444,10 @@ export class Player extends AbstractPlayer {
241
444
  * everything else is 0 (unskilled).
242
445
  */
243
446
  skillRating(name) {
447
+ // Incompetent (p.81): the skill isn't low, it's ABSENT -- pinned to 0
448
+ // no matter what the sheet or the fallback would say.
449
+ if (this.qualities.has(`incompetent-${name}`))
450
+ return 0;
244
451
  const explicit = this.skills[name];
245
452
  if (typeof explicit === 'number')
246
453
  return Math.max(0, explicit);
@@ -334,14 +541,21 @@ export class Player extends AbstractPlayer {
334
541
  set equipment(newEquipment) {
335
542
  this._equipment = newEquipment;
336
543
  }
544
+ // An explicit cap set through the setter wins over the strength-derived
545
+ // default -- companion shells use this (a palm-sized drone slings a
546
+ // credstick, not a strength-1 humanoid's 30kg). The setter used to
547
+ // write a field the getter never read; it was dead code.
548
+ _carryCapOverrideKg;
337
549
  get maxCarryingWeight() {
550
+ if (this._carryCapOverrideKg !== undefined)
551
+ return this._carryCapOverrideKg;
338
552
  // Strength-derived: 20 + 10/point. The old flat default (50kg) is
339
553
  // exactly what a strength-3 runner gets, so pre-sheet balance holds.
340
554
  return 20 + this.strength * 10;
341
555
  }
342
556
  set maxCarryingWeight(weight) {
343
557
  if (weight > 0) {
344
- this._maxCarryingWeight = weight;
558
+ this._carryCapOverrideKg = weight;
345
559
  }
346
560
  else {
347
561
  console.error('Max carrying weight must be positive.');
@@ -628,7 +842,22 @@ export class Player extends AbstractPlayer {
628
842
  * at everything, including not getting shot.
629
843
  */
630
844
  get woundModifier() {
631
- return -(Math.floor(this._damageTaken / 3) + Math.floor(this._stunTaken / 3));
845
+ const painEditor = this.augmentations.has('pain-editor');
846
+ // High Pain Tolerance (p.74): one box simply doesn't count toward
847
+ // modifiers -- taken off the physical track first, spilling to stun if
848
+ // there's nothing physical to ignore. RAW forbids stacking it with a
849
+ // pain editor, so the chrome wins when both are present.
850
+ const ignore = !painEditor && this.qualities.has('high-pain-tolerance') ? 1 : 0;
851
+ const dmg = Math.max(0, this._damageTaken - ignore);
852
+ const spill = Math.max(0, ignore - this._damageTaken);
853
+ const stun = Math.max(0, this._stunTaken - spill);
854
+ // Low Pain Tolerance (p.82): the -1 arrives every 2 boxes, not 3.
855
+ const step = this.qualities.has('low-pain-tolerance') ? 2 : 3;
856
+ const raw = Math.floor(dmg / step) + Math.floor(stun / step);
857
+ // A pain editor files the first wound tier under "later": the -1 never
858
+ // arrives, though everything past it still does.
859
+ const eased = painEditor ? Math.max(0, raw - 1) : raw;
860
+ return -eased;
632
861
  }
633
862
  /** Dead: the physical track is full. */
634
863
  isDown() {
@@ -680,10 +909,17 @@ export class Player extends AbstractPlayer {
680
909
  const filled = Math.min(max, this._damageTaken);
681
910
  return `${'■'.repeat(filled)}${'□'.repeat(max - filled)} (${filled}/${max})`;
682
911
  }
683
- /** Clears stun boxes; returns how many were actually recovered. */
912
+ /**
913
+ * Clears stun boxes; returns how many were actually recovered. FATIGUE
914
+ * boxes (see lockedFatigue) are untouchable until their need is met --
915
+ * SR5e p.172: fatigue damage cannot be healed while the condition
916
+ * causing it still exists. Eating/sleeping unlocks them (markFed /
917
+ * markSlept), after which they heal like any stun.
918
+ */
684
919
  healStun(boxes) {
685
920
  const before = this._stunTaken;
686
- this._stunTaken = Math.max(0, before - Math.max(0, Math.floor(boxes)));
921
+ const floor = Math.min(before, this.lockedFatigue);
922
+ this._stunTaken = Math.max(floor, before - Math.max(0, Math.floor(boxes)));
687
923
  return before - this._stunTaken;
688
924
  }
689
925
  /** The stun track, same rendering as conditionSummary(). */
@@ -702,7 +938,14 @@ export class Player extends AbstractPlayer {
702
938
  // Off the meat plane your gun is just a memory: Matrix combat runs on
703
939
  // attack programs (Logic-scaled), astral combat on raw will given
704
940
  // form (Magic-scaled). Hot-sim's deeper immersion sharpens the
705
- // programs' bite.
941
+ // programs' bite. A jumped-in drone fights with its MOUNT (armed
942
+ // airframes only -- attack.ts refuses an unarmed recon frame).
943
+ if (this.plane === 'drone') {
944
+ const quality = this.riggedDrone?.qualityBonus ?? 0;
945
+ return this.droneArmed
946
+ ? { name: `${this.riggedDrone?.name ?? 'drone'} weapon mount`, damage: 2 + quality, poolBonus: 0 }
947
+ : { name: 'no weapon mounts', damage: 0, poolBonus: 0 };
948
+ }
706
949
  if (this.plane === 'matrix') {
707
950
  const hot = this.simMode === 'hot' ? 1 : 0;
708
951
  // The deck IS the arsenal: its quality feeds program damage the way
@@ -717,19 +960,21 @@ export class Player extends AbstractPlayer {
717
960
  if (this.plane === 'astral') {
718
961
  return { name: 'astral strike', damage: 2 + Math.floor(Math.max(1, this.magic) / 2), poolBonus: 0 };
719
962
  }
720
- // Muscle drives melee: unarmed 1 + STR/2, melee weapons 2 + STR/2 +
721
- // quality (a strength-3 actor lands exactly the old flat numbers).
722
- // Firearms don't care how strong you are. A HOLSTERED weapon counts
723
- // for nothing -- until it's drawn (brandish, or the auto-draw when an
724
- // exchange opens), your hands are empty.
963
+ // Muscle drives melee, and the weapon CLASS drives everything else
964
+ // now (RAW gear pass, see Item.rangedBase/meleeBase/armorPen): a
965
+ // holdout no longer hits like a shotgun, and a knife's bite comes
966
+ // partly from ignoring armor. Quality still rides on top. A HOLSTERED
967
+ // weapon counts for nothing -- until it's drawn (brandish, or the
968
+ // auto-draw when an exchange opens), your hands are empty.
725
969
  const strengthBase = Math.floor(this.strength / 2);
726
970
  const weapon = this.weaponDrawn ? this.getCarriedWeapon() : null;
727
971
  if (!weapon) {
728
- return { name: 'bare fists', damage: 1 + strengthBase, poolBonus: 0 };
972
+ // Bone lacing turns the fists themselves into weapons.
973
+ return { name: 'bare fists', damage: 1 + strengthBase + this.augBonus('unarmed-damage'), poolBonus: 0, armorPen: 0 };
729
974
  }
730
975
  const bonus = weapon.qualityBonus;
731
- const base = weapon.category === Category.MeleeWeapon ? 2 + strengthBase : 4;
732
- return { name: weapon.name, damage: base + bonus, poolBonus: bonus };
976
+ const base = weapon.category === Category.MeleeWeapon ? weapon.meleeBase + strengthBase : weapon.rangedBase;
977
+ return { name: weapon.name, damage: base + bonus, poolBonus: bonus, armorPen: weapon.armorPen };
733
978
  }
734
979
  /** The weapon riding in a hand slot, drawn or holstered. */
735
980
  getCarriedWeapon() {
@@ -754,14 +999,28 @@ export class Player extends AbstractPlayer {
754
999
  }
755
1000
  /** Total soak dice from every equipped armor-category item. */
756
1001
  getArmorValue() {
1002
+ // RAW armor pass (p.436-437, compressed): the BODY piece carries the
1003
+ // real protection (1 + quality, +1 for jacket/plate-class weight --
1004
+ // RAW clothing 6 vs jacket 12), a helmet or shield adds its flat
1005
+ // point, and trim (boots, gloves, arm/leg pieces) is flavor, not
1006
+ // dice -- stacking four rated accessories used to out-armor the
1007
+ // jacket they hung off.
757
1008
  let total = 0;
758
1009
  for (const group of Object.values(this._equipment)) {
759
1010
  if (!group)
760
1011
  continue;
761
1012
  for (const item of Object.values(group)) {
762
- if (item && Player.ARMOR_CATEGORIES.has(item.category)) {
763
- total += 1 + item.qualityBonus;
764
- }
1013
+ if (!item || !Player.ARMOR_CATEGORIES.has(item.category))
1014
+ continue;
1015
+ if (item.category === Category.Helmet)
1016
+ total += 1;
1017
+ else if (item.category === Category.Shield)
1018
+ total += 2;
1019
+ else if (item.category === Category.Boots || item.category === Category.Gloves
1020
+ || item.category === Category.ArmArmor || item.category === Category.LegArmor)
1021
+ total += 0;
1022
+ else
1023
+ total += 1 + item.qualityBonus + (item.isHeavyArmor ? 1 : 0);
765
1024
  }
766
1025
  }
767
1026
  return total;
@@ -769,21 +1028,33 @@ export class Player extends AbstractPlayer {
769
1028
  getAttackPool() {
770
1029
  // Plane decides which attribute drives the fight: meat is muscle
771
1030
  // (Agility), the Matrix is mind (Logic, hot-sim +2), the astral is
772
- // will (Willpower + half Magic). The SKILL is per-armament now (see
1031
+ // will (Willpower + half Magic), and a jumped-in drone is GUNNERY --
1032
+ // Logic + piloting through the control rig (its +2 rides
1033
+ // augBonus('piloting')), hot-sim +1 (p.266: matrix bonuses apply to
1034
+ // vehicle actions). The SKILL is per-armament now (see
773
1035
  // activeCombatSkill / skillRating): firearms, blades, or unarmed in
774
1036
  // the meat; hacking is cybercombat; astral strikes ride unarmed (will
775
1037
  // given form). Wounds and sustaining drag everywhere.
1038
+ if (this.plane === 'drone') {
1039
+ const hot = this.simMode === 'hot' ? 1 : 0;
1040
+ return Math.max(0, this.logic + this.skillRating('piloting') + this.augBonus('piloting') + this.qualityBonus('piloting') + hot + this.woundModifier - this.sustainingPenalty);
1041
+ }
776
1042
  if (this.plane === 'matrix') {
777
1043
  const hot = this.simMode === 'hot' ? 2 : 0;
778
- return Math.max(0, this.logic + this.skillRating('hacking') + Math.floor(this.resonance / 2) + hot + this.woundModifier - this.sustainingPenalty);
1044
+ // Attack Upgrade echo (p.258): +1 on the offensive matrix pool.
1045
+ return Math.max(0, this.logic + this.skillRating('hacking') + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('hacking') + (this.hasMetamagic('attack-upgrade') ? 1 : 0) + this.woundModifier - this.sustainingPenalty);
779
1046
  }
780
1047
  if (this.plane === 'astral') {
781
1048
  return Math.max(0, this.willpower + Math.floor(this.magic / 2) + this.skillRating('unarmed') + this.woundModifier - this.sustainingPenalty);
782
1049
  }
783
1050
  // An adept's magic lives in their hands: half of it (rounded down)
784
1051
  // rides every meat-plane strike -- killing hands, improved reflexes.
785
- const adeptBonus = this.adept ? Math.floor(this.magic / 2) : 0;
786
- return Math.max(0, this.agility + this.skillRating(this.activeCombatSkill()) + adeptBonus + this.getWeaponProfile().poolBonus + this.woundModifier - this.sustainingPenalty);
1052
+ // Chrome rides here too: muscle toner on every swing, the smartlink
1053
+ // only when the drawn armament is actually a firearm.
1054
+ // Power Point (initiation, adepts): one more point of magic-as-muscle.
1055
+ const adeptBonus = this.adept ? Math.floor(this.magic / 2) + (this.hasMetamagic('power-point') ? 1 : 0) : 0;
1056
+ const smartlink = this.activeCombatSkill() === 'firearms' && this.weaponDrawn ? this.augBonus('firearms') : 0;
1057
+ return Math.max(0, this.agility + this.skillRating(this.activeCombatSkill()) + adeptBonus + this.getWeaponProfile().poolBonus + this.augBonus('attack') + smartlink + this.woundModifier - this.sustainingPenalty);
787
1058
  }
788
1059
  /**
789
1060
  * The subdual pool: bare-hands work regardless of what's in the other
@@ -791,34 +1062,44 @@ export class Player extends AbstractPlayer {
791
1062
  * See subdue.ts.
792
1063
  */
793
1064
  getUnarmedAttackPool() {
794
- const adeptBonus = this.adept ? Math.floor(this.magic / 2) : 0;
795
- return Math.max(0, this.agility + this.skillRating('unarmed') + adeptBonus + this.woundModifier - this.sustainingPenalty);
1065
+ const adeptBonus = this.adept ? Math.floor(this.magic / 2) + (this.hasMetamagic('power-point') ? 1 : 0) : 0;
1066
+ return Math.max(0, this.agility + this.skillRating('unarmed') + adeptBonus + this.augBonus('attack') + this.woundModifier - this.sustainingPenalty);
796
1067
  }
797
1068
  getDefensePool() {
798
1069
  // Reaction plus HALF intuition (rounded down) -- full SR
799
1070
  // Reaction+Intuition turned text-paced exchanges into whiff-fests, so
800
1071
  // gut feel contributes at half weight. Intuition 3 (the default) adds
801
1072
  // +1 over the old reaction-only pool. Off-plane variants swap the
802
- // reflex attribute but keep the same shape.
1073
+ // reflex attribute but keep the same shape. Jumped-in: the rigger's
1074
+ // own reflexes drive the airframe, control rig sharpening handling.
1075
+ if (this.plane === 'drone') {
1076
+ return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + this.augBonus('piloting') + this.qualityBonus('piloting') + this.woundModifier - this.sustainingPenalty);
1077
+ }
803
1078
  if (this.plane === 'matrix') {
804
1079
  const hot = this.simMode === 'hot' ? 1 : 0;
805
- return Math.max(0, this.intuition + Math.floor(this.logic / 2) + Math.floor(this.resonance / 2) + hot + this.woundModifier - this.sustainingPenalty);
1080
+ return Math.max(0, this.intuition + Math.floor(this.logic / 2) + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('matrix') + this.woundModifier - this.sustainingPenalty);
806
1081
  }
807
1082
  if (this.plane === 'astral') {
808
1083
  return Math.max(0, this.intuition + Math.floor(this.magic / 2) + this.woundModifier - this.sustainingPenalty);
809
1084
  }
810
1085
  // Combat sense: the adept's magic reads the fight before it happens.
1086
+ // Wired reflexes read it faster still.
811
1087
  const adeptBonus = this.adept ? Math.floor(this.magic / 2) : 0;
812
- return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + adeptBonus + this.woundModifier - this.sustainingPenalty);
1088
+ return Math.max(0, this.reaction + Math.floor(this.intuition / 2) + adeptBonus + this.augBonus('defense') + this.woundModifier - this.sustainingPenalty);
813
1089
  }
814
1090
  getSoakPool() {
815
1091
  // Matrix: firewall -- Logic plus the DECK's quality (your deck is your
816
1092
  // armor); a technomancer's firewall is Willpower plus half resonance
817
1093
  // (their mind guards itself). Astral: pure Willpower; worn armor is
818
- // meat left behind.
1094
+ // meat left behind. Drone: the HULL soaks -- airframe body scaled by
1095
+ // build quality; the rider's armor is streets away with their body.
1096
+ if (this.plane === 'drone') {
1097
+ return Math.max(0, 4 + 2 * (this.riggedDrone?.qualityBonus ?? 0));
1098
+ }
819
1099
  if (this.plane === 'matrix') {
820
1100
  if (this.isTechnomancer()) {
821
- return Math.max(0, this.willpower + Math.floor(this.resonance / 2));
1101
+ // Firewall Upgrade echo (p.258): the living persona hardens.
1102
+ return Math.max(0, this.willpower + Math.floor(this.resonance / 2) + (this.hasMetamagic('firewall-upgrade') ? 1 : 0));
822
1103
  }
823
1104
  const deck = this.activeDeck ?? this.getCyberdeck();
824
1105
  return Math.max(0, this.logic + (deck?.qualityBonus ?? 0));
@@ -826,7 +1107,7 @@ export class Player extends AbstractPlayer {
826
1107
  if (this.plane === 'astral') {
827
1108
  return Math.max(0, this.willpower);
828
1109
  }
829
- return Math.max(0, this.body + this.getArmorValue() + this._sustainedArmor);
1110
+ return Math.max(0, this.body + this.getArmorValue() + this._sustainedArmor + this.augBonus('soak') + this.qualityBonus('soak'));
830
1111
  }
831
1112
  /**
832
1113
  * Initiative: Reaction + Intuition (straight SR), plus the AR edge from
@@ -838,18 +1119,34 @@ export class Player extends AbstractPlayer {
838
1119
  getInitiativePool() {
839
1120
  // Matrix speed is thought-speed: Logic + Intuition, hot-sim +2 (the
840
1121
  // whole point of running hot). Astral forms move at the speed of
841
- // intention: Intuition doubled.
1122
+ // intention: Intuition doubled. Jumped-in rides VR reflexes (p.266:
1123
+ // cold +2, hot +3 -- the engine's flattened take on the extra dice).
1124
+ //
1125
+ // NO sustaining penalty here (RAW spell pass): initiative is a score,
1126
+ // not a TEST -- holding a spell slows what you DO, never when you get
1127
+ // to do it. (This also lets Increase Reflexes actually work: its +2
1128
+ // used to be eaten whole by its own -2 sustaining burden.) Wounds
1129
+ // still slow the draw -- pain is not a spell.
1130
+ if (this.plane === 'drone') {
1131
+ const vr = this.simMode === 'hot' ? 3 : 2;
1132
+ return Math.max(0, this.reaction + this.intuition + vr + this.woundModifier);
1133
+ }
842
1134
  if (this.plane === 'matrix') {
843
- const hot = this.simMode === 'hot' ? 2 : 0;
844
- return Math.max(0, this.logic + this.intuition + Math.floor(this.resonance / 2) + hot + this.woundModifier - this.sustainingPenalty);
1135
+ // Overclocking echo (p.258): +1 while running hot -- the living
1136
+ // persona accelerated past spec.
1137
+ const hot = this.simMode === 'hot' ? 2 + (this.hasMetamagic('overclocking') ? 1 : 0) : 0;
1138
+ return Math.max(0, this.logic + this.intuition + Math.floor(this.resonance / 2) + hot + this.augBonus('matrix') + this.qualityBonus('matrix') + this.woundModifier);
845
1139
  }
846
1140
  if (this.plane === 'astral') {
847
- return Math.max(0, this.intuition * 2 + this.woundModifier - this.sustainingPenalty);
1141
+ return Math.max(0, this.intuition * 2 + this.woundModifier);
848
1142
  }
849
1143
  const link = this._equipment.head?.commlink;
850
1144
  // AR needs a link that's on you, broadcasting, and not hacked dark.
851
1145
  const arBonus = link && !this.runningSilent && !this.arOffline ? link.qualityBonus : 0;
852
- return Math.max(0, this.reaction + this.intuition + arBonus + this.woundModifier - this.sustainingPenalty);
1146
+ // Increase Reflexes (sustained, cast.ts): the spellwork equivalent of
1147
+ // wired reflexes, for as long as the caster holds it.
1148
+ const spellReflexes = this.sustainedReflexes ? 2 : 0;
1149
+ return Math.max(0, this.reaction + this.intuition + arBonus + this.augBonus('initiative') + spellReflexes + this.woundModifier);
853
1150
  }
854
1151
  /**
855
1152
  * If an edge boost is armed (see commands/edge.ts), consume it: returns
@@ -863,11 +1160,60 @@ export class Player extends AbstractPlayer {
863
1160
  this.edgeBoostArmed = false;
864
1161
  return this.edge;
865
1162
  }
866
- /** New job, fresh luck -- and a fresh seat at the negotiating table. */
867
- refreshEdge() {
868
- this.edgeRemaining = this.edge;
1163
+ // A comfortable home sharpens the luck: Middle+ lifestyles bank a bonus
1164
+ // edge each job (set by Game from the active tier; 0 for NPCs).
1165
+ lifestyleEdgeBonus = 0;
1166
+ // ==================== Needs (SR5e p.56 / p.172) ====================
1167
+ // Edge comes back from "a fulfilling meal and a good night's sleep" --
1168
+ // so eating (use.ts) and sleeping (rest.ts, at a Residence) are tracked
1169
+ // per JOB-CYCLE and cashed in at departure (Game.applyDepartureNeeds).
1170
+ // Ignore a need across multiple cycles and RAW fatigue stacks: 1S, 2S,
1171
+ // 3S... resisted Body + Willpower -- and fatigue stun is LOCKED (won't
1172
+ // heal by rest, stim, food's own recovery, or the doc) until the need
1173
+ // that caused it is finally met.
1174
+ // Sessions start fresh off your own cot: fed, slept, sharp.
1175
+ ateThisCycle = true;
1176
+ sleptThisCycle = true;
1177
+ hungerStreak = 0;
1178
+ sleepStreak = 0;
1179
+ _hungerFatigue = 0;
1180
+ _sleepFatigue = 0;
1181
+ /** Stun boxes no recovery can touch until their need is met. */
1182
+ get lockedFatigue() {
1183
+ return this._hungerFatigue + this._sleepFatigue;
1184
+ }
1185
+ /** A real meal: hunger resets and hunger-fatigue unlocks (heals normally now). */
1186
+ markFed() {
1187
+ this.ateThisCycle = true;
1188
+ this.hungerStreak = 0;
1189
+ this._hungerFatigue = 0;
1190
+ }
1191
+ /** A real sleep (a bed, not a jobsite corner): sleep debt resets and unlocks. */
1192
+ markSlept() {
1193
+ this.sleptThisCycle = true;
1194
+ this.sleepStreak = 0;
1195
+ this._sleepFatigue = 0;
1196
+ }
1197
+ /** Records freshly-taken stun as need-locked fatigue. */
1198
+ addLockedFatigue(kind, boxes) {
1199
+ if (kind === 'hunger')
1200
+ this._hungerFatigue += Math.max(0, boxes);
1201
+ else
1202
+ this._sleepFatigue += Math.max(0, boxes);
1203
+ }
1204
+ /**
1205
+ * New job bookkeeping -- the boost and the once-per-job haggle always
1206
+ * reset, but the edge POOL only refills when the runner left fed and
1207
+ * slept (SR5e p.56: a fulfilling meal and a good night's sleep).
1208
+ */
1209
+ refreshEdge(wellRested = true) {
1210
+ if (wellRested) {
1211
+ this.edgeRemaining = this.edge + this.lifestyleEdgeBonus;
1212
+ }
869
1213
  this.edgeBoostArmed = false;
870
1214
  this.haggledThisJob = false;
1215
+ // A new job is a new room to read: First Impression re-arms (p.74).
1216
+ this._firstImpressionUsed = false;
871
1217
  }
872
1218
  // ======================== Magic (see commands/cast.ts) ========================
873
1219
  // Two roads to casting. GEAR-DRIVEN: what a mundane can cast comes from
@@ -900,11 +1246,25 @@ export class Player extends AbstractPlayer {
900
1246
  setSustainedArmor(bonus) {
901
1247
  this._sustainedArmor = Math.max(0, Math.floor(bonus));
902
1248
  }
1249
+ // The other sustained spells (RAW gear/spell pass): invisibility bends
1250
+ // light around the bearer (+3 on sneak contests, npc.ts), increase
1251
+ // reflexes runs the nerves hot (+2 initiative). Each holds the standard
1252
+ // -2 sustaining burden; cast again to release (cast.ts).
1253
+ sustainedInvisibility = false;
1254
+ sustainedReflexes = false;
903
1255
  get sustainingPenalty() {
904
1256
  // Each sustained effect costs -2 dice across the board: the armor
905
- // spell, and open astral senses (see commands/assense.ts). They stack
906
- // -- holding both is walking around half-blind.
907
- return (this._sustainedArmor > 0 ? 2 : 0) + (this.astralPerceiving ? 2 : 0);
1257
+ // spell, invisibility, hot reflexes, and open astral senses (see
1258
+ // commands/assense.ts). They stack -- holding two is walking around
1259
+ // half-blind. Dumpshock disorientation (p.229) rides the same rail.
1260
+ // QUICKENING (metamagic, p.326 adapted): the armor spell anchors into
1261
+ // the world and sustains itself -- its -2 simply never bills.
1262
+ const armorCost = this._sustainedArmor > 0 && !this.hasMetamagic('quickening') ? 2 : 0;
1263
+ return armorCost
1264
+ + (this.sustainedInvisibility ? 2 : 0)
1265
+ + (this.sustainedReflexes ? 2 : 0)
1266
+ + (this.astralPerceiving ? 2 : 0)
1267
+ + (this.dumpshocked ? 2 : 0);
908
1268
  }
909
1269
  // Astral perception (see commands/assense.ts): senses held OPEN as a
910
1270
  // sustained state -- passive aura reads, at -2 dice to everything else.
@@ -915,9 +1275,40 @@ export class Player extends AbstractPlayer {
915
1275
  // Sneaking-vs-Perception rolls per witness (NPC.see). Broken by any
916
1276
  // loud act -- attacking, casting, speaking, brandishing, calling.
917
1277
  sneaking = false;
1278
+ // ==================== Jumped-in rigging (commands/jump.ts) ====================
1279
+ // The drone currently being ridden while plane === 'drone': the item
1280
+ // whose hull track (Item.droneDamage) eats what would hit the rider,
1281
+ // whose quality feeds the drone pools, and whose destruction is a
1282
+ // DUMPSHOCK (see planes.ts leaveDrone).
1283
+ riggedDrone;
1284
+ /** Whether the ridden drone carries a weapon mount (name/description duck-typing). */
1285
+ get droneArmed() {
1286
+ const d = this.riggedDrone;
1287
+ if (!d)
1288
+ return false;
1289
+ return /armed|combat|gun|roto|assault|sentry|weapon/i.test(`${d.name} ${d.description ?? ''}`);
1290
+ }
1291
+ // Dumpshock disorientation (SR5e p.229): -2 on everything for
1292
+ // (10 - Willpower) minutes after a forced dump, wall-clock -- the
1293
+ // penalty rides sustainingPenalty so every pool already feels it.
1294
+ dumpshockedUntil = 0;
1295
+ get dumpshocked() {
1296
+ return Date.now() < this.dumpshockedUntil;
1297
+ }
918
1298
  // One haggle per job (see commands/haggle.ts); reset with the edge pool
919
1299
  // on each new job (refreshEdge).
920
1300
  haggledThisJob = false;
1301
+ // A drink in you (see use.ts): +1 die on the next social roll --
1302
+ // persuade, haggle, bluff -- then it's gone. A second drink before it's
1303
+ // spent doesn't stack, it just costs a stun box ("one too many").
1304
+ liquidCourage = false;
1305
+ /** Spends the drink: returns its bonus die for the roll happening NOW. */
1306
+ consumeLiquidCourage() {
1307
+ if (!this.liquidCourage)
1308
+ return 0;
1309
+ this.liquidCourage = false;
1310
+ return 1;
1311
+ }
921
1312
  // Where this actor just came from and the direction BACK -- the HUD's
922
1313
  // breadcrumb (see game.ts updateStatus / go.ts). Big scenes (the hub's
923
1314
  // 18 rooms) made "which way did I come in" a real question. Cleared on