@opencharacter/swn-revised 0.0.0-stage → 0.1.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 (302) hide show
  1. package/LICENSE +147 -0
  2. package/README.md +128 -2
  3. package/content/armor/armored-undersuit.yaml +8 -0
  4. package/content/armor/armored-vacc-suit.yaml +8 -0
  5. package/content/armor/assault-suit.yaml +8 -0
  6. package/content/armor/combat-field-uniform.yaml +8 -0
  7. package/content/armor/deflector-array.yaml +8 -0
  8. package/content/armor/field-emitter-panoply.yaml +9 -0
  9. package/content/armor/force-pavis.yaml +9 -0
  10. package/content/armor/heavy-primitive-armor.yaml +8 -0
  11. package/content/armor/icarus-harness.yaml +8 -0
  12. package/content/armor/light-primitive-armor.yaml +8 -0
  13. package/content/armor/medium-primitive-armor.yaml +8 -0
  14. package/content/armor/secure-clothing.yaml +8 -0
  15. package/content/armor/security-armor.yaml +8 -0
  16. package/content/armor/shield.yaml +9 -0
  17. package/content/armor/storm-armor.yaml +9 -0
  18. package/content/armor/vestimentum.yaml +8 -0
  19. package/content/armor/warpaint.yaml +8 -0
  20. package/content/armor/woven-body-armor.yaml +8 -0
  21. package/content/backgrounds/barbarian.yaml +20 -0
  22. package/content/backgrounds/clergy.yaml +20 -0
  23. package/content/backgrounds/courtesan.yaml +20 -0
  24. package/content/backgrounds/criminal.yaml +20 -0
  25. package/content/backgrounds/dilettante.yaml +20 -0
  26. package/content/backgrounds/entertainer.yaml +20 -0
  27. package/content/backgrounds/merchant.yaml +20 -0
  28. package/content/backgrounds/noble.yaml +20 -0
  29. package/content/backgrounds/official.yaml +20 -0
  30. package/content/backgrounds/peasant.yaml +20 -0
  31. package/content/backgrounds/physician.yaml +20 -0
  32. package/content/backgrounds/pilot.yaml +20 -0
  33. package/content/backgrounds/politician.yaml +20 -0
  34. package/content/backgrounds/scholar.yaml +20 -0
  35. package/content/backgrounds/soldier.yaml +20 -0
  36. package/content/backgrounds/spacer.yaml +20 -0
  37. package/content/backgrounds/technician.yaml +20 -0
  38. package/content/backgrounds/thug.yaml +20 -0
  39. package/content/backgrounds/vagabond.yaml +20 -0
  40. package/content/backgrounds/worker.yaml +20 -0
  41. package/content/classes/adventurer.yaml +7 -0
  42. package/content/classes/expert.yaml +14 -0
  43. package/content/classes/psychic.yaml +11 -0
  44. package/content/classes/warrior.yaml +27 -0
  45. package/content/cyberware/adrenal-suppression-pump.yaml +17 -0
  46. package/content/cyberware/bioadaptation-augments.yaml +6 -0
  47. package/content/cyberware/body-arsenal-array.yaml +6 -0
  48. package/content/cyberware/body-sculpting.yaml +6 -0
  49. package/content/cyberware/dermal-armor.yaml +8 -0
  50. package/content/cyberware/drone-control-link.yaml +6 -0
  51. package/content/cyberware/eelskin-capacitor-mesh.yaml +7 -0
  52. package/content/cyberware/gecko-anchors.yaml +6 -0
  53. package/content/cyberware/ghost-talker-transceiver.yaml +6 -0
  54. package/content/cyberware/holdout-cavity.yaml +6 -0
  55. package/content/cyberware/holoskin-emitters.yaml +6 -0
  56. package/content/cyberware/identity-submersion-trigger.yaml +6 -0
  57. package/content/cyberware/immunofiltration-system.yaml +6 -0
  58. package/content/cyberware/induced-coma-trigger.yaml +6 -0
  59. package/content/cyberware/neurointruder-alert.yaml +11 -0
  60. package/content/cyberware/panspectral-optics.yaml +6 -0
  61. package/content/cyberware/pressure-sheathing.yaml +6 -0
  62. package/content/cyberware/prosthetic-limb.yaml +6 -0
  63. package/content/cyberware/revenant-wiring.yaml +6 -0
  64. package/content/cyberware/slowtime-window.yaml +7 -0
  65. package/content/cyberware/stabilization-overrides.yaml +8 -0
  66. package/content/cyberware/tagger-nanites.yaml +7 -0
  67. package/content/cyberware/toxin-injector.yaml +7 -0
  68. package/content/cyberware/twitchlock-actuators.yaml +7 -0
  69. package/content/foci/alert.yaml +9 -0
  70. package/content/foci/armsman.yaml +9 -0
  71. package/content/foci/assassin.yaml +5 -0
  72. package/content/foci/authority.yaml +5 -0
  73. package/content/foci/close-combatant.yaml +7 -0
  74. package/content/foci/connected.yaml +5 -0
  75. package/content/foci/die-hard.yaml +7 -0
  76. package/content/foci/diplomat.yaml +10 -0
  77. package/content/foci/gunslinger.yaml +8 -0
  78. package/content/foci/hacker.yaml +10 -0
  79. package/content/foci/healer.yaml +7 -0
  80. package/content/foci/henchkeeper.yaml +7 -0
  81. package/content/foci/ironhide.yaml +7 -0
  82. package/content/foci/psychic-training.yaml +18 -0
  83. package/content/foci/savage-fray.yaml +5 -0
  84. package/content/foci/shocking-assault.yaml +8 -0
  85. package/content/foci/sniper.yaml +10 -0
  86. package/content/foci/specialist.yaml +36 -0
  87. package/content/foci/star-captain.yaml +5 -0
  88. package/content/foci/starfarer.yaml +5 -0
  89. package/content/foci/tinker.yaml +5 -0
  90. package/content/foci/unarmed-combatant.yaml +11 -0
  91. package/content/foci/unique-gift.yaml +6 -0
  92. package/content/foci/wanderer.yaml +5 -0
  93. package/content/foci/wild-psychic-talent.yaml +20 -0
  94. package/content/gear/ammo-20-rounds.yaml +9 -0
  95. package/content/gear/ammo-missile.yaml +8 -0
  96. package/content/gear/atmofilter.yaml +7 -0
  97. package/content/gear/backpack-tl0.yaml +7 -0
  98. package/content/gear/backpack-tl4.yaml +7 -0
  99. package/content/gear/bezoar.yaml +9 -0
  100. package/content/gear/binoculars-tl3.yaml +7 -0
  101. package/content/gear/binoculars-tl4.yaml +7 -0
  102. package/content/gear/bioscanner.yaml +7 -0
  103. package/content/gear/black-slab.yaml +7 -0
  104. package/content/gear/brainwave.yaml +8 -0
  105. package/content/gear/climbing-harness.yaml +7 -0
  106. package/content/gear/comm-server.yaml +7 -0
  107. package/content/gear/compad.yaml +7 -0
  108. package/content/gear/data-phase-tap.yaml +7 -0
  109. package/content/gear/data-protocol.yaml +7 -0
  110. package/content/gear/dataslab.yaml +7 -0
  111. package/content/gear/field-radio.yaml +7 -0
  112. package/content/gear/glowbug.yaml +7 -0
  113. package/content/gear/grapnel-launcher.yaml +7 -0
  114. package/content/gear/grav-chute-tl4.yaml +7 -0
  115. package/content/gear/grav-chute-tl5.yaml +7 -0
  116. package/content/gear/grav-harness.yaml +7 -0
  117. package/content/gear/hush.yaml +8 -0
  118. package/content/gear/instapanel.yaml +8 -0
  119. package/content/gear/lazarus-patch.yaml +8 -0
  120. package/content/gear/lift.yaml +11 -0
  121. package/content/gear/line-shunt.yaml +7 -0
  122. package/content/gear/low-light-goggles.yaml +7 -0
  123. package/content/gear/medkit.yaml +7 -0
  124. package/content/gear/metatool.yaml +7 -0
  125. package/content/gear/navcomp.yaml +7 -0
  126. package/content/gear/portabox.yaml +7 -0
  127. package/content/gear/postech-toolkit.yaml +7 -0
  128. package/content/gear/power-cell-a.yaml +9 -0
  129. package/content/gear/power-cell-b.yaml +8 -0
  130. package/content/gear/pressure-tent.yaml +7 -0
  131. package/content/gear/pretech-cosmetic.yaml +9 -0
  132. package/content/gear/pretech-toolkit.yaml +7 -0
  133. package/content/gear/psych.yaml +8 -0
  134. package/content/gear/rations-1-day.yaml +8 -0
  135. package/content/gear/remote-link-unit.yaml +7 -0
  136. package/content/gear/reverie.yaml +9 -0
  137. package/content/gear/rope-tl0.yaml +7 -0
  138. package/content/gear/rope-tl4.yaml +7 -0
  139. package/content/gear/scout-report.yaml +7 -0
  140. package/content/gear/solar-recharger.yaml +7 -0
  141. package/content/gear/spare-parts.yaml +8 -0
  142. package/content/gear/squeal.yaml +8 -0
  143. package/content/gear/stiletto-charge.yaml +6 -0
  144. package/content/gear/storage-unit.yaml +7 -0
  145. package/content/gear/survey-scanner.yaml +7 -0
  146. package/content/gear/survival-kit.yaml +7 -0
  147. package/content/gear/tailored-antiallergens.yaml +7 -0
  148. package/content/gear/telekinetic-generator.yaml +7 -0
  149. package/content/gear/telescoping-pole.yaml +7 -0
  150. package/content/gear/thermal-flare.yaml +7 -0
  151. package/content/gear/tightbeam-link-unit.yaml +7 -0
  152. package/content/gear/trade-goods.yaml +8 -0
  153. package/content/gear/trade-metals.yaml +8 -0
  154. package/content/gear/translator-torc.yaml +7 -0
  155. package/content/gear/tsunami.yaml +9 -0
  156. package/content/gear/vacc-fresher.yaml +7 -0
  157. package/content/gear/vacc-skin.yaml +7 -0
  158. package/content/gear/vacc-suit.yaml +7 -0
  159. package/content/partial-classes/expert.yaml +5 -0
  160. package/content/partial-classes/psychic.yaml +18 -0
  161. package/content/partial-classes/warrior.yaml +7 -0
  162. package/content/techniques/accelerated-succor.yaml +10 -0
  163. package/content/techniques/alternate-outcome.yaml +10 -0
  164. package/content/techniques/anguished-vision.yaml +10 -0
  165. package/content/techniques/burdened-apportation.yaml +10 -0
  166. package/content/techniques/cloak-powers.yaml +10 -0
  167. package/content/techniques/concert-of-minds.yaml +10 -0
  168. package/content/techniques/cursed-luck.yaml +59 -0
  169. package/content/techniques/deep-intrusion.yaml +10 -0
  170. package/content/techniques/destinys-shield.yaml +10 -0
  171. package/content/techniques/effortless-apportation.yaml +10 -0
  172. package/content/techniques/facile-mind.yaml +10 -0
  173. package/content/techniques/far-thought.yaml +10 -0
  174. package/content/techniques/flawless-mastery.yaml +10 -0
  175. package/content/techniques/force-puppetry.yaml +10 -0
  176. package/content/techniques/forced-outcome.yaml +10 -0
  177. package/content/techniques/holistic-optimization-patterning.yaml +10 -0
  178. package/content/techniques/impact-sump.yaml +10 -0
  179. package/content/techniques/impervious-pavis-of-will.yaml +10 -0
  180. package/content/techniques/intuitive-response.yaml +10 -0
  181. package/content/techniques/invincible-stand.yaml +10 -0
  182. package/content/techniques/kinetic-transversal.yaml +10 -0
  183. package/content/techniques/major-organ-restoration.yaml +10 -0
  184. package/content/techniques/mastered-succor.yaml +10 -0
  185. package/content/techniques/memory-editing.yaml +10 -0
  186. package/content/techniques/metadimensional-friction.yaml +10 -0
  187. package/content/techniques/metamorph.yaml +10 -0
  188. package/content/techniques/mindtracing.yaml +10 -0
  189. package/content/techniques/neural-trap.yaml +10 -0
  190. package/content/techniques/not-my-time.yaml +10 -0
  191. package/content/techniques/offensive-apportation.yaml +10 -0
  192. package/content/techniques/oracle.yaml +6 -0
  193. package/content/techniques/organic-purification-protocols.yaml +10 -0
  194. package/content/techniques/perceptive-dislocation.yaml +10 -0
  195. package/content/techniques/personal-apportation.yaml +6 -0
  196. package/content/techniques/pressure-field.yaml +10 -0
  197. package/content/techniques/proficient-apportation.yaml +10 -0
  198. package/content/techniques/prophecy.yaml +10 -0
  199. package/content/techniques/psychic-refinement.yaml +11 -0
  200. package/content/techniques/psychic-static.yaml +10 -0
  201. package/content/techniques/psychic-succor.yaml +39 -0
  202. package/content/techniques/psychic-tutelage.yaml +10 -0
  203. package/content/techniques/quintessential-reconstruction.yaml +10 -0
  204. package/content/techniques/reactive-telekinesis.yaml +10 -0
  205. package/content/techniques/reflex-response.yaml +10 -0
  206. package/content/techniques/remote-repair.yaml +10 -0
  207. package/content/techniques/rift-reduplication.yaml +10 -0
  208. package/content/techniques/sense-the-need.yaml +10 -0
  209. package/content/techniques/slip-field.yaml +10 -0
  210. package/content/techniques/spatial-awareness.yaml +10 -0
  211. package/content/techniques/spatial-synchrony-mandala.yaml +10 -0
  212. package/content/techniques/stutterjump.yaml +10 -0
  213. package/content/techniques/suppress-cognition.yaml +10 -0
  214. package/content/techniques/surge-momentum.yaml +10 -0
  215. package/content/techniques/suspended-manifestation.yaml +10 -0
  216. package/content/techniques/synthetic-adaptation.yaml +10 -0
  217. package/content/techniques/tangible-force-construct.yaml +10 -0
  218. package/content/techniques/telekinetic-armory.yaml +10 -0
  219. package/content/techniques/telekinetic-expertise.yaml +10 -0
  220. package/content/techniques/telekinetic-flight.yaml +10 -0
  221. package/content/techniques/telekinetic-manipulation.yaml +6 -0
  222. package/content/techniques/telekinetic-ram.yaml +10 -0
  223. package/content/techniques/telepathic-assault.yaml +10 -0
  224. package/content/techniques/telepathic-contact.yaml +6 -0
  225. package/content/techniques/teratic-overload.yaml +10 -0
  226. package/content/techniques/terminal-reflection.yaml +10 -0
  227. package/content/techniques/thermokinesis.yaml +10 -0
  228. package/content/techniques/tissue-integrity-field.yaml +10 -0
  229. package/content/techniques/transmit-thought.yaml +10 -0
  230. package/content/techniques/unity-of-thought.yaml +10 -0
  231. package/content/templates/trained-unarmed.yaml +9 -0
  232. package/content/weapons/advanced-bow.yaml +15 -0
  233. package/content/weapons/anti-vehicle-laser.yaml +18 -0
  234. package/content/weapons/combat-rifle.yaml +18 -0
  235. package/content/weapons/combat-shotgun.yaml +18 -0
  236. package/content/weapons/conversion-bow.yaml +15 -0
  237. package/content/weapons/crude-pistol.yaml +18 -0
  238. package/content/weapons/demo-charge.yaml +13 -0
  239. package/content/weapons/distortion-cannon.yaml +18 -0
  240. package/content/weapons/grenade.yaml +14 -0
  241. package/content/weapons/heavy-machine-gun.yaml +19 -0
  242. package/content/weapons/hydra-array.yaml +18 -0
  243. package/content/weapons/knife.yaml +7 -0
  244. package/content/weapons/large-advanced-weapon.yaml +13 -0
  245. package/content/weapons/large-primitive-weapon.yaml +12 -0
  246. package/content/weapons/laser-pistol.yaml +18 -0
  247. package/content/weapons/laser-rifle.yaml +19 -0
  248. package/content/weapons/mag-pistol.yaml +17 -0
  249. package/content/weapons/mag-rifle.yaml +17 -0
  250. package/content/weapons/medium-advanced-weapon.yaml +13 -0
  251. package/content/weapons/medium-primitive-weapon.yaml +12 -0
  252. package/content/weapons/monoblade-knife.yaml +7 -0
  253. package/content/weapons/monoblade-sword.yaml +4 -0
  254. package/content/weapons/musket.yaml +18 -0
  255. package/content/weapons/plasma-projector.yaml +18 -0
  256. package/content/weapons/primitive-bow.yaml +15 -0
  257. package/content/weapons/railgun.yaml +18 -0
  258. package/content/weapons/revolver.yaml +17 -0
  259. package/content/weapons/rifle.yaml +17 -0
  260. package/content/weapons/rocket-launcher.yaml +18 -0
  261. package/content/weapons/semi-auto-pistol.yaml +17 -0
  262. package/content/weapons/shear-rifle.yaml +19 -0
  263. package/content/weapons/shotgun.yaml +17 -0
  264. package/content/weapons/small-advanced-weapon.yaml +14 -0
  265. package/content/weapons/small-primitive-weapon.yaml +13 -0
  266. package/content/weapons/sniper-rifle.yaml +17 -0
  267. package/content/weapons/spear.yaml +8 -0
  268. package/content/weapons/spike-thrower.yaml +18 -0
  269. package/content/weapons/stun-baton.yaml +14 -0
  270. package/content/weapons/submachine-gun.yaml +18 -0
  271. package/content/weapons/suit-ripper.yaml +11 -0
  272. package/content/weapons/thermal-knife.yaml +7 -0
  273. package/content/weapons/thermal-pistol.yaml +18 -0
  274. package/content/weapons/thunder-gun.yaml +18 -0
  275. package/content/weapons/unarmed.yaml +8 -0
  276. package/content/weapons/void-carbine.yaml +17 -0
  277. package/content/weapons/vortex-cannon.yaml +16 -0
  278. package/content/weapons/wheatcutter-belt.yaml +16 -0
  279. package/dist/i18n/en.d.ts +3 -0
  280. package/dist/i18n/en.js +2 -0
  281. package/dist/i18n/en.json +1356 -0
  282. package/dist/index.d.ts +3 -0
  283. package/dist/index.js +2 -0
  284. package/dist/release.json +30238 -0
  285. package/i18n/en/content.yaml +394 -0
  286. package/i18n/en/foci.yaml +199 -0
  287. package/i18n/en/rules.yaml +759 -0
  288. package/i18n/en/text.yaml +249 -0
  289. package/i18n/en.yaml +1 -0
  290. package/pack.yaml +15 -0
  291. package/package.json +58 -3
  292. package/rules/advancement.yaml +461 -0
  293. package/rules/character.yaml +114 -0
  294. package/rules/checks.yaml +160 -0
  295. package/rules/combat.yaml +312 -0
  296. package/rules/core.yaml +111 -0
  297. package/rules/creation.yaml +633 -0
  298. package/rules/gear.yaml +176 -0
  299. package/rules/layout.yaml +195 -0
  300. package/rules/psionics.yaml +105 -0
  301. package/rules/start.yaml +403 -0
  302. package/rules/tags.yaml +60 -0
