@alpaca-software/40kdc-data 0.1.1 → 0.1.2
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.
- package/dist/abilities-resolver/index.d.ts +9 -0
- package/dist/abilities-resolver/index.d.ts.map +1 -0
- package/dist/abilities-resolver/index.js +9 -0
- package/dist/abilities-resolver/index.js.map +1 -0
- package/dist/abilities-resolver/resolver.d.ts +64 -0
- package/dist/abilities-resolver/resolver.d.ts.map +1 -0
- package/dist/abilities-resolver/resolver.js +135 -0
- package/dist/abilities-resolver/resolver.js.map +1 -0
- package/dist/bundle-schemas.d.ts +1 -0
- package/dist/bundle-schemas.d.ts.map +1 -0
- package/dist/bundle-schemas.js +1 -0
- package/dist/bundle-schemas.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -0
- package/dist/codegen-data.d.ts +1 -0
- package/dist/codegen-data.d.ts.map +1 -0
- package/dist/codegen-data.js +2 -0
- package/dist/codegen-data.js.map +1 -0
- package/dist/commands/import.d.ts +1 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +1 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/translate.d.ts +1 -0
- package/dist/commands/translate.d.ts.map +1 -0
- package/dist/commands/translate.js +1 -0
- package/dist/commands/translate.js.map +1 -0
- package/dist/commands/validate-all.d.ts +1 -0
- package/dist/commands/validate-all.d.ts.map +1 -0
- package/dist/commands/validate-all.js +1 -0
- package/dist/commands/validate-all.js.map +1 -0
- package/dist/commands/validate-core.d.ts +1 -0
- package/dist/commands/validate-core.d.ts.map +1 -0
- package/dist/commands/validate-core.js +1 -0
- package/dist/commands/validate-core.js.map +1 -0
- package/dist/commands/validate-enrichment.d.ts +1 -0
- package/dist/commands/validate-enrichment.d.ts.map +1 -0
- package/dist/commands/validate-enrichment.js +1 -0
- package/dist/commands/validate-enrichment.js.map +1 -0
- package/dist/convert-faction.d.ts +1 -0
- package/dist/convert-faction.d.ts.map +1 -0
- package/dist/convert-faction.js +1 -0
- package/dist/convert-faction.js.map +1 -0
- package/dist/converters/configs/adepta-sororitas.d.ts +1 -0
- package/dist/converters/configs/adepta-sororitas.d.ts.map +1 -0
- package/dist/converters/configs/adepta-sororitas.js +1 -0
- package/dist/converters/configs/adepta-sororitas.js.map +1 -0
- package/dist/converters/configs/adeptus-astartes.d.ts +1 -0
- package/dist/converters/configs/adeptus-astartes.d.ts.map +1 -0
- package/dist/converters/configs/adeptus-astartes.js +1 -0
- package/dist/converters/configs/adeptus-astartes.js.map +1 -0
- package/dist/converters/configs/adeptus-custodes.d.ts +1 -0
- package/dist/converters/configs/adeptus-custodes.d.ts.map +1 -0
- package/dist/converters/configs/adeptus-custodes.js +1 -0
- package/dist/converters/configs/adeptus-custodes.js.map +1 -0
- package/dist/converters/configs/adeptus-mechanicus.d.ts +1 -0
- package/dist/converters/configs/adeptus-mechanicus.d.ts.map +1 -0
- package/dist/converters/configs/adeptus-mechanicus.js +1 -0
- package/dist/converters/configs/adeptus-mechanicus.js.map +1 -0
- package/dist/converters/configs/aeldari.d.ts +1 -0
- package/dist/converters/configs/aeldari.d.ts.map +1 -0
- package/dist/converters/configs/aeldari.js +1 -0
- package/dist/converters/configs/aeldari.js.map +1 -0
- package/dist/converters/configs/agents-of-the-imperium.d.ts +1 -0
- package/dist/converters/configs/agents-of-the-imperium.d.ts.map +1 -0
- package/dist/converters/configs/agents-of-the-imperium.js +1 -0
- package/dist/converters/configs/agents-of-the-imperium.js.map +1 -0
- package/dist/converters/configs/astra-militarum.d.ts +1 -0
- package/dist/converters/configs/astra-militarum.d.ts.map +1 -0
- package/dist/converters/configs/astra-militarum.js +1 -0
- package/dist/converters/configs/astra-militarum.js.map +1 -0
- package/dist/converters/configs/black-templars.d.ts +1 -0
- package/dist/converters/configs/black-templars.d.ts.map +1 -0
- package/dist/converters/configs/black-templars.js +1 -0
- package/dist/converters/configs/black-templars.js.map +1 -0
- package/dist/converters/configs/blood-angels.d.ts +1 -0
- package/dist/converters/configs/blood-angels.d.ts.map +1 -0
- package/dist/converters/configs/blood-angels.js +1 -0
- package/dist/converters/configs/blood-angels.js.map +1 -0
- package/dist/converters/configs/chaos-daemons.d.ts +1 -0
- package/dist/converters/configs/chaos-daemons.d.ts.map +1 -0
- package/dist/converters/configs/chaos-daemons.js +1 -0
- package/dist/converters/configs/chaos-daemons.js.map +1 -0
- package/dist/converters/configs/chaos-knights.d.ts +1 -0
- package/dist/converters/configs/chaos-knights.d.ts.map +1 -0
- package/dist/converters/configs/chaos-knights.js +1 -0
- package/dist/converters/configs/chaos-knights.js.map +1 -0
- package/dist/converters/configs/chaos-space-marines.d.ts +1 -0
- package/dist/converters/configs/chaos-space-marines.d.ts.map +1 -0
- package/dist/converters/configs/chaos-space-marines.js +1 -0
- package/dist/converters/configs/chaos-space-marines.js.map +1 -0
- package/dist/converters/configs/crimson-fists.d.ts +1 -0
- package/dist/converters/configs/crimson-fists.d.ts.map +1 -0
- package/dist/converters/configs/crimson-fists.js +1 -0
- package/dist/converters/configs/crimson-fists.js.map +1 -0
- package/dist/converters/configs/dark-angels.d.ts +1 -0
- package/dist/converters/configs/dark-angels.d.ts.map +1 -0
- package/dist/converters/configs/dark-angels.js +1 -0
- package/dist/converters/configs/dark-angels.js.map +1 -0
- package/dist/converters/configs/death-guard.d.ts +1 -0
- package/dist/converters/configs/death-guard.d.ts.map +1 -0
- package/dist/converters/configs/death-guard.js +1 -0
- package/dist/converters/configs/death-guard.js.map +1 -0
- package/dist/converters/configs/deathwatch.d.ts +1 -0
- package/dist/converters/configs/deathwatch.d.ts.map +1 -0
- package/dist/converters/configs/deathwatch.js +1 -0
- package/dist/converters/configs/deathwatch.js.map +1 -0
- package/dist/converters/configs/drukhari.d.ts +1 -0
- package/dist/converters/configs/drukhari.d.ts.map +1 -0
- package/dist/converters/configs/drukhari.js +1 -0
- package/dist/converters/configs/drukhari.js.map +1 -0
- package/dist/converters/configs/emperors-children.d.ts +1 -0
- package/dist/converters/configs/emperors-children.d.ts.map +1 -0
- package/dist/converters/configs/emperors-children.js +1 -0
- package/dist/converters/configs/emperors-children.js.map +1 -0
- package/dist/converters/configs/genestealer-cults.d.ts +1 -0
- package/dist/converters/configs/genestealer-cults.d.ts.map +1 -0
- package/dist/converters/configs/genestealer-cults.js +1 -0
- package/dist/converters/configs/genestealer-cults.js.map +1 -0
- package/dist/converters/configs/grey-knights.d.ts +1 -0
- package/dist/converters/configs/grey-knights.d.ts.map +1 -0
- package/dist/converters/configs/grey-knights.js +1 -0
- package/dist/converters/configs/grey-knights.js.map +1 -0
- package/dist/converters/configs/imperial-fists.d.ts +1 -0
- package/dist/converters/configs/imperial-fists.d.ts.map +1 -0
- package/dist/converters/configs/imperial-fists.js +1 -0
- package/dist/converters/configs/imperial-fists.js.map +1 -0
- package/dist/converters/configs/imperial-knights.d.ts +1 -0
- package/dist/converters/configs/imperial-knights.d.ts.map +1 -0
- package/dist/converters/configs/imperial-knights.js +1 -0
- package/dist/converters/configs/imperial-knights.js.map +1 -0
- package/dist/converters/configs/iron-hands.d.ts +1 -0
- package/dist/converters/configs/iron-hands.d.ts.map +1 -0
- package/dist/converters/configs/iron-hands.js +1 -0
- package/dist/converters/configs/iron-hands.js.map +1 -0
- package/dist/converters/configs/leagues-of-votann.d.ts +1 -0
- package/dist/converters/configs/leagues-of-votann.d.ts.map +1 -0
- package/dist/converters/configs/leagues-of-votann.js +1 -0
- package/dist/converters/configs/leagues-of-votann.js.map +1 -0
- package/dist/converters/configs/necrons.d.ts +1 -0
- package/dist/converters/configs/necrons.d.ts.map +1 -0
- package/dist/converters/configs/necrons.js +1 -0
- package/dist/converters/configs/necrons.js.map +1 -0
- package/dist/converters/configs/orks.d.ts +1 -0
- package/dist/converters/configs/orks.d.ts.map +1 -0
- package/dist/converters/configs/orks.js +1 -0
- package/dist/converters/configs/orks.js.map +1 -0
- package/dist/converters/configs/raven-guard.d.ts +1 -0
- package/dist/converters/configs/raven-guard.d.ts.map +1 -0
- package/dist/converters/configs/raven-guard.js +1 -0
- package/dist/converters/configs/raven-guard.js.map +1 -0
- package/dist/converters/configs/salamanders.d.ts +1 -0
- package/dist/converters/configs/salamanders.d.ts.map +1 -0
- package/dist/converters/configs/salamanders.js +1 -0
- package/dist/converters/configs/salamanders.js.map +1 -0
- package/dist/converters/configs/space-wolves.d.ts +1 -0
- package/dist/converters/configs/space-wolves.d.ts.map +1 -0
- package/dist/converters/configs/space-wolves.js +1 -0
- package/dist/converters/configs/space-wolves.js.map +1 -0
- package/dist/converters/configs/tau-empire.d.ts +1 -0
- package/dist/converters/configs/tau-empire.d.ts.map +1 -0
- package/dist/converters/configs/tau-empire.js +1 -0
- package/dist/converters/configs/tau-empire.js.map +1 -0
- package/dist/converters/configs/thousand-sons.d.ts +1 -0
- package/dist/converters/configs/thousand-sons.d.ts.map +1 -0
- package/dist/converters/configs/thousand-sons.js +1 -0
- package/dist/converters/configs/thousand-sons.js.map +1 -0
- package/dist/converters/configs/tyranids.d.ts +1 -0
- package/dist/converters/configs/tyranids.d.ts.map +1 -0
- package/dist/converters/configs/tyranids.js +1 -0
- package/dist/converters/configs/tyranids.js.map +1 -0
- package/dist/converters/configs/ultramarines.d.ts +1 -0
- package/dist/converters/configs/ultramarines.d.ts.map +1 -0
- package/dist/converters/configs/ultramarines.js +1 -0
- package/dist/converters/configs/ultramarines.js.map +1 -0
- package/dist/converters/configs/white-scars.d.ts +1 -0
- package/dist/converters/configs/white-scars.d.ts.map +1 -0
- package/dist/converters/configs/white-scars.js +1 -0
- package/dist/converters/configs/white-scars.js.map +1 -0
- package/dist/converters/configs/world-eaters.d.ts +1 -0
- package/dist/converters/configs/world-eaters.d.ts.map +1 -0
- package/dist/converters/configs/world-eaters.js +1 -0
- package/dist/converters/configs/world-eaters.js.map +1 -0
- package/dist/converters/faction-config.d.ts +1 -0
- package/dist/converters/faction-config.d.ts.map +1 -0
- package/dist/converters/faction-config.js +1 -0
- package/dist/converters/faction-config.js.map +1 -0
- package/dist/converters/id-generator.d.ts +1 -0
- package/dist/converters/id-generator.d.ts.map +1 -0
- package/dist/converters/id-generator.js +1 -0
- package/dist/converters/id-generator.js.map +1 -0
- package/dist/converters/keyword-filter.d.ts +1 -0
- package/dist/converters/keyword-filter.d.ts.map +1 -0
- package/dist/converters/keyword-filter.js +1 -0
- package/dist/converters/keyword-filter.js.map +1 -0
- package/dist/converters/stat-parser.d.ts +1 -0
- package/dist/converters/stat-parser.d.ts.map +1 -0
- package/dist/converters/stat-parser.js +1 -0
- package/dist/converters/stat-parser.js.map +1 -0
- package/dist/converters/view-selector.d.ts +1 -0
- package/dist/converters/view-selector.d.ts.map +1 -0
- package/dist/converters/view-selector.js +1 -0
- package/dist/converters/view-selector.js.map +1 -0
- package/dist/converters/weapon-dedup.d.ts +1 -0
- package/dist/converters/weapon-dedup.d.ts.map +1 -0
- package/dist/converters/weapon-dedup.js +1 -0
- package/dist/converters/weapon-dedup.js.map +1 -0
- package/dist/cruncher/buffs.d.ts +184 -0
- package/dist/cruncher/buffs.d.ts.map +1 -0
- package/dist/cruncher/buffs.js +150 -0
- package/dist/cruncher/buffs.js.map +1 -0
- package/dist/cruncher/engine.d.ts +50 -0
- package/dist/cruncher/engine.d.ts.map +1 -0
- package/dist/cruncher/engine.js +312 -0
- package/dist/cruncher/engine.js.map +1 -0
- package/dist/cruncher/from-dsl.d.ts +69 -0
- package/dist/cruncher/from-dsl.d.ts.map +1 -0
- package/dist/cruncher/from-dsl.js +523 -0
- package/dist/cruncher/from-dsl.js.map +1 -0
- package/dist/cruncher/from-keyword.d.ts +35 -0
- package/dist/cruncher/from-keyword.d.ts.map +1 -0
- package/dist/cruncher/from-keyword.js +159 -0
- package/dist/cruncher/from-keyword.js.map +1 -0
- package/dist/cruncher/get-buffs.d.ts +12 -0
- package/dist/cruncher/get-buffs.d.ts.map +1 -0
- package/dist/cruncher/get-buffs.js +7 -0
- package/dist/cruncher/get-buffs.js.map +1 -0
- package/dist/cruncher/index.d.ts +11 -0
- package/dist/cruncher/index.d.ts.map +1 -0
- package/dist/cruncher/index.js +11 -0
- package/dist/cruncher/index.js.map +1 -0
- package/dist/data/bundle.generated.d.ts +1 -0
- package/dist/data/bundle.generated.d.ts.map +1 -0
- package/dist/data/bundle.generated.js +2 -1
- package/dist/data/bundle.generated.js.map +1 -0
- package/dist/data/collection.d.ts +1 -0
- package/dist/data/collection.d.ts.map +1 -0
- package/dist/data/collection.js +1 -0
- package/dist/data/collection.js.map +1 -0
- package/dist/data/dataset.d.ts +54 -2
- package/dist/data/dataset.d.ts.map +1 -0
- package/dist/data/dataset.js +111 -1
- package/dist/data/dataset.js.map +1 -0
- package/dist/data/entities.d.ts +70 -2
- package/dist/data/entities.d.ts.map +1 -0
- package/dist/data/entities.js +122 -0
- package/dist/data/entities.js.map +1 -0
- package/dist/data/index.d.ts +9 -1
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +14 -1
- package/dist/data/index.js.map +1 -0
- package/dist/data/normalize.d.ts +1 -0
- package/dist/data/normalize.d.ts.map +1 -0
- package/dist/data/normalize.js +1 -0
- package/dist/data/normalize.js.map +1 -0
- package/dist/data/roster-resolve.d.ts +33 -0
- package/dist/data/roster-resolve.d.ts.map +1 -0
- package/dist/data/roster-resolve.js +36 -0
- package/dist/data/roster-resolve.js.map +1 -0
- package/dist/data/types.d.ts +4 -1
- package/dist/data/types.d.ts.map +1 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/export/helpers.d.ts +33 -0
- package/dist/export/helpers.d.ts.map +1 -0
- package/dist/export/helpers.js +57 -0
- package/dist/export/helpers.js.map +1 -0
- package/dist/export/index.d.ts +21 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +25 -0
- package/dist/export/index.js.map +1 -0
- package/dist/export/newrecruit-json.d.ts +3 -0
- package/dist/export/newrecruit-json.d.ts.map +1 -0
- package/dist/export/newrecruit-json.js +140 -0
- package/dist/export/newrecruit-json.js.map +1 -0
- package/dist/export/newrecruit-simple.d.ts +3 -0
- package/dist/export/newrecruit-simple.d.ts.map +1 -0
- package/dist/export/newrecruit-simple.js +76 -0
- package/dist/export/newrecruit-simple.js.map +1 -0
- package/dist/export/newrecruit-wtc.d.ts +4 -0
- package/dist/export/newrecruit-wtc.d.ts.map +1 -0
- package/dist/export/newrecruit-wtc.js +142 -0
- package/dist/export/newrecruit-wtc.js.map +1 -0
- package/dist/export/roster-json.d.ts +3 -0
- package/dist/export/roster-json.d.ts.map +1 -0
- package/dist/export/roster-json.js +8 -0
- package/dist/export/roster-json.js.map +1 -0
- package/dist/export/serializer.d.ts +27 -0
- package/dist/export/serializer.d.ts.map +1 -0
- package/dist/export/serializer.js +2 -0
- package/dist/export/serializer.js.map +1 -0
- package/dist/gen-conformance.d.ts +1 -0
- package/dist/gen-conformance.d.ts.map +1 -0
- package/dist/gen-conformance.js +73 -12
- package/dist/gen-conformance.js.map +1 -0
- package/dist/generated.d.ts +194 -118
- package/dist/generated.d.ts.map +1 -0
- package/dist/generated.js +1 -0
- package/dist/generated.js.map +1 -0
- package/dist/import/adapter.d.ts +4 -3
- package/dist/import/adapter.d.ts.map +1 -0
- package/dist/import/adapter.js +1 -0
- package/dist/import/adapter.js.map +1 -0
- package/dist/import/decode.d.ts +1 -0
- package/dist/import/decode.d.ts.map +1 -0
- package/dist/import/decode.js +1 -0
- package/dist/import/decode.js.map +1 -0
- package/dist/import/import-roster.d.ts +35 -0
- package/dist/import/import-roster.d.ts.map +1 -0
- package/dist/import/import-roster.js +97 -0
- package/dist/import/import-roster.js.map +1 -0
- package/dist/import/index.d.ts +7 -3
- package/dist/import/index.d.ts.map +1 -0
- package/dist/import/index.js +5 -1
- package/dist/import/index.js.map +1 -0
- package/dist/import/listforge.d.ts +1 -0
- package/dist/import/listforge.d.ts.map +1 -0
- package/dist/import/listforge.js +7 -1
- package/dist/import/listforge.js.map +1 -0
- package/dist/import/newrecruit-json.d.ts +31 -0
- package/dist/import/newrecruit-json.d.ts.map +1 -0
- package/dist/import/newrecruit-json.js +224 -0
- package/dist/import/newrecruit-json.js.map +1 -0
- package/dist/import/newrecruit-simple.d.ts +29 -0
- package/dist/import/newrecruit-simple.d.ts.map +1 -0
- package/dist/import/newrecruit-simple.js +200 -0
- package/dist/import/newrecruit-simple.js.map +1 -0
- package/dist/import/newrecruit-text.d.ts +48 -0
- package/dist/import/newrecruit-text.d.ts.map +1 -0
- package/dist/import/newrecruit-text.js +96 -0
- package/dist/import/newrecruit-text.js.map +1 -0
- package/dist/import/newrecruit-wtc.d.ts +36 -0
- package/dist/import/newrecruit-wtc.d.ts.map +1 -0
- package/dist/import/newrecruit-wtc.js +334 -0
- package/dist/import/newrecruit-wtc.js.map +1 -0
- package/dist/import/resolve.d.ts +3 -2
- package/dist/import/resolve.d.ts.map +1 -0
- package/dist/import/resolve.js +5 -2
- package/dist/import/resolve.js.map +1 -0
- package/dist/import/types.d.ts +11 -1
- package/dist/import/types.d.ts.map +1 -0
- package/dist/import/types.js +1 -0
- package/dist/import/types.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -0
- package/dist/known-support-10e.d.ts +1 -0
- package/dist/known-support-10e.d.ts.map +1 -0
- package/dist/known-support-10e.js +1 -0
- package/dist/known-support-10e.js.map +1 -0
- package/dist/link-abilities.d.ts +41 -0
- package/dist/link-abilities.d.ts.map +1 -0
- package/dist/link-abilities.js +159 -0
- package/dist/link-abilities.js.map +1 -0
- package/dist/migrations/2026-weapon-keywords.d.ts +2 -0
- package/dist/migrations/2026-weapon-keywords.d.ts.map +1 -0
- package/dist/migrations/2026-weapon-keywords.js +243 -0
- package/dist/migrations/2026-weapon-keywords.js.map +1 -0
- package/dist/port-10e-faction.d.ts +1 -0
- package/dist/port-10e-faction.d.ts.map +1 -0
- package/dist/port-10e-faction.js +1 -0
- package/dist/port-10e-faction.js.map +1 -0
- package/dist/report.d.ts +1 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +1 -0
- package/dist/report.js.map +1 -0
- package/dist/rube-goldberg.d.ts +3 -0
- package/dist/rube-goldberg.d.ts.map +1 -0
- package/dist/rube-goldberg.js +109 -0
- package/dist/rube-goldberg.js.map +1 -0
- package/dist/schema-loader.d.ts +1 -0
- package/dist/schema-loader.d.ts.map +1 -0
- package/dist/schema-loader.js +1 -0
- package/dist/schema-loader.js.map +1 -0
- package/dist/validate.d.ts +1 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +2 -0
- package/dist/validate.js.map +1 -0
- package/package.json +7 -2
- package/schemas/core/roster.schema.json +17 -4
- package/schemas/core/weapon-keyword.schema.json +31 -0
- package/schemas/core/weapon.schema.json +22 -1
- package/schemas/enrichment/ability-dsl/effect.schema.json +23 -1
- package/dist/import/import-listforge.d.ts +0 -23
- package/dist/import/import-listforge.js +0 -32
package/dist/data/index.d.ts
CHANGED
|
@@ -22,10 +22,15 @@
|
|
|
22
22
|
export { Dataset } from "./dataset.js";
|
|
23
23
|
export { Collection } from "./collection.js";
|
|
24
24
|
export type { CollectionConfig } from "./collection.js";
|
|
25
|
-
export { UnitView, AbilityView, WeaponView, FactionView } from "./entities.js";
|
|
25
|
+
export { UnitView, AbilityView, WeaponView, WeaponKeywordView, FactionView, } from "./entities.js";
|
|
26
26
|
export { normalizeName } from "./normalize.js";
|
|
27
27
|
export { emptyRawData } from "./types.js";
|
|
28
28
|
export type { RawData } from "./types.js";
|
|
29
|
+
export * from "../cruncher/index.js";
|
|
30
|
+
export { effectToBuffs, parseKeywordGrant } from "../cruncher/from-dsl.js";
|
|
31
|
+
export type { EffectTranslation, TranslationPerspective, UnsupportedFragment, } from "../cruncher/from-dsl.js";
|
|
32
|
+
export * from "../abilities-resolver/index.js";
|
|
33
|
+
export { resolveRosterUnit, resolveRosterWargear } from "./roster-resolve.js";
|
|
29
34
|
import { Dataset } from "./dataset.js";
|
|
30
35
|
/** The dataset built from this package's embedded data. */
|
|
31
36
|
export declare const dataset: Dataset;
|
|
@@ -33,6 +38,8 @@ export declare const dataset: Dataset;
|
|
|
33
38
|
export declare const units: import("./collection.js").Collection<import("../generated.js").Unit, import("./entities.js").UnitView>;
|
|
34
39
|
/** All weapons, linked to the units that carry them. */
|
|
35
40
|
export declare const weapons: import("./collection.js").Collection<import("../generated.js").Weapon, import("./entities.js").WeaponView>;
|
|
41
|
+
/** Catalog of weapon keywords (Lethal Hits, Sustained Hits N, Anti-X N+, ...). */
|
|
42
|
+
export declare const weaponKeywords: import("./collection.js").Collection<import("../generated.js").WeaponKeyword, import("./entities.js").WeaponKeywordView>;
|
|
36
43
|
/** All factions, linked to their units, abilities, and weapons. */
|
|
37
44
|
export declare const factions: import("./collection.js").Collection<import("../generated.js").Faction, import("./entities.js").FactionView>;
|
|
38
45
|
/** All abilities, linked to their phases and the units that have them. */
|
|
@@ -57,3 +64,4 @@ export declare const deploymentPatterns: import("./collection.js").Collection<im
|
|
|
57
64
|
export declare const forceDispositions: import("./collection.js").Collection<import("../generated.js").ForceDisposition, import("../generated.js").ForceDisposition>;
|
|
58
65
|
/** All resource pools. */
|
|
59
66
|
export declare const resourcePools: import("./collection.js").Collection<import("../generated.js").ResourcePool, import("../generated.js").ResourcePool>;
|
|
67
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/data/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,QAAQ,EACR,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,cAAc,sBAAsB,CAAC;AAGrC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AAGjC,cAAc,gCAAgC,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2DAA2D;AAC3D,eAAO,MAAM,OAAO,SAAqB,CAAC;AAE1C,kEAAkE;AAClE,eAAO,MAAM,KAAK,wGAAgB,CAAC;AACnC,wDAAwD;AACxD,eAAO,MAAM,OAAO,4GAAkB,CAAC;AACvC,kFAAkF;AAClF,eAAO,MAAM,cAAc,0HAAyB,CAAC;AACrD,mEAAmE;AACnE,eAAO,MAAM,QAAQ,8GAAmB,CAAC;AACzC,0EAA0E;AAC1E,eAAO,MAAM,SAAS,sHAAoB,CAAC;AAC3C,uBAAuB;AACvB,eAAO,MAAM,WAAW,kHAAsB,CAAC;AAC/C,wBAAwB;AACxB,eAAO,MAAM,YAAY,oHAAuB,CAAC;AACjD,sBAAsB;AACtB,eAAO,MAAM,UAAU,gHAAqB,CAAC;AAC7C,2BAA2B;AAC3B,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,oBAAoB;AACpB,eAAO,MAAM,QAAQ,4GAAmB,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,eAAe,0HAA0B,CAAC;AACvD,mCAAmC;AACnC,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,+BAA+B;AAC/B,eAAO,MAAM,kBAAkB,gIAA6B,CAAC;AAC7D,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,8HAA4B,CAAC;AAC3D,0BAA0B;AAC1B,eAAO,MAAM,aAAa,sHAAwB,CAAC"}
|
package/dist/data/index.js
CHANGED
|
@@ -21,9 +21,19 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export { Dataset } from "./dataset.js";
|
|
23
23
|
export { Collection } from "./collection.js";
|
|
24
|
-
export { UnitView, AbilityView, WeaponView, FactionView } from "./entities.js";
|
|
24
|
+
export { UnitView, AbilityView, WeaponView, WeaponKeywordView, FactionView, } from "./entities.js";
|
|
25
25
|
export { normalizeName } from "./normalize.js";
|
|
26
26
|
export { emptyRawData } from "./types.js";
|
|
27
|
+
// The cruncher surface — buff types + the engine — re-exported from the data
|
|
28
|
+
// package so downstream callers can import their whole 40kdc API from
|
|
29
|
+
// `@alpaca-software/40kdc-data` without reaching into subpaths.
|
|
30
|
+
export * from "../cruncher/index.js";
|
|
31
|
+
// The DSL→Buff translator that powers AbilityView.getBuffs / describeBuffs.
|
|
32
|
+
export { effectToBuffs, parseKeywordGrant } from "../cruncher/from-dsl.js";
|
|
33
|
+
// The eligible-abilities resolver (also reachable as Dataset.eligibleAbilities).
|
|
34
|
+
export * from "../abilities-resolver/index.js";
|
|
35
|
+
// Bridge helpers from the importer's RosterUnit → linked views.
|
|
36
|
+
export { resolveRosterUnit, resolveRosterWargear } from "./roster-resolve.js";
|
|
27
37
|
import { Dataset } from "./dataset.js";
|
|
28
38
|
/** The dataset built from this package's embedded data. */
|
|
29
39
|
export const dataset = Dataset.embedded();
|
|
@@ -31,6 +41,8 @@ export const dataset = Dataset.embedded();
|
|
|
31
41
|
export const units = dataset.units;
|
|
32
42
|
/** All weapons, linked to the units that carry them. */
|
|
33
43
|
export const weapons = dataset.weapons;
|
|
44
|
+
/** Catalog of weapon keywords (Lethal Hits, Sustained Hits N, Anti-X N+, ...). */
|
|
45
|
+
export const weaponKeywords = dataset.weaponKeywords;
|
|
34
46
|
/** All factions, linked to their units, abilities, and weapons. */
|
|
35
47
|
export const factions = dataset.factions;
|
|
36
48
|
/** All abilities, linked to their phases and the units that have them. */
|
|
@@ -55,3 +67,4 @@ export const deploymentPatterns = dataset.deploymentPatterns;
|
|
|
55
67
|
export const forceDispositions = dataset.forceDispositions;
|
|
56
68
|
/** All resource pools. */
|
|
57
69
|
export const resourcePools = dataset.resourcePools;
|
|
70
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/data/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EACL,QAAQ,EACR,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,6EAA6E;AAC7E,sEAAsE;AACtE,gEAAgE;AAChE,cAAc,sBAAsB,CAAC;AAErC,4EAA4E;AAC5E,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAO3E,iFAAiF;AACjF,cAAc,gCAAgC,CAAC;AAE/C,gEAAgE;AAChE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2DAA2D;AAC3D,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;AAE1C,kEAAkE;AAClE,MAAM,CAAC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;AACnC,wDAAwD;AACxD,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;AACvC,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACrD,mEAAmE;AACnE,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACzC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;AAC3C,uBAAuB;AACvB,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;AAC/C,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AACjD,sBAAsB;AACtB,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC7C,2BAA2B;AAC3B,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACrD,oBAAoB;AACpB,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACzC,4BAA4B;AAC5B,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;AACvD,mCAAmC;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACrD,+BAA+B;AAC/B,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;AAC7D,8BAA8B;AAC9B,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;AAC3D,0BAA0B;AAC1B,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC","sourcesContent":["/**\n * The linked, typed 40kdc dataset.\n *\n * The default {@link dataset} is built once from the data embedded in this\n * package; the top-level collections below are its accessors, re-exported for\n * the ergonomic one-liner form.\n *\n * @packageDocumentation\n *\n * @example\n * import { units } from \"@alpaca-software/40kdc-data\";\n *\n * units.find(\"Kharn\")!.abilities\n * .filter(a => a.phases.includes(\"shooting\"))\n * .map(a => a.id); // [\"berzerker-frenzy\"]\n *\n * @example\n * import { factions } from \"@alpaca-software/40kdc-data\";\n *\n * factions.find(\"World Eaters\")!.units.length;\n */\nexport { Dataset } from \"./dataset.js\";\nexport { Collection } from \"./collection.js\";\nexport type { CollectionConfig } from \"./collection.js\";\nexport {\n UnitView,\n AbilityView,\n WeaponView,\n WeaponKeywordView,\n FactionView,\n} from \"./entities.js\";\nexport { normalizeName } from \"./normalize.js\";\nexport { emptyRawData } from \"./types.js\";\nexport type { RawData } from \"./types.js\";\n\n// The cruncher surface — buff types + the engine — re-exported from the data\n// package so downstream callers can import their whole 40kdc API from\n// `@alpaca-software/40kdc-data` without reaching into subpaths.\nexport * from \"../cruncher/index.js\";\n\n// The DSL→Buff translator that powers AbilityView.getBuffs / describeBuffs.\nexport { effectToBuffs, parseKeywordGrant } from \"../cruncher/from-dsl.js\";\nexport type {\n EffectTranslation,\n TranslationPerspective,\n UnsupportedFragment,\n} from \"../cruncher/from-dsl.js\";\n\n// The eligible-abilities resolver (also reachable as Dataset.eligibleAbilities).\nexport * from \"../abilities-resolver/index.js\";\n\n// Bridge helpers from the importer's RosterUnit → linked views.\nexport { resolveRosterUnit, resolveRosterWargear } from \"./roster-resolve.js\";\n\nimport { Dataset } from \"./dataset.js\";\n\n/** The dataset built from this package's embedded data. */\nexport const dataset = Dataset.embedded();\n\n/** All units, linked to their faction, weapons, and abilities. */\nexport const units = dataset.units;\n/** All weapons, linked to the units that carry them. */\nexport const weapons = dataset.weapons;\n/** Catalog of weapon keywords (Lethal Hits, Sustained Hits N, Anti-X N+, ...). */\nexport const weaponKeywords = dataset.weaponKeywords;\n/** All factions, linked to their units, abilities, and weapons. */\nexport const factions = dataset.factions;\n/** All abilities, linked to their phases and the units that have them. */\nexport const abilities = dataset.abilities;\n/** All detachments. */\nexport const detachments = dataset.detachments;\n/** All enhancements. */\nexport const enhancements = dataset.enhancements;\n/** All stratagems. */\nexport const stratagems = dataset.stratagems;\n/** All wargear options. */\nexport const wargearOptions = dataset.wargearOptions;\n/** All missions. */\nexport const missions = dataset.missions;\n/** All mission matchups. */\nexport const missionMatchups = dataset.missionMatchups;\n/** All secondary mission cards. */\nexport const secondaryCards = dataset.secondaryCards;\n/** All deployment patterns. */\nexport const deploymentPatterns = dataset.deploymentPatterns;\n/** All force dispositions. */\nexport const forceDispositions = dataset.forceDispositions;\n/** All resource pools. */\nexport const resourcePools = dataset.resourcePools;\n"]}
|
package/dist/data/normalize.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/data/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQnD"}
|
package/dist/data/normalize.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/data/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,WAAW,EAAE;SACb,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE,CAAC;AACZ,CAAC","sourcesContent":["/**\n * Name normalization for diacritic- and punctuation-insensitive lookup.\n *\n * Warhammer 40,000 is played globally and many entity names carry diacritics or\n * punctuation — \"Khârn the Betrayer\", \"T'au\", \"Be'lakor\". A user typing the\n * plain-ASCII form of a name must still find the entity. Every name comparison\n * in this package routes through {@link normalizeName} so the matching rule is\n * defined in exactly one place; consumers can import it to reproduce the same\n * behaviour in their own search UIs.\n *\n * @packageDocumentation\n */\n\n/**\n * Reduce a display name to a canonical lookup key.\n *\n * The transform, in order:\n * 1. Unicode NFD-decompose, then strip combining marks — `Khârn` → `Kharn`.\n * 2. Casefold to lower case.\n * 3. Remove apostrophe and quote variants (`' ’ ‘ \\` \" “ ”`) — `T'au` → `Tau`.\n * 4. Collapse any run of whitespace or hyphens to a single space, then trim —\n * `Be'lakor` → `belakor`, `the betrayer` → `the betrayer`.\n *\n * The result is intended only for comparison; it is not a display value.\n *\n * @example\n * normalizeName(\"Khârn the Betrayer\"); // \"kharn the betrayer\"\n * normalizeName(\"T'au\"); // \"tau\"\n */\nexport function normalizeName(input: string): string {\n return input\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase()\n .replace(/['’‘`\"“”]/g, \"\")\n .replace(/[\\s-]+/g, \" \")\n .trim();\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge helpers between the importer's flat-data {@link Roster} types and
|
|
3
|
+
* the linked {@link UnitView} the cruncher consumes. The importer ships
|
|
4
|
+
* unit entries as plain interfaces (`RosterUnit` is data, not behaviour),
|
|
5
|
+
* so the lookup is a free function rather than a method.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { RosterUnit, RosterWargear } from "../import/types.js";
|
|
10
|
+
import type { Dataset } from "./dataset.js";
|
|
11
|
+
import type { UnitView, WeaponView } from "./entities.js";
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a roster's unit entry against the dataset, returning the linked
|
|
14
|
+
* {@link UnitView}. Returns `undefined` when:
|
|
15
|
+
* - the roster's `ref.id` is `null` (the importer couldn't match the unit), or
|
|
16
|
+
* - the id doesn't appear in the dataset (e.g. the roster was authored
|
|
17
|
+
* against an older dataslate than the bundled one).
|
|
18
|
+
*
|
|
19
|
+
* Doesn't surface diagnostics — the caller already has them on the roster's
|
|
20
|
+
* own `diagnostics` field.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveRosterUnit(rosterUnit: RosterUnit, dataset: Dataset): UnitView | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve every wargear entry on a roster unit to a {@link WeaponView},
|
|
25
|
+
* keeping each entry's count alongside. Unresolved entries are dropped
|
|
26
|
+
* silently (matching {@link resolveRosterUnit}). Useful when the SPA
|
|
27
|
+
* needs to enumerate firing options after the user picks a roster unit.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveRosterWargear(wargear: RosterWargear[], dataset: Dataset): {
|
|
30
|
+
weapon: WeaponView;
|
|
31
|
+
count: number;
|
|
32
|
+
}[];
|
|
33
|
+
//# sourceMappingURL=roster-resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster-resolve.d.ts","sourceRoot":"","sources":["../../src/data/roster-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE1D;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO,GACf,QAAQ,GAAG,SAAS,CAItB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EAAE,EACxB,OAAO,EAAE,OAAO,GACf;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAUzC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a roster's unit entry against the dataset, returning the linked
|
|
3
|
+
* {@link UnitView}. Returns `undefined` when:
|
|
4
|
+
* - the roster's `ref.id` is `null` (the importer couldn't match the unit), or
|
|
5
|
+
* - the id doesn't appear in the dataset (e.g. the roster was authored
|
|
6
|
+
* against an older dataslate than the bundled one).
|
|
7
|
+
*
|
|
8
|
+
* Doesn't surface diagnostics — the caller already has them on the roster's
|
|
9
|
+
* own `diagnostics` field.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveRosterUnit(rosterUnit, dataset) {
|
|
12
|
+
const id = rosterUnit.ref.id;
|
|
13
|
+
if (id === null)
|
|
14
|
+
return undefined;
|
|
15
|
+
return dataset.units.get(id);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve every wargear entry on a roster unit to a {@link WeaponView},
|
|
19
|
+
* keeping each entry's count alongside. Unresolved entries are dropped
|
|
20
|
+
* silently (matching {@link resolveRosterUnit}). Useful when the SPA
|
|
21
|
+
* needs to enumerate firing options after the user picks a roster unit.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveRosterWargear(wargear, dataset) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const w of wargear) {
|
|
26
|
+
const id = w.ref.id;
|
|
27
|
+
if (id === null)
|
|
28
|
+
continue;
|
|
29
|
+
const weapon = dataset.weapons.get(id);
|
|
30
|
+
if (!weapon)
|
|
31
|
+
continue;
|
|
32
|
+
out.push({ weapon, count: w.count });
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=roster-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster-resolve.js","sourceRoot":"","sources":["../../src/data/roster-resolve.ts"],"names":[],"mappings":"AAYA;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAsB,EACtB,OAAgB;IAEhB,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClC,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAwB,EACxB,OAAgB;IAEhB,MAAM,GAAG,GAA4C,EAAE,CAAC;IACxD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,EAAE,KAAK,IAAI;YAAE,SAAS;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Bridge helpers between the importer's flat-data {@link Roster} types and\n * the linked {@link UnitView} the cruncher consumes. The importer ships\n * unit entries as plain interfaces (`RosterUnit` is data, not behaviour),\n * so the lookup is a free function rather than a method.\n *\n * @packageDocumentation\n */\nimport type { RosterUnit, RosterWargear } from \"../import/types.js\";\nimport type { Dataset } from \"./dataset.js\";\nimport type { UnitView, WeaponView } from \"./entities.js\";\n\n/**\n * Resolve a roster's unit entry against the dataset, returning the linked\n * {@link UnitView}. Returns `undefined` when:\n * - the roster's `ref.id` is `null` (the importer couldn't match the unit), or\n * - the id doesn't appear in the dataset (e.g. the roster was authored\n * against an older dataslate than the bundled one).\n *\n * Doesn't surface diagnostics — the caller already has them on the roster's\n * own `diagnostics` field.\n */\nexport function resolveRosterUnit(\n rosterUnit: RosterUnit,\n dataset: Dataset,\n): UnitView | undefined {\n const id = rosterUnit.ref.id;\n if (id === null) return undefined;\n return dataset.units.get(id);\n}\n\n/**\n * Resolve every wargear entry on a roster unit to a {@link WeaponView},\n * keeping each entry's count alongside. Unresolved entries are dropped\n * silently (matching {@link resolveRosterUnit}). Useful when the SPA\n * needs to enumerate firing options after the user picks a roster unit.\n */\nexport function resolveRosterWargear(\n wargear: RosterWargear[],\n dataset: Dataset,\n): { weapon: WeaponView; count: number }[] {\n const out: { weapon: WeaponView; count: number }[] = [];\n for (const w of wargear) {\n const id = w.ref.id;\n if (id === null) continue;\n const weapon = dataset.weapons.get(id);\n if (!weapon) continue;\n out.push({ weapon, count: w.count });\n }\n return out;\n}\n"]}
|
package/dist/data/types.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
|
-
import type { AbilityDSLEntry, DeploymentPattern, Detachment, Enhancement, Faction, ForceDisposition, GameVersion, InteractionFlag, LeaderAttachment, Mission, MissionMatchup, PhaseMapping, ResourcePool, SecondaryCard, Stratagem, TimingFlag, Unit, UnitComposition, WargearOption, Weapon } from "../generated.js";
|
|
10
|
+
import type { AbilityDSLEntry, DeploymentPattern, Detachment, Enhancement, Faction, ForceDisposition, GameVersion, InteractionFlag, LeaderAttachment, Mission, MissionMatchup, PhaseMapping, ResourcePool, SecondaryCard, Stratagem, TimingFlag, Unit, UnitComposition, WargearOption, Weapon, WeaponKeyword } from "../generated.js";
|
|
11
11
|
/**
|
|
12
12
|
* Every entity collection in the dataset, keyed by camelCase collection name.
|
|
13
13
|
*
|
|
@@ -18,6 +18,8 @@ import type { AbilityDSLEntry, DeploymentPattern, Detachment, Enhancement, Facti
|
|
|
18
18
|
export interface RawData {
|
|
19
19
|
units: Unit[];
|
|
20
20
|
weapons: Weapon[];
|
|
21
|
+
/** Catalog of weapon keywords (Lethal Hits, Sustained Hits N, Anti-X N+, ...). */
|
|
22
|
+
weaponKeywords: WeaponKeyword[];
|
|
21
23
|
factions: Faction[];
|
|
22
24
|
/** Community-authored ability mechanics (key is `ability_id`, not `id`). */
|
|
23
25
|
abilities: AbilityDSLEntry[];
|
|
@@ -41,3 +43,4 @@ export interface RawData {
|
|
|
41
43
|
}
|
|
42
44
|
/** A `RawData` with every collection initialised to an empty array. */
|
|
43
45
|
export declare function emptyRawData(): RawData;
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/data/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,OAAO,EACP,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,SAAS,EACT,UAAU,EACV,IAAI,EACJ,eAAe,EACf,aAAa,EACb,MAAM,EACN,aAAa,EACd,MAAM,iBAAiB,CAAC;AAEzB;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,4EAA4E;IAC5E,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,8EAA8E;IAC9E,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC;AAED,uEAAuE;AACvE,wBAAgB,YAAY,IAAI,OAAO,CAwBtC"}
|
package/dist/data/types.js
CHANGED
|
@@ -3,6 +3,7 @@ export function emptyRawData() {
|
|
|
3
3
|
return {
|
|
4
4
|
units: [],
|
|
5
5
|
weapons: [],
|
|
6
|
+
weaponKeywords: [],
|
|
6
7
|
factions: [],
|
|
7
8
|
abilities: [],
|
|
8
9
|
phaseMappings: [],
|
|
@@ -23,3 +24,4 @@ export function emptyRawData() {
|
|
|
23
24
|
interactionFlags: [],
|
|
24
25
|
};
|
|
25
26
|
}
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/data/types.ts"],"names":[],"mappings":"AAmEA,uEAAuE;AACvE,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAE;QACX,cAAc,EAAE,EAAE;QAClB,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,aAAa,EAAE,EAAE;QACjB,WAAW,EAAE,EAAE;QACf,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,EAAE;QACpB,cAAc,EAAE,EAAE;QAClB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,EAAE;QACZ,eAAe,EAAE,EAAE;QACnB,cAAc,EAAE,EAAE;QAClB,kBAAkB,EAAE,EAAE;QACtB,iBAAiB,EAAE,EAAE;QACrB,aAAa,EAAE,EAAE;QACjB,WAAW,EAAE,EAAE;QACf,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The shape of the embedded data bundle: one named array per entity collection.\n *\n * `RawData` is the boundary between the generated JSON-Schema types and the\n * linked view layer. The codegen ({@link file://../codegen-data.ts}) emits a\n * `RAW_DATA: RawData` constant; {@link Dataset} wraps it with linked accessors.\n *\n * @packageDocumentation\n */\nimport type {\n AbilityDSLEntry,\n DeploymentPattern,\n Detachment,\n Enhancement,\n Faction,\n ForceDisposition,\n GameVersion,\n InteractionFlag,\n LeaderAttachment,\n Mission,\n MissionMatchup,\n PhaseMapping,\n ResourcePool,\n SecondaryCard,\n Stratagem,\n TimingFlag,\n Unit,\n UnitComposition,\n WargearOption,\n Weapon,\n WeaponKeyword,\n} from \"../generated.js\";\n\n/**\n * Every entity collection in the dataset, keyed by camelCase collection name.\n *\n * Collections with no authored data yet (e.g. `interactionFlags`) are present\n * as empty arrays so the API surface is stable and new data flows through\n * automatically once authored.\n */\nexport interface RawData {\n units: Unit[];\n weapons: Weapon[];\n /** Catalog of weapon keywords (Lethal Hits, Sustained Hits N, Anti-X N+, ...). */\n weaponKeywords: WeaponKeyword[];\n factions: Faction[];\n /** Community-authored ability mechanics (key is `ability_id`, not `id`). */\n abilities: AbilityDSLEntry[];\n /** Phase assignments, joined to abilities/stratagems/etc. via `source_id`. */\n phaseMappings: PhaseMapping[];\n detachments: Detachment[];\n stratagems: Stratagem[];\n enhancements: Enhancement[];\n leaderAttachments: LeaderAttachment[];\n unitCompositions: UnitComposition[];\n wargearOptions: WargearOption[];\n gameVersions: GameVersion[];\n missions: Mission[];\n missionMatchups: MissionMatchup[];\n secondaryCards: SecondaryCard[];\n deploymentPatterns: DeploymentPattern[];\n forceDispositions: ForceDisposition[];\n resourcePools: ResourcePool[];\n timingFlags: TimingFlag[];\n interactionFlags: InteractionFlag[];\n}\n\n/** A `RawData` with every collection initialised to an empty array. */\nexport function emptyRawData(): RawData {\n return {\n units: [],\n weapons: [],\n weaponKeywords: [],\n factions: [],\n abilities: [],\n phaseMappings: [],\n detachments: [],\n stratagems: [],\n enhancements: [],\n leaderAttachments: [],\n unitCompositions: [],\n wargearOptions: [],\n gameVersions: [],\n missions: [],\n missionMatchups: [],\n secondaryCards: [],\n deploymentPatterns: [],\n forceDispositions: [],\n resourcePools: [],\n timingFlags: [],\n interactionFlags: [],\n };\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the roster exporters.
|
|
3
|
+
*
|
|
4
|
+
* Exporters are deterministic and Dataset-free: they read the Roster only and
|
|
5
|
+
* regenerate format-specific decoration (display names, Char-slot numbering,
|
|
6
|
+
* displayed unit totals) from what's stored. Anything the Roster doesn't model
|
|
7
|
+
* — char-slot numbers, the detachment "<X> Character" keyword, secondary-
|
|
8
|
+
* objective summaries — is either derived heuristically here or dropped.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { Roster, RosterUnit } from "../import/types.js";
|
|
13
|
+
/** Convert a kebab-case entity id ("chaos-knights") to a Title Case display
|
|
14
|
+
* name ("Chaos Knights"). This is the round-trip best-effort when the Roster
|
|
15
|
+
* doesn't store the source's raw faction/detachment name. */
|
|
16
|
+
export declare function titleCaseId(id: string | null): string | null;
|
|
17
|
+
/** Sum of unit base pts + enhancement pts (= the figure most text formats display). */
|
|
18
|
+
export declare function displayedUnitPoints(u: RosterUnit): number | null;
|
|
19
|
+
/** Sum of every unit's displayed total + every enhancement cost line. */
|
|
20
|
+
export declare function totalArmyPoints(roster: Roster): number;
|
|
21
|
+
/**
|
|
22
|
+
* Heuristic re-derivation of which units would carry a `CharN:` prefix on
|
|
23
|
+
* export to a wtc text format. The Roster doesn't track unit categories, so we
|
|
24
|
+
* approximate "is a character" as "is the warlord OR has an enhancement OR has
|
|
25
|
+
* a leader attachment". CharN: numbering follows declaration order.
|
|
26
|
+
*
|
|
27
|
+
* Returns a parallel array: `slot[i]` is the 1-based char index for unit i, or
|
|
28
|
+
* `null` if that unit doesn't get a CharN: prefix.
|
|
29
|
+
*/
|
|
30
|
+
export declare function charSlotAssignment(units: readonly RosterUnit[]): (number | null)[];
|
|
31
|
+
/** Pretty JSON with a trailing newline — matches the repo's 2-space convention. */
|
|
32
|
+
export declare function prettyJson(value: unknown): string;
|
|
33
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/export/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;6DAE6D;AAC7D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAO5D;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAalF;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Convert a kebab-case entity id ("chaos-knights") to a Title Case display
|
|
2
|
+
* name ("Chaos Knights"). This is the round-trip best-effort when the Roster
|
|
3
|
+
* doesn't store the source's raw faction/detachment name. */
|
|
4
|
+
export function titleCaseId(id) {
|
|
5
|
+
if (id === null)
|
|
6
|
+
return null;
|
|
7
|
+
if (id.length === 0)
|
|
8
|
+
return id;
|
|
9
|
+
return id
|
|
10
|
+
.split("-")
|
|
11
|
+
.map((seg) => (seg.length === 0 ? seg : seg[0].toUpperCase() + seg.slice(1)))
|
|
12
|
+
.join(" ");
|
|
13
|
+
}
|
|
14
|
+
/** Sum of unit base pts + enhancement pts (= the figure most text formats display). */
|
|
15
|
+
export function displayedUnitPoints(u) {
|
|
16
|
+
if (u.points === null)
|
|
17
|
+
return null;
|
|
18
|
+
return u.points + (u.enhancement_points ?? 0);
|
|
19
|
+
}
|
|
20
|
+
/** Sum of every unit's displayed total + every enhancement cost line. */
|
|
21
|
+
export function totalArmyPoints(roster) {
|
|
22
|
+
let total = 0;
|
|
23
|
+
for (const u of roster.units) {
|
|
24
|
+
total += u.points ?? 0;
|
|
25
|
+
total += u.enhancement_points ?? 0;
|
|
26
|
+
}
|
|
27
|
+
return total;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Heuristic re-derivation of which units would carry a `CharN:` prefix on
|
|
31
|
+
* export to a wtc text format. The Roster doesn't track unit categories, so we
|
|
32
|
+
* approximate "is a character" as "is the warlord OR has an enhancement OR has
|
|
33
|
+
* a leader attachment". CharN: numbering follows declaration order.
|
|
34
|
+
*
|
|
35
|
+
* Returns a parallel array: `slot[i]` is the 1-based char index for unit i, or
|
|
36
|
+
* `null` if that unit doesn't get a CharN: prefix.
|
|
37
|
+
*/
|
|
38
|
+
export function charSlotAssignment(units) {
|
|
39
|
+
const result = [];
|
|
40
|
+
let next = 1;
|
|
41
|
+
for (const u of units) {
|
|
42
|
+
const isChar = u.is_warlord || u.enhancement !== null || u.leader_attachment !== null;
|
|
43
|
+
if (isChar) {
|
|
44
|
+
result.push(next);
|
|
45
|
+
next += 1;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
result.push(null);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/** Pretty JSON with a trailing newline — matches the repo's 2-space convention. */
|
|
54
|
+
export function prettyJson(value) {
|
|
55
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/export/helpers.ts"],"names":[],"mappings":"AAaA;;6DAE6D;AAC7D,MAAM,UAAU,WAAW,CAAC,EAAiB;IAC3C,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,OAAO,EAAE;SACN,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;SAC5E,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,CAAa;IAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QACvB,KAAK,IAAI,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA4B;IAC7D,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,IAAI,CAAC,CAAC,iBAAiB,KAAK,IAAI,CAAC;QACtF,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC","sourcesContent":["/**\n * Shared helpers for the roster exporters.\n *\n * Exporters are deterministic and Dataset-free: they read the Roster only and\n * regenerate format-specific decoration (display names, Char-slot numbering,\n * displayed unit totals) from what's stored. Anything the Roster doesn't model\n * — char-slot numbers, the detachment \"<X> Character\" keyword, secondary-\n * objective summaries — is either derived heuristically here or dropped.\n *\n * @packageDocumentation\n */\nimport type { Roster, RosterUnit } from \"../import/types.js\";\n\n/** Convert a kebab-case entity id (\"chaos-knights\") to a Title Case display\n * name (\"Chaos Knights\"). This is the round-trip best-effort when the Roster\n * doesn't store the source's raw faction/detachment name. */\nexport function titleCaseId(id: string | null): string | null {\n if (id === null) return null;\n if (id.length === 0) return id;\n return id\n .split(\"-\")\n .map((seg) => (seg.length === 0 ? seg : seg[0].toUpperCase() + seg.slice(1)))\n .join(\" \");\n}\n\n/** Sum of unit base pts + enhancement pts (= the figure most text formats display). */\nexport function displayedUnitPoints(u: RosterUnit): number | null {\n if (u.points === null) return null;\n return u.points + (u.enhancement_points ?? 0);\n}\n\n/** Sum of every unit's displayed total + every enhancement cost line. */\nexport function totalArmyPoints(roster: Roster): number {\n let total = 0;\n for (const u of roster.units) {\n total += u.points ?? 0;\n total += u.enhancement_points ?? 0;\n }\n return total;\n}\n\n/**\n * Heuristic re-derivation of which units would carry a `CharN:` prefix on\n * export to a wtc text format. The Roster doesn't track unit categories, so we\n * approximate \"is a character\" as \"is the warlord OR has an enhancement OR has\n * a leader attachment\". CharN: numbering follows declaration order.\n *\n * Returns a parallel array: `slot[i]` is the 1-based char index for unit i, or\n * `null` if that unit doesn't get a CharN: prefix.\n */\nexport function charSlotAssignment(units: readonly RosterUnit[]): (number | null)[] {\n const result: (number | null)[] = [];\n let next = 1;\n for (const u of units) {\n const isChar = u.is_warlord || u.enhancement !== null || u.leader_attachment !== null;\n if (isChar) {\n result.push(next);\n next += 1;\n } else {\n result.push(null);\n }\n }\n return result;\n}\n\n/** Pretty JSON with a trailing newline — matches the repo's 2-space convention. */\nexport function prettyJson(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roster exporters — the symmetric counterpart to the importer.
|
|
3
|
+
*
|
|
4
|
+
* `exportRoster(roster, format)` dispatches to one of five registered
|
|
5
|
+
* serializers (NewRecruit JSON, the three NewRecruit text formats, and the
|
|
6
|
+
* canonical Roster JSON). Each serializer is deterministic and Dataset-free,
|
|
7
|
+
* so the TS and Rust mirrors can produce byte-identical output for
|
|
8
|
+
* cross-implementation conformance.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { Roster } from "../import/types.js";
|
|
13
|
+
import type { ExportFormat } from "./serializer.js";
|
|
14
|
+
export type { ExportFormat, RosterSerializer } from "./serializer.js";
|
|
15
|
+
export { newRecruitJsonSerializer } from "./newrecruit-json.js";
|
|
16
|
+
export { newRecruitSimpleSerializer } from "./newrecruit-simple.js";
|
|
17
|
+
export { newRecruitWtcCompactSerializer, newRecruitWtcFullSerializer, } from "./newrecruit-wtc.js";
|
|
18
|
+
export { rosterJsonSerializer } from "./roster-json.js";
|
|
19
|
+
/** Serialize a {@link Roster} into the named target format. */
|
|
20
|
+
export declare function exportRoster(roster: Roster, format: ExportFormat): string;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAQjD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AAEtE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAWxD,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAQzE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { newRecruitJsonSerializer } from "./newrecruit-json.js";
|
|
2
|
+
import { newRecruitSimpleSerializer } from "./newrecruit-simple.js";
|
|
3
|
+
import { newRecruitWtcCompactSerializer, newRecruitWtcFullSerializer, } from "./newrecruit-wtc.js";
|
|
4
|
+
import { rosterJsonSerializer } from "./roster-json.js";
|
|
5
|
+
export { newRecruitJsonSerializer } from "./newrecruit-json.js";
|
|
6
|
+
export { newRecruitSimpleSerializer } from "./newrecruit-simple.js";
|
|
7
|
+
export { newRecruitWtcCompactSerializer, newRecruitWtcFullSerializer, } from "./newrecruit-wtc.js";
|
|
8
|
+
export { rosterJsonSerializer } from "./roster-json.js";
|
|
9
|
+
/** All registered serializers, keyed by their {@link ExportFormat} id. */
|
|
10
|
+
const SERIALIZERS = [
|
|
11
|
+
newRecruitJsonSerializer,
|
|
12
|
+
newRecruitWtcCompactSerializer,
|
|
13
|
+
newRecruitWtcFullSerializer,
|
|
14
|
+
newRecruitSimpleSerializer,
|
|
15
|
+
rosterJsonSerializer,
|
|
16
|
+
];
|
|
17
|
+
/** Serialize a {@link Roster} into the named target format. */
|
|
18
|
+
export function exportRoster(roster, format) {
|
|
19
|
+
const s = SERIALIZERS.find((s) => s.id === format);
|
|
20
|
+
if (!s) {
|
|
21
|
+
throw new Error(`unknown export format: ${format} (registered: ${SERIALIZERS.map((s) => s.id).join(", ")})`);
|
|
22
|
+
}
|
|
23
|
+
return s.serialize(roster);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAIxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,0EAA0E;AAC1E,MAAM,WAAW,GAAgC;IAC/C,wBAAwB;IACxB,8BAA8B;IAC9B,2BAA2B;IAC3B,0BAA0B;IAC1B,oBAAoB;CACrB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,MAAoB;IAC/D,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,0BAA0B,MAAM,iBAAiB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC5F,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["/**\n * Roster exporters — the symmetric counterpart to the importer.\n *\n * `exportRoster(roster, format)` dispatches to one of five registered\n * serializers (NewRecruit JSON, the three NewRecruit text formats, and the\n * canonical Roster JSON). Each serializer is deterministic and Dataset-free,\n * so the TS and Rust mirrors can produce byte-identical output for\n * cross-implementation conformance.\n *\n * @packageDocumentation\n */\nimport type { Roster } from \"../import/types.js\";\nimport { newRecruitJsonSerializer } from \"./newrecruit-json.js\";\nimport { newRecruitSimpleSerializer } from \"./newrecruit-simple.js\";\nimport {\n newRecruitWtcCompactSerializer,\n newRecruitWtcFullSerializer,\n} from \"./newrecruit-wtc.js\";\nimport { rosterJsonSerializer } from \"./roster-json.js\";\nimport type { ExportFormat, RosterSerializer } from \"./serializer.js\";\n\nexport type { ExportFormat, RosterSerializer } from \"./serializer.js\";\nexport { newRecruitJsonSerializer } from \"./newrecruit-json.js\";\nexport { newRecruitSimpleSerializer } from \"./newrecruit-simple.js\";\nexport {\n newRecruitWtcCompactSerializer,\n newRecruitWtcFullSerializer,\n} from \"./newrecruit-wtc.js\";\nexport { rosterJsonSerializer } from \"./roster-json.js\";\n\n/** All registered serializers, keyed by their {@link ExportFormat} id. */\nconst SERIALIZERS: readonly RosterSerializer[] = [\n newRecruitJsonSerializer,\n newRecruitWtcCompactSerializer,\n newRecruitWtcFullSerializer,\n newRecruitSimpleSerializer,\n rosterJsonSerializer,\n];\n\n/** Serialize a {@link Roster} into the named target format. */\nexport function exportRoster(roster: Roster, format: ExportFormat): string {\n const s = SERIALIZERS.find((s) => s.id === format);\n if (!s) {\n throw new Error(\n `unknown export format: ${format} (registered: ${SERIALIZERS.map((s) => s.id).join(\", \")})`,\n );\n }\n return s.serialize(roster);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"newrecruit-json.d.ts","sourceRoot":"","sources":["../../src/export/newrecruit-json.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAkJxD,eAAO,MAAM,wBAAwB,EAAE,gBAuCtC,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { prettyJson, titleCaseId, totalArmyPoints } from "./helpers.js";
|
|
2
|
+
const PTS_TYPE_ID = "pts-type";
|
|
3
|
+
const NEWRECRUIT_XMLNS = "http://www.battlescribe.net/schema/rosterSchema";
|
|
4
|
+
const NEWRECRUIT_GENERATED_BY = "https://newrecruit.eu";
|
|
5
|
+
/** Build a "Faction: <name>" category from the unit's roster context. */
|
|
6
|
+
function factionCategory(roster) {
|
|
7
|
+
const display = titleCaseId(roster.faction_id);
|
|
8
|
+
if (display === null)
|
|
9
|
+
return null;
|
|
10
|
+
return { name: `Faction: ${display}`, primary: false };
|
|
11
|
+
}
|
|
12
|
+
function wargearSelection(idx, w) {
|
|
13
|
+
return {
|
|
14
|
+
id: `w-${idx}`,
|
|
15
|
+
name: w.ref.raw_name,
|
|
16
|
+
type: "upgrade",
|
|
17
|
+
number: w.count,
|
|
18
|
+
// The NewRecruit importer recognises a wargear selection by a category
|
|
19
|
+
// ending in " Weapon" — emit a generic "Ranged Weapon" so we don't have
|
|
20
|
+
// to track ranged-vs-melee separation the Roster doesn't model.
|
|
21
|
+
categories: [{ name: "Ranged Weapon", primary: false }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function unitSelection(idx, u, faction) {
|
|
25
|
+
const inner = [];
|
|
26
|
+
if (u.is_warlord) {
|
|
27
|
+
inner.push({ id: `u${idx}-warlord`, name: "Warlord", type: "upgrade", number: 1 });
|
|
28
|
+
}
|
|
29
|
+
if (u.enhancement) {
|
|
30
|
+
const enhCost = u.enhancement_points === null
|
|
31
|
+
? undefined
|
|
32
|
+
: [{ name: "pts", typeId: PTS_TYPE_ID, value: u.enhancement_points }];
|
|
33
|
+
inner.push({
|
|
34
|
+
id: `u${idx}-enh`,
|
|
35
|
+
name: u.enhancement.raw_name,
|
|
36
|
+
type: "upgrade",
|
|
37
|
+
number: 1,
|
|
38
|
+
group: "Enhancements",
|
|
39
|
+
...(enhCost ? { costs: enhCost } : {}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const wargearSelections = u.wargear.map((w, wi) => wargearSelection(wi, w));
|
|
43
|
+
const ownCategories = faction ? [faction] : [];
|
|
44
|
+
if (u.model_count <= 1) {
|
|
45
|
+
return {
|
|
46
|
+
id: `u-${idx}`,
|
|
47
|
+
name: u.ref.raw_name,
|
|
48
|
+
type: "model",
|
|
49
|
+
number: 1,
|
|
50
|
+
categories: ownCategories,
|
|
51
|
+
...(u.points === null ? {} : { costs: [{ name: "pts", typeId: PTS_TYPE_ID, value: u.points }] }),
|
|
52
|
+
selections: [...inner, ...wargearSelections],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Multi-model: wrap in a `type: "unit"` with a nested `type: "model"` that
|
|
56
|
+
// carries the model count and the (collapsed, per-unit) wargear.
|
|
57
|
+
return {
|
|
58
|
+
id: `u-${idx}`,
|
|
59
|
+
name: u.ref.raw_name,
|
|
60
|
+
type: "unit",
|
|
61
|
+
number: 1,
|
|
62
|
+
categories: ownCategories,
|
|
63
|
+
...(u.points === null ? {} : { costs: [{ name: "pts", typeId: PTS_TYPE_ID, value: u.points }] }),
|
|
64
|
+
selections: [
|
|
65
|
+
...inner,
|
|
66
|
+
{
|
|
67
|
+
id: `u${idx}-model`,
|
|
68
|
+
name: u.ref.raw_name,
|
|
69
|
+
type: "model",
|
|
70
|
+
number: u.model_count,
|
|
71
|
+
selections: wargearSelections,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function configSelection(name, value, idx) {
|
|
77
|
+
return {
|
|
78
|
+
id: `cfg-${idx}`,
|
|
79
|
+
name,
|
|
80
|
+
type: "upgrade",
|
|
81
|
+
number: 1,
|
|
82
|
+
categories: [{ name: "Configuration", primary: true }],
|
|
83
|
+
selections: [
|
|
84
|
+
{
|
|
85
|
+
id: `cfg-${idx}-val`,
|
|
86
|
+
name: value,
|
|
87
|
+
type: "upgrade",
|
|
88
|
+
number: 1,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function battleSizeLabel(roster) {
|
|
94
|
+
if (roster.battle_size === "strike-force") {
|
|
95
|
+
const limit = roster.points.declared_limit ?? 2000;
|
|
96
|
+
return `Strike Force (${limit} Point limit)`;
|
|
97
|
+
}
|
|
98
|
+
if (roster.battle_size === "incursion") {
|
|
99
|
+
const limit = roster.points.declared_limit ?? 1000;
|
|
100
|
+
return `Incursion (${limit} Point limit)`;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
export const newRecruitJsonSerializer = {
|
|
105
|
+
id: "newrecruit-json",
|
|
106
|
+
serialize(roster) {
|
|
107
|
+
const faction = factionCategory(roster);
|
|
108
|
+
const factionDisplay = titleCaseId(roster.faction_id) ?? "Unknown";
|
|
109
|
+
const detachmentDisplay = titleCaseId(roster.detachment_id);
|
|
110
|
+
const battleSize = battleSizeLabel(roster);
|
|
111
|
+
const config = [];
|
|
112
|
+
if (battleSize)
|
|
113
|
+
config.push(configSelection("Battle Size", battleSize, "battle-size"));
|
|
114
|
+
if (detachmentDisplay)
|
|
115
|
+
config.push(configSelection("Detachment", detachmentDisplay, "detachment"));
|
|
116
|
+
const force = {
|
|
117
|
+
id: "force-1",
|
|
118
|
+
name: "Army Roster",
|
|
119
|
+
catalogueName: factionDisplay,
|
|
120
|
+
selections: [
|
|
121
|
+
...config,
|
|
122
|
+
...roster.units.map((u, i) => unitSelection(i, u, faction)),
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
const total = totalArmyPoints(roster);
|
|
126
|
+
const payload = {
|
|
127
|
+
name: roster.name,
|
|
128
|
+
generatedBy: NEWRECRUIT_GENERATED_BY,
|
|
129
|
+
roster: {
|
|
130
|
+
name: roster.name,
|
|
131
|
+
xmlns: NEWRECRUIT_XMLNS,
|
|
132
|
+
generatedBy: NEWRECRUIT_GENERATED_BY,
|
|
133
|
+
costs: [{ name: "pts", typeId: PTS_TYPE_ID, value: total }],
|
|
134
|
+
forces: [force],
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
return prettyJson(payload);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=newrecruit-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"newrecruit-json.js","sourceRoot":"","sources":["../../src/export/newrecruit-json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * NewRecruit JSON exporter — emits a BattleScribe-shaped roster skeleton that\n * round-trips through {@link newRecruitJsonAdapter}.\n *\n * The shape carries only fields the importer reads: `name`, `type`, `number`,\n * `costs[]`, `categories[].name`, `group`, and `catalogueName`. No `rules` /\n * `profiles` / `description` ever appear — we don't store them, and emitting\n * them would be an IP violation.\n *\n * Faction and detachment display names come from\n * {@link titleCaseId}(faction_id) — the Roster doesn't carry the source's raw\n * faction name, so we reconstruct it from the kebab-case id. This is the only\n * lossy hop in the JSON round-trip (e.g. `tau-empire` → \"Tau Empire\" rather\n * than the canonical \"T'au Empire\").\n *\n * @packageDocumentation\n */\nimport type { Roster, RosterUnit, RosterWargear } from \"../import/types.js\";\nimport { prettyJson, titleCaseId, totalArmyPoints } from \"./helpers.js\";\nimport type { RosterSerializer } from \"./serializer.js\";\n\nconst PTS_TYPE_ID = \"pts-type\";\nconst NEWRECRUIT_XMLNS = \"http://www.battlescribe.net/schema/rosterSchema\";\nconst NEWRECRUIT_GENERATED_BY = \"https://newrecruit.eu\";\n\ninterface JsonSelection {\n id: string;\n name: string;\n type: \"model\" | \"unit\" | \"upgrade\";\n number: number;\n group?: string;\n categories?: { name: string; primary: boolean }[];\n costs?: { name: string; typeId: string; value: number }[];\n selections?: JsonSelection[];\n}\n\ninterface JsonForce {\n id: string;\n name: string;\n catalogueName: string;\n selections: JsonSelection[];\n}\n\ninterface JsonRoster {\n name: string;\n xmlns: string;\n generatedBy: string;\n costs: { name: string; typeId: string; value: number }[];\n forces: JsonForce[];\n}\n\ninterface JsonPayload {\n name: string;\n generatedBy: string;\n roster: JsonRoster;\n}\n\n/** Build a \"Faction: <name>\" category from the unit's roster context. */\nfunction factionCategory(roster: Roster): { name: string; primary: boolean } | null {\n const display = titleCaseId(roster.faction_id);\n if (display === null) return null;\n return { name: `Faction: ${display}`, primary: false };\n}\n\nfunction wargearSelection(idx: number, w: RosterWargear): JsonSelection {\n return {\n id: `w-${idx}`,\n name: w.ref.raw_name,\n type: \"upgrade\",\n number: w.count,\n // The NewRecruit importer recognises a wargear selection by a category\n // ending in \" Weapon\" — emit a generic \"Ranged Weapon\" so we don't have\n // to track ranged-vs-melee separation the Roster doesn't model.\n categories: [{ name: \"Ranged Weapon\", primary: false }],\n };\n}\n\nfunction unitSelection(idx: number, u: RosterUnit, faction: { name: string; primary: boolean } | null): JsonSelection {\n const inner: JsonSelection[] = [];\n if (u.is_warlord) {\n inner.push({ id: `u${idx}-warlord`, name: \"Warlord\", type: \"upgrade\", number: 1 });\n }\n if (u.enhancement) {\n const enhCost =\n u.enhancement_points === null\n ? undefined\n : [{ name: \"pts\", typeId: PTS_TYPE_ID, value: u.enhancement_points }];\n inner.push({\n id: `u${idx}-enh`,\n name: u.enhancement.raw_name,\n type: \"upgrade\",\n number: 1,\n group: \"Enhancements\",\n ...(enhCost ? { costs: enhCost } : {}),\n });\n }\n\n const wargearSelections = u.wargear.map((w, wi) => wargearSelection(wi, w));\n\n const ownCategories = faction ? [faction] : [];\n\n if (u.model_count <= 1) {\n return {\n id: `u-${idx}`,\n name: u.ref.raw_name,\n type: \"model\",\n number: 1,\n categories: ownCategories,\n ...(u.points === null ? {} : { costs: [{ name: \"pts\", typeId: PTS_TYPE_ID, value: u.points }] }),\n selections: [...inner, ...wargearSelections],\n };\n }\n\n // Multi-model: wrap in a `type: \"unit\"` with a nested `type: \"model\"` that\n // carries the model count and the (collapsed, per-unit) wargear.\n return {\n id: `u-${idx}`,\n name: u.ref.raw_name,\n type: \"unit\",\n number: 1,\n categories: ownCategories,\n ...(u.points === null ? {} : { costs: [{ name: \"pts\", typeId: PTS_TYPE_ID, value: u.points }] }),\n selections: [\n ...inner,\n {\n id: `u${idx}-model`,\n name: u.ref.raw_name,\n type: \"model\",\n number: u.model_count,\n selections: wargearSelections,\n },\n ],\n };\n}\n\nfunction configSelection(name: string, value: string, idx: string): JsonSelection {\n return {\n id: `cfg-${idx}`,\n name,\n type: \"upgrade\",\n number: 1,\n categories: [{ name: \"Configuration\", primary: true }],\n selections: [\n {\n id: `cfg-${idx}-val`,\n name: value,\n type: \"upgrade\",\n number: 1,\n },\n ],\n };\n}\n\nfunction battleSizeLabel(roster: Roster): string | null {\n if (roster.battle_size === \"strike-force\") {\n const limit = roster.points.declared_limit ?? 2000;\n return `Strike Force (${limit} Point limit)`;\n }\n if (roster.battle_size === \"incursion\") {\n const limit = roster.points.declared_limit ?? 1000;\n return `Incursion (${limit} Point limit)`;\n }\n return null;\n}\n\nexport const newRecruitJsonSerializer: RosterSerializer = {\n id: \"newrecruit-json\",\n\n serialize(roster: Roster): string {\n const faction = factionCategory(roster);\n const factionDisplay = titleCaseId(roster.faction_id) ?? \"Unknown\";\n const detachmentDisplay = titleCaseId(roster.detachment_id);\n const battleSize = battleSizeLabel(roster);\n\n const config: JsonSelection[] = [];\n if (battleSize) config.push(configSelection(\"Battle Size\", battleSize, \"battle-size\"));\n if (detachmentDisplay) config.push(configSelection(\"Detachment\", detachmentDisplay, \"detachment\"));\n\n const force: JsonForce = {\n id: \"force-1\",\n name: \"Army Roster\",\n catalogueName: factionDisplay,\n selections: [\n ...config,\n ...roster.units.map((u, i) => unitSelection(i, u, faction)),\n ],\n };\n\n const total = totalArmyPoints(roster);\n\n const payload: JsonPayload = {\n name: roster.name,\n generatedBy: NEWRECRUIT_GENERATED_BY,\n roster: {\n name: roster.name,\n xmlns: NEWRECRUIT_XMLNS,\n generatedBy: NEWRECRUIT_GENERATED_BY,\n costs: [{ name: \"pts\", typeId: PTS_TYPE_ID, value: total }],\n forces: [force],\n },\n };\n\n return prettyJson(payload);\n },\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"newrecruit-simple.d.ts","sourceRoot":"","sources":["../../src/export/newrecruit-simple.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAkDxD,eAAO,MAAM,0BAA0B,EAAE,gBAgCxC,CAAC"}
|