@alpaca-software/40kdc-data 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 (155) hide show
  1. package/README.md +78 -0
  2. package/dist/bundle-schemas.d.ts +3 -0
  3. package/dist/bundle-schemas.js +137 -0
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.js +31 -0
  6. package/dist/codegen-data.d.ts +1 -0
  7. package/dist/codegen-data.js +128 -0
  8. package/dist/commands/translate.d.ts +7 -0
  9. package/dist/commands/translate.js +238 -0
  10. package/dist/commands/validate-all.d.ts +3 -0
  11. package/dist/commands/validate-all.js +20 -0
  12. package/dist/commands/validate-core.d.ts +3 -0
  13. package/dist/commands/validate-core.js +12 -0
  14. package/dist/commands/validate-enrichment.d.ts +3 -0
  15. package/dist/commands/validate-enrichment.js +12 -0
  16. package/dist/convert-faction.d.ts +45 -0
  17. package/dist/convert-faction.js +479 -0
  18. package/dist/converters/configs/adepta-sororitas.d.ts +3 -0
  19. package/dist/converters/configs/adepta-sororitas.js +70 -0
  20. package/dist/converters/configs/adeptus-astartes.d.ts +3 -0
  21. package/dist/converters/configs/adeptus-astartes.js +74 -0
  22. package/dist/converters/configs/adeptus-custodes.d.ts +3 -0
  23. package/dist/converters/configs/adeptus-custodes.js +14 -0
  24. package/dist/converters/configs/adeptus-mechanicus.d.ts +3 -0
  25. package/dist/converters/configs/adeptus-mechanicus.js +51 -0
  26. package/dist/converters/configs/aeldari.d.ts +3 -0
  27. package/dist/converters/configs/aeldari.js +79 -0
  28. package/dist/converters/configs/agents-of-the-imperium.d.ts +3 -0
  29. package/dist/converters/configs/agents-of-the-imperium.js +57 -0
  30. package/dist/converters/configs/astra-militarum.d.ts +3 -0
  31. package/dist/converters/configs/astra-militarum.js +80 -0
  32. package/dist/converters/configs/black-templars.d.ts +3 -0
  33. package/dist/converters/configs/black-templars.js +16 -0
  34. package/dist/converters/configs/blood-angels.d.ts +3 -0
  35. package/dist/converters/configs/blood-angels.js +16 -0
  36. package/dist/converters/configs/chaos-daemons.d.ts +3 -0
  37. package/dist/converters/configs/chaos-daemons.js +40 -0
  38. package/dist/converters/configs/chaos-knights.d.ts +3 -0
  39. package/dist/converters/configs/chaos-knights.js +14 -0
  40. package/dist/converters/configs/chaos-space-marines.d.ts +3 -0
  41. package/dist/converters/configs/chaos-space-marines.js +95 -0
  42. package/dist/converters/configs/crimson-fists.d.ts +3 -0
  43. package/dist/converters/configs/crimson-fists.js +16 -0
  44. package/dist/converters/configs/dark-angels.d.ts +3 -0
  45. package/dist/converters/configs/dark-angels.js +16 -0
  46. package/dist/converters/configs/death-guard.d.ts +3 -0
  47. package/dist/converters/configs/death-guard.js +30 -0
  48. package/dist/converters/configs/deathwatch.d.ts +3 -0
  49. package/dist/converters/configs/deathwatch.js +16 -0
  50. package/dist/converters/configs/drukhari.d.ts +3 -0
  51. package/dist/converters/configs/drukhari.js +51 -0
  52. package/dist/converters/configs/emperors-children.d.ts +3 -0
  53. package/dist/converters/configs/emperors-children.js +38 -0
  54. package/dist/converters/configs/genestealer-cults.d.ts +3 -0
  55. package/dist/converters/configs/genestealer-cults.js +36 -0
  56. package/dist/converters/configs/grey-knights.d.ts +3 -0
  57. package/dist/converters/configs/grey-knights.js +39 -0
  58. package/dist/converters/configs/imperial-fists.d.ts +3 -0
  59. package/dist/converters/configs/imperial-fists.js +16 -0
  60. package/dist/converters/configs/imperial-knights.d.ts +3 -0
  61. package/dist/converters/configs/imperial-knights.js +14 -0
  62. package/dist/converters/configs/iron-hands.d.ts +3 -0
  63. package/dist/converters/configs/iron-hands.js +16 -0
  64. package/dist/converters/configs/leagues-of-votann.d.ts +3 -0
  65. package/dist/converters/configs/leagues-of-votann.js +32 -0
  66. package/dist/converters/configs/necrons.d.ts +3 -0
  67. package/dist/converters/configs/necrons.js +19 -0
  68. package/dist/converters/configs/orks.d.ts +3 -0
  69. package/dist/converters/configs/orks.js +71 -0
  70. package/dist/converters/configs/raven-guard.d.ts +3 -0
  71. package/dist/converters/configs/raven-guard.js +16 -0
  72. package/dist/converters/configs/salamanders.d.ts +3 -0
  73. package/dist/converters/configs/salamanders.js +16 -0
  74. package/dist/converters/configs/space-wolves.d.ts +3 -0
  75. package/dist/converters/configs/space-wolves.js +16 -0
  76. package/dist/converters/configs/tau-empire.d.ts +3 -0
  77. package/dist/converters/configs/tau-empire.js +44 -0
  78. package/dist/converters/configs/thousand-sons.d.ts +3 -0
  79. package/dist/converters/configs/thousand-sons.js +30 -0
  80. package/dist/converters/configs/tyranids.d.ts +3 -0
  81. package/dist/converters/configs/tyranids.js +27 -0
  82. package/dist/converters/configs/ultramarines.d.ts +3 -0
  83. package/dist/converters/configs/ultramarines.js +16 -0
  84. package/dist/converters/configs/white-scars.d.ts +3 -0
  85. package/dist/converters/configs/white-scars.js +16 -0
  86. package/dist/converters/configs/world-eaters.d.ts +3 -0
  87. package/dist/converters/configs/world-eaters.js +43 -0
  88. package/dist/converters/faction-config.d.ts +53 -0
  89. package/dist/converters/faction-config.js +22 -0
  90. package/dist/converters/id-generator.d.ts +14 -0
  91. package/dist/converters/id-generator.js +65 -0
  92. package/dist/converters/keyword-filter.d.ts +26 -0
  93. package/dist/converters/keyword-filter.js +78 -0
  94. package/dist/converters/stat-parser.d.ts +22 -0
  95. package/dist/converters/stat-parser.js +84 -0
  96. package/dist/converters/view-selector.d.ts +54 -0
  97. package/dist/converters/view-selector.js +96 -0
  98. package/dist/converters/weapon-dedup.d.ts +60 -0
  99. package/dist/converters/weapon-dedup.js +120 -0
  100. package/dist/data/bundle.generated.d.ts +3 -0
  101. package/dist/data/bundle.generated.js +3 -0
  102. package/dist/data/collection.d.ts +64 -0
  103. package/dist/data/collection.js +118 -0
  104. package/dist/data/dataset.d.ts +50 -0
  105. package/dist/data/dataset.js +134 -0
  106. package/dist/data/entities.d.ts +80 -0
  107. package/dist/data/entities.js +133 -0
  108. package/dist/data/index.d.ts +59 -0
  109. package/dist/data/index.js +57 -0
  110. package/dist/data/normalize.d.ts +29 -0
  111. package/dist/data/normalize.js +37 -0
  112. package/dist/data/types.d.ts +43 -0
  113. package/dist/data/types.js +25 -0
  114. package/dist/generated.d.ts +1084 -0
  115. package/dist/generated.js +2 -0
  116. package/dist/index.d.ts +3 -0
  117. package/dist/index.js +7 -0
  118. package/dist/known-support-10e.d.ts +31 -0
  119. package/dist/known-support-10e.js +113 -0
  120. package/dist/port-10e-faction.d.ts +52 -0
  121. package/dist/port-10e-faction.js +413 -0
  122. package/dist/report.d.ts +3 -0
  123. package/dist/report.js +31 -0
  124. package/dist/schema-loader.d.ts +15 -0
  125. package/dist/schema-loader.js +79 -0
  126. package/dist/validate.d.ts +21 -0
  127. package/dist/validate.js +124 -0
  128. package/package.json +77 -0
  129. package/schemas/$defs/common.schema.json +86 -0
  130. package/schemas/$defs/game-version-ref.schema.json +11 -0
  131. package/schemas/core/deployment-pattern.schema.json +102 -0
  132. package/schemas/core/detachment.schema.json +56 -0
  133. package/schemas/core/enhancement.schema.json +46 -0
  134. package/schemas/core/faction.schema.json +29 -0
  135. package/schemas/core/force-disposition.schema.json +22 -0
  136. package/schemas/core/game-version.schema.json +20 -0
  137. package/schemas/core/leader-attachment.schema.json +18 -0
  138. package/schemas/core/mission-matchup.schema.json +25 -0
  139. package/schemas/core/mission.schema.json +42 -0
  140. package/schemas/core/roster.schema.json +203 -0
  141. package/schemas/core/secondary-card.schema.json +195 -0
  142. package/schemas/core/stratagem.schema.json +58 -0
  143. package/schemas/core/terrain-layout.schema.json +135 -0
  144. package/schemas/core/unit-composition.schema.json +38 -0
  145. package/schemas/core/unit.schema.json +125 -0
  146. package/schemas/core/wargear-option.schema.json +47 -0
  147. package/schemas/core/weapon.schema.json +56 -0
  148. package/schemas/enrichment/ability-dsl/ability.schema.json +60 -0
  149. package/schemas/enrichment/ability-dsl/condition.schema.json +48 -0
  150. package/schemas/enrichment/ability-dsl/effect.schema.json +145 -0
  151. package/schemas/enrichment/ability-dsl/scope.schema.json +12 -0
  152. package/schemas/enrichment/interaction-flag.schema.json +17 -0
  153. package/schemas/enrichment/phase-mapping.schema.json +14 -0
  154. package/schemas/enrichment/resource-pool.schema.json +36 -0
  155. package/schemas/enrichment/timing-flag.schema.json +28 -0
