summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cover.1x.jpgbin9402 -> 27012 bytes
-rw-r--r--cover.2x.jpgbin29152 -> 57726 bytes
-rw-r--r--info/playbook.html4
-rw-r--r--info/rulebook.html4
-rw-r--r--play.css20
-rw-r--r--play.html11
-rw-r--r--play.js81
-rw-r--r--rules.js304
-rw-r--r--title.sql2
9 files changed, 225 insertions, 201 deletions
diff --git a/cover.1x.jpg b/cover.1x.jpg
index 75e8673..7889c4d 100644
--- a/cover.1x.jpg
+++ b/cover.1x.jpg
Binary files differ
diff --git a/cover.2x.jpg b/cover.2x.jpg
index 31c1a36..09dc03e 100644
--- a/cover.2x.jpg
+++ b/cover.2x.jpg
Binary files differ
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>
diff --git a/play.css b/play.css
index 9b3d60c..436377f 100644
--- a/play.css
+++ b/play.css
@@ -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 {
diff --git a/play.html b/play.html
index 435c237..f162900 100644
--- a/play.html
+++ b/play.html
@@ -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>
diff --git a/play.js b/play.js
index 3e1e763..e5b2cc5 100644
--- a/play.js
+++ b/play.js
@@ -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, "&amp;");
text = text.replace(/</g, "&lt;");
@@ -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"]);
diff --git a/rules.js b/rules.js
index 85c9861..6e61b80 100644
--- a/rules.js
+++ b/rules.js
@@ -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;
diff --git a/title.sql b/title.sql
index be6fe09..675540a 100644
--- a/title.sql
+++ b/title.sql
@@ -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' );