diff options
author | Frans Bongers <fransbongers@franss-mbp.home> | 2025-02-09 21:16:38 +0100 |
---|---|---|
committer | Frans Bongers <fransbongers@franss-mbp.home> | 2025-02-09 21:16:38 +0100 |
commit | f7ec325ebd06e17391cb0b024ad1897a19fc0c0f (patch) | |
tree | 7793704b13eb3f4575c241edf86774f7ce375d4b | |
parent | c663569c22315835f280866f9b0bf247233e9901 (diff) | |
download | land-and-freedom-f7ec325ebd06e17391cb0b024ad1897a19fc0c0f.tar.gz |
-rw-r--r-- | data.js | 6 | ||||
-rw-r--r-- | data.ts | 6 | ||||
-rw-r--r-- | play.js | 1 | ||||
-rw-r--r-- | play.ts | 1 | ||||
-rw-r--r-- | rules.js | 92 | ||||
-rw-r--r-- | rules.ts | 110 | ||||
-rw-r--r-- | types.d.ts | 1 |
7 files changed, 140 insertions, 77 deletions
@@ -818,7 +818,7 @@ const data = { effects: [ create_effect('attack', SOUTHERN, -3, INITIATIVE_PLAYER), create_effect('attack', CLOSEST_TO_VICTORY, -2, INITIATIVE_PLAYER), - create_effect('bonus', MORALE_BONUS, OFF, INITIATIVE_PLAYER), + create_effect('bonus', TEAMWORK_BONUS, OFF, INITIATIVE_PLAYER), ], test: { front: SOUTHERN, @@ -1285,7 +1285,7 @@ const data = { test: { front: ARAGON, value: -1, - pass: create_effect('track', GOVERNMENT, TOWARDS_CENTER), + pass: create_effect('track', GOVERNMENT, AWAY_FROM_CENTER), fail: create_effect('track', COLLECTIVIZATION, -1), }, title: 'BATTLE OF TERUEL', @@ -1872,7 +1872,7 @@ const data = { null, null, null, - create_effect('track', LIBERTY, -1), + create_effect('track', COLLECTIVIZATION, -1), null, create_effect('front', ANY, 1, MODERATES_ID), create_effect('track', GOVERNMENT, 1), @@ -840,7 +840,7 @@ const data: StaticData = { effects: [ create_effect('attack', SOUTHERN, -3, INITIATIVE_PLAYER), create_effect('attack', CLOSEST_TO_VICTORY, -2, INITIATIVE_PLAYER), - create_effect('bonus', MORALE_BONUS, OFF, INITIATIVE_PLAYER), + create_effect('bonus', TEAMWORK_BONUS, OFF, INITIATIVE_PLAYER), ], test: { front: SOUTHERN, @@ -1322,7 +1322,7 @@ const data: StaticData = { test: { front: ARAGON, value: -1, - pass: create_effect('track', GOVERNMENT, TOWARDS_CENTER), + pass: create_effect('track', GOVERNMENT, AWAY_FROM_CENTER), fail: create_effect('track', COLLECTIVIZATION, -1), }, title: 'BATTLE OF TERUEL', @@ -1944,7 +1944,7 @@ const data: StaticData = { null, null, null, - create_effect('track', LIBERTY, -1), + create_effect('track', COLLECTIVIZATION, -1), null, create_effect('front', ANY, 1, MODERATES_ID), create_effect('track', GOVERNMENT, 1), @@ -427,6 +427,7 @@ function on_update() { action_button('move_track', 'Move a Track'); action_button('turn_on_bonus', 'Turn on a Bonus'); action_button('add_glory', 'Add to Bag of Glory'); + action_button('draw_glory', 'Draw from Bag of Glory'); action_button('up', 'Up'); action_button('down', 'Down'); action_button('next', 'Next'); @@ -544,6 +544,7 @@ function on_update() { action_button('turn_on_bonus', 'Turn on a Bonus'); action_button('add_glory', 'Add to Bag of Glory'); + action_button('draw_glory', 'Draw from Bag of Glory'); action_button('up', 'Up'); action_button('down', 'Down'); action_button('next', 'Next'); @@ -175,6 +175,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_function_node('resolve_fascist_test'), create_function_node('setup_bag_of_glory'), ]; @@ -197,6 +198,7 @@ const engine_functions = { checkpoint, end_of_player_turn, end_of_turn, + end_of_year_cleanup, end_resolving_event_effects, setup_bag_of_glory, setup_choose_card, @@ -287,7 +289,10 @@ function insert_after_active_node(node, engine = game.engine) { function insert_before_active_node(node, engine = game.engine) { insert_before_or_after_active_node(node, 'before', engine); } -function next() { +function next(checkpoint = false) { + if (checkpoint) { + clear_undo(); + } const node = get_active_node(game.engine); if (node.t === function_node && engine_functions[node.f]) { const args = node.a; @@ -313,18 +318,15 @@ function next() { } } } -function resolve_active_node(checkpoint = false) { +function resolve_active_node() { const next_node = get_active_node(game.engine); if (next_node !== null) { next_node.r = resolved; } - if (checkpoint) { - clear_undo(); - } } function resolve_active_and_proceed(checkpoint = false) { - resolve_active_node(checkpoint); - next(); + resolve_active_node(); + next(checkpoint); } function game_view(state, current) { game = state; @@ -465,6 +467,7 @@ function setup(seed, _scenario, _options) { top_of_events_deck: null, turn: 0, year: 0, + glory_current_year: null, }; game.player_order.push(exports.roles[random(2)]); game.player_order.push(game.player_order[1] === data_1.ANARCHIST ? data_1.COMMUNIST : data_1.ANARCHIST); @@ -830,6 +833,15 @@ states.break_tie_winner = { resolve_active_and_proceed(); }, }; +states.change_active_player = { + inactive: '', + auto_resolve() { + return true; + }, + prompt() { + view.prompt = ''; + } +}; states.choose_area_ap = { inactive: 'choose area to use Action Points', prompt() { @@ -1050,11 +1062,6 @@ states.confirm_turn = { }; states.draw_card = { inactive: 'draw a card', - auto_resolve() { - const { v } = get_active_node_args(); - draw_hand_cards(get_active_faction(), v); - return true; - }, prompt() { gen_spend_hero_points(); const { v } = get_active_node_args(); @@ -1075,6 +1082,30 @@ states.draw_card = { resolve_active_and_proceed(); }, }; +states.draw_glory = { + inactive: 'draw from the Bag of Glory', + prompt() { + gen_spend_hero_points(); + view.prompt = 'Draw from the Bag of Glory'; + gen_action('draw_glory'); + }, + draw_glory() { + const index = random(game.bag_of_glory.length); + const faction = game.bag_of_glory[index]; + game.glory.push(faction); + if (!game.glory_current_year) { + game.glory_current_year = game.glory_current_year = { + a: false, + c: false, + m: false, + }; + } + game.glory_current_year[faction] = true; + array_remove(game.bag_of_glory, index); + log(`${get_player(get_active_faction())} draws <ft${faction}> from the Bag of Glory`); + resolve_active_and_proceed(true); + }, +}; states.end_of_year_discard = { inactive: 'discard cards from hand and tableau', prompt() { @@ -1120,17 +1151,6 @@ states.end_of_year_discard = { }; states.hero_points = { inactive: 'gain Hero Points', - auto_resolve() { - const { v } = get_active_node_args(); - const faction = get_active_faction(); - if (v < 0) { - lose_hero_points(faction, v); - } - else if (v > 0 && game.hero_points.pool > 0) { - gain_hero_points(faction, v); - } - return true; - }, prompt() { gen_spend_hero_points(); const value = get_active_node_args().v; @@ -2103,33 +2123,32 @@ function end_of_year() { log_h1('End of year'); } const glory_to_draw = [0, 1, 2, 5]; - const glory_this_year = { + game.glory_current_year = { a: false, c: false, m: false, }; - const drawn_glory = []; + const player_order = get_player_order(); + const engine = []; for (let i = 0; i < glory_to_draw[game.year]; ++i) { - const index = random(game.bag_of_glory.length); - const faction = game.bag_of_glory[index]; - game.glory.push(faction); - drawn_glory.push(faction); - glory_this_year[faction] = true; - array_remove(game.bag_of_glory, index); + engine.push(create_leaf_node('draw_glory', player_order[i % 3])); } - log(`Tokens pulled from the Bag of Glory: ${drawn_glory - .map((faction_id) => `<ft${faction_id}>`) - .join('')}`); + engine.push(create_function_node('end_of_year_cleanup')); + game.engine = engine; + next(true); +} +function end_of_year_cleanup() { if (game.year === 3) { determine_winner(); return; } - const players_to_gain_hero_points = role_ids.filter((f) => !glory_this_year[f]); + const players_to_gain_hero_points = role_ids.filter((f) => !game.glory_current_year?.[f]); gain_hero_points_in_player_order(players_to_gain_hero_points, game.year); game.engine = get_player_order().map((f) => create_function_node('check_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; + game.glory_current_year = null; next(); } function end_resolving_event_effects() { @@ -2273,6 +2292,9 @@ function get_fronts_to_add_to(target, not = []) { else if (game.fronts[target].status === data_1.DEFEAT) { return get_fronts_closest_to(data_1.CLOSEST_TO_DEFEAT); } + else if (game.fronts[target].status === data_1.VICTORY) { + return get_fronts_to_add_to(data_1.ANY); + } else { return [target]; } @@ -332,6 +332,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_function_node('resolve_fascist_test'), create_function_node('setup_bag_of_glory'), ]; @@ -355,6 +356,7 @@ const engine_functions: Record<string, Function> = { checkpoint, end_of_player_turn, end_of_turn, + end_of_year_cleanup, end_resolving_event_effects, setup_bag_of_glory, setup_choose_card, @@ -472,7 +474,10 @@ function insert_before_active_node( insert_before_or_after_active_node(node, 'before', engine); } -function next() { +function next(checkpoint = false) { + if (checkpoint) { + clear_undo(); + } const node = get_active_node(game.engine); if (node.t === function_node && engine_functions[node.f]) { const args = node.a; @@ -502,19 +507,16 @@ function next() { } } -function resolve_active_node(checkpoint = false) { +function resolve_active_node() { const next_node = get_active_node(game.engine); if (next_node !== null) { next_node.r = resolved; } - if (checkpoint) { - clear_undo(); - } } function resolve_active_and_proceed(checkpoint = false) { - resolve_active_node(checkpoint); - next(); + resolve_active_node(); + next(checkpoint); } // #endregion @@ -670,6 +672,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) { top_of_events_deck: null, turn: 0, year: 0, + glory_current_year: null, }; // Randomly choose second player @@ -1072,6 +1075,21 @@ states.break_tie_winner = { }, }; +/** + * Change does not do anything, but it will change + * active player and trigger a confirm for the previous + * state + */ +states.change_active_player = { + inactive: '', + auto_resolve() { + return true; + }, + prompt() { + view.prompt = ''; + } +} + states.choose_area_ap = { inactive: 'choose area to use Action Points', prompt() { @@ -1332,11 +1350,6 @@ states.confirm_turn = { states.draw_card = { inactive: 'draw a card', - auto_resolve() { - const { v } = get_active_node_args(); - draw_hand_cards(get_active_faction(), v); - return true; - }, prompt() { gen_spend_hero_points(); const { v } = get_active_node_args(); @@ -1358,6 +1371,37 @@ states.draw_card = { }, }; +states.draw_glory = { + inactive: 'draw from the Bag of Glory', + prompt() { + gen_spend_hero_points(); + view.prompt = 'Draw from the Bag of Glory'; + gen_action('draw_glory'); + }, + draw_glory() { + const index = random(game.bag_of_glory.length); + const faction = game.bag_of_glory[index]; + + game.glory.push(faction); + + // 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 = { + a: false, + c: false, + m: false, + }; + } + game.glory_current_year[faction] = true; + + array_remove(game.bag_of_glory, index); + + log(`${get_player(get_active_faction())} draws <ft${faction}> from the Bag of Glory`) + resolve_active_and_proceed(true); + }, +}; + states.end_of_year_discard = { inactive: 'discard cards from hand and tableau', prompt() { @@ -1407,16 +1451,6 @@ states.end_of_year_discard = { states.hero_points = { inactive: 'gain Hero Points', - auto_resolve() { - const { v } = get_active_node_args(); - const faction = get_active_faction(); - if (v < 0) { - lose_hero_points(faction, v); - } else if (v > 0 && game.hero_points.pool > 0) { - gain_hero_points(faction, v); - } - return true; - }, prompt() { gen_spend_hero_points(); const value = get_active_node_args().v; @@ -2583,26 +2617,27 @@ function end_of_year() { } const glory_to_draw = [0, 1, 2, 5]; - const glory_this_year: Record<FactionId, boolean> = { + + game.glory_current_year = { a: false, c: false, m: false, }; - const drawn_glory = []; + + const player_order = get_player_order(); + + const engine = []; + for (let i = 0; i < glory_to_draw[game.year]; ++i) { - const index = random(game.bag_of_glory.length); - const faction = game.bag_of_glory[index]; - game.glory.push(faction); - drawn_glory.push(faction); - glory_this_year[faction] = true; - array_remove(game.bag_of_glory, index); + engine.push(create_leaf_node('draw_glory', player_order[i % 3])); } - log( - `Tokens pulled from the Bag of Glory: ${drawn_glory - .map((faction_id: string) => `<ft${faction_id}>`) - .join('')}` - ); + engine.push(create_function_node('end_of_year_cleanup')); + + game.engine = engine; + next(true); +} +function end_of_year_cleanup() { if (game.year === 3) { // end of game determine_winner(); @@ -2610,7 +2645,7 @@ function end_of_year() { } const players_to_gain_hero_points = role_ids.filter( - (f) => !glory_this_year[f] + (f) => !game.glory_current_year?.[f] ); gain_hero_points_in_player_order(players_to_gain_hero_points, game.year); @@ -2624,6 +2659,7 @@ function end_of_year() { // New deck is used for next year so clear top card // if it was set game.top_of_events_deck = null; + game.glory_current_year = null; next(); } @@ -2816,6 +2852,8 @@ function get_fronts_to_add_to(target: string, not: FrontId[] = []): FrontId[] { ); } else if (game.fronts[target].status === DEFEAT) { return get_fronts_closest_to(CLOSEST_TO_DEFEAT); + } else if (game.fronts[target].status === VICTORY) { + return get_fronts_to_add_to(ANY); } else { return [target as FrontId]; } @@ -72,6 +72,7 @@ export interface Game { trash: Record<FactionId, CardId[]>; triggered_track_effects: number[]; used_medallions: number[]; + glory_current_year?: Record<FactionId, boolean> | null; } export interface View { |