From b9aced407fd405cdc46df7d5dde9968909724b3d Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Oct 2024 00:45:54 +0100 Subject: Introductory game. --- play.js | 164 ++++++++++++++++++++++++++++++++++----------------------------- rules.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 238 insertions(+), 86 deletions(-) diff --git a/play.js b/play.js index 6ffb8df..cef2bdd 100644 --- a/play.js +++ b/play.js @@ -1168,7 +1168,9 @@ function player_from_power(pow) { case P_PRUSSIA: return R_FREDERICK case P_PRAGMATIC: - return R_FREDERICK + if (view.pol_deck) + return R_FREDERICK + return "NONE" case P_AUSTRIA: return R_MARIA_THERESA case P_SAXONY: @@ -1233,6 +1235,36 @@ function show_tc(c) { return ui.tc[c] } +function update_political() { + ui.pol_tracks.replaceChildren() + layout_political_marker(ui.saxony, view.saxony, 0) + layout_political_marker(ui.russia, view.russia, 1) + layout_political_marker(ui.italy, view.italy, 2) + + ui.pc_deck.replaceChildren() + for (let e of ui.political_back[view.pol_deck]) + ui.pc_deck.appendChild(e) + if (view.flags & F_IMPERIAL_ELECTION) + ui.pc_deck.appendChild(ui.political[24]) + + for (let pc = 0; pc < 25; ++pc) + ui.political[pc].classList.toggle("selected", pc === view.pc) + + for (let pow of all_major_powers) { + ui.pc_placed[pow].replaceChildren() + for (let tc of view.face_up[pow]) + ui.pc_placed[pow].appendChild(show_tc(tc)) + for (let tc of view.face_down[pow]) + ui.pc_placed[pow].appendChild(show_tc(tc)) + } + + ui.pc_show.replaceChildren() + if (view.political) { + for (let pc of view.political) + ui.pc_show.appendChild(ui.political[pc]) + } +} + function on_update() { ui.header.classList.toggle("france", view.power === P_FRANCE) ui.header.classList.toggle("bavaria", view.power === P_BAVARIA) @@ -1252,6 +1284,10 @@ function on_update() { back.fill(0) sort_power_panel(true) + if (!view.pol_deck) { + ui.power_panel[P_PRAGMATIC].style.display = "none" + ui.power_panel[6].style.display = "none" + } for (let p = 0; p < 20; ++p) layout_general(p, view.pos[p]) @@ -1292,33 +1328,8 @@ function on_update() { ui.hand[view.power].appendChild(ui.tc[c]) } - ui.pol_tracks.replaceChildren() - layout_political_marker(ui.saxony, view.saxony, 0) - layout_political_marker(ui.russia, view.russia, 1) - layout_political_marker(ui.italy, view.italy, 2) - - ui.pc_deck.replaceChildren() - for (let e of ui.political_back[view.pol_deck]) - ui.pc_deck.appendChild(e) - if (view.flags & F_IMPERIAL_ELECTION) - ui.pc_deck.appendChild(ui.political[24]) - - for (let pc = 0; pc < 25; ++pc) - ui.political[pc].classList.toggle("selected", pc === view.pc) - - for (let pow of all_major_powers) { - ui.pc_placed[pow].replaceChildren() - for (let tc of view.face_up[pow]) - ui.pc_placed[pow].appendChild(show_tc(tc)) - for (let tc of view.face_down[pow]) - ui.pc_placed[pow].appendChild(show_tc(tc)) - } - - ui.pc_show.replaceChildren() - if (view.political) { - for (let pc of view.political) - ui.pc_show.appendChild(ui.political[pc]) - } + if (view.pol_deck) + update_political() for (let deck = 0; deck < 4; ++deck) { ui.discard[deck].replaceChildren() @@ -1330,58 +1341,61 @@ function on_update() { for (let i = 0; i < view.victory.length; i += 2) layout_victory(view.victory[i], view.victory[i+1]) - for (let i = 0; i < view.elector.length; i += 2) - layout_elector(view.elector[i], view.elector[i+1]) for (let i = 0; i < view.retro.length; i += 2) layout_retro(view.retro[i], view.retro[i+1]) - layout_electoral_college(0, P_PRAGMATIC) - for (let i = 0; i < all_electoral_colleges.length; ++i) { - for (let pow of all_powers) - if (is_power_controlled_fortress(pow, all_electoral_colleges[i])) - layout_electoral_college(i+1, pow) - } + if (view.pol_deck) { + for (let i = 0; i < view.elector.length; i += 2) + layout_elector(view.elector[i], view.elector[i+1]) - layout_victory_pool(P_FRANCE, 11, 83, 1560) - layout_victory_pool(P_AUSTRIA, 8, 2334, 1561) - layout_victory_pool(P_PRUSSIA, 13, 1927, 76) - layout_victory_pool(P_PRAGMATIC, 8, 797, 98) - - // battle vp - if (view.vp[P_PRUSSIA] > 0) layout_victory_box(P_PRUSSIA, 0, 0) - if (view.vp[P_PRUSSIA] > 1) layout_victory_box(P_PRUSSIA, 0, 1) - if (view.vp[P_PRAGMATIC] > 0) layout_victory_box(P_PRAGMATIC, 1, 0) - if (view.vp[P_PRAGMATIC] > 1) layout_victory_box(P_PRAGMATIC, 1, 1) - if (view.vp[P_FRANCE] > 0) layout_victory_box(P_FRANCE, 2, 0) - if (view.vp[P_FRANCE] > 1) layout_victory_box(P_FRANCE, 2, 1) - if (view.vp[P_AUSTRIA] > 0) layout_victory_box(P_AUSTRIA, 3, 0) - if (view.vp[P_AUSTRIA] > 1) layout_victory_box(P_AUSTRIA, 3, 1) - - if (view.flags & F_SILESIA_ANNEXED) - layout_victory_box(P_PRUSSIA, 0, 2) - - let elector_france = 0 - let elector_pragmatic = 0 - for (let i = 0; i < 8; i += 2) { - if (view.elector[i+1] === P_FRANCE) - elector_france ++ - else - elector_pragmatic ++ + layout_electoral_college(0, P_PRAGMATIC) + for (let i = 0; i < all_electoral_colleges.length; ++i) { + for (let pow of all_powers) + if (is_power_controlled_fortress(pow, all_electoral_colleges[i])) + layout_electoral_college(i+1, pow) + } + + layout_victory_pool(P_FRANCE, 11, 83, 1560) + layout_victory_pool(P_AUSTRIA, 8, 2334, 1561) + layout_victory_pool(P_PRUSSIA, 13, 1927, 76) + layout_victory_pool(P_PRAGMATIC, 8, 797, 98) + + // battle vp + if (view.vp[P_PRUSSIA] > 0) layout_victory_box(P_PRUSSIA, 0, 0) + if (view.vp[P_PRUSSIA] > 1) layout_victory_box(P_PRUSSIA, 0, 1) + if (view.vp[P_PRAGMATIC] > 0) layout_victory_box(P_PRAGMATIC, 1, 0) + if (view.vp[P_PRAGMATIC] > 1) layout_victory_box(P_PRAGMATIC, 1, 1) + if (view.vp[P_FRANCE] > 0) layout_victory_box(P_FRANCE, 2, 0) + if (view.vp[P_FRANCE] > 1) layout_victory_box(P_FRANCE, 2, 1) + if (view.vp[P_AUSTRIA] > 0) layout_victory_box(P_AUSTRIA, 3, 0) + if (view.vp[P_AUSTRIA] > 1) layout_victory_box(P_AUSTRIA, 3, 1) + + if (view.flags & F_SILESIA_ANNEXED) + layout_victory_box(P_PRUSSIA, 0, 2) + + let elector_france = 0 + let elector_pragmatic = 0 + for (let i = 0; i < 8; i += 2) { + if (view.elector[i+1] === P_FRANCE) + elector_france ++ + else + elector_pragmatic ++ + } + if (elector_france >= 3) + layout_victory_box(P_FRANCE, 1, 2) + if (elector_pragmatic >= 3) + layout_victory_box(P_PRAGMATIC, 1, 2) + + if (view.flags & F_ITALY_FRANCE) + layout_victory_box(P_FRANCE, 2, 2) + if (view.flags & F_ITALY_AUSTRIA) + layout_victory_box(P_AUSTRIA, 2, 2) + + if (view.flags & F_EMPEROR_FRANCE) + layout_victory_box(P_FRANCE, 3, 2) + if (view.flags & F_EMPEROR_AUSTRIA) + layout_victory_box(P_AUSTRIA, 3, 2) } - if (elector_france >= 3) - layout_victory_box(P_FRANCE, 1, 2) - if (elector_pragmatic >= 3) - layout_victory_box(P_PRAGMATIC, 1, 2) - - if (view.flags & F_ITALY_FRANCE) - layout_victory_box(P_FRANCE, 2, 2) - if (view.flags & F_ITALY_AUSTRIA) - layout_victory_box(P_AUSTRIA, 2, 2) - - if (view.flags & F_EMPEROR_FRANCE) - layout_victory_box(P_FRANCE, 3, 2) - if (view.flags & F_EMPEROR_AUSTRIA) - layout_victory_box(P_AUSTRIA, 3, 2) layout_turn_marker() diff --git a/rules.js b/rules.js index 1d57af3..9a1395b 100644 --- a/rules.js +++ b/rules.js @@ -17,13 +17,24 @@ const R_LOUIS_XV = "Louis XV" const R_FREDERICK = "Frederick" const R_MARIA_THERESA = "Maria Theresa" +const R_PLAYER_A = "Player A" +const R_PLAYER_B = "Player B" + exports.roles = [ R_MARIA_THERESA, R_FREDERICK, R_LOUIS_XV ] -exports.scenarios = [ "Advanced" ] +const SCENARIO_INDEX = { + "Advanced": 0, + "Introductory": 1, +} + +exports.scenarios = [ + "Advanced", + "Introductory", +] const data = require("./data.js") const political_cards = require("./cards.js") @@ -855,11 +866,22 @@ function is_protected_from_conquest(s) { /* STATE */ +function is_intro() { + return game.scenario & 1 +} + +function is_two_player() { + return game.scenario & 2 +} + const tc_per_turn_table = [ 5, 3, 3, 5, 1, 1 ] function tc_per_turn() { let n = tc_per_turn_table[game.power] + if (game.power === P_FRANCE && is_intro()) + n = 3 + // Saxony track if (game.power === P_SAXONY) { if (game.saxony > 4) @@ -1095,7 +1117,10 @@ function goto_start_turn() { if (game.turn % 4 === 0) { goto_winter_turn() } else { - goto_politics() + if (is_intro()) + start_sequence_of_play() + else + goto_politics() } } @@ -1107,7 +1132,7 @@ function goto_end_turn() { goto_start_turn() } -const sequence_of_play = [ +const advanced_sequence_of_play = [ { power: P_AUSTRIA, action: goto_place_hussars }, { power: P_FRANCE, action: start_action_stage }, @@ -1177,12 +1202,66 @@ const sequence_of_play = [ { power: P_FRANCE, action: goto_end_turn }, ] +const intro_sequence_of_play = [ + { power: P_AUSTRIA, action: goto_place_hussars }, + + { power: P_FRANCE, action: start_action_stage }, + { power: P_FRANCE, action: goto_tactical_cards }, + { power: P_BAVARIA, action: goto_tactical_cards }, + { power: P_FRANCE, action: goto_supply }, + { power: P_FRANCE, action: init_movement }, + { power: P_FRANCE, action: goto_movement_global }, + { power: P_FRANCE, action: end_movement }, + { power: P_FRANCE, action: goto_combat }, + { power: P_FRANCE, action: end_action_stage }, + + { power: P_PRUSSIA, action: start_action_stage }, + { power: P_PRUSSIA, action: goto_tactical_cards }, + { + power: P_SAXONY, + action() { + if (is_saxony_prussian()) + goto_tactical_cards() + else + next_sequence_of_play() + }, + }, + { power: P_PRUSSIA, action: goto_supply }, + { power: P_PRUSSIA, action: init_movement }, + { power: P_PRUSSIA, action: goto_movement_global }, + { power: P_PRUSSIA, action: end_movement }, + { power: P_PRUSSIA, action: goto_combat }, + { power: P_PRUSSIA, action: end_action_stage }, + + { power: P_AUSTRIA, action: start_action_stage }, + { power: P_AUSTRIA, action: goto_tactical_cards }, + { + power: P_SAXONY, + action() { + if (is_saxony_prussian()) + next_sequence_of_play() + else + goto_tactical_cards() + }, + }, + { power: P_AUSTRIA, action: goto_supply }, + { power: P_AUSTRIA, action: init_movement }, + { power: P_AUSTRIA, action: goto_movement_global }, + { power: P_AUSTRIA, action: end_movement }, + { power: P_AUSTRIA, action: goto_combat }, + { power: P_AUSTRIA, action: end_action_stage }, + + { power: P_FRANCE, action: goto_end_turn }, +] + function start_sequence_of_play() { game.stage = -1 next_sequence_of_play() } function current_sequence_of_play() { + if (is_intro()) + return intro_sequence_of_play[game.stage] return sequence_of_play[game.stage] } @@ -1913,6 +1992,12 @@ function give_troops(total) { function init_movement() { set_clear(game.moved) + if (is_intro()) { + for (let p of all_pieces) + if (is_flanders_space(game.pos[p])) + set_add(game.moved, p) + } + log_br() game.move_re_entered = 0 @@ -2207,6 +2292,8 @@ function can_move_train_to(p, from, to) { } function is_illegal_cross_map_move(from, to) { + if (is_intro() && is_flanders_space(to)) + return true return ( game.power !== P_FRANCE && game.power !== P_AUSTRIA && ( (is_flanders_space(from) && is_bohemia_space(to)) || @@ -2959,6 +3046,11 @@ function goto_winter_turn() { function goto_winter_stage() { set_active_to_current_winter_stage() + if (is_intro() && game.power === P_PRAGMATIC) { + end_winter_stage() + return + } + log(title_from_winter_stage[game.stage]) goto_recruit() @@ -3032,6 +3124,8 @@ states.recruit = { let av_general = 0 let av_troops = 0 for (let p of all_power_generals[game.power]) { + if (is_intro() && is_flanders_space(game.pos[p])) + continue if (is_piece_on_map(p)) av_troops += 8 - game.troops[p] else if (is_piece_eliminated(p) && has_re_entry_space_for_general(p)) { @@ -3066,6 +3160,8 @@ states.recruit = { if (paid >= 4) { for (let p of all_power_generals[game.power]) { + if (is_intro() && is_flanders_space(game.pos[p])) + continue if (game.troops[p] > 0 && game.troops[p] < 8) gen_action_piece(p) else if (is_piece_eliminated(p) && has_re_entry_space_for_general(p)) @@ -3811,6 +3907,13 @@ function lose_vp(pow, n) { } function finish_combat() { + if (is_intro()) { + delete game.lost_generals + delete game.lost_troops + next_combat() + return + } + let n = game.lost_troops / 3 | 0 if (n === 0 && game.lost_generals > 0) n = 1 @@ -5454,8 +5557,16 @@ const POWER_FROM_SETUP_STAGE = [ P_AUSTRIA, ] +const INTRO_POWER_FROM_SETUP_STAGE = [ + P_PRUSSIA, + P_AUSTRIA, +] + function set_active_setup_power() { - set_active_to_power(POWER_FROM_SETUP_STAGE[game.stage]) + if (is_intro()) + set_active_to_power(INTRO_POWER_FROM_SETUP_STAGE[game.stage]) + else + set_active_to_power(POWER_FROM_SETUP_STAGE[game.stage]) } const setup_initial_tcs = [ 2, 9, 3, 5, 5, 3 ] @@ -5595,9 +5706,10 @@ function make_tactics_discard(n) { }) } -exports.setup = function (seed, _scenario, _options) { +exports.setup = function (seed, scenario, _options) { game = { seed: seed, + scenario: SCENARIO_INDEX[scenario], undo: [], log: [], @@ -5620,7 +5732,6 @@ exports.setup = function (seed, _scenario, _options) { loser_power: -1, trump: SPADES, - pol_deck: null, deck: null, hand1: [ [], [], [], [], [], [] ], hand2: [ [], [], [], [], [], [] ], @@ -5651,7 +5762,20 @@ exports.setup = function (seed, _scenario, _options) { count: 0, } - game.pol_deck = make_political_deck() + if (is_intro()) { + log("Introductory") + + game.troops[ARENBERG] = 4 + game.troops[0] = 3 + game.troops[1] = 8 + game.troops[2] = 8 + game.troops[3] = 3 + game.troops[4] = 4 + } + + if (!is_intro()) + game.pol_deck = make_political_deck() + game.deck = make_tactics_deck(0) shuffle_bigint(game.deck) @@ -5670,12 +5794,17 @@ exports.setup = function (seed, _scenario, _options) { map_set(game.victory, COSEL, P_AUSTRIA) // Deal initial cards - for (let pow of all_powers) + for (let pow of all_powers) { + if (is_intro() && pow === P_PRAGMATIC) + continue for (let i = 0; i < setup_initial_tcs[pow]; ++i) set_add(game.hand1[pow], game.deck.pop()) + } log("# 1741") + goto_setup() + return game } @@ -5759,8 +5888,15 @@ states.setup_general = { }, } +function goto_setup() { + game.stage = 0 + set_active_setup_power() + game.state = "setup" +} + function end_setup() { - if (++game.stage === 4) { + let end = is_intro() ? 2 : 4 + if (++game.stage === end) { goto_start_turn() } else { set_active_setup_power() @@ -5865,7 +6001,6 @@ exports.view = function (state, player) { discard: total_discard_list(), contracts: game.contracts, - pol_deck: mask_pol_deck(), face_up: game.face_up, face_down: mask_face_down(), @@ -5878,6 +6013,9 @@ exports.view = function (state, player) { view.defender = game.defender } + if (!is_intro()) + view.pol_deck = mask_pol_deck() + if (game.political) view.political = game.political @@ -5906,7 +6044,7 @@ exports.view = function (state, player) { } // subsidy contracts actions for active player - if (!game.proposal) { + if (!game.proposal && !is_intro()) { if (may_cancel_subsidy()) view.actions.cancel_subsidy = 1 view.actions.propose_subsidy = 1 -- cgit v1.2.3