diff options
author | Tor Andersson <tor@ccxvii.net> | 2022-05-21 13:26:14 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2022-05-21 13:33:56 +0200 |
commit | 9be7f7d8beb3d037cc656b06d8289c7133d1249e (patch) | |
tree | c64e4f861a8243e2de534c2020737aaed678a845 | |
parent | faba28c7c3c55124f8be117cfee9d75b33ec1c7c (diff) | |
download | washingtons-war-9be7f7d8beb3d037cc656b06d8289c7133d1249e.tar.gz |
Keep the bit rot away.
-rw-r--r-- | cover.1x.jpg | bin | 9402 -> 27012 bytes | |||
-rw-r--r-- | cover.2x.jpg | bin | 29152 -> 57726 bytes | |||
-rw-r--r-- | info/playbook.html | 4 | ||||
-rw-r--r-- | info/rulebook.html | 4 | ||||
-rw-r--r-- | play.css | 20 | ||||
-rw-r--r-- | play.html | 11 | ||||
-rw-r--r-- | play.js | 81 | ||||
-rw-r--r-- | rules.js | 304 | ||||
-rw-r--r-- | title.sql | 2 |
9 files changed, 225 insertions, 201 deletions
diff --git a/cover.1x.jpg b/cover.1x.jpg Binary files differindex 75e8673..7889c4d 100644 --- a/cover.1x.jpg +++ b/cover.1x.jpg diff --git a/cover.2x.jpg b/cover.2x.jpg Binary files differindex 31c1a36..09dc03e 100644 --- a/cover.2x.jpg +++ b/cover.2x.jpg diff --git a/info/playbook.html b/info/playbook.html index 2e5e291..47e8fb1 100644 --- a/info/playbook.html +++ b/info/playbook.html @@ -2,11 +2,11 @@ <html> <head> <title>Washington's War - Playbook</title> -<link rel="stylesheet" href="/fonts/gentium.css"> +<link rel="stylesheet" href="/fonts/fonts.css"> <style> body{background-color:slategray} div{position:relative;background-color:white;margin:1em auto;line-height:0.8;box-shadow:1px 1px 8px -2px black} -p{position:absolute;white-space:pre;margin:0;font-family:Gentium Basic} +p{position:absolute;white-space:pre;margin:0;font-family:Times New Roman} div{width:765pt;height:990pt;background-position:-26pt -26pt;} div p{transform:translate(-26pt,-26pt)} </style> diff --git a/info/rulebook.html b/info/rulebook.html index 35fa587..c969308 100644 --- a/info/rulebook.html +++ b/info/rulebook.html @@ -2,11 +2,11 @@ <html> <head> <title>Washington's War - Rules</title> -<link rel="stylesheet" href="/fonts/gentium.css"> +<link rel="stylesheet" href="/fonts/fonts.css"> <style> body{background-color:slategray} div{position:relative;background-color:white;margin:1em auto;line-height:0.8;box-shadow:1px 1px 8px -2px black} -p{position:absolute;white-space:pre;margin:0;font-family:Gentium Basic} +p{position:absolute;white-space:pre;margin:0;font-family:Times New Roman} </style> </head> <body> @@ -1,10 +1,13 @@ main { background-color: slategray; } header { background-color: silver; } +aside { background-color: #f5e8d7; } body.British header.your_turn { background-color: salmon; } body.American header.your_turn { background-color: skyblue; } -aside { background-color: #f5e8d7; } -.one .role_name { background-color: skyblue; } -.two .role_name { background-color: salmon; } +#role_American .role_name { background-color: skyblue; } +#role_British .role_name { background-color: salmon; } +.role_vp { float: right; } + +#log > div { padding-left: 20px; text-indent: -12px; } #log .h1 { background-color: tan; @@ -22,7 +25,7 @@ aside { background-color: #f5e8d7; } #log .h2.american { background-color: skyblue; } #log .h2.british { background-color: salmon; } -#log .card_tip { text-decoration: dotted underline; } +#log .card_tip:hover { text-decoration: dotted underline; } aside { width: 230px; @@ -42,6 +45,15 @@ aside { margin: 5px auto; } +.hand { + margin: 15px; + display: flex; + flex-wrap: wrap; + justify-content: center; + min-height: 370px; + gap: 15px; +} + /* CARD ACTION POPUP MENU */ #popup { @@ -6,10 +6,9 @@ <title>WASHINGTON'S WAR</title> <link rel="icon" href="favicon.png"> <link rel="stylesheet" href="/fonts/fonts.css"> -<link rel="stylesheet" href="/common/grid.css"> +<link rel="stylesheet" href="/common/play.css"> <link rel="stylesheet" href="play.css"> -<script defer src="/socket.io/socket.io.min.js"></script> -<script defer src="/common/client.js"></script> +<script defer src="/common/play.js"></script> <script defer src="data.js"></script> <script defer src="cards.js"></script> <script defer src="play.js"></script> @@ -54,8 +53,6 @@ <div class="menu"> <div class="menu_title"><img src="/images/cog.svg"></div> <div class="menu_popup"> - <div class="menu_item" onclick="toggle_fullscreen()">Fullscreen</div> - <div class="menu_separator"></div> <div class="menu_item" onclick="window.open('info/playbook.html', '_blank')">Playbook</div> <div class="menu_item" onclick="window.open('info/rulebook.html', '_blank')">Rulebook</div> <div class="menu_item" onclick="window.open('cards.html', '_blank')">Cards</div> @@ -77,7 +74,7 @@ <aside> <div class="roles"> - <div class="role one"> + <div class="role" id="role_American"> <div class="role_name"> American <div class="role_vp" id="american_vp">VP</div> @@ -85,7 +82,7 @@ </div> <div class="role_info" id="american_info">$N cards in hand.</div> </div> - <div class="role two"> + <div class="role" id="role_British"> <div class="role_name"> British <div class="role_vp" id="british_vp"></div> @@ -17,7 +17,7 @@ let ui = { cu: [], }; -create_log_entry = function (text) { +function on_log(text) { let p = document.createElement("div"); text = text.replace(/&/g, "&"); text = text.replace(/</g, "<"); @@ -193,21 +193,21 @@ function update_units() { const cuX = 20; const cuY = 10; - update_marker(ui.turn, "Game Turn " + game.year); - if (game.regulars) + update_marker(ui.turn, "Game Turn " + view.year); + if (view.regulars) ui.turn.classList.remove("no-regulars"); else ui.turn.classList.add("no-regulars"); - update_marker(ui.congress, game.congress); + update_marker(ui.congress, view.congress); - update_marker(ui.french_alliance, "French Alliance Track " + game.french_alliance); - if (game.european_war) + update_marker(ui.french_alliance, "French Alliance Track " + view.french_alliance); + if (view.european_war) ui.french_alliance.classList.add("european-war"); else ui.french_alliance.classList.remove("european-war"); - if (game.french_navy == "French Reinforcements") { + if (view.french_navy == "French Reinforcements") { let x = BOXES["French Reinforcements"].x-130/2-10; let y = BOXES["French Reinforcements"].y-32; let w = 126/2; @@ -215,11 +215,11 @@ function update_units() { ui.french_navy.style.left = ((x-w/2)|0) + "px"; ui.french_navy.style.top = ((y-h/2)|0) + "px"; } else { - update_marker(ui.french_navy, game.french_navy); + update_marker(ui.french_navy, view.french_navy); } for (let space in SPACES) { - let space_pc = game.pc[space]; + let space_pc = view.pc[space]; let e = document.getElementById(space+"-pc"); if (e) { if (space_pc === BRITISH) { @@ -238,9 +238,9 @@ function update_units() { for (let c in COLONIES) { let control = 0; for (let space of COLONIES[c]) { - if (game.pc[space] == BRITISH) + if (view.pc[space] == BRITISH) --control; - else if (game.pc[space] == AMERICAN) + else if (view.pc[space] == AMERICAN) ++control; } if (control < 0) @@ -254,7 +254,7 @@ function update_units() { let offset = {}; for (let g in GENERALS) { let e = ui.generals[g]; - let unit = game.generals[g]; + let unit = view.generals[g]; let space = SPACES[unit.location] || BOXES[unit.location]; if (space) { let o = (offset[space.name]|0); @@ -264,7 +264,7 @@ function update_units() { } else { e.classList.add("offmap"); } - if (game.who == g) + if (view.who == g) e.classList.add("selected"); else e.classList.remove("selected"); @@ -273,8 +273,8 @@ function update_units() { // TODO: reuse CU elements offset = {}; clear_group("cu"); - for (let i = 0; i < game.cu.length; ++i) { - let cu = game.cu[i]; + for (let i = 0; i < view.cu.length; ++i) { + let cu = view.cu[i]; let space = SPACES[cu.location] || BOXES[cu.location]; let o = (offset[space.name]|0); let x = space.x + o * cuX; @@ -296,9 +296,9 @@ function player_info(player, nc, nq) { if (nq > 0) info += "\n" + nq + " OPS in queue."; if (player == AMERICAN) { - if (game.pennsylvania_and_new_jersey_line_mutinies) + if (view.pennsylvania_and_new_jersey_line_mutinies) info += "\nPennsylvania and New Jersey Line Mutinies!"; - if (game.congress == CONTINENTAL_CONGRESS_DISPERSED) + if (view.congress == CONTINENTAL_CONGRESS_DISPERSED) info += "\nContinental Congress Dispersed!"; } return info; @@ -307,13 +307,13 @@ function player_info(player, nc, nq) { function on_update() { let e; - document.getElementById("british_info").textContent = player_info(BRITISH, game.b_cards, game.b_queue); - document.getElementById("american_info").textContent = player_info(AMERICAN, game.a_cards, game.a_queue); + document.getElementById("british_info").textContent = player_info(BRITISH, view.b_cards, view.b_queue); + document.getElementById("american_info").textContent = player_info(AMERICAN, view.a_cards, view.a_queue); - if (!game.last_played) + if (!view.last_played) document.getElementById("last_played").className = "card show card_back"; else - document.getElementById("last_played").className = "card show card_" + game.last_played; + document.getElementById("last_played").className = "card show card_" + view.last_played; action_button("pickup_british_cu", "Pick up British CU"); action_button("pickup_american_cu", "Pick up American CU"); @@ -333,30 +333,30 @@ function on_update() { e.classList.remove("year_1781"); e.classList.remove("year_1782"); e.classList.remove("year_1783"); - if (game.war_ends) - e.classList.add("year_" + game.war_ends); + if (view.war_ends) + e.classList.add("year_" + view.war_ends); e = document.getElementById("played_british_reinforcements"); e.classList.remove("ops_1"); e.classList.remove("ops_2"); e.classList.remove("ops_3"); - e.classList.add("ops_" + game.played_british_reinforcements); + e.classList.add("ops_" + view.played_british_reinforcements); e = document.getElementById("played_american_reinforcements_1"); e.classList.remove("ops_1"); e.classList.remove("ops_2"); e.classList.remove("ops_3"); - if (game.played_american_reinforcements.length >= 1) - e.classList.add("ops_" + game.played_american_reinforcements[0]); + if (view.played_american_reinforcements.length >= 1) + e.classList.add("ops_" + view.played_american_reinforcements[0]); e = document.getElementById("played_american_reinforcements_2"); e.classList.remove("ops_1"); e.classList.remove("ops_2"); e.classList.remove("ops_3"); - if (game.played_american_reinforcements.length >= 2) - e.classList.add("ops_" + game.played_american_reinforcements[1]); + if (view.played_american_reinforcements.length >= 2) + e.classList.add("ops_" + view.played_american_reinforcements[1]); - let cards = game.hand; + let cards = view.hand; for (let c = 1; c <= 110; ++c) { ui.cards[c].classList.remove('enabled'); if (cards && cards.includes(c)) @@ -376,11 +376,11 @@ function on_update() { update_units(); - if (player != game.active) + if (player != view.active) return; - for (let action of Object.keys(game.actions)) { - let args = game.actions[action]; + for (let action of Object.keys(view.actions)) { + let args = view.actions[action]; switch (action) { case 'card_play_event': case 'card_discard_event': @@ -489,11 +489,11 @@ function on_exchange_for_discard() { } function on_card(evt) { - if (game.actions) { + if (view.actions) { let c = evt.target.id.split("+")[1] | 0; let menu = []; - for (let action in game.actions) - if (Array.isArray(game.actions[action]) && game.actions[action].includes(c)) + for (let action in view.actions) + if (Array.isArray(view.actions[action]) && view.actions[action].includes(c)) menu.push(action); if (menu.length > 0) { current_popup_card = c; @@ -503,8 +503,8 @@ function on_card(evt) { } function get_action_from_arg(x) { - for (let action of Object.keys(game.actions)) { - let args = game.actions[action]; + for (let action of Object.keys(view.actions)) { + let args = view.actions[action]; if (Array.isArray(args) && args.includes(x)) return action; } @@ -512,7 +512,7 @@ function get_action_from_arg(x) { } function on_space(evt) { - if (game.actions) { + if (view.actions) { let space = evt.target.id; let action = get_action_from_arg(space); if (action) @@ -521,7 +521,7 @@ function on_space(evt) { } function on_general(evt) { - if (game.actions) { + if (view.actions) { let general = evt.target.id; let action = get_action_from_arg(general); if (action) @@ -534,6 +534,3 @@ function toggle_markers() { } scroll_with_middle_mouse("main", 2); -init_map_zoom(); -init_shift_zoom(); -init_client(["American", "British"]); @@ -7,6 +7,11 @@ exports.scenarios = [ "Historical" ]; +exports.roles = [ + "American", + "British", +]; + const CARDS = require('./cards'); const DATA = require('./data'); const SPACES = DATA.SPACES; @@ -63,15 +68,25 @@ let states = {}; let events = {}; let game; +let view; function random(n) { - return Math.floor(((game.seed = game.seed * 48271 % 0x7fffffff) / 0x7fffffff) * n); + return ((game.seed = game.seed * 69621 % 0x7fffffff) / 0x7fffffff) * n | 0; +} + +function logbr() { + if (game.log.length > 0 && game.log[game.log.length-1] !== "") + game.log.push(""); } function log(s) { game.log.push(s); } +function logp(s) { + game.log.push(game.active[0] + " " + s); +} + function clear_undo() { game.undo = []; } @@ -85,10 +100,10 @@ function push_undo() { } function pop_undo() { - let undo = game.undo; + let save_undo = game.undo; let save_log = game.log; - game = JSON.parse(undo.pop()); - game.undo = undo; + game = JSON.parse(save_undo.pop()); + game.undo = save_undo; save_log.length = game.log; game.log = save_log; } @@ -199,7 +214,7 @@ function create_deck() { } function reshuffle_deck() { - game.log.push("The deck is reshuffled."); + log("Reshuffled the deck."); game.reshuffle = false; game.deck = game.deck.concat(game.discard); game.discard = []; @@ -230,9 +245,9 @@ function active_hand() { function play_card(c, reason) { if (reason) - game.log.push(game.active[0] + " plays [" + c + ": " + CARDS[c].title + "] " + reason); + log(game.active[0] + " played #" + c + " " + reason); else - game.log.push(game.active[0] + " plays [" + c + ": " + CARDS[c].title + "]"); + log(game.active[0] + " played #" + c); if (CARDS[c].reshuffle == 'if_played') game.reshuffle = true; remove_from_array(active_hand(), c); @@ -240,7 +255,7 @@ function play_card(c, reason) { if (!CARDS[c].once) game.discard.push(c); else - game.log.push("Card " + c + " removed from game."); + log("Removed card " + c + "."); } function discard_card_from_hand(hand, c) { @@ -248,16 +263,16 @@ function discard_card_from_hand(hand, c) { game.discard.push(c); if (CARDS[c].reshuffle == 'if_discarded') game.reshuffle = true; - game.log.push(game.active[0] + " discards [" + c + ": " + CARDS[c].title + "]"); + logp("discarded #" + c); } function discard_card(c, reason) { game.last_played = c; discard_card_from_hand(active_hand(), c); if (reason) - game.log.push(game.active[0] + " discards [" + c + ": " + CARDS[c].title + "] " + reason); + logp("discarded #" + c + " " + reason); else - game.log.push(game.active[0] + " discards [" + c + ": " + CARDS[c].title + "]"); + logp("discarded #" + c); } function can_exchange_for_discard(c) { @@ -392,30 +407,30 @@ function is_adjacent_to_american_pc(a) { } function place_british_pc(space) { - game.log.push("B places PC in " + space); + logp("placed PC in " + space); if (game.british_pc_space_list) remove_from_array(game.british_pc_space_list, space); game.pc[space] = BRITISH; } function place_american_pc(space) { - game.log.push("A places PC in " + space); + logp("placed PC in " + space); game.pc[space] = AMERICAN; } function remove_pc(space) { if (game.active == BRITISH) - game.log.push("B removes PC in " + space); + logp("removed PC in " + space); else - game.log.push("A removes PC in " + space); + logp("removed PC in " + space); game.pc[space] = undefined; } function flip_pc(space) { if (game.active == BRITISH) - game.log.push("B flips PC in " + space); + logp("flipped PC in " + space); else - game.log.push("A flips PC in " + space); + logp("flipped PC in " + space); game.pc[space] = ENEMY[game.pc[space]]; } @@ -693,13 +708,13 @@ function capture_washington() { function capture_british_general(where) { let g = find_british_general(where); - game.log.push(g + " is captured!"); + log(g + " was captured!"); move_general(g, CAPTURED_GENERALS); } function capture_american_or_french_general(where) { let g = find_american_or_french_general(where); - game.log.push(g + " is captured!"); + log(g + " was captured!"); if (g == WASHINGTON) capture_washington(); else @@ -715,7 +730,7 @@ function capture_enemy_general(where) { function remove_benedict_arnold() { if (game.generals[ARNOLD].location) { - game.log.push("Arnold is removed from the game!"); + log("Removed Arnold from the game!"); game.generals[ARNOLD].location = null; } } @@ -756,11 +771,11 @@ function place_british_reinforcements(who, count, where) { move_general(already_there, BRITISH_REINFORCEMENTS); } if (who) { - game.log.push("B reinforces " + where + " with " + who); + logp("reinforced " + where + " with " + who); move_general(who, where); } if (count > 0) { - game.log.push("B reinforces " + where + " with " + count + " CU"); + logp("reinforced " + where + " with " + count + " CU"); move_british_cu(BRITISH_REINFORCEMENTS, where, count); } } @@ -775,10 +790,10 @@ function place_american_reinforcements(who, count, where) { move_general(already_there, AMERICAN_REINFORCEMENTS); } if (who) { - game.log.push("A reinforces " + where + " with " + who); + logp("reinforced " + where + " with " + who); move_general(who, where); } - game.log.push("A reinforces " + where + " with " + count + " CU"); + logp("reinforced " + where + " with " + count + " CU"); place_american_cu(where, count); } @@ -792,10 +807,10 @@ function place_french_reinforcements(who, where) { move_general(already_there, AMERICAN_REINFORCEMENTS); } if (who) { - game.log.push("A reinforces " + where + " with " + who); + logp("reinforced " + where + " with " + who); move_general(who, where); } - game.log.push("A reinforces " + where + " with the French CU"); + logp("reinforced " + where + " with the French CU"); move_cu(FRENCH, FRENCH_REINFORCEMENTS, where, count_french_cu(FRENCH_REINFORCEMENTS)); move_cu(FRENCH, AMERICAN_REINFORCEMENTS, where, count_french_cu(AMERICAN_REINFORCEMENTS)); } @@ -848,7 +863,7 @@ function intercept_army(who, from, to) { } function overrun(where) { - game.log.push(game.active[0] + " overruns CU in " + where); + logp("overran CU in " + where); let cu; if (game.active == BRITISH) cu = find_american_cu(where) || find_french_cu(where); @@ -889,7 +904,7 @@ function surrender_british_army(where) { } function disperse_continental_congress(where) { - game.log.push("Contintental Congress dispersed!"); + log("Contintental Congress dispersed!"); game.congress = CONTINENTAL_CONGRESS_DISPERSED; game.congress_was_dispersed = true; } @@ -897,25 +912,25 @@ function disperse_continental_congress(where) { /* MOVE GENERATORS */ function gen_action(action, argument) { - if (!game.actions) - game.actions = {} + if (!view.actions) + view.actions = {} if (argument != undefined) { - if (!(action in game.actions)) - game.actions[action] = [ argument ]; + if (!(action in view.actions)) + view.actions[action] = [ argument ]; else - game.actions[action].push(argument); + view.actions[action].push(argument); } else { - game.actions[action] = 1; + view.actions[action] = 1; } } function gen_action_undo() { - if (!game.actions) - game.actions = {} + if (!view.actions) + view.actions = {} if (game.undo && game.undo.length > 0) - game.actions.undo = 1; + view.actions.undo = 1; else - game.actions.undo = 0; + view.actions.undo = 0; } function gen_pass() { @@ -1003,15 +1018,16 @@ function gen_place_american_pc_in(list_of_colonies) { function goto_committees_of_correspondence() { log(".h2.american Committes of Correspondence"); - log(""); + logbr(); game.active = AMERICAN; game.state = 'committees_of_correspondence'; game.coc = THE_13_COLONIES.slice(); } states.committees_of_correspondence = { + inactive: "Committees of Correspondence", prompt: function (current) { - game.prompt = "Committees of Correspondence: Place 1 PC marker in each of the 13 colonies. " + game.coc.length + " left."; + view.prompt = "Committees of Correspondence: Place 1 PC marker in each of the 13 colonies. " + game.coc.length + " left."; if (game.coc.length > 0) gen_place_american_pc_in(game.coc); else @@ -1030,9 +1046,9 @@ states.committees_of_correspondence = { } function goto_for_the_king() { - log(""); + logbr(); log(".h2.british For the King"); - log(""); + logbr(); delete game.coc; game.active = BRITISH; game.state = 'for_the_king'; @@ -1041,8 +1057,9 @@ function goto_for_the_king() { } states.for_the_king = { + inactive: "For the King", prompt: function (current) { - game.prompt = "For the King: Place 3 PC markers. " + game.count + " left."; + view.prompt = "For the King: Place 3 PC markers. " + game.count + " left."; if (game.count > 0) gen_british_pc_ops(); else @@ -1075,7 +1092,7 @@ function automatic_victory() { game.active = "None"; game.result = BRITISH; game.state = 'game_over'; - game.log.push(game.victory); + log(game.victory); return true; } if (n_british == 0) { @@ -1083,16 +1100,16 @@ function automatic_victory() { game.active = "None"; game.result = AMERICAN; game.state = 'game_over'; - game.log.push(game.victory); + log(game.victory); return true; } return false; } function goto_start_year() { - log(""); + logbr(); log(".h1 Year " + game.year); - log(""); + logbr(); // Prisoner exchange for (let g of BRITISH_GENERALS) @@ -1142,7 +1159,7 @@ function goto_start_year() { states.british_declare_first = { prompt: function (current) { - game.prompt = "Declare yourself as the first player by playing a campaign card?"; + view.prompt = "Declare yourself as the first player by playing a campaign card?"; gen_pass(); for (let c of CAMPAIGN_CARDS) { if (game.b_hand.includes(c)) { @@ -1152,7 +1169,7 @@ states.british_declare_first = { }, card_campaign: function (c) { delete game.congress_was_dispersed; - game.log.push("B goes first by playing a campaign card"); + logp("went first by playing a campaign card"); game.active = BRITISH; goto_campaign(c); }, @@ -1168,16 +1185,16 @@ states.british_declare_first = { states.choose_first_player = { prompt: function (current) { - game.prompt = "Choose who will play the first strategy card."; + view.prompt = "Choose who will play the first strategy card."; gen_action('american_first'); gen_action('british_first'); }, american_first: function (c) { - game.log.push("A goes first"); + logp("went first"); goto_strategy_phase(AMERICAN); }, british_first: function (c) { - game.log.push("B goes first"); + logp("went first"); goto_strategy_phase(BRITISH); }, } @@ -1187,17 +1204,18 @@ states.choose_first_player = { function goto_strategy_phase(new_active) { game.active = new_active; game.state = 'strategy_phase'; - log(""); + logbr(); if (game.active === AMERICAN) log(".h2.american American Turn"); else log(".h2.british British Turn"); - log(""); + logbr(); } states.strategy_phase = { + inactive: "strategy phase", prompt: function (current) { - game.prompt = "Play a strategy card."; + view.prompt = "Play a strategy card."; gen_strategy_plays(active_hand()); }, card_campaign: function (c) { @@ -1250,7 +1268,7 @@ states.strategy_phase = { let d = game.discard.pop(); discard_card(c); active_hand().push(d); - game.log.push(game.active[0] + " picks up " + d + ": " + CARDS[d].title); + logp("picked up up #" + d); }, } @@ -1272,7 +1290,7 @@ function end_strategy_card() { } if (!game.french_alliance_triggered && game.french_alliance == 9) { - game.log.push("The French sign an alliance with the Americans!"); + log("The French signed an alliance with the Americans!"); game.french_alliance_triggered = true; if (game.french_navy == FRENCH_REINFORCEMENTS) { game.save_active = game.active; @@ -1350,7 +1368,7 @@ function gen_strategy_plays(hand) { states.discard_event_pc_action = { prompt: function (current) { - game.prompt = "Place, flip, or remove PC marker."; + view.prompt = "Place, flip, or remove PC marker."; gen_pass(); if (game.active == BRITISH) gen_british_discard_event_pc_action(); @@ -1419,7 +1437,7 @@ function goto_ops_pc(count) { states.ops_pc = { prompt: function (current) { - game.prompt = "Place or flip PC markers. " + game.count + " left."; + view.prompt = "Place or flip PC markers. " + game.count + " left."; gen_pass(); if (game.count > 0) { if (game.active == BRITISH) @@ -1507,8 +1525,8 @@ function goto_ops_reinforcements(c) { states.ops_british_reinforcements_who = { prompt: function (current) { - game.prompt = "Reinforcements: choose an available general or pass to bring only CU." - game.prompt += " Carrying " + game.count + " British CU."; + view.prompt = "Reinforcements: choose an available general or pass to bring only CU." + view.prompt += " Carrying " + game.count + " British CU."; gen_pass(); gen_british_reinforcements_who(); }, @@ -1532,8 +1550,8 @@ states.ops_british_reinforcements_who = { states.ops_british_reinforcements_where = { prompt: function (current) { - game.prompt = "Reinforcements: choose a port space."; - game.prompt += " Carrying " + game.count + " British CU."; + view.prompt = "Reinforcements: choose a port space."; + view.prompt += " Carrying " + game.count + " British CU."; gen_british_reinforcements_where(); }, drop_british_cu: function () { @@ -1551,7 +1569,7 @@ states.ops_british_reinforcements_where = { states.ops_american_reinforcements_who = { prompt: function (current) { - game.prompt = "Reinforcements: choose an available general or pass to bring only CU."; + view.prompt = "Reinforcements: choose an available general or pass to bring only CU."; gen_pass(); gen_american_reinforcements_who(); }, @@ -1569,7 +1587,7 @@ states.ops_american_reinforcements_who = { states.ops_american_reinforcements_where = { prompt: function (current) { - game.prompt = "Reinforcements: choose a space."; + view.prompt = "Reinforcements: choose a space."; gen_american_reinforcements_where(game.who); }, place_reinforcements: function (space) { @@ -1646,11 +1664,11 @@ function goto_ops_general(c) { states.ops_general_who = { prompt: function (current) { if (game.campaign && game.landing_party) - game.prompt = "Campaign: Activate a general or use a landing party. " + game.campaign + " left."; + view.prompt = "Campaign: Activate a general or use a landing party. " + game.campaign + " left."; else if (game.campaign) - game.prompt = "Campaign: Activate a general. " + game.campaign + " left."; + view.prompt = "Campaign: Activate a general. " + game.campaign + " left."; else - game.prompt = "Activate a general with strategy rating " + game.count + " or lower."; + view.prompt = "Activate a general with strategy rating " + game.count + " or lower."; if (game.landing_party) gen_landing_party(); gen_activate_general(); @@ -1714,7 +1732,7 @@ function goto_remove_general(where) { states.remove_general = { prompt: function (current) { - game.prompt = "Remove a general to the reinforcements box."; + view.prompt = "Remove a general to the reinforcements box."; gen_remove_general(); }, select_general: function (g) { @@ -1732,7 +1750,7 @@ function goto_remove_general_after_intercept() { states.remove_general_after_intercept = { prompt: function (current) { - game.prompt = "Remove a general to the reinforcements box."; + view.prompt = "Remove a general to the reinforcements box."; gen_remove_general(); }, select_general: function (g) { @@ -1751,7 +1769,7 @@ function goto_remove_general_after_retreat(where) { states.remove_general_after_retreat = { prompt: function (current) { - game.prompt = "Remove a general to the reinforcements box."; + view.prompt = "Remove a general to the reinforcements box."; gen_remove_general(); }, select_general: function (g) { @@ -1807,27 +1825,27 @@ function goto_ops_general_move(g, marblehead) { states.ops_general_move = { prompt: function (current) { - game.prompt = "Move " + game.who + " with "; + view.prompt = "Move " + game.who + " with "; if (game.carry_british > 0) { - game.prompt += game.carry_british + " British CU."; + view.prompt += game.carry_british + " British CU."; } else if (game.carry_french + game.carry_american > 0) { if (game.carry_french > 0) { if (game.carry_american > 0) { - game.prompt += game.carry_french + " French CU and "; - game.prompt += game.carry_american + " American CU."; + view.prompt += game.carry_french + " French CU and "; + view.prompt += game.carry_american + " American CU."; } else { - game.prompt += game.carry_french + " French CU."; + view.prompt += game.carry_french + " French CU."; } } else { - game.prompt += game.carry_american + " American CU."; + view.prompt += game.carry_american + " American CU."; } } else { - game.prompt += game.carry_american + " no CU."; + view.prompt += game.carry_american + " no CU."; } if (game.count == 1) - game.prompt += " " + game.count + " move left."; + view.prompt += " " + game.count + " move left."; else if (game.count > 1) - game.prompt += " " + game.count + " moves left."; + view.prompt += " " + game.count + " moves left."; // Cannot stop on enemy general if (!has_enemy_general(location_of_general(game.who))) @@ -1946,16 +1964,15 @@ function goto_intercept(from, where) { states.intercept = { prompt: function (current) { - game.prompt = "Intercept " + game.save_who + " in " + game.where + "?"; + view.prompt = "Intercept " + game.save_who + " in " + game.where + "?"; gen_pass(); gen_intercept(); }, select_general: function (g) { - // TODO: roll for intercept! game.moved[g] = 1; let die = roll_d6(); if (die <= GENERALS[g].agility) { - game.log.push(g + " intercepts (" + die + " <= " + GENERALS[g].agility + ")"); + log(g + " intercepted (" + die + " <= " + GENERALS[g].agility + ")"); game.did_intercept = 1; let save_carry_british = game.carry_british; @@ -1974,7 +1991,7 @@ states.intercept = { else end_intercept(); } else { - game.log.push(g + " fails to intercept (" + die + " > " + GENERALS[g].agility + ")"); + log(g + " failed to intercept (" + die + " > " + GENERALS[g].agility + ")"); if (!can_intercept_to(game.where)) end_intercept(); } @@ -2103,8 +2120,8 @@ function goto_campaign(c) { /* EVENTS */ events.the_war_ends = function (c, card) { - game.log.push(game.active[0] + " plays " + c + ": " + CARDS[c].title); - game.log.push("The war will end in " + card.year); + logp("played #" + c); + log("The war will end in " + card.year); game.last_played = c; remove_from_array(active_hand(), c); if (game.war_ends) @@ -2140,13 +2157,13 @@ function advance_french_alliance(count) { game.french_alliance += count; if (game.french_alliance > 9) game.french_alliance = 9; - game.log.push("French alliance advances to " + count); + log("French alliance advanced to " + count); } } function lose_regular_advantage() { if (game.regulars) { - game.log.push("The British Regulars Advantage is lost!"); + log("British Regulars Advantage lost!"); game.regulars = false; advance_french_alliance(2); } @@ -2156,7 +2173,7 @@ events.baron_von_steuben_trains_the_continental_army = function (c, card) { play_card(c); if (is_general_on_map(WASHINGTON)) { let where = location_of_general(WASHINGTON); - game.log.push("A places 2 CU with Washington in " + where); + logp("placed 2 CU with Washington in " + where); place_american_cu(where, 2); lose_regular_advantage(); } @@ -2184,7 +2201,7 @@ events.remove_british_pc_from = function (c, card) { states.remove_british_pc_from = { prompt: function (current) { - game.prompt = "Remove British PC markers from " + game.where.join(", ") + ". " + game.count + " left."; + view.prompt = "Remove British PC markers from " + game.where.join(", ") + ". " + game.count + " left."; gen_pass(); gen_remove_british_pc_from(game.where); }, @@ -2209,7 +2226,7 @@ events.remove_american_pc = function (c, card) { states.remove_american_pc = { prompt: function (current) { - game.prompt = "Remove American PC markers. " + game.count + " left."; + view.prompt = "Remove American PC markers. " + game.count + " left."; gen_pass(); gen_remove_american_pc(); }, @@ -2233,7 +2250,7 @@ events.remove_american_pc_from = function (c, card) { states.remove_american_pc_from = { prompt: function (current) { - game.prompt = "Remove American PC markers from " + game.where.join(", ") + ". " + game.count + " left."; + view.prompt = "Remove American PC markers from " + game.where.join(", ") + ". " + game.count + " left."; gen_pass(); gen_remove_american_pc_from(game.where); }, @@ -2259,7 +2276,7 @@ events.remove_american_pc_from_non_port = function (c, card) { states.remove_american_pc_from_non_port = { prompt: function (current) { - game.prompt = "Remove American PC markers from non-Port space in " + game.where.join(", ") + ". " + game.count + " left."; + view.prompt = "Remove American PC markers from non-Port space in " + game.where.join(", ") + ". " + game.count + " left."; gen_pass(); gen_remove_american_pc_from_non_port(game.where); }, @@ -2284,7 +2301,7 @@ events.remove_american_pc_within_two_spaces_of_a_british_general = function (c, states.remove_american_pc_within_two_spaces_of_a_british_general = { prompt: function (current) { - game.prompt = "Remove American PC markers within two spaces of a British general. " + game.count + " left."; + view.prompt = "Remove American PC markers within two spaces of a British general. " + game.count + " left."; gen_pass(); gen_remove_american_pc_within_two_spaces_of_a_british_general(); }, @@ -2309,7 +2326,7 @@ events.place_american_pc = function (c, card) { states.place_american_pc = { prompt: function (current) { - game.prompt = "Place American PC markers. " + game.count + " left."; + view.prompt = "Place American PC markers. " + game.count + " left."; gen_pass(); gen_place_american_pc(); }, @@ -2333,7 +2350,7 @@ events.place_american_pc_in = function (c, card) { states.place_american_pc_in = { prompt: function (current) { - game.prompt = "Place American PC markers in " + game.where.join(", ") + ". " + game.count + " left."; + view.prompt = "Place American PC markers in " + game.where.join(", ") + ". " + game.count + " left."; gen_pass(); gen_place_american_pc_in(game.where); }, @@ -2359,7 +2376,7 @@ events.lord_sandwich_coastal_raids = function (c, card) { states.lord_sandwich_coastal_raids = { prompt: function (current) { - game.prompt = "Remove two or flip one American PC in a port space."; + view.prompt = "Remove two or flip one American PC in a port space."; gen_pass(); gen_lord_sandwich_coastal_raids(game.where); }, @@ -2395,7 +2412,7 @@ events.remove_american_cu = function (c, card) { states.remove_american_cu = { prompt: function (current) { - game.prompt = "Remove one American CU from any space."; + view.prompt = "Remove one American CU from any space."; gen_pass(); gen_remove_american_cu(); }, @@ -2433,7 +2450,7 @@ events.pennsylvania_and_new_jersey_line_mutinies = function (c, card) { states.pennsylvania_and_new_jersey_line_mutinies = { prompt: function (current) { - game.prompt = "Remove two American CUs from the map, one each from two different spaces."; + view.prompt = "Remove two American CUs from the map, one each from two different spaces."; gen_pass(); gen_pennsylvania_and_new_jersey_line_mutinies(game.where); }, @@ -2465,7 +2482,7 @@ events.john_glovers_marblehead_regiment = function (c, card) { states.john_glovers_marblehead_regiment_who = { prompt: function (current) { - game.prompt = "Activate an American general."; + view.prompt = "Activate an American general."; gen_activate_general(); }, select_general: function (g) { @@ -2483,8 +2500,8 @@ events.declaration_of_independence = function (c, card) { states.declaration_of_independence = { prompt: function (current) { - game.prompt = "Declaration of Independence: Place 1 PC marker in each of the 13 colonies. "; - game.prompt += game.doi.length + " left."; + view.prompt = "Declaration of Independence: Place 1 PC marker in each of the 13 colonies. "; + view.prompt += game.doi.length + " left."; gen_pass(); gen_place_american_pc_in(game.doi); }, @@ -2520,7 +2537,7 @@ function goto_george_washington_captured() { states.george_washington_captured = { prompt: function (current) { - game.prompt = "George Washington is captured! Remove American PC markers. " + game.count + " left."; + view.prompt = "George Washington is captured! Remove American PC markers. " + game.count + " left."; gen_pass(); gen_remove_american_pc(); }, @@ -2587,7 +2604,7 @@ function goto_retreat_before_battle() { states.retreat_before_battle = { prompt: function (current) { - game.prompt = "Attempt retreat before battle?"; + view.prompt = "Attempt retreat before battle?"; gen_pass(); gen_defender_retreat(); }, @@ -2597,12 +2614,12 @@ states.retreat_before_battle = { agility += 2; let roll = roll_d6(); if (roll <= agility) { - game.log.push("A successfully retreats before battle: " + roll + " <= " + agility); + logp("successfully retreated before battle: " + roll + " <= " + agility); pickup_max_american_cu(game.where); move_army(game.who, game.where, to); goto_remove_general_after_retreat_before_battle(to); } else { - game.log.push("A fails to retreat before battle: " + roll + " > " + agility); + logp("failed to retreat before battle: " + roll + " > " + agility); end_retreat_before_battle(); } }, @@ -2623,7 +2640,7 @@ function goto_remove_general_after_retreat_before_battle(to) { states.remove_general_after_retreat_before_battle = { prompt: function (current) { - game.prompt = "Remove a general to the reinforcements box."; + view.prompt = "Remove a general to the reinforcements box."; gen_remove_general(); }, select_general: function (g) { @@ -2715,7 +2732,7 @@ function goto_play_attacker_battle_card() { states.play_attacker_battle_card = { prompt: function (current) { - game.prompt = "Attack: Play or discard event for DRM."; + view.prompt = "Attack: Play or discard event for DRM."; gen_pass(); gen_battle_card(); }, @@ -2755,7 +2772,7 @@ function goto_play_defender_battle_card() { states.play_defender_battle_card = { prompt: function (current) { - game.prompt = "Defend: Play or discard event for DRM."; + view.prompt = "Defend: Play or discard event for DRM."; gen_pass(); gen_battle_card(); }, @@ -3010,9 +3027,9 @@ function resolve_battle() { capture_american_or_french_general(game.where); } - game.log.push("BRITISH BATTLE REPORT:\n" + b_log.join("\n")); - game.log.push("AMERICAN BATTLE REPORT:\n" + a_log.join("\n")); - game.log.push(victor + " victory in " + game.where + "!"); + log("BRITISH BATTLE REPORT:\n" + b_log.join("\n")); + log("AMERICAN BATTLE REPORT:\n" + a_log.join("\n")); + log(victor + " victory in " + game.where + "!"); if (victor == AMERICAN) advance_french_alliance(1); @@ -3036,7 +3053,7 @@ function goto_retreat_after_battle(victor) { states.retreat_after_battle = { prompt: function (current) { - game.prompt = "Retreat after battle."; + view.prompt = "Retreat after battle."; gen_action('surrender'); if (game.active == game.attacker) gen_attacker_retreat(); @@ -3044,7 +3061,7 @@ states.retreat_after_battle = { gen_defender_retreat(); }, move: function (to) { - game.log.push(game.active[0] + " retreats to " + to); + logp("retreated to " + to); if (game.active == BRITISH) retreat_british_army(game.where, to); else @@ -3060,7 +3077,7 @@ states.retreat_after_battle = { let where = game.where; end_battle(); - game.log.push(active[0] + " surrenders"); + logp("surrendered"); if (active == BRITISH) surrender_british_army(where); else @@ -3071,6 +3088,9 @@ states.retreat_after_battle = { function end_battle() { game.active = game.attacker; + if (game.active == BRITISH && game.congress == game.where) + disperse_continental_congress(game.where); + if (game.british_losses >= 3) lose_regular_advantage(); @@ -3097,22 +3117,22 @@ function end_battle() { function apply_single_winter_attrition(owner, space) { let die = roll_d6(); - game.log.push(owner[0] + " attrition roll " + die + " in " + space); + log(owner[0] + " attrition roll " + die + " in " + space); if (die <= 3) { - game.log.push(owner[0] + " lose 1 CU in " + space); + log(owner[0] + " lost 1 CU in " + space); remove_cu(owner, space, 1); } } function apply_winter_attrition(owner, space, n) { let half = Math.floor(n / 2); - game.log.push(owner[0] + " lose " + half + " CU in " + space); + log(owner[0] + " lost " + half + " CU in " + space); remove_cu(owner, space, half); } function goto_winter_attrition_phase() { - game.log.push(""); - game.log.push("Winter Attrition"); + logbr(); + log("Winter Attrition"); for (let space in SPACES) { let wq = is_winter_quarter_space(space); @@ -3148,13 +3168,13 @@ function goto_winter_attrition_phase() { // TODO: let player choose (but why would he ever choose the french?) let lose_american = Math.min(half, n_american); - game.log.push(owner[0] + " lose " + lose_american + " American CU in " + space); + log(owner[0] + " lost " + lose_american + " American CU in " + space); remove_cu(AMERICAN, space, n_american); half -= lose_american; n_american -= lose_american; if (half > 0) { - game.log.push(owner[0] + " lose " + half + " French CU in " + space); + log(owner[0] + " lost " + half + " French CU in " + space); remove_cu(FRENCH, space, half); } } @@ -3187,11 +3207,11 @@ function gen_place_french_navy() { states.place_french_navy_trigger = { prompt: function (current) { - game.prompt = "Place the French Navy in a blockade zone."; + view.prompt = "Place the French Navy in a blockade zone."; gen_place_french_navy(); }, place_navy: function (zone) { - game.log.push("A places French Navy."); + logp("placed French Navy."); game.french_navy = zone; game.active = game.save_active; delete game.save_active; @@ -3201,11 +3221,11 @@ states.place_french_navy_trigger = { states.place_french_navy = { prompt: function (current) { - game.prompt = "Place the French Navy in a blockade zone."; + view.prompt = "Place the French Navy in a blockade zone."; gen_place_french_navy(); }, place_navy: function (zone) { - game.log.push("A places French Navy."); + logp("placed French Navy."); game.french_navy = zone; goto_political_control_phase(); }, @@ -3289,7 +3309,7 @@ function spread_british_path(seen, from) { } function remove_isolated_american_pc_segment() { - game.log.push("Removing isolated American PC"); + log("Removed isolated American PC"); let seen = {}; for (let space in SPACES) { if (is_american_pc_root(space)) { @@ -3303,7 +3323,7 @@ function remove_isolated_american_pc_segment() { } function remove_isolated_british_pc_segment() { - game.log.push("Removing isolated British PC"); + log("Removed isolated British PC"); let seen = {}; for (let space in SPACES) { if (is_british_pc_root(space)) { @@ -3340,7 +3360,7 @@ function goto_political_control_phase() { states.return_continental_congress = { prompt: function () { - game.prompt = "Return Continental Congress to a space in the 13 colonies."; + view.prompt = "Return Continental Congress to a space in the 13 colonies."; if (gen_place_continental_congress() == 0) gen_pass(); }, @@ -3362,7 +3382,7 @@ function goto_political_control_phase_2() { states.european_war = { prompt: function () { - game.prompt = "European War: Remove 2 British CU from any spaces. " + game.count + " left."; + view.prompt = "European War: Remove 2 British CU from any spaces. " + game.count + " left."; gen_pass(); gen_remove_british_cu(); }, @@ -3394,7 +3414,7 @@ function norths_government_falls() { game.active = "None"; game.state = 'game_over'; - game.log.push(game.victory); + log(game.victory); } function goto_end_phase() { @@ -3418,7 +3438,7 @@ function goto_end_phase() { states.game_over = { prompt: function () { - game.prompt = game.victory; + view.prompt = game.victory; } } @@ -3447,23 +3467,21 @@ exports.action = function (state, current, action, arg) { throw new Error("Invalid action: " + action); } } + + update_colony_control(); + return game; } function list_actions(current) { - game.actions = {} - game.prompt = ""; + view.actions = {} states[game.state].prompt(current); } exports.view = function(state, current) { game = state; - list_actions(current); - - update_colony_control(); - - let view = { + view = { active: state.active, year: state.year, war_ends: state.war_ends, @@ -3498,11 +3516,13 @@ exports.view = function(state, current) { view.hand = []; if (current == state.active) { + list_actions(current); gen_action_undo(); - view.prompt = state.prompt; - view.actions = state.actions; } else { - view.prompt = "Waiting for " + game.active + " player \u2014 " + game.prompt; + let inactive = states[game.state].inactive; + if (typeof inactive !== 'string') + inactive = game.state; + view.prompt = "Waiting for " + game.active + " player \u2014 " + inactive + "."; } return view; @@ -1,3 +1 @@ insert or ignore into titles ( title_id, title_name, bgg ) values ( 'washingtons-war', 'Washington''s War', 38996 ); -insert or replace into roles values ( 'washingtons-war', 'American' ); -insert or replace into roles values ( 'washingtons-war', 'British' ); |