diff options
author | Frans Bongers <fransbongers@franss-mbp.home> | 2024-12-30 22:21:32 +0100 |
---|---|---|
committer | Frans Bongers <fransbongers@franss-mbp.home> | 2024-12-30 22:21:32 +0100 |
commit | 4148cddd0370fa1ea9b9580a79ac1c73109c88a1 (patch) | |
tree | 6f94af6ca4c45bed4c9a8da9cf4adb2e24700270 /rules.js | |
parent | 2122adacc6c569e78bc71b049ea5cdffe13208a5 (diff) | |
download | land-and-freedom-4148cddd0370fa1ea9b9580a79ac1c73109c88a1.tar.gz |
spend hero points at any point during a turn
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 185 |
1 files changed, 144 insertions, 41 deletions
@@ -70,6 +70,13 @@ function gen_action_medallion(medallion_id) { function gen_action_standee(track_id) { gen_action('standee', track_id); } +function gen_spend_hero_points() { + const faction = get_active_faction(); + const can_spend_hp = game.faction_turn === faction && game.hero_points[faction] > 0; + if (can_spend_hp) { + gen_action('spend_hp'); + } +} function action(state, player, action, arg) { console.log('action', player, action, arg); if (action !== 'undo') { @@ -141,13 +148,29 @@ function setup_final_bid() { next(); } function setup_player_turn() { - const player_order = get_player_order(); - game.engine = player_order.map((faction_id) => create_seq_node([ - create_function_node('start_of_player_turn', { f: faction_id }), - create_leaf_node('player_turn', faction_id), - ])); - game.engine.push(create_function_node('resolve_fascist_test')); - game.engine.push(create_function_node('setup_bag_of_glory')); + const next_faction = game.first_player === null + ? get_player_order()[0] + : get_next_faction(get_active_faction()); + if (game.first_player === null) { + game.first_player = next_faction; + } + game.engine = [ + create_function_node('start_of_player_turn', { f: next_faction }), + create_leaf_node('player_turn', next_faction), + create_function_node('end_of_player_turn', { f: next_faction }), + ]; + next(); +} +function end_of_player_turn() { + if (get_next_faction(get_active_faction()) === game.first_player) { + game.engine = [ + create_function_node('resolve_fascist_test'), + create_function_node('setup_bag_of_glory'), + ]; + } + else { + game.engine = [create_function_node('setup_player_turn')]; + } next(); } function start_of_player_turn() { @@ -160,6 +183,7 @@ function start_of_player_turn() { const engine_functions = { check_activate_icon, checkpoint, + end_of_player_turn, end_of_turn, setup_bag_of_glory, setup_choose_card, @@ -357,6 +381,7 @@ function setup(seed, _scenario, _options) { }, }, glory: [], + first_player: null, hands: { a: [], c: [], @@ -453,12 +478,16 @@ function start_turn() { states.activate_icon = { inactive: 'activate an icon', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose an icon to activate'; const c = cards[game.played_card]; for (const i of c.icons) { gen_action(i); } }, + spend_hp() { + resolve_spend_hp(); + }, add_to_front() { insert_after_active_node(create_leaf_node('add_to_front', get_active_faction(), { t: data_1.ANY, @@ -584,6 +613,7 @@ states.activate_icon = { states.add_card_to_tableau = { inactive: 'add a card to their tableau', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose a card to add to your tableau'; const faction = get_active_faction(); for (const c of game.hands[faction]) { @@ -593,6 +623,9 @@ states.add_card_to_tableau = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, card(c) { const faction_id = get_active_faction(); const card = cards[c]; @@ -608,9 +641,13 @@ states.add_card_to_tableau = { states.add_glory = { inactive: 'add tokens to the Bag of Glory', prompt() { + gen_spend_hero_points(); view.prompt = 'Add tokens to the Bag of Glory'; gen_action('add_glory'); }, + spend_hp() { + resolve_spend_hp(); + }, add_glory() { let number = 1; if (game.turn === 4) { @@ -623,6 +660,7 @@ states.add_glory = { states.add_to_front = { inactive: 'add strength to a Front', prompt() { + gen_spend_hero_points(); const args = get_active_node_args(); const possible_fronts = get_fronts_to_add_to(args.t); view.prompt = @@ -633,6 +671,9 @@ states.add_to_front = { gen_action_front(f); } }, + spend_hp() { + resolve_spend_hp(); + }, front(f) { const value = get_active_node_args().v; update_front(f, value, get_active_faction()); @@ -642,6 +683,7 @@ states.add_to_front = { states.attack_front = { inactive: 'attack a Front', prompt() { + gen_spend_hero_points(); const { t: target, n } = get_active_node_args(); let fronts = []; if (target === data_1.ANY) { @@ -665,6 +707,9 @@ states.attack_front = { : 'Attack a front'; fronts.forEach((id) => gen_action('front', id)); }, + spend_hp() { + resolve_spend_hp(); + }, front(f) { const node = get_active_node(); const value = node.a.v; @@ -722,6 +767,7 @@ states.break_tie_winner = { states.choose_area_ap = { inactive: 'choose area to use Action Points', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose area of the board to affect'; for (const track of tracks) { gen_action_standee(track.id); @@ -736,6 +782,9 @@ states.choose_area_ap = { } } }, + spend_hp() { + resolve_spend_hp(); + }, bonus(b) { update_bonus(b, data_1.ON); const s = get_active_node_args().strength; @@ -747,13 +796,13 @@ states.choose_area_ap = { }, front(f) { const s = get_active_node_args().strength; - update_front(f, s, get_active_faction_id()); + update_front(f, s, get_active_faction()); resolve_active_and_proceed(); }, standee(track_id) { insert_after_active_node({ t: leaf_node, - p: get_active_faction_id(), + p: get_active_faction(), s: 'move_track_up_or_down', a: { track_id, @@ -766,6 +815,7 @@ states.choose_area_ap = { states.change_bonus = { inactive: 'select Bonus', prompt() { + gen_spend_hero_points(); const args = get_active_node_args(); if ((args.v === data_1.ON && game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON && @@ -786,6 +836,9 @@ states.change_bonus = { gen_action_bonus(args.t); } }, + spend_hp() { + resolve_spend_hp(); + }, bonus(b) { const value = get_active_node_args().v; update_bonus(b, value); @@ -798,6 +851,7 @@ states.change_bonus = { states.choose_card = { inactive: 'choose a card', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose a card to play this turn'; const faction = get_active_faction(); const hand = game.hands[faction]; @@ -807,6 +861,9 @@ states.choose_card = { } } }, + spend_hp() { + resolve_spend_hp(); + }, card(c) { const faction = get_active_faction(); game.selected_cards[faction].push(c); @@ -847,6 +904,7 @@ states.choose_final_bid = { states.choose_medallion = { inactive: 'choose a medallion', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose a medallion'; for (let m of game.medallions.pool) { gen_action_medallion(m); @@ -855,6 +913,9 @@ states.choose_medallion = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, medallion(m) { const faction = get_active_faction(); const medallion = medallions[m]; @@ -893,10 +954,14 @@ states.confirm_turn = { states.draw_card = { inactive: 'draw a card', prompt() { + gen_spend_hero_points(); const { v } = get_active_node_args(); view.prompt = v === 1 ? 'Draw a card' : `Draw ${v} cards`; gen_action(v === 1 ? 'draw_card' : 'draw_cards'); }, + spend_hp() { + resolve_spend_hp(); + }, draw_card() { const { v } = get_active_node_args(); draw_hand_cards(get_active_faction(), v); @@ -943,9 +1008,10 @@ states.end_of_year_discard = { } }, }; -states.gain_hero_points = { +states.hero_points = { inactive: 'gain Hero Points', prompt() { + gen_spend_hero_points(); const value = get_active_node_args().v; if (value < 0) { view.prompt = @@ -965,6 +1031,9 @@ states.gain_hero_points = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, gain_hp() { const value = get_active_node_args().v; gain_hero_points(get_active_faction(), value); @@ -972,7 +1041,7 @@ states.gain_hero_points = { }, lose_hp() { const value = get_active_node_args().v; - lose_hero_point(get_active_faction(), value); + lose_hero_points(get_active_faction(), value); resolve_active_and_proceed(); }, skip() { @@ -990,7 +1059,7 @@ states.game_over = { function resolve_player_with_most_hero_points(faction) { const value = get_active_node_args().v; if (value < 0) { - lose_hero_point(faction, value); + lose_hero_points(faction, value); } else { gain_hero_points(faction, value); @@ -1000,6 +1069,7 @@ function resolve_player_with_most_hero_points(faction) { states.select_player_with_most_hero_points = { inactive: 'choose a Player', prompt() { + gen_spend_hero_points(); const { v } = get_active_node_args(); view.prompt = v < 0 @@ -1010,6 +1080,9 @@ states.select_player_with_most_hero_points = { gen_action(faction_player_map[faction_id]); } }, + spend_hp() { + resolve_spend_hp(); + }, Anarchist() { resolve_player_with_most_hero_points(data_1.ANARCHISTS_ID); }, @@ -1020,15 +1093,10 @@ states.select_player_with_most_hero_points = { resolve_player_with_most_hero_points(data_1.MODERATES_ID); }, }; -states.move_attacks = { - inactive: 'move attacks', - prompt() { - view.prompt = 'Choose a Front'; - }, -}; states.move_track = { inactive: 'move a Track', prompt() { + gen_spend_hero_points(); const node = get_active_node(); const track = node.a.t; const value = node.a.v; @@ -1050,6 +1118,9 @@ states.move_track = { gen_action_standee(track); } }, + spend_hp() { + resolve_spend_hp(); + }, standee(s) { const node = get_active_node(); let value = node.a.v; @@ -1065,11 +1136,15 @@ states.move_track = { states.move_track_up_or_down = { inactive: 'move a track', prompt() { + gen_spend_hero_points(); const node = get_active_node(); view.prompt = `Move ${get_track_name(node.a.track_id)} up or down`; gen_action('up'); gen_action('down'); }, + spend_hp() { + resolve_spend_hp(); + }, down() { const node = get_active_node(); move_track(node.a.track_id, -1 * node.a.strength); @@ -1084,11 +1159,15 @@ states.move_track_up_or_down = { states.peek_fascist_cards = { inactive: 'peek at Fascist cards', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose one card to return to the top of the deck'; for (const c of game.selectable_cards) { gen_action_card(c); } }, + spend_hp() { + resolve_spend_hp(); + }, card(c) { game.top_of_events_deck = c; for (const ec of game.selectable_cards) { @@ -1100,10 +1179,16 @@ states.peek_fascist_cards = { resolve_active_and_proceed(); }, }; +function resolve_spend_hp() { + insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction())); + log('Spends Hero Points'); + next(); +} states.player_turn = { inactive: 'play their turn', prompt() { - const faction_id = get_faction_id(game.active); + gen_spend_hero_points(); + const faction_id = get_active_faction(); 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; view.prompt = 'Play a card or spend Hero points'; @@ -1123,9 +1208,9 @@ states.player_turn = { else { gen_action('done'); } - if (can_spend_hp) { - gen_action('spend_hp'); - } + }, + spend_hp() { + resolve_spend_hp(); }, done() { game.faction_turn = null; @@ -1149,20 +1234,19 @@ states.player_turn = { insert_before_active_node(create_effects_node(effects)); next(); }, - spend_hp() { - insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction())); - log('Spends Hero Points'); - next(); - }, }; states.remove_blank_marker = { inactive: 'remove a Blank marker', prompt() { + gen_spend_hero_points(); view.prompt = 'Remove a Blank marker'; for (const b of game.triggered_track_effects) { gen_action_blank_marker(b); } }, + spend_hp() { + resolve_spend_hp(); + }, blank_marker(b) { const faction = get_active_faction(); pay_hero_points(faction, 1); @@ -1177,6 +1261,7 @@ states.remove_blank_marker = { states.remove_attack_from_fronts = { inactive: 'remove attacks', prompt() { + gen_spend_hero_points(); const { f, v: card_id } = get_active_node_args(); view.prompt = card_id === 6 @@ -1199,6 +1284,9 @@ states.remove_attack_from_fronts = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, front(id) { const { f, v: card_id } = get_active_node_args(); const removed_value = card_id === 6 ? 1 : Math.min(3, Math.abs(game.fronts[id].value)); @@ -1233,6 +1321,7 @@ states.remove_attack_from_fronts = { states.return_card = { inactive: 'return a card to their hand', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose a card to return to your hand'; if (game.selectable_cards.length === 0) { view.prompt = 'No card in trash to return. You must skip'; @@ -1242,6 +1331,9 @@ states.return_card = { gen_action_card(c); } }, + spend_hp() { + resolve_spend_hp(); + }, card(c) { const faction = get_active_faction(); array_remove(game.trash[faction], game.trash[faction].indexOf(c)); @@ -1353,6 +1445,7 @@ states.spend_hero_points = { states.swap_card_tableau_hand = { inactive: 'swap cards', prompt() { + gen_spend_hero_points(); view.prompt = 'Choose a card in your tableau and a card in your hand to swap'; const faction = get_active_faction(); @@ -1376,6 +1469,9 @@ states.swap_card_tableau_hand = { } } }, + spend_hp() { + resolve_spend_hp(); + }, card(c) { console.log('card', c); const faction = get_active_faction(); @@ -1403,13 +1499,14 @@ states.swap_card_tableau_hand = { function resolve_take_hero_points(faction) { const { v } = get_active_node_args(); const amount = Math.min(v, game.hero_points[faction]); - lose_hero_point(faction, amount); + lose_hero_points(faction, amount); gain_hero_points(get_active_faction(), amount); resolve_active_and_proceed(); } states.take_hero_points = { inactive: 'take Hero Points', prompt() { + gen_spend_hero_points(); const { v } = get_active_node_args(); view.prompt = v === 1 @@ -1422,6 +1519,9 @@ states.take_hero_points = { } } }, + spend_hp() { + resolve_spend_hp(); + }, Anarchist() { resolve_take_hero_points(data_1.ANARCHISTS_ID); }, @@ -1435,10 +1535,14 @@ states.take_hero_points = { states.use_organization_medallion = { inactive: 'use Organization Medallion', prompt() { + gen_spend_hero_points(); view.prompt = 'Use Organization Medallion?'; gen_action('yes'); gen_action('no'); }, + spend_hp() { + resolve_spend_hp(); + }, yes() { const faction = get_active_faction(); pay_hero_points(faction, 1); @@ -1462,10 +1566,14 @@ states.use_organization_medallion = { states.use_strategy_medallion = { inactive: 'use Strategy Medallion', prompt() { + gen_spend_hero_points(); view.prompt = 'Use Strategy Medallion?'; gen_action('yes'); gen_action('no'); }, + spend_hp() { + resolve_spend_hp(); + }, yes() { game.used_medallions.push(data_1.STRATEGY_MEDALLION_ID); const { f } = get_active_node_args(); @@ -1587,7 +1695,7 @@ function add_glory(faction, amount, indent = false) { } function check_activate_icon() { if (game.bonuses[data_1.MORALE_BONUS] === data_1.ON) { - insert_after_active_node(create_leaf_node('activate_icon', get_active_faction_id())); + insert_after_active_node(create_leaf_node('activate_icon', get_active_faction())); } resolve_active_and_proceed(); } @@ -1696,6 +1804,7 @@ function end_of_year() { 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); game.engine = get_player_order().map((f) => create_leaf_node('end_of_year_discard', f)); + game.engine.push(create_function_node('checkpoint')); game.engine.push(create_function_node('start_year')); game.top_of_events_deck = null; next(); @@ -2031,7 +2140,7 @@ function resolve_effect(effect) { { condition: effect.type === 'hero_points' && effect.target === data_1.ALL_PLAYERS, resolve: () => { - return create_seq_node(get_player_order().map((faction) => create_leaf_node('gain_hero_points', faction, { + return create_seq_node(get_player_order().map((faction) => create_leaf_node('hero_points', faction, { v: effect.value, }))); }, @@ -2039,20 +2148,20 @@ function resolve_effect(effect) { { condition: effect.type === 'hero_points' && effect.target === data_1.SELF, resolve: () => { - return create_leaf_node('gain_hero_points', faction, args); + return create_leaf_node('hero_points', faction, args); }, }, { condition: effect.type === 'hero_points' && role_ids.includes(effect.target), resolve: () => { - return create_leaf_node('gain_hero_points', effect.target, args); + return create_leaf_node('hero_points', effect.target, args); }, }, { condition: effect.type === 'hero_points' && effect.target === data_1.INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('gain_hero_points', game.initiative, args); + return create_leaf_node('hero_points', game.initiative, args); }, }, { @@ -2134,7 +2243,7 @@ function draw_fascist_card() { } return draw_card(list_deck(data_1.FASCIST_ID)); } -function lose_hero_point(faction, value) { +function lose_hero_points(faction, value) { const points_lost = Math.min(game.hero_points[faction], Math.abs(value)); game.hero_points.pool += points_lost; game.hero_points[faction] -= points_lost; @@ -2185,15 +2294,9 @@ function log_h3(msg) { function get_active_faction() { return player_faction_map[game.active]; } -function get_active_faction_id() { - return player_faction_map[game.active]; -} function get_blank_marker_id(track_id, space_id) { return track_id * 11 + space_id; } -function get_faction_id(player) { - return player_faction_map[player]; -} function get_front_name(id) { return front_names[id]; } @@ -2209,7 +2312,7 @@ function get_defeated_front_count() { } return count; } -function get_icon_count_in_tableau(icon, faction = get_active_faction_id()) { +function get_icon_count_in_tableau(icon, faction = get_active_faction()) { let count = 0; for (const c of game.tableaus[faction]) { const card = cards[c]; |