diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 188 |
1 files changed, 105 insertions, 83 deletions
@@ -79,7 +79,7 @@ function gen_spend_hero_points() { } } function action(state, player, action, arg) { - if (action !== 'undo') { + if (action !== 'undo' && state.state !== 'choose_card') { state.undo = push_undo(); } game = state; @@ -92,13 +92,13 @@ function action(state, player, action, arg) { throw new Error('Invalid action: ' + action); return game; } -const leaf_node = 'l'; +const state_node = 'l'; const seq_node = 's'; const function_node = 'f'; const resolved = 1; -function create_leaf_node(state, faction, args) { +function create_state_node(state, faction, args) { return { - t: leaf_node, + t: state_node, s: state, p: faction, a: args, @@ -121,27 +121,27 @@ function create_seq_node(children) { } function checkpoint() { if (game.undo.length > 0) { - insert_after_active_node(create_leaf_node('confirm_turn', get_active_faction())); + insert_after_active_node(create_state_node('confirm_turn', get_active_faction())); } resolve_active_and_proceed(); } function setup_bag_of_glory() { log_h1('Bag of Glory'); game.engine = [ - create_leaf_node('add_glory', game.initiative), + create_state_node('add_glory', game.initiative), create_function_node('end_of_turn'), ]; next(); } function setup_choose_card() { - game.engine = [create_leaf_node('choose_card', 'all')]; + game.engine = [create_state_node('choose_card', 'all')]; game.engine.push(create_function_node('setup_player_turn')); 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 = player_order.map((faction_id) => create_state_node('choose_final_bid', faction_id)); game.engine.push(create_function_node('checkpoint')); game.engine.push(create_function_node('resolve_final_bid')); game.engine.push(create_function_node('setup_choose_card')); @@ -156,7 +156,7 @@ function setup_player_turn() { } game.engine = [ create_function_node('start_of_player_turn', { f: next_faction }), - create_leaf_node('player_turn', next_faction), + create_state_node('player_turn', next_faction), create_function_node('end_of_player_turn', { f: next_faction }), ]; next(); @@ -165,7 +165,7 @@ function check_end_of_year_discard() { const { f: faction } = get_active_node_args(); if (game.hands[faction].length > get_hand_limit(faction) || game.tableaus[faction].length > game.year) { - insert_after_active_node(create_leaf_node('end_of_year_discard', faction)); + insert_after_active_node(create_state_node('end_of_year_discard', faction)); } resolve_active_and_proceed(); } @@ -173,7 +173,7 @@ function end_of_player_turn() { const { f: faction } = get_active_node_args(); if (get_next_faction_in_player_order(faction) === game.first_player) { game.engine = [ - create_leaf_node('change_active_player', game.initiative), + create_state_node('change_active_player', game.initiative), create_function_node('resolve_fascist_test'), create_function_node('setup_bag_of_glory'), ]; @@ -227,7 +227,7 @@ const engine_functions = { }; function get_active(engine) { for (let i of engine) { - if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) { + if ((i.t === state_node || i.t === function_node) && i.r !== resolved) { return { parent: engine, node: i }; } if (i.t === seq_node) { @@ -246,7 +246,7 @@ function get_active_node(engine = game.engine) { function get_nodes_for_state(state, engine = game.engine) { let nodes = []; for (let i of engine) { - if (i.t === leaf_node && i.s === state) { + if (i.t === state_node && i.s === state) { nodes.push(i); } if (i.t === seq_node) { @@ -257,14 +257,14 @@ function get_nodes_for_state(state, engine = game.engine) { } function get_active_node_args() { const node = get_active_node(game.engine); - if (node.t === leaf_node || node.t === function_node) { + if (node.t === state_node || node.t === function_node) { return node.a ?? {}; } return null; } function update_active_node_args(args) { const node = get_active_node(game.engine); - if (node.t === leaf_node || node.t === function_node) { + if (node.t === state_node || node.t === function_node) { node.a = { ...node.a, ...args, @@ -320,7 +320,7 @@ function next(checkpoint = false) { const current_active = game.active; 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())); + insert_before_active_node(create_state_node('confirm_turn', get_active_faction())); game.state = 'confirm_turn'; return; } @@ -380,17 +380,20 @@ function game_view(state, current) { if (game.state === 'game_over') { view.prompt = game.victory; } - else if (current !== game.active) { + else if (current !== game.active && + !game.active.includes(current)) { let inactive = states[game.state].inactive || game.state; - view.prompt = `Waiting for ${game.active} to ${inactive}.`; + view.prompt = Array.isArray(game.active) + ? `Waiting for other players to ${inactive}.` + : `Waiting for ${game.active} to ${inactive}.`; } else { view.actions = {}; - states[game.state].prompt(); if (game.undo && game.undo.length > 0) view.actions.undo = 1; else view.actions.undo = 0; + states[game.state].prompt(current); } return view; } @@ -560,7 +563,7 @@ states.activate_icon = { resolve_spend_hp(); }, add_to_front() { - insert_after_active_node(create_leaf_node('add_to_front', get_active_faction(), { + insert_after_active_node(create_state_node('add_to_front', get_active_faction(), { t: data_1.ANY, v: get_icon_count_in_tableau('add_to_front'), })); @@ -890,15 +893,10 @@ states.choose_area_ap = { resolve_active_and_proceed(); }, standee(track_id) { - insert_after_active_node({ - t: leaf_node, - p: get_active_faction(), - s: 'move_track_up_or_down', - a: { - track_id, - strength: get_active_node()?.a.strength, - }, - }); + insert_after_active_node(create_state_node('move_track_up_or_down', get_active_faction(), { + track_id, + strength: get_active_node()?.a.strength, + })); resolve_active_and_proceed(); }, }; @@ -938,17 +936,12 @@ states.change_bonus = { resolve_active_and_proceed(); }, }; -states.choose_card = { +states.play_card = { inactive: 'choose a card', - prompt(player) { - console.log('player', player); + prompt() { gen_spend_hero_points(); - const { src } = get_active_node_args(); - view.prompt = 'Choose a card to play this turn'; - if (src === 'momentum') { - view.prompt = 'Choose a card to play'; - } - const faction = player_faction_map[player]; + view.prompt = 'Choose a card to play'; + const faction = get_active_faction(); const hand = game.hands[faction]; for (let c of hand) { if (!game.selected_cards[faction].includes(c)) { @@ -959,12 +952,49 @@ states.choose_card = { spend_hp() { resolve_spend_hp(); }, + card(c) { + const faction = get_active_faction(); + game.selected_cards[faction].push(c); + game.played_card = game.selected_cards[faction][0]; + resolve_active_and_proceed(); + }, +}; +states.choose_card = { + inactive: 'choose a card', + prompt(player) { + gen_spend_hero_points(); + view.prompt = 'Choose a card to play this turn'; + const faction = player_faction_map[player]; + if (game.selected_cards[faction].length === 0) { + view.actions.undo = 0; + const hand = game.hands[faction]; + for (let c of hand) { + if (!game.selected_cards[faction].includes(c)) { + gen_action_card(c); + } + } + } + else { + view.actions.undo = 1; + view.actions.confirm = 1; + view.prompt = 'Confirm your actions or undo'; + } + }, + spend_hp() { + resolve_spend_hp(); + }, card(c, 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]; + }, + undo(_, player) { + const faction = player_faction_map[player]; + game.selected_cards[faction] = []; + }, + confirm(_, player) { + set_delete(game.active, player); + if (game.active.length === 0) { + resolve_active_and_proceed(); } }, }; @@ -1004,7 +1034,6 @@ function setup_momentum() { return; } const node = get_nodes_for_state('player_turn')[0]; - console.log('node', node); const player_needs_to_play_card = game.selected_cards[faction].length > 0; const { use_ap, use_morale_bonus, resolving_event } = node.a ?? {}; if (player_needs_to_play_card || @@ -1017,7 +1046,7 @@ function setup_momentum() { }; } else { - insert_before_active_node(create_leaf_node('choose_card', faction, { + insert_after_active_node(create_state_node('play_card', faction, { src: 'momentum', })); } @@ -1393,17 +1422,11 @@ states.peek_fascist_cards = { }, }; function resolve_spend_hp() { - insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction())); + insert_before_active_node(create_state_node('spend_hero_points', get_active_faction())); log('Spends Hero Points'); next(); } function set_player_turn_prompt({ can_play_card, can_spend_hp, use_ap, use_momentum, use_morale_bonus, }) { - console.log('set_player_turn_prompt', { - can_play_card, - use_ap, - use_morale_bonus, - use_momentum, - }); if (can_play_card && can_spend_hp) { view.prompt = 'Play a card or spend Hero points'; } @@ -1446,7 +1469,6 @@ states.player_turn = { use_morale_bonus = use_morale_bonus && game.bonuses[data_1.MORALE_BONUS] === data_1.ON; const can_spend_hp = game.faction_turn === faction_id && game.hero_points[faction_id] > 0; const can_play_card = game.selected_cards[faction_id].length > 0; - console.log('can_play_card', can_play_card); if (use_momentum) { gen_action('use_momentum'); if (use_ap || use_morale_bonus || can_play_card) { @@ -1509,7 +1531,7 @@ states.player_turn = { update_active_node_args({ use_ap: false, }); - insert_before_active_node(create_leaf_node('choose_area_ap', faction, { + insert_before_active_node(create_state_node('choose_area_ap', faction, { strength, })); next(); @@ -1526,7 +1548,7 @@ states.player_turn = { update_active_node_args({ use_morale_bonus: false, }); - insert_before_active_node(create_leaf_node('activate_icon', get_active_faction())); + insert_before_active_node(create_state_node('activate_icon', get_active_faction())); next(); }, }; @@ -1593,7 +1615,7 @@ states.remove_attack_from_fronts = { resolve_active_and_proceed(); } else if (card_id === 39 || card_id === 16) { - insert_after_active_node(create_leaf_node('attack_front', get_active_faction(), { + insert_after_active_node(create_state_node('attack_front', get_active_faction(), { t: data_1.ANY, v: card_id === 39 ? -2 : -1 * removed_value, n: card_id === 16 ? id : undefined, @@ -1605,7 +1627,7 @@ states.remove_attack_from_fronts = { const { f, v: card_id } = get_active_node_args(); const values = Object.values(f ?? {}); if (card_id === 39 && values.length > 0) { - insert_after_active_node(create_leaf_node('attack_front', get_active_faction(), { + insert_after_active_node(create_state_node('attack_front', get_active_faction(), { t: data_1.ANY, v: -2, })); @@ -1712,11 +1734,11 @@ states.spend_hero_points = { const faction = get_active_faction(); pay_hero_points(faction, 1); insert_after_active_node(create_seq_node([ - create_leaf_node('add_to_front', faction, { + create_state_node('add_to_front', faction, { t: data_1.ANY, v: 1, }), - create_leaf_node('spend_hero_points', faction), + create_state_node('spend_hero_points', faction), ])); resolve_active_and_proceed(); }, @@ -1752,8 +1774,8 @@ states.spend_hero_points = { game.used_medallions = [data_1.ARCHIVES_MEDALLION_ID]; } insert_after_active_node(create_seq_node([ - create_leaf_node('remove_blank_marker', faction), - create_leaf_node('spend_hero_points', faction), + create_state_node('remove_blank_marker', faction), + create_state_node('spend_hero_points', faction), ])); resolve_active_and_proceed(); }, @@ -1771,11 +1793,11 @@ states.spend_hero_points = { const faction = get_active_faction(); pay_hero_points(faction, amount); insert_after_active_node(create_seq_node([ - create_leaf_node('move_track_up_or_down', faction, { + create_state_node('move_track_up_or_down', faction, { track_id, strength: 1, }), - create_leaf_node('spend_hero_points', faction), + create_state_node('spend_hero_points', faction), ])); resolve_active_and_proceed(); }, @@ -2096,7 +2118,7 @@ function determine_winner() { win_game(faction_player_map[winners[0]], highest_glory); } else { - insert_after_active_node(create_leaf_node('break_tie_winner', game.initiative, { + insert_after_active_node(create_state_node('break_tie_winner', game.initiative, { winners, glory: highest_glory, })); @@ -2143,7 +2165,7 @@ function end_of_year() { const player_order = get_player_order(); const engine = []; for (let i = 0; i < glory_to_draw[game.year]; ++i) { - engine.push(create_leaf_node('draw_glory', player_order[i % 3])); + engine.push(create_state_node('draw_glory', player_order[i % 3])); } engine.push(create_function_node('end_of_year_cleanup')); game.engine = engine; @@ -2200,7 +2222,7 @@ function gain_hero_points(faction_id, value, skip_abilities = false) { logi(`${get_player(faction_id)} +${gain} ${gain === 1 ? 'Hero Point' : 'Hero Points'}`); } function game_over(result, victory) { - insert_after_active_node(create_leaf_node('game_over', 'None')); + insert_after_active_node(create_state_node('game_over', 'None')); game.result = result; game.victory = victory; game.undo = []; @@ -2290,7 +2312,7 @@ function resolve_final_bid() { win_final_bid(winners[0]); } else { - insert_after_active_node(create_leaf_node('break_tie_final_bid', game.initiative, { winners })); + insert_after_active_node(create_state_node('break_tie_final_bid', game.initiative, { winners })); } resolve_active_and_proceed(); } @@ -2391,7 +2413,7 @@ function can_use_medallion(medallion_id, faction) { } function insert_use_organization_medallion_node(track_id, value) { const faction = get_active_faction(); - insert_after_active_node(create_leaf_node('use_organization_medallion', faction, { + insert_after_active_node(create_state_node('use_organization_medallion', faction, { t: track_id, v: value, })); @@ -2429,7 +2451,7 @@ function update_front(front_id, change, faction_id = null) { faction_id !== undefined && game.fronts[front_id].value < 10 && can_use_medallion(data_1.STRATEGY_MEDALLION_ID)) { - insert_after_active_node(create_leaf_node('use_strategy_medallion', get_active_faction(), { + insert_after_active_node(create_state_node('use_strategy_medallion', get_active_faction(), { f: front_id, })); } @@ -2501,87 +2523,87 @@ function resolve_effect(effect, source) { return create_function_node(effect.target); } if (effect.type === 'state') { - return create_leaf_node(effect.target, faction, { + return create_state_node(effect.target, faction, { v: effect.value, src: source, }); } let state = effect_type_state_map[effect.type]; if (state !== undefined) { - return create_leaf_node(state, faction, args); + return create_state_node(state, faction, args); } const strategies = [ { condition: effect.type === 'hero_points' && effect.target === data_1.PLAYER_WITH_MOST_HERO_POINTS, resolve: () => { - return create_leaf_node('select_player_with_most_hero_points', faction, args); + return create_state_node('select_player_with_most_hero_points', faction, args); }, }, { condition: effect.type === 'hero_points' && effect.target === data_1.ALL_PLAYERS, resolve: () => { - return create_seq_node(get_player_order().map((faction) => create_leaf_node('hero_points', faction, args))); + return create_seq_node(get_player_order().map((faction) => create_state_node('hero_points', faction, args))); }, }, { condition: effect.type === 'hero_points' && effect.target === data_1.SELF, resolve: () => { - return create_leaf_node('hero_points', faction, args); + return create_state_node('hero_points', faction, args); }, }, { condition: effect.type === 'hero_points' && role_ids.includes(effect.target), resolve: () => { - return create_leaf_node('hero_points', effect.target, args); + return create_state_node('hero_points', effect.target, args); }, }, { condition: effect.type === 'hero_points' && effect.target === data_1.INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('hero_points', game.initiative, args); + return create_state_node('hero_points', game.initiative, args); }, }, { condition: effect.type === 'draw_card' && effect.target === data_1.SELF, resolve: () => { - return create_leaf_node('draw_card', faction, args); + return create_state_node('draw_card', faction, args); }, }, { condition: effect.type === 'draw_card' && effect.target === data_1.INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('draw_card', game.initiative, args); + return create_state_node('draw_card', game.initiative, args); }, }, { condition: effect.type === 'draw_card' && role_ids.includes(effect.target), resolve: () => { - return create_leaf_node('draw_card', effect.target, args); + return create_state_node('draw_card', effect.target, args); }, }, { condition: effect.type === 'draw_card' && effect.target === data_1.ALL_PLAYERS, resolve: () => { - return create_seq_node(get_player_order(get_active_faction()).map((faction) => create_leaf_node('draw_card', faction, args))); + return create_seq_node(get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args))); }, }, { condition: effect.type === 'draw_card' && effect.target === data_1.OTHER_PLAYERS, resolve: () => { - const leaf_nodes = get_player_order(get_active_faction()).map((faction) => create_leaf_node('draw_card', faction, args)); - array_remove(leaf_nodes, 0); - return create_seq_node(leaf_nodes); + const state_nodes = get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args)); + array_remove(state_nodes, 0); + return create_seq_node(state_nodes); }, }, { condition: effect.type === 'play_card', resolve: () => { return create_seq_node([ - create_leaf_node('choose_card', faction, { src: source }), - create_leaf_node('player_turn', faction, { src: source }), + create_state_node('play_card', faction, { src: source }), + create_state_node('player_turn', faction, { src: source }), ]); }, }, |