warhammer-oracle 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 (78) hide show
  1. package/README.md +147 -0
  2. package/dist/data/keywords.d.ts +3 -0
  3. package/dist/data/keywords.d.ts.map +1 -0
  4. package/dist/data/keywords.js +193 -0
  5. package/dist/data/keywords.js.map +1 -0
  6. package/dist/data/kill-team-operatives.d.ts +3 -0
  7. package/dist/data/kill-team-operatives.d.ts.map +1 -0
  8. package/dist/data/kill-team-operatives.js +24956 -0
  9. package/dist/data/kill-team-operatives.js.map +1 -0
  10. package/dist/data/kill-team-rules.d.ts +5 -0
  11. package/dist/data/kill-team-rules.d.ts.map +1 -0
  12. package/dist/data/kill-team-rules.js +94 -0
  13. package/dist/data/kill-team-rules.js.map +1 -0
  14. package/dist/data/phases.d.ts +3 -0
  15. package/dist/data/phases.d.ts.map +1 -0
  16. package/dist/data/phases.js +232 -0
  17. package/dist/data/phases.js.map +1 -0
  18. package/dist/data/rules.d.ts +5 -0
  19. package/dist/data/rules.d.ts.map +1 -0
  20. package/dist/data/rules.js +138 -0
  21. package/dist/data/rules.js.map +1 -0
  22. package/dist/data/units.d.ts +3 -0
  23. package/dist/data/units.d.ts.map +1 -0
  24. package/dist/data/units.js +222240 -0
  25. package/dist/data/units.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +8 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/search.d.ts +2 -0
  31. package/dist/lib/search.d.ts.map +1 -0
  32. package/dist/lib/search.js +14 -0
  33. package/dist/lib/search.js.map +1 -0
  34. package/dist/lib/xml-parser.d.ts +28 -0
  35. package/dist/lib/xml-parser.d.ts.map +1 -0
  36. package/dist/lib/xml-parser.js +411 -0
  37. package/dist/lib/xml-parser.js.map +1 -0
  38. package/dist/lib/xml-parser.test.d.ts +2 -0
  39. package/dist/lib/xml-parser.test.d.ts.map +1 -0
  40. package/dist/lib/xml-parser.test.js +202 -0
  41. package/dist/lib/xml-parser.test.js.map +1 -0
  42. package/dist/register-tools.d.ts +3 -0
  43. package/dist/register-tools.d.ts.map +1 -0
  44. package/dist/register-tools.js +15 -0
  45. package/dist/register-tools.js.map +1 -0
  46. package/dist/server.d.ts +3 -0
  47. package/dist/server.d.ts.map +1 -0
  48. package/dist/server.js +11 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/tools/compare-units.d.ts +3 -0
  51. package/dist/tools/compare-units.d.ts.map +1 -0
  52. package/dist/tools/compare-units.js +191 -0
  53. package/dist/tools/compare-units.js.map +1 -0
  54. package/dist/tools/game-flow.d.ts +3 -0
  55. package/dist/tools/game-flow.d.ts.map +1 -0
  56. package/dist/tools/game-flow.js +119 -0
  57. package/dist/tools/game-flow.js.map +1 -0
  58. package/dist/tools/lookup-keyword.d.ts +3 -0
  59. package/dist/tools/lookup-keyword.d.ts.map +1 -0
  60. package/dist/tools/lookup-keyword.js +84 -0
  61. package/dist/tools/lookup-keyword.js.map +1 -0
  62. package/dist/tools/lookup-phase.d.ts +3 -0
  63. package/dist/tools/lookup-phase.d.ts.map +1 -0
  64. package/dist/tools/lookup-phase.js +74 -0
  65. package/dist/tools/lookup-phase.js.map +1 -0
  66. package/dist/tools/lookup-unit.d.ts +3 -0
  67. package/dist/tools/lookup-unit.d.ts.map +1 -0
  68. package/dist/tools/lookup-unit.js +175 -0
  69. package/dist/tools/lookup-unit.js.map +1 -0
  70. package/dist/tools/search-units.d.ts +3 -0
  71. package/dist/tools/search-units.d.ts.map +1 -0
  72. package/dist/tools/search-units.js +108 -0
  73. package/dist/tools/search-units.js.map +1 -0
  74. package/dist/types.d.ts +92 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +3 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # warhammer-oracle