@@ -0,0 +1,461 @@
1
+ fields:
2
+ # Unspent skill points: any skill; non-combat, non-psychic skills only (Expert); psychic skills
3
+ # and techniques only (a psychic's point each level); the Psychic Training discipline only.
4
+ skillPoints.general: { type: integer, input: true, default: 0, min: 0 }
5
+ skillPoints.expert: { type: integer, input: true, default: 0, min: 0 }
6
+ skillPoints.psychic: { type: integer, input: true, default: 0, min: 0 }
7
+ skillPoints.training: { type: integer, input: true, default: 0, min: 0 }
8
+ # A focus's bonus skill points that didn't buy a level, saved toward that skill only.
9
+ 'skills.{skill}.credit': { type: integer, input: true, default: 0, min: 0, max: 4 }
10
+ # Attribute boosts bought with skill points, at most five.
11
+ attributes.boosts: { type: integer, input: true, default: 0, min: 0, max: 5 }
12
+
13
+ followUps:
14
+ - id: level-up
15
+ while: '!character.creating && level < 10 && xp.current >= xp.next'
16
+ action: level-up
17
+
18
+ actions:
19
+ # A new level: hit points rolled again, three skill points (one of them psychic for a psychic,
20
+ # plus the Expert's bonus point), and a focus level at 2nd, 5th, 7th and 10th level. Saving
21
+ # throws and the attack bonus follow the level.
22
+ - id: level-up
23
+ tags: [level-up]
24
+ available: '!character.creating && level < 20'
25
+ results: [advanced]
26
+ steps:
27
+ - id: start
28
+ kind: branch
29
+ cases:
30
+ - when: true
31
+ flags:
32
+ before: hp.max
33
+ next: level + 1
34
+ trained: countCopies(copy.item == @foci.psychic-training && copy.state.level >= 2) > 0
35
+ - id: level
36
+ kind: changes
37
+ changes:
38
+ - { type: set, field: level, value: flags.next }
39
+ # The new level's d6, Constitution added to each (none below 1). The new maximum is the
40
+ # total with the per-level bonuses if that is more, else one more than before.
41
+ - id: hp
42
+ kind: roll
43
+ tags: [level-up]
44
+ parts: [{ dice: (flags.next)d6 }]
45
+ - id: maximum
46
+ kind: branch
47
+ sheet: proposed
48
+ cases:
49
+ - when: true
50
+ flags:
51
+ total: >-
52
+ sumList(steps.hp.faces, max(1, x + attributes.con.mod)) + hp.perLevel * level
53
+ - id: hitPoints
54
+ kind: changes
55
+ sheet: proposed
56
+ changes:
57
+ - type: set
58
+ field: hp.rolled
59
+ value: '(flags.total > flags.before ? flags.total : flags.before + 1) - hp.perLevel * level'
60
+ - type: adjust
61
+ field: hp.current
62
+ by: '(flags.total > flags.before ? flags.total : flags.before + 1) - flags.before'
63
+ tags: [healing]
64
+ - id: focus
65
+ kind: action
66
+ when: flags.next in [2, 5, 7, 10]
67
+ use: gain-focus
68
+ params: { advancement: true }
69
+ # Psychic Training's second level: a point each level, and the levels before it when it is
70
+ # taken late.
71
+ - id: points
72
+ kind: changes
73
+ sheet: proposed
74
+ changes:
75
+ - { type: adjust, field: skillPoints.general, by: "psionics.access == 'none' ? 3 : 2" }
76
+ - { type: adjust, field: skillPoints.psychic, by: 1, when: "psionics.access != 'none'" }
77
+ - {
78
+ type: adjust,
79
+ field: skillPoints.expert,
80
+ by: class.expertPoints,
81
+ when: class.expertPoints > 0,
82
+ }
83
+ - type: adjust
84
+ when: >-
85
+ countCopies(copy.item == @foci.psychic-training && copy.state.level >= 2) > 0
86
+ field: skillPoints.training
87
+ by: 'flags.trained ? 1 : flags.next - 1'
88
+ - { id: result, kind: outcome, result: "'advanced'" }
89
+
90
+ # A focus's bonus skill taken on advancement: three skill points toward it, with any credit
91
+ # already saved toward it, buying levels in order (level-0 costs 1, level-1 2 and so on). What
92
+ # doesn't buy a level is saved as credit toward the skill; on a skill at level-4 it goes to the
93
+ # general pool.
94
+ - id: focus-skill-points
95
+ tags: [level-up]
96
+ params:
97
+ entry:
98
+ type: enum
99
+ required: true
100
+ values:
101
+ [
102
+ administer,
103
+ connect,
104
+ exert,
105
+ fix,
106
+ heal,
107
+ know,
108
+ lead,
109
+ notice,
110
+ perform,
111
+ pilot,
112
+ program,
113
+ punch,
114
+ shoot,
115
+ sneak,
116
+ stab,
117
+ survive,
118
+ talk,
119
+ trade,
120
+ work,
121
+ biopsionics,
122
+ metapsionics,
123
+ precognition,
124
+ telekinesis,
125
+ telepathy,
126
+ teleportation,
127
+ any-combat,
128
+ punch-or-stab,
129
+ any-psychic,
130
+ any-skill,
131
+ ]
132
+ random: { type: boolean, default: false }
133
+ results: [spent]
134
+ steps:
135
+ - id: start
136
+ kind: branch
137
+ sheet: proposed
138
+ cases:
139
+ - when: true
140
+ flags:
141
+ open: >-
142
+ params.entry == 'any-combat' ? ['punch', 'shoot', 'stab']
143
+ : params.entry == 'punch-or-stab' ? ['punch', 'stab']
144
+ : params.entry == 'any-psychic'
145
+ ? filterList(keysOf('psychic'), psionics.access == 'unrestricted'
146
+ || x == psionics.discipline)
147
+ : params.entry == 'any-skill' ? keysOf('general') + keysOf('combat')
148
+ : [params.entry]
149
+ - id: choose
150
+ kind: choice
151
+ when: size(flags.open) > 1
152
+ random: params.random
153
+ recommend: flags.open[0]
154
+ options: { values: { formula: flags.open } }
155
+ - id: picked
156
+ kind: branch
157
+ sheet: proposed
158
+ cases:
159
+ - when: true
160
+ flags:
161
+ skill: 'size(flags.open) == 1 ? flags.open[0] : steps.choose.picked'
162
+ from: "fieldOf('skills.{skill}.level', size(flags.open) == 1 ? flags.open[0] : steps.choose.picked)"
163
+ pool: >-
164
+ 3 + fieldOf('skills.{skill}.credit',
165
+ size(flags.open) == 1 ? flags.open[0] : steps.choose.picked)
166
+ - id: level
167
+ kind: branch
168
+ cases:
169
+ - when: true
170
+ flags: { level: flags.from, had: flags.pool - 3 }
171
+ - id: buy1
172
+ kind: branch
173
+ when: flags.level < 4 && flags.pool >= flags.level + 2
174
+ cases:
175
+ - when: true
176
+ flags: { pool: flags.pool - flags.level - 2, level: flags.level + 1 }
177
+ - id: buy2
178
+ kind: branch
179
+ when: flags.level < 4 && flags.pool >= flags.level + 2
180
+ cases:
181
+ - when: true
182
+ flags: { pool: flags.pool - flags.level - 2, level: flags.level + 1 }
183
+ - id: buy3
184
+ kind: branch
185
+ when: flags.level < 4 && flags.pool >= flags.level + 2
186
+ cases:
187
+ - when: true
188
+ flags: { pool: flags.pool - flags.level - 2, level: flags.level + 1 }
189
+ - id: spend
190
+ kind: changes
191
+ changes:
192
+ - type: set
193
+ when: flags.level != flags.from
194
+ field: { formula: "'skills.' + flags.skill + '.level'" }
195
+ value: flags.level
196
+ - type: set
197
+ when: '(flags.level >= 4 ? 0 : flags.pool) != flags.had'
198
+ field: { formula: "'skills.' + flags.skill + '.credit'" }
199
+ value: 'flags.level >= 4 ? 0 : flags.pool'
200
+ - type: adjust
201
+ when: flags.level >= 4 && flags.pool > 0
202
+ field: skillPoints.general
203
+ by: flags.pool
204
+ # A free technique for each new psychic skill level from level-1 up.
205
+ - id: technique
206
+ kind: action
207
+ when: flags.skill in keysOf('psychic') && flags.level >= 1 && flags.level > flags.from
208
+ use: pick-technique
209
+ params: { discipline: flags.skill, random: params.random }
210
+ - id: technique2
211
+ kind: action
212
+ when: flags.skill in keysOf('psychic') && flags.level >= 2 && flags.level - flags.from >= 2
213
+ use: pick-technique
214
+ params: { discipline: flags.skill, random: params.random }
215
+ - { id: result, kind: outcome, result: "'spent'", data: { skill: flags.skill } }
216
+
217
+ # A technique of the discipline at or below its skill level, free with a new skill level.
218
+ - id: pick-technique
219
+ tags: [level-up]
220
+ params:
221
+ discipline:
222
+ type: enum
223
+ required: true
224
+ values: [biopsionics, metapsionics, precognition, telekinesis, telepathy, teleportation]
225
+ random: { type: boolean, default: false }
226
+ results: [picked]
227
+ steps:
228
+ - id: technique
229
+ kind: choice
230
+ sheet: proposed
231
+ random: params.random
232
+ recommend: options[0]
233
+ options:
234
+ content:
235
+ type: technique
236
+ where: >-
237
+ item.discipline == params.discipline && !item.core
238
+ && item.level <= fieldOf('skills.{psychic}.level', params.discipline)
239
+ && countCopies(copy.item == item.id) == 0
240
+ changes:
241
+ - { type: addItem, entry: { item: picked.id } }
242
+ - { id: result, kind: outcome, result: "'picked'" }
243
+
244
+ # Spend skill points on a skill's next level. Each level costs its number plus one, and the
245
+ # higher levels need a character level: level-2 at 3rd, level-3 at 6th, level-4 at 9th.
246
+ - id: raise-skill
247
+ tags: [level-up]
248
+ available: '!character.creating'
249
+ params:
250
+ skill:
251
+ type: enum
252
+ required: true
253
+ values:
254
+ [
255
+ administer,
256
+ connect,
257
+ exert,
258
+ fix,
259
+ heal,
260
+ know,
261
+ lead,
262
+ notice,
263
+ perform,
264
+ pilot,
265
+ program,
266
+ punch,
267
+ shoot,
268
+ sneak,
269
+ stab,
270
+ survive,
271
+ talk,
272
+ trade,
273
+ work,
274
+ biopsionics,
275
+ metapsionics,
276
+ precognition,
277
+ telekinesis,
278
+ telepathy,
279
+ teleportation,
280
+ ]
281
+ results: [raised, refused]
282
+ steps:
283
+ - id: start
284
+ kind: branch
285
+ cases:
286
+ - when: true
287
+ flags:
288
+ from: "fieldOf('skills.{skill}.level', params.skill)"
289
+ credit: "fieldOf('skills.{skill}.credit', params.skill)"
290
+ psychic: params.skill in keysOf('psychic')
291
+ general: params.skill in keysOf('general')
292
+ # Credit saved toward the skill pays first.
293
+ - id: cost
294
+ kind: branch
295
+ cases:
296
+ - when: true
297
+ flags:
298
+ fromCredit: min(flags.from + 2, flags.credit)
299
+ cost: flags.from + 2 - min(flags.from + 2, flags.credit)
300
+ - id: training
301
+ kind: branch
302
+ cases:
303
+ - when: true
304
+ flags:
305
+ training: >-
306
+ params.skill == psionics.training ? min(flags.cost, skillPoints.training) : 0
307
+ - id: pools
308
+ kind: branch
309
+ cases:
310
+ - when: true
311
+ flags:
312
+ fromPsychic: 'flags.psychic ? min(flags.cost - flags.training, skillPoints.psychic) : 0'
313
+ fromExpert: 'flags.general ? min(flags.cost - flags.training, skillPoints.expert) : 0'
314
+ - id: check
315
+ kind: branch
316
+ cases:
317
+ - when: true
318
+ flags:
319
+ fromGeneral: flags.cost - flags.training - flags.fromPsychic - flags.fromExpert
320
+ allowed: >-
321
+ flags.from < 4 && level >= [1, 1, 3, 6, 9][flags.from + 1]
322
+ && (!flags.psychic || psionics.access == 'unrestricted'
323
+ || (psionics.access == 'restricted' && params.skill == psionics.discipline))
324
+ - id: pay
325
+ kind: changes
326
+ when: flags.allowed && flags.fromGeneral <= skillPoints.general
327
+ changes:
328
+ - type: adjust
329
+ when: flags.fromCredit > 0
330
+ field: { formula: "'skills.' + params.skill + '.credit'" }
331
+ by: -flags.fromCredit
332
+ - {
333
+ type: adjust,
334
+ field: skillPoints.training,
335
+ by: -flags.training,
336
+ when: flags.training > 0,
337
+ }
338
+ - {
339
+ type: adjust,
340
+ field: skillPoints.psychic,
341
+ by: -flags.fromPsychic,
342
+ when: flags.fromPsychic > 0,
343
+ }
344
+ - {
345
+ type: adjust,
346
+ field: skillPoints.expert,
347
+ by: -flags.fromExpert,
348
+ when: flags.fromExpert > 0,
349
+ }
350
+ - {
351
+ type: adjust,
352
+ field: skillPoints.general,
353
+ by: -flags.fromGeneral,
354
+ when: flags.fromGeneral > 0,
355
+ }
356
+ - { type: adjust, field: { formula: "'skills.' + params.skill + '.level'" }, by: 1 }
357
+ - id: technique
358
+ kind: action
359
+ when: >-
360
+ flags.psychic && flags.allowed && flags.fromGeneral <= skillPoints.general
361
+ && flags.from >= 0
362
+ use: pick-technique
363
+ params: { discipline: params.skill }
364
+ - id: result
365
+ kind: outcome
366
+ result: "flags.allowed && flags.fromGeneral <= skillPoints.general ? 'raised' : 'refused'"
367
+
368
+ # Spend skill points on a technique: as many as its level.
369
+ - id: learn-technique
370
+ tags: [level-up]
371
+ available: "!character.creating && psionics.access != 'none'"
372
+ params:
373
+ technique: { type: id, of: technique, required: true, where: '!item.core' }
374
+ results: [learned, refused]
375
+ steps:
376
+ - id: start
377
+ kind: choice
378
+ options: { content: { type: technique, where: item.id == params.technique } }
379
+ recommend: options[0]
380
+ - id: cost
381
+ kind: branch
382
+ cases:
383
+ - when: true
384
+ flags:
385
+ cost: steps.start.item.level
386
+ training: >-
387
+ steps.start.item.discipline == psionics.training
388
+ ? min(steps.start.item.level, skillPoints.training) : 0
389
+ - id: pools
390
+ kind: branch
391
+ cases:
392
+ - when: true
393
+ flags:
394
+ fromPsychic: min(flags.cost - flags.training, skillPoints.psychic)
395
+ - id: check
396
+ kind: branch
397
+ cases:
398
+ - when: true
399
+ flags:
400
+ fromGeneral: flags.cost - flags.training - flags.fromPsychic
401
+ allowed: >-
402
+ countCopies(copy.item == params.technique) == 0
403
+ && fieldOf('skills.{psychic}.level', steps.start.item.discipline)
404
+ >= steps.start.item.level
405
+ - id: pay
406
+ kind: changes
407
+ when: flags.allowed && flags.fromGeneral <= skillPoints.general
408
+ changes:
409
+ - {
410
+ type: adjust,
411
+ field: skillPoints.training,
412
+ by: -flags.training,
413
+ when: flags.training > 0,
414
+ }
415
+ - {
416
+ type: adjust,
417
+ field: skillPoints.psychic,
418
+ by: -flags.fromPsychic,
419
+ when: flags.fromPsychic > 0,
420
+ }
421
+ - {
422
+ type: adjust,
423
+ field: skillPoints.general,
424
+ by: -flags.fromGeneral,
425
+ when: flags.fromGeneral > 0,
426
+ }
427
+ - { type: addItem, entry: { item: params.technique } }
428
+ - id: result
429
+ kind: outcome
430
+ result: "flags.allowed && flags.fromGeneral <= skillPoints.general ? 'learned' : 'refused'"
431
+
432
+ # Spend skill points on +1 to an attribute: the first boost costs 1, the second 2 and so on,
433
+ # the third needs 3rd level, the fourth 6th and the fifth 9th.
434
+ - id: boost-attribute
435
+ tags: [level-up]
436
+ available: '!character.creating && attributes.boosts < 5'
437
+ params:
438
+ attribute: { type: enum, required: true, values: [str, dex, con, int, wis, cha] }
439
+ results: [boosted, refused]
440
+ steps:
441
+ - id: start
442
+ kind: branch
443
+ cases:
444
+ - when: true
445
+ flags:
446
+ allowed: >-
447
+ skillPoints.general >= attributes.boosts + 1
448
+ && level >= [1, 1, 3, 6, 9][attributes.boosts]
449
+ && fieldOf('attributes.{attr}.score', params.attribute) < 18
450
+ - id: pay
451
+ kind: changes
452
+ when: flags.allowed
453
+ changes:
454
+ - { type: adjust, field: skillPoints.general, by: -(attributes.boosts + 1) }
455
+ - { type: adjust, field: attributes.boosts, by: 1 }
456
+ - {
457
+ type: adjust,
458
+ field: { formula: "'attributes.' + params.attribute + '.score'" },
459
+ by: 1,
460
+ }
461
+ - { id: result, kind: outcome, result: "flags.allowed ? 'boosted' : 'refused'" }
@@ -0,0 +1,114 @@
1
+ families:
2
+ growthRow: [1, 2, 3, 4, 5, 6]
3
+ learningRow: [1, 2, 3, 4, 5, 6, 7, 8]
4
+
5
+ fields:
6
+ # Set by the class and its partial classes.
7
+ psionics.access: { type: enum, values: [none, restricted, unrestricted], formula: "'none'" }
8
+ # A restricted psychic's one discipline, picked with the Partial Psychic option.
9
+ psionics.discipline: { type: string, formula: "''" }
10
+ # Extra focus levels a new character picks: non-combat (Expert) and combat (Warrior).
11
+ class.focus.noncombat: { type: integer, formula: 0 }
12
+ class.focus.combat: { type: integer, formula: 0 }
13
+ # Extra skill points each level for non-combat, non-psychic skills (Expert).
14
+ class.expertPoints: { type: integer, formula: 0 }
15
+ # Spent for the scene: the Expert's reroll and the Warrior's luck.
16
+ scene.expertReroll: { type: boolean, input: true, default: false }
17
+ scene.warriorLuck: { type: boolean, input: true, default: false }
18
+
19
+ contentTypes:
20
+ class:
21
+ copies: { max: 1 }
22
+ # Picked, two of them, by the Adventurer.
23
+ partialClass:
24
+ copies: { max: 0 }
25
+ background:
26
+ copies: { max: 1 }
27
+ fields:
28
+ freeSkill:
29
+ type: enum
30
+ required: true
31
+ values:
32
+ &entries [
33
+ administer,
34
+ connect,
35
+ exert,
36
+ fix,
37
+ heal,
38
+ know,
39
+ lead,
40
+ notice,
41
+ perform,
42
+ pilot,
43
+ program,
44
+ punch,
45
+ shoot,
46
+ sneak,
47
+ stab,
48
+ survive,
49
+ talk,
50
+ trade,
51
+ work,
52
+ any-combat,
53
+ any-skill,
54
+ stab-or-shoot,
55
+ shoot-or-trade,
56
+ any-stat,
57
+ physical,
58
+ mental,
59
+ ]
60
+ # The quick skills besides the free one.
61
+ quick1: { type: enum, required: true, values: *entries }
62
+ quick2: { type: enum, required: true, values: *entries }
63
+ 'growth{growthRow}': { type: enum, required: true, values: *entries }
64
+ 'learning{learningRow}': { type: enum, required: true, values: *entries }
65
+ focus:
66
+ fields:
67
+ kind: { type: enum, values: [combat, noncombat, psychic, special], required: true }
68
+ # The bonus skill its first level gives; `chosen` is the skill the focus's own choice names.
69
+ skill:
70
+ type: enum
71
+ default: none
72
+ values:
73
+ [
74
+ administer,
75
+ connect,
76
+ exert,
77
+ fix,
78
+ heal,
79
+ know,
80
+ lead,
81
+ notice,
82
+ perform,
83
+ pilot,
84
+ program,
85
+ punch,
86
+ shoot,
87
+ sneak,
88
+ stab,
89
+ survive,
90
+ talk,
91
+ trade,
92
+ work,
93
+ any-combat,
94
+ punch-or-stab,
95
+ chosen,
96
+ none,
97
+ ]
98
+ maxLevel: { type: integer, default: 2, min: 1 }
99
+ # Taken more than once, each time for something else.
100
+ repeatable: { type: boolean, default: false }
101
+ state:
102
+ level: { type: integer, default: 1, min: 1, max: item.maxLevel }
103
+
104
+ constraints:
105
+ - id: creation-class-background
106
+ check: >-
107
+ !character.creating || (countCopies(copy.type == 'class') > 0
108
+ && countCopies(copy.type == 'background') > 0)
109
+ - id: creation-skill-cap
110
+ check: "!character.creating || maxFields(f.value, 'skill' in f.tags) <= 1"
111
+ data: { limit: 1 }
112
+ - id: focus-once
113
+ copies: { type: focus, where: '!item.repeatable' }
114
+ check: countCopies(copy.item == item.id) <= 1