From dbd1660fc0f297d2b7b571af6038e53d6596161c Mon Sep 17 00:00:00 2001 From: Frans Bongers Date: Sun, 16 Feb 2025 19:51:08 +0100 Subject: initial multiactive updates --- rules.ts | 145 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 89 insertions(+), 56 deletions(-) (limited to 'rules.ts') diff --git a/rules.ts b/rules.ts index db25c1b..e47abe9 100644 --- a/rules.ts +++ b/rules.ts @@ -224,7 +224,7 @@ const resolved = 1; function create_leaf_node( state: string, - faction: FactionId | 'None', + faction: FactionId | 'None' | 'all' | FactionId[], args?: T ): LeafNode { return { @@ -272,13 +272,15 @@ function setup_bag_of_glory() { } function setup_choose_card() { - const player_order = get_player_order(); - game.engine = player_order.map((faction_id) => - create_leaf_node('choose_card', faction_id) - ); + game.engine = [create_leaf_node('choose_card', 'all')]; + + // const player_order = get_player_order(); + // game.engine = player_order.map((faction_id) => + // create_leaf_node('choose_card', faction_id) + // ); // Add extra confirm, otherwise first players card will be revealed // before confirm - game.engine.push(create_leaf_node('confirm_turn', player_order[2])); + // game.engine.push(create_leaf_node('confirm_turn', player_order[2])); game.engine.push(create_function_node('setup_player_turn')); next(); } @@ -474,6 +476,20 @@ function insert_before_active_node( insert_before_or_after_active_node(node, 'before', engine); } +function get_next_active(p: LeafNode['p']): Player | Player[] | 'None' { + if (Array.isArray(p)) { + return p.map((faction) => faction_player_map[faction]); + } + if (p === 'all') { + return roles; + } + if (p === 'None') { + return 'None'; + } else { + return faction_player_map[p]; + } +} + function next(checkpoint = false) { if (checkpoint) { clear_undo(); @@ -492,7 +508,9 @@ function next(checkpoint = false) { // Control switches to another player and player can undo // so ask to confirm turn const current_active = game.active; - const next_active = faction_player_map[node.p]; + + const next_active = get_next_active(node.p); + if (next_active !== current_active && game.undo.length > 0) { insert_before_active_node( create_leaf_node('confirm_turn', get_active_faction()) @@ -532,6 +550,7 @@ function game_view(state: Game, current: Player | 'Observer') { current === OBSERVER ? null : player_faction_map[current]; view = { + active: game.active, engine: game.engine, // TODO: remove log: game.log, prompt: null, @@ -694,25 +713,28 @@ function draw_hand_cards(faction_id: FactionId, count: number) { if (game.medallions[faction_id].includes(INTELLIGENCE_MEDALLION_ID)) { count++; } - const drawn_cards = count; + let drawn_cards = 0; // Draw all remaining cards if (deck.length < count) { count = count - deck.length; + drawn_cards += deck.length; game.hands[faction_id] = game.hands[faction_id].concat(deck); game.discard[faction_id] = []; } + for (let i = 0; i < count; i++) { + const deck = list_deck(faction_id); + if (deck.length > 0) { + game.hands[faction_id].push(draw_card(deck)); + drawn_cards++; + } + } const log = drawn_cards === 1 ? `${get_player(faction_id)} draws 1 card` : `${get_player(faction_id)} draws ${drawn_cards} cards`; logi(log); - - for (let i = 0; i < count; i++) { - const deck = list_deck(faction_id); - game.hands[faction_id].push(draw_card(deck)); - } } // #endregion @@ -956,10 +978,18 @@ states.add_to_front = { gen_spend_hero_points(); const args = get_active_node_args(); const possible_fronts = get_fronts_to_add_to(args.t); - view.prompt = - possible_fronts.length === 1 - ? `Add strength to ${front_names[possible_fronts[0]]}` - : 'Add strength to a Front'; + + const number_of_fronts = possible_fronts.length; + + if (number_of_fronts === 0) { + view.prompt = 'No valid front to target. You must skip'; + gen_action('skip'); + } else if (number_of_fronts === 1) { + view.prompt = `Add strength to ${front_names[possible_fronts[0]]}`; + } else { + view.prompt = 'Add strength to a Front'; + } + for (let f of possible_fronts) { gen_action_front(f); } @@ -975,6 +1005,9 @@ states.add_to_front = { update_front(f, value, get_active_faction()); resolve_active_and_proceed(); }, + skip() { + resolve_active_and_proceed(); + }, }; states.attack_front = { @@ -983,47 +1016,35 @@ states.attack_front = { gen_spend_hero_points(); const { t: target, n, src } = get_active_node_args(); - let fronts: Array = []; + const possible_fronts = get_fronts_to_add_to(target, n); + const number_of_fronts = possible_fronts.length; - if (target === ANY) { - fronts = get_fronts_to_add_to(ANY, n); - } else if (target === 'd' || target === 'v') { - fronts = get_fronts_closest_to(target); - } else if (game.fronts[target].status === DEFEAT) { - fronts = get_fronts_closest_to('d'); - } else if (game.fronts[target].status === VICTORY) { - fronts = get_fronts_to_add_to(ANY); + if (number_of_fronts === 0) { + view.prompt = 'No valid front to target. You must skip'; + gen_action('skip'); + } else if (number_of_fronts === 1) { + view.prompt = `Attack ${front_names[possible_fronts[0]]}`; } else { - fronts.push(target); + view.prompt = 'Attack a Front'; } - view.prompt = - fronts.length === 1 - ? `Attack ${front_names[fronts[0]]}` - : 'Attack a front'; - let prefix = ''; if (src) { - prefix = `${get_source_name(src)}: `; - } - if (fronts.length === 1) { - view.prompt = prefix - ? `${prefix}attack ${front_names[fronts[0]]}` - : `Attack ${front_names[fronts[0]]}`; - } else { - view.prompt = prefix ? `${prefix}attack a front` : `Attack a front`; + view.prompt = add_prompt_prefix(view.prompt, get_source_name(src)); } - fronts.forEach((id) => gen_action('front', id)); + possible_fronts.forEach((id) => gen_action('front', id)); }, spend_hp() { resolve_spend_hp(); }, front(f: FrontId) { - const node = get_active_node(); - const value = node.a.v; + const value = get_active_node_args().v; update_front(f, value); resolve_active_and_proceed(); }, + skip() { + resolve_active_and_proceed(); + }, }; states.break_tie_final_bid = { @@ -1076,7 +1097,7 @@ states.break_tie_winner = { }; /** - * Change does not do anything, but it will change + * Change does not do anything, but it will change * active player and trigger a confirm for the previous * state */ @@ -1087,8 +1108,8 @@ states.change_active_player = { }, prompt() { view.prompt = ''; - } -} + }, +}; states.choose_area_ap = { inactive: 'choose area to use Action Points', @@ -1186,7 +1207,8 @@ states.change_bonus = { states.choose_card = { inactive: 'choose a card', - prompt() { + prompt(player: Player) { + console.log('player', player); gen_spend_hero_points(); const { src } = get_active_node_args(); view.prompt = 'Choose a card to play this turn'; @@ -1194,7 +1216,7 @@ states.choose_card = { view.prompt = 'Choose a card to play'; } - const faction = get_active_faction(); + const faction = player_faction_map[player]; const hand = game.hands[faction]; for (let c of hand) { if (!game.selected_cards[faction].includes(c)) { @@ -1205,14 +1227,14 @@ states.choose_card = { spend_hp() { resolve_spend_hp(); }, - card(c: CardId) { - const faction = get_active_faction(); + card(c: CardId, player: Player) { + const faction = player_faction_map[player]; game.selected_cards[faction].push(c); const { src } = get_active_node_args(); if (src === 'momentum') { game.played_card = game.selected_cards[faction][0]; } - resolve_active_and_proceed(); + // resolve_active_and_proceed(); }, }; @@ -1362,12 +1384,12 @@ states.draw_card = { draw_card() { const { v } = get_active_node_args(); draw_hand_cards(get_active_faction(), v); - resolve_active_and_proceed(); + resolve_active_and_proceed(true); }, draw_cards() { const { v } = get_active_node_args(); draw_hand_cards(get_active_faction(), v); - resolve_active_and_proceed(); + resolve_active_and_proceed(true); }, }; @@ -1384,7 +1406,7 @@ states.draw_glory = { game.glory.push(faction); - // TODO: remove if statement, just here atm to not break + // TODO: remove if statement, just here atm to not break // running games if (!game.glory_current_year) { game.glory_current_year = game.glory_current_year = { @@ -1397,7 +1419,11 @@ states.draw_glory = { array_remove(game.bag_of_glory, index); - log(`${get_player(get_active_faction())} draws from the Bag of Glory`) + log( + `${get_player( + get_active_faction() + )} draws from the Bag of Glory` + ); resolve_active_and_proceed(true); }, }; @@ -3299,6 +3325,13 @@ function get_fronts_closest_to(target: 'd' | 'v'): FrontId[] { }, [] ); + + // Possible if all fronts have either been + // defeated or are victorious + if (values.length === 0) { + return []; + } + const targetValue = target === 'd' ? Math.min(...values) : Math.max(...values); return Object.keys(game.fronts).filter( @@ -3382,7 +3415,7 @@ function add_prompt_prefix(prompt: string, prefix: string) { } function get_active_faction(): FactionId { - return player_faction_map[game.active]; + return player_faction_map[game.active as Player]; } function get_blank_marker_id(track_id: number, space_id: number) { -- cgit v1.2.3