@alpaca-software/40kdc-data 0.5.11 → 0.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +10 -0
  2. package/dist/author-input-pack.d.ts +27 -0
  3. package/dist/author-input-pack.d.ts.map +1 -0
  4. package/dist/author-input-pack.js +198 -0
  5. package/dist/author-input-pack.js.map +1 -0
  6. package/dist/codegen-data.js +1 -0
  7. package/dist/codegen-data.js.map +1 -1
  8. package/dist/commands/import.d.ts.map +1 -1
  9. package/dist/commands/import.js +3 -1
  10. package/dist/commands/import.js.map +1 -1
  11. package/dist/convert-faction.d.ts.map +1 -1
  12. package/dist/convert-faction.js +22 -2
  13. package/dist/convert-faction.js.map +1 -1
  14. package/dist/data/bundle.generated.js +1 -1
  15. package/dist/data/bundle.generated.js.map +1 -1
  16. package/dist/data/dataset.d.ts +2 -1
  17. package/dist/data/dataset.d.ts.map +1 -1
  18. package/dist/data/dataset.js +2 -0
  19. package/dist/data/dataset.js.map +1 -1
  20. package/dist/data/entities.d.ts +22 -0
  21. package/dist/data/entities.d.ts.map +1 -1
  22. package/dist/data/entities.js +28 -0
  23. package/dist/data/entities.js.map +1 -1
  24. package/dist/data/index.d.ts +3 -1
  25. package/dist/data/index.d.ts.map +1 -1
  26. package/dist/data/index.js +3 -1
  27. package/dist/data/index.js.map +1 -1
  28. package/dist/data/roster-resolve.d.ts +16 -1
  29. package/dist/data/roster-resolve.d.ts.map +1 -1
  30. package/dist/data/roster-resolve.js +19 -0
  31. package/dist/data/roster-resolve.js.map +1 -1
  32. package/dist/data/types.d.ts +3 -1
  33. package/dist/data/types.d.ts.map +1 -1
  34. package/dist/data/types.js +1 -0
  35. package/dist/data/types.js.map +1 -1
  36. package/dist/export/newrecruit-json.d.ts.map +1 -1
  37. package/dist/export/newrecruit-json.js +4 -3
  38. package/dist/export/newrecruit-json.js.map +1 -1
  39. package/dist/export/newrecruit-simple.js +2 -2
  40. package/dist/export/newrecruit-simple.js.map +1 -1
  41. package/dist/export/newrecruit-wtc.d.ts.map +1 -1
  42. package/dist/export/newrecruit-wtc.js +3 -1
  43. package/dist/export/newrecruit-wtc.js.map +1 -1
  44. package/dist/export/rosterizer.d.ts.map +1 -1
  45. package/dist/export/rosterizer.js +6 -7
  46. package/dist/export/rosterizer.js.map +1 -1
  47. package/dist/extract-faction-pack.d.ts +61 -0
  48. package/dist/extract-faction-pack.d.ts.map +1 -0
  49. package/dist/extract-faction-pack.js +318 -0
  50. package/dist/extract-faction-pack.js.map +1 -0
  51. package/dist/gen-conformance.js +37 -4
  52. package/dist/gen-conformance.js.map +1 -1
  53. package/dist/generated.d.ts +46 -2
  54. package/dist/generated.d.ts.map +1 -1
  55. package/dist/generated.js.map +1 -1
  56. package/dist/import/gw-headerless.d.ts +70 -0
  57. package/dist/import/gw-headerless.d.ts.map +1 -0
  58. package/dist/import/gw-headerless.js +343 -0
  59. package/dist/import/gw-headerless.js.map +1 -0
  60. package/dist/import/gw.js +1 -1
  61. package/dist/import/gw.js.map +1 -1
  62. package/dist/import/import-roster.d.ts.map +1 -1
  63. package/dist/import/import-roster.js +8 -0
  64. package/dist/import/import-roster.js.map +1 -1
  65. package/dist/import/listforge-text.js +1 -1
  66. package/dist/import/listforge-text.js.map +1 -1
  67. package/dist/import/listforge.d.ts.map +1 -1
  68. package/dist/import/listforge.js +18 -3
  69. package/dist/import/listforge.js.map +1 -1
  70. package/dist/import/newrecruit-json.d.ts.map +1 -1
  71. package/dist/import/newrecruit-json.js +21 -3
  72. package/dist/import/newrecruit-json.js.map +1 -1
  73. package/dist/import/newrecruit-simple.js +1 -1
  74. package/dist/import/newrecruit-simple.js.map +1 -1
  75. package/dist/import/newrecruit-wtc.js +1 -1
  76. package/dist/import/newrecruit-wtc.js.map +1 -1
  77. package/dist/import/resolve.d.ts.map +1 -1
  78. package/dist/import/resolve.js +46 -18
  79. package/dist/import/resolve.js.map +1 -1
  80. package/dist/import/roster-json.d.ts +5 -3
  81. package/dist/import/roster-json.d.ts.map +1 -1
  82. package/dist/import/roster-json.js +8 -4
  83. package/dist/import/roster-json.js.map +1 -1
  84. package/dist/import/rosterizer.js +3 -3
  85. package/dist/import/rosterizer.js.map +1 -1
  86. package/dist/import/types.d.ts +15 -4
  87. package/dist/import/types.d.ts.map +1 -1
  88. package/dist/import/types.js.map +1 -1
  89. package/dist/index.d.ts +1 -0
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +4 -0
  92. package/dist/index.js.map +1 -1
  93. package/dist/merge-faction-pack.d.ts +2 -0
  94. package/dist/merge-faction-pack.d.ts.map +1 -0
  95. package/dist/merge-faction-pack.js +157 -0
  96. package/dist/merge-faction-pack.js.map +1 -0
  97. package/dist/pack-blocks.d.ts +32 -0
  98. package/dist/pack-blocks.d.ts.map +1 -0
  99. package/dist/pack-blocks.js +99 -0
  100. package/dist/pack-blocks.js.map +1 -0
  101. package/dist/rube-goldberg.js +1 -1
  102. package/dist/rube-goldberg.js.map +1 -1
  103. package/dist/runner.d.ts.map +1 -1
  104. package/dist/runner.js +33 -1
  105. package/dist/runner.js.map +1 -1
  106. package/dist/scope.d.ts +31 -0
  107. package/dist/scope.d.ts.map +1 -0
  108. package/dist/scope.js +34 -0
  109. package/dist/scope.js.map +1 -0
  110. package/dist/seed-detachment-abilities.d.ts +2 -0
  111. package/dist/seed-detachment-abilities.d.ts.map +1 -0
  112. package/dist/seed-detachment-abilities.js +137 -0
  113. package/dist/seed-detachment-abilities.js.map +1 -0
  114. package/dist/translate/effect.d.ts +17 -3
  115. package/dist/translate/effect.d.ts.map +1 -1
  116. package/dist/translate/effect.js +23 -4
  117. package/dist/translate/effect.js.map +1 -1
  118. package/dist/translate/index.d.ts +1 -1
  119. package/dist/translate/index.d.ts.map +1 -1
  120. package/dist/translate/index.js +1 -1
  121. package/dist/translate/index.js.map +1 -1
  122. package/dist/validate.d.ts.map +1 -1
  123. package/dist/validate.js +1 -0
  124. package/dist/validate.js.map +1 -1
  125. package/package.json +3 -1
  126. package/schemas/core/detachment.schema.json +6 -0
  127. package/schemas/core/hull-shape.schema.json +30 -0
  128. package/schemas/core/roster.schema.json +24 -8
  129. package/schemas/core/stratagem.schema.json +2 -2
  130. package/schemas/core/unit-composition.schema.json +7 -0
  131. package/schemas/enrichment/ability-dsl/ability.schema.json +14 -0
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @packageDocumentation
7
7
  */
8
- import type { DeploymentPattern, Detachment, Enhancement, ForceDisposition, GameVersion, InteractionFlag, LeaderAttachment, Mission, MissionMatchup, Phase, ResourcePool, SecondaryCard, Stratagem, TargetProfile, TerrainLayout, TerrainTemplate, TimingFlag, Unit, UnitComposition, Wargear, WargearOption, WeaponKeyword } from "../generated.js";
8
+ import type { DeploymentPattern, Detachment, Enhancement, ForceDisposition, GameVersion, HullShape, InteractionFlag, LeaderAttachment, Mission, MissionMatchup, Phase, ResourcePool, SecondaryCard, Stratagem, TargetProfile, TerrainLayout, TerrainTemplate, TimingFlag, Unit, UnitComposition, Wargear, WargearOption, WeaponKeyword } from "../generated.js";
9
9
  import { Collection } from "./collection.js";
10
10
  import { AbilityView, FactionView, UnitView, WeaponKeywordView, WeaponView } from "./entities.js";
11
11
  import { type RawData } from "./types.js";
