From a4c2b8458d1059c373c4a714bce0b5f68a3ce20f Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 2 Mar 2025 12:03:00 +0100 Subject: Some rules.ts changes for new client. Remove unused properties from view object. Use separate fascist_cards and trash view properties. Remove common selectable_cards property. Use game.fascist when resolving fascist event and test. - prevent saving undo states (no choices here) - color header brown for active player (not their own faction) Use abbreviated player order in view.player_order. --- rules.js | 80 +++++++++++++++++++++++------------------------- rules.ts | 101 +++++++++++++++++++++++++++++++------------------------------ types.d.ts | 24 +++++++-------- 3 files changed, 100 insertions(+), 105 deletions(-) diff --git a/rules.js b/rules.js index f8eb5d9..46a9176 100644 --- a/rules.js +++ b/rules.js @@ -80,7 +80,7 @@ function gen_spend_hero_points() { } function action(state, player, action, arg) { game = state; - if (action !== 'undo' && game.state !== 'choose_card') { + if (action !== 'undo' && game.state !== 'choose_card' && !game.fascist) { push_undo(); } let S = states[game.state]; @@ -134,11 +134,13 @@ function setup_bag_of_glory() { next(); } function setup_choose_card() { + game.fascist = 0; game.engine = [create_state_node('choose_card', 'all')]; game.engine.push(create_function_node('setup_player_turn')); next(); } function setup_final_bid() { + game.fascist = 0; log_h1('Final Bid'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => create_state_node('choose_final_bid', faction_id)); @@ -148,6 +150,7 @@ function setup_final_bid() { next(); } function setup_player_turn() { + game.fascist = 0; const next_faction = game.first_player === null ? get_player_order()[0] : get_next_faction_in_player_order(get_active_faction()); @@ -344,38 +347,28 @@ function game_view(state, current) { game = state; const faction = current === OBSERVER ? null : player_faction_map[current]; view = { - active: game.active, - engine: game.engine, log: game.log, prompt: null, - state: game.state, bag_of_glory: game.bag_of_glory, bag_of_glory_count: game.bag_of_glory.length, bonuses: game.bonuses, - current, - current_player_faction: faction, current_events: game.current_events, first_player: game.first_player, fronts: game.fronts, glory: game.glory, hand: faction === null ? [] : game.hands[faction], - discard: faction === null ? [] : game.discard[faction], - trash: faction === null ? [] : game.trash[faction], - deck: faction === null ? [] : list_deck(faction), hero_points: game.hero_points, initiative: game.initiative, medallions: game.medallions, played_card: game.played_card, - player_order: current === OBSERVER - ? game.player_order - : get_player_order_in_game(faction).map((id) => faction_player_map[id]), - selectable_cards: game.selectable_cards, + player_order: current === OBSERVER ? get_player_order() : get_player_order_in_game(faction), selected_cards: current === OBSERVER ? [] : game.selected_cards[faction], tableaus: game.tableaus, tracks: game.tracks, triggered_track_effects: game.triggered_track_effects, used_medallions: game.used_medallions, year: game.year, + fascist: game.fascist, }; if (game.state === 'game_over') { view.prompt = game.victory; @@ -404,7 +397,6 @@ function setup(seed, _scenario, _options) { active: data_1.ANARCHIST, active_abilities: [], bag_of_glory: [data_1.ANARCHISTS_ID, data_1.COMMUNISTS_ID, data_1.MODERATES_ID], - blank_markers: [[], [], [], [], []], bonuses: [data_1.ON, data_1.ON], current_events: [], discard: { @@ -459,7 +451,6 @@ function setup(seed, _scenario, _options) { }, played_card: null, player_order: [data_1.MODERATE], - selectable_cards: [], selected_cards: { a: [], c: [], @@ -484,6 +475,7 @@ function setup(seed, _scenario, _options) { turn: 0, year: 0, glory_current_year: null, + fascist: 0, }; 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); @@ -531,6 +523,7 @@ function start_turn() { const card = cards[cardId]; log_h2('Fascist Event', 'fascist'); log(card.title); + game.fascist = 1; game.engine = card.effects.map((effect) => resolve_effect(effect, 'fascist_event')); if (game.year === 3 && game.turn === 4) { game.engine.push(create_function_node('setup_final_bid')); @@ -1403,7 +1396,8 @@ states.peek_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) { + view.fascist_cards = game.fascist_cards; + for (const c of game.fascist_cards) { gen_action_card(c); } }, @@ -1412,12 +1406,12 @@ states.peek_fascist_cards = { }, card(c) { game.top_of_events_deck = c; - for (const ec of game.selectable_cards) { + for (const ec of game.fascist_cards) { if (ec !== c) { game.discard.f.push(ec); } } - game.selectable_cards = []; + delete game.fascist_cards; resolve_active_and_proceed(); }, }; @@ -1636,16 +1630,22 @@ states.remove_attack_from_fronts = { }, }; states.return_card = { - inactive: 'return a card to their hand', + inactive: "return a card to their hand", prompt() { + const faction = get_active_faction(); 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'; - gen_action('skip'); + view.prompt = "Choose a card to return to your hand"; + view.trash = game.trash[faction]; + let possible = false; + for (let c of game.trash[faction]) { + if (c !== game.played_card) { + gen_action_card(c); + possible = true; + } } - for (const c of game.selectable_cards) { - gen_action_card(c); + if (!possible) { + view.prompt = "No card in trash to return. You must skip"; + gen_action("skip"); } }, spend_hp() { @@ -1656,11 +1656,9 @@ states.return_card = { array_remove(game.trash[faction], game.trash[faction].indexOf(c)); game.hands[faction].push(c); logi(`${faction_player_map[faction]} returns a card to their hand`); - game.selectable_cards = []; resolve_active_and_proceed(); }, skip() { - game.selectable_cards = []; resolve_active_and_proceed(); }, }; @@ -2029,8 +2027,9 @@ function card45_event2() { resolve_active_and_proceed(); } function card46_event3() { + game.fascist_cards = []; for (let i = 0; i < 3; ++i) { - game.selectable_cards.push(draw_fascist_card()); + game.fascist_cards.push(draw_fascist_card()); } resolve_active_and_proceed(); } @@ -2050,8 +2049,6 @@ function card54_event1() { resolve_active_and_proceed(); } function setup_return_card_from_trash() { - const faction = get_active_faction(); - game.selectable_cards = game.trash[faction].filter((c) => c !== game.played_card); resolve_active_and_proceed(); } function add_glory(faction, amount, indent = false) { @@ -2254,6 +2251,7 @@ function play_card(faction, type) { return card; } function resolve_fascist_test() { + game.fascist = 1; log_h2('Fascist Test', 'fascist'); const test = get_current_event().test; const status = game.fronts[test.front].status; @@ -2684,7 +2682,7 @@ function log_h1(msg) { } function log_h2(msg, player) { log_br(); - log(`.h2${player ? `.${player}` : ''} ${msg}`); + log(`#${player[0].toLowerCase()} ${msg}`); log_br(); } function log_h3(msg) { @@ -2810,18 +2808,16 @@ function list_deck(id) { const deck = []; const card_list = id === data_1.FASCIST_ID ? fascist_decks[game.year] : faction_cards[id]; card_list.forEach((card) => { - if (game.discard[id].includes(card) || - game.selectable_cards.includes(card)) { - return; - } - if (id === data_1.FASCIST_ID && game.current_events.includes(card)) { - return; + if (id === data_1.FASCIST_ID) { + if (game.current_events.includes(card)) + return; + if (game.discard[id].includes(card)) + return; } - else if (id !== data_1.FASCIST_ID && - (game.hands[id].includes(card) || - game.discard[id].includes(card) || - game.tableaus[id].includes(card) || - game.trash[id].includes(card))) { + else if (game.hands[id].includes(card) || + game.discard[id].includes(card) || + game.tableaus[id].includes(card) || + game.trash[id].includes(card)) { return; } deck.push(card); diff --git a/rules.ts b/rules.ts index 97b2350..cc35bdf 100644 --- a/rules.ts +++ b/rules.ts @@ -174,7 +174,7 @@ export function action( ) { game = state; - if (action !== 'undo' && game.state !== 'choose_card') { + if (action !== 'undo' && game.state !== 'choose_card' && !game.fascist) { push_undo(); } @@ -242,6 +242,8 @@ function setup_bag_of_glory() { } function setup_choose_card() { + game.fascist = 0; + game.engine = [create_state_node('choose_card', 'all')]; game.engine.push(create_function_node('setup_player_turn')); @@ -249,6 +251,8 @@ function setup_choose_card() { } function setup_final_bid() { + game.fascist = 0; + log_h1('Final Bid'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => @@ -261,6 +265,8 @@ function setup_final_bid() { } function setup_player_turn() { + game.fascist = 0; + const next_faction = game.first_player === null ? get_player_order()[0] @@ -513,39 +519,36 @@ function game_view(state: Game, current: Player | 'Observer') { current === OBSERVER ? null : player_faction_map[current]; view = { - active: game.active, - engine: game.engine, // TODO: remove + // active: game.active, + // engine: game.engine, // TODO: remove log: game.log, prompt: null, - state: game.state, + // state: game.state, bag_of_glory: game.bag_of_glory, bag_of_glory_count: game.bag_of_glory.length, bonuses: game.bonuses, - current, - current_player_faction: faction, + // current, + // current_player_faction: faction, current_events: game.current_events, first_player: game.first_player, fronts: game.fronts, glory: game.glory, hand: faction === null ? [] : game.hands[faction], - discard: faction === null ? [] : game.discard[faction], - trash: faction === null ? [] : game.trash[faction], - deck: faction === null ? [] : list_deck(faction), + // discard: faction === null ? [] : game.discard[faction], + // trash: faction === null ? [] : game.trash[faction], + // deck: faction === null ? [] : list_deck(faction), hero_points: game.hero_points, initiative: game.initiative, medallions: game.medallions, played_card: game.played_card, - player_order: - current === OBSERVER - ? game.player_order - : get_player_order_in_game(faction).map((id) => faction_player_map[id]), - selectable_cards: game.selectable_cards, + player_order: current === OBSERVER ? get_player_order() : get_player_order_in_game(faction), selected_cards: current === OBSERVER ? [] : game.selected_cards[faction], tableaus: game.tableaus, tracks: game.tracks, triggered_track_effects: game.triggered_track_effects, used_medallions: game.used_medallions, year: game.year, + fascist: game.fascist, }; if (game.state === 'game_over') { @@ -580,7 +583,6 @@ export function setup(seed: number, _scenario: string, _options: unknown) { active: ANARCHIST, active_abilities: [], bag_of_glory: [ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID], - blank_markers: [[], [], [], [], []], bonuses: [ON, ON], current_events: [], discard: { @@ -635,7 +637,6 @@ export function setup(seed: number, _scenario: string, _options: unknown) { }, played_card: null, player_order: [MODERATE], - selectable_cards: [], selected_cards: { a: [], c: [], @@ -660,6 +661,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) { turn: 0, year: 0, glory_current_year: null, + fascist: 0, }; // Randomly choose second player @@ -727,6 +729,7 @@ function start_turn() { log_h2('Fascist Event', 'fascist'); log(card.title); + game.fascist = 1; game.engine = card.effects.map((effect) => resolve_effect(effect, 'fascist_event') ); @@ -1719,7 +1722,8 @@ states.peek_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) { + view.fascist_cards = game.fascist_cards + for (const c of game.fascist_cards) { gen_action_card(c); } }, @@ -1728,12 +1732,12 @@ states.peek_fascist_cards = { }, card(c: CardId) { game.top_of_events_deck = c; - for (const ec of game.selectable_cards) { + for (const ec of game.fascist_cards) { if (ec !== c) { game.discard.f.push(ec); } } - game.selectable_cards = []; + delete game.fascist_cards; resolve_active_and_proceed(); }, }; @@ -2011,16 +2015,22 @@ states.remove_attack_from_fronts = { }; states.return_card = { - inactive: 'return a card to their hand', + inactive: "return a card to their hand", prompt() { + const faction = get_active_faction(); 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'; - gen_action('skip'); + view.prompt = "Choose a card to return to your hand"; + view.trash = game.trash[faction] + let possible = false; + for (let c of game.trash[faction]) { + if (c !== game.played_card) { + gen_action_card(c); + possible = true; + } } - for (const c of game.selectable_cards) { - gen_action_card(c); + if (!possible) { + view.prompt = "No card in trash to return. You must skip"; + gen_action("skip"); } }, spend_hp() { @@ -2031,15 +2041,12 @@ states.return_card = { array_remove(game.trash[faction], game.trash[faction].indexOf(c)); game.hands[faction].push(c); logi(`${faction_player_map[faction]} returns a card to their hand`); - - game.selectable_cards = []; resolve_active_and_proceed(); }, skip() { - game.selectable_cards = []; resolve_active_and_proceed(); }, -}; +} states.spend_hero_points = { inactive: 'spend Hero points', @@ -2484,8 +2491,9 @@ function card45_event2() { } function card46_event3() { + game.fascist_cards = [] for (let i = 0; i < 3; ++i) { - game.selectable_cards.push(draw_fascist_card()); + game.fascist_cards.push(draw_fascist_card()); } resolve_active_and_proceed(); } @@ -2513,10 +2521,6 @@ function card54_event1() { } function setup_return_card_from_trash() { - const faction = get_active_faction(); - game.selectable_cards = game.trash[faction].filter( - (c) => c !== game.played_card - ); resolve_active_and_proceed(); } @@ -2779,6 +2783,8 @@ function play_card( } function resolve_fascist_test() { + game.fascist = 1 + log_h2('Fascist Test', 'fascist'); const test = get_current_event().test; @@ -3357,7 +3363,7 @@ function log_h1(msg: string) { function log_h2(msg: string, player?: Player | 'fascist') { log_br(); - log(`.h2${player ? `.${player}` : ''} ${msg}`); + log(`#${player[0].toLowerCase()} ${msg}`); log_br(); } @@ -3538,21 +3544,16 @@ function list_deck(id: FactionId | 'f') { const card_list: CardId[] = id === FASCIST_ID ? fascist_decks[game.year] : faction_cards[id]; card_list.forEach((card) => { - if ( - game.discard[id].includes(card) || - game.selectable_cards.includes(card) - ) { - return; - } - - if (id === FASCIST_ID && game.current_events.includes(card)) { - return; + if (id === FASCIST_ID) { + if (game.current_events.includes(card)) + return; + if (game.discard[id].includes(card)) + return; } else if ( - id !== FASCIST_ID && - (game.hands[id].includes(card) || - game.discard[id].includes(card) || - game.tableaus[id].includes(card) || - game.trash[id].includes(card)) + game.hands[id].includes(card) || + game.discard[id].includes(card) || + game.tableaus[id].includes(card) || + game.trash[id].includes(card) ) { return; } diff --git a/types.d.ts b/types.d.ts index 4594775..fc769b6 100644 --- a/types.d.ts +++ b/types.d.ts @@ -32,7 +32,6 @@ export interface Game { turn: number; year: number; bag_of_glory: FactionId[]; - blank_markers: number[][]; bonuses: number[]; current_events: CardId[]; discard: Record; @@ -61,7 +60,7 @@ export interface Game { played_card: CardId | null; player_order: Player[]; selected_cards: Record; - selectable_cards: CardId[]; // used for specific events + fascist_cards?: CardId[]; // used for specific events tableaus: Record; /** * Used for event effect that allows Anarchist to put an event @@ -73,18 +72,16 @@ export interface Game { triggered_track_effects: number[]; used_medallions: number[]; glory_current_year?: Record | null; + fascist: 0 | 1; } export interface View { - engine: Game['engine']; log: number | string[]; - active: string | string[] | null; prompt: string | null; - state: Game['state']; actions?: any; victory?: string; - current: Player | 'Observer'; - current_player_faction: FactionId | null; + // current: Player | 'Observer'; + // current_player_faction: FactionId | null; selected_cards: number[]; bag_of_glory: Game['bag_of_glory']; // TODO: remove bag_of_glory_count: number; @@ -94,20 +91,21 @@ export interface View { fronts: Game['fronts']; glory: Game['glory']; hand: number[]; - discard: number[]; - deck: number[]; - trash: number[]; + // discard: number[]; + // deck: number[]; + fascist_cards?: number[]; + trash?: number[]; hero_points: Game['hero_points']; initiative: Game['initiative']; medallions: Game['medallions']; played_card: Game['played_card']; - player_order: Game['player_order']; - selectable_cards: Game['selectable_cards']; + player_order: FactionId[]; tableaus: Game['tableaus']; tracks: number[]; triggered_track_effects: Game['triggered_track_effects']; used_medallions: Game['used_medallions']; year: number; + fascist?: number; } export type States = { @@ -263,4 +261,4 @@ export interface PlayerTurnArgs extends EngineNodeArgsBase { use_momentum?: boolean; } -// #endregion \ No newline at end of file +// #endregion -- cgit v1.2.3