From 5ab7ccf00da33d2fcc02d582b8528596d92f22ed Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 22 Oct 2024 14:41:58 +0200 Subject: political phase (play cards, no effect) --- play.css | 10 +++ play.html | 24 ++++--- play.js | 86 +++++++++++++++++++----- rules.js | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 305 insertions(+), 40 deletions(-) diff --git a/play.css b/play.css index c915ed1..93df297 100644 --- a/play.css +++ b/play.css @@ -51,6 +51,16 @@ body { border: 1px solid #0008; } +.pc_pile { + position: absolute; + top: 325px; +} + +#display_prussia { left: 42px; } +#display_france { left: 246px; } +#display_pragmatic { left: 449px; } +#display_austria { left: 653px; } + header { background-color: hsl(44, 35%, 80%); color: #000c; } header.your_turn { color: black; } header.your_turn.france { background-color: var(--color-light-france); } diff --git a/play.html b/play.html index 07dd3e8..1152bce 100644 --- a/play.html +++ b/play.html @@ -98,24 +98,28 @@
Political Display
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+ +
+
-
-
diff --git a/play.js b/play.js index e012337..f34d5f6 100644 --- a/play.js +++ b/play.js @@ -59,6 +59,7 @@ const F_EMPEROR_AUSTRIA = 2 const F_ITALY_FRANCE = 4 const F_ITALY_AUSTRIA = 8 const F_SILESIA_ANNEXED = 16 +const F_IMPERIAL_ELECTION = 32 const SPADES = 0 const CLUBS = 1 @@ -71,6 +72,7 @@ const IMPERIAL_ELECTION = 25 const ELIMINATED = data.cities.name.length const all_powers = [ 0, 1, 2, 3, 4, 5 ] +const all_major_powers = [ 0, 1, 2, 3 ] const all_power_generals = [ [ 0, 1, 2, 3, 4 ], @@ -358,7 +360,15 @@ const ui = { spaces_element: document.getElementById("spaces"), pieces_element: document.getElementById("pieces"), markers_element: document.getElementById("markers"), - political_display: document.getElementById("political_display"), + pol_tracks: document.getElementById("pol_tracks"), + pc_deck: document.getElementById("pc_deck"), + pc_show: document.getElementById("pc_show"), + pc_display: [ + document.getElementById("display_france"), + document.getElementById("display_prussia"), + document.getElementById("display_pragmatic"), + document.getElementById("display_austria"), + ], discard: [ document.getElementById("discard_1"), document.getElementById("discard_2"), @@ -486,12 +496,18 @@ function make_tc_deck_hand(n) { return list } -function make_political_card(fc) { +function make_political_card(pc) { let e = document.createElement("div") - if (fc === 0) - e.className = "card political reverse" + if (pc === 0) + e.className = "card polcard reverse" else - e.className = "card political c" + fc + e.className = "card polcard c" + pc + return e +} + +function make_political_back(deck) { + let e = document.createElement("div") + e.className = "pile card polcard reverse deck_" + deck return e } @@ -612,8 +628,16 @@ function on_init() { ui.italy.className = "marker political_marker_italy" ui.political = [] - for (let fc = 0; fc <= 18; ++fc) - ui.political[fc] = make_political_card(fc) + for (let pc = 1; pc <= 25; ++pc) + ui.political[pc] = make_political_card(pc) + + ui.political_back = [ + [], + [ make_political_back(4), make_political_back(3), make_political_back(2), make_political_back(1) ], + [ make_political_back(4), make_political_back(3), make_political_back(2) ], + [ make_political_back(4), make_political_back(3) ], + [ make_political_back(4) ], + ] for (let a = 0; a <= last_city; ++a) { let e = (ui.cities[a] = document.createElement("div")) @@ -908,7 +932,7 @@ function layout_political_marker(e, col, row) { let y = 72 + row * 86 + 16 e.style.left = (x - 16) + "px" e.style.top = (y - 16) + "px" - ui.political_display.appendChild(e) + ui.pol_tracks.appendChild(e) } function layout_retro(s, pow) { @@ -997,6 +1021,14 @@ function on_prompt(text) { return colorize(text) } +let back = [ 0, 0, 0, 0 ] + +function show_tc(c) { + if ((c & 15) === 0) + return ui.tc_back[c>>7][back[c>>7]++] + return ui.tc[c] +} + function on_update() { ui.header.classList.toggle("france", view.power === P_FRANCE) ui.header.classList.toggle("bavaria", view.power === P_BAVARIA) @@ -1009,6 +1041,8 @@ function on_update() { used_elector.fill(0) used_retro.fill(0) + back.fill(0) + sort_power_panel(true) for (let p = 0; p < 20; ++p) @@ -1018,8 +1052,6 @@ function on_update() { for (let p = 30; p < 32; ++p) layout_hussar(p, view.pos[p]) - let back = [ 0, 0, 0, 0 ] - for (let pow of all_powers) { /* let banner = `${power_name[pow]} \u2014 ${view.pt[pow]} troops` @@ -1040,10 +1072,7 @@ function on_update() { } else { view.hand[pow].sort(cmp_tc) for (let c of view.hand[pow]) { - if ((c & 15) === 0) - ui.hand[pow].appendChild(ui.tc_back[c>>7][back[c>>7]++]) - else - ui.hand[pow].appendChild(ui.tc[c]) + ui.hand[pow].appendChild(show_tc(c)) } } } @@ -1056,11 +1085,31 @@ function on_update() { ui.hand[view.power].appendChild(ui.tc[c]) } - ui.political_display.replaceChildren() + 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[25]) + + for (let pow of all_major_powers) { + ui.pc_display[pow].replaceChildren() + for (let tc of view.saved[pow]) + ui.pc_display[pow].appendChild(show_tc(tc)) + if (view.display[pow] >= 0) + ui.pc_display[pow].appendChild(show_tc(view.display[pow])) + } + + ui.pc_show.replaceChildren() + if (view.political) { + for (let pc of view.political) + ui.pc_show.appendChild(ui.political[pc]) + } + for (let deck = 0; deck < 4; ++deck) { ui.discard[deck].replaceChildren() for (let i = 0; i < view.discard[deck]; ++i) @@ -1164,6 +1213,12 @@ function on_update() { /* LOG */ +function sub_political(match, p1) { + let x = p1 | 0 + let n = political_name[x] + return `${n}` +} + function sub_piece(match, p1) { let x = p1 | 0 let n = piece_log_name[x] @@ -1213,6 +1268,7 @@ function on_log(text) { text = colorize(text) text = text.replace(/S(\d+)/g, sub_space) text = text.replace(/P(\d+)/g, sub_piece) + text = text.replace(/C(\d+)/g, sub_political) if (text.startsWith("#")) { p.className = "h turn" diff --git a/rules.js b/rules.js index 8a123f6..357fe73 100644 --- a/rules.js +++ b/rules.js @@ -6,23 +6,20 @@ show who controls which power in player list +set-aside victory marker areas + austria/pragmatic move order on flanders (start with pragmatic) only stack if agree (undo/move to previous space) winter scoring -tc draw - no TCs for minor power if major fortress enemy controlled +when are subsidies given? + example france subsidy to prussia + at france's stage? + at prussia's stage? -- advanced -- -VICTORY BOXES - vp for winning (tracks) - electors (check victory markers) - emperor (bit) - italy (bit) - silesia (bit) - silesia home country for prussia tc subsidies @@ -49,7 +46,6 @@ neutrality // PLAN: move all austria on bohemia first, then alternate pragmatic and austria activations on flanders map // TODO: confirm mixed stack creation on flanders map (force "undo" to previous location if denied?) -// TODO: supreme commander in mixed stacks // TODO: TC subsidies // TODO: subsidy markers? @@ -93,6 +89,7 @@ const F_EMPEROR_AUSTRIA = 2 const F_ITALY_FRANCE = 4 const F_ITALY_AUSTRIA = 8 const F_SILESIA_ANNEXED = 16 +const F_IMPERIAL_ELECTION = 32 // imperial election card revealed! const SPADES = 0 const CLUBS = 1 @@ -105,6 +102,7 @@ const IMPERIAL_ELECTION = 25 const ELIMINATED = data.cities.name.length const all_powers = [ 0, 1, 2, 3, 4, 5 ] +const all_major_powers = [ 0, 1, 2, 3 ] const all_power_generals = [ [ 0, 1, 2, 3, 4 ], @@ -1093,6 +1091,12 @@ function count_used_cards() { held[to_deck(c)]++ } + // count cards in saved political display + for (let pow of all_major_powers) { + for (let c of game.saved[pow]) + held[to_deck(c)]++ + } + // count cards currently being held if (game.draw) for (let c of game.draw) @@ -3112,8 +3116,6 @@ function finish_combat() { lose_vp(game.loser_power, n) } - delete game.loser_power - delete game.winner_power delete game.lost_generals delete game.lost_troops @@ -3218,11 +3220,151 @@ function check_instant_victory() { /* POLITICS */ +const POWER_FROM_POLITICAL_STAGE = [ + P_PRUSSIA, + P_FRANCE, + P_PRAGMATIC, + P_AUSTRIA, +] + +function set_active_political_power() { + set_active_to_power(POWER_FROM_POLITICAL_STAGE[game.stage]) +} + function goto_politics() { - end_politics() + game.political = [] + game.display = [ 0, 0, 0, 0 ] + game.stage = 0 + + while (game.political.length < 2) { + let pc = game.pol_deck.pop() + if (pc === IMPERIAL_ELECTION) + game.flags |= F_IMPERIAL_ELECTION + else + game.political.push(pc) + } + + if (game.winner_power >= 0) { + set_active_to_power(game.winner_power) + game.state = "determine_trump_suit" + } else { + for (;;) { + draw_tc(1) + log("Trump " + format_card(game.draw[0]) + ".") + if (!is_reserve(game.draw[0])) { + game.trump = to_suit(game.draw[0]) + break + } + delete game.draw + } + goto_place_tc_on_display() + } +} + +states.determine_trump_suit = { + inactive: "determine the political trump suit", + prompt() { + prompt("Determine the political trump suit.") + view.actions.suit = [ 0, 1, 2, 3 ] + }, + suit(s) { + game.trump = s + goto_place_tc_on_display() + } +} + +function goto_place_tc_on_display() { + set_active_political_power() + game.state = "place_tc_on_display" +} + +states.place_tc_on_display = { + inactive: "place TC on political display", + prompt() { + prompt(`Place TC on political display (${suit_name[game.trump]} is trump).`) + for (let c of game.hand[game.power]) + gen_action_card(c) + view.actions.pass = 1 + }, + card(c) { + push_undo() + log(power_name[game.power] + " placed a TC.") + set_delete(game.hand[game.power], c) + game.display[game.power] = c + game.state = "place_tc_on_display_done" + }, + pass() { + log(power_name[game.power] + " passed.") + end_place_tc_on_display() + }, +} + +states.place_tc_on_display_done = { + inactive: "place TC on political display", + prompt() { + prompt(`Place TC on political display (${suit_name[game.trump]} is trump) done.`) + view.actions.next = 1 + }, + next() { + end_place_tc_on_display() + }, +} + +function end_place_tc_on_display() { + if (++game.stage === 4) + goto_determine_order_of_influence() + else + goto_place_tc_on_display() +} + +function goto_determine_order_of_influence() { + // Turn cards face-up and return bluff cards + for (let pow of all_major_powers) { + let c = game.display[pow] + if (c > 0) { + if (to_suit(c) === game.trump) { + log(power_name[pow] + " played " + format_card(c) + ".") + set_add(game.saved[pow], c) + } else { + log(power_name[pow] + " bluffed " + format_card(c) + ".") + set_add(game.hand[pow], c) + } + } + } + delete game.display + + game.stage = 4 + goto_select_political_cards() +} + +function goto_select_political_cards() { + if (--game.stage < 0) + end_politics() + set_active_to_power(most_influence()) + game.state = "select_political_card" +} + +states.select_political_card = { + inactive: "select a political card", + prompt() { + prompt(`Select a political card or save your TC.`) + for (let pc of game.political) + gen_action_political(pc) + view.actions.pass = 1 + }, + political(pc) { + push_undo() + log(power_name[game.power] + " chose C" + pc + ".") + throw "TODO" + }, + pass() { + log(power_name[game.power] + " saved its TC.") + goto_select_political_cards() + }, } function end_politics() { + delete game.political if (check_instant_victory()) return goto_place_hussars() @@ -3238,8 +3380,7 @@ const POWER_FROM_SETUP_STAGE = [ ] function set_active_setup_power() { - game.power = POWER_FROM_SETUP_STAGE[game.stage] - game.active = current_player() + set_active_to_power(POWER_FROM_SETUP_STAGE[game.stage]) } const setup_initial_tcs = [ 2, 9, 3, 5, 5, 3 ] @@ -3366,6 +3507,9 @@ function make_tactics_discard(n) { for (let pow of all_powers) if (set_has(game.hand[pow], c)) return false + for (let pow of all_major_powers) + if (set_has(game.saved[pow], c)) + return false return true }) } @@ -3389,9 +3533,16 @@ exports.setup = function (seed, _scenario, _options) { italy: 5, // political track flags: 0, // emperor vp, italy vp, silesia annexed, etc + // for tracking VP gains/losses and political trump suit + winner_power: -1, + loser_power: -1, + trump: SPADES, + pol_deck: null, deck: null, hand: [ [], [], [], [], [], [] ], + saved: [ [], [], [], [] ], + display: [ [], [], [], [] ], pos: setup_piece_position.slice(), oos: 0, @@ -3526,6 +3677,16 @@ function end_setup() { /* VIEW */ +function mask_pol_deck() { + if (game.pol_deck.length > 0) { + let top = game.pol_deck[game.pol_deck.length - 1] + if (top === IMPERIAL_ELECTION) + return 2 + return 1 + ((top-1) / 6 | 0) + } + return 0 +} + function mask_troops(player) { let view_troops = [] for (let pow of all_powers) { @@ -3559,6 +3720,30 @@ function mask_hand(player) { return view_hand } +function mask_display(player) { + let view_display = [] + for (let pow of all_major_powers) { + if (game.display[pow] === 0) + view_display[pow] = -1 + else if (player_from_power(pow) === player) + view_display[pow] = game.display[pow] + else + view_display[pow] = game.display[pow] & ~127 + } + return view_display +} + +function mask_saved(player) { + let view_saved = [] + for (let pow of all_major_powers) { + if (player_from_power(pow) === player) + view_saved[pow] = game.saved[pow] + else + view_saved[pow] = game.saved[pow].map(c => c & ~127) + } + return view_saved +} + function total_troops_list() { let list = [] for (let pow of all_powers) { @@ -3594,6 +3779,9 @@ exports.view = function (state, player) { pt: total_troops_list(), discard: total_discard_list(), + pol_deck: mask_pol_deck(), + saved: mask_saved(player), + power: game.power, retro: game.retro, } @@ -3603,6 +3791,9 @@ exports.view = function (state, player) { view.defender = game.defender } + if (game.display) + view.display = mask_display(player) + if (game.state === "game_over") { view.prompt = game.victory view.troops = game.troops @@ -3693,6 +3884,10 @@ function gen_action_card(c) { gen_action("card", c) } +function gen_action_political(c) { + gen_action("political", c) +} + function log(msg) { game.log.push(msg) } -- cgit v1.2.3