@@ -62,6 +62,7 @@ export declare class Dataset {
62
62
  readonly forceDispositions: Collection<ForceDisposition, ForceDisposition>;
63
63
  readonly terrainTemplates: Collection<TerrainTemplate, TerrainTemplate>;
64
64
  readonly terrainLayouts: Collection<TerrainLayout, TerrainLayout>;
65
+ readonly hullShapes: Collection<HullShape, HullShape>;
65
66
  readonly resourcePools: Collection<ResourcePool, ResourcePool>;
66
67
  readonly leaderAttachments: readonly LeaderAttachment[];
67
68
  readonly unitCompositions: readonly UnitComposition[];
@@ -1 +1 @@
1
- {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../../src/data/dataset.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../../src/data/dataset.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,7 @@ export class Dataset {
26
26
  forceDispositions;
27
27
  terrainTemplates;
28
28
  terrainLayouts;
29
+ hullShapes;
29
30
  resourcePools;
30
31
  // Id-less collections, exposed as plain arrays.
31
32
  leaderAttachments;
@@ -93,6 +94,7 @@ export class Dataset {
93
94
  this.forceDispositions = idCollection(raw.forceDispositions);
94
95
  this.terrainTemplates = idCollection(raw.terrainTemplates);
95
96
  this.terrainLayouts = idCollection(raw.terrainLayouts);
97
+ this.hullShapes = idCollection(raw.hullShapes);
96
98
  this.resourcePools = idCollection(raw.resourcePools);
97
99
  this.leaderAttachments = raw.leaderAttachments;
98
100
  this.unitCompositions = raw.unitCompositions;
@@ -1 +1 @@
1
- {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/data/dataset.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * {@link Dataset} ties the embedded records together: it owns every\n * {@link Collection}, builds the cross-entity indexes once, and is the `this`\n * the linked views resolve against.\n *\n * @packageDocumentation\n */\nimport type {\n DeploymentPattern,\n Detachment,\n Enhancement,\n ForceDisposition,\n GameVersion,\n InteractionFlag,\n LeaderAttachment,\n Mission,\n MissionMatchup,\n Phase,\n ResourcePool,\n SecondaryCard,\n Stratagem,\n TargetProfile,\n TerrainLayout,\n TerrainTemplate,\n TimingFlag,\n Unit,\n UnitComposition,\n Wargear,\n WargearOption,\n WeaponKeyword,\n} from \"../generated.js\";\nimport { Collection } from \"./collection.js\";\nimport {\n AbilityView,\n FactionView,\n UnitView,\n WeaponKeywordView,\n WeaponView,\n} from \"./entities.js\";\nimport { emptyRawData, type RawData } from \"./types.js\";\nimport { RAW_DATA } from \"./bundle.generated.js\";\nimport { resolveLayout, type ResolvedPiece } from \"../terrain/resolve.js\";\nimport type { Buff, BuffSource, EngineContext } from \"../cruncher/buffs.js\";\nimport {\n resolveEligibleAbilities,\n type EligibilityInput,\n type EligibleAbility,\n} from \"../abilities-resolver/index.js\";\n\n/**\n * One toggleable buff lever for damage analysis: the contributions it adds and\n * whether it's on by default. `enabled` is `true` for buffs that always apply\n * (intrinsic keywords, unconditional abilities) and `false` for player\n * decisions — stratagems (CP cost) and activatable gates (dice-pool options,\n * `choice` branches, timing-gated activations). A consumer flips `enabled`,\n * then crunches the enabled subset; an optimizer searches it.\n *\n * @see {@link Dataset.stackableBuffsFor}\n */\nexport type StackableBuff = {\n /** Stable toggle id (stable across re-enumeration of the same input). */\n id: string;\n /** Human label for the lever. */\n label: string;\n /** Contributions this lever adds when enabled (≥1). */\n buffs: Buff[];\n /** Default selection state. */\n enabled: boolean;\n /** Where the lever came from. */\n source: BuffSource;\n /** Id of the mutually-limited {@link StackableBuffGroup} this belongs to, if any. */\n group?: string;\n};\n\n/** A pool of {@link StackableBuff} levers limited to `maxActivations` at once. */\nexport type StackableBuffGroup = {\n id: string;\n label: string;\n maxActivations: number;\n};\n\n/** The whole dataset, with linked accessors over every entity collection. */\nexport class Dataset {\n // Richly-linked collections.\n readonly units: Collection<Unit, UnitView>;\n readonly weapons: Collection<RawData[\"weapons\"][number], WeaponView>;\n readonly weaponKeywords: Collection<WeaponKeyword, WeaponKeywordView>;\n readonly factions: Collection<RawData[\"factions\"][number], FactionView>;\n readonly abilities: Collection<RawData[\"abilities\"][number], AbilityView>;\n\n // Id-bearing collections without bespoke views (records returned as-is).\n readonly targetProfiles: Collection<TargetProfile, TargetProfile>;\n readonly detachments: Collection<Detachment, Detachment>;\n readonly enhancements: Collection<Enhancement, Enhancement>;\n readonly stratagems: Collection<Stratagem, Stratagem>;\n readonly wargearOptions: Collection<WargearOption, WargearOption>;\n readonly wargear: Collection<Wargear, Wargear>;\n readonly missions: Collection<Mission, Mission>;\n readonly missionMatchups: Collection<MissionMatchup, MissionMatchup>;\n readonly missionCards: Collection<SecondaryCard, SecondaryCard>;\n readonly deploymentPatterns: Collection<DeploymentPattern, DeploymentPattern>;\n readonly forceDispositions: Collection<ForceDisposition, ForceDisposition>;\n readonly terrainTemplates: Collection<TerrainTemplate, TerrainTemplate>;\n readonly terrainLayouts: Collection<TerrainLayout, TerrainLayout>;\n readonly resourcePools: Collection<ResourcePool, ResourcePool>;\n\n // Id-less collections, exposed as plain arrays.\n readonly leaderAttachments: readonly LeaderAttachment[];\n readonly unitCompositions: readonly UnitComposition[];\n readonly gameVersions: readonly GameVersion[];\n readonly timingFlags: readonly TimingFlag[];\n readonly interactionFlags: readonly InteractionFlag[];\n readonly phaseMappings: readonly RawData[\"phaseMappings\"][number][];\n\n /** `source_type:source_id` → unioned phases. */\n private readonly phaseIndex = new Map<string, Phase[]>();\n /** ability id → units that list it. */\n private readonly unitsByAbility = new Map<string, Unit[]>();\n /** weapon id → units that list it. */\n private readonly unitsByWeapon = new Map<string, Unit[]>();\n /** weapon-keyword id → weapons whose profiles reference it. */\n private readonly weaponsByKeyword = new Map<string, RawData[\"weapons\"][number][]>();\n /** unit id → wargear options authored for it (declared order preserved). */\n private readonly wargearOptionsByUnit = new Map<string, WargearOption[]>();\n\n constructor(raw: RawData = emptyRawData()) {\n this.units = new Collection({\n items: raw.units,\n idOf: (u) => u.id,\n // The same unit id is shared across factions (e.g. ministorum-priest);\n // keep each faction's copy, collapse only true within-faction duplicates.\n dedupeKeyOf: (u) => `${u.faction_id}::${u.id}`,\n nameOf: (u) => u.name,\n factionOf: (u) => u.faction_id,\n wrap: (u) => new UnitView(u, this),\n });\n this.weapons = new Collection({\n items: raw.weapons,\n idOf: (w) => w.id,\n nameOf: (w) => w.name,\n wrap: (w) => new WeaponView(w, this),\n });\n this.weaponKeywords = new Collection({\n items: raw.weaponKeywords,\n idOf: (k) => k.id,\n nameOf: (k) => k.name,\n wrap: (k) => new WeaponKeywordView(k, this),\n });\n this.factions = new Collection({\n items: raw.factions,\n idOf: (f) => f.id,\n nameOf: (f) => f.name,\n wrap: (f) => new FactionView(f, this),\n });\n this.abilities = new Collection({\n items: raw.abilities,\n idOf: (a) => a.ability_id,\n nameOf: (a) => a.name,\n factionOf: (a) => a.faction_id,\n wrap: (a) => new AbilityView(a, this),\n });\n\n this.targetProfiles = idCollection(raw.targetProfiles, (p) => p.faction_id);\n this.detachments = idCollection(raw.detachments, (d) => d.faction_id);\n this.enhancements = idCollection(raw.enhancements);\n this.stratagems = idCollection(raw.stratagems);\n this.wargearOptions = idCollection(raw.wargearOptions);\n this.wargear = idCollection(raw.wargear);\n this.missions = idCollection(raw.missions);\n this.missionMatchups = idCollection(raw.missionMatchups);\n this.missionCards = idCollection(raw.missionCards);\n this.deploymentPatterns = idCollection(raw.deploymentPatterns);\n this.forceDispositions = idCollection(raw.forceDispositions);\n this.terrainTemplates = idCollection(raw.terrainTemplates);\n this.terrainLayouts = idCollection(raw.terrainLayouts);\n this.resourcePools = idCollection(raw.resourcePools);\n\n this.leaderAttachments = raw.leaderAttachments;\n this.unitCompositions = raw.unitCompositions;\n this.gameVersions = raw.gameVersions;\n this.timingFlags = raw.timingFlags;\n this.interactionFlags = raw.interactionFlags;\n this.phaseMappings = raw.phaseMappings;\n\n this.buildIndexes(raw);\n }\n\n /** The dataset built from the package's embedded data. */\n static embedded(): Dataset {\n return new Dataset(RAW_DATA);\n }\n\n /** Phases a source acts in, unioned across its phase-mappings. */\n phasesFor(sourceType: string, sourceId: string): Phase[] {\n return this.phaseIndex.get(`${sourceType}:${sourceId}`) ?? [];\n }\n\n /**\n * Resolve a terrain layout to absolute board-space vertices using this\n * dataset's embedded terrain-template catalog — the layout-id →\n * renderable-geometry hop. Mirror of Rust `Dataset::resolve_terrain`; the\n * geometry is pinned by the `terrain-resolver` conformance corpus.\n */\n resolveTerrain(layout: TerrainLayout): ResolvedPiece[] {\n // The resolver takes its own structurally-identical input types, decoupled\n // from the generated `anyOf`/newtype shapes; the underlying JSON is the same.\n return resolveLayout(layout as never, this.terrainTemplates.all as never);\n }\n\n /**\n * The terrain layouts a deployment pattern recommends, in declared order,\n * skipping any ids absent from the dataset.\n */\n recommendedTerrainLayouts(pattern: DeploymentPattern): TerrainLayout[] {\n return (pattern.recommended_terrain_layout_ids ?? [])\n .map((id) => this.terrainLayouts.get(id))\n .filter((l): l is TerrainLayout => l !== undefined);\n }\n\n /** Units that list the given ability id. */\n unitsWithAbility(abilityId: string): UnitView[] {\n return (this.unitsByAbility.get(abilityId) ?? []).map((u) => new UnitView(u, this));\n }\n\n /** Units that list the given weapon id. */\n unitsWithWeapon(weaponId: string): UnitView[] {\n return (this.unitsByWeapon.get(weaponId) ?? []).map((u) => new UnitView(u, this));\n }\n\n /** Weapons whose profiles reference the given weapon-keyword id. */\n weaponsWithKeyword(keywordId: string): WeaponView[] {\n return (this.weaponsByKeyword.get(keywordId) ?? []).map((w) => new WeaponView(w, this));\n }\n\n /**\n * Wargear options authored for the given unit, in declared order. Mirror of\n * Rust `Dataset::wargear_options_of`. Empty for a unit with no options.\n */\n wargearOptionsOf(unit: Unit): WargearOption[] {\n return this.wargearOptionsByUnit.get(unit.id) ?? [];\n }\n\n /**\n * Leaders whose leader-attachment data lists `bodyguardUnitId` among its\n * eligible body units, sorted by name. The attachment is stored on the\n * leader pointing down to its bodyguards, so answering \"which leaders can\n * attach to this unit?\" means scanning the attachment list. Returns an empty\n * array for a unit that no leader can attach to (including leader units).\n */\n leadersAttachableTo(bodyguardUnitId: string): UnitView[] {\n return this.leaderAttachments\n .filter((la) => la.eligible_bodyguard_ids.includes(bodyguardUnitId))\n .map((la) => this.units.get(la.leader_id))\n .filter((u): u is UnitView => u !== undefined)\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * The inverse of {@link leadersAttachableTo}: the body units the given\n * leader can attach to, sorted by name. Scans the same leader-attachment\n * data from the leader's side (`leader_id` matches; resolve each\n * `eligible_bodyguard_ids` entry), deduped by id. Empty for a non-leader\n * unit. Together the two queries give the bidirectional attachment graph the\n * SPA needs to offer a partner dropdown from either end.\n */\n bodyguardsAttachableFrom(leaderUnitId: string): UnitView[] {\n const seen = new Set<string>();\n const out: UnitView[] = [];\n for (const la of this.leaderAttachments) {\n if (la.leader_id !== leaderUnitId) continue;\n for (const bodyguardId of la.eligible_bodyguard_ids) {\n if (seen.has(bodyguardId)) continue;\n const unit = this.units.get(bodyguardId);\n if (!unit) continue;\n seen.add(bodyguardId);\n out.push(unit);\n }\n }\n return out.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Enumerate every ability that could apply to the given unit in `phase`,\n * grouped by source. The SPA uses this to render the abilities pane.\n */\n eligibleAbilities(input: EligibilityInput, phase: Phase): EligibleAbility[] {\n return resolveEligibleAbilities(this, input, phase);\n }\n\n /**\n * Attacker-perspective {@link Buff} stack for a (unit, phase) combination:\n * intrinsic weapon-profile keywords plus every eligible ability whose DSL\n * effect translates to an attacker-side buff (army, detachment, unit,\n * attached members, support, plus any stratagems the caller has opted into).\n *\n * The result includes only buffs the buff layer can express today — the\n * `unsupported` half of the DSL→Buff translation is dropped here so callers\n * who just want the stack don't need to thread diagnostics through. Use\n * {@link AbilityView.describeBuffs} when you need the diagnostics for an\n * individual ability. Symmetric to {@link defensiveBuffsFor}, which walks\n * the same eligibility set under target perspective.\n */\n buffsFor(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n /** Stratagem ids the caller has opted into spending CP on. */\n optedInStratagemIds?: string[];\n },\n context: EngineContext,\n ): Buff[] {\n return this.collectBuffs(input, context, \"attacker\");\n }\n\n /**\n * Defender-perspective buff stack for the chosen unit: walks the same\n * eligible-abilities set as {@link buffsFor} but translates each ability's\n * DSL effect as defensive (FNP, save mods from `stat-modifier Sv`,\n * toughness mods from `stat-modifier T`, save rerolls, incoming hit\n * penalties from `bs-modifier`). Use this when the chosen unit is being\n * crunched as the *target* — the engine reads `feelNoPain`/`saveMod`/\n * `toughnessMod` out of `resolveBuffs` so wiring the result into `crunch`\n * just means concatenating onto the existing `buffs` array.\n *\n * `weaponProfiles` are ignored under target perspective — weapon-keyword\n * effects ride with the firing weapon, not the receiving unit.\n */\n defensiveBuffsFor(\n input: EligibilityInput & { optedInStratagemIds?: string[] },\n context: EngineContext,\n ): Buff[] {\n return this.collectBuffs(input, context, \"target\");\n }\n\n /**\n * Enumerate every attacker-side buff a unit could stack in `context` as a\n * list of toggleable levers, plus the activation groups that limit them.\n *\n * Unlike {@link buffsFor} — which returns only the buffs that auto-apply —\n * this surfaces the *player decisions* too: stratagems, and the activatable\n * gates the DSL models as dice-pool options, `choice` branches, or\n * timing-gated activations (e.g. Blessings of Khorne's three keyword grants).\n * Each lever carries `enabled` (its default state) and, where it's part of a\n * limited pool, a `group` id whose {@link StackableBuffGroup} caps how many\n * can fire at once. The intended loop:\n *\n * ```ts\n * const { buffs } = ds.stackableBuffsFor(input, ctx);\n * const chosen = buffs.filter(b => b.enabled).flatMap(b => b.buffs);\n * crunch({ ...profiles, buffs: chosen, context: ctx }, ds);\n * ```\n *\n * Target/phase conditions a lever still carries (e.g. \"vs Infantry\") ride on\n * each buff's `applicableWhen`, so toggling it on is always safe — the\n * resolver gates it per-target.\n */\n stackableBuffsFor(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n },\n context: EngineContext,\n ): { buffs: StackableBuff[]; groups: StackableBuffGroup[] } {\n const buffs: StackableBuff[] = [];\n const groups = new Map<string, StackableBuffGroup>();\n\n // Surface the attachment fact to the DSL translator so `is-attached` /\n // `model-is-leader` conditions can evaluate. Clone — never mutate the\n // caller's context. An explicitly-set flag wins over the derivation.\n const ctx: EngineContext = {\n ...context,\n attackerAttached: context.attackerAttached ?? (input.attachedUnitIds?.length ?? 0) > 0,\n };\n\n // Intrinsic weapon-profile keywords — always on.\n for (const ref of input.weaponProfiles ?? []) {\n const weapon = this.weapons.get(ref.weaponId);\n if (!weapon) continue;\n const wk = weapon.profileBuffs(ref.profileIndex, ctx);\n if (wk.length === 0) continue;\n buffs.push({\n id: `weapon:${ref.weaponId}:${ref.profileIndex}`,\n label: `${weapon.name} keywords`,\n buffs: wk,\n enabled: true,\n source: wk[0].source,\n });\n }\n\n for (const entry of this.eligibleAbilities(input, ctx.phase)) {\n const source = bufferSourceFromEligible(entry);\n const { applied, activatable } = entry.ability.describeBuffs(source, ctx, \"attacker\");\n // Stratagems cost CP — opt-in, not on by default.\n const isStratagem = entry.source.kind === \"detachment-stratagem\";\n\n if (applied.length > 0) {\n buffs.push({\n id: `${entry.source.kind}:${entry.ability.id}`,\n label: entry.ability.name,\n buffs: applied,\n enabled: !isStratagem,\n source,\n });\n }\n\n for (const act of activatable) {\n let groupId: string | undefined;\n if (act.group) {\n groupId = act.group.id;\n if (!groups.has(groupId)) {\n groups.set(groupId, {\n id: groupId,\n label: entry.ability.name,\n maxActivations: act.group.maxActivations,\n });\n }\n }\n buffs.push({\n id: act.id,\n label: `${entry.ability.name} — ${act.label}`,\n buffs: act.buffs,\n enabled: false,\n source,\n group: groupId,\n });\n }\n }\n\n return { buffs, groups: [...groups.values()] };\n }\n\n /** Shared implementation for buffsFor / defensiveBuffsFor. */\n private collectBuffs(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n optedInStratagemIds?: string[];\n },\n context: EngineContext,\n perspective: \"attacker\" | \"target\",\n ): Buff[] {\n const out: Buff[] = [];\n\n // Surface the attachment fact to the DSL translator (see stackableBuffsFor).\n // Clone — never mutate the caller's context; explicit flag wins.\n const ctx: EngineContext = {\n ...context,\n attackerAttached: context.attackerAttached ?? (input.attachedUnitIds?.length ?? 0) > 0,\n };\n\n // Weapon-profile keywords are attacker-only.\n if (perspective === \"attacker\") {\n for (const ref of input.weaponProfiles ?? []) {\n const weapon = this.weapons.get(ref.weaponId);\n if (!weapon) continue;\n out.push(...weapon.profileBuffs(ref.profileIndex, ctx));\n }\n }\n\n const optedIn = new Set(input.optedInStratagemIds ?? []);\n for (const entry of this.eligibleAbilities(input, ctx.phase)) {\n if (entry.source.kind === \"detachment-stratagem\" && !optedIn.has(entry.source.stratagemId)) {\n continue;\n }\n const source = bufferSourceFromEligible(entry);\n out.push(...entry.ability.getBuffs(source, ctx, perspective));\n }\n\n return out;\n }\n\n private buildIndexes(raw: RawData): void {\n for (const pm of raw.phaseMappings) {\n const key = `${pm.source_type}:${pm.source_id}`;\n const existing = this.phaseIndex.get(key) ?? [];\n for (const phase of pm.phases) {\n if (!existing.includes(phase)) existing.push(phase);\n }\n this.phaseIndex.set(key, existing);\n }\n for (const unit of raw.units) {\n for (const abilityId of unit.ability_ids ?? []) push(this.unitsByAbility, abilityId, unit);\n for (const weaponId of unit.weapon_ids ?? []) push(this.unitsByWeapon, weaponId, unit);\n }\n for (const option of raw.wargearOptions) {\n push(this.wargearOptionsByUnit, option.unit_id, option);\n }\n const seenByKeyword = new Map<string, Set<string>>();\n for (const weapon of raw.weapons) {\n for (const profile of weapon.profiles) {\n for (const ref of profile.keywords ?? []) {\n let seen = seenByKeyword.get(ref.keyword_id);\n if (!seen) {\n seen = new Set();\n seenByKeyword.set(ref.keyword_id, seen);\n }\n if (seen.has(weapon.id)) continue;\n seen.add(weapon.id);\n push(this.weaponsByKeyword, ref.keyword_id, weapon);\n }\n }\n }\n }\n}\n\n/** Build a passthrough collection for an id-bearing record type. */\nfunction idCollection<T extends { id: string }>(\n items: T[],\n factionOf?: (item: T) => string | null | undefined,\n): Collection<T, T> {\n return new Collection<T, T>({\n items,\n idOf: (i) => i.id,\n nameOf: (i) => (i as { name?: string }).name,\n factionOf,\n wrap: (i) => i,\n });\n}\n\nfunction push<T>(map: Map<string, T[]>, key: string, value: T): void {\n const existing = map.get(key);\n if (existing) existing.push(value);\n else map.set(key, [value]);\n}\n\n/** Map an EligibleAbility back to the BuffSource the translator expects. */\nfunction bufferSourceFromEligible(entry: EligibleAbility): BuffSource {\n const abilityId = entry.ability.id;\n switch (entry.source.kind) {\n case \"army\":\n return { kind: \"ability\", abilityId, abilityKind: \"army\" };\n case \"detachment\":\n return { kind: \"ability\", abilityId, abilityKind: \"detachment\" };\n case \"detachment-stratagem\":\n return { kind: \"ability\", abilityId, abilityKind: \"detachment-stratagem\" };\n case \"unit\":\n return { kind: \"ability\", abilityId, abilityKind: \"unit\" };\n case \"attached\":\n return {\n kind: \"ability\",\n abilityId,\n abilityKind: \"attached\",\n sourceUnitId: entry.source.unitId,\n };\n case \"support\":\n return { kind: \"ability\", abilityId, abilityKind: \"support\" };\n }\n}\n"]}
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/data/dataset.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * {@link Dataset} ties the embedded records together: it owns every\n * {@link Collection}, builds the cross-entity indexes once, and is the `this`\n * the linked views resolve against.\n *\n * @packageDocumentation\n */\nimport type {\n DeploymentPattern,\n Detachment,\n Enhancement,\n ForceDisposition,\n GameVersion,\n HullShape,\n InteractionFlag,\n LeaderAttachment,\n Mission,\n MissionMatchup,\n Phase,\n ResourcePool,\n SecondaryCard,\n Stratagem,\n TargetProfile,\n TerrainLayout,\n TerrainTemplate,\n TimingFlag,\n Unit,\n UnitComposition,\n Wargear,\n WargearOption,\n WeaponKeyword,\n} from \"../generated.js\";\nimport { Collection } from \"./collection.js\";\nimport {\n AbilityView,\n FactionView,\n UnitView,\n WeaponKeywordView,\n WeaponView,\n} from \"./entities.js\";\nimport { emptyRawData, type RawData } from \"./types.js\";\nimport { RAW_DATA } from \"./bundle.generated.js\";\nimport { resolveLayout, type ResolvedPiece } from \"../terrain/resolve.js\";\nimport type { Buff, BuffSource, EngineContext } from \"../cruncher/buffs.js\";\nimport {\n resolveEligibleAbilities,\n type EligibilityInput,\n type EligibleAbility,\n} from \"../abilities-resolver/index.js\";\n\n/**\n * One toggleable buff lever for damage analysis: the contributions it adds and\n * whether it's on by default. `enabled` is `true` for buffs that always apply\n * (intrinsic keywords, unconditional abilities) and `false` for player\n * decisions — stratagems (CP cost) and activatable gates (dice-pool options,\n * `choice` branches, timing-gated activations). A consumer flips `enabled`,\n * then crunches the enabled subset; an optimizer searches it.\n *\n * @see {@link Dataset.stackableBuffsFor}\n */\nexport type StackableBuff = {\n /** Stable toggle id (stable across re-enumeration of the same input). */\n id: string;\n /** Human label for the lever. */\n label: string;\n /** Contributions this lever adds when enabled (≥1). */\n buffs: Buff[];\n /** Default selection state. */\n enabled: boolean;\n /** Where the lever came from. */\n source: BuffSource;\n /** Id of the mutually-limited {@link StackableBuffGroup} this belongs to, if any. */\n group?: string;\n};\n\n/** A pool of {@link StackableBuff} levers limited to `maxActivations` at once. */\nexport type StackableBuffGroup = {\n id: string;\n label: string;\n maxActivations: number;\n};\n\n/** The whole dataset, with linked accessors over every entity collection. */\nexport class Dataset {\n // Richly-linked collections.\n readonly units: Collection<Unit, UnitView>;\n readonly weapons: Collection<RawData[\"weapons\"][number], WeaponView>;\n readonly weaponKeywords: Collection<WeaponKeyword, WeaponKeywordView>;\n readonly factions: Collection<RawData[\"factions\"][number], FactionView>;\n readonly abilities: Collection<RawData[\"abilities\"][number], AbilityView>;\n\n // Id-bearing collections without bespoke views (records returned as-is).\n readonly targetProfiles: Collection<TargetProfile, TargetProfile>;\n readonly detachments: Collection<Detachment, Detachment>;\n readonly enhancements: Collection<Enhancement, Enhancement>;\n readonly stratagems: Collection<Stratagem, Stratagem>;\n readonly wargearOptions: Collection<WargearOption, WargearOption>;\n readonly wargear: Collection<Wargear, Wargear>;\n readonly missions: Collection<Mission, Mission>;\n readonly missionMatchups: Collection<MissionMatchup, MissionMatchup>;\n readonly missionCards: Collection<SecondaryCard, SecondaryCard>;\n readonly deploymentPatterns: Collection<DeploymentPattern, DeploymentPattern>;\n readonly forceDispositions: Collection<ForceDisposition, ForceDisposition>;\n readonly terrainTemplates: Collection<TerrainTemplate, TerrainTemplate>;\n readonly terrainLayouts: Collection<TerrainLayout, TerrainLayout>;\n readonly hullShapes: Collection<HullShape, HullShape>;\n readonly resourcePools: Collection<ResourcePool, ResourcePool>;\n\n // Id-less collections, exposed as plain arrays.\n readonly leaderAttachments: readonly LeaderAttachment[];\n readonly unitCompositions: readonly UnitComposition[];\n readonly gameVersions: readonly GameVersion[];\n readonly timingFlags: readonly TimingFlag[];\n readonly interactionFlags: readonly InteractionFlag[];\n readonly phaseMappings: readonly RawData[\"phaseMappings\"][number][];\n\n /** `source_type:source_id` → unioned phases. */\n private readonly phaseIndex = new Map<string, Phase[]>();\n /** ability id → units that list it. */\n private readonly unitsByAbility = new Map<string, Unit[]>();\n /** weapon id → units that list it. */\n private readonly unitsByWeapon = new Map<string, Unit[]>();\n /** weapon-keyword id → weapons whose profiles reference it. */\n private readonly weaponsByKeyword = new Map<string, RawData[\"weapons\"][number][]>();\n /** unit id → wargear options authored for it (declared order preserved). */\n private readonly wargearOptionsByUnit = new Map<string, WargearOption[]>();\n\n constructor(raw: RawData = emptyRawData()) {\n this.units = new Collection({\n items: raw.units,\n idOf: (u) => u.id,\n // The same unit id is shared across factions (e.g. ministorum-priest);\n // keep each faction's copy, collapse only true within-faction duplicates.\n dedupeKeyOf: (u) => `${u.faction_id}::${u.id}`,\n nameOf: (u) => u.name,\n factionOf: (u) => u.faction_id,\n wrap: (u) => new UnitView(u, this),\n });\n this.weapons = new Collection({\n items: raw.weapons,\n idOf: (w) => w.id,\n nameOf: (w) => w.name,\n wrap: (w) => new WeaponView(w, this),\n });\n this.weaponKeywords = new Collection({\n items: raw.weaponKeywords,\n idOf: (k) => k.id,\n nameOf: (k) => k.name,\n wrap: (k) => new WeaponKeywordView(k, this),\n });\n this.factions = new Collection({\n items: raw.factions,\n idOf: (f) => f.id,\n nameOf: (f) => f.name,\n wrap: (f) => new FactionView(f, this),\n });\n this.abilities = new Collection({\n items: raw.abilities,\n idOf: (a) => a.ability_id,\n nameOf: (a) => a.name,\n factionOf: (a) => a.faction_id,\n wrap: (a) => new AbilityView(a, this),\n });\n\n this.targetProfiles = idCollection(raw.targetProfiles, (p) => p.faction_id);\n this.detachments = idCollection(raw.detachments, (d) => d.faction_id);\n this.enhancements = idCollection(raw.enhancements);\n this.stratagems = idCollection(raw.stratagems);\n this.wargearOptions = idCollection(raw.wargearOptions);\n this.wargear = idCollection(raw.wargear);\n this.missions = idCollection(raw.missions);\n this.missionMatchups = idCollection(raw.missionMatchups);\n this.missionCards = idCollection(raw.missionCards);\n this.deploymentPatterns = idCollection(raw.deploymentPatterns);\n this.forceDispositions = idCollection(raw.forceDispositions);\n this.terrainTemplates = idCollection(raw.terrainTemplates);\n this.terrainLayouts = idCollection(raw.terrainLayouts);\n this.hullShapes = idCollection(raw.hullShapes);\n this.resourcePools = idCollection(raw.resourcePools);\n\n this.leaderAttachments = raw.leaderAttachments;\n this.unitCompositions = raw.unitCompositions;\n this.gameVersions = raw.gameVersions;\n this.timingFlags = raw.timingFlags;\n this.interactionFlags = raw.interactionFlags;\n this.phaseMappings = raw.phaseMappings;\n\n this.buildIndexes(raw);\n }\n\n /** The dataset built from the package's embedded data. */\n static embedded(): Dataset {\n return new Dataset(RAW_DATA);\n }\n\n /** Phases a source acts in, unioned across its phase-mappings. */\n phasesFor(sourceType: string, sourceId: string): Phase[] {\n return this.phaseIndex.get(`${sourceType}:${sourceId}`) ?? [];\n }\n\n /**\n * Resolve a terrain layout to absolute board-space vertices using this\n * dataset's embedded terrain-template catalog — the layout-id →\n * renderable-geometry hop. Mirror of Rust `Dataset::resolve_terrain`; the\n * geometry is pinned by the `terrain-resolver` conformance corpus.\n */\n resolveTerrain(layout: TerrainLayout): ResolvedPiece[] {\n // The resolver takes its own structurally-identical input types, decoupled\n // from the generated `anyOf`/newtype shapes; the underlying JSON is the same.\n return resolveLayout(layout as never, this.terrainTemplates.all as never);\n }\n\n /**\n * The terrain layouts a deployment pattern recommends, in declared order,\n * skipping any ids absent from the dataset.\n */\n recommendedTerrainLayouts(pattern: DeploymentPattern): TerrainLayout[] {\n return (pattern.recommended_terrain_layout_ids ?? [])\n .map((id) => this.terrainLayouts.get(id))\n .filter((l): l is TerrainLayout => l !== undefined);\n }\n\n /** Units that list the given ability id. */\n unitsWithAbility(abilityId: string): UnitView[] {\n return (this.unitsByAbility.get(abilityId) ?? []).map((u) => new UnitView(u, this));\n }\n\n /** Units that list the given weapon id. */\n unitsWithWeapon(weaponId: string): UnitView[] {\n return (this.unitsByWeapon.get(weaponId) ?? []).map((u) => new UnitView(u, this));\n }\n\n /** Weapons whose profiles reference the given weapon-keyword id. */\n weaponsWithKeyword(keywordId: string): WeaponView[] {\n return (this.weaponsByKeyword.get(keywordId) ?? []).map((w) => new WeaponView(w, this));\n }\n\n /**\n * Wargear options authored for the given unit, in declared order. Mirror of\n * Rust `Dataset::wargear_options_of`. Empty for a unit with no options.\n */\n wargearOptionsOf(unit: Unit): WargearOption[] {\n return this.wargearOptionsByUnit.get(unit.id) ?? [];\n }\n\n /**\n * Leaders whose leader-attachment data lists `bodyguardUnitId` among its\n * eligible body units, sorted by name. The attachment is stored on the\n * leader pointing down to its bodyguards, so answering \"which leaders can\n * attach to this unit?\" means scanning the attachment list. Returns an empty\n * array for a unit that no leader can attach to (including leader units).\n */\n leadersAttachableTo(bodyguardUnitId: string): UnitView[] {\n return this.leaderAttachments\n .filter((la) => la.eligible_bodyguard_ids.includes(bodyguardUnitId))\n .map((la) => this.units.get(la.leader_id))\n .filter((u): u is UnitView => u !== undefined)\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * The inverse of {@link leadersAttachableTo}: the body units the given\n * leader can attach to, sorted by name. Scans the same leader-attachment\n * data from the leader's side (`leader_id` matches; resolve each\n * `eligible_bodyguard_ids` entry), deduped by id. Empty for a non-leader\n * unit. Together the two queries give the bidirectional attachment graph the\n * SPA needs to offer a partner dropdown from either end.\n */\n bodyguardsAttachableFrom(leaderUnitId: string): UnitView[] {\n const seen = new Set<string>();\n const out: UnitView[] = [];\n for (const la of this.leaderAttachments) {\n if (la.leader_id !== leaderUnitId) continue;\n for (const bodyguardId of la.eligible_bodyguard_ids) {\n if (seen.has(bodyguardId)) continue;\n const unit = this.units.get(bodyguardId);\n if (!unit) continue;\n seen.add(bodyguardId);\n out.push(unit);\n }\n }\n return out.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Enumerate every ability that could apply to the given unit in `phase`,\n * grouped by source. The SPA uses this to render the abilities pane.\n */\n eligibleAbilities(input: EligibilityInput, phase: Phase): EligibleAbility[] {\n return resolveEligibleAbilities(this, input, phase);\n }\n\n /**\n * Attacker-perspective {@link Buff} stack for a (unit, phase) combination:\n * intrinsic weapon-profile keywords plus every eligible ability whose DSL\n * effect translates to an attacker-side buff (army, detachment, unit,\n * attached members, support, plus any stratagems the caller has opted into).\n *\n * The result includes only buffs the buff layer can express today — the\n * `unsupported` half of the DSL→Buff translation is dropped here so callers\n * who just want the stack don't need to thread diagnostics through. Use\n * {@link AbilityView.describeBuffs} when you need the diagnostics for an\n * individual ability. Symmetric to {@link defensiveBuffsFor}, which walks\n * the same eligibility set under target perspective.\n */\n buffsFor(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n /** Stratagem ids the caller has opted into spending CP on. */\n optedInStratagemIds?: string[];\n },\n context: EngineContext,\n ): Buff[] {\n return this.collectBuffs(input, context, \"attacker\");\n }\n\n /**\n * Defender-perspective buff stack for the chosen unit: walks the same\n * eligible-abilities set as {@link buffsFor} but translates each ability's\n * DSL effect as defensive (FNP, save mods from `stat-modifier Sv`,\n * toughness mods from `stat-modifier T`, save rerolls, incoming hit\n * penalties from `bs-modifier`). Use this when the chosen unit is being\n * crunched as the *target* — the engine reads `feelNoPain`/`saveMod`/\n * `toughnessMod` out of `resolveBuffs` so wiring the result into `crunch`\n * just means concatenating onto the existing `buffs` array.\n *\n * `weaponProfiles` are ignored under target perspective — weapon-keyword\n * effects ride with the firing weapon, not the receiving unit.\n */\n defensiveBuffsFor(\n input: EligibilityInput & { optedInStratagemIds?: string[] },\n context: EngineContext,\n ): Buff[] {\n return this.collectBuffs(input, context, \"target\");\n }\n\n /**\n * Enumerate every attacker-side buff a unit could stack in `context` as a\n * list of toggleable levers, plus the activation groups that limit them.\n *\n * Unlike {@link buffsFor} — which returns only the buffs that auto-apply —\n * this surfaces the *player decisions* too: stratagems, and the activatable\n * gates the DSL models as dice-pool options, `choice` branches, or\n * timing-gated activations (e.g. Blessings of Khorne's three keyword grants).\n * Each lever carries `enabled` (its default state) and, where it's part of a\n * limited pool, a `group` id whose {@link StackableBuffGroup} caps how many\n * can fire at once. The intended loop:\n *\n * ```ts\n * const { buffs } = ds.stackableBuffsFor(input, ctx);\n * const chosen = buffs.filter(b => b.enabled).flatMap(b => b.buffs);\n * crunch({ ...profiles, buffs: chosen, context: ctx }, ds);\n * ```\n *\n * Target/phase conditions a lever still carries (e.g. \"vs Infantry\") ride on\n * each buff's `applicableWhen`, so toggling it on is always safe — the\n * resolver gates it per-target.\n */\n stackableBuffsFor(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n },\n context: EngineContext,\n ): { buffs: StackableBuff[]; groups: StackableBuffGroup[] } {\n const buffs: StackableBuff[] = [];\n const groups = new Map<string, StackableBuffGroup>();\n\n // Surface the attachment fact to the DSL translator so `is-attached` /\n // `model-is-leader` conditions can evaluate. Clone — never mutate the\n // caller's context. An explicitly-set flag wins over the derivation.\n const ctx: EngineContext = {\n ...context,\n attackerAttached: context.attackerAttached ?? (input.attachedUnitIds?.length ?? 0) > 0,\n };\n\n // Intrinsic weapon-profile keywords — always on.\n for (const ref of input.weaponProfiles ?? []) {\n const weapon = this.weapons.get(ref.weaponId);\n if (!weapon) continue;\n const wk = weapon.profileBuffs(ref.profileIndex, ctx);\n if (wk.length === 0) continue;\n buffs.push({\n id: `weapon:${ref.weaponId}:${ref.profileIndex}`,\n label: `${weapon.name} keywords`,\n buffs: wk,\n enabled: true,\n source: wk[0].source,\n });\n }\n\n for (const entry of this.eligibleAbilities(input, ctx.phase)) {\n const source = bufferSourceFromEligible(entry);\n const { applied, activatable } = entry.ability.describeBuffs(source, ctx, \"attacker\");\n // Stratagems cost CP — opt-in, not on by default.\n const isStratagem = entry.source.kind === \"detachment-stratagem\";\n\n if (applied.length > 0) {\n buffs.push({\n id: `${entry.source.kind}:${entry.ability.id}`,\n label: entry.ability.name,\n buffs: applied,\n enabled: !isStratagem,\n source,\n });\n }\n\n for (const act of activatable) {\n let groupId: string | undefined;\n if (act.group) {\n groupId = act.group.id;\n if (!groups.has(groupId)) {\n groups.set(groupId, {\n id: groupId,\n label: entry.ability.name,\n maxActivations: act.group.maxActivations,\n });\n }\n }\n buffs.push({\n id: act.id,\n label: `${entry.ability.name} — ${act.label}`,\n buffs: act.buffs,\n enabled: false,\n source,\n group: groupId,\n });\n }\n }\n\n return { buffs, groups: [...groups.values()] };\n }\n\n /** Shared implementation for buffsFor / defensiveBuffsFor. */\n private collectBuffs(\n input: EligibilityInput & {\n weaponProfiles?: { weaponId: string; profileIndex: number }[];\n optedInStratagemIds?: string[];\n },\n context: EngineContext,\n perspective: \"attacker\" | \"target\",\n ): Buff[] {\n const out: Buff[] = [];\n\n // Surface the attachment fact to the DSL translator (see stackableBuffsFor).\n // Clone — never mutate the caller's context; explicit flag wins.\n const ctx: EngineContext = {\n ...context,\n attackerAttached: context.attackerAttached ?? (input.attachedUnitIds?.length ?? 0) > 0,\n };\n\n // Weapon-profile keywords are attacker-only.\n if (perspective === \"attacker\") {\n for (const ref of input.weaponProfiles ?? []) {\n const weapon = this.weapons.get(ref.weaponId);\n if (!weapon) continue;\n out.push(...weapon.profileBuffs(ref.profileIndex, ctx));\n }\n }\n\n const optedIn = new Set(input.optedInStratagemIds ?? []);\n for (const entry of this.eligibleAbilities(input, ctx.phase)) {\n if (entry.source.kind === \"detachment-stratagem\" && !optedIn.has(entry.source.stratagemId)) {\n continue;\n }\n const source = bufferSourceFromEligible(entry);\n out.push(...entry.ability.getBuffs(source, ctx, perspective));\n }\n\n return out;\n }\n\n private buildIndexes(raw: RawData): void {\n for (const pm of raw.phaseMappings) {\n const key = `${pm.source_type}:${pm.source_id}`;\n const existing = this.phaseIndex.get(key) ?? [];\n for (const phase of pm.phases) {\n if (!existing.includes(phase)) existing.push(phase);\n }\n this.phaseIndex.set(key, existing);\n }\n for (const unit of raw.units) {\n for (const abilityId of unit.ability_ids ?? []) push(this.unitsByAbility, abilityId, unit);\n for (const weaponId of unit.weapon_ids ?? []) push(this.unitsByWeapon, weaponId, unit);\n }\n for (const option of raw.wargearOptions) {\n push(this.wargearOptionsByUnit, option.unit_id, option);\n }\n const seenByKeyword = new Map<string, Set<string>>();\n for (const weapon of raw.weapons) {\n for (const profile of weapon.profiles) {\n for (const ref of profile.keywords ?? []) {\n let seen = seenByKeyword.get(ref.keyword_id);\n if (!seen) {\n seen = new Set();\n seenByKeyword.set(ref.keyword_id, seen);\n }\n if (seen.has(weapon.id)) continue;\n seen.add(weapon.id);\n push(this.weaponsByKeyword, ref.keyword_id, weapon);\n }\n }\n }\n }\n}\n\n/** Build a passthrough collection for an id-bearing record type. */\nfunction idCollection<T extends { id: string }>(\n items: T[],\n factionOf?: (item: T) => string | null | undefined,\n): Collection<T, T> {\n return new Collection<T, T>({\n items,\n idOf: (i) => i.id,\n nameOf: (i) => (i as { name?: string }).name,\n factionOf,\n wrap: (i) => i,\n });\n}\n\nfunction push<T>(map: Map<string, T[]>, key: string, value: T): void {\n const existing = map.get(key);\n if (existing) existing.push(value);\n else map.set(key, [value]);\n}\n\n/** Map an EligibleAbility back to the BuffSource the translator expects. */\nfunction bufferSourceFromEligible(entry: EligibleAbility): BuffSource {\n const abilityId = entry.ability.id;\n switch (entry.source.kind) {\n case \"army\":\n return { kind: \"ability\", abilityId, abilityKind: \"army\" };\n case \"detachment\":\n return { kind: \"ability\", abilityId, abilityKind: \"detachment\" };\n case \"detachment-stratagem\":\n return { kind: \"ability\", abilityId, abilityKind: \"detachment-stratagem\" };\n case \"unit\":\n return { kind: \"ability\", abilityId, abilityKind: \"unit\" };\n case \"attached\":\n return {\n kind: \"ability\",\n abilityId,\n abilityKind: \"attached\",\n sourceUnitId: entry.source.unitId,\n };\n case \"support\":\n return { kind: \"ability\", abilityId, abilityKind: \"support\" };\n }\n}\n"]}
@@ -9,6 +9,7 @@ import type { AbilityDSLEntry, Faction, Phase, Unit, WargearOption, Weapon, Weap
9
9
  import type { Buff, BuffSource, EngineContext } from "../cruncher/buffs.js";
10
10
  import { type EffectTranslation, type TranslationPerspective } from "../cruncher/from-dsl.js";
11
11
  import type { Dataset } from "./dataset.js";
12
+ import { type AbilityAppliesTo } from "../translate/effect.js";
12
13
  /** A unit, linked to its faction, weapons, and abilities. */
13
14
  export declare class UnitView {
14
15
  /** The full generated `Unit` record. */
@@ -64,6 +65,27 @@ export declare class AbilityView {
64
65
  get phases(): Phase[];
65
66
  /** Units that list this ability in their `ability_ids`. */
66
67
  get units(): UnitView[];
68
+ /**
69
+ * The curated `applies_to` keyword filter, or `undefined` when this ability
70
+ * declares no resolvable unit scope. When present, it names which datasheet
71
+ * units the ability benefits — the contract for roster-side highlighting
72
+ * (e.g. a detachment rule that only buffs `POSSESSED` units).
73
+ */
74
+ get appliesTo(): AbilityAppliesTo | undefined;
75
+ /**
76
+ * Whether this ability's `applies_to` scope includes `unit` — true iff the
77
+ * unit carries every `required_keywords` entry and no `excluded_keywords`,
78
+ * across its `keywords` + `faction_keywords`. Always `false` when the ability
79
+ * has no `applies_to` (no resolvable scope → no highlight). Pinned by the
80
+ * `conformance/applies-to` corpus.
81
+ */
82
+ affectsUnit(unit: UnitView): boolean;
83
+ /**
84
+ * The subset of `candidates` (typically a roster's units) this ability's
85
+ * `applies_to` scope benefits, preserving input order. Empty when the ability
86
+ * declares no scope.
87
+ */
88
+ affectedUnits(candidates: UnitView[]): UnitView[];
67
89
  /**
68
90
  * Buff stack this ability contributes against `context`, with provenance
69
91
  * tagged via `source` (the caller knows whether this ability is being read
@@ -1 +1 @@
1
- {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/data/entities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/data/entities.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,KAAK,EACL,IAAI,EACJ,aAAa,EACb,MAAM,EACN,aAAa,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAGhF,6DAA6D;AAC7D,qBAAa,QAAQ;IAEjB,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,IAAI;IAClB,OAAO,CAAC,QAAQ,CAAC,EAAE;;IAFnB,wCAAwC;IAC/B,GAAG,EAAE,IAAI,EACD,EAAE,EAAE,OAAO;IAG9B,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,yEAAyE;IACzE,IAAI,OAAO,IAAI,WAAW,GAAG,SAAS,CAErC;IAED,sEAAsE;IACtE,IAAI,OAAO,IAAI,UAAU,EAAE,CAE1B;IAED,yEAAyE;IACzE,IAAI,SAAS,IAAI,WAAW,EAAE,CAE7B;IAED,+EAA+E;IAC/E,IAAI,cAAc,IAAI,aAAa,EAAE,CAEpC;IAED;;;;OAIG;IACH,SAAS,CAAC,CAAC,SAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;CAS3C;AAED;;;;;;;;GAQG;AACH,qBAAa,WAAW;IAEpB,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,eAAe;IAC7B,OAAO,CAAC,QAAQ,CAAC,EAAE;;IAFnB,yCAAyC;IAChC,GAAG,EAAE,eAAe,EACZ,EAAE,EAAE,OAAO;IAG9B,yDAAyD;IACzD,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAIlB,2EAA2E;IAC3E,IAAI,MAAM,IAAI,KAAK,EAAE,CAEpB;IAED,2DAA2D;IAC3D,IAAI,KAAK,IAAI,QAAQ,EAAE,CAEtB;IAED;;;;;OAKG;IACH,IAAI,SAAS,IAAI,gBAAgB,GAAG,SAAS,CAE5C;IAED;;;;;;OAMG;IACH,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO;IAIpC;;;;OAIG;IACH,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE;IAIjD;;;;;;;;OAQG;IACH,QAAQ,CACN,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,WAAW,GAAE,sBAAmC,GAC/C,IAAI,EAAE;IAIT;;;;OAIG;IACH,aAAa,CACX,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,WAAW,GAAE,sBAAmC,GAC/C,iBAAiB;CAmBrB;AAED,mDAAmD;AACnD,qBAAa,UAAU;IAEnB,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,EAAE;;IAFnB,0CAA0C;IACjC,GAAG,EAAE,MAAM,EACH,EAAE,EAAE,OAAO;IAG9B,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,yDAAyD;IACzD,IAAI,KAAK,IAAI,QAAQ,EAAE,CAEtB;IAED,iDAAiD;IACjD,SAAS,CAAC,CAAC,SAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IAU5C;;;OAGG;IACH,UAAU,CACR,CAAC,SAAI,GACJ;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,EAAE;IAepF;;;;OAIG;IACH,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,EAAE;CAgBpE;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;IAE1B,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,aAAa;IAC3B,OAAO,CAAC,QAAQ,CAAC,EAAE;;IAFnB,iDAAiD;IACxC,GAAG,EAAE,aAAa,EACV,EAAE,EAAE,OAAO;IAG9B,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,wDAAwD;IACxD,IAAI,OAAO,IAAI,UAAU,EAAE,CAE1B;IAED;;;;;OAKG;IACH,QAAQ,CACN,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,GACrB,IAAI,EAAE;CASV;AAED,mEAAmE;AACnE,qBAAa,WAAW;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,EAAE,OAAO;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE;;IAFnB,2CAA2C;IAClC,GAAG,EAAE,OAAO,EACJ,EAAE,EAAE,OAAO;IAG9B,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,8EAA8E;IAC9E,IAAI,KAAK,IAAI,QAAQ,EAAE,CAEtB;IAED,+EAA+E;IAC/E,IAAI,SAAS,IAAI,WAAW,EAAE,CAE7B;IAED,wDAAwD;IACxD,IAAI,OAAO,IAAI,UAAU,EAAE,CAW1B;CACF"}
@@ -1,6 +1,7 @@
1
1
  import { buffsFromKeyword } from "../cruncher/from-keyword.js";
2
2
  import { effectToBuffs, } from "../cruncher/from-dsl.js";
3
3
  import { describeAbility } from "../translate/effect.js";
4
+ import { unitMatchesAppliesTo } from "../scope.js";
4
5
  /** A unit, linked to its faction, weapons, and abilities. */
5
6
  export class UnitView {
6
7
  raw;
@@ -88,6 +89,33 @@ export class AbilityView {
88
89
  get units() {
89
90
  return this.ds.unitsWithAbility(this.raw.ability_id);
90
91
  }
92
+ /**
93
+ * The curated `applies_to` keyword filter, or `undefined` when this ability
94
+ * declares no resolvable unit scope. When present, it names which datasheet
95
+ * units the ability benefits — the contract for roster-side highlighting
96
+ * (e.g. a detachment rule that only buffs `POSSESSED` units).
97
+ */
98
+ get appliesTo() {
99
+ return this.raw.applies_to ?? undefined;
100
+ }
101
+ /**
102
+ * Whether this ability's `applies_to` scope includes `unit` — true iff the
103
+ * unit carries every `required_keywords` entry and no `excluded_keywords`,
104
+ * across its `keywords` + `faction_keywords`. Always `false` when the ability
105
+ * has no `applies_to` (no resolvable scope → no highlight). Pinned by the
106
+ * `conformance/applies-to` corpus.
107
+ */
108
+ affectsUnit(unit) {
109
+ return unitMatchesAppliesTo(this.raw.applies_to, unit.raw);
110
+ }
111
+ /**
112
+ * The subset of `candidates` (typically a roster's units) this ability's
113
+ * `applies_to` scope benefits, preserving input order. Empty when the ability
114
+ * declares no scope.
115
+ */
116
+ affectedUnits(candidates) {
117
+ return candidates.filter((u) => this.affectsUnit(u));
118
+ }
91
119
  /**
92
120
  * Buff stack this ability contributes against `context`, with provenance
93
121
  * tagged via `source` (the caller knows whether this ability is being read
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/data/entities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Linked views over the richly-connected entity types. Each wraps a raw\n * generated record and resolves its relationships lazily against the owning\n * {@link Dataset}; the full underlying record is always available via `.raw`.\n *\n * @packageDocumentation\n */\nimport type {\n AbilityDSLEntry,\n Faction,\n Phase,\n Unit,\n WargearOption,\n Weapon,\n WeaponKeyword,\n} from \"../generated.js\";\nimport type { Buff, BuffSource, EngineContext } from \"../cruncher/buffs.js\";\nimport { buffsFromKeyword } from \"../cruncher/from-keyword.js\";\nimport {\n effectToBuffs,\n type EffectTranslation,\n type TranslationPerspective,\n} from \"../cruncher/from-dsl.js\";\nimport type { Dataset } from \"./dataset.js\";\nimport { describeAbility } from \"../translate/effect.js\";\n\n/** A unit, linked to its faction, weapons, and abilities. */\nexport class UnitView {\n constructor(\n /** The full generated `Unit` record. */\n readonly raw: Unit,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** The unit's faction, or `undefined` if its `faction_id` is unknown. */\n get faction(): FactionView | undefined {\n return this.ds.factions.get(this.raw.faction_id);\n }\n\n /** Weapons referenced by `weapon_ids`; unresolved ids are skipped. */\n get weapons(): WeaponView[] {\n return resolveAll(this.raw.weapon_ids, (id) => this.ds.weapons.get(id));\n }\n\n /** Abilities referenced by `ability_ids`; unresolved ids are skipped. */\n get abilities(): AbilityView[] {\n return resolveAll(this.raw.ability_ids, (id) => this.ds.abilities.get(id));\n }\n\n /** Wargear options (weapon swaps, add-ons, choices) authored for this unit. */\n get wargearOptions(): WargearOption[] {\n return this.ds.wargearOptionsOf(this.raw);\n }\n\n /**\n * The stat profile at index `i` (default 0). Returns the schema-generated\n * profile object directly so callers can feed it straight to the engine\n * without an intermediate wrapper.\n */\n profileAt(i = 0): Unit[\"profiles\"][number] {\n const profile = this.raw.profiles[i];\n if (profile === undefined) {\n throw new RangeError(\n `UnitView(${this.raw.id}).profileAt(${i}): only ${this.raw.profiles.length} profile(s) defined`,\n );\n }\n return profile;\n }\n}\n\n/**\n * An ability, linked to the phases it acts in and the units that have it.\n *\n * Phases are not stored on the ability — they live in `phase-mappings` records.\n *\n * @example\n * units.find(\"Kharn\")!.abilities\n * .filter(a => a.phases.includes(\"shooting\"));\n */\nexport class AbilityView {\n constructor(\n /** The full generated ability record. */\n readonly raw: AbilityDSLEntry,\n private readonly ds: Dataset,\n ) {}\n\n /** The ability's id (`ability_id` in the raw record). */\n get id(): string {\n return this.raw.ability_id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /**\n * Generated plain-English approximation of this ability's effect + scope,\n * rendered from the DSL by the conformance-pinned describer\n * (`translate/effect.ts`). The dataset carries no rules prose; this is the\n * displayable stand-in.\n */\n describe(): string {\n return describeAbility(this.raw);\n }\n\n /** Game phases this ability acts in, unioned across its phase-mappings. */\n get phases(): Phase[] {\n return this.ds.phasesFor(\"ability\", this.raw.ability_id);\n }\n\n /** Units that list this ability in their `ability_ids`. */\n get units(): UnitView[] {\n return this.ds.unitsWithAbility(this.raw.ability_id);\n }\n\n /**\n * Buff stack this ability contributes against `context`, with provenance\n * tagged via `source` (the caller knows whether this ability is being read\n * as army, detachment, unit, leader, etc.). DSL branches the buff layer\n * can't auto-apply are dropped here; call {@link describeBuffs} if you\n * also want the diagnostics. `perspective` defaults to `\"attacker\"`; pass\n * `\"target\"` to translate the ability as a defensive buff (FNP, T/Sv\n * stat-mods, save rerolls, incoming hit penalties).\n */\n getBuffs(\n source: BuffSource,\n context?: EngineContext,\n perspective: TranslationPerspective = \"attacker\",\n ): Buff[] {\n return this.describeBuffs(source, context, perspective).applied;\n }\n\n /**\n * Full DSL→Buff translation, including the `unsupported` list of effect\n * fragments the buff layer can't model. The SPA renders these as warnings\n * so users see which abilities have effects that need a manual toggle.\n */\n describeBuffs(\n source: BuffSource,\n context?: EngineContext,\n perspective: TranslationPerspective = \"attacker\",\n ): EffectTranslation {\n const ctx: EngineContext = context ?? { phase: \"shooting\" };\n const translated = effectToBuffs(this.raw.effect, source, ctx, perspective);\n // A range-scoped ability (DSL `scope.range_inches`, e.g. a \"within 18\\\"\"\n // reroll) gates on distance to the target. Stamp it here rather than in the\n // effect translator so the `effect-translation` corpus (bare effects) is\n // unaffected; the gate is permissive until a caller sets `distanceInches`.\n const range = (this.raw.scope as { range_inches?: number } | undefined)?.range_inches;\n if (typeof range !== \"number\") return translated;\n const gate = (b: Buff): Buff => ({\n ...b,\n applicableWhen: { ...b.applicableWhen, maxRangeInches: range },\n });\n return {\n applied: translated.applied.map(gate),\n unsupported: translated.unsupported,\n activatable: translated.activatable.map((a) => ({ ...a, buffs: a.buffs.map(gate) })),\n };\n }\n}\n\n/** A weapon, linked to the units that carry it. */\nexport class WeaponView {\n constructor(\n /** The full generated `Weapon` record. */\n readonly raw: Weapon,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Units that list this weapon in their `weapon_ids`. */\n get units(): UnitView[] {\n return this.ds.unitsWithWeapon(this.raw.id);\n }\n\n /** The stat profile at index `i` (default 0). */\n profileAt(i = 0): Weapon[\"profiles\"][number] {\n const profile = this.raw.profiles[i];\n if (profile === undefined) {\n throw new RangeError(\n `WeaponView(${this.raw.id}).profileAt(${i}): only ${this.raw.profiles.length} profile(s) defined`,\n );\n }\n return profile;\n }\n\n /**\n * Catalog views for each keyword referenced by profile `i`, paired with the\n * reference-site parameters. Unresolved keyword ids are skipped.\n */\n keywordsAt(\n i = 0,\n ): { keyword: WeaponKeywordView; parameters: Record<string, unknown> | undefined }[] {\n const profile = this.profileAt(i);\n const refs = profile.keywords ?? [];\n const out: { keyword: WeaponKeywordView; parameters: Record<string, unknown> | undefined }[] = [];\n for (const ref of refs) {\n const view = this.ds.weaponKeywords.get(ref.keyword_id);\n if (!view) continue;\n out.push({\n keyword: view,\n parameters: ref.parameters as Record<string, unknown> | undefined,\n });\n }\n return out;\n }\n\n /**\n * Buffs contributed by profile `i`'s intrinsic keywords against `context` —\n * the natural \"what does this profile bring on its own?\" call the engine\n * makes automatically before adding ability/manual buffs.\n */\n profileBuffs(i: number | undefined, context: EngineContext): Buff[] {\n const index = i ?? 0;\n const out: Buff[] = [];\n for (const { keyword, parameters } of this.keywordsAt(index)) {\n out.push(\n ...buffsFromKeyword({\n keywordId: keyword.id,\n weaponId: this.raw.id,\n effect: keyword.raw.effect,\n ...(parameters !== undefined ? { parameters } : {}),\n context,\n }),\n );\n }\n return out;\n }\n}\n\n/**\n * A weapon-keyword catalog entry, linked to the weapons whose profiles\n * reference it. Exposes the keyword's mechanical effect as a buff stack\n * via {@link getBuffs}.\n */\nexport class WeaponKeywordView {\n constructor(\n /** The full generated `WeaponKeyword` record. */\n readonly raw: WeaponKeyword,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Weapons whose profiles reference this keyword id. */\n get weapons(): WeaponView[] {\n return this.ds.weaponsWithKeyword(this.raw.id);\n }\n\n /**\n * Buff contributions from this catalog entry, for one reference site:\n * pass the keyword's `parameters` (e.g. `{ value: 1 }` for Sustained Hits 1)\n * along with the `weaponId` that's carrying it (used as the buff source)\n * and the engine `context` (e.g. attacker stationary?).\n */\n getBuffs(\n parameters: Record<string, unknown> | undefined,\n weaponId: string,\n context: EngineContext,\n ): Buff[] {\n return buffsFromKeyword({\n keywordId: this.raw.id,\n weaponId,\n effect: this.raw.effect,\n ...(parameters !== undefined ? { parameters } : {}),\n context,\n });\n }\n}\n\n/** A faction, linked to its units and the records scoped to it. */\nexport class FactionView {\n constructor(\n /** The full generated `Faction` record. */\n readonly raw: Faction,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Units whose `faction_id` is this faction (may be empty for successors). */\n get units(): UnitView[] {\n return this.ds.units.byFaction(this.raw.id);\n }\n\n /** Faction-scoped abilities (abilities whose `faction_id` is this faction). */\n get abilities(): AbilityView[] {\n return this.ds.abilities.byFaction(this.raw.id);\n }\n\n /** Distinct weapons carried by this faction's units. */\n get weapons(): WeaponView[] {\n const seen = new Set<string>();\n const out: WeaponView[] = [];\n for (const unit of this.units) {\n for (const weapon of unit.weapons) {\n if (seen.has(weapon.id)) continue;\n seen.add(weapon.id);\n out.push(weapon);\n }\n }\n return out;\n }\n}\n\n/** Resolve a list of ids, dropping any that don't resolve. */\nfunction resolveAll<V>(ids: string[] | undefined, get: (id: string) => V | undefined): V[] {\n const out: V[] = [];\n for (const id of ids ?? []) {\n const v = get(id);\n if (v !== undefined) out.push(v);\n }\n return out;\n}\n"]}
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/data/entities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Linked views over the richly-connected entity types. Each wraps a raw\n * generated record and resolves its relationships lazily against the owning\n * {@link Dataset}; the full underlying record is always available via `.raw`.\n *\n * @packageDocumentation\n */\nimport type {\n AbilityDSLEntry,\n Faction,\n Phase,\n Unit,\n WargearOption,\n Weapon,\n WeaponKeyword,\n} from \"../generated.js\";\nimport type { Buff, BuffSource, EngineContext } from \"../cruncher/buffs.js\";\nimport { buffsFromKeyword } from \"../cruncher/from-keyword.js\";\nimport {\n effectToBuffs,\n type EffectTranslation,\n type TranslationPerspective,\n} from \"../cruncher/from-dsl.js\";\nimport type { Dataset } from \"./dataset.js\";\nimport { describeAbility, type AbilityAppliesTo } from \"../translate/effect.js\";\nimport { unitMatchesAppliesTo } from \"../scope.js\";\n\n/** A unit, linked to its faction, weapons, and abilities. */\nexport class UnitView {\n constructor(\n /** The full generated `Unit` record. */\n readonly raw: Unit,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** The unit's faction, or `undefined` if its `faction_id` is unknown. */\n get faction(): FactionView | undefined {\n return this.ds.factions.get(this.raw.faction_id);\n }\n\n /** Weapons referenced by `weapon_ids`; unresolved ids are skipped. */\n get weapons(): WeaponView[] {\n return resolveAll(this.raw.weapon_ids, (id) => this.ds.weapons.get(id));\n }\n\n /** Abilities referenced by `ability_ids`; unresolved ids are skipped. */\n get abilities(): AbilityView[] {\n return resolveAll(this.raw.ability_ids, (id) => this.ds.abilities.get(id));\n }\n\n /** Wargear options (weapon swaps, add-ons, choices) authored for this unit. */\n get wargearOptions(): WargearOption[] {\n return this.ds.wargearOptionsOf(this.raw);\n }\n\n /**\n * The stat profile at index `i` (default 0). Returns the schema-generated\n * profile object directly so callers can feed it straight to the engine\n * without an intermediate wrapper.\n */\n profileAt(i = 0): Unit[\"profiles\"][number] {\n const profile = this.raw.profiles[i];\n if (profile === undefined) {\n throw new RangeError(\n `UnitView(${this.raw.id}).profileAt(${i}): only ${this.raw.profiles.length} profile(s) defined`,\n );\n }\n return profile;\n }\n}\n\n/**\n * An ability, linked to the phases it acts in and the units that have it.\n *\n * Phases are not stored on the ability — they live in `phase-mappings` records.\n *\n * @example\n * units.find(\"Kharn\")!.abilities\n * .filter(a => a.phases.includes(\"shooting\"));\n */\nexport class AbilityView {\n constructor(\n /** The full generated ability record. */\n readonly raw: AbilityDSLEntry,\n private readonly ds: Dataset,\n ) {}\n\n /** The ability's id (`ability_id` in the raw record). */\n get id(): string {\n return this.raw.ability_id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /**\n * Generated plain-English approximation of this ability's effect + scope,\n * rendered from the DSL by the conformance-pinned describer\n * (`translate/effect.ts`). The dataset carries no rules prose; this is the\n * displayable stand-in.\n */\n describe(): string {\n return describeAbility(this.raw);\n }\n\n /** Game phases this ability acts in, unioned across its phase-mappings. */\n get phases(): Phase[] {\n return this.ds.phasesFor(\"ability\", this.raw.ability_id);\n }\n\n /** Units that list this ability in their `ability_ids`. */\n get units(): UnitView[] {\n return this.ds.unitsWithAbility(this.raw.ability_id);\n }\n\n /**\n * The curated `applies_to` keyword filter, or `undefined` when this ability\n * declares no resolvable unit scope. When present, it names which datasheet\n * units the ability benefits — the contract for roster-side highlighting\n * (e.g. a detachment rule that only buffs `POSSESSED` units).\n */\n get appliesTo(): AbilityAppliesTo | undefined {\n return this.raw.applies_to ?? undefined;\n }\n\n /**\n * Whether this ability's `applies_to` scope includes `unit` — true iff the\n * unit carries every `required_keywords` entry and no `excluded_keywords`,\n * across its `keywords` + `faction_keywords`. Always `false` when the ability\n * has no `applies_to` (no resolvable scope → no highlight). Pinned by the\n * `conformance/applies-to` corpus.\n */\n affectsUnit(unit: UnitView): boolean {\n return unitMatchesAppliesTo(this.raw.applies_to, unit.raw);\n }\n\n /**\n * The subset of `candidates` (typically a roster's units) this ability's\n * `applies_to` scope benefits, preserving input order. Empty when the ability\n * declares no scope.\n */\n affectedUnits(candidates: UnitView[]): UnitView[] {\n return candidates.filter((u) => this.affectsUnit(u));\n }\n\n /**\n * Buff stack this ability contributes against `context`, with provenance\n * tagged via `source` (the caller knows whether this ability is being read\n * as army, detachment, unit, leader, etc.). DSL branches the buff layer\n * can't auto-apply are dropped here; call {@link describeBuffs} if you\n * also want the diagnostics. `perspective` defaults to `\"attacker\"`; pass\n * `\"target\"` to translate the ability as a defensive buff (FNP, T/Sv\n * stat-mods, save rerolls, incoming hit penalties).\n */\n getBuffs(\n source: BuffSource,\n context?: EngineContext,\n perspective: TranslationPerspective = \"attacker\",\n ): Buff[] {\n return this.describeBuffs(source, context, perspective).applied;\n }\n\n /**\n * Full DSL→Buff translation, including the `unsupported` list of effect\n * fragments the buff layer can't model. The SPA renders these as warnings\n * so users see which abilities have effects that need a manual toggle.\n */\n describeBuffs(\n source: BuffSource,\n context?: EngineContext,\n perspective: TranslationPerspective = \"attacker\",\n ): EffectTranslation {\n const ctx: EngineContext = context ?? { phase: \"shooting\" };\n const translated = effectToBuffs(this.raw.effect, source, ctx, perspective);\n // A range-scoped ability (DSL `scope.range_inches`, e.g. a \"within 18\\\"\"\n // reroll) gates on distance to the target. Stamp it here rather than in the\n // effect translator so the `effect-translation` corpus (bare effects) is\n // unaffected; the gate is permissive until a caller sets `distanceInches`.\n const range = (this.raw.scope as { range_inches?: number } | undefined)?.range_inches;\n if (typeof range !== \"number\") return translated;\n const gate = (b: Buff): Buff => ({\n ...b,\n applicableWhen: { ...b.applicableWhen, maxRangeInches: range },\n });\n return {\n applied: translated.applied.map(gate),\n unsupported: translated.unsupported,\n activatable: translated.activatable.map((a) => ({ ...a, buffs: a.buffs.map(gate) })),\n };\n }\n}\n\n/** A weapon, linked to the units that carry it. */\nexport class WeaponView {\n constructor(\n /** The full generated `Weapon` record. */\n readonly raw: Weapon,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Units that list this weapon in their `weapon_ids`. */\n get units(): UnitView[] {\n return this.ds.unitsWithWeapon(this.raw.id);\n }\n\n /** The stat profile at index `i` (default 0). */\n profileAt(i = 0): Weapon[\"profiles\"][number] {\n const profile = this.raw.profiles[i];\n if (profile === undefined) {\n throw new RangeError(\n `WeaponView(${this.raw.id}).profileAt(${i}): only ${this.raw.profiles.length} profile(s) defined`,\n );\n }\n return profile;\n }\n\n /**\n * Catalog views for each keyword referenced by profile `i`, paired with the\n * reference-site parameters. Unresolved keyword ids are skipped.\n */\n keywordsAt(\n i = 0,\n ): { keyword: WeaponKeywordView; parameters: Record<string, unknown> | undefined }[] {\n const profile = this.profileAt(i);\n const refs = profile.keywords ?? [];\n const out: { keyword: WeaponKeywordView; parameters: Record<string, unknown> | undefined }[] = [];\n for (const ref of refs) {\n const view = this.ds.weaponKeywords.get(ref.keyword_id);\n if (!view) continue;\n out.push({\n keyword: view,\n parameters: ref.parameters as Record<string, unknown> | undefined,\n });\n }\n return out;\n }\n\n /**\n * Buffs contributed by profile `i`'s intrinsic keywords against `context` —\n * the natural \"what does this profile bring on its own?\" call the engine\n * makes automatically before adding ability/manual buffs.\n */\n profileBuffs(i: number | undefined, context: EngineContext): Buff[] {\n const index = i ?? 0;\n const out: Buff[] = [];\n for (const { keyword, parameters } of this.keywordsAt(index)) {\n out.push(\n ...buffsFromKeyword({\n keywordId: keyword.id,\n weaponId: this.raw.id,\n effect: keyword.raw.effect,\n ...(parameters !== undefined ? { parameters } : {}),\n context,\n }),\n );\n }\n return out;\n }\n}\n\n/**\n * A weapon-keyword catalog entry, linked to the weapons whose profiles\n * reference it. Exposes the keyword's mechanical effect as a buff stack\n * via {@link getBuffs}.\n */\nexport class WeaponKeywordView {\n constructor(\n /** The full generated `WeaponKeyword` record. */\n readonly raw: WeaponKeyword,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Weapons whose profiles reference this keyword id. */\n get weapons(): WeaponView[] {\n return this.ds.weaponsWithKeyword(this.raw.id);\n }\n\n /**\n * Buff contributions from this catalog entry, for one reference site:\n * pass the keyword's `parameters` (e.g. `{ value: 1 }` for Sustained Hits 1)\n * along with the `weaponId` that's carrying it (used as the buff source)\n * and the engine `context` (e.g. attacker stationary?).\n */\n getBuffs(\n parameters: Record<string, unknown> | undefined,\n weaponId: string,\n context: EngineContext,\n ): Buff[] {\n return buffsFromKeyword({\n keywordId: this.raw.id,\n weaponId,\n effect: this.raw.effect,\n ...(parameters !== undefined ? { parameters } : {}),\n context,\n });\n }\n}\n\n/** A faction, linked to its units and the records scoped to it. */\nexport class FactionView {\n constructor(\n /** The full generated `Faction` record. */\n readonly raw: Faction,\n private readonly ds: Dataset,\n ) {}\n\n get id(): string {\n return this.raw.id;\n }\n\n get name(): string {\n return this.raw.name;\n }\n\n /** Units whose `faction_id` is this faction (may be empty for successors). */\n get units(): UnitView[] {\n return this.ds.units.byFaction(this.raw.id);\n }\n\n /** Faction-scoped abilities (abilities whose `faction_id` is this faction). */\n get abilities(): AbilityView[] {\n return this.ds.abilities.byFaction(this.raw.id);\n }\n\n /** Distinct weapons carried by this faction's units. */\n get weapons(): WeaponView[] {\n const seen = new Set<string>();\n const out: WeaponView[] = [];\n for (const unit of this.units) {\n for (const weapon of unit.weapons) {\n if (seen.has(weapon.id)) continue;\n seen.add(weapon.id);\n out.push(weapon);\n }\n }\n return out;\n }\n}\n\n/** Resolve a list of ids, dropping any that don't resolve. */\nfunction resolveAll<V>(ids: string[] | undefined, get: (id: string) => V | undefined): V[] {\n const out: V[] = [];\n for (const id of ids ?? []) {\n const v = get(id);\n if (v !== undefined) out.push(v);\n }\n return out;\n}\n"]}
@@ -33,7 +33,7 @@ export * from "../cruncher/index.js";
33
33
  export { effectToBuffs, parseKeywordGrant } from "../cruncher/from-dsl.js";
34
34
  export type { ActivatableBuff, ActivatableGroupRef, EffectTranslation, TranslationPerspective, UnsupportedFragment, } from "../cruncher/from-dsl.js";
35
35
  export * from "../abilities-resolver/index.js";
36
- export { resolveRosterUnit, resolveRosterWargear, resolveAttachedLeader, resolveAttachmentPartners, } from "./roster-resolve.js";
36
+ export { resolveRosterUnit, resolveRosterWargear, resolveAttachedLeader, resolveAttachmentPartners, primaryDetachment, primaryDetachmentId, } from "./roster-resolve.js";
37
37
  import { Dataset } from "./dataset.js";
38
38
  /** The dataset built from this package's embedded data. */
39
39
  export declare const dataset: Dataset;
@@ -73,6 +73,8 @@ export declare const forceDispositions: import("./collection.js").Collection<imp
73
73
  export declare const terrainTemplates: import("./collection.js").Collection<import("../generated.js").TerrainTemplate, import("../generated.js").TerrainTemplate>;
74
74
  /** All terrain layouts. */
75
75
  export declare const terrainLayouts: import("./collection.js").Collection<import("../generated.js").TerrainLayout, import("../generated.js").TerrainLayout>;
76
+ /** Reusable model collision hulls (polygon footprints) referenced by id. */
77
+ export declare const hullShapes: import("./collection.js").Collection<import("../generated.js").HullShape, import("../generated.js").HullShape>;
76
78
  /** All resource pools. */
77
79
  export declare const resourcePools: import("./collection.js").Collection<import("../generated.js").ResourcePool, import("../generated.js").ResourcePool>;
78
80
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACtE,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;AAG1C,OAAO,EACL,SAAS,EACT,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,cAAc,sBAAsB,CAAC;AAGrC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AAGjC,cAAc,gCAAgC,CAAC;AAG/C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2DAA2D;AAC3D,eAAO,MAAM,OAAO,SAAqB,CAAC;AAE1C,kEAAkE;AAClE,eAAO,MAAM,KAAK,wGAAgB,CAAC;AACnC,6EAA6E;AAC7E,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,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,yDAAyD;AACzD,eAAO,MAAM,OAAO,4GAAkB,CAAC;AACvC,oBAAoB;AACpB,eAAO,MAAM,QAAQ,4GAAmB,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,eAAe,0HAA0B,CAAC;AACvD,mCAAmC;AACnC,eAAO,MAAM,YAAY,wHAAuB,CAAC;AACjD,+BAA+B;AAC/B,eAAO,MAAM,kBAAkB,gIAA6B,CAAC;AAC7D,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,8HAA4B,CAAC;AAC3D,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,4HAA2B,CAAC;AACzD,2BAA2B;AAC3B,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,0BAA0B;AAC1B,eAAO,MAAM,aAAa,sHAAwB,CAAC"}
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,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACtE,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;AAG1C,OAAO,EACL,SAAS,EACT,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,cAAc,sBAAsB,CAAC;AAGrC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AAGjC,cAAc,gCAAgC,CAAC;AAG/C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2DAA2D;AAC3D,eAAO,MAAM,OAAO,SAAqB,CAAC;AAE1C,kEAAkE;AAClE,eAAO,MAAM,KAAK,wGAAgB,CAAC;AACnC,6EAA6E;AAC7E,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,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,yDAAyD;AACzD,eAAO,MAAM,OAAO,4GAAkB,CAAC;AACvC,oBAAoB;AACpB,eAAO,MAAM,QAAQ,4GAAmB,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,eAAe,0HAA0B,CAAC;AACvD,mCAAmC;AACnC,eAAO,MAAM,YAAY,wHAAuB,CAAC;AACjD,+BAA+B;AAC/B,eAAO,MAAM,kBAAkB,gIAA6B,CAAC;AAC7D,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,8HAA4B,CAAC;AAC3D,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,4HAA2B,CAAC;AACzD,2BAA2B;AAC3B,eAAO,MAAM,cAAc,wHAAyB,CAAC;AACrD,4EAA4E;AAC5E,eAAO,MAAM,UAAU,gHAAqB,CAAC;AAC7C,0BAA0B;AAC1B,eAAO,MAAM,aAAa,sHAAwB,CAAC"}
@@ -35,7 +35,7 @@ export { effectToBuffs, parseKeywordGrant } from "../cruncher/from-dsl.js";
35
35
  // The eligible-abilities resolver (also reachable as Dataset.eligibleAbilities).
36
36
  export * from "../abilities-resolver/index.js";
37
37
  // Bridge helpers from the importer's RosterUnit → linked views.
38
- export { resolveRosterUnit, resolveRosterWargear, resolveAttachedLeader, resolveAttachmentPartners, } from "./roster-resolve.js";
38
+ export { resolveRosterUnit, resolveRosterWargear, resolveAttachedLeader, resolveAttachmentPartners, primaryDetachment, primaryDetachmentId, } from "./roster-resolve.js";
39
39
  import { Dataset } from "./dataset.js";
40
40
  /** The dataset built from this package's embedded data. */
41
41
  export const dataset = Dataset.embedded();
@@ -75,6 +75,8 @@ export const forceDispositions = dataset.forceDispositions;
75
75
  export const terrainTemplates = dataset.terrainTemplates;
76
76
  /** All terrain layouts. */
77
77
  export const terrainLayouts = dataset.terrainLayouts;
78
+ /** Reusable model collision hulls (polygon footprints) referenced by id. */
79
+ export const hullShapes = dataset.hullShapes;
78
80
  /** All resource pools. */
79
81
  export const resourcePools = dataset.resourcePools;
80
82
  //# sourceMappingURL=index.js.map
@@ -1 +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;AAEvC,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,iFAAiF;AACjF,OAAO,EACL,SAAS,EACT,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,6EAA6E;AAC7E,sEAAsE;AACtE,gEAAgE;AAChE,cAAc,sBAAsB,CAAC;AAErC,4EAA4E;AAC5E,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAS3E,iFAAiF;AACjF,cAAc,gCAAgC,CAAC;AAE/C,gEAAgE;AAChE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,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,6EAA6E;AAC7E,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACrD,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,yDAAyD;AACzD,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;AACvC,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,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AACjD,+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,qEAAqE;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;AACzD,2BAA2B;AAC3B,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACrD,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 type { StackableBuff, StackableBuffGroup } 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// Wargear-loadout maths: maximal default loadout, per-weapon bounds, validation.\nexport {\n optionCap,\n maximalLoadout,\n weaponBounds,\n clampWeaponCount,\n validateLoadout,\n} from \"./loadout.js\";\nexport type { Loadout, WeaponBound, Violation } from \"./loadout.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 ActivatableBuff,\n ActivatableGroupRef,\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 {\n resolveRosterUnit,\n resolveRosterWargear,\n resolveAttachedLeader,\n resolveAttachmentPartners,\n} 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/** Named target archetypes referencing real units (faction_id + unit_id). */\nexport const targetProfiles = dataset.targetProfiles;\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 non-weapon wargear items (icons, attachments). */\nexport const wargear = dataset.wargear;\n/** All missions. */\nexport const missions = dataset.missions;\n/** All mission matchups. */\nexport const missionMatchups = dataset.missionMatchups;\n/** All secondary mission cards. */\nexport const missionCards = dataset.missionCards;\n/** All deployment patterns. */\nexport const deploymentPatterns = dataset.deploymentPatterns;\n/** All force dispositions. */\nexport const forceDispositions = dataset.forceDispositions;\n/** Reusable terrain catalog: standard areas and scenery features. */\nexport const terrainTemplates = dataset.terrainTemplates;\n/** All terrain layouts. */\nexport const terrainLayouts = dataset.terrainLayouts;\n/** All resource pools. */\nexport const resourcePools = dataset.resourcePools;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/data/index.ts"],"names":[],"mappings":"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","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 type { StackableBuff, StackableBuffGroup } 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// Wargear-loadout maths: maximal default loadout, per-weapon bounds, validation.\nexport {\n optionCap,\n maximalLoadout,\n weaponBounds,\n clampWeaponCount,\n validateLoadout,\n} from \"./loadout.js\";\nexport type { Loadout, WeaponBound, Violation } from \"./loadout.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 ActivatableBuff,\n ActivatableGroupRef,\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 {\n resolveRosterUnit,\n resolveRosterWargear,\n resolveAttachedLeader,\n resolveAttachmentPartners,\n primaryDetachment,\n primaryDetachmentId,\n} 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/** Named target archetypes referencing real units (faction_id + unit_id). */\nexport const targetProfiles = dataset.targetProfiles;\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 non-weapon wargear items (icons, attachments). */\nexport const wargear = dataset.wargear;\n/** All missions. */\nexport const missions = dataset.missions;\n/** All mission matchups. */\nexport const missionMatchups = dataset.missionMatchups;\n/** All secondary mission cards. */\nexport const missionCards = dataset.missionCards;\n/** All deployment patterns. */\nexport const deploymentPatterns = dataset.deploymentPatterns;\n/** All force dispositions. */\nexport const forceDispositions = dataset.forceDispositions;\n/** Reusable terrain catalog: standard areas and scenery features. */\nexport const terrainTemplates = dataset.terrainTemplates;\n/** All terrain layouts. */\nexport const terrainLayouts = dataset.terrainLayouts;\n/** Reusable model collision hulls (polygon footprints) referenced by id. */\nexport const hullShapes = dataset.hullShapes;\n/** All resource pools. */\nexport const resourcePools = dataset.resourcePools;\n"]}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @packageDocumentation
8
8
  */
9
- import type { Roster, RosterUnit, RosterWargear } from "../import/types.js";
9
+ import type { Roster, RosterDetachment, RosterUnit, RosterWargear } from "../import/types.js";
10
10
  import type { Dataset } from "./dataset.js";
11
11
  import type { UnitView, WeaponView } from "./entities.js";
12
12
  /**
@@ -55,4 +55,19 @@ export declare function resolveAttachedLeader(roster: Roster, bodyguardUnitId: s
55
55
  * edition's multi-member attachments without an API change.
56
56
  */
57
57
  export declare function resolveAttachmentPartners(roster: Roster, unitId: string): RosterUnit[];
58
+ /**
59
+ * The roster's **primary detachment** — the first in source order. 11th
60
+ * edition rosters may field several detachments under a detachment-point cap,
61
+ * but single-detachment consumers (and every pre-11e list) just want "the"
62
+ * detachment. This names that choice so callers stop reaching into
63
+ * `detachments[0]` directly. Returns `undefined` only when the roster carries
64
+ * no detachment at all (the source declared none, or none parsed).
65
+ */
66
+ export declare function primaryDetachment(roster: Roster): RosterDetachment | undefined;
67
+ /**
68
+ * The resolved entity id of the {@link primaryDetachment}. `null` when the
69
+ * roster carries no detachment, or when the primary one failed to resolve to a
70
+ * known id (the raw name is still retained on the detachment's `ref`).
71
+ */
72
+ export declare function primaryDetachmentId(roster: Roster): string | null;
58
73
  //# sourceMappingURL=roster-resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"roster-resolve.d.ts","sourceRoot":"","sources":["../../src/data/roster-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC5E,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;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GACtB,UAAU,GAAG,SAAS,CAIxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,UAAU,EAAE,CAkBd"}
1
+ {"version":3,"file":"roster-resolve.d.ts","sourceRoot":"","sources":["../../src/data/roster-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9F,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;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GACtB,UAAU,GAAG,SAAS,CAIxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,UAAU,EAAE,CAkBd;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjE"}
@@ -79,4 +79,23 @@ export function resolveAttachmentPartners(roster, unitId) {
79
79
  }
80
80
  return out;
81
81
  }
82
+ /**
83
+ * The roster's **primary detachment** — the first in source order. 11th
84
+ * edition rosters may field several detachments under a detachment-point cap,
85
+ * but single-detachment consumers (and every pre-11e list) just want "the"
86
+ * detachment. This names that choice so callers stop reaching into
87
+ * `detachments[0]` directly. Returns `undefined` only when the roster carries
88
+ * no detachment at all (the source declared none, or none parsed).
89
+ */
90
+ export function primaryDetachment(roster) {
91
+ return roster.detachments[0];
92
+ }
93
+ /**
94
+ * The resolved entity id of the {@link primaryDetachment}. `null` when the
95
+ * roster carries no detachment, or when the primary one failed to resolve to a
96
+ * known id (the raw name is still retained on the detachment's `ref`).
97
+ */
98
+ export function primaryDetachmentId(roster) {
99
+ return roster.detachments[0]?.ref.id ?? null;
100
+ }
82
101
  //# sourceMappingURL=roster-resolve.js.map
@@ -1 +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;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,eAAuB;IAEvB,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,EAAE,aAAa,CAAC,EAAE,KAAK,eAAe,CACjE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAc,EACd,MAAc;IAEd,MAAM,IAAI,GAAG,IAAI,GAAG,EAAc,CAAC;IACnC,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAyB,EAAE,EAAE;QACxC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO;QAC9B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACZ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,iDAAiD;QACjD,IAAI,CAAC,CAAC,iBAAiB,EAAE,aAAa,CAAC,EAAE,KAAK,MAAM;YAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7D,iEAAiE;QACjE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC;YAC/C,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,iBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,CAAC;IACH,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 { Roster, 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\n/**\n * The roster's leader entry attached to `bodyguardUnitId`, if any. Import\n * stores the inferred (always-provisional) attachment on the *leader's*\n * {@link RosterUnit}, pointing down to its bodyguard via\n * `leader_attachment.bodyguard_ref`. Selection UIs start from the body unit,\n * so this scans for the leader whose `bodyguard_ref.id` matches. Returns\n * `undefined` when no leader in the roster is attached to that unit (the\n * common case — attachments are optional at game start).\n */\nexport function resolveAttachedLeader(\n roster: Roster,\n bodyguardUnitId: string,\n): RosterUnit | undefined {\n return roster.units.find(\n (u) => u.leader_attachment?.bodyguard_ref.id === bodyguardUnitId,\n );\n}\n\n/**\n * Every roster unit attached to `unitId`, resolved from *either* end of the\n * attachment. A leader+bodyguard are one combined unit, so a selection UI may\n * start from either half:\n * - `unitId` is the **bodyguard** → the leader(s) whose\n * `leader_attachment.bodyguard_ref.id` points at it (body-first, the\n * {@link resolveAttachedLeader} direction), and\n * - `unitId` is the **leader** → the bodyguard its own `leader_attachment`\n * points to.\n * Returns the partner {@link RosterUnit}s (deduped, source order). Empty when\n * the unit has no attachment in this roster — the common case, since\n * attachments are optional at game start. Shaped as a list to carry 11th\n * edition's multi-member attachments without an API change.\n */\nexport function resolveAttachmentPartners(\n roster: Roster,\n unitId: string,\n): RosterUnit[] {\n const seen = new Set<RosterUnit>();\n const out: RosterUnit[] = [];\n const add = (u: RosterUnit | undefined) => {\n if (!u || seen.has(u)) return;\n seen.add(u);\n out.push(u);\n };\n\n for (const u of roster.units) {\n // Body-first: leaders pointing down at `unitId`.\n if (u.leader_attachment?.bodyguard_ref.id === unitId) add(u);\n // Leader-first: `unitId`'s own entry points down at a bodyguard.\n if (u.ref.id === unitId && u.leader_attachment) {\n add(roster.units.find((b) => b.ref.id === u.leader_attachment!.bodyguard_ref.id));\n }\n }\n return out;\n}\n"]}
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;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,eAAuB;IAEvB,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,EAAE,aAAa,CAAC,EAAE,KAAK,eAAe,CACjE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAc,EACd,MAAc;IAEd,MAAM,IAAI,GAAG,IAAI,GAAG,EAAc,CAAC;IACnC,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAyB,EAAE,EAAE;QACxC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO;QAC9B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACZ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,iDAAiD;QACjD,IAAI,CAAC,CAAC,iBAAiB,EAAE,aAAa,CAAC,EAAE,KAAK,MAAM;YAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7D,iEAAiE;QACjE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC;YAC/C,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,iBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC;AAC/C,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 { Roster, RosterDetachment, 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\n/**\n * The roster's leader entry attached to `bodyguardUnitId`, if any. Import\n * stores the inferred (always-provisional) attachment on the *leader's*\n * {@link RosterUnit}, pointing down to its bodyguard via\n * `leader_attachment.bodyguard_ref`. Selection UIs start from the body unit,\n * so this scans for the leader whose `bodyguard_ref.id` matches. Returns\n * `undefined` when no leader in the roster is attached to that unit (the\n * common case — attachments are optional at game start).\n */\nexport function resolveAttachedLeader(\n roster: Roster,\n bodyguardUnitId: string,\n): RosterUnit | undefined {\n return roster.units.find(\n (u) => u.leader_attachment?.bodyguard_ref.id === bodyguardUnitId,\n );\n}\n\n/**\n * Every roster unit attached to `unitId`, resolved from *either* end of the\n * attachment. A leader+bodyguard are one combined unit, so a selection UI may\n * start from either half:\n * - `unitId` is the **bodyguard** → the leader(s) whose\n * `leader_attachment.bodyguard_ref.id` points at it (body-first, the\n * {@link resolveAttachedLeader} direction), and\n * - `unitId` is the **leader** → the bodyguard its own `leader_attachment`\n * points to.\n * Returns the partner {@link RosterUnit}s (deduped, source order). Empty when\n * the unit has no attachment in this roster — the common case, since\n * attachments are optional at game start. Shaped as a list to carry 11th\n * edition's multi-member attachments without an API change.\n */\nexport function resolveAttachmentPartners(\n roster: Roster,\n unitId: string,\n): RosterUnit[] {\n const seen = new Set<RosterUnit>();\n const out: RosterUnit[] = [];\n const add = (u: RosterUnit | undefined) => {\n if (!u || seen.has(u)) return;\n seen.add(u);\n out.push(u);\n };\n\n for (const u of roster.units) {\n // Body-first: leaders pointing down at `unitId`.\n if (u.leader_attachment?.bodyguard_ref.id === unitId) add(u);\n // Leader-first: `unitId`'s own entry points down at a bodyguard.\n if (u.ref.id === unitId && u.leader_attachment) {\n add(roster.units.find((b) => b.ref.id === u.leader_attachment!.bodyguard_ref.id));\n }\n }\n return out;\n}\n\n/**\n * The roster's **primary detachment** — the first in source order. 11th\n * edition rosters may field several detachments under a detachment-point cap,\n * but single-detachment consumers (and every pre-11e list) just want \"the\"\n * detachment. This names that choice so callers stop reaching into\n * `detachments[0]` directly. Returns `undefined` only when the roster carries\n * no detachment at all (the source declared none, or none parsed).\n */\nexport function primaryDetachment(roster: Roster): RosterDetachment | undefined {\n return roster.detachments[0];\n}\n\n/**\n * The resolved entity id of the {@link primaryDetachment}. `null` when the\n * roster carries no detachment, or when the primary one failed to resolve to a\n * known id (the raw name is still retained on the detachment's `ref`).\n */\nexport function primaryDetachmentId(roster: Roster): string | null {\n return roster.detachments[0]?.ref.id ?? null;\n}\n"]}