Support board-wide Truthtrance force questions
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
f1007a4ea8
commit
89589878fc
12 files changed
+144
-29
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { location, territory } from './board';
|
||||
import { location, territory, validLocation } from './board';
|
||||
import type { FactionId } from './catalog';
|
||||
|
||||
export type ForceCountFact = {
|
||||
@@ -6,6 +6,7 @@ export type ForceCountFact = {
|
||||
zone:
|
||||
| { kind: 'reserves' }
|
||||
| { kind: 'tanks' }
|
||||
| { kind: 'board' }
|
||||
| { kind: 'territory'; territory: string }
|
||||
| { kind: 'location'; territory: string; sector: number };
|
||||
counter: 'total' | 'normal' | 'elite';
|
||||
@@ -80,9 +81,9 @@ export function parseForceCountFact(
|
||||
const zone = value.zone;
|
||||
if (!record(zone)) throw new Error('Choose a current force pool.');
|
||||
let parsedZone: ForceCountFact['zone'];
|
||||
if (zone.kind === 'reserves' || zone.kind === 'tanks') {
|
||||
if (zone.kind === 'reserves' || zone.kind === 'tanks' || zone.kind === 'board') {
|
||||
if (!exactKeys(zone, ['kind']))
|
||||
throw new Error('Reserve and Tanks facts cannot name a board location.');
|
||||
throw new Error('Reserve, Tanks and board facts cannot name a location.');
|
||||
parsedZone = { kind: zone.kind };
|
||||
} else if (zone.kind === 'territory') {
|
||||
if (
|
||||
@@ -110,7 +111,7 @@ export function parseForceCountFact(
|
||||
sector: zone.sector as number,
|
||||
};
|
||||
} else {
|
||||
throw new Error('Choose reserves, the Tanks, a current territory, or a board location.');
|
||||
throw new Error('Choose reserves, the Tanks, the board, a current territory, or a board location.');
|
||||
}
|
||||
return {
|
||||
kind: 'forceCount',
|
||||
@@ -133,7 +134,7 @@ export function forceCountCounterError(
|
||||
: null;
|
||||
}
|
||||
|
||||
function chosenPool(player: ForceCountPlayer, fact: ForceCountFact) {
|
||||
function chosenPool(player: ForceCountPlayer, fact: ForceCountFact, boardLocations?: readonly ValidLocation[]) {
|
||||
const eliteState = player.elites;
|
||||
if (eliteState !== undefined && !record(eliteState))
|
||||
throw new Error('The tracked special-force split is malformed.');
|
||||
@@ -151,12 +152,27 @@ function chosenPool(player: ForceCountPlayer, fact: ForceCountFact) {
|
||||
throw new Error('The selected board force pool is malformed.');
|
||||
if (eliteState !== undefined && !record(eliteState.forces))
|
||||
throw new Error('The selected elite board force pool is malformed.');
|
||||
const sectors = fact.zone.kind === 'territory'
|
||||
? territory(fact.zone.territory).sectors
|
||||
: [fact.zone.sector];
|
||||
const zone = fact.zone;
|
||||
const selected = zone.kind === 'board'
|
||||
? boardLocations
|
||||
: zone.kind === 'territory'
|
||||
? territory(zone.territory).sectors.map(sector => ({ territory: zone.territory, sector }))
|
||||
: [{ territory: zone.territory, sector: zone.sector }];
|
||||
if (!selected || !selected.length) throw new Error('Current board locations are unavailable.');
|
||||
if (zone.kind === 'board') {
|
||||
const seen = new Set<string>();
|
||||
for (const entry of selected) {
|
||||
if (!entry || typeof entry.territory !== 'string' || !Number.isSafeInteger(entry.sector) ||
|
||||
!validLocation(entry.territory, entry.sector))
|
||||
throw new Error('Current board locations are malformed.');
|
||||
const key = location(entry.territory, entry.sector);
|
||||
if (seen.has(key)) throw new Error('Current board locations contain a duplicate.');
|
||||
seen.add(key);
|
||||
}
|
||||
}
|
||||
let total = 0, elite = 0;
|
||||
for (const sector of sectors) {
|
||||
const key = location(fact.zone.territory, sector);
|
||||
for (const selectedLocation of selected) {
|
||||
const key = location(selectedLocation.territory, selectedLocation.sector);
|
||||
const sectorTotal = count(player.forces[key] === undefined ? 0 : player.forces[key], 'The selected physical force count');
|
||||
const storedElite = eliteState?.forces[key];
|
||||
const sectorElite = count(storedElite === undefined ? 0 : storedElite, 'The selected elite force count');
|
||||
@@ -181,10 +197,11 @@ function chosenPool(player: ForceCountPlayer, fact: ForceCountFact) {
|
||||
export function forceCountFactMatches(
|
||||
player: ForceCountPlayer,
|
||||
fact: ForceCountFact,
|
||||
boardLocations?: readonly ValidLocation[],
|
||||
): boolean {
|
||||
const unavailable = forceCountCounterError(player, fact.counter);
|
||||
if (unavailable) throw new Error(unavailable);
|
||||
const pool = chosenPool(player, fact);
|
||||
const pool = chosenPool(player, fact, boardLocations);
|
||||
const actual =
|
||||
fact.counter === 'total'
|
||||
? pool.total
|
||||
@@ -217,8 +234,10 @@ export function forceCountFactText(fact: ForceCountFact): string {
|
||||
? 'in your reserves'
|
||||
: fact.zone.kind === 'tanks'
|
||||
? 'in the Tleilaxu Tanks'
|
||||
: fact.zone.kind === 'territory'
|
||||
? `in ${territory(fact.zone.territory).name} across all sectors`
|
||||
: `in ${territory(fact.zone.territory).name}, sector ${fact.zone.sector}`;
|
||||
: fact.zone.kind === 'board'
|
||||
? 'on the current Arrakis board'
|
||||
: fact.zone.kind === 'territory'
|
||||
? `in ${territory(fact.zone.territory).name} across all sectors`
|
||||
: `in ${territory(fact.zone.territory).name}, sector ${fact.zone.sector}`;
|
||||
return `you currently have ${comparison} ${fact.value} ${counters} ${zone}`;
|
||||
}
|
||||
Reference in new issue
Block a user