2
+
3
+ Warhammer 40K rules, unit stats, and game flow — as an MCP server.
4
+
5
+ Ask your AI assistant about datasheets, keywords, phase sequences, and more. Covers Warhammer 40,000, Combat Patrol, and Kill Team.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npx warhammer-oracle
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g warhammer-oracle
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ ### Claude Desktop
22
+
23
+ Add to your `claude_desktop_config.json`:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "warhammer-oracle": {
29
+ "command": "npx",
30
+ "args": ["-y", "warhammer-oracle"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Claude Code
37
+
38
+ ```bash
39
+ claude mcp add warhammer-oracle -- npx -y warhammer-oracle
40
+ ```
41
+
42
+ ## Tools
43
+
44
+ ### `lookup_unit`
45
+
46
+ Look up a unit datasheet by name. Returns stat profiles, ranged and melee weapons, abilities, and keywords.
47
+
48
+ ```
49
+ "Look up the Intercessor Squad datasheet"
50
+ "What are the stats for a Leman Russ Battle Tank?"
51
+ ```
52
+
53
+ **Parameters:** `unit_name` (required), `faction` (optional), `game_mode` (optional: `40k`, `combat_patrol`, `kill_team`)
54
+
55
+ ### `lookup_keyword`
56
+
57
+ Look up a keyword or rule. Returns the official definition, a plain English explanation, examples, and which game modes it applies to.
58
+
59
+ ```
60
+ "What does Devastating Wounds do?"
61
+ "Explain the Feel No Pain keyword"
62
+ ```
63
+
64
+ **Parameters:** `keyword` (required), `game_mode` (optional)
65
+
66
+ ### `lookup_phase`
67
+
68
+ Look up a game phase by name. Returns step-by-step instructions and tips.
69
+
70
+ ```
71
+ "Walk me through the Shooting phase"
72
+ "How does the Firefight phase work in Kill Team?"
73
+ ```
74
+
75
+ **Parameters:** `phase_name` (required), `game_mode` (optional, default: `40k`)
76
+
77
+ ### `search_units`
78
+
79
+ Search units by name, faction, or keywords. Returns a compact list (max 10 results) with faction, points, and keywords.
80
+
81
+ ```
82
+ "Find all Necron units under 100 points"
83
+ "Search for units with the Fly keyword"
84
+ ```
85
+
86
+ **Parameters:** `query` (required), `faction` (optional), `max_points` (optional), `game_mode` (optional)
87
+
88
+ ### `compare_units`
89
+
90
+ Compare 2-4 units side by side. Shows full datasheets for each unit in a single response.
91
+
92
+ ```
93
+ "Compare Intercessors vs Tactical Marines"
94
+ "Compare the Leman Russ, Predator, and Hammerhead side by side"
95
+ ```
96
+
97
+ **Parameters:** `units` (required, array of 2-4 unit names)
98
+
99
+ ### `game_flow`
100
+
101
+ Show the full turn sequence for a game mode, or highlight where you are in the turn and what comes next.
102
+
103
+ ```
104
+ "Show me the 40K turn sequence"
105
+ "I'm in the Shooting phase — what's next?"
106
+ "Show the Kill Team turn sequence"
107
+ ```
108
+
109
+ **Parameters:** `current_phase` (optional), `game_mode` (optional, default: `40k`)
110
+
111
+ ## Data
112
+
113
+ All data is embedded at build time — no network calls at runtime.
114
+
115
+ | Category | Count | Source |
116
+ |---|---|---|
117
+ | Unit datasheets | 1,066 | [BSData](https://github.com/BSData/wh40k-10e) community project |
118
+ | Shared rules | 33 | BSData |
119
+ | Curated keywords | 25 | Hand-written, plain English |
120
+ | Game mode sequences | 3 | Hand-curated (40K, Combat Patrol, Kill Team) |
121
+
122
+ ### Game modes
123
+
124
+ - **Warhammer 40,000** (40k) — full-scale battles
125
+ - **Combat Patrol** (combat_patrol) — smaller, starter-friendly format
126
+ - **Kill Team** (kill_team) — squad-level skirmish game
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ npm install
132
+ npm run build
133
+ npm test
134
+ ```
135
+
136
+ To refresh unit data from BSData:
137
+
138
+ ```bash
139
+ npm run fetch-data
140
+ npm run build
141
+ ```
142
+
143
+ ## License
144
+
145
+ MIT (for the MCP server code).
146
+
147
+ Unit data sourced from the [BSData](https://github.com/BSData/wh40k-10e) community project. Game rules and army rules are the intellectual property of Games Workshop. This tool provides reference data for personal use during gameplay.
@@ -0,0 +1,3 @@
1
+ import type { KeywordDefinition } from "../types.js";
2
+ export declare const KEYWORDS: KeywordDefinition[];
3
+ //# sourceMappingURL=keywords.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keywords.d.ts","sourceRoot":"","sources":["../../src/data/keywords.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,eAAO,MAAM,QAAQ,EAAE,iBAAiB,EAkPvC,CAAC"}
@@ -0,0 +1,193 @@
1
+ export const KEYWORDS = [
2
+ // === Weapon Keywords ===
3
+ {
4
+ name: "Devastating Wounds",
5
+ description: "Critical wounds (unmodified wound roll of 6) inflict mortal wounds equal to the weapon's Damage characteristic instead of normal damage. These mortal wounds bypass the target's armour save entirely.",
6
+ plainEnglish: "When you roll a 6 to wound (before any modifiers), the hit skips the enemy's armour save completely and deals mortal wounds equal to the weapon's damage value. This is huge against tough targets with great saves — they just take the damage, no save allowed. The confusing part: it replaces the normal damage for that hit, it doesn't add extra on top.",
7
+ gameModes: ["40k", "combat_patrol"],
8
+ examples: [
9
+ "A weapon with Damage 2 and Devastating Wounds rolls a 6 to wound — the target takes 2 mortal wounds with no save.",
10
+ "An assault cannon with Dev Wounds can punch through Terminators' 2+ save on lucky rolls.",
11
+ ],
12
+ },
13
+ {
14
+ name: "Lethal Hits",
15
+ description: "Critical hits (unmodified hit roll of 6) automatically wound the target — no wound roll required.",
16
+ plainEnglish: "When you roll a natural 6 to hit, you skip the wound roll entirely — it just wounds automatically. This is great against really tough targets where you'd normally need a 5+ or 6 to wound, because you bypass that bad wound roll completely.",
17
+ gameModes: ["40k", "combat_patrol"],
18
+ examples: [
19
+ "Shooting Strength 4 weapons into Toughness 10? Normally you need 6s to wound. With Lethal Hits, every 6 to hit just wounds automatically.",
20
+ ],
21
+ },
22
+ {
23
+ name: "Sustained Hits",
24
+ description: "Critical hits (unmodified hit roll of 6) generate extra hits. Sustained Hits X generates X additional hits; Sustained Hits without a number generates 1 extra hit.",
25
+ plainEnglish: "Every time you roll a natural 6 to hit, you score bonus hits on top of the one you already landed. 'Sustained Hits 1' means one extra hit per 6. 'Sustained Hits 2' means two extra. These bonus hits still need to roll to wound as normal — they're not auto-wounds.",
26
+ gameModes: ["40k", "combat_patrol"],
27
+ examples: [
28
+ "A unit with Sustained Hits 1 rolls three 6s out of ten shots — that's 13 hits total (10 original + 3 bonus).",
29
+ ],
30
+ },
31
+ {
32
+ name: "Anti-X",
33
+ description: "Improves the critical wound threshold against targets with a specific keyword. Anti-Infantry 4+ means unmodified wound rolls of 4+ are critical wounds against Infantry.",
34
+ plainEnglish: "This weapon is specialised against a particular type of target. 'Anti-Infantry 4+' means when shooting at Infantry, your wound rolls of 4+ count as critical wounds (6s). Why does that matter? Because critical wounds trigger things like Devastating Wounds and Sustained Hits. On its own, Anti-X doesn't change your wound roll — but paired with Dev Wounds, it's devastating.",
35
+ gameModes: ["40k", "combat_patrol"],
36
+ examples: [
37
+ "A weapon with Anti-Vehicle 4+ and Devastating Wounds against a tank: every wound roll of 4+ becomes mortal wounds.",
38
+ ],
39
+ },
40
+ {
41
+ name: "Blast",
42
+ description: "When targeting a unit with 6+ models, add 1 to the Attacks characteristic for each 5 models in the target unit (minimum +1). Cannot be used for Overwatch.",
43
+ plainEnglish: "Blast weapons get bonus shots when shooting at big groups — the bigger the mob, the more shots you get. For every 5 models in the target unit, add 1 attack. A unit of 10 models gives you +2 attacks. The catch: you can never use Blast weapons during Overwatch (the reactive shooting when someone charges you).",
44
+ gameModes: ["40k", "combat_patrol"],
45
+ examples: [
46
+ "A frag missile (Blast, D6 attacks) shoots at a 20-model unit: roll D6 and add 4 attacks.",
47
+ ],
48
+ },
49
+ {
50
+ name: "Heavy",
51
+ description: "If the bearer's unit Remained Stationary this phase, add 1 to hit rolls made with this weapon.",
52
+ plainEnglish: "Stand still and you shoot better — you get +1 to your hit rolls. If you moved at all this turn, you shoot at normal accuracy. This rewards a 'hold your ground and shoot' playstyle. The +1 can make a big difference: a 4+ to hit becomes a 3+.",
53
+ gameModes: ["40k", "combat_patrol"],
54
+ },
55
+ {
56
+ name: "Rapid Fire",
57
+ description: "When targeting a unit within half the weapon's range, increase the Attacks characteristic by the Rapid Fire value.",
58
+ plainEnglish: "Get close and you fire more shots. 'Rapid Fire 1' on a 24\" weapon means at 12\" or closer, you get +1 attack per model. 'Rapid Fire 2' means +2 attacks up close. The classic bolter is Rapid Fire 1 — at long range you fire one shot, up close you fire two.",
59
+ gameModes: ["40k", "combat_patrol"],
60
+ examples: [
61
+ "A bolt rifle (Rapid Fire 1, 30\" range, 2 attacks) fires at 15\" or closer: each model gets 3 attacks instead of 2.",
62
+ ],
63
+ },
64
+ {
65
+ name: "Assault",
66
+ description: "This weapon can be fired even if the bearer's unit Advanced this turn, but hit rolls suffer a -1 penalty if the unit Advanced.",
67
+ plainEnglish: "You can shoot this weapon even after Advancing (the extra-fast move). Normally, if you Advance you can't shoot at all. Assault weapons let you — but you take a -1 to hit as a trade-off. Great for aggressive play where you want to close distance fast while still shooting.",
68
+ gameModes: ["40k", "combat_patrol"],
69
+ },
70
+ {
71
+ name: "Pistol",
72
+ description: "This weapon can be fired even if the bearer's unit is within Engagement Range of enemy models, but it must target one of those enemy units and can only be fired if the unit is not eligible to shoot other weapons.",
73
+ plainEnglish: "Pistols are the only ranged weapons you can fire while locked in melee combat. If your unit is in close combat (within Engagement Range), you can still shoot your pistol at whoever you're fighting. You can't shoot any other guns though — it's pistols or nothing when you're in melee.",
74
+ gameModes: ["40k", "combat_patrol"],
75
+ },
76
+ {
77
+ name: "Torrent",
78
+ description: "This weapon automatically hits the target — no hit roll required.",
79
+ plainEnglish: "Flamers and similar weapons — you don't roll to hit at all, every attack automatically hits. This makes Torrent weapons extremely reliable. A Torrent weapon with D6 attacks will always land D6 hits. Ballistic Skill doesn't matter; modifiers to hit don't matter. Just point and burn.",
80
+ gameModes: ["40k", "combat_patrol"],
81
+ examples: [
82
+ "A heavy flamer (Torrent, D6 attacks) always scores D6 hits — no rolling to hit needed.",
83
+ ],
84
+ },
85
+ {
86
+ name: "Twin-linked",
87
+ description: "This weapon can re-roll its wound rolls.",
88
+ plainEnglish: "You can re-roll any failed wound rolls with this weapon. Not hit rolls — wound rolls. This makes the weapon much more consistent at actually dealing damage. If you roll badly on your wound step, just pick up those dice and roll them again.",
89
+ gameModes: ["40k", "combat_patrol"],
90
+ },
91
+ {
92
+ name: "Melta",
93
+ description: "When targeting a unit within half the weapon's range, increase the Damage characteristic by the Melta value.",
94
+ plainEnglish: "Get close and the weapon hits way harder. 'Melta 2' on a 12\" weapon means at 6\" or less, add 2 to the damage of each hit. Melta weapons are the classic tank-busters — already strong at range, absolutely devastating up close. The risk-reward is real: you have to get dangerously close.",
95
+ gameModes: ["40k", "combat_patrol"],
96
+ examples: [
97
+ "A multi-melta (Melta 2, Damage D6, 18\" range) at 9\" or less: each hit deals D6+2 damage.",
98
+ ],
99
+ },
100
+ {
101
+ name: "Hazardous",
102
+ description: "After resolving attacks with this weapon, roll one D6 for each Hazardous weapon fired. On a 1, the bearer's unit suffers 3 mortal wounds (or the bearer is destroyed if it's a Character, Monster, or Vehicle).",
103
+ plainEnglish: "Powerful but risky — after shooting, you roll a die for each Hazardous weapon you fired. On a 1, your own unit takes 3 mortal wounds (or the model just dies if it's a Character/Monster/Vehicle). Think of plasma guns overheating. Usually worth the risk, but sometimes your own guy explodes.",
104
+ gameModes: ["40k", "combat_patrol"],
105
+ examples: [
106
+ "A squad fires 3 plasma guns (Hazardous). After resolving shots, roll 3 dice — each 1 means 3 mortal wounds to your own unit.",
107
+ ],
108
+ },
109
+ {
110
+ name: "Precision",
111
+ description: "Critical hits (unmodified hit roll of 6) can target an attached Leader or Character model instead of the unit.",
112
+ plainEnglish: "Normally, you can't snipe Characters who are leading a unit — your hits get allocated to the bodyguard models first. Precision weapons can bypass this on a natural 6 to hit: those critical hits can be allocated directly to the Character. This is how you pick off enemy leaders hiding behind their squads.",
113
+ gameModes: ["40k", "combat_patrol"],
114
+ },
115
+ {
116
+ name: "Indirect Fire",
117
+ description: "This weapon can target units that are not visible to the bearer. When doing so, subtract 1 from hit rolls and the target gets the Benefit of Cover.",
118
+ plainEnglish: "You can shoot at enemies you can't see — hidden behind walls, in buildings, whatever. But it's less accurate (-1 to hit) and the target gets cover (usually +1 to their save). Still very useful because some units hide all game; this forces them to move or take damage. Your opponent might not realise you can hit their hidden units.",
119
+ gameModes: ["40k", "combat_patrol"],
120
+ },
121
+ // === Unit Keywords ===
122
+ {
123
+ name: "Feel No Pain",
124
+ description: "Each time this model would lose a wound, roll one D6: if the result equals or exceeds the Feel No Pain value, that wound is not lost.",
125
+ plainEnglish: "An extra saving throw after all your other saves have failed. Your model is about to lose a wound? Roll a die — on the FNP value or higher, you shrug it off. 'Feel No Pain 5+' means you ignore each wound on a 5 or 6 (a 1-in-3 chance). This even works against mortal wounds, which is rare and very powerful. It stacks on top of your armour save — it's a completely separate roll.",
126
+ gameModes: ["40k", "combat_patrol"],
127
+ examples: [
128
+ "A model with FNP 5+ takes 3 wounds — roll 3 dice, each 5+ prevents one wound.",
129
+ "Death Guard plague marines famously have FNP 5+, making them annoyingly tough to kill.",
130
+ ],
131
+ },
132
+ {
133
+ name: "Lone Operative",
134
+ description: "Unless it is within 12\" of an enemy unit, this unit can only be selected as the target of a ranged attack if the attacking model is within 12\".",
135
+ plainEnglish: "This model is hard to shoot at range — enemies can only target it with ranged weapons if they're within 12\". Beyond that, it's basically invisible to enemy guns. Great for sneaky characters moving up the board. But once an enemy gets within 12\", the protection vanishes completely.",
136
+ gameModes: ["40k", "combat_patrol"],
137
+ },
138
+ {
139
+ name: "Stealth",
140
+ description: "When targeted by ranged attacks, subtract 1 from the hit roll.",
141
+ plainEnglish: "Enemies shooting at this unit take a -1 penalty to their hit rolls. Simple but effective — it turns a 3+ to hit into a 4+ to hit, which means roughly 17% fewer hits. Stacks with other to-hit penalties like cover, making the unit surprisingly hard to pin down at range.",
142
+ gameModes: ["40k", "combat_patrol"],
143
+ },
144
+ {
145
+ name: "Scouts",
146
+ description: "At the start of the first battle round, before the first turn, units with Scouts X can make a Normal move of up to X inches. Cannot end closer to enemy models.",
147
+ plainEnglish: "Before the game even starts, this unit gets a free move — 'Scouts 6\"' means a free 6\" move at the very beginning. This lets you grab objectives early or get into a better position. You can't move closer to the enemy than you started, but it's still a big advantage for board control on turn 1.",
148
+ gameModes: ["40k", "combat_patrol"],
149
+ },
150
+ {
151
+ name: "Deep Strike",
152
+ description: "During the Declare Battle Formations step, this unit can be set up in Reserves instead of on the battlefield. In the Reinforcements step of your Movement phase, set it up anywhere more than 9\" from enemy models.",
153
+ plainEnglish: "Instead of deploying on the table at the start, this unit waits off-board and drops in later — anywhere you want, as long as it's more than 9\" from enemies. This is incredibly flexible for getting behind enemy lines or grabbing undefended objectives. The downside: 9\" is far for a charge (you need a 9 on 2D6), so shooting units benefit more than melee units unless you have charge bonuses.",
154
+ gameModes: ["40k", "combat_patrol"],
155
+ },
156
+ {
157
+ name: "Deadly Demise",
158
+ description: "When this model is destroyed, roll one D6. On a 6, each unit within 6\" suffers D3 mortal wounds (Deadly Demise D3) or D6 mortal wounds (Deadly Demise D6).",
159
+ plainEnglish: "When this model dies, it might explode and hurt everyone nearby. Roll a D6 — on a 6, every unit within 6\" (friend and foe!) takes mortal wounds. This mainly applies to vehicles and big monsters. It means your opponent should think twice about killing your tank in melee, and you should think about where your own models are standing.",
160
+ gameModes: ["40k", "combat_patrol"],
161
+ examples: [
162
+ "A Rhino with Deadly Demise D3 gets destroyed — on a 6, all units within 6\" take D3 mortal wounds, including your own nearby infantry.",
163
+ ],
164
+ },
165
+ {
166
+ name: "Fights First",
167
+ description: "Units with this ability that are eligible to fight do so in the Fights First step, before other units.",
168
+ plainEnglish: "In the Fight phase, this unit swings before almost everyone else. Normally, the player whose turn it is picks first, but Fights First overrides that. If you charge a unit with Fights First, they hit you before you get to attack — which can be nasty. If both sides have Fights First, it goes back to normal alternating order.",
169
+ gameModes: ["40k", "combat_patrol"],
170
+ },
171
+ {
172
+ name: "Infiltrators",
173
+ description: "During deployment, this unit can be set up anywhere on the battlefield more than 9\" from enemy models and the enemy deployment zone.",
174
+ plainEnglish: "Deploy this unit almost anywhere on the board instead of just your deployment zone — as long as it's 9\" from enemy models and their deployment zone. This is amazing for grabbing mid-board objectives on turn 1 or setting up early threat pressure. Unlike Deep Strike, Infiltrators are on the board from the start, so they score objectives immediately.",
175
+ gameModes: ["40k", "combat_patrol"],
176
+ },
177
+ {
178
+ name: "Invulnerable Save",
179
+ description: "An unmodifiable save that can be used instead of the model's normal armour save. Not affected by the Armour Penetration characteristic of the attack.",
180
+ plainEnglish: "A backup save that ignores AP (armour penetration). Normally, weapons with high AP shred your armour save — AP -3 turns a 3+ save into a 6+. But an invulnerable save (like 4++) stays at 4+ no matter how much AP the weapon has. You always choose the better option: your modified armour save or your invuln. Against low-AP weapons, use your armour save; against high-AP weapons, use the invuln.",
181
+ gameModes: ["40k", "combat_patrol"],
182
+ examples: [
183
+ "A Terminator has a 2+ armour save and a 4+ invulnerable save. Against AP 0, use the 2+. Against AP -3, the armour becomes 5+ so use the 4+ invuln instead.",
184
+ ],
185
+ },
186
+ {
187
+ name: "Leader",
188
+ description: "This model can be attached to a compatible Bodyguard unit during deployment. While leading, the Leader's abilities apply to the combined unit.",
189
+ plainEnglish: "This Character can join a specific squad (its Bodyguard unit) to form one combined unit. The squad protects the leader — wounds go on the bodyguard models first. The leader's special abilities buff the whole squad while attached. Check the leader's datasheet for which squads they can join. Leaders can't join just any unit — the compatible units are listed specifically.",
190
+ gameModes: ["40k", "combat_patrol"],
191
+ },
192
+ ];
193
+ //# sourceMappingURL=keywords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keywords.js","sourceRoot":"","sources":["../../src/data/keywords.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { KillTeamOperative } from "../types.js";
2
+ export declare const KILL_TEAM_OPERATIVES: KillTeamOperative[];
3
+ //# sourceMappingURL=kill-team-operatives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-team-operatives.d.ts","sourceRoot":"","sources":["../../src/data/kill-team-operatives.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAu3wBnD,CAAC"}