Compare commits

..

2 Commits

6 changed files with 166 additions and 55 deletions

View File

@@ -1,7 +1,5 @@
import "./styles.css"; import "./styles.css";
console.log("[DEBUG] Starting main.ts import...");
import { import {
ROOT_SHIFT_COST, ROOT_SHIFT_COST,
ROUND_ANIMATION_BONUS_MS, ROUND_ANIMATION_BONUS_MS,
@@ -28,6 +26,7 @@ import {
createInitiativeDraft, createInitiativeDraft,
} from "./rules-initiative"; } from "./rules-initiative";
import { import {
WEATHER_CARDS,
createWeatherDraft, createWeatherDraft,
getCurrentWeatherPlayerId, getCurrentWeatherPlayerId,
getWeatherCard, getWeatherCard,
@@ -54,22 +53,17 @@ import type {
import { keyFor, parseKey, tint, wait } from "./utils"; import { keyFor, parseKey, tint, wait } from "./utils";
const app = document.querySelector("#app"); const app = document.querySelector("#app");
console.log("[DEBUG] App element found:", app);
if (!(app instanceof HTMLElement)) { if (!(app instanceof HTMLElement)) {
console.error("[DEBUG] #app container not found or not HTMLElement");
throw new Error("#app container not found"); throw new Error("#app container not found");
} }
console.log("[DEBUG] Initializing state...");
let roundAnimationToken = 0; let roundAnimationToken = 0;
let setup: SetupState = createSetupState(); let setup: SetupState = createSetupState();
console.log("[DEBUG] Setup created:", setup);
let state: GameState = createInitialState(setup); let state: GameState = createInitialState(setup);
console.log("[DEBUG] Initial state created:", state);
let isNewGameModalOpen = false; let isNewGameModalOpen = false;
let previousScoreSnapshot: ScoreSnapshot[] | null = null; let previousScoreSnapshot: ScoreSnapshot[] | null = null;
console.log("[DEBUG] State initialized, proceeding to render..."); let setupTab: "board" | "rules" | "events" | "players" = "board";
function rebuildSetup(overrides: Partial<SetupState> = {}) { function rebuildSetup(overrides: Partial<SetupState> = {}) {
setup = createSetupState( setup = createSetupState(
@@ -84,6 +78,7 @@ function rebuildSetup(overrides: Partial<SetupState> = {}) {
overrides.initiativeMode ?? setup.initiativeMode, overrides.initiativeMode ?? setup.initiativeMode,
overrides.biddingOrderRule ?? setup.biddingOrderRule, overrides.biddingOrderRule ?? setup.biddingOrderRule,
overrides.weatherDraftEnabled ?? setup.weatherDraftEnabled, overrides.weatherDraftEnabled ?? setup.weatherDraftEnabled,
overrides.weatherDraftCount ?? setup.weatherDraftCount,
overrides.winCondition ?? setup.winCondition, overrides.winCondition ?? setup.winCondition,
overrides.maxRounds ?? setup.maxRounds, overrides.maxRounds ?? setup.maxRounds,
overrides.topLeafTarget ?? setup.topLeafTarget, overrides.topLeafTarget ?? setup.topLeafTarget,
@@ -154,6 +149,10 @@ function getTurnLabel() {
return state.gameOver ? "Game Over" : `${getCurrentPlayer().name}'s turn`; return state.gameOver ? "Game Over" : `${getCurrentPlayer().name}'s turn`;
} }
function isBankingEnabled() {
return state.activeRoundEffects.includes("storehouse");
}
function awardGrowth(player: Player, amount: number) { function awardGrowth(player: Player, amount: number) {
if (amount <= 0) { if (amount <= 0) {
return; return;
@@ -207,7 +206,15 @@ function getSelectedRootShiftMoves() {
} }
function getLegalMovesForSource(sourceKey: NodeKey, player: Player) { function getLegalMovesForSource(sourceKey: NodeKey, player: Player) {
return getLegalMovesForSourceForState(state, sourceKey, player); const moves = getLegalMovesForSourceForState(state, sourceKey, player);
if (!state.activeRoundEffects.includes("sun_ladder")) {
return moves;
}
return moves.map((move) => move.direction === "vertical"
? { ...move, cost: Math.max(0, move.cost - 1) }
: move);
} }
function playerHasLegalMove(player: Player) { function playerHasLegalMove(player: Player) {
@@ -621,6 +628,10 @@ function endTurn() {
} }
function bankGrowthAndEndTurn() { function bankGrowthAndEndTurn() {
if (!isBankingEnabled()) {
return;
}
const player = getCurrentPlayer(); const player = getCurrentPlayer();
if (player.growthPoints <= 0) { if (player.growthPoints <= 0) {
endTurn(); endTurn();
@@ -787,6 +798,11 @@ function randomizeStartingLocations() {
render(); render();
} }
function setSetupTab(tab: typeof setupTab) {
setupTab = tab;
render();
}
function renderNewGameModal() { function renderNewGameModal() {
if (!isNewGameModalOpen) { if (!isNewGameModalOpen) {
return ""; return "";
@@ -794,6 +810,7 @@ function renderNewGameModal() {
const maxSeeds = getMaxStartingNodesPerPlayer(setup.playerCount, setup.columns); const maxSeeds = getMaxStartingNodesPerPlayer(setup.playerCount, setup.columns);
const previewPlayers = createPlayers(setup.playerCount, setup.paletteOrder); const previewPlayers = createPlayers(setup.playerCount, setup.paletteOrder);
const draftCountMax = WEATHER_CARDS.length;
return ` return `
<div class="modal-backdrop" id="new-game-modal-backdrop"> <div class="modal-backdrop" id="new-game-modal-backdrop">
@@ -805,9 +822,15 @@ function renderNewGameModal() {
</div> </div>
<button class="ghost-button" id="close-new-game">Close</button> <button class="ghost-button" id="close-new-game">Close</button>
</header> </header>
<nav class="setup-tabs" aria-label="Setup categories">
<button class="setup-tab${setupTab === "board" ? " setup-tab--active" : ""}" data-setup-tab="board"><span aria-hidden="true">▦</span><span>Board</span></button>
<button class="setup-tab${setupTab === "rules" ? " setup-tab--active" : ""}" data-setup-tab="rules"><span aria-hidden="true">⚖</span><span>Rules</span></button>
<button class="setup-tab${setupTab === "events" ? " setup-tab--active" : ""}" data-setup-tab="events"><span aria-hidden="true">☀</span><span>Events</span></button>
<button class="setup-tab${setupTab === "players" ? " setup-tab--active" : ""}" data-setup-tab="players"><span aria-hidden="true">◉</span><span>Players</span></button>
</nav>
<div class="modal-body"> <div class="modal-body">
<!-- Board Settings Section --> ${setupTab === "board" ? `
<section class="setup-section"> <section class="setup-section">
<h2 class="setup-section__title">Board Settings</h2> <h2 class="setup-section__title">Board Settings</h2>
<div class="setup-grid"> <div class="setup-grid">
@@ -832,8 +855,9 @@ function renderNewGameModal() {
</label> </label>
</div> </div>
</section> </section>
` : ""}
<!-- Game Rules Section --> ${setupTab === "rules" ? `
<section class="setup-section"> <section class="setup-section">
<h2 class="setup-section__title">Game Rules</h2> <h2 class="setup-section__title">Game Rules</h2>
<div class="setup-grid setup-grid--2col"> <div class="setup-grid setup-grid--2col">
@@ -871,10 +895,21 @@ function renderNewGameModal() {
</select> </select>
</label> </label>
` : ""} ` : ""}
<label class="setup-field setup-field--checkbox">
<span class="setup-field__label">Weather Draft</span>
<input id="weather-draft-toggle" type="checkbox" ${setup.weatherDraftEnabled ? "checked" : ""} />
</label>
${setup.weatherDraftEnabled ? `
<label class="setup-field">
<span class="setup-field__label">Weather cards in draft</span>
<input id="weather-draft-count" type="number" min="1" max="${draftCountMax}" step="1" value="${setup.weatherDraftCount}" />
</label>
` : ""}
</div> </div>
</section> </section>
` : ""}
<!-- Random Events Section --> ${setupTab === "events" ? `
<section class="setup-section"> <section class="setup-section">
<h2 class="setup-section__title">Random Events</h2> <h2 class="setup-section__title">Random Events</h2>
<div class="setup-grid setup-grid--3col"> <div class="setup-grid setup-grid--3col">
@@ -886,14 +921,11 @@ function renderNewGameModal() {
<span class="setup-field__label">Disease %</span> <span class="setup-field__label">Disease %</span>
<input id="disease-chance" type="number" min="0" max="100" step="5" value="${setup.diseaseChance}" /> <input id="disease-chance" type="number" min="0" max="100" step="5" value="${setup.diseaseChance}" />
</label> </label>
<label class="setup-field setup-field--checkbox">
<span class="setup-field__label">Weather Draft</span>
<input id="weather-draft-toggle" type="checkbox" ${setup.weatherDraftEnabled ? "checked" : ""} />
</label>
</div> </div>
</section> </section>
` : ""}
<!-- Players Section --> ${setupTab === "players" ? `
<section class="setup-section"> <section class="setup-section">
<h2 class="setup-section__title">Players & Colors</h2> <h2 class="setup-section__title">Players & Colors</h2>
<p class="setup-section__help">Reorder players to set turn order. Starting positions are auto-assigned.</p> <p class="setup-section__help">Reorder players to set turn order. Starting positions are auto-assigned.</p>
@@ -912,6 +944,20 @@ function renderNewGameModal() {
`).join("")} `).join("")}
</div> </div>
</section> </section>
<section class="setup-section">
<h2 class="setup-section__title">Starting columns</h2>
<p class="setup-section__help">Use 1-based column numbers. Duplicate or invalid picks are auto-corrected.</p>
<button class="ghost-button randomize-button" id="randomize-starting-locations">Randomize Starting Locations</button>
<div class="player-list">
${previewPlayers.map((currentPlayer, index) => `
<label class="setup-field">
<span class="setup-field__label" style="color: ${currentPlayer.color};">${currentPlayer.name}</span>
<input class="seed-input" data-player-id="${index}" type="text" value="${setup.seedInputs[index] ?? ""}" placeholder="e.g. 2, 5" />
</label>
`).join("")}
</div>
</section>
` : ""}
</div> </div>
<footer class="modal-footer"> <footer class="modal-footer">
@@ -1259,7 +1305,7 @@ function renderSidebar() {
</div> </div>
<div class="button-row"> <div class="button-row">
<button id="end-turn" ${boardLocked ? "disabled" : ""}>End Turn</button> <button id="end-turn" ${boardLocked ? "disabled" : ""}>End Turn</button>
<button id="bank-turn" class="ghost-button" ${boardLocked ? "disabled" : ""}>Bank Remaining</button> <button id="bank-turn" class="ghost-button" ${boardLocked || !isBankingEnabled() ? "disabled" : ""}>Bank Remaining</button>
</div> </div>
</section> </section>
@@ -1386,6 +1432,11 @@ function attachEvents() {
}); });
document.querySelector<HTMLInputElement>("#weather-draft-toggle")?.addEventListener("change", (event) => { document.querySelector<HTMLInputElement>("#weather-draft-toggle")?.addEventListener("change", (event) => {
setup.weatherDraftEnabled = (event.currentTarget as HTMLInputElement).checked; setup.weatherDraftEnabled = (event.currentTarget as HTMLInputElement).checked;
render();
});
document.querySelector<HTMLInputElement>("#weather-draft-count")?.addEventListener("change", (event) => {
rebuildSetup({ weatherDraftCount: Math.max(1, Number((event.currentTarget as HTMLInputElement).value) || 1) });
render();
}); });
document.querySelector<HTMLSelectElement>("#win-condition")?.addEventListener("change", (event) => { document.querySelector<HTMLSelectElement>("#win-condition")?.addEventListener("change", (event) => {
setup.winCondition = (event.currentTarget as HTMLSelectElement).value as SetupState["winCondition"]; setup.winCondition = (event.currentTarget as HTMLSelectElement).value as SetupState["winCondition"];
@@ -1427,13 +1478,15 @@ function attachEvents() {
chooseWeatherAction(button.dataset.weatherCard as WeatherCardId, button.dataset.weatherAction as "draft" | "ban"); chooseWeatherAction(button.dataset.weatherCard as WeatherCardId, button.dataset.weatherAction as "draft" | "ban");
}); });
}); });
document.querySelectorAll<HTMLElement>("[data-setup-tab]").forEach((button) => {
button.addEventListener("click", () => {
setSetupTab(button.dataset.setupTab as typeof setupTab);
});
});
} }
function render() { function render() {
console.log("[DEBUG] render() called, playerCount:", state?.players?.length);
try {
const playerCount = state.players.length; const playerCount = state.players.length;
console.log("[DEBUG] Building HTML...");
app.innerHTML = ` app.innerHTML = `
<main class="layout" style="--player-count: ${playerCount}"> <main class="layout" style="--player-count: ${playerCount}">
<section class="game-area"> <section class="game-area">
@@ -1448,17 +1501,8 @@ function render() {
${renderInitiativeModal()} ${renderInitiativeModal()}
${renderWeatherDraftModal()} ${renderWeatherDraftModal()}
`; `;
console.log("[DEBUG] HTML rendered, attaching events...");
attachEvents(); attachEvents();
console.log("[DEBUG] Events attached, getting score snapshot...");
previousScoreSnapshot = getScoreSnapshot(); previousScoreSnapshot = getScoreSnapshot();
console.log("[DEBUG] Render complete!");
} catch (error) {
console.error("[DEBUG] Error in render():", error);
throw error;
}
} }
console.log("[DEBUG] About to call render()...");
render(); render();
console.log("[DEBUG] render() executed");

View File

@@ -28,6 +28,48 @@ function getLeafCounts(state: GameState) {
return counts; return counts;
} }
function getColumnPresence(state: GameState, column: number) {
const owners = new Set<PlayerId>();
for (let row = 0; row < state.config.rows; row += 1) {
const ownerId = state.nodes.get(keyFor(row, column))?.ownerId;
if (ownerId !== undefined) {
owners.add(ownerId);
}
}
return [...owners];
}
function applyColumnBaseEnergy(state: GameState, scores: number[], ownerId: PlayerId, playersPresent: PlayerId[]) {
const contested = playersPresent.length > 1;
if (!contested) {
scores[ownerId] += 1;
return;
}
if (state.activeRoundEffects.includes("stalemate")) {
return;
}
if (state.activeRoundEffects.includes("split_light")) {
playersPresent.forEach((playerId) => {
scores[playerId] += 0.5;
});
return;
}
if (state.activeRoundEffects.includes("shared_light")) {
playersPresent.forEach((playerId) => {
scores[playerId] += 1;
});
return;
}
scores[ownerId] += 1;
}
function applyWeatherEffects(state: GameState, scores: number[], energySimulation: EnergySimulation) { function applyWeatherEffects(state: GameState, scores: number[], energySimulation: EnergySimulation) {
if (state.activeRoundEffects.length === 0) { if (state.activeRoundEffects.length === 0) {
return; return;
@@ -132,6 +174,7 @@ export function buildEnergySimulation(state: GameState): EnergySimulation {
terminalRow: state.config.rows - 1, terminalRow: state.config.rows - 1,
intercepted: false, intercepted: false,
ownerId: null, ownerId: null,
playersPresent: [],
hitNode: null, hitNode: null,
rootKey: null, rootKey: null,
branchNodes: [], branchNodes: [],
@@ -142,6 +185,7 @@ export function buildEnergySimulation(state: GameState): EnergySimulation {
const hitNode = parseKey(hitNodeKey); const hitNode = parseKey(hitNodeKey);
const ownerId = state.nodes.get(hitNodeKey)?.ownerId as PlayerId; const ownerId = state.nodes.get(hitNodeKey)?.ownerId as PlayerId;
const playersPresent = getColumnPresence(state, column);
const branchNodes = [hitNode]; const branchNodes = [hitNode];
const branchEdges = []; const branchEdges = [];
let cursor = hitNodeKey; let cursor = hitNodeKey;
@@ -153,12 +197,13 @@ export function buildEnergySimulation(state: GameState): EnergySimulation {
cursor = parentKey; cursor = parentKey;
} }
scores[ownerId] += 1; applyColumnBaseEnergy(state, scores, ownerId, playersPresent);
columns.push({ columns.push({
column, column,
terminalRow: hitNode.row, terminalRow: hitNode.row,
intercepted: true, intercepted: true,
ownerId, ownerId,
playersPresent,
hitNode, hitNode,
rootKey: cursor, rootKey: cursor,
branchNodes, branchNodes,

View File

@@ -4,12 +4,17 @@ import { shuffleArray } from "./utils";
export const WEATHER_CARDS: WeatherCardDefinition[] = [ export const WEATHER_CARDS: WeatherCardDefinition[] = [
{ id: "leaf_surge", title: "Leaf Surge", description: "Each leaf gives +1." }, { id: "leaf_surge", title: "Leaf Surge", description: "Each leaf gives +1." },
{ id: "branching_season", title: "Branching Season", description: "Each leaf after your first gives +1." }, { id: "branching_season", title: "Branching Season", description: "Each leaf after your first gives +1." },
{ id: "storehouse", title: "Storehouse", description: "Banking is enabled this round." },
{ id: "sun_ladder", title: "Sun Ladder", description: "Straight-up growth costs 0." },
{ id: "west_light", title: "West Light", description: "Left third columns give +1." }, { id: "west_light", title: "West Light", description: "Left third columns give +1." },
{ id: "east_light", title: "East Light", description: "Right third columns give +1." }, { id: "east_light", title: "East Light", description: "Right third columns give +1." },
{ id: "high_noon", title: "High Noon", description: "Center third columns give +1." }, { id: "high_noon", title: "High Noon", description: "Center third columns give +1." },
{ id: "edge_bloom", title: "Edge Bloom", description: "Edge columns give +1." }, { id: "edge_bloom", title: "Edge Bloom", description: "Edge columns give +1." },
{ id: "wide_reach", title: "Wide Reach", description: "Most columns gets +2." }, { id: "wide_reach", title: "Wide Reach", description: "Most columns gets +2." },
{ id: "tall_reward", title: "Tall Reward", description: "Your tallest leaf gives +2." }, { id: "tall_reward", title: "Tall Reward", description: "Your tallest leaf gives +2." },
{ id: "stalemate", title: "Stalemate", description: "Contested columns give no energy." },
{ id: "split_light", title: "Split Light", description: "Contested columns give half to each player there." },
{ id: "shared_light", title: "Shared Light", description: "Contested columns give full energy to each player there." },
]; ];
export const WEATHER_CARD_LOOKUP = new Map<WeatherCardId, WeatherCardDefinition>( export const WEATHER_CARD_LOOKUP = new Map<WeatherCardId, WeatherCardDefinition>(
@@ -17,7 +22,7 @@ export const WEATHER_CARD_LOOKUP = new Map<WeatherCardId, WeatherCardDefinition>
); );
export function createWeatherDraft(state: GameState): WeatherDraftState { export function createWeatherDraft(state: GameState): WeatherDraftState {
const rowSize = Math.min(WEATHER_CARDS.length, state.players.length + 2); const rowSize = Math.min(WEATHER_CARDS.length, state.config.weatherDraftCount);
return { return {
playerOrder: [...state.turnOrder], playerOrder: [...state.turnOrder],

View File

@@ -79,15 +79,17 @@ export function createSetupState(
initiativeMode: SetupState["initiativeMode"] = "fixed", initiativeMode: SetupState["initiativeMode"] = "fixed",
biddingOrderRule: SetupState["biddingOrderRule"] = "rotating", biddingOrderRule: SetupState["biddingOrderRule"] = "rotating",
weatherDraftEnabled = true, weatherDraftEnabled = true,
weatherDraftCount = playerCount + 2,
winCondition: SetupState["winCondition"] = "rounds", winCondition: SetupState["winCondition"] = "rounds",
maxRounds = 12, maxRounds = 12,
topLeafTarget = 4, topLeafTarget = 4,
): SetupState { ): SetupState {
console.log("[DEBUG] createSetupState started");
const clampedSeeds = Math.min(startingNodesPerPlayer, getMaxStartingNodesPerPlayer(playerCount, columns)); const clampedSeeds = Math.min(startingNodesPerPlayer, getMaxStartingNodesPerPlayer(playerCount, columns));
const defaults = createDefaultSeedInputs(playerCount, columns, clampedSeeds); const defaults = createDefaultSeedInputs(playerCount, columns, clampedSeeds);
const paletteDefaults = createDefaultPaletteOrder(playerCount); const paletteDefaults = createDefaultPaletteOrder(playerCount);
return { const result = {
playerCount, playerCount,
columns, columns,
rows, rows,
@@ -99,10 +101,13 @@ export function createSetupState(
initiativeMode, initiativeMode,
biddingOrderRule, biddingOrderRule,
weatherDraftEnabled, weatherDraftEnabled,
weatherDraftCount: Math.max(1, weatherDraftCount),
winCondition, winCondition,
maxRounds, maxRounds,
topLeafTarget: Math.max(1, Math.min(columns, topLeafTarget)), topLeafTarget: Math.max(1, Math.min(columns, topLeafTarget)),
}; };
console.log("[DEBUG] createSetupState completed");
return result;
} }
export function createPlayers(playerCount: number, paletteOrder = createDefaultPaletteOrder(playerCount)): Player[] { export function createPlayers(playerCount: number, paletteOrder = createDefaultPaletteOrder(playerCount)): Player[] {
@@ -162,6 +167,7 @@ export function normalizeSeedInputs(setup: SetupState) {
} }
export function createInitialState(setup: SetupState): GameState { export function createInitialState(setup: SetupState): GameState {
console.log("[DEBUG] createInitialState started");
const playerPaletteOrder = [...setup.paletteOrder]; const playerPaletteOrder = [...setup.paletteOrder];
const players = createPlayers(setup.playerCount, playerPaletteOrder); const players = createPlayers(setup.playerCount, playerPaletteOrder);
const turnOrder = players.map((player) => player.id); const turnOrder = players.map((player) => player.id);
@@ -175,6 +181,7 @@ export function createInitialState(setup: SetupState): GameState {
}); });
}); });
console.log("[DEBUG] createInitialState completed");
return { return {
config: { config: {
columns: setup.columns, columns: setup.columns,
@@ -185,6 +192,7 @@ export function createInitialState(setup: SetupState): GameState {
initiativeMode: setup.initiativeMode, initiativeMode: setup.initiativeMode,
biddingOrderRule: setup.biddingOrderRule, biddingOrderRule: setup.biddingOrderRule,
weatherDraftEnabled: setup.weatherDraftEnabled, weatherDraftEnabled: setup.weatherDraftEnabled,
weatherDraftCount: setup.weatherDraftCount,
winCondition: setup.winCondition, winCondition: setup.winCondition,
maxRounds: setup.maxRounds, maxRounds: setup.maxRounds,
topLeafTarget: setup.topLeafTarget, topLeafTarget: setup.topLeafTarget,

View File

@@ -91,14 +91,14 @@ html, body {
/* Board - fits within shell */ /* Board - fits within shell */
.board { .board {
position: relative; position: relative;
width: 100%; width: auto;
height: 100%; height: auto;
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
aspect-ratio: var(--board-columns) / var(--board-rows); aspect-ratio: var(--board-columns) / var(--board-rows);
display: grid; display: grid;
grid-template-columns: repeat(var(--board-columns), 1fr); grid-template-columns: repeat(var(--board-columns), minmax(0, 1fr));
grid-template-rows: repeat(var(--board-rows), 1fr); grid-template-rows: repeat(var(--board-rows), minmax(0, 1fr));
gap: clamp(2px, 0.3cqmin, 4px); gap: clamp(2px, 0.3cqmin, 4px);
margin: 0 auto; margin: 0 auto;
} }
@@ -174,6 +174,7 @@ html, body {
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
aspect-ratio: 1 / 1;
} }
.cell__shade { .cell__shade {

View File

@@ -24,6 +24,7 @@ export type SetupState = {
initiativeMode: "fixed" | "bid"; initiativeMode: "fixed" | "bid";
biddingOrderRule: "rotating" | "lowest_growth_income"; biddingOrderRule: "rotating" | "lowest_growth_income";
weatherDraftEnabled: boolean; weatherDraftEnabled: boolean;
weatherDraftCount: number;
winCondition: "rounds" | "top_leaves"; winCondition: "rounds" | "top_leaves";
maxRounds: number; maxRounds: number;
topLeafTarget: number; topLeafTarget: number;
@@ -52,6 +53,7 @@ export type GameConfig = {
initiativeMode: SetupState["initiativeMode"]; initiativeMode: SetupState["initiativeMode"];
biddingOrderRule: SetupState["biddingOrderRule"]; biddingOrderRule: SetupState["biddingOrderRule"];
weatherDraftEnabled: boolean; weatherDraftEnabled: boolean;
weatherDraftCount: number;
winCondition: SetupState["winCondition"]; winCondition: SetupState["winCondition"];
maxRounds: number; maxRounds: number;
topLeafTarget: number; topLeafTarget: number;
@@ -110,6 +112,7 @@ export type ColumnEnergy = {
terminalRow: number; terminalRow: number;
intercepted: boolean; intercepted: boolean;
ownerId: PlayerId | null; ownerId: PlayerId | null;
playersPresent: PlayerId[];
hitNode: Position | null; hitNode: Position | null;
rootKey: NodeKey | null; rootKey: NodeKey | null;
branchNodes: Position[]; branchNodes: Position[];
@@ -183,12 +186,17 @@ export type GamePhase = "initiative" | "turn" | "round_end" | "game_over";
export type WeatherCardId = export type WeatherCardId =
| "leaf_surge" | "leaf_surge"
| "branching_season" | "branching_season"
| "storehouse"
| "sun_ladder"
| "west_light" | "west_light"
| "east_light" | "east_light"
| "high_noon" | "high_noon"
| "edge_bloom" | "edge_bloom"
| "wide_reach" | "wide_reach"
| "tall_reward"; | "tall_reward"
| "stalemate"
| "split_light"
| "shared_light";
export type WeatherCardDefinition = { export type WeatherCardDefinition = {
id: WeatherCardId; id: WeatherCardId;