@@ -0,0 +1,135 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/core/terrain-layout.schema.json",
4
+ "title": "Terrain Layout",
5
+ "description": "A recommended arrangement of terrain pieces on the board, independent of the deployment map (a deployment-pattern references the layouts it recommends via recommended_terrain_layout_ids). Geometry is the source of truth; the GW standard piece templates are expressed as explicit footprints, with an optional descriptive `template` label. Footprints are deliberately open (not enum-locked) — the launch catalog and its size are unconfirmed, so this models any shape rather than a fixed set. No layout data is authored yet.",
6
+ "type": "object",
7
+ "$defs": {
8
+ "footprint": {
9
+ "description": "A terrain piece's 2D footprint, relative to the piece's `position`. Axis-aligned rectangle, right triangle (right angle at the local origin, legs along +x/+y), or an explicit polygon. GW's standard templates (e.g. 7\"×11.5\" rectangles, 8\"×11.5\" right triangles, 6\"×4\" rectangles, 10\"×2.5\" and 6\"×2\" lines) are all expressible here; lines are thin rectangles.",
10
+ "oneOf": [
11
+ {
12
+ "type": "object",
13
+ "properties": {
14
+ "type": { "const": "rectangle" },
15
+ "width": { "type": "number", "exclusiveMinimum": 0 },
16
+ "height": { "type": "number", "exclusiveMinimum": 0 }
17
+ },
18
+ "required": ["type", "width", "height"],
19
+ "additionalProperties": false
20
+ },
21
+ {
22
+ "type": "object",
23
+ "properties": {
24
+ "type": { "const": "right-triangle" },
25
+ "width": { "type": "number", "exclusiveMinimum": 0 },
26
+ "height": { "type": "number", "exclusiveMinimum": 0 }
27
+ },
28
+ "required": ["type", "width", "height"],
29
+ "additionalProperties": false
30
+ },
31
+ {
32
+ "type": "object",
33
+ "properties": {
34
+ "type": { "const": "polygon" },
35
+ "points": {
36
+ "type": "array",
37
+ "items": { "$ref": "../defs/common.schema.json#/$defs/vec2" },
38
+ "minItems": 3
39
+ }
40
+ },
41
+ "required": ["type", "points"],
42
+ "additionalProperties": false
43
+ }
44
+ ]
45
+ },
46
+ "terrain-area-keyword": {
47
+ "type": "string",
48
+ "enum": ["obscuring", "hidden", "plunging-fire"],
49
+ "description": "An 11e terrain-area keyword. Confirmed launch set; extend as further keywords publish on dataslate."
50
+ },
51
+ "piece": {
52
+ "type": "object",
53
+ "description": "One terrain feature placed on the board.",
54
+ "properties": {
55
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
56
+ "footprint": { "$ref": "#/$defs/footprint" },
57
+ "position": {
58
+ "$ref": "../defs/common.schema.json#/$defs/vec2",
59
+ "description": "Board-inch placement of the footprint's local origin."
60
+ },
61
+ "rotation_degrees": {
62
+ "type": "number",
63
+ "minimum": 0,
64
+ "exclusiveMaximum": 360,
65
+ "description": "Clockwise rotation of the footprint about `position`. Absent or 0 means axis-aligned."
66
+ },
67
+ "template": {
68
+ "type": "string",
69
+ "minLength": 1,
70
+ "maxLength": 64,
71
+ "description": "Optional descriptive label for the GW standard template this piece uses (e.g. 'large-ruin', 'long-wall'). Free-form, not enum-locked — the geometry in `footprint` is authoritative."
72
+ },
73
+ "height_inches": {
74
+ "type": "number",
75
+ "minimum": 0,
76
+ "description": "Height of the piece in inches. Gates Plunging Fire (a piece 3\" or taller confers +1 BS on ground-level targets)."
77
+ },
78
+ "terrain_area_keywords": {
79
+ "type": "array",
80
+ "uniqueItems": true,
81
+ "description": "Terrain-area keywords this piece's area carries.",
82
+ "items": { "$ref": "#/$defs/terrain-area-keyword" }
83
+ },
84
+ "link_group": {
85
+ "type": "string",
86
+ "minLength": 1,
87
+ "maxLength": 64,
88
+ "description": "Pieces sharing a `link_group` value are linked terrain — treated as a single terrain feature (and, where an objective sits among them, a single objective)."
89
+ },
90
+ "is_objective": {
91
+ "type": "boolean",
92
+ "default": false,
93
+ "description": "Whether this piece carries an objective marker."
94
+ },
95
+ "objective": {
96
+ "type": "object",
97
+ "description": "Objective-marker metadata. Only meaningful when `is_objective` is true.",
98
+ "properties": {
99
+ "position": {
100
+ "$ref": "../defs/common.schema.json#/$defs/vec2",
101
+ "description": "Board-inch position of the marker. Absent means the piece's `position`."
102
+ },
103
+ "control_range_inches": {
104
+ "type": "number",
105
+ "exclusiveMinimum": 0,
106
+ "description": "Range from the marker within which models contribute to control."
107
+ }
108
+ },
109
+ "additionalProperties": false
110
+ }
111
+ },
112
+ "required": ["footprint", "position"],
113
+ "additionalProperties": false
114
+ }
115
+ },
116
+ "properties": {
117
+ "id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
118
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
119
+ "source": {
120
+ "type": "string",
121
+ "minLength": 1,
122
+ "maxLength": 64,
123
+ "description": "Mission pack or source the layout originates from."
124
+ },
125
+ "description": { "type": "string" },
126
+ "pieces": {
127
+ "type": "array",
128
+ "description": "Terrain pieces composing the layout. May be empty while a layout is registered by name ahead of its confirmed geometry.",
129
+ "items": { "$ref": "#/$defs/piece" }
130
+ },
131
+ "game_version": { "$ref": "../defs/game-version-ref.schema.json" }
132
+ },
133
+ "required": ["id", "name", "game_version"],
134
+ "additionalProperties": false
135
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/core/unit-composition.schema.json",
4
+ "title": "Unit Composition",
5
+ "description": "Describes the internal model-type breakdown of a unit.",
6
+ "type": "object",
7
+ "properties": {
8
+ "unit_id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
9
+ "models": {
10
+ "type": "array",
11
+ "items": {
12
+ "type": "object",
13
+ "properties": {
14
+ "name": { "type": "string", "minLength": 1 },
15
+ "profile_name": {
16
+ "oneOf": [
17
+ { "type": "string", "minLength": 1 },
18
+ { "type": "null" }
19
+ ]
20
+ },
21
+ "min": { "type": "integer", "minimum": 0 },
22
+ "max": { "type": "integer", "minimum": 1 },
23
+ "default_weapon_ids": {
24
+ "type": "array",
25
+ "items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" }
26
+ },
27
+ "is_leader_model": { "type": "boolean", "default": false }
28
+ },
29
+ "required": ["name", "min", "max"],
30
+ "additionalProperties": false
31
+ },
32
+ "minItems": 1
33
+ },
34
+ "game_version": { "$ref": "../defs/game-version-ref.schema.json" }
35
+ },
36
+ "required": ["unit_id", "models", "game_version"],
37
+ "additionalProperties": false
38
+ }
@@ -0,0 +1,125 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/core/unit.schema.json",
4
+ "title": "Unit",
5
+ "description": "A unit datasheet entry with stat profiles and point costs.",
6
+ "type": "object",
7
+ "properties": {
8
+ "id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
9
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
10
+ "faction_id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
11
+ "role": {
12
+ "type": "string",
13
+ "enum": ["character", "battleline", "dedicated-transport", "fortification", "allied", "epic-hero"],
14
+ "description": "Battlefield role from the datasheet header. Unit types (Infantry, Vehicle, etc.) belong in keywords."
15
+ },
16
+ "attachment_role": {
17
+ "oneOf": [
18
+ { "enum": ["leader", "support"] },
19
+ { "type": "null" }
20
+ ],
21
+ "description": "Character attachment role (11e). 'support' implies the unit is only legal when attached to a host unit (cannot be taken solo); 'leader' is valid as a standalone list entry. null/absent for non-attaching units."
22
+ },
23
+ "profiles": {
24
+ "type": "array",
25
+ "items": {
26
+ "type": "object",
27
+ "properties": {
28
+ "name": { "type": "string", "description": "Profile name (e.g., 'Wounded' for degrading)" },
29
+ "M": { "$ref": "../defs/common.schema.json#/$defs/stat-value" },
30
+ "T": { "type": "integer", "minimum": 1 },
31
+ "W": { "type": "integer", "minimum": 1 },
32
+ "Sv": { "type": "integer", "minimum": 2, "maximum": 7 },
33
+ "invuln_sv": {
34
+ "oneOf": [
35
+ { "type": "integer", "minimum": 2, "maximum": 6 },
36
+ { "type": "null" }
37
+ ]
38
+ },
39
+ "Ld": { "type": "integer", "minimum": 2, "maximum": 10 },
40
+ "OC": { "type": "integer", "minimum": 0 }
41
+ },
42
+ "required": ["M", "T", "W", "Sv", "Ld", "OC"]
43
+ },
44
+ "minItems": 1
45
+ },
46
+ "points": {
47
+ "type": "array",
48
+ "items": {
49
+ "type": "object",
50
+ "properties": {
51
+ "models": { "type": "integer", "minimum": 1 },
52
+ "cost": { "type": "integer", "minimum": 0 }
53
+ },
54
+ "required": ["models", "cost"]
55
+ }
56
+ },
57
+ "points_provisional": {
58
+ "type": "boolean",
59
+ "default": false,
60
+ "description": "True when point costs are carried over provisionally (e.g. seeded from a prior edition during migration) and not yet confirmed against the current dataslate."
61
+ },
62
+ "keywords": { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
63
+ "faction_keywords": { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
64
+ "base_size_mm": {
65
+ "oneOf": [
66
+ {
67
+ "type": "object",
68
+ "properties": {
69
+ "shape": { "enum": ["round", "oval"] },
70
+ "diameter": { "type": "number" },
71
+ "width": { "type": "number" },
72
+ "length": { "type": "number" }
73
+ },
74
+ "required": ["shape"]
75
+ },
76
+ { "type": "null" }
77
+ ]
78
+ },
79
+ "model_count": {
80
+ "type": "object",
81
+ "properties": {
82
+ "min": { "type": "integer", "minimum": 1 },
83
+ "max": { "type": "integer", "minimum": 1 }
84
+ },
85
+ "required": ["min", "max"]
86
+ },
87
+ "weapon_ids": {
88
+ "type": "array",
89
+ "items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" }
90
+ },
91
+ "ability_ids": {
92
+ "type": "array",
93
+ "items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" }
94
+ },
95
+ "transport_capacity": {
96
+ "oneOf": [
97
+ {
98
+ "type": "object",
99
+ "properties": {
100
+ "capacity": { "type": "integer", "minimum": 1 },
101
+ "keyword_restrictions": {
102
+ "oneOf": [
103
+ { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
104
+ { "type": "null" }
105
+ ]
106
+ },
107
+ "exclusion_keywords": {
108
+ "oneOf": [
109
+ { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
110
+ { "type": "null" }
111
+ ]
112
+ }
113
+ },
114
+ "required": ["capacity"],
115
+ "additionalProperties": false
116
+ },
117
+ { "type": "null" }
118
+ ]
119
+ },
120
+ "game_version": { "$ref": "../defs/game-version-ref.schema.json" },
121
+ "is_legend": { "type": "boolean", "default": false }
122
+ },
123
+ "required": ["id", "name", "faction_id", "profiles", "game_version"],
124
+ "additionalProperties": false
125
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/core/wargear-option.schema.json",
4
+ "title": "Wargear Option",
5
+ "description": "A weapon substitution option available to models within a unit.",
6
+ "type": "object",
7
+ "properties": {
8
+ "id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
9
+ "unit_id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
10
+ "model_constraint": {
11
+ "oneOf": [
12
+ {
13
+ "type": "object",
14
+ "properties": {
15
+ "model_name": { "type": "string", "minLength": 1 },
16
+ "per_n_models": { "type": "integer", "minimum": 1 },
17
+ "max_count": { "type": "integer", "minimum": 1 }
18
+ },
19
+ "additionalProperties": false
20
+ },
21
+ { "type": "null" }
22
+ ]
23
+ },
24
+ "replaces": {
25
+ "type": "array",
26
+ "items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
27
+ "minItems": 1,
28
+ "description": "Weapon IDs being removed"
29
+ },
30
+ "replacement": {
31
+ "type": "array",
32
+ "items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
33
+ "minItems": 1,
34
+ "description": "Weapon IDs being added"
35
+ },
36
+ "is_free": { "type": "boolean", "default": true },
37
+ "additional_cost": {
38
+ "oneOf": [
39
+ { "type": "integer", "minimum": 0 },
40
+ { "type": "null" }
41
+ ]
42
+ },
43
+ "game_version": { "$ref": "../defs/game-version-ref.schema.json" }
44
+ },
45
+ "required": ["id", "unit_id", "replaces", "replacement", "game_version"],
46
+ "additionalProperties": false
47
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/core/weapon.schema.json",
4
+ "title": "Weapon",
5
+ "description": "A weapon entry with one or more stat profiles (e.g., standard and overcharge modes).",
6
+ "type": "object",
7
+ "properties": {
8
+ "id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
9
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
10
+ "type": { "enum": ["ranged", "melee"] },
11
+ "profiles": {
12
+ "type": "array",
13
+ "items": {
14
+ "type": "object",
15
+ "properties": {
16
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
17
+ "range": {
18
+ "oneOf": [
19
+ { "type": "integer", "minimum": 0 },
20
+ { "type": "string", "const": "Melee" }
21
+ ]
22
+ },
23
+ "stats": {
24
+ "type": "object",
25
+ "properties": {
26
+ "A": { "$ref": "../defs/common.schema.json#/$defs/stat-value" },
27
+ "BS": {
28
+ "oneOf": [
29
+ { "type": "integer", "minimum": 2, "maximum": 6 },
30
+ { "type": "null" }
31
+ ]
32
+ },
33
+ "WS": {
34
+ "oneOf": [
35
+ { "type": "integer", "minimum": 2, "maximum": 6 },
36
+ { "type": "null" }
37
+ ]
38
+ },
39
+ "S": { "$ref": "../defs/common.schema.json#/$defs/stat-value" },
40
+ "AP": { "type": "integer" },
41
+ "D": { "$ref": "../defs/common.schema.json#/$defs/stat-value" }
42
+ },
43
+ "required": ["A", "S", "AP", "D"]
44
+ },
45
+ "keywords": { "$ref": "../defs/common.schema.json#/$defs/keyword-list" }
46
+ },
47
+ "required": ["name", "stats"],
48
+ "additionalProperties": false
49
+ },
50
+ "minItems": 1
51
+ },
52
+ "game_version": { "$ref": "../defs/game-version-ref.schema.json" }
53
+ },
54
+ "required": ["id", "name", "type", "profiles", "game_version"],
55
+ "additionalProperties": false
56
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/enrichment/ability-dsl/ability.schema.json",
4
+ "title": "Ability DSL Entry",
5
+ "description": "Community-authored structured representation of what a game ability does. NOT GW text.",
6
+ "type": "object",
7
+ "properties": {
8
+ "ability_id": { "$ref": "../../defs/common.schema.json#/$defs/entity-id" },
9
+ "name": { "type": "string" },
10
+ "authored_by": { "$ref": "../../defs/common.schema.json#/$defs/contributor-ref" },
11
+ "game_version": { "$ref": "../../defs/game-version-ref.schema.json" },
12
+ "version": { "$ref": "../../defs/common.schema.json#/$defs/dataslate-version" },
13
+ "supersedes": {
14
+ "oneOf": [
15
+ { "$ref": "../../defs/common.schema.json#/$defs/dataslate-version" },
16
+ { "type": "null" }
17
+ ]
18
+ },
19
+ "unit_ids": { "type": "array", "items": { "$ref": "../../defs/common.schema.json#/$defs/entity-id" } },
20
+ "faction_id": {
21
+ "oneOf": [
22
+ { "$ref": "../../defs/common.schema.json#/$defs/entity-id" },
23
+ { "type": "null" }
24
+ ],
25
+ "description": "For faction-type abilities, the faction this rule belongs to"
26
+ },
27
+ "detachment_id": {
28
+ "oneOf": [
29
+ { "$ref": "../../defs/common.schema.json#/$defs/entity-id" },
30
+ { "type": "null" }
31
+ ],
32
+ "description": "For detachment/enhancement/stratagem-type abilities, the associated detachment"
33
+ },
34
+ "ability_type": { "type": "string", "enum": ["core", "faction", "detachment", "unit", "enhancement", "stratagem"] },
35
+ "behavior": {
36
+ "type": "string",
37
+ "enum": ["passive", "activated", "reactive", "aura"],
38
+ "description": "How this ability interacts with the game flow — not a runtime predicate"
39
+ },
40
+ "effect": { "$ref": "effect.schema.json" },
41
+ "scope": { "$ref": "scope.schema.json" },
42
+ "interactions": {
43
+ "type": "array",
44
+ "items": {
45
+ "type": "object",
46
+ "properties": {
47
+ "ability_ref": { "$ref": "../../defs/common.schema.json#/$defs/entity-id" },
48
+ "type": { "type": "string", "enum": ["conflicts-with", "combos-with", "superseded-by", "requires", "replaces"] },
49
+ "notes": { "type": "string" }
50
+ },
51
+ "required": ["ability_ref", "type"]
52
+ }
53
+ },
54
+ "disputed": { "type": "boolean", "default": false },
55
+ "dispute_notes": { "type": "string" },
56
+ "community_notes": { "type": "string" }
57
+ },
58
+ "required": ["ability_id", "name", "authored_by", "game_version", "effect", "scope"],
59
+ "additionalProperties": false
60
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/enrichment/ability-dsl/condition.schema.json",
4
+ "title": "Ability Condition",
5
+ "$defs": {
6
+ "condition-node": {
7
+ "oneOf": [
8
+ { "$ref": "#/$defs/simple-condition" },
9
+ { "$ref": "#/$defs/compound-condition" }
10
+ ]
11
+ },
12
+ "simple-condition": {
13
+ "type": "object",
14
+ "$comment": "Board/meta-state and scoring predicates. `parameters` is intentionally open (additionalProperties: true); each type documents its own param convention. Scoring predicates added for mission cards: `units-destroyed` { side: 'enemy'|'friendly', window: 'this-turn'|'previous-turn', count_min: int } — at least count_min units of `side` were destroyed in `window`. `units-destroyed-comparison` { subject: {side, window}, comparator: 'greater-than'|'greater-or-equal', reference: {side, window} } — compares two destruction tallies (e.g. more enemy units destroyed this turn than friendly last turn). `objective-majority` { relative_to: 'opponent' } — you control more objectives than the named party. `controls-objective` params: { count_min: int, objective_role?: 'central', exclude?: 'home', objective?: 'opponent-home' }.",
15
+ "properties": {
16
+ "type": {
17
+ "type": "string",
18
+ "enum": [
19
+ "phase-is", "timing-is", "player-turn-is",
20
+ "unit-below-starting-strength", "unit-below-half-strength",
21
+ "unit-has-keyword", "unit-within-range-of",
22
+ "model-is-leader", "target-has-keyword",
23
+ "charged-this-turn", "advanced-this-turn", "remained-stationary",
24
+ "is-battle-shocked", "has-lost-wounds",
25
+ "opponent-unit-within-range", "within-range-of-objective",
26
+ "attack-is-type", "has-fought-this-phase",
27
+ "destroyed-by-attack-type", "controls-objective", "is-attached",
28
+ "terrain-area-control", "engagement-state", "territory-control",
29
+ "fights-first", "disposition-matches",
30
+ "units-destroyed", "units-destroyed-comparison", "objective-majority"
31
+ ]
32
+ },
33
+ "parameters": { "type": "object", "additionalProperties": true },
34
+ "negated": { "type": "boolean", "default": false }
35
+ },
36
+ "required": ["type"]
37
+ },
38
+ "compound-condition": {
39
+ "type": "object",
40
+ "properties": {
41
+ "operator": { "type": "string", "enum": ["and", "or", "not"] },
42
+ "operands": { "type": "array", "items": { "$ref": "#/$defs/condition-node" }, "minItems": 1 }
43
+ },
44
+ "required": ["operator", "operands"]
45
+ }
46
+ },
47
+ "$ref": "#/$defs/condition-node"
48
+ }
@@ -0,0 +1,145 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://40kdc.dev/schemas/enrichment/ability-dsl/effect.schema.json",
4
+ "title": "Ability Effect",
5
+ "$defs": {
6
+ "effect-node": {
7
+ "oneOf": [
8
+ { "$ref": "#/$defs/single-effect" },
9
+ { "$ref": "#/$defs/choice-effect" },
10
+ { "$ref": "#/$defs/sequence-effect" },
11
+ { "$ref": "#/$defs/dice-gated-effect" },
12
+ { "$ref": "#/$defs/conditional-effect" },
13
+ { "$ref": "#/$defs/dice-pool-allocation-effect" }
14
+ ]
15
+ },
16
+ "single-effect": {
17
+ "type": "object",
18
+ "properties": {
19
+ "type": {
20
+ "type": "string",
21
+ "enum": [
22
+ "stat-modifier", "roll-modifier", "re-roll",
23
+ "mortal-wounds", "feel-no-pain", "invulnerable-save", "ward",
24
+ "keyword-grant", "movement-modifier", "deep-strike",
25
+ "fallback-and-act", "fight-first", "fight-last",
26
+ "shoot-on-death", "fight-on-death",
27
+ "objective-control-modifier", "leadership-modifier",
28
+ "damage-reduction", "attack-restriction",
29
+ "ability-grant", "cp-gain", "cp-refund",
30
+ "model-destruction", "resurrection",
31
+ "resource-gain", "resource-spend",
32
+ "charge-roll-modifier", "terrain-area-tag",
33
+ "bs-modifier", "engagement-passthrough"
34
+ ]
35
+ },
36
+ "target": {
37
+ "type": "string",
38
+ "enum": [
39
+ "self", "bearer", "unit", "attached-unit",
40
+ "attacker", "defender",
41
+ "friendly-within-aura", "enemy-within-aura",
42
+ "all-friendly", "all-enemy"
43
+ ]
44
+ },
45
+ "modifier": { "type": "object", "additionalProperties": true }
46
+ },
47
+ "required": ["type", "target"]
48
+ },
49
+ "choice-effect": {
50
+ "type": "object",
51
+ "properties": {
52
+ "type": { "const": "choice" },
53
+ "options": { "type": "array", "items": { "$ref": "#/$defs/effect-node" }, "minItems": 2 },
54
+ "choice_label": { "type": "string" }
55
+ },
56
+ "required": ["type", "options"]
57
+ },
58
+ "sequence-effect": {
59
+ "type": "object",
60
+ "properties": {
61
+ "type": { "const": "sequence" },
62
+ "steps": { "type": "array", "items": { "$ref": "#/$defs/effect-node" }, "minItems": 1 }
63
+ },
64
+ "required": ["type", "steps"]
65
+ },
66
+ "dice-gated-effect": {
67
+ "type": "object",
68
+ "properties": {
69
+ "type": { "const": "dice-gated" },
70
+ "dice": { "type": "string", "description": "Dice expression, e.g. 'D6', '2D6'" },
71
+ "threshold": {
72
+ "oneOf": [
73
+ { "type": "integer" },
74
+ { "type": "string", "enum": ["leadership", "toughness", "save"] }
75
+ ],
76
+ "description": "Fixed threshold or model characteristic to compare against"
77
+ },
78
+ "comparison": {
79
+ "type": "string",
80
+ "enum": ["gte", "lte", "gt", "lt", "eq"],
81
+ "default": "gte"
82
+ },
83
+ "on_success": {
84
+ "oneOf": [
85
+ { "$ref": "#/$defs/effect-node" },
86
+ { "type": "null" }
87
+ ]
88
+ },
89
+ "on_fail": {
90
+ "oneOf": [
91
+ { "$ref": "#/$defs/effect-node" },
92
+ { "type": "null" }
93
+ ]
94
+ }
95
+ },
96
+ "required": ["type", "dice", "threshold"]
97
+ },
98
+ "conditional-effect": {
99
+ "type": "object",
100
+ "properties": {
101
+ "type": { "const": "conditional" },
102
+ "condition": { "$ref": "condition.schema.json" },
103
+ "effect": { "$ref": "#/$defs/effect-node" }
104
+ },
105
+ "required": ["type", "condition", "effect"]
106
+ },
107
+ "dice-pool-allocation-effect": {
108
+ "type": "object",
109
+ "properties": {
110
+ "type": { "const": "dice-pool-allocation" },
111
+ "pool": {
112
+ "type": "object",
113
+ "properties": {
114
+ "count": { "type": "integer", "minimum": 1 },
115
+ "die": { "type": "string" }
116
+ },
117
+ "required": ["count", "die"]
118
+ },
119
+ "max_activations": { "type": "integer", "minimum": 1 },
120
+ "options": {
121
+ "type": "array",
122
+ "items": {
123
+ "type": "object",
124
+ "properties": {
125
+ "name": { "type": "string" },
126
+ "requirement": {
127
+ "type": "object",
128
+ "properties": {
129
+ "type": { "type": "string", "enum": ["pair", "triple", "single", "run"] },
130
+ "min_value": { "type": "integer", "minimum": 1, "maximum": 6 }
131
+ },
132
+ "required": ["type", "min_value"]
133
+ },
134
+ "effect": { "$ref": "#/$defs/effect-node" }
135
+ },
136
+ "required": ["name", "requirement", "effect"]
137
+ },
138
+ "minItems": 1
139
+ }
140
+ },
141
+ "required": ["type", "pool", "max_activations", "options"]
142
+ }
143
+ },
144
+ "$ref": "#/$defs/effect-node"
145
+ }