diff --git a/components/game-table.tsx b/components/game-table.tsx index be51b584..89bd69c9 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -95,6 +95,7 @@ import { NexusHarkonnenBetrayal } from './nexus-harkonnen-betrayal'; import { OrnithopterMovement } from './ornithopter-movement'; import { DiscoveryOrnithopterMovement } from './discovery-flight-movement'; import { PlanetologistMovement } from './planetologist-movement'; +import { selectedOriginElites } from '@/game/planetologist-movement'; import { SandmasterMovement } from './sandmaster-movement'; import { SandmasterWormChoice } from './sandmaster-worm'; import { sandmasterWormCollection } from '@/game/sandmaster-worm'; @@ -177,7 +178,7 @@ import { SmugglerShipmentChoice } from './smuggler-shipment'; import { nexusGuildSecretAllyAction, nexusGuildSecretAllyQuote } from '@/game/nexus-guild-secret-ally-options'; import { nexusRicheseAction, nexusRicheseQuote } from '@/game/nexus-richese-options'; import { guildTransportQuote } from '@/game/transport-quote'; -import { fremenReserveEntry, botGroundMoveAllowed } from '@/game/bot-mobility'; +import { fremenReserveEntry, botGroundMoveAllowed, botMovementRange, groundMoveRouteDistance } from '@/game/bot-mobility'; import { isAlliedShipmentVisit } from '@/game/allied-separation'; import { location as boardLocation } from '@/game/board'; import { fighterCount, isAdvisor } from '@/game/advisors'; @@ -814,6 +815,15 @@ export function GameTable({ count > 0 && !botGroundMoveAllowed(g, me, key, boardLocation(selected, sector), 0), ); + const movementTarget = boardLocation(selected, sector); + const selectedMovementElite = combineSectors + ? selectedOriginElites(physicalDraftEntries, eliteForces, splitLocation(source).territory) + : eliteAmount; + const ordinaryMovementRange = botMovementRange(g, me, selectedMovementElite); + const ordinaryMovementRouteBlocked = physicalDraftEntries.some(([key, count]) => + count > 0 && groundMoveRouteDistance(g, me, key, movementTarget) > ordinaryMovementRange) || + (!!includesNoField && !!ownNoField && groundMoveRouteDistance(g, me, + boardLocation(ownNoField.location.territory, ownNoField.location.sector), movementTarget) > ordinaryMovementRange); const act = (a: Action) => { // Control of one's own seat is independent of every gameplay response. if (a.type === 'setAutopilot') { void send(a); return; } @@ -4674,6 +4684,7 @@ export function GameTable({ ordinaryMovementAction, !source || canceledFremenRouteBlocked || + ordinaryMovementRouteBlocked || selectedDestinationInStorm || (me.moved ?? 0) >= (me.movesAllowed ?? 1) || (includesNoField @@ -4681,9 +4692,11 @@ export function GameTable({ : (me.forces[source] ?? 0) === 0), selectedDestinationInStorm ? 'movement-storm-unavailable' - : canceledFremenRouteBlocked - ? 'movement-range-unavailable' - : undefined, + : ordinaryMovementRouteBlocked + ? 'movement-route-unavailable' + : canceledFremenRouteBlocked + ? 'movement-range-unavailable' + : undefined, )} {canceledFremenRouteBlocked && !selectedDestinationInStorm && ( @@ -4697,6 +4710,13 @@ export function GameTable({ territory, or use available ornithopters.

)} + {ordinaryMovementRouteBlocked && !selectedDestinationInStorm && ( +

+ Movement unavailable: this force group has no unblocked + ground route within its ordinary range. Choose a reachable + source or destination outside the storm and blocked strongholds. +

+ )} {selectedDestinationInStorm && (

= (me.movesAllowed ?? 1) ? 'No ordinary movement remains this turn.' + : ordinaryMovementRouteBlocked + ? 'Choose an unblocked source and route outside the storm.' : canceledFremenRouteBlocked ? 'Choose an ordinary route within your available range.' : selectedDestinationInStorm diff --git a/components/nexus-advisors.tsx b/components/nexus-advisors.tsx index d54c0b33..0c328993 100644 --- a/components/nexus-advisors.tsx +++ b/components/nexus-advisors.tsx @@ -79,6 +79,11 @@ function AdvisorSelection({ game, act, busy }: Props) { This spends your Nexus card once. Karama may cancel the entire selection. It costs no spice and uses no shipment or movement allowance.

+

+ Provisional card exception: Cunning may convert freshly accompanied or + storm-bound advisors in place. Ordinary flipping, storm movement and + battle restrictions remain unchanged. +

{offer.blocked &&

{offer.blocked}

}