diff options
Diffstat (limited to 'rules.ts')
-rw-r--r-- | rules.ts | 219 |
1 files changed, 165 insertions, 54 deletions
@@ -171,6 +171,15 @@ function gen_action_standee(track_id: number) { 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 gen_action_space(space) { // gen_action('space', space); // } @@ -279,15 +288,42 @@ function setup_final_bid() { } 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; + } + + // 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 = [ + 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 }), + ]; + + // game.engine.push(create_function_node('resolve_fascist_test')); + // game.engine.push(create_function_node('setup_bag_of_glory')); + 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(); } @@ -302,8 +338,8 @@ function start_of_player_turn() { const engine_functions: Record<string, Function> = { check_activate_icon, checkpoint, + end_of_player_turn, end_of_turn, - // end_of_year, setup_bag_of_glory, setup_choose_card, setup_final_bid, @@ -541,6 +577,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) { }, }, glory: [], + first_player: null, hands: { a: [], c: [], @@ -660,12 +697,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] as PlayerCard; 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(), { @@ -790,6 +831,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]) { @@ -799,6 +841,9 @@ states.add_card_to_tableau = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, card(c: CardId) { const faction_id = get_active_faction(); const card = cards[c]; @@ -817,9 +862,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) { @@ -833,6 +882,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 = @@ -843,6 +893,9 @@ states.add_to_front = { gen_action_front(f); } }, + spend_hp() { + resolve_spend_hp(); + }, front(f: FrontId) { const value = get_active_node_args().v; update_front(f, value, get_active_faction()); @@ -853,6 +906,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: Array<FrontId> = []; @@ -875,6 +929,9 @@ states.attack_front = { 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; @@ -935,6 +992,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); @@ -949,6 +1007,9 @@ states.choose_area_ap = { } } }, + spend_hp() { + resolve_spend_hp(); + }, bonus(b: number) { // Turn on bonus update_bonus(b, ON); @@ -965,13 +1026,13 @@ states.choose_area_ap = { }, front(f: FrontId) { const s: number = 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: number) { 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, @@ -985,6 +1046,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 === ON && @@ -1008,6 +1070,9 @@ states.change_bonus = { gen_action_bonus(args.t); } }, + spend_hp() { + resolve_spend_hp(); + }, bonus(b: number) { const value = get_active_node_args().v; update_bonus(b, value); @@ -1021,6 +1086,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]; @@ -1030,6 +1096,9 @@ states.choose_card = { } } }, + spend_hp() { + resolve_spend_hp(); + }, card(c: CardId) { const faction = get_active_faction(); game.selected_cards[faction].push(c); @@ -1073,6 +1142,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); @@ -1081,6 +1151,9 @@ states.choose_medallion = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, medallion(m: number) { const faction = get_active_faction(); const medallion = medallions[m]; @@ -1125,10 +1198,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); @@ -1178,10 +1255,10 @@ states.end_of_year_discard = { }, }; -// TODO: rename to change_hero_points -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 = @@ -1200,6 +1277,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); @@ -1207,7 +1287,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() { @@ -1227,7 +1307,7 @@ states.game_over = { function resolve_player_with_most_hero_points(faction: FactionId) { 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); } @@ -1237,6 +1317,7 @@ function resolve_player_with_most_hero_points(faction: FactionId) { 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 @@ -1248,6 +1329,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(ANARCHISTS_ID); }, @@ -1259,17 +1343,10 @@ states.select_player_with_most_hero_points = { }, }; -// TODO: implement -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; @@ -1293,6 +1370,9 @@ states.move_track = { gen_action_standee(track); } }, + spend_hp() { + resolve_spend_hp(); + }, standee(s: number) { const node = get_active_node(); let value = node.a.v; @@ -1314,11 +1394,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); @@ -1334,11 +1418,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: CardId) { game.top_of_events_deck = c; for (const ec of game.selectable_cards) { @@ -1351,10 +1439,22 @@ states.peek_fascist_cards = { }, }; +function resolve_spend_hp() { + // insert spend hero points node before current node + // so it will return to current node after resolving + 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 as Player); + 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; @@ -1378,9 +1478,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; @@ -1409,27 +1509,21 @@ states.player_turn = { next(); }, - spend_hp() { - // insert spend hero points node before current node - // so it will return to current node after resolving - 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: number) { const faction = get_active_faction(); pay_hero_points(faction, 1); @@ -1461,6 +1555,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 @@ -1484,6 +1579,9 @@ states.remove_attack_from_fronts = { gen_action('skip'); } }, + spend_hp() { + resolve_spend_hp(); + }, front(id: FrontId) { const { f, v: card_id } = get_active_node_args(); @@ -1528,6 +1626,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'; @@ -1537,6 +1636,9 @@ states.return_card = { gen_action_card(c); } }, + spend_hp() { + resolve_spend_hp(); + }, card(c: CardId) { const faction = get_active_faction(); array_remove(game.trash[faction], game.trash[faction].indexOf(c)); @@ -1658,6 +1760,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(); @@ -1689,6 +1792,9 @@ states.swap_card_tableau_hand = { } } }, + spend_hp() { + resolve_spend_hp(); + }, card(c: CardId) { console.log('card', c); const faction = get_active_faction(); @@ -1727,7 +1833,7 @@ states.swap_card_tableau_hand = { function resolve_take_hero_points(faction: FactionId) { 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(); } @@ -1735,6 +1841,7 @@ function resolve_take_hero_points(faction: FactionId) { states.take_hero_points = { inactive: 'take Hero Points', prompt() { + gen_spend_hero_points(); const { v } = get_active_node_args(); view.prompt = v === 1 @@ -1747,6 +1854,9 @@ states.take_hero_points = { } } }, + spend_hp() { + resolve_spend_hp(); + }, Anarchist() { resolve_take_hero_points(ANARCHISTS_ID); }, @@ -1761,11 +1871,15 @@ 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); @@ -1793,11 +1907,15 @@ 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(STRATEGY_MEDALLION_ID); const { f } = get_active_node_args(); @@ -1982,7 +2100,7 @@ function add_glory( function check_activate_icon() { if (game.bonuses[MORALE_BONUS] === ON) { insert_after_active_node( - create_leaf_node('activate_icon', get_active_faction_id()) + create_leaf_node('activate_icon', get_active_faction()) ); } resolve_active_and_proceed(); @@ -2103,6 +2221,7 @@ function end_of_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')); // New deck is used for next year so clear top card @@ -2553,7 +2672,7 @@ function resolve_effect( resolve: () => { return create_seq_node( get_player_order().map((faction) => - create_leaf_node('gain_hero_points', faction, { + create_leaf_node('hero_points', faction, { v: effect.value, }) ) @@ -2563,7 +2682,7 @@ function resolve_effect( { condition: effect.type === 'hero_points' && effect.target === SELF, resolve: () => { - return create_leaf_node('gain_hero_points', faction, args); + return create_leaf_node('hero_points', faction, args); }, }, { @@ -2572,7 +2691,7 @@ function resolve_effect( role_ids.includes(effect.target as FactionId), resolve: () => { return create_leaf_node( - 'gain_hero_points', + 'hero_points', effect.target as FactionId, args ); @@ -2582,7 +2701,7 @@ function resolve_effect( condition: effect.type === 'hero_points' && effect.target === INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('gain_hero_points', game.initiative, args); + return create_leaf_node('hero_points', game.initiative, args); }, }, { @@ -2691,7 +2810,7 @@ function draw_fascist_card(): CardId { return draw_card(list_deck(FASCIST_ID)); } -function lose_hero_point(faction: FactionId, value: number) { +function lose_hero_points(faction: FactionId, value: number) { const points_lost = Math.min(game.hero_points[faction], Math.abs(value)); game.hero_points.pool += points_lost; game.hero_points[faction] -= points_lost; @@ -2797,18 +2916,10 @@ function get_active_faction(): FactionId { return player_faction_map[game.active]; } -function get_active_faction_id(): FactionId { - return player_faction_map[game.active]; -} - function get_blank_marker_id(track_id: number, space_id: number) { return track_id * 11 + space_id; } -function get_faction_id(player: Player): FactionId { - return player_faction_map[player]; -} - function get_front_name(id: FrontId | 'd' | 'v') { return front_names[id]; } @@ -2831,7 +2942,7 @@ function get_defeated_front_count() { function get_icon_count_in_tableau( icon: Icon, - faction: FactionId = get_active_faction_id() + faction: FactionId = get_active_faction() ) { let count = 0; for (const c of game.tableaus[faction]) { |