diff --git a/README.md b/README.md index 46fe931..fb91d9d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A [Warmaster Revolution](https://www.wm-revolution.com/) army list builder — c ## Development -Requires Node.js `^20.19.0` or `>=22.12.0`. +Requires Node.js `>=22.12.0`. ```sh npm ci @@ -20,6 +20,7 @@ Army data is generated from the Markdown source lists, per [schema.md](schema.md ```sh npm run generate:data # data/source/WMR_Armies_*.md + data/curation/ -> src/data/generated/ +npm run scouting:data # assign scouting values and regenerate the scouting audit report npm run validate:data # sanity-check the generated JSON npm run generate:diff # compare against data/previous/ snapshot (if present) ``` @@ -31,7 +32,32 @@ npm run generate:diff # compare against data/previous/ snapshot (if present) - `reports/` — normalization and version-diff reports. - `scripts/archive/` — one-time bootstrap scripts that produced the initial `data/curation/`; kept for reference, not part of the regular pipeline. See [scripts/archive/README.md](scripts/archive/README.md). -See [roadmap.md](roadmap.md) for the project roadmap. +See [roadmap.md](src/content/info/roadmap.md) for the project roadmap. + +## Rule sets + +Configuration offers three independently saved rule sets: **Warmaster Revolution**, +**WMR - 2026 Playtest**, and **A Matter of Mustaches**. The 2026 Playtest begins as a complete +copy of Warmaster Revolution but has its own rule-set ID, army data objects, lists, folders, and +import preferences so it can diverge safely. + +## Optional Scouting rules + +Enable **Scouting** from Configuration to show generated scouting points in the unit catalog, +totals for each configured stack in the roster, and the army's available scouting total at the +top of the rules printout. The adjacent information button contains the deployment procedure and +the Flyers/Scouts/Patrols table. + +Scouting is a display preference stored in the browser; it does not change the saved-list schema. +The army total assumes every eligible entry in the list is committed. Flying mounts and attached +Scout/Patrol upgrades use the higher of the parent and upgrade values. + +`scripts/apply-scouting.mjs` assigns values programmatically from structured troop type, armour, +Flying subtype, and the named exceptions in the rule table. It updates the Revolution army JSON, +builds the derived Playtest and A Matter of Mustaches lookups in +`src/data/generated/scouting-points.json`, and +writes `reports/scouting-points.md`, grouped by rule set, army, and scouting value 3 through 0. +`npm run generate:data` runs this step automatically after rebuilding the base army tables. Warmuster is an unofficial, non-commercial fan project, not affiliated with or endorsed by Games Workshop. @@ -41,4 +67,4 @@ Original source code is licensed under the [MIT License](LICENSE). Game data, tr ## In-app information pages -The Privacy, Changelog, Roadmap, and Credits dialogs are maintained as Markdown files in `src/content/info/`. `InfoDialog.tsx` imports them as raw text and `MarkdownContent.tsx` renders headings, paragraphs, bullet lists, and external links. Update the Markdown files to change the dialog copy; no component edit is required. \ No newline at end of file +The Privacy, Changelog, Roadmap, and Credits dialogs are maintained as Markdown files in `src/content/info/`. `InfoDialog.tsx` imports them as raw text and `MarkdownContent.tsx` renders headings, paragraphs, bullet lists, and external links. Update the Markdown files to change the dialog copy; no component edit is required. diff --git a/package.json b/package.json index 1b4b299..f6413be 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,14 @@ "version": "0.1.0", "license": "MIT", "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=22.12.0" }, "type": "module", "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "generate:data": "node scripts/generate-army-json.mjs", + "generate:data": "node scripts/generate-army-json.mjs && node --experimental-strip-types scripts/apply-scouting.mjs", + "scouting:data": "node --experimental-strip-types scripts/apply-scouting.mjs", "validate:data": "node scripts/validate-data.mjs", "generate:diff": "node scripts/generate-version-diff.mjs", "test": "npm run validate:data && vitest run", diff --git a/reports/scouting-points.md b/reports/scouting-points.md new file mode 100644 index 0000000..162b0ee --- /dev/null +++ b/reports/scouting-points.md @@ -0,0 +1,2306 @@ +# Scouting points report + +Generated by `npm run scouting:data`. Units are grouped by army and then by scouting points from 3 to 0. + +# Warmaster Revolution + +## Empire + +### 3 scouting points + +- Griffon (`empire:griffon`) + +### 2 scouting points + +- General (`empire:general`) +- Skirmishers (`empire:skirmishers`) + +### 1 scouting points + +- Hero (`empire:hero`) +- Pistoliers (`empire:pistoliers`) +- Wizard (`empire:wizard`) + +### 0 scouting points + +- Cannon (`empire:cannon`) +- Crossbowmen (`empire:crossbowmen`) +- Flagellants (`empire:flagellants`) +- Halberdiers (`empire:halberdiers`) +- Handgunners (`empire:handgunners`) +- Helblaster (`empire:helblaster`) +- Knights (`empire:knights`) +- Steam Tank (`empire:steam-tank`) +- War Altar (`empire:war-altar`) + +## Tomb Kings + +### 3 scouting points + +- Carrion (`tomb-kings:carrion`) +- Zombie Dragon (`tomb-kings:zombie-dragon`) + +### 2 scouting points + +- Tomb King (`tomb-kings:tomb-king`) + +### 1 scouting points + +- Liche Priest (`tomb-kings:liche-priest`) +- Skeleton Cavalry (`tomb-kings:skeleton-cavalry`) + +### 0 scouting points + +- Bone Giant (`tomb-kings:bone-giant`) +- Bone Thrower (`tomb-kings:bone-thrower`) +- Liche Chariot (`tomb-kings:liche-chariot`) +- Skeletal Chariots (`tomb-kings:skeletal-chariots`) +- Skeleton Bowmen (`tomb-kings:skeleton-bowmen`) +- Skeletons (`tomb-kings:skeletons`) +- Skull Chukka (`tomb-kings:skull-chukka`) +- Sphinx (`tomb-kings:sphinx`) + +## Chaos + +### 3 scouting points + +- Chaos Dragon (`chaos:chaos-dragon`) +- Harpies (`chaos:harpies`) + +### 2 scouting points + +- General (`chaos:general`) + +### 1 scouting points + +- Chaos Hounds (`chaos:chaos-hounds`) +- Hero (`chaos:hero`) +- Marauder Horsemen (`chaos:marauder-horsemen`) +- Sorcerer (`chaos:sorcerer`) + +### 0 scouting points + +- Chaos Chariots (`chaos:chaos-chariots`) +- Chaos Knights (`chaos:chaos-knights`) +- Chaos Marauders (`chaos:chaos-marauders`) +- Chaos Spawn (`chaos:chaos-spawn`) +- Chaos Warriors (`chaos:chaos-warriors`) +- Chariot (`chaos:chariot`) +- Dragon Ogres (`chaos:dragon-ogres`) +- Ogres (`chaos:ogres`) +- Trolls (`chaos:trolls`) + +## Orcs + +### 3 scouting points + +- Wyvern (`orcs:wyvern`) + +### 2 scouting points + +- Orc General (`orcs:orc-general`) +- Wolf Riders (`orcs:wolf-riders`) + +### 1 scouting points + +- Boar Riders (`orcs:boar-riders`) +- Goblin Hero (`orcs:goblin-hero`) +- Goblin Shaman (`orcs:goblin-shaman`) +- Orc Hero (`orcs:orc-hero`) +- Orc Shaman (`orcs:orc-shaman`) + +### 0 scouting points + +- Black Orcs (`orcs:black-orcs`) +- Chariot (`orcs:chariot`) +- Giant (`orcs:giant`) +- Goblins (`orcs:goblins`) +- Ogres (`orcs:ogres`) +- Orc Warriors (`orcs:orc-warriors`) +- Rock Lobber (`orcs:rock-lobber`) +- Trolls (`orcs:trolls`) +- Wolf Chariots (`orcs:wolf-chariots`) + +## High Elves + +### 3 scouting points + +- Dragon (`high-elves:dragon`) +- Dragon Rider (`high-elves:dragon-rider`) +- Giant Eagle (`high-elves:giant-eagle`) +- Giant Eagles (`high-elves:giant-eagles`) + +### 2 scouting points + +- General (`high-elves:general`) +- Reavers (`high-elves:reavers`) + +### 1 scouting points + +- Hero (`high-elves:hero`) +- Mage (`high-elves:mage`) + +### 0 scouting points + +- Archers (`high-elves:archers`) +- Chariot (`high-elves:chariot`) +- Chariots (`high-elves:chariots`) +- Elven Bolt Thrower (`high-elves:elven-bolt-thrower`) +- Silver Helms (`high-elves:silver-helms`) +- Spearmen (`high-elves:spearmen`) + +## Dwarfs + +### 3 scouting points + +- Gyrocopter (`dwarfs:gyrocopter`) +- Rangers (`dwarfs:rangers`) + +### 2 scouting points + +- General (`dwarfs:general`) + +### 1 scouting points + +- Hero (`dwarfs:hero`) +- Runesmith (`dwarfs:runesmith`) + +### 0 scouting points + +- Anvil (`dwarfs:anvil`) +- Cannon (`dwarfs:cannon`) +- Flame Cannon (`dwarfs:flame-cannon`) +- Handgunners (`dwarfs:handgunners`) +- Miners (`dwarfs:miners`) +- Oathstone (`dwarfs:oathstone`) +- Troll Slayers (`dwarfs:troll-slayers`) +- Warriors (`dwarfs:warriors`) + +## Skaven + +### 3 scouting points + +- Gutter Runners (`skaven:gutter-runners`) + +### 2 scouting points + +- Grey Seer (`skaven:grey-seer`) + +### 1 scouting points + +- Hero (`skaven:hero`) +- Rat Swarms (`skaven:rat-swarms`) +- Warlock (`skaven:warlock`) + +### 0 scouting points + +- Clanrats (`skaven:clanrats`) +- Doom Wheel (`skaven:doom-wheel`) +- Jezzails (`skaven:jezzails`) +- Plague Monks (`skaven:plague-monks`) +- Rat Ogre Bodyguard (`skaven:rat-ogre-bodyguard`) +- Rat Ogres (`skaven:rat-ogres`) +- Screaming Bell (`skaven:screaming-bell`) +- Stormvermin (`skaven:stormvermin`) +- Warp Lightning Cannon (`skaven:warp-lightning-cannon`) + +## Lizardmen + +### 3 scouting points + +- Terradons (`lizardmen:terradons`) + +### 2 scouting points + +- Slann Mage Palanquin (`lizardmen:slann-mage-palanquin`) + +### 1 scouting points + +- Saurus Hero (`lizardmen:saurus-hero`) +- Skink Hero (`lizardmen:skink-hero`) +- Skink Shaman (`lizardmen:skink-shaman`) +- Skinks (`lizardmen:skinks`) + +### 0 scouting points + +- Carnosaur (`lizardmen:carnosaur`) +- Cold One Riders (`lizardmen:cold-one-riders`) +- Kroxigor (`lizardmen:kroxigor`) +- Salamander (`lizardmen:salamander`) +- Saurus (`lizardmen:saurus`) +- Stegadon (`lizardmen:stegadon`) +- Stegadon (`lizardmen:stegadon-monstrous-mount`) +- Temple Guard (`lizardmen:temple-guard`) + +## Bretonnia + +### 3 scouting points + +- Hippogriff (`bretonnia:hippogriff`) +- Pegasus (`bretonnia:pegasus`) +- Pegasus Knights (`bretonnia:pegasus-knights`) + +### 2 scouting points + +- General (`bretonnia:general`) +- Squires (`bretonnia:squires`) + +### 1 scouting points + +- Enchantress (`bretonnia:enchantress`) +- Hero (`bretonnia:hero`) + +### 0 scouting points + +- Bowmen (`bretonnia:bowmen`) +- Grail Knights (`bretonnia:grail-knights`) +- Grail Reliquae (`bretonnia:grail-reliquae`) +- Knights (`bretonnia:knights`) +- Men-at-Arms (`bretonnia:men-at-arms`) +- Peasants (`bretonnia:peasants`) +- Trebuchet (`bretonnia:trebuchet`) +- Unicorn (`bretonnia:unicorn`) + +## Kislev + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`kislev:general`) +- Horse Archers (`kislev:horse-archers`) + +### 1 scouting points + +- Cossacks (`kislev:cossacks`) +- Hero (`kislev:hero`) +- Shaman (`kislev:shaman`) +- Winged Lancers (`kislev:winged-lancers`) + +### 0 scouting points + +- Axemen (`kislev:axemen`) +- Bear (`kislev:bear`) +- Bears (`kislev:bears`) +- Bowmen (`kislev:bowmen`) +- Red Guard (`kislev:red-guard`) +- Tzarina (`kislev:tzarina`) +- War Wagon (`kislev:war-wagon`) +- Yozhin (`kislev:yozhin`) + +## Dark Elves + +### 3 scouting points + +- Harpies (`dark-elves:harpies`) +- Manticore (`dark-elves:manticore`) + +### 2 scouting points + +- Dark Riders (`dark-elves:dark-riders`) +- General (`dark-elves:general`) + +### 1 scouting points + +- Hero (`dark-elves:hero`) +- Sorceress (`dark-elves:sorceress`) + +### 0 scouting points + +- Bolt Thrower (`dark-elves:bolt-thrower`) +- Cauldron of Blood (`dark-elves:cauldron-of-blood`) +- Chariot (`dark-elves:chariot`) +- Cold One Knights (`dark-elves:cold-one-knights`) +- Crossbowmen (`dark-elves:crossbowmen`) +- Spearmen (`dark-elves:spearmen`) +- War Hydra (`dark-elves:war-hydra`) +- Witch Elves (`dark-elves:witch-elves`) + +## Daemons + +### 3 scouting points + +- Daemon Flyers (`daemons:daemon-flyers`) +- Daemonic Wings (`daemons:daemonic-wings`) +- Greater Daemon (`daemons:greater-daemon`) + +### 2 scouting points + +- Daemon Overlord (`daemons:daemon-overlord`) + +### 1 scouting points + +- Daemon Cavalry (`daemons:daemon-cavalry`) +- Daemon Hounds (`daemons:daemon-hounds`) +- Daemon Lord Hero (`daemons:daemon-lord-hero`) +- Daemon Sorcerer (`daemons:daemon-sorcerer`) + +### 0 scouting points + +- Chariot (`daemons:chariot`) +- Daemon Beasts (`daemons:daemon-beasts`) +- Daemon Chariots (`daemons:daemon-chariots`) +- Daemon Horde (`daemons:daemon-horde`) +- Daemon Swarm (`daemons:daemon-swarm`) +- Favour of the Gods (`daemons:favour-of-the-gods`) + +## Vampire Counts + +### 3 scouting points + +- Fell Bats (`vampire-counts:fell-bats`) +- Winged Nightmare (`vampire-counts:winged-nightmare`) + +### 2 scouting points + +- Ethereal Host (`vampire-counts:ethereal-host`) +- Vampire Lord (`vampire-counts:vampire-lord`) + +### 1 scouting points + +- Dire Wolves (`vampire-counts:dire-wolves`) +- Necromancer (`vampire-counts:necromancer`) +- Vampire (`vampire-counts:vampire`) + +### 0 scouting points + +- Black Coach (`vampire-counts:black-coach`) +- Black Knights (`vampire-counts:black-knights`) +- Ghouls (`vampire-counts:ghouls`) +- Grave Guard (`vampire-counts:grave-guard`) +- Skeletons (`vampire-counts:skeletons`) +- Zombies (`vampire-counts:zombies`) + +## Araby + +### 3 scouting points + +- Djinn (`araby:djinn`) +- Flying Carpet (`araby:flying-carpet`) +- Magic Carpets (`araby:magic-carpets`) + +### 2 scouting points + +- Desert Riders (`araby:desert-riders`) +- General (`araby:general`) + +### 1 scouting points + +- Camel Riders (`araby:camel-riders`) +- Hero (`araby:hero`) +- Wizard (`araby:wizard`) + +### 0 scouting points + +- Bowmen (`araby:bowmen`) +- Elephant (`araby:elephant`) +- Elephants (`araby:elephants`) +- Guards (`araby:guards`) +- Knights (`araby:knights`) +- Spearmen (`araby:spearmen`) + +## Dogs of War + +### 3 scouting points + +- Birdmen (`dogs-of-war:birdmen`) +- Griffon (`dogs-of-war:griffon`) + +### 2 scouting points + +- General (`dogs-of-war:general`) +- Light Cavalry (`dogs-of-war:light-cavalry`) + +### 1 scouting points + +- Hero (`dogs-of-war:hero`) +- Paymaster (`dogs-of-war:paymaster`) +- Wizard (`dogs-of-war:wizard`) + +### 0 scouting points + +- Crossbowmen (`dogs-of-war:crossbowmen`) +- Dwarfs (`dogs-of-war:dwarfs`) +- Galloper Guns (`dogs-of-war:galloper-guns`) +- Giant (`dogs-of-war:giant`) +- Handgunners (`dogs-of-war:handgunners`) +- Knights (`dogs-of-war:knights`) +- Marauders (`dogs-of-war:marauders`) +- Ogres (`dogs-of-war:ogres`) +- Pay Wagon (`dogs-of-war:pay-wagon`) +- Pikemen (`dogs-of-war:pikemen`) +- Swordsmen (`dogs-of-war:swordsmen`) + +## Ogre Kingdoms + +### 3 scouting points + +- Gorgers (`ogre-kingdoms:gorgers`) + +### 2 scouting points + +- Tyrant (`ogre-kingdoms:tyrant`) + +### 1 scouting points + +- Bruiser (`ogre-kingdoms:bruiser`) +- Butcher (`ogre-kingdoms:butcher`) +- Rhinox Riders (`ogre-kingdoms:rhinox-riders`) +- Sabretusks (`ogre-kingdoms:sabretusks`) + +### 0 scouting points + +- Bull Ogres (`ogre-kingdoms:bull-ogres`) +- Bull Rhinox (`ogre-kingdoms:bull-rhinox`) +- Gnoblars (`ogre-kingdoms:gnoblars`) +- Ironguts (`ogre-kingdoms:ironguts`) +- Leadbelchers (`ogre-kingdoms:leadbelchers`) +- Scrap Launcher (`ogre-kingdoms:scrap-launcher`) +- Slave Giant (`ogre-kingdoms:slave-giant`) +- Yhetees (`ogre-kingdoms:yhetees`) + +## Albion + +### 3 scouting points + +- Giant Eagle (`albion:giant-eagle`) +- Giant Eagles (`albion:giant-eagles`) + +### 2 scouting points + +- General (`albion:general`) + +### 1 scouting points + +- Druid (`albion:druid`) +- Hero (`albion:hero`) +- Riders (`albion:riders`) +- Wolfhounds (`albion:wolfhounds`) + +### 0 scouting points + +- Chariot (`albion:chariot`) +- Chariots (`albion:chariots`) +- Fenbeast (`albion:fenbeast`) +- Giant (`albion:giant`) +- Ogres (`albion:ogres`) +- Slingers (`albion:slingers`) +- Warriors (`albion:warriors`) + +## Goblins + +### 3 scouting points + +- Wyvern (`goblins:wyvern`) + +### 2 scouting points + +- Goblin Warboss (`goblins:goblin-warboss`) +- Wolf Riders (`goblins:wolf-riders`) + +### 1 scouting points + +- Goblin Hero (`goblins:goblin-hero`) +- Goblin Shaman (`goblins:goblin-shaman`) + +### 0 scouting points + +- Chariot (`goblins:chariot`) +- Doom Diver (`goblins:doom-diver`) +- Giant (`goblins:giant`) +- Goblins (`goblins:goblins`) +- Pump Wagon (`goblins:pump-wagon`) +- Spear Chukka (`goblins:spear-chukka`) +- Squig Herd (`goblins:squig-herd`) +- Trolls (`goblins:trolls`) +- Wolf Chariots (`goblins:wolf-chariots`) + +## Witch Hunters + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`witch-hunters:general`) +- Warhounds (`witch-hunters:warhounds`) + +### 1 scouting points + +- Hero (`witch-hunters:hero`) +- Pistoliers (`witch-hunters:pistoliers`) +- Warrior Priest (`witch-hunters:warrior-priest`) + +### 0 scouting points + +- Cannon (`witch-hunters:cannon`) +- Crossbowmen (`witch-hunters:crossbowmen`) +- Flagellants (`witch-hunters:flagellants`) +- Halberdiers (`witch-hunters:halberdiers`) +- Handgunners (`witch-hunters:handgunners`) +- Knights (`witch-hunters:knights`) +- War Altar (`witch-hunters:war-altar`) +- Zealots (`witch-hunters:zealots`) + +## Chaos Dwarfs + +### 3 scouting points + +- Great Taurus (`chaos-dwarfs:great-taurus`) +- Lammasu (`chaos-dwarfs:lammasu`) + +### 2 scouting points + +- General (`chaos-dwarfs:general`) +- Hobgoblin Wolf Riders (`chaos-dwarfs:hobgoblin-wolf-riders`) + +### 1 scouting points + +- Bull Centaurs (`chaos-dwarfs:bull-centaurs`) +- Hero (`chaos-dwarfs:hero`) +- Sorcerer (`chaos-dwarfs:sorcerer`) + +### 0 scouting points + +- Black Orcs (`chaos-dwarfs:black-orcs`) +- Blunderbusses (`chaos-dwarfs:blunderbusses`) +- Bolt Thrower (`chaos-dwarfs:bolt-thrower`) +- Chaos Dwarfs (`chaos-dwarfs:chaos-dwarfs`) +- Death Rocket (`chaos-dwarfs:death-rocket`) +- Earthshaker Cannon (`chaos-dwarfs:earthshaker-cannon`) +- Hobgoblins (`chaos-dwarfs:hobgoblins`) +- Orc Slaves (`chaos-dwarfs:orc-slaves`) +- Sorcerer Lord (`chaos-dwarfs:sorcerer-lord`) + +## Wood Elves + +### 3 scouting points + +- Forest Dragon (`wood-elves:forest-dragon`) +- Warhawk (`wood-elves:warhawk`) +- Warhawk Riders (`wood-elves:warhawk-riders`) +- Waywatchers (`wood-elves:waywatchers`) + +### 2 scouting points + +- General (`wood-elves:general`) +- Glade Riders (`wood-elves:glade-riders`) + +### 1 scouting points + +- Branchwraith (`wood-elves:branchwraith`) +- Noble (`wood-elves:noble`) +- Spellweaver (`wood-elves:spellweaver`) +- Treeman Ancient (`wood-elves:treeman-ancient`) +- Wild Riders (`wood-elves:wild-riders`) + +### 0 scouting points + +- Dryads (`wood-elves:dryads`) +- Eternal Guard (`wood-elves:eternal-guard`) +- Giant Stag (`wood-elves:giant-stag`) +- Glade Guard (`wood-elves:glade-guard`) +- Treekin (`wood-elves:treekin`) +- Treeman (`wood-elves:treeman`) +- Unicorn (`wood-elves:unicorn`) +- Wardancers (`wood-elves:wardancers`) + +## Beastmen + +### 3 scouting points + +- None + +### 2 scouting points + +- Beastlord (`beastmen:beastlord`) +- Centigors (`beastmen:centigors`) +- Gor (`beastmen:gor`) +- Ungor (`beastmen:ungor`) + +### 1 scouting points + +- Bray Shaman (`beastmen:bray-shaman`) +- Chaos Hounds (`beastmen:chaos-hounds`) +- Doombull (`beastmen:doombull`) +- Wargor (`beastmen:wargor`) + +### 0 scouting points + +- Bestigors (`beastmen:bestigors`) +- Chaos Spawn (`beastmen:chaos-spawn`) +- Dragon Ogre Shaggoth (`beastmen:dragon-ogre-shaggoth`) +- Dragon Ogres (`beastmen:dragon-ogres`) +- Minotaurs (`beastmen:minotaurs`) +- Tuskgor Chariot (`beastmen:tuskgor-chariot`) +- Tuskgore Chariots (`beastmen:tuskgore-chariots`) + +## Norse + +### 3 scouting points + +- Valkyries (`norse:valkyries`) + +### 2 scouting points + +- Huntsmen (`norse:huntsmen`) +- Jarl (`norse:jarl`) + +### 1 scouting points + +- Hero (`norse:hero`) +- Hird Horsemen (`norse:hird-horsemen`) +- Shaman (`norse:shaman`) + +### 0 scouting points + +- Berserkers (`norse:berserkers`) +- Bondsmen (`norse:bondsmen`) +- Horn of Resounding (`norse:horn-of-resounding`) +- Huscarls (`norse:huscarls`) +- Storm Giant (`norse:storm-giant`) +- Ulfwerener (`norse:ulfwerener`) +- War Mammoth (`norse:war-mammoth`) +- Were Kin (`norse:were-kin`) + +## Cathay + +### 3 scouting points + +- Celestial Dragon (`cathay:celestial-dragon`) + +### 2 scouting points + +- General (`cathay:general`) +- Hu Cavalry (`cathay:hu-cavalry`) + +### 1 scouting points + +- Hero (`cathay:hero`) +- Imperial Cavalry (`cathay:imperial-cavalry`) +- Sorcerer (`cathay:sorcerer`) + +### 0 scouting points + +- Bannermen (`cathay:bannermen`) +- Chariot (`cathay:chariot`) +- Chariots (`cathay:chariots`) +- Crossbows (`cathay:crossbows`) +- Handguns (`cathay:handguns`) +- Maneaters (`cathay:maneaters`) +- Qilin (`cathay:qilin`) +- Rocket Launcher (`cathay:rocket-launcher`) +- Terracotta Warriors (`cathay:terracotta-warriors`) +- Tiger (`cathay:tiger`) +- Triple Bows (`cathay:triple-bows`) + +## Nippon + +### 3 scouting points + +- Tatsu (`nippon:tatsu`) +- Tengu (`nippon:tengu`) + +### 2 scouting points + +- Shogun (`nippon:shogun`) + +### 1 scouting points + +- Daimyo (`nippon:daimyo`) +- Komainu (`nippon:komainu`) +- Samurai Cavalry (`nippon:samurai-cavalry`) +- Shugenja (`nippon:shugenja`) + +### 0 scouting points + +- Ashigaru (`nippon:ashigaru`) +- Ashigaru Bowmen (`nippon:ashigaru-bowmen`) +- Ashigaru Teppo (`nippon:ashigaru-teppo`) +- Mikata (Ronin & Monks) (`nippon:mikata-ronin-monks`) +- Ninja (`nippon:ninja`) +- Oni (`nippon:oni`) +- Samurai (`nippon:samurai`) +- Shrine (`nippon:shrine`) + +## Regiments of Renown + +### 3 scouting points + +- Asarnil The Dragonlord (`regiments-of-renown:asarnil-the-dragonlord`) +- Birdmen of Catrazza (`regiments-of-renown:birdmen-of-catrazza`) + +### 2 scouting points + +- Al Muktar‘s Desert Dogs (`regiments-of-renown:al-muktars-desert-dogs`) +- Oglah Khan‘s Wolfboyz (`regiments-of-renown:oglah-khans-wolfboyz`) +- Tichi-Huichi‘s Raiders (`regiments-of-renown:tichi-huichis-raiders`) + +### 1 scouting points + +- Gotrek and Felix (`regiments-of-renown:gotrek-and-felix`) +- The Witch Hunters (`regiments-of-renown:the-witch-hunters`) + +### 0 scouting points + +- Alcatani Fellowship (`regiments-of-renown:alcatani-fellowship`) +- Anakonda‘s Amazons (`regiments-of-renown:anakondas-amazons`) +- Bearmen of Urslo (`regiments-of-renown:bearmen-of-urslo`) +- Braganza‘s Besiegers (`regiments-of-renown:braganzas-besiegers`) +- Bronzino‘s Galloper Guns (`regiments-of-renown:bronzinos-galloper-guns`) +- Golgfag‘s Mercenary Ogres (`regiments-of-renown:golgfags-mercenary-ogres`) +- Leopold‘s Leopard Company (`regiments-of-renown:leopolds-leopard-company`) +- Long Drong‘s Slayer Pirates (`regiments-of-renown:long-drongs-slayer-pirates`) +- Lumpin Croop‘s Cocks (`regiments-of-renown:lumpin-croops-cocks`) +- Marksmen of Miragliano (`regiments-of-renown:marksmen-of-miragliano`) +- Mengil‘s Manflyers (`regiments-of-renown:mengils-manflyers`) +- Pirazzo‘s Lost Legion (`regiments-of-renown:pirazzos-lost-legion`) +- Ricco‘s Republican Guard (`regiments-of-renown:riccos-republican-guard`) +- Ruglud‘s Armoured Orcs (`regiments-of-renown:rugluds-armoured-orcs`) +- The Cursed Company (`regiments-of-renown:the-cursed-company`) +- Vespero‘s Vendetta (`regiments-of-renown:vesperos-vendetta`) +- Voland‘s Venators (`regiments-of-renown:volands-venators`) + +# WMR - 2026 Playtest + +## Empire + +### 3 scouting points + +- Griffon (`empire:griffon`) + +### 2 scouting points + +- General (`empire:general`) +- Skirmishers (`empire:skirmishers`) + +### 1 scouting points + +- Hero (`empire:hero`) +- Pistoliers (`empire:pistoliers`) +- Wizard (`empire:wizard`) + +### 0 scouting points + +- Cannon (`empire:cannon`) +- Crossbowmen (`empire:crossbowmen`) +- Flagellants (`empire:flagellants`) +- Halberdiers (`empire:halberdiers`) +- Handgunners (`empire:handgunners`) +- Helblaster (`empire:helblaster`) +- Knights (`empire:knights`) +- Steam Tank (`empire:steam-tank`) +- War Altar (`empire:war-altar`) + +## Tomb Kings + +### 3 scouting points + +- Carrion (`tomb-kings:carrion`) +- Zombie Dragon (`tomb-kings:zombie-dragon`) + +### 2 scouting points + +- Tomb King (`tomb-kings:tomb-king`) + +### 1 scouting points + +- Liche Priest (`tomb-kings:liche-priest`) +- Skeleton Cavalry (`tomb-kings:skeleton-cavalry`) + +### 0 scouting points + +- Bone Giant (`tomb-kings:bone-giant`) +- Bone Thrower (`tomb-kings:bone-thrower`) +- Liche Chariot (`tomb-kings:liche-chariot`) +- Skeletal Chariots (`tomb-kings:skeletal-chariots`) +- Skeleton Bowmen (`tomb-kings:skeleton-bowmen`) +- Skeletons (`tomb-kings:skeletons`) +- Skull Chukka (`tomb-kings:skull-chukka`) +- Sphinx (`tomb-kings:sphinx`) + +## Chaos + +### 3 scouting points + +- Chaos Dragon (`chaos:chaos-dragon`) +- Harpies (`chaos:harpies`) + +### 2 scouting points + +- General (`chaos:general`) + +### 1 scouting points + +- Chaos Hounds (`chaos:chaos-hounds`) +- Hero (`chaos:hero`) +- Marauder Horsemen (`chaos:marauder-horsemen`) +- Sorcerer (`chaos:sorcerer`) + +### 0 scouting points + +- Chaos Chariots (`chaos:chaos-chariots`) +- Chaos Knights (`chaos:chaos-knights`) +- Chaos Marauders (`chaos:chaos-marauders`) +- Chaos Spawn (`chaos:chaos-spawn`) +- Chaos Warriors (`chaos:chaos-warriors`) +- Chariot (`chaos:chariot`) +- Dragon Ogres (`chaos:dragon-ogres`) +- Ogres (`chaos:ogres`) +- Trolls (`chaos:trolls`) + +## Orcs + +### 3 scouting points + +- Wyvern (`orcs:wyvern`) + +### 2 scouting points + +- Orc General (`orcs:orc-general`) +- Wolf Riders (`orcs:wolf-riders`) + +### 1 scouting points + +- Boar Riders (`orcs:boar-riders`) +- Goblin Hero (`orcs:goblin-hero`) +- Goblin Shaman (`orcs:goblin-shaman`) +- Orc Hero (`orcs:orc-hero`) +- Orc Shaman (`orcs:orc-shaman`) + +### 0 scouting points + +- Black Orcs (`orcs:black-orcs`) +- Chariot (`orcs:chariot`) +- Giant (`orcs:giant`) +- Goblins (`orcs:goblins`) +- Ogres (`orcs:ogres`) +- Orc Warriors (`orcs:orc-warriors`) +- Rock Lobber (`orcs:rock-lobber`) +- Trolls (`orcs:trolls`) +- Wolf Chariots (`orcs:wolf-chariots`) + +## High Elves + +### 3 scouting points + +- Dragon (`high-elves:dragon`) +- Dragon Rider (`high-elves:dragon-rider`) +- Giant Eagle (`high-elves:giant-eagle`) +- Giant Eagles (`high-elves:giant-eagles`) + +### 2 scouting points + +- General (`high-elves:general`) +- Reavers (`high-elves:reavers`) + +### 1 scouting points + +- Hero (`high-elves:hero`) +- Mage (`high-elves:mage`) + +### 0 scouting points + +- Archers (`high-elves:archers`) +- Chariot (`high-elves:chariot`) +- Chariots (`high-elves:chariots`) +- Elven Bolt Thrower (`high-elves:elven-bolt-thrower`) +- Silver Helms (`high-elves:silver-helms`) +- Spearmen (`high-elves:spearmen`) + +## Dwarfs + +### 3 scouting points + +- Gyrocopter (`dwarfs:gyrocopter`) +- Rangers (`dwarfs:rangers`) + +### 2 scouting points + +- General (`dwarfs:general`) + +### 1 scouting points + +- Hero (`dwarfs:hero`) +- Runesmith (`dwarfs:runesmith`) + +### 0 scouting points + +- Anvil (`dwarfs:anvil`) +- Cannon (`dwarfs:cannon`) +- Flame Cannon (`dwarfs:flame-cannon`) +- Handgunners (`dwarfs:handgunners`) +- Miners (`dwarfs:miners`) +- Oathstone (`dwarfs:oathstone`) +- Troll Slayers (`dwarfs:troll-slayers`) +- Warriors (`dwarfs:warriors`) + +## Skaven + +### 3 scouting points + +- Gutter Runners (`skaven:gutter-runners`) + +### 2 scouting points + +- Grey Seer (`skaven:grey-seer`) + +### 1 scouting points + +- Hero (`skaven:hero`) +- Rat Swarms (`skaven:rat-swarms`) +- Warlock (`skaven:warlock`) + +### 0 scouting points + +- Clanrats (`skaven:clanrats`) +- Doom Wheel (`skaven:doom-wheel`) +- Jezzails (`skaven:jezzails`) +- Plague Monks (`skaven:plague-monks`) +- Rat Ogre Bodyguard (`skaven:rat-ogre-bodyguard`) +- Rat Ogres (`skaven:rat-ogres`) +- Screaming Bell (`skaven:screaming-bell`) +- Stormvermin (`skaven:stormvermin`) +- Warp Lightning Cannon (`skaven:warp-lightning-cannon`) + +## Lizardmen + +### 3 scouting points + +- Terradons (`lizardmen:terradons`) + +### 2 scouting points + +- Slann Mage Palanquin (`lizardmen:slann-mage-palanquin`) + +### 1 scouting points + +- Saurus Hero (`lizardmen:saurus-hero`) +- Skink Hero (`lizardmen:skink-hero`) +- Skink Shaman (`lizardmen:skink-shaman`) +- Skinks (`lizardmen:skinks`) + +### 0 scouting points + +- Carnosaur (`lizardmen:carnosaur`) +- Cold One Riders (`lizardmen:cold-one-riders`) +- Kroxigor (`lizardmen:kroxigor`) +- Salamander (`lizardmen:salamander`) +- Saurus (`lizardmen:saurus`) +- Stegadon (`lizardmen:stegadon`) +- Stegadon (`lizardmen:stegadon-monstrous-mount`) +- Temple Guard (`lizardmen:temple-guard`) + +## Bretonnia + +### 3 scouting points + +- Hippogriff (`bretonnia:hippogriff`) +- Pegasus (`bretonnia:pegasus`) +- Pegasus Knights (`bretonnia:pegasus-knights`) + +### 2 scouting points + +- General (`bretonnia:general`) +- Squires (`bretonnia:squires`) + +### 1 scouting points + +- Enchantress (`bretonnia:enchantress`) +- Hero (`bretonnia:hero`) + +### 0 scouting points + +- Bowmen (`bretonnia:bowmen`) +- Grail Knights (`bretonnia:grail-knights`) +- Grail Reliquae (`bretonnia:grail-reliquae`) +- Knights (`bretonnia:knights`) +- Men-at-Arms (`bretonnia:men-at-arms`) +- Peasants (`bretonnia:peasants`) +- Trebuchet (`bretonnia:trebuchet`) +- Unicorn (`bretonnia:unicorn`) + +## Kislev + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`kislev:general`) +- Horse Archers (`kislev:horse-archers`) + +### 1 scouting points + +- Cossacks (`kislev:cossacks`) +- Hero (`kislev:hero`) +- Shaman (`kislev:shaman`) +- Winged Lancers (`kislev:winged-lancers`) + +### 0 scouting points + +- Axemen (`kislev:axemen`) +- Bear (`kislev:bear`) +- Bears (`kislev:bears`) +- Bowmen (`kislev:bowmen`) +- Red Guard (`kislev:red-guard`) +- Tzarina (`kislev:tzarina`) +- War Wagon (`kislev:war-wagon`) +- Yozhin (`kislev:yozhin`) + +## Dark Elves + +### 3 scouting points + +- Harpies (`dark-elves:harpies`) +- Manticore (`dark-elves:manticore`) + +### 2 scouting points + +- Dark Riders (`dark-elves:dark-riders`) +- General (`dark-elves:general`) + +### 1 scouting points + +- Hero (`dark-elves:hero`) +- Sorceress (`dark-elves:sorceress`) + +### 0 scouting points + +- Bolt Thrower (`dark-elves:bolt-thrower`) +- Cauldron of Blood (`dark-elves:cauldron-of-blood`) +- Chariot (`dark-elves:chariot`) +- Cold One Knights (`dark-elves:cold-one-knights`) +- Crossbowmen (`dark-elves:crossbowmen`) +- Spearmen (`dark-elves:spearmen`) +- War Hydra (`dark-elves:war-hydra`) +- Witch Elves (`dark-elves:witch-elves`) + +## Daemons + +### 3 scouting points + +- Daemon Flyers (`daemons:daemon-flyers`) +- Daemonic Wings (`daemons:daemonic-wings`) +- Greater Daemon (`daemons:greater-daemon`) + +### 2 scouting points + +- Daemon Overlord (`daemons:daemon-overlord`) + +### 1 scouting points + +- Daemon Cavalry (`daemons:daemon-cavalry`) +- Daemon Hounds (`daemons:daemon-hounds`) +- Daemon Lord Hero (`daemons:daemon-lord-hero`) +- Daemon Sorcerer (`daemons:daemon-sorcerer`) + +### 0 scouting points + +- Chariot (`daemons:chariot`) +- Daemon Beasts (`daemons:daemon-beasts`) +- Daemon Chariots (`daemons:daemon-chariots`) +- Daemon Horde (`daemons:daemon-horde`) +- Daemon Swarm (`daemons:daemon-swarm`) +- Favour of the Gods (`daemons:favour-of-the-gods`) + +## Vampire Counts + +### 3 scouting points + +- Fell Bats (`vampire-counts:fell-bats`) +- Winged Nightmare (`vampire-counts:winged-nightmare`) + +### 2 scouting points + +- Ethereal Host (`vampire-counts:ethereal-host`) +- Vampire Lord (`vampire-counts:vampire-lord`) + +### 1 scouting points + +- Dire Wolves (`vampire-counts:dire-wolves`) +- Necromancer (`vampire-counts:necromancer`) +- Vampire (`vampire-counts:vampire`) + +### 0 scouting points + +- Black Coach (`vampire-counts:black-coach`) +- Black Knights (`vampire-counts:black-knights`) +- Ghouls (`vampire-counts:ghouls`) +- Grave Guard (`vampire-counts:grave-guard`) +- Skeletons (`vampire-counts:skeletons`) +- Zombies (`vampire-counts:zombies`) + +## Araby + +### 3 scouting points + +- Djinn (`araby:djinn`) +- Flying Carpet (`araby:flying-carpet`) +- Magic Carpets (`araby:magic-carpets`) + +### 2 scouting points + +- Desert Riders (`araby:desert-riders`) +- General (`araby:general`) + +### 1 scouting points + +- Camel Riders (`araby:camel-riders`) +- Hero (`araby:hero`) +- Wizard (`araby:wizard`) + +### 0 scouting points + +- Bowmen (`araby:bowmen`) +- Elephant (`araby:elephant`) +- Elephants (`araby:elephants`) +- Guards (`araby:guards`) +- Knights (`araby:knights`) +- Spearmen (`araby:spearmen`) + +## Dogs of War + +### 3 scouting points + +- Birdmen (`dogs-of-war:birdmen`) +- Griffon (`dogs-of-war:griffon`) + +### 2 scouting points + +- General (`dogs-of-war:general`) +- Light Cavalry (`dogs-of-war:light-cavalry`) + +### 1 scouting points + +- Hero (`dogs-of-war:hero`) +- Paymaster (`dogs-of-war:paymaster`) +- Wizard (`dogs-of-war:wizard`) + +### 0 scouting points + +- Crossbowmen (`dogs-of-war:crossbowmen`) +- Dwarfs (`dogs-of-war:dwarfs`) +- Galloper Guns (`dogs-of-war:galloper-guns`) +- Giant (`dogs-of-war:giant`) +- Handgunners (`dogs-of-war:handgunners`) +- Knights (`dogs-of-war:knights`) +- Marauders (`dogs-of-war:marauders`) +- Ogres (`dogs-of-war:ogres`) +- Pay Wagon (`dogs-of-war:pay-wagon`) +- Pikemen (`dogs-of-war:pikemen`) +- Swordsmen (`dogs-of-war:swordsmen`) + +## Ogre Kingdoms + +### 3 scouting points + +- Gorgers (`ogre-kingdoms:gorgers`) + +### 2 scouting points + +- Tyrant (`ogre-kingdoms:tyrant`) + +### 1 scouting points + +- Bruiser (`ogre-kingdoms:bruiser`) +- Butcher (`ogre-kingdoms:butcher`) +- Rhinox Riders (`ogre-kingdoms:rhinox-riders`) +- Sabretusks (`ogre-kingdoms:sabretusks`) + +### 0 scouting points + +- Bull Ogres (`ogre-kingdoms:bull-ogres`) +- Bull Rhinox (`ogre-kingdoms:bull-rhinox`) +- Gnoblars (`ogre-kingdoms:gnoblars`) +- Ironguts (`ogre-kingdoms:ironguts`) +- Leadbelchers (`ogre-kingdoms:leadbelchers`) +- Scrap Launcher (`ogre-kingdoms:scrap-launcher`) +- Slave Giant (`ogre-kingdoms:slave-giant`) +- Yhetees (`ogre-kingdoms:yhetees`) + +## Albion + +### 3 scouting points + +- Giant Eagle (`albion:giant-eagle`) +- Giant Eagles (`albion:giant-eagles`) + +### 2 scouting points + +- General (`albion:general`) + +### 1 scouting points + +- Druid (`albion:druid`) +- Hero (`albion:hero`) +- Riders (`albion:riders`) +- Wolfhounds (`albion:wolfhounds`) + +### 0 scouting points + +- Chariot (`albion:chariot`) +- Chariots (`albion:chariots`) +- Fenbeast (`albion:fenbeast`) +- Giant (`albion:giant`) +- Ogres (`albion:ogres`) +- Slingers (`albion:slingers`) +- Warriors (`albion:warriors`) + +## Goblins + +### 3 scouting points + +- Wyvern (`goblins:wyvern`) + +### 2 scouting points + +- Goblin Warboss (`goblins:goblin-warboss`) +- Wolf Riders (`goblins:wolf-riders`) + +### 1 scouting points + +- Goblin Hero (`goblins:goblin-hero`) +- Goblin Shaman (`goblins:goblin-shaman`) + +### 0 scouting points + +- Chariot (`goblins:chariot`) +- Doom Diver (`goblins:doom-diver`) +- Giant (`goblins:giant`) +- Goblins (`goblins:goblins`) +- Pump Wagon (`goblins:pump-wagon`) +- Spear Chukka (`goblins:spear-chukka`) +- Squig Herd (`goblins:squig-herd`) +- Trolls (`goblins:trolls`) +- Wolf Chariots (`goblins:wolf-chariots`) + +## Witch Hunters + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`witch-hunters:general`) +- Warhounds (`witch-hunters:warhounds`) + +### 1 scouting points + +- Hero (`witch-hunters:hero`) +- Pistoliers (`witch-hunters:pistoliers`) +- Warrior Priest (`witch-hunters:warrior-priest`) + +### 0 scouting points + +- Cannon (`witch-hunters:cannon`) +- Crossbowmen (`witch-hunters:crossbowmen`) +- Flagellants (`witch-hunters:flagellants`) +- Halberdiers (`witch-hunters:halberdiers`) +- Handgunners (`witch-hunters:handgunners`) +- Knights (`witch-hunters:knights`) +- War Altar (`witch-hunters:war-altar`) +- Zealots (`witch-hunters:zealots`) + +## Chaos Dwarfs + +### 3 scouting points + +- Great Taurus (`chaos-dwarfs:great-taurus`) +- Lammasu (`chaos-dwarfs:lammasu`) + +### 2 scouting points + +- General (`chaos-dwarfs:general`) +- Hobgoblin Wolf Riders (`chaos-dwarfs:hobgoblin-wolf-riders`) + +### 1 scouting points + +- Bull Centaurs (`chaos-dwarfs:bull-centaurs`) +- Hero (`chaos-dwarfs:hero`) +- Sorcerer (`chaos-dwarfs:sorcerer`) + +### 0 scouting points + +- Black Orcs (`chaos-dwarfs:black-orcs`) +- Blunderbusses (`chaos-dwarfs:blunderbusses`) +- Bolt Thrower (`chaos-dwarfs:bolt-thrower`) +- Chaos Dwarfs (`chaos-dwarfs:chaos-dwarfs`) +- Death Rocket (`chaos-dwarfs:death-rocket`) +- Earthshaker Cannon (`chaos-dwarfs:earthshaker-cannon`) +- Hobgoblins (`chaos-dwarfs:hobgoblins`) +- Orc Slaves (`chaos-dwarfs:orc-slaves`) +- Sorcerer Lord (`chaos-dwarfs:sorcerer-lord`) + +## Wood Elves + +### 3 scouting points + +- Forest Dragon (`wood-elves:forest-dragon`) +- Warhawk (`wood-elves:warhawk`) +- Warhawk Riders (`wood-elves:warhawk-riders`) +- Waywatchers (`wood-elves:waywatchers`) + +### 2 scouting points + +- General (`wood-elves:general`) +- Glade Riders (`wood-elves:glade-riders`) + +### 1 scouting points + +- Branchwraith (`wood-elves:branchwraith`) +- Noble (`wood-elves:noble`) +- Spellweaver (`wood-elves:spellweaver`) +- Treeman Ancient (`wood-elves:treeman-ancient`) +- Wild Riders (`wood-elves:wild-riders`) + +### 0 scouting points + +- Dryads (`wood-elves:dryads`) +- Eternal Guard (`wood-elves:eternal-guard`) +- Giant Stag (`wood-elves:giant-stag`) +- Glade Guard (`wood-elves:glade-guard`) +- Treekin (`wood-elves:treekin`) +- Treeman (`wood-elves:treeman`) +- Unicorn (`wood-elves:unicorn`) +- Wardancers (`wood-elves:wardancers`) + +## Beastmen + +### 3 scouting points + +- None + +### 2 scouting points + +- Beastlord (`beastmen:beastlord`) +- Centigors (`beastmen:centigors`) +- Gor (`beastmen:gor`) +- Ungor (`beastmen:ungor`) + +### 1 scouting points + +- Bray Shaman (`beastmen:bray-shaman`) +- Chaos Hounds (`beastmen:chaos-hounds`) +- Doombull (`beastmen:doombull`) +- Wargor (`beastmen:wargor`) + +### 0 scouting points + +- Bestigors (`beastmen:bestigors`) +- Chaos Spawn (`beastmen:chaos-spawn`) +- Dragon Ogre Shaggoth (`beastmen:dragon-ogre-shaggoth`) +- Dragon Ogres (`beastmen:dragon-ogres`) +- Minotaurs (`beastmen:minotaurs`) +- Tuskgor Chariot (`beastmen:tuskgor-chariot`) +- Tuskgore Chariots (`beastmen:tuskgore-chariots`) + +## Norse + +### 3 scouting points + +- Valkyries (`norse:valkyries`) + +### 2 scouting points + +- Huntsmen (`norse:huntsmen`) +- Jarl (`norse:jarl`) + +### 1 scouting points + +- Hero (`norse:hero`) +- Hird Horsemen (`norse:hird-horsemen`) +- Shaman (`norse:shaman`) + +### 0 scouting points + +- Berserkers (`norse:berserkers`) +- Bondsmen (`norse:bondsmen`) +- Horn of Resounding (`norse:horn-of-resounding`) +- Huscarls (`norse:huscarls`) +- Storm Giant (`norse:storm-giant`) +- Ulfwerener (`norse:ulfwerener`) +- War Mammoth (`norse:war-mammoth`) +- Were Kin (`norse:were-kin`) + +## Cathay + +### 3 scouting points + +- Celestial Dragon (`cathay:celestial-dragon`) + +### 2 scouting points + +- General (`cathay:general`) +- Hu Cavalry (`cathay:hu-cavalry`) + +### 1 scouting points + +- Hero (`cathay:hero`) +- Imperial Cavalry (`cathay:imperial-cavalry`) +- Sorcerer (`cathay:sorcerer`) + +### 0 scouting points + +- Bannermen (`cathay:bannermen`) +- Chariot (`cathay:chariot`) +- Chariots (`cathay:chariots`) +- Crossbows (`cathay:crossbows`) +- Handguns (`cathay:handguns`) +- Maneaters (`cathay:maneaters`) +- Qilin (`cathay:qilin`) +- Rocket Launcher (`cathay:rocket-launcher`) +- Terracotta Warriors (`cathay:terracotta-warriors`) +- Tiger (`cathay:tiger`) +- Triple Bows (`cathay:triple-bows`) + +## Nippon + +### 3 scouting points + +- Tatsu (`nippon:tatsu`) +- Tengu (`nippon:tengu`) + +### 2 scouting points + +- Shogun (`nippon:shogun`) + +### 1 scouting points + +- Daimyo (`nippon:daimyo`) +- Komainu (`nippon:komainu`) +- Samurai Cavalry (`nippon:samurai-cavalry`) +- Shugenja (`nippon:shugenja`) + +### 0 scouting points + +- Ashigaru (`nippon:ashigaru`) +- Ashigaru Bowmen (`nippon:ashigaru-bowmen`) +- Ashigaru Teppo (`nippon:ashigaru-teppo`) +- Mikata (Ronin & Monks) (`nippon:mikata-ronin-monks`) +- Ninja (`nippon:ninja`) +- Oni (`nippon:oni`) +- Samurai (`nippon:samurai`) +- Shrine (`nippon:shrine`) + +## Regiments of Renown + +### 3 scouting points + +- Asarnil The Dragonlord (`regiments-of-renown:asarnil-the-dragonlord`) +- Birdmen of Catrazza (`regiments-of-renown:birdmen-of-catrazza`) + +### 2 scouting points + +- Al Muktar‘s Desert Dogs (`regiments-of-renown:al-muktars-desert-dogs`) +- Oglah Khan‘s Wolfboyz (`regiments-of-renown:oglah-khans-wolfboyz`) +- Tichi-Huichi‘s Raiders (`regiments-of-renown:tichi-huichis-raiders`) + +### 1 scouting points + +- Gotrek and Felix (`regiments-of-renown:gotrek-and-felix`) +- The Witch Hunters (`regiments-of-renown:the-witch-hunters`) + +### 0 scouting points + +- Alcatani Fellowship (`regiments-of-renown:alcatani-fellowship`) +- Anakonda‘s Amazons (`regiments-of-renown:anakondas-amazons`) +- Bearmen of Urslo (`regiments-of-renown:bearmen-of-urslo`) +- Braganza‘s Besiegers (`regiments-of-renown:braganzas-besiegers`) +- Bronzino‘s Galloper Guns (`regiments-of-renown:bronzinos-galloper-guns`) +- Golgfag‘s Mercenary Ogres (`regiments-of-renown:golgfags-mercenary-ogres`) +- Leopold‘s Leopard Company (`regiments-of-renown:leopolds-leopard-company`) +- Long Drong‘s Slayer Pirates (`regiments-of-renown:long-drongs-slayer-pirates`) +- Lumpin Croop‘s Cocks (`regiments-of-renown:lumpin-croops-cocks`) +- Marksmen of Miragliano (`regiments-of-renown:marksmen-of-miragliano`) +- Mengil‘s Manflyers (`regiments-of-renown:mengils-manflyers`) +- Pirazzo‘s Lost Legion (`regiments-of-renown:pirazzos-lost-legion`) +- Ricco‘s Republican Guard (`regiments-of-renown:riccos-republican-guard`) +- Ruglud‘s Armoured Orcs (`regiments-of-renown:rugluds-armoured-orcs`) +- The Cursed Company (`regiments-of-renown:the-cursed-company`) +- Vespero‘s Vendetta (`regiments-of-renown:vesperos-vendetta`) +- Voland‘s Venators (`regiments-of-renown:volands-venators`) + +# A Matter of Mustaches + +## Empire + +### 3 scouting points + +- Griffon (`empire:griffon`) + +### 2 scouting points + +- General (`empire:general`) +- Skirmishers (`empire:skirmishers`) + +### 1 scouting points + +- Hero (`empire:hero`) +- Pistoliers (`empire:pistoliers`) +- Wizard (`empire:wizard`) + +### 0 scouting points + +- Cannon (`empire:cannon`) +- Crossbowmen (`empire:crossbowmen`) +- Flagellants (`empire:flagellants`) +- Halberdiers (`empire:halberdiers`) +- Handgunners (`empire:handgunners`) +- Helblaster (`empire:helblaster`) +- Knights (`empire:knights`) +- Steam Tank (`empire:steam-tank`) +- War Altar (`empire:war-altar`) + +## Tomb Kings + +### 3 scouting points + +- Carrion (`tomb-kings:carrion`) +- Zombie Dragon (`tomb-kings:zombie-dragon`) + +### 2 scouting points + +- Tomb King (`tomb-kings:tomb-king`) + +### 1 scouting points + +- Liche Priest (`tomb-kings:liche-priest`) +- Skeleton Cavalry (`tomb-kings:skeleton-cavalry`) + +### 0 scouting points + +- Bone Giant (`tomb-kings:bone-giant`) +- Bone Thrower (`tomb-kings:bone-thrower`) +- Liche Chariot (`tomb-kings:liche-chariot`) +- Skeletal Chariots (`tomb-kings:skeletal-chariots`) +- Skeleton Bowmen (`tomb-kings:skeleton-bowmen`) +- Skeletons (`tomb-kings:skeletons`) +- Skull Chukka (`tomb-kings:skull-chukka`) +- Sphinx (`tomb-kings:sphinx`) + +## Chaos + +### 3 scouting points + +- Chaos Dragon (`chaos:chaos-dragon`) +- Harpies (`chaos:harpies`) + +### 2 scouting points + +- General (`chaos:general`) + +### 1 scouting points + +- Chaos Hounds (`chaos:chaos-hounds`) +- Hero (`chaos:hero`) +- Marauder Horsemen (`chaos:marauder-horsemen`) +- Sorcerer (`chaos:sorcerer`) + +### 0 scouting points + +- Chaos Chariots (`chaos:chaos-chariots`) +- Chaos Knights (`chaos:chaos-knights`) +- Chaos Marauders (`chaos:chaos-marauders`) +- Chaos Spawn (`chaos:chaos-spawn`) +- Chaos Warriors (`chaos:chaos-warriors`) +- Chariot (`chaos:chariot`) +- Dragon Ogres (`chaos:dragon-ogres`) +- Ogres (`chaos:ogres`) +- Trolls (`chaos:trolls`) + +## Orcs + +### 3 scouting points + +- Wyvern (`orcs:wyvern`) + +### 2 scouting points + +- Orc General (`orcs:orc-general`) +- Wolf Riders (`orcs:wolf-riders`) + +### 1 scouting points + +- Boar Riders (`orcs:boar-riders`) +- Goblin Hero (`orcs:goblin-hero`) +- Goblin Shaman (`orcs:goblin-shaman`) +- Orc Hero (`orcs:orc-hero`) +- Orc Shaman (`orcs:orc-shaman`) + +### 0 scouting points + +- Black Orcs (`orcs:black-orcs`) +- Chariot (`orcs:chariot`) +- Giant (`orcs:giant`) +- Goblins (`orcs:goblins`) +- Ogres (`orcs:ogres`) +- Orc Warriors (`orcs:orc-warriors`) +- Rock Lobber (`orcs:rock-lobber`) +- Trolls (`orcs:trolls`) +- Wolf Chariots (`orcs:wolf-chariots`) + +## High Elves + +### 3 scouting points + +- Dragon (`high-elves:dragon`) +- Dragon Rider (`high-elves:dragon-rider`) +- Giant Eagle (`high-elves:giant-eagle`) +- Giant Eagles (`high-elves:giant-eagles`) + +### 2 scouting points + +- General (`high-elves:general`) +- Reavers (`high-elves:reavers`) + +### 1 scouting points + +- Hero (`high-elves:hero`) +- Mage (`high-elves:mage`) + +### 0 scouting points + +- Archers (`high-elves:archers`) +- Chariot (`high-elves:chariot`) +- Chariots (`high-elves:chariots`) +- Elven Bolt Thrower (`high-elves:elven-bolt-thrower`) +- Silver Helms (`high-elves:silver-helms`) +- Spearmen (`high-elves:spearmen`) + +## Dwarfs + +### 3 scouting points + +- Gyrocopter (`dwarfs:gyrocopter`) +- Rangers (`dwarfs:rangers`) +- Torgo Thungnisson (Mounted) (`dwarfs:torgo-thungnisson-mounted`) + +### 2 scouting points + +- Ancestral Warriors (`dwarfs:ancestral-warriors`) +- General (`dwarfs:general`) + +### 1 scouting points + +- Aldrich of the Unberogen (`dwarfs:aldrich-of-the-unberogen`) +- Brok Wheatbrow (`dwarfs:brok-wheatbrow`) +- Dorin Khazadsson (`dwarfs:dorin-khazadsson`) +- Dramar Thungnisson (`dwarfs:dramar-thungnisson`) +- Dramar Thungnisson (Mounted) (`dwarfs:dramar-thungnisson-mounted`) +- Hero (`dwarfs:hero`) +- Kadri Broadbrew (`dwarfs:kadri-broadbrew`) +- Logor Hardhaft (`dwarfs:logor-hardhaft`) +- Ram Riders (`dwarfs:ram-riders`) +- Roknar Gromdal (`dwarfs:roknar-gromdal`) +- Runesmith (`dwarfs:runesmith`) +- Rungni Silverhorn (`dwarfs:rungni-silverhorn`) +- Torgo Thungnisson (`dwarfs:torgo-thungnisson`) +- Yorri Thungnisson (`dwarfs:yorri-thungnisson`) + +### 0 scouting points + +- Anvil (`dwarfs:anvil`) +- Bull Ogres (`dwarfs:bull-ogres`) +- Cannon (`dwarfs:cannon`) +- Flame Cannon (`dwarfs:flame-cannon`) +- Handgunners (`dwarfs:handgunners`) +- Iron Breakers (`dwarfs:iron-breakers`) +- Miners (`dwarfs:miners`) +- Oathstone (`dwarfs:oathstone`) +- Troll Slayers (`dwarfs:troll-slayers`) +- Warriors (`dwarfs:warriors`) + +## Skaven + +### 3 scouting points + +- Gutter Runners (`skaven:gutter-runners`) + +### 2 scouting points + +- Grey Seer (`skaven:grey-seer`) + +### 1 scouting points + +- Hero (`skaven:hero`) +- Rat Swarms (`skaven:rat-swarms`) +- Warlock (`skaven:warlock`) + +### 0 scouting points + +- Clanrats (`skaven:clanrats`) +- Doom Wheel (`skaven:doom-wheel`) +- Jezzails (`skaven:jezzails`) +- Plague Monks (`skaven:plague-monks`) +- Rat Ogre Bodyguard (`skaven:rat-ogre-bodyguard`) +- Rat Ogres (`skaven:rat-ogres`) +- Screaming Bell (`skaven:screaming-bell`) +- Stormvermin (`skaven:stormvermin`) +- Warp Lightning Cannon (`skaven:warp-lightning-cannon`) + +## Lizardmen + +### 3 scouting points + +- Terradons (`lizardmen:terradons`) + +### 2 scouting points + +- Slann Mage Palanquin (`lizardmen:slann-mage-palanquin`) + +### 1 scouting points + +- Saurus Hero (`lizardmen:saurus-hero`) +- Skink Hero (`lizardmen:skink-hero`) +- Skink Shaman (`lizardmen:skink-shaman`) +- Skinks (`lizardmen:skinks`) + +### 0 scouting points + +- Carnosaur (`lizardmen:carnosaur`) +- Cold One Riders (`lizardmen:cold-one-riders`) +- Kroxigor (`lizardmen:kroxigor`) +- Salamander (`lizardmen:salamander`) +- Saurus (`lizardmen:saurus`) +- Stegadon (`lizardmen:stegadon`) +- Stegadon (`lizardmen:stegadon-monstrous-mount`) +- Temple Guard (`lizardmen:temple-guard`) + +## Bretonnia + +### 3 scouting points + +- Hippogriff (`bretonnia:hippogriff`) +- Pegasus (`bretonnia:pegasus`) +- Pegasus Knights (`bretonnia:pegasus-knights`) + +### 2 scouting points + +- General (`bretonnia:general`) +- Squires (`bretonnia:squires`) + +### 1 scouting points + +- Enchantress (`bretonnia:enchantress`) +- Hero (`bretonnia:hero`) + +### 0 scouting points + +- Bowmen (`bretonnia:bowmen`) +- Grail Knights (`bretonnia:grail-knights`) +- Grail Reliquae (`bretonnia:grail-reliquae`) +- Knights (`bretonnia:knights`) +- Men-at-Arms (`bretonnia:men-at-arms`) +- Peasants (`bretonnia:peasants`) +- Trebuchet (`bretonnia:trebuchet`) +- Unicorn (`bretonnia:unicorn`) + +## Kislev + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`kislev:general`) +- Horse Archers (`kislev:horse-archers`) + +### 1 scouting points + +- Cossacks (`kislev:cossacks`) +- Hero (`kislev:hero`) +- Shaman (`kislev:shaman`) +- Winged Lancers (`kislev:winged-lancers`) + +### 0 scouting points + +- Axemen (`kislev:axemen`) +- Bear (`kislev:bear`) +- Bears (`kislev:bears`) +- Bowmen (`kislev:bowmen`) +- Red Guard (`kislev:red-guard`) +- Tzarina (`kislev:tzarina`) +- War Wagon (`kislev:war-wagon`) +- Yozhin (`kislev:yozhin`) + +## Dark Elves + +### 3 scouting points + +- Harpies (`dark-elves:harpies`) +- Manticore (`dark-elves:manticore`) + +### 2 scouting points + +- Dark Riders (`dark-elves:dark-riders`) +- General (`dark-elves:general`) + +### 1 scouting points + +- Hero (`dark-elves:hero`) +- Sorceress (`dark-elves:sorceress`) + +### 0 scouting points + +- Bolt Thrower (`dark-elves:bolt-thrower`) +- Cauldron of Blood (`dark-elves:cauldron-of-blood`) +- Chariot (`dark-elves:chariot`) +- Cold One Knights (`dark-elves:cold-one-knights`) +- Crossbowmen (`dark-elves:crossbowmen`) +- Spearmen (`dark-elves:spearmen`) +- War Hydra (`dark-elves:war-hydra`) +- Witch Elves (`dark-elves:witch-elves`) + +## Daemons + +### 3 scouting points + +- Daemon Flyers (`daemons:daemon-flyers`) +- Daemonic Wings (`daemons:daemonic-wings`) +- Greater Daemon (`daemons:greater-daemon`) + +### 2 scouting points + +- Daemon Overlord (`daemons:daemon-overlord`) + +### 1 scouting points + +- Daemon Cavalry (`daemons:daemon-cavalry`) +- Daemon Hounds (`daemons:daemon-hounds`) +- Daemon Lord Hero (`daemons:daemon-lord-hero`) +- Daemon Sorcerer (`daemons:daemon-sorcerer`) + +### 0 scouting points + +- Chariot (`daemons:chariot`) +- Daemon Beasts (`daemons:daemon-beasts`) +- Daemon Chariots (`daemons:daemon-chariots`) +- Daemon Horde (`daemons:daemon-horde`) +- Daemon Swarm (`daemons:daemon-swarm`) +- Favour of the Gods (`daemons:favour-of-the-gods`) + +## Vampire Counts + +### 3 scouting points + +- Fell Bats (`vampire-counts:fell-bats`) +- Winged Nightmare (`vampire-counts:winged-nightmare`) + +### 2 scouting points + +- Ethereal Host (`vampire-counts:ethereal-host`) +- Vampire Lord (`vampire-counts:vampire-lord`) + +### 1 scouting points + +- Dire Wolves (`vampire-counts:dire-wolves`) +- Necromancer (`vampire-counts:necromancer`) +- Vampire (`vampire-counts:vampire`) + +### 0 scouting points + +- Black Coach (`vampire-counts:black-coach`) +- Black Knights (`vampire-counts:black-knights`) +- Ghouls (`vampire-counts:ghouls`) +- Grave Guard (`vampire-counts:grave-guard`) +- Skeletons (`vampire-counts:skeletons`) +- Zombies (`vampire-counts:zombies`) + +## Araby + +### 3 scouting points + +- Djinn (`araby:djinn`) +- Flying Carpet (`araby:flying-carpet`) +- Magic Carpets (`araby:magic-carpets`) + +### 2 scouting points + +- Desert Riders (`araby:desert-riders`) +- General (`araby:general`) + +### 1 scouting points + +- Camel Riders (`araby:camel-riders`) +- Hero (`araby:hero`) +- Wizard (`araby:wizard`) + +### 0 scouting points + +- Bowmen (`araby:bowmen`) +- Elephant (`araby:elephant`) +- Elephants (`araby:elephants`) +- Guards (`araby:guards`) +- Knights (`araby:knights`) +- Spearmen (`araby:spearmen`) + +## Dogs of War + +### 3 scouting points + +- Birdmen (`dogs-of-war:birdmen`) +- Griffon (`dogs-of-war:griffon`) + +### 2 scouting points + +- General (`dogs-of-war:general`) +- Light Cavalry (`dogs-of-war:light-cavalry`) + +### 1 scouting points + +- Hero (`dogs-of-war:hero`) +- Paymaster (`dogs-of-war:paymaster`) +- Wizard (`dogs-of-war:wizard`) + +### 0 scouting points + +- Crossbowmen (`dogs-of-war:crossbowmen`) +- Dwarfs (`dogs-of-war:dwarfs`) +- Galloper Guns (`dogs-of-war:galloper-guns`) +- Giant (`dogs-of-war:giant`) +- Handgunners (`dogs-of-war:handgunners`) +- Knights (`dogs-of-war:knights`) +- Marauders (`dogs-of-war:marauders`) +- Ogres (`dogs-of-war:ogres`) +- Pay Wagon (`dogs-of-war:pay-wagon`) +- Pikemen (`dogs-of-war:pikemen`) +- Swordsmen (`dogs-of-war:swordsmen`) + +## Ogre Kingdoms + +### 3 scouting points + +- Gorgers (`ogre-kingdoms:gorgers`) + +### 2 scouting points + +- Tyrant (`ogre-kingdoms:tyrant`) + +### 1 scouting points + +- Bruiser (`ogre-kingdoms:bruiser`) +- Butcher (`ogre-kingdoms:butcher`) +- Rhinox Riders (`ogre-kingdoms:rhinox-riders`) +- Sabretusks (`ogre-kingdoms:sabretusks`) + +### 0 scouting points + +- Bull Ogres (`ogre-kingdoms:bull-ogres`) +- Bull Rhinox (`ogre-kingdoms:bull-rhinox`) +- Gnoblars (`ogre-kingdoms:gnoblars`) +- Ironguts (`ogre-kingdoms:ironguts`) +- Leadbelchers (`ogre-kingdoms:leadbelchers`) +- Scrap Launcher (`ogre-kingdoms:scrap-launcher`) +- Slave Giant (`ogre-kingdoms:slave-giant`) +- Yhetees (`ogre-kingdoms:yhetees`) + +## Albion + +### 3 scouting points + +- Giant Eagle (`albion:giant-eagle`) +- Giant Eagles (`albion:giant-eagles`) + +### 2 scouting points + +- General (`albion:general`) + +### 1 scouting points + +- Druid (`albion:druid`) +- Hero (`albion:hero`) +- Riders (`albion:riders`) +- Wolfhounds (`albion:wolfhounds`) + +### 0 scouting points + +- Chariot (`albion:chariot`) +- Chariots (`albion:chariots`) +- Fenbeast (`albion:fenbeast`) +- Giant (`albion:giant`) +- Ogres (`albion:ogres`) +- Slingers (`albion:slingers`) +- Warriors (`albion:warriors`) + +## Goblins + +### 3 scouting points + +- Wyvern (`goblins:wyvern`) + +### 2 scouting points + +- Goblin Warboss (`goblins:goblin-warboss`) +- Wolf Riders (`goblins:wolf-riders`) + +### 1 scouting points + +- Goblin Hero (`goblins:goblin-hero`) +- Goblin Shaman (`goblins:goblin-shaman`) + +### 0 scouting points + +- Chariot (`goblins:chariot`) +- Doom Diver (`goblins:doom-diver`) +- Giant (`goblins:giant`) +- Giant Squig (`goblins:giant-squig`) +- Goblins (`goblins:goblins`) +- Pump Wagon (`goblins:pump-wagon`) +- Spear Chukka (`goblins:spear-chukka`) +- Squig Herd (`goblins:squig-herd`) +- Trolls (`goblins:trolls`) +- Wolf Chariots (`goblins:wolf-chariots`) + +## Witch Hunters + +### 3 scouting points + +- None + +### 2 scouting points + +- General (`witch-hunters:general`) +- Warhounds (`witch-hunters:warhounds`) + +### 1 scouting points + +- Hero (`witch-hunters:hero`) +- Pistoliers (`witch-hunters:pistoliers`) +- Warrior Priest (`witch-hunters:warrior-priest`) + +### 0 scouting points + +- Cannon (`witch-hunters:cannon`) +- Crossbowmen (`witch-hunters:crossbowmen`) +- Flagellants (`witch-hunters:flagellants`) +- Halberdiers (`witch-hunters:halberdiers`) +- Handgunners (`witch-hunters:handgunners`) +- Knights (`witch-hunters:knights`) +- War Altar (`witch-hunters:war-altar`) +- Zealots (`witch-hunters:zealots`) + +## Chaos Dwarfs + +### 3 scouting points + +- Great Taurus (`chaos-dwarfs:great-taurus`) +- Lammasu (`chaos-dwarfs:lammasu`) + +### 2 scouting points + +- General (`chaos-dwarfs:general`) +- Hobgoblin Wolf Riders (`chaos-dwarfs:hobgoblin-wolf-riders`) + +### 1 scouting points + +- Bull Centaurs (`chaos-dwarfs:bull-centaurs`) +- Hero (`chaos-dwarfs:hero`) +- Sorcerer (`chaos-dwarfs:sorcerer`) + +### 0 scouting points + +- Black Orcs (`chaos-dwarfs:black-orcs`) +- Blunderbusses (`chaos-dwarfs:blunderbusses`) +- Bolt Thrower (`chaos-dwarfs:bolt-thrower`) +- Chaos Dwarfs (`chaos-dwarfs:chaos-dwarfs`) +- Death Rocket (`chaos-dwarfs:death-rocket`) +- Earthshaker Cannon (`chaos-dwarfs:earthshaker-cannon`) +- Hobgoblins (`chaos-dwarfs:hobgoblins`) +- Orc Slaves (`chaos-dwarfs:orc-slaves`) +- Sorcerer Lord (`chaos-dwarfs:sorcerer-lord`) + +## Wood Elves + +### 3 scouting points + +- Forest Dragon (`wood-elves:forest-dragon`) +- Warhawk (`wood-elves:warhawk`) +- Warhawk Riders (`wood-elves:warhawk-riders`) +- Waywatchers (`wood-elves:waywatchers`) + +### 2 scouting points + +- General (`wood-elves:general`) +- Glade Riders (`wood-elves:glade-riders`) + +### 1 scouting points + +- Branchwraith (`wood-elves:branchwraith`) +- Noble (`wood-elves:noble`) +- Spellweaver (`wood-elves:spellweaver`) +- Treeman Ancient (`wood-elves:treeman-ancient`) +- Wild Riders (`wood-elves:wild-riders`) + +### 0 scouting points + +- Dryads (`wood-elves:dryads`) +- Eternal Guard (`wood-elves:eternal-guard`) +- Giant Stag (`wood-elves:giant-stag`) +- Glade Guard (`wood-elves:glade-guard`) +- Treekin (`wood-elves:treekin`) +- Treeman (`wood-elves:treeman`) +- Unicorn (`wood-elves:unicorn`) +- Wardancers (`wood-elves:wardancers`) + +## Beastmen + +### 3 scouting points + +- None + +### 2 scouting points + +- Beastlord (`beastmen:beastlord`) +- Centigors (`beastmen:centigors`) +- Gor (`beastmen:gor`) +- Ungor (`beastmen:ungor`) + +### 1 scouting points + +- Bray Shaman (`beastmen:bray-shaman`) +- Chaos Hounds (`beastmen:chaos-hounds`) +- Doombull (`beastmen:doombull`) +- Wargor (`beastmen:wargor`) + +### 0 scouting points + +- Bestigors (`beastmen:bestigors`) +- Chaos Spawn (`beastmen:chaos-spawn`) +- Dragon Ogre Shaggoth (`beastmen:dragon-ogre-shaggoth`) +- Dragon Ogres (`beastmen:dragon-ogres`) +- Minotaurs (`beastmen:minotaurs`) +- Tuskgor Chariot (`beastmen:tuskgor-chariot`) +- Tuskgore Chariots (`beastmen:tuskgore-chariots`) + +## Norse + +### 3 scouting points + +- Valkyries (`norse:valkyries`) + +### 2 scouting points + +- Huntsmen (`norse:huntsmen`) +- Jarl (`norse:jarl`) + +### 1 scouting points + +- Hero (`norse:hero`) +- Hird Horsemen (`norse:hird-horsemen`) +- Shaman (`norse:shaman`) + +### 0 scouting points + +- Berserkers (`norse:berserkers`) +- Bondsmen (`norse:bondsmen`) +- Horn of Resounding (`norse:horn-of-resounding`) +- Huscarls (`norse:huscarls`) +- Storm Giant (`norse:storm-giant`) +- Ulfwerener (`norse:ulfwerener`) +- War Mammoth (`norse:war-mammoth`) +- Were Kin (`norse:were-kin`) + +## Cathay + +### 3 scouting points + +- Celestial Dragon (`cathay:celestial-dragon`) + +### 2 scouting points + +- General (`cathay:general`) +- Hu Cavalry (`cathay:hu-cavalry`) + +### 1 scouting points + +- Hero (`cathay:hero`) +- Imperial Cavalry (`cathay:imperial-cavalry`) +- Sorcerer (`cathay:sorcerer`) + +### 0 scouting points + +- Bannermen (`cathay:bannermen`) +- Chariot (`cathay:chariot`) +- Chariots (`cathay:chariots`) +- Crossbows (`cathay:crossbows`) +- Handguns (`cathay:handguns`) +- Maneaters (`cathay:maneaters`) +- Qilin (`cathay:qilin`) +- Rocket Launcher (`cathay:rocket-launcher`) +- Terracotta Warriors (`cathay:terracotta-warriors`) +- Tiger (`cathay:tiger`) +- Triple Bows (`cathay:triple-bows`) + +## Nippon + +### 3 scouting points + +- Tatsu (`nippon:tatsu`) +- Tengu (`nippon:tengu`) + +### 2 scouting points + +- Shogun (`nippon:shogun`) + +### 1 scouting points + +- Daimyo (`nippon:daimyo`) +- Komainu (`nippon:komainu`) +- Samurai Cavalry (`nippon:samurai-cavalry`) +- Shugenja (`nippon:shugenja`) + +### 0 scouting points + +- Ashigaru (`nippon:ashigaru`) +- Ashigaru Bowmen (`nippon:ashigaru-bowmen`) +- Ashigaru Teppo (`nippon:ashigaru-teppo`) +- Mikata (Ronin & Monks) (`nippon:mikata-ronin-monks`) +- Ninja (`nippon:ninja`) +- Oni (`nippon:oni`) +- Samurai (`nippon:samurai`) +- Shrine (`nippon:shrine`) + +## Regiments of Renown + +### 3 scouting points + +- Asarnil The Dragonlord (`regiments-of-renown:asarnil-the-dragonlord`) +- Birdmen of Catrazza (`regiments-of-renown:birdmen-of-catrazza`) + +### 2 scouting points + +- Al Muktar‘s Desert Dogs (`regiments-of-renown:al-muktars-desert-dogs`) +- Oglah Khan‘s Wolfboyz (`regiments-of-renown:oglah-khans-wolfboyz`) +- Tichi-Huichi‘s Raiders (`regiments-of-renown:tichi-huichis-raiders`) + +### 1 scouting points + +- Gotrek and Felix (`regiments-of-renown:gotrek-and-felix`) +- The Witch Hunters (`regiments-of-renown:the-witch-hunters`) + +### 0 scouting points + +- Alcatani Fellowship (`regiments-of-renown:alcatani-fellowship`) +- Anakonda‘s Amazons (`regiments-of-renown:anakondas-amazons`) +- Bearmen of Urslo (`regiments-of-renown:bearmen-of-urslo`) +- Braganza‘s Besiegers (`regiments-of-renown:braganzas-besiegers`) +- Bronzino‘s Galloper Guns (`regiments-of-renown:bronzinos-galloper-guns`) +- Golgfag‘s Mercenary Ogres (`regiments-of-renown:golgfags-mercenary-ogres`) +- Leopold‘s Leopard Company (`regiments-of-renown:leopolds-leopard-company`) +- Long Drong‘s Slayer Pirates (`regiments-of-renown:long-drongs-slayer-pirates`) +- Lumpin Croop‘s Cocks (`regiments-of-renown:lumpin-croops-cocks`) +- Marksmen of Miragliano (`regiments-of-renown:marksmen-of-miragliano`) +- Mengil‘s Manflyers (`regiments-of-renown:mengils-manflyers`) +- Pirazzo‘s Lost Legion (`regiments-of-renown:pirazzos-lost-legion`) +- Ricco‘s Republican Guard (`regiments-of-renown:riccos-republican-guard`) +- Ruglud‘s Armoured Orcs (`regiments-of-renown:rugluds-armoured-orcs`) +- The Cursed Company (`regiments-of-renown:the-cursed-company`) +- Vespero‘s Vendetta (`regiments-of-renown:vesperos-vendetta`) +- Voland‘s Venators (`regiments-of-renown:volands-venators`) diff --git a/roadmap.md b/roadmap.md deleted file mode 100644 index bf74830..0000000 --- a/roadmap.md +++ /dev/null @@ -1,18 +0,0 @@ -# Roadmap & Bug Tracker - -## Main UI -1. X to delete lists is not centered -2. We have duplicate general validations: -The army must include a General. -Warriors: at least 8 required (0 selected). -General: at least 1 required (0 selected). -Get rid of the top one. -3. If a list is invalid add a one-click fix, if the army is missing units had a 'Add Minimums' button under the validator, if the army has units that are too large have a 'Remove Excess' button under the validator. if both are happening show both buttons -4. Make changelog bigger - -## List Building -1. ~~Create Magic Item UI~~ Done — "Assign magic items" button in the catalog opens the assignment dialog; items split their unit out of the stack. - -## Unit Cards -1. If ranged attacks are 2+bounce or otherwise long have this break to next line to not overlap with special rules -2. Print cannon rules on cannon cards \ No newline at end of file diff --git a/schema.md b/schema.md index 202153e..b17ca0f 100644 --- a/schema.md +++ b/schema.md @@ -34,6 +34,7 @@ Every unit object should use the same field structure: - `type` - `subType` - `category` +- `scoutingPoints` — generated integer from 0 to 3 for the optional Scouting deployment rules - `facing` - `speed` - `halfPace` diff --git a/scripts/apply-scouting.mjs b/scripts/apply-scouting.mjs new file mode 100644 index 0000000..502c33f --- /dev/null +++ b/scripts/apply-scouting.mjs @@ -0,0 +1,134 @@ +// Assign Scouting deployment values to normalized army data and produce an +// auditable report for all shipped rule sets. +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { applyCustomUnits } from "../src/data/customUnits.ts"; +import { applyPlaytest2026 } from "../src/data/playtest2026.ts"; + +const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const baseFile = path.join(root, "src", "data", "generated", "warmaster-revolution.json"); +const lookupFile = path.join(root, "src", "data", "generated", "scouting-points.json"); +const reportFile = path.join(root, "reports", "scouting-points.md"); + +const FLYER_IDS = new Set([ + // These units fly by special rule but predate the normalized Flying subtype. + "dogs-of-war:birdmen", + "regiments-of-renown:birdmen-of-catrazza", + "dwarfs:torgo-thungnisson-mounted", +]); +const FLYER_SCOUT_IDS = new Set([ + "dwarfs:rangers", + "skaven:gutter-runners", + "ogre-kingdoms:gorgers", + "wood-elves:waywatchers", +]); +const SCOUT_IDS = new Set([ + "vampire-counts:ethereal-host", + "witch-hunters:warhounds", + "norse:huntsmen", + "beastmen:gor", + "beastmen:ungor", + "empire:skirmishers", + "dwarfs:ancestral-warriors", +]); +const PATROL_IDS = new Set(["skaven:rat-swarms", "lizardmen:skinks"]); + +function isFlying(unit) { + return unit.subType?.toLowerCase() === "flying" || FLYER_IDS.has(unit.unitId); +} + +export function scoutingPointsFor(unit) { + if (isFlying(unit) || FLYER_SCOUT_IDS.has(unit.unitId)) return 3; + if (SCOUT_IDS.has(unit.unitId)) return 2; + if (unit.type === "Cavalry" && unit.armour === "6+") return 2; + if (unit.type === "General") return 2; + if (PATROL_IDS.has(unit.unitId)) return 1; + if (unit.type === "Cavalry" && (unit.armour == null || unit.armour === "5+")) return 1; + if (unit.category === "character") return 1; + return 0; +} + +function assign(ruleSet) { + for (const army of ruleSet.armies) { + for (const unit of army.units) unit.scoutingPoints = scoutingPointsFor(unit); + } + return ruleSet; +} + +function lookup(ruleSet) { + return Object.fromEntries( + ruleSet.armies.flatMap((army) => + army.units.map((unit) => [unit.unitId, unit.scoutingPoints]), + ), + ); +} + +function report(ruleSets) { + const lines = [ + "# Scouting points report", + "", + "Generated by `npm run scouting:data`. Units are grouped by army and then by scouting points from 3 to 0.", + "", + ]; + for (const ruleSet of ruleSets) { + lines.push(`# ${ruleSet.name}`, ""); + for (const army of ruleSet.armies) { + lines.push(`## ${army.name}`, ""); + for (const points of [3, 2, 1, 0]) { + const units = army.units + .filter((unit) => unit.scoutingPoints === points) + .sort((a, b) => a.troop.localeCompare(b.troop)); + lines.push(`### ${points} scouting points`, ""); + if (units.length === 0) lines.push("- None"); + else for (const unit of units) lines.push(`- ${unit.troop} (\`${unit.unitId}\`)`); + lines.push(""); + } + } + } + return lines.join("\n"); +} + +function main() { + const revolution = assign(JSON.parse(fs.readFileSync(baseFile, "utf8"))); + fs.writeFileSync(baseFile, `${JSON.stringify(revolution, null, 2)}\n`); + + const custom = structuredClone(revolution); + custom.id = "warmaster-custom"; + custom.name = "A Matter of Mustaches"; + applyCustomUnits(custom, revolution); + assign(custom); + + const playtest = structuredClone(revolution); + playtest.id = "wmr-2026-playtest"; + playtest.name = "WMR - 2026 Playtest"; + for (const army of playtest.armies) { + army.ruleSet = playtest.id; + for (const unit of army.units) unit.ruleSet = playtest.id; + } + applyPlaytest2026(playtest); + assign(playtest); + + fs.writeFileSync( + lookupFile, + `${JSON.stringify({ + "warmaster-revolution": lookup(revolution), + "wmr-2026-playtest": lookup(playtest), + "warmaster-custom": lookup(custom), + }, null, 2)}\n`, + ); + fs.mkdirSync(path.dirname(reportFile), { recursive: true }); + fs.writeFileSync(reportFile, report([revolution, playtest, custom])); + + for (const ruleSet of [revolution, playtest, custom]) { + const totals = [3, 2, 1, 0].map( + (points) => + ruleSet.armies.flatMap((army) => army.units).filter((unit) => unit.scoutingPoints === points) + .length, + ); + console.log(`${ruleSet.name}: ${totals[0]} at 3, ${totals[1]} at 2, ${totals[2]} at 1, ${totals[3]} at 0.`); + } + console.log(`Wrote ${path.relative(root, lookupFile)} and ${path.relative(root, reportFile)}.`); +} + +main(); diff --git a/scripts/generate-army-json.mjs b/scripts/generate-army-json.mjs index 7730e10..5714cf3 100644 --- a/scripts/generate-army-json.mjs +++ b/scripts/generate-army-json.mjs @@ -253,6 +253,8 @@ export function normalizeRow(row, armyId) { type: row.type, subType: null, category, + // Filled by apply-scouting.mjs after the normalized tables are generated. + scoutingPoints: 0, facing: category === "upgrade" ? null : (FACING_DEFAULTS[row.type] ?? null), speed: category === "upgrade" ? null : (MOVE_DEFAULTS[row.type]?.[0] ?? null), halfPace: category === "upgrade" ? null : (MOVE_DEFAULTS[row.type]?.[1] ?? null), diff --git a/scripts/validate-data.mjs b/scripts/validate-data.mjs index b486f83..61f65e3 100644 --- a/scripts/validate-data.mjs +++ b/scripts/validate-data.mjs @@ -35,6 +35,9 @@ for (const army of data.armies ?? []) { if (!unit.unitId?.startsWith(`${army.army}:`)) fail(`${uctx} unitId not prefixed with army`); if (!CATEGORIES.has(unit.category)) fail(`${uctx} bad category ${unit.category}`); if (!FACINGS.has(unit.facing)) fail(`${uctx} bad facing ${unit.facing}`); + if (![0, 1, 2, 3].includes(unit.scoutingPoints)) { + fail(`${uctx} bad scoutingPoints ${unit.scoutingPoints}`); + } if (!Array.isArray(unit.specials)) fail(`${uctx} specials not an array`); if (!Array.isArray(unit.eligibleToUpgrade)) fail(`${uctx} eligibleToUpgrade not an array`); diff --git a/src/App.tsx b/src/App.tsx index a330afa..3f0e12a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -117,6 +117,15 @@ export default function App() { useEffect(() => { localStorage.setItem("warmuster.simplifiedView", String(simplifiedView)); }, [simplifiedView]); + // Optional Scouting deployment values are a display preference and do not + // alter saved army lists. + const [scoutingEnabled, setScoutingEnabled] = useState( + () => localStorage.getItem("warmuster.scouting") === "true", + ); + + useEffect(() => { + localStorage.setItem("warmuster.scouting", String(scoutingEnabled)); + }, [scoutingEnabled]); // Duplex calibration (mm): shifts printed card backs right (+) or left (-) // to line up with the fronts on this printer. Saved per browser; defaults // to 1mm right until the user calibrates. @@ -346,6 +355,7 @@ export default function App() { onSetPointsLimit={(pts) => mutate((l) => setPointsLimit(l, pts))} onSetNotes={(notes) => mutate((l) => setNotes(l, notes))} onSetAllowMercenaries={(allow) => mutate((l) => setAllowMercenaries(l, allow))} + scoutingEnabled={scoutingEnabled} /> @@ -382,6 +393,8 @@ export default function App() { onSelectRuleSet={handleSelectRuleSet} simplifiedView={simplifiedView} onToggleSimplifiedView={setSimplifiedView} + scoutingEnabled={scoutingEnabled} + onToggleScouting={setScoutingEnabled} lists={lists} folders={folders} importFolderTarget={normalizeImportFolderTarget( @@ -474,6 +487,7 @@ export default function App() { army={army} duplexOffsetMm={printMode === "cards" ? duplexOffset : 0} cardOptions={cardPrintOptions} + scoutingEnabled={scoutingEnabled} /> )} diff --git a/src/components/Catalog.test.tsx b/src/components/Catalog.test.tsx index 81f2c66..4224a13 100644 --- a/src/components/Catalog.test.tsx +++ b/src/components/Catalog.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, within } from "@testing-library/react"; +import { fireEvent, render, screen, within } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { getArmy, getRuleSet } from "../data/gameData"; import { createList } from "../domain/lists"; @@ -15,6 +15,7 @@ function renderCatalog(ruleSetId: string) { onAddUnit={vi.fn()} onAddCharacter={vi.fn()} onOpenMagicItems={vi.fn()} + scoutingEnabled={false} />, ); } @@ -47,4 +48,36 @@ describe("Catalog", () => { expect(fixedCapRow).toHaveTextContent("Min/Max -/1"); expect(scaledCapRow).toHaveTextContent("Min/Max -/4"); }); + + it("portals details outside a faded at-maximum row", () => { + const ruleSet = getRuleSet("warmaster-revolution")!; + const army = getArmy("warmaster-revolution", "dwarfs")!; + const list = createList( + "warmaster-revolution", + ruleSet.version, + "dwarfs", + "Maximum Rangers", + 2000, + ); + list.units = [ + { unitId: "dwarfs:rangers", quantity: 4, upgrades: [], magicItems: [] }, + ]; + render( + , + ); + + const row = screen.getByText("Rangers", { exact: true }).closest("li")!; + expect(row).toHaveClass("at-max"); + fireEvent.click(within(row).getByRole("button", { name: "View details for Rangers" })); + const dialog = screen.getByRole("dialog", { name: "Rangers details" }); + expect(row).not.toContainElement(dialog); + expect(dialog.closest(".modal-backdrop")?.parentElement).toBe(document.body); + }); }); diff --git a/src/components/Catalog.tsx b/src/components/Catalog.tsx index a498f47..41a4007 100644 --- a/src/components/Catalog.tsx +++ b/src/components/Catalog.tsx @@ -12,6 +12,7 @@ interface CatalogProps { onAddUnit: (unitId: string) => void; onAddCharacter: (unitId: string) => void; onOpenMagicItems: () => void; + scoutingEnabled: boolean; } function CatalogRow({ @@ -20,12 +21,14 @@ function CatalogRow({ count, scale, onAdd, + scoutingEnabled, }: { unit: UnitData; army: ArmyData; count: number; scale: number; onAdd?: () => void; + scoutingEnabled: boolean; }) { const isGeneral = unit.type === "General"; const max = @@ -86,6 +89,11 @@ function CatalogRow({
+ {scoutingEnabled && ( + + {unit.scoutingPoints} SP + + )} {pointsLabel(unit)} pts {onAdd && ( +
+

+ Shows each unit’s scouting value and army scouting totals. +

+

Backup

Lists are saved in this browser only. A backup file holds every list from every rule @@ -178,6 +214,52 @@ export default function ConfigDialog({ {error &&

{error}

} {imported &&

Lists replaced from the backup.

} + {scoutingRulesOpen && ( +
{ + event.stopPropagation(); + setScoutingRulesOpen(false); + }} + > +
event.stopPropagation()} + > +
+

Scouting

+ +
+
+

{SCOUTING_RULES.introduction}

+

{SCOUTING_RULES.commitment}

+

{SCOUTING_RULES.resolution}

+ + + + + + {SCOUTING_RULES.roles.map((role) => ( + + + + ))} + +
RolePointsTroop types
{role.role}{role.points}{role.troopTypes}
+
+
+
+ )} {pending && (
setPending(null)}>