diff options
Diffstat (limited to 'rules.ts')
-rw-r--r-- | rules.ts | 296 |
1 files changed, 279 insertions, 17 deletions
@@ -230,6 +230,17 @@ function setup_choose_card() { next(); } +function setup_final_bid() { + log_h1('Final Bid'); + const player_order = get_player_order(); + game.engine = player_order.map((faction_id) => + create_leaf_node('choose_final_bid', faction_id) + ); + game.engine.push(create_function_node('resolve_final_bid')); + game.engine.push(create_function_node('setup_choose_card')); + next(); +} + function setup_player_turn() { const player_order = get_player_order(); game.engine = player_order.map((faction_id) => @@ -255,12 +266,15 @@ function start_of_player_turn() { const engine_functions: Record<string, Function> = { check_activate_icon, end_of_turn, - end_of_year, + // end_of_year, setup_bag_of_glory, setup_choose_card, + setup_final_bid, setup_player_turn, start_of_player_turn, + start_year, resolve_fascist_test, + resolve_final_bid, }; function get_active( @@ -373,6 +387,7 @@ function game_view(state: Game, player: Player) { bag_of_glory: game.bag_of_glory, bonuses: game.bonuses, current_events: game.current_events, + final_bid: game.final_bid[faction_id], fronts: game.fronts, glory: game.glory, hand: game.hands[faction_id], @@ -422,6 +437,11 @@ export function setup(seed: number, _scenario: string, _options: unknown) { f: [], }, engine: [], + final_bid: { + [ANARCHISTS_ID]: [], + [COMMUNISTS_ID]: [], + [MODERATES_ID]: [], + }, fronts: { a: { value: -2, @@ -483,8 +503,8 @@ export function setup(seed: number, _scenario: string, _options: unknown) { triggered_track_effects: [[], [], [], [], []], log: [], undo: [], - turn: 1, - year: 1, + turn: 0, + year: 0, state_data: null, }; @@ -493,11 +513,22 @@ export function setup(seed: number, _scenario: string, _options: unknown) { } function draw_hand_cards(faction_id: FactionId, count: number) { + const deck = list_deck(faction_id); + + // Draw all remaining cards + if (deck.length < count) { + count = count - deck.length; + game.hands[faction_id] = game.hands[faction_id].concat(deck); + game.discard[faction_id] = []; + } + + console.log('draw_hand_cards', count); const log = count === 1 ? `${get_player(faction_id)} draws 1 card` : `${get_player(faction_id)} draws ${count} cards`; logi(log); + console.log('deck', list_deck(faction_id)); for (let i = 0; i < count; i++) { const deck = list_deck(faction_id); game.hands[faction_id].push(draw_card(deck)); @@ -507,7 +538,8 @@ function draw_hand_cards(faction_id: FactionId, count: number) { // #endregion function start_year() { - // log_h1('Year ' + game.year); + game.year++; + game.turn = 1; game.current_events = []; role_ids.forEach((role) => { draw_hand_cards(role, 5); @@ -528,10 +560,11 @@ function start_turn() { game.engine = card.effects.map((effect) => resolve_effect(effect, game.initiative) ); - game.engine.push({ - t: 'f', - f: 'setup_choose_card', - }); + if (game.year === 3 && game.turn === 1) { + game.engine.push(create_function_node('setup_final_bid')); + } else { + game.engine.push(create_function_node('setup_choose_card')); + } next(); // game.state = 'resolve_event'; // game.active = faction_player_map[game.initiative]; @@ -702,6 +735,55 @@ states.attack_front = { }, }; +states.break_tie_final_bid = { + inactive: 'break tie for Final Bid', + prompt() { + view.prompt = 'Choose the winner of the Final Bid'; + const { winners } = get_active_node_args(); + for (const f of winners) { + gen_action(faction_player_map[f]); + } + }, + Anarchist() { + win_final_bid(ANARCHISTS_ID); + resolve_active_and_proceed(); + }, + Communist() { + win_final_bid(COMMUNISTS_ID); + resolve_active_and_proceed(); + }, + Moderate() { + win_final_bid(MODERATES_ID); + resolve_active_and_proceed(); + }, +}; + +states.break_tie_winner = { + inactive: 'break tie for winner of the game', + prompt() { + view.prompt = 'Choose the winner of the game'; + const { winners } = get_active_node_args(); + for (const f of winners) { + gen_action(faction_player_map[f]); + } + }, + Anarchist() { + const { glory } = get_active_node_args(); + win_game(ANARCHIST, glory); + resolve_active_and_proceed(); + }, + Communist() { + const { glory } = get_active_node_args(); + win_game(COMMUNIST, glory); + resolve_active_and_proceed(); + }, + Moderate() { + const { glory } = get_active_node_args(); + win_game(MODERATE, glory); + resolve_active_and_proceed(); + }, +}; + states.choose_area_ap = { inactive: 'choose area to use Action Points', prompt() { @@ -790,6 +872,70 @@ states.choose_card = { }, }; +states.choose_final_bid = { + inactive: 'choose Final Bid', + prompt() { + view.prompt = 'Choose a card to add to the Final Bid'; + const faction = get_active_faction(); + for (let c of game.hands[faction]) { + if (!game.final_bid[faction].includes(c)) { + gen_action_card(c); + } + } + gen_action('done'); + }, + card(c: CardId) { + const faction = get_active_faction(); + game.final_bid[faction].push(c); + if (game.final_bid[faction].length < 3) { + next(); + } else { + resolve_active_and_proceed(); + } + }, + done() { + resolve_active_and_proceed(); + }, +}; + +states.end_of_year_discard = { + inactive: 'discard cards from hand and tableau', + prompt() { + view.prompt = 'Discard a card'; + const faction_id = get_active_faction(); + const hand = game.hands[faction_id]; + if (hand.length > game.year) { + for (let c of hand) gen_action_card(c); + } + const tableau = game.tableaus[faction_id]; + if (tableau.length > game.year) { + for (let c of tableau) gen_action_card(c); + } + }, + card(c: CardId) { + const faction_id = get_active_faction(); + console.log('list deck', list_deck(faction_id)); + if (game.hands[faction_id].includes(c)) { + game.hands[faction_id] = game.hands[faction_id].filter((id) => id !== c); + } else if (game.tableaus[faction_id].includes(c)) { + game.tableaus[faction_id] = game.tableaus[faction_id].filter( + (id) => id !== c + ); + } + game.discard[faction_id].push(c); + if ( + game.hands[faction_id].length > game.year || + game.tableaus[faction_id].length > game.year + ) { + // More cards to discard so resolve same state again + next(); + } else { + log(`${faction_player_map[faction_id]} discards cards`); + resolve_active_and_proceed(); + } + }, +}; + states.gain_hero_points = { inactive: 'gain Hero Points', prompt() { @@ -833,12 +979,12 @@ states.lose_hero_points = { }, Communist() { const value = get_active_node_args().v; - lose_hero_point(ANARCHISTS_ID, value); + lose_hero_point(COMMUNISTS_ID, value); resolve_active_and_proceed(); }, Moderate() { const value = get_active_node_args().v; - lose_hero_point(ANARCHISTS_ID, value); + lose_hero_point(MODERATES_ID, value); resolve_active_and_proceed(); }, }; @@ -1089,11 +1235,52 @@ function check_initiative() { logi(`${faction_player_map[initiative]} claims the Initiative`); } +function war_is_won() { + let won_fronts = 0; + for (const f of FRONTS) { + if (game.fronts[f].value >= 1) { + won_fronts++; + } + } + return won_fronts >= 3; +} + +function determine_winner() { + const glory = { + [ANARCHISTS_ID]: 0, + [COMMUNISTS_ID]: 0, + [MODERATES_ID]: 0, + }; + for (const g of game.glory) { + glory[g]++; + } + let highest_glory = 0; + let winners = []; + for (let f of role_ids) { + if (glory[f] === highest_glory) { + winners.push(f); + } else if (glory[f] > highest_glory) { + highest_glory = glory[f]; + winners = [f]; + } + } + if (winners.length === 1) { + win_game(faction_player_map[winners[0]], highest_glory); + } else { + insert_after_active_node( + create_leaf_node('break_tie_winner', game.initiative, { + winners, + glory: highest_glory, + }) + ); + } + resolve_active_and_proceed(); +} + function end_of_turn() { Object.keys(game.fronts).forEach((front_id) => { game.fronts[front_id].contributions = []; }); - // log_h2('End of turn'); if (game.turn === 4) { end_of_year(); } else { @@ -1103,15 +1290,47 @@ function end_of_turn() { } function end_of_year() { - const gloryToDraw = [0, 1, 2, 5]; - for (let i = 0; i < gloryToDraw[game.year]; ++i) { + if (game.year === 3) { + log_h1('End of the game'); + const is_won = war_is_won(); + if (is_won) { + log('The war is won!'); + } else { + game_over('None', 'The war is lost. All Players lose the game!'); + return; + } + } + const glory_to_draw = [0, 1, 2, 5]; + const glory_this_year: Record<FactionId, boolean> = { + [ANARCHISTS_ID]: false, + [COMMUNISTS_ID]: false, + [MODERATES_ID]: false, + }; + for (let i = 0; i < glory_to_draw[game.year]; ++i) { const index = random(game.bag_of_glory.length); - game.glory.push(game.bag_of_glory[index]); + const faction = game.bag_of_glory[index]; + game.glory.push(faction); + glory_this_year[faction] = true; array_remove(game.bag_of_glory, index); } - game.year++; - start_year(); + if (game.year === 3) { + // end of game + determine_winner(); + return; + } + + const players_to_gain_hero_points = role_ids.filter( + (f) => !glory_this_year[f] + ); + gain_hero_points_in_player_order(players_to_gain_hero_points, game.year); + + // Setup card discarding + game.engine = get_player_order().map((f) => + create_leaf_node('end_of_year_discard', f) + ); + game.engine.push(create_function_node('start_year')); + next(); } function gain_hero_points_in_player_order(factions: FactionId[], value) { @@ -1169,6 +1388,37 @@ function resolve_fascist_test() { resolve_active_and_proceed(); } +function resolve_final_bid() { + let highest_bid = 0; + let winners: FactionId[] = []; + for (const f of get_player_order()) { + let player_bid = 0; + for (const c of game.final_bid[f]) { + player_bid += (cards[c] as PlayerCard).strength; + } + log(`${faction_player_map[f]} bids ${player_bid}`); + if (player_bid === highest_bid) { + winners.push(f); + } else if (player_bid > highest_bid) { + highest_bid = player_bid; + winners = [f]; + } + game.hands[f] = game.hands[f].filter((c) => !game.final_bid[f].includes(c)); + game.discard[f].concat(game.final_bid[f]); + game.final_bid[f] = []; + } + + if (winners.length === 1) { + win_final_bid(winners[0]); + } else { + insert_after_active_node( + create_leaf_node('break_tie_final_bid', game.initiative, { winners }) + ); + } + + resolve_active_and_proceed(); +} + // TODO: check for defeated / won fronts function get_fronts_to_add_to(target: string): FrontId[] { console.log('get_fronts_to_add_to', target); @@ -1386,6 +1636,16 @@ function resolve_effect( return state === undefined ? null : create_leaf_node(state, faction, args); } +function win_final_bid(faction_id: FactionId) { + log_br(); + log(`${faction_player_map[faction_id]} wins the Final Bid`); + game.glory.push(faction_id); +} + +function win_game(player: Player, glory: number) { + game_over(player, `${player} wins the game with a total of ${glory} Glory!`); +} + // #endregion // #region CARDS @@ -1662,7 +1922,9 @@ function list_deck(id: FactionId | 'fascist') { return; } else if ( id !== 'fascist' && - (game.hands[id].includes(card) || game.discard[id].includes(card)) + (game.hands[id].includes(card) || + game.discard[id].includes(card) || + game.tableaus[id].includes(card)) ) { return; } |