52 lines
2.5 KiB
TypeScript
52 lines
2.5 KiB
TypeScript
import type { Game, GameView } from './engine';
|
|
import { splitLocation, validGameLocation } from './board';
|
|
import { faction } from './catalog';
|
|
import { nativeExpansionLeaderSkillsProfile } from './leader-skill-profile';
|
|
import { sandmasterLeader, sandmasterModeSupported } from './sandmaster-movement';
|
|
|
|
export type SandmasterWormCollection = {
|
|
leader: string;
|
|
key: string | null;
|
|
before: number;
|
|
blocked: string | null;
|
|
};
|
|
|
|
/** A worm ride names a destination, not a traversed ground route. */
|
|
export function sandmasterWormCollection(
|
|
game: Game | GameView,
|
|
player: string,
|
|
destination: string,
|
|
sector: number,
|
|
decision: Game['decision'] | GameView['decision'] = game.decision,
|
|
): SandmasterWormCollection | null {
|
|
const leader = sandmasterLeader(game, player);
|
|
if (!leader || game.phase !== 1 || decision?.kind !== 'wormRide' ||
|
|
decision.player !== player ||
|
|
game.players.find(p => p.id === player)?.faction !== 'fremen') return null;
|
|
const blocked = (reason: string): SandmasterWormCollection =>
|
|
({ leader, key: null, before: 0, blocked: reason });
|
|
if (!sandmasterModeSupported(game) || game.ecazTreachery ||
|
|
!(nativeExpansionLeaderSkillsProfile(game) ||
|
|
game.players.every(p => faction(p.faction).expansion === 'base')))
|
|
return blocked('Sandmaster worm collection with other optional modules is still being integrated.');
|
|
if (destination === decision.territory ||
|
|
!validGameLocation(game, destination, sector) || sector === game.storm)
|
|
return blocked('Choose a different legal destination for the worm ride.');
|
|
const piles = Object.entries(game.spice)
|
|
.filter(([key]) => splitLocation(key).territory === destination);
|
|
if (piles.some(([key, amount]) => {
|
|
const at = splitLocation(key);
|
|
return !validGameLocation(game, at.territory, at.sector) ||
|
|
key !== `${at.territory}:${at.sector}` || !Number.isSafeInteger(amount) || amount < 0;
|
|
})) return blocked('Sandmaster needs a valid destination spice pile.');
|
|
const positive = piles.filter(([, amount]) => amount > 0);
|
|
if (positive.length > 1)
|
|
return blocked('Sandmaster collection among multiple spice piles awaits its allocation ruling.');
|
|
if (!positive.length) return blocked('There is no spice to collect at this destination.');
|
|
const [key, before] = positive[0];
|
|
const spice = game.players.find(p => p.id === player)?.spice;
|
|
if (!Number.isSafeInteger(spice) || !Number.isSafeInteger(spice! + 1))
|
|
return blocked('Sandmaster collection needs a valid spice balance.');
|
|
return { leader, key, before, blocked: null };
|
|
}
|