diff options
author | Frans Bongers <fransbongers@franss-mbp.home> | 2025-02-22 13:36:46 +0100 |
---|---|---|
committer | Frans Bongers <fransbongers@franss-mbp.home> | 2025-02-22 13:36:46 +0100 |
commit | 6379a462c75faefb08ac1e8e367fc7ead0c206c3 (patch) | |
tree | a7b0ba7ba139e6089dcb41a2d026281528e36b21 | |
parent | fbbf82d8db3bf6bf840553de4bc5ed8276f67ac7 (diff) | |
download | land-and-freedom-6379a462c75faefb08ac1e8e367fc7ead0c206c3.tar.gz |
-rw-r--r-- | rules.js | 117 | ||||
-rw-r--r-- | rules.ts | 190 | ||||
-rw-r--r-- | types.d.ts | 4 |
3 files changed, 127 insertions, 184 deletions
@@ -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; } @@ -383,7 +383,9 @@ function game_view(state, current) { else if (current !== game.active && !game.active.includes(current)) { let inactive = states[game.state].inactive || game.state; - view.prompt = Array.isArray(game.active) ? `Waiting for other players to ${inactive}.` : `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 = {}; @@ -561,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'), })); @@ -891,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(); }, }; @@ -1049,7 +1046,7 @@ function setup_momentum() { }; } else { - insert_after_active_node(create_leaf_node('play_card', faction, { + insert_after_active_node(create_state_node('play_card', faction, { src: 'momentum', })); } @@ -1425,7 +1422,7 @@ 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(); } @@ -1534,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(); @@ -1551,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(); }, }; @@ -1618,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, @@ -1630,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, })); @@ -1737,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(); }, @@ -1777,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(); }, @@ -1796,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(); }, @@ -2121,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, })); @@ -2168,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; @@ -2225,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 = []; @@ -2315,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(); } @@ -2416,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, })); @@ -2454,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, })); } @@ -2526,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('play_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 }), ]); }, }, @@ -12,7 +12,7 @@ import { FunctionNode, Game, Icon, - LeafNode, + StateNode, PlayCardArgs, Player, PlayerCard, @@ -29,14 +29,6 @@ import data, { ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID, - // LIBERTY, - // COLLECTIVIZATION, - // GOVERNMENT, - // SOVIET_SUPPORT, - // FOREIGN_AID, - // MORALE_BONUS, - // TEAMWORK_BONUS, - // OFF, LIBERTY, CLOSEST_TO_DEFEAT, CLOSEST_TO_VICTORY, @@ -60,7 +52,6 @@ import data, { TOWARDS_CENTER, ARCHIVES_MEDALLION_ID, INTELLIGENCE_MEDALLION_ID, - // VOLUNTEERS_MEDALLION_ID, ORGANIZATION_MEDALLION_ID, STRATEGY_MEDALLION_ID, PROPAGANDA_MEDALLION_ID, @@ -75,24 +66,14 @@ import data, { ARAGON, FASCIST_ID, NORTHERN, - // StaticData, - // PLAYER_WITH_MOST_HERO_POINTS, } from './data'; -// interface State { -// inactive: string; -// prompt: () => void; -// } const OBSERVER = 'Observer'; const states = {} as States; let game = {} as Game; // = null var view = {} as View; // = null -// export const ANARCHIST = 'Anarchist' as Player; -// export const COMMUNIST = 'Communist' as Player; -// export const MODERATE = 'Moderate' as Player; - const role_ids: FactionId[] = [ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID]; const faction_player_map: Record<FactionId, Player> = { @@ -185,18 +166,6 @@ function gen_spend_hero_points() { } } -// function gen_action_space(space) { -// gen_action('space', space); -// } - -// function gen_action_piece(piece) { -// gen_action('piece', piece); -// } - -// function gen_action_card(card) { -// gen_action('card', card); -// } - export function action( state: Game, player: Player, @@ -217,18 +186,18 @@ export function action( // #region ENGINE -const leaf_node = 'l'; +const state_node = 'l'; const seq_node = 's'; const function_node = 'f'; const resolved = 1; -function create_leaf_node<T = any>( +function create_state_node<T = any>( state: string, faction: FactionId | 'None' | 'all' | FactionId[], args?: T -): LeafNode { +): StateNode { return { - t: leaf_node, + t: state_node, s: state, p: faction, a: args, @@ -256,7 +225,7 @@ function create_seq_node(children: EngineNode[]): SeqNode { function checkpoint() { if (game.undo.length > 0) { insert_after_active_node( - create_leaf_node('confirm_turn', get_active_faction()) + create_state_node('confirm_turn', get_active_faction()) ); } resolve_active_and_proceed(); @@ -265,22 +234,15 @@ function checkpoint() { 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')]; - - // 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 = [create_state_node('choose_card', 'all')]; + game.engine.push(create_function_node('setup_player_turn')); next(); } @@ -289,7 +251,7 @@ 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) + create_state_node('choose_final_bid', faction_id) ); game.engine.push(create_function_node('checkpoint')); game.engine.push(create_function_node('resolve_final_bid')); @@ -309,14 +271,14 @@ 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(); } -// Check if player needs to discard cards. If so inserts leaf node +// Check if player needs to discard cards. If so inserts state node function check_end_of_year_discard() { const { f: faction } = get_active_node_args(); @@ -324,7 +286,7 @@ function check_end_of_year_discard() { 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(); @@ -334,7 +296,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'), ]; @@ -391,9 +353,9 @@ const engine_functions: Record<string, Function> = { function get_active( engine: EngineNode[] -): { parent: EngineNode[]; node: FunctionNode | LeafNode } | null { +): { parent: EngineNode[]; node: FunctionNode | StateNode } | null { 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) { @@ -408,7 +370,7 @@ function get_active( function get_active_node( engine: EngineNode[] = game.engine -): FunctionNode | LeafNode | null { +): FunctionNode | StateNode | null { const a = get_active(engine); return a === null ? null : a.node; } @@ -416,10 +378,10 @@ function get_active_node( function get_nodes_for_state( state: string, engine: EngineNode[] = game.engine -): LeafNode[] { +): StateNode[] { 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) { @@ -431,7 +393,7 @@ function get_nodes_for_state( function get_active_node_args<T = any>(): T { 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; @@ -439,7 +401,7 @@ function get_active_node_args<T = any>(): T { function update_active_node_args<T = any>(args: Partial<T>) { 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, @@ -476,7 +438,7 @@ function insert_before_active_node( insert_before_or_after_active_node(node, 'before', engine); } -function get_next_active(p: LeafNode['p']): Player | Player[] | 'None' { +function get_next_active(p: StateNode['p']): Player | Player[] | 'None' { if (Array.isArray(p)) { return p.map((faction) => faction_player_map[faction]); } @@ -513,7 +475,7 @@ function next(checkpoint = false) { if (next_active !== current_active && game.undo.length > 0) { insert_before_active_node( - create_leaf_node('confirm_turn', get_active_faction()) + create_state_node('confirm_turn', get_active_faction()) ); game.state = 'confirm_turn'; return; @@ -592,7 +554,9 @@ function game_view(state: Game, current: Player | 'Observer') { !game.active.includes(current as Player) ) { let inactive = states[game.state].inactive || game.state; - view.prompt = Array.isArray(game.active) ? `Waiting for other players to ${inactive}.` : `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 = {}; if (game.undo && game.undo.length > 0) view.actions.undo = 1; @@ -771,11 +735,6 @@ function start_turn() { game.engine.push(create_function_node('setup_choose_card')); } next(); - // game.state = 'resolve_event'; - // game.active = faction_player_map[game.initiative]; - // game.state_data = { - // current_effect: 0, - // }; } // region STATES @@ -805,7 +764,7 @@ states.activate_icon = { }, add_to_front() { insert_after_active_node( - create_leaf_node('add_to_front', get_active_faction(), { + create_state_node('add_to_front', get_active_faction(), { t: ANY, v: get_icon_count_in_tableau('add_to_front'), }) @@ -1155,15 +1114,12 @@ states.choose_area_ap = { resolve_active_and_proceed(); }, standee(track_id: number) { - insert_after_active_node({ - t: leaf_node, - p: get_active_faction(), - s: 'move_track_up_or_down', - a: { + 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(); }, }; @@ -1328,7 +1284,7 @@ function setup_momentum() { // right away or not. Depends on whether card for this turn has been fully resolved or not // Get player turn node - const node: LeafNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; + const node: StateNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; const player_needs_to_play_card = game.selected_cards[faction].length > 0; const { use_ap, use_morale_bonus, resolving_event } = @@ -1348,7 +1304,7 @@ function setup_momentum() { } else { // Player can resolve choosing a new card insert_after_active_node( - create_leaf_node<PlayCardArgs>('play_card', faction, { + create_state_node<PlayCardArgs>('play_card', faction, { src: 'momentum', }) ); @@ -1785,7 +1741,7 @@ 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()) + create_state_node('spend_hero_points', get_active_faction()) ); log('Spends Hero Points'); @@ -1911,7 +1867,7 @@ states.player_turn = { use_ap: false, }); insert_before_active_node( - create_leaf_node('choose_area_ap', faction, { + create_state_node('choose_area_ap', faction, { strength, }) ); @@ -1935,7 +1891,7 @@ states.player_turn = { use_morale_bonus: false, }); insert_before_active_node( - create_leaf_node('activate_icon', get_active_faction()) + create_state_node('activate_icon', get_active_faction()) ); next(); }, @@ -2029,7 +1985,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(), { + create_state_node('attack_front', get_active_faction(), { t: ANY, v: card_id === 39 ? -2 : -1 * removed_value, n: card_id === 16 ? id : undefined, @@ -2043,7 +1999,7 @@ states.remove_attack_from_fronts = { const values: number[] = Object.values(f ?? {}); if (card_id === 39 && values.length > 0) { insert_after_active_node( - create_leaf_node('attack_front', get_active_faction(), { + create_state_node('attack_front', get_active_faction(), { t: ANY, v: -2, }) @@ -2161,11 +2117,11 @@ states.spend_hero_points = { 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: ANY, v: 1, }), - create_leaf_node('spend_hero_points', faction), + create_state_node('spend_hero_points', faction), ]) ); resolve_active_and_proceed(); @@ -2202,8 +2158,8 @@ states.spend_hero_points = { } 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(); @@ -2222,11 +2178,11 @@ states.spend_hero_points = { 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(); @@ -2637,7 +2593,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, { + create_state_node('break_tie_winner', game.initiative, { winners, glory: highest_glory, }) @@ -2689,7 +2645,7 @@ function end_of_year() { 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')); @@ -2726,7 +2682,7 @@ function end_of_year_cleanup() { function end_resolving_event_effects() { // Get player turn node - const node: LeafNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; + const node: StateNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; // Update args node.a = { @@ -2784,9 +2740,7 @@ function gain_hero_points( } function game_over(result: Player | 'None', victory: string) { - insert_after_active_node(create_leaf_node('game_over', 'None')); - // game.state = 'game_over'; - // game.active = 'None'; + insert_after_active_node(create_state_node('game_over', 'None')); game.result = result; game.victory = victory; game.undo = []; @@ -2896,7 +2850,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 }) + create_state_node('break_tie_final_bid', game.initiative, { winners }) ); } @@ -3025,7 +2979,7 @@ function insert_use_organization_medallion_node( const faction = get_active_faction(); insert_after_active_node( - create_leaf_node('use_organization_medallion', faction, { + create_state_node('use_organization_medallion', faction, { t: track_id, v: value, }) @@ -3085,7 +3039,7 @@ function update_front( can_use_medallion(STRATEGY_MEDALLION_ID) ) { insert_after_active_node( - create_leaf_node('use_strategy_medallion', get_active_faction(), { + create_state_node('use_strategy_medallion', get_active_faction(), { f: front_id, }) ); @@ -3170,7 +3124,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { return create_function_node(effect.target as string); } if (effect.type === 'state') { - return create_leaf_node(effect.target as string, faction, { + return create_state_node(effect.target as string, faction, { v: effect.value, src: source, }); @@ -3178,7 +3132,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { // Default cases where effect type is mapped to a state 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); } // Specific mapping based on target @@ -3188,7 +3142,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'hero_points' && effect.target === PLAYER_WITH_MOST_HERO_POINTS, resolve: () => { - return create_leaf_node( + return create_state_node( 'select_player_with_most_hero_points', faction, args @@ -3200,7 +3154,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { resolve: () => { return create_seq_node( get_player_order().map((faction) => - create_leaf_node('hero_points', faction, args) + create_state_node('hero_points', faction, args) ) ); }, @@ -3208,7 +3162,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { { condition: effect.type === 'hero_points' && effect.target === SELF, resolve: () => { - return create_leaf_node('hero_points', faction, args); + return create_state_node('hero_points', faction, args); }, }, { @@ -3216,7 +3170,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'hero_points' && role_ids.includes(effect.target as FactionId), resolve: () => { - return create_leaf_node( + return create_state_node( 'hero_points', effect.target as FactionId, args @@ -3227,20 +3181,20 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { condition: effect.type === 'hero_points' && effect.target === 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 === 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 === INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('draw_card', game.initiative, args); + return create_state_node('draw_card', game.initiative, args); }, }, { @@ -3248,7 +3202,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'draw_card' && role_ids.includes(effect.target as FactionId), resolve: () => { - return create_leaf_node('draw_card', effect.target as FactionId, args); + return create_state_node('draw_card', effect.target as FactionId, args); }, }, { @@ -3256,7 +3210,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { resolve: () => { return create_seq_node( get_player_order(get_active_faction()).map((faction) => - create_leaf_node('draw_card', faction, args) + create_state_node('draw_card', faction, args) ) ); }, @@ -3264,19 +3218,19 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { { condition: effect.type === 'draw_card' && effect.target === OTHER_PLAYERS, resolve: () => { - const leaf_nodes = get_player_order(get_active_faction()).map( - (faction) => create_leaf_node('draw_card', faction, args) + const state_nodes = get_player_order(get_active_faction()).map( + (faction) => create_state_node('draw_card', faction, args) ); - array_remove(leaf_nodes, 0); // Remove current player - return create_seq_node(leaf_nodes); + array_remove(state_nodes, 0); // Remove current player + return create_seq_node(state_nodes); }, }, { condition: effect.type === 'play_card', resolve: () => { return create_seq_node([ - create_leaf_node('play_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 }), ]); }, }, @@ -3305,14 +3259,6 @@ function win_game(player: Player, glory: number) { // #endregion // #region CARDS -// function draw_faction_card(faction: Player): CardId { -// return draw_faction_cards(faction, 1)[0]; -// } - -// function draw_faction_cards(faction: Player, count: number = 1): CardId[] { -// const drawnCards = []; - -// } function draw_card(deck: CardId[]): CardId { clear_undo(); @@ -114,7 +114,7 @@ export type States = { [key: string]: any; }; -export type EngineNode = FunctionNode | LeafNode | SeqNode; +export type EngineNode = FunctionNode | StateNode | SeqNode; export interface FunctionNode { t: 'f'; @@ -128,7 +128,7 @@ export interface SeqNode { c: EngineNode[]; } -export interface LeafNode<T = any> { +export interface StateNode<T = any> { t: 'l'; s: string; // State p: FactionId | 'None' | 'all' | FactionId[]; // Player |