summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2021-06-06 22:11:00 +0200
committerTor Andersson <tor@ccxvii.net>2023-02-18 12:12:42 +0100
commit5718d3695e0783ce8695aeb51f3af797d6037e60 (patch)
treed8eb8d09a7f8f617dd61c11fc1618b04d2c67707
parent7edd71f76123e10a15daa5918f7f3eae2dfbf062 (diff)
downloadshores-of-tripoli-5718d3695e0783ce8695aeb51f3af797d6037e60.tar.gz
tripoli: Naval bombardment and combat.
-rw-r--r--play.html154
-rw-r--r--rules.js396
-rw-r--r--ui.js33
3 files changed, 498 insertions, 85 deletions
diff --git a/play.html b/play.html
index 11b74e7..f25e54a 100644
--- a/play.html
+++ b/play.html
@@ -88,6 +88,12 @@
opacity: 1
}
+#map .harbor.where, #map .patrol_zone.where {
+ opacity: 1;
+ stroke: yellow;
+ fill: yellow;
+}
+
#map .patrol_zone {
fill: white;
fill-opacity: 0.3;
@@ -100,6 +106,19 @@
opacity: 1
}
+#map .us_frigate { fill: #0977bd; }
+#map .us_gunboat { fill: #0977bd; }
+#map .se_frigate { fill: #fdd527; }
+#map .tr_frigate { fill: #de333e; }
+#map .tr_corsair { fill: #de333e; }
+#map .al_corsair { fill: #f39331; }
+#map .us_marine { fill: #0977bd; }
+#map .ar_infantry { fill: #e0e0e0; }
+#map .tr_infantry { fill: #de333e; }
+
+#map .us_frigate.damaged { fill: #005577; }
+#map .tr_frigate.damaged { fill: #881111; }
+
/* CARD ACTION POPUP MENU */
#popup {
@@ -257,6 +276,7 @@
</symbol>
<symbol id="frigate" x="-20" y="-15" width="42.9" height="29.6" viewBox="0 0 428 296">
<path d="M153.5 17.2c-.3.7-.6 42.7-.8 93.3l-.2 92-16.2.3c-9.3.1-16.3-.1-16.3-.7 0-.5 1.1-3.7 2.4-7.2 4-10.6 5.1-23.4 3.3-36.4-5.2-36.2-28-73.7-63.4-104.4-11.9-10.4-10.8-17.6-11.1 71.7L51 203h-9.6c-7.9 0-10.1.3-12.4 2-4.9 3.5-5.1 5.5-4.8 35.1.3 26.9.3 27.5 2.7 31.6 1.4 2.4 4.3 5.5 6.5 7 4 2.6 4.7 2.7 22.1 3.4 9.9.4 61.2.8 114 .8 101 .1 104 0 117-4.4 14-4.8 26.4-13.8 37-27.1 11.3-14 15.9-17.5 36.1-27.8 9.8-5.1 23.3-12 30-15.4 12.6-6.5 14.4-8.2 14.4-14.2 0-6.3-4.5-11-10.5-11-2.2 0-33.6 10.1-48.5 15.6-10.6 3.9-24 5.6-24 3.1 0-.7.4-1.7.9-2.3 1.9-1.9 5-13.7 6.2-23.3 1.3-10.6.3-24.7-2.7-37.6-4.9-21.7-19.8-50.7-37.4-72.7-6.8-8.6-29.4-30.8-31.3-30.8-.8 0-1.8.7-2.1 1.6-.3.9-.6 38.7-.6 84V203h-18c-9.9 0-18-.2-18-.5 0-.2 1.4-3.9 3-8.1 1.7-4.2 3.7-11.1 4.5-15.3 2.9-15.2 1.3-42.1-3.4-56.9-1.2-3.5-2.6-8.2-3.2-10.5-.6-2.3-3.1-8.9-5.5-14.6-11.7-27.2-26.7-49.6-48.1-71.9-8.7-9-10.9-10.5-11.8-8z" stroke-width="10"/>
+</symbol>
<symbol id="infantry" x="-10" y="-10" width="21" height="21" viewBox="0 0 105 105">
<path d="M8 53v44h89V9H8v44z" stroke-width="5"/>
</symbol>
@@ -281,73 +301,73 @@
<circle id="season" r="26" cx="893" cy="728"/>
</g>
<g stroke="black">
-<use id="us_frigate_1" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_2" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_3" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_4" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_5" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_6" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_7" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="us_frigate_8" href="#frigate" x="0" y="0" fill="#0977bd"/>
-<use id="tr_frigate_1" href="#frigate" x="0" y="0" fill="#de333e"/>
-<use id="tr_frigate_2" href="#frigate" x="0" y="0" fill="#de333e"/>
-<use id="se_frigate_1" href="#frigate" x="0" y="0" fill="#fdd527"/>
-<use id="se_frigate_2" href="#frigate" x="0" y="0" fill="#fdd527"/>
-<use id="us_gunboat_1" href="#corsair" x="0" y="0" fill="#0977bd"/>
-<use id="us_gunboat_2" href="#corsair" x="0" y="0" fill="#0977bd"/>
-<use id="us_gunboat_3" href="#corsair" x="0" y="0" fill="#0977bd"/>
-<use id="tr_corsair_1" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_2" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_3" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_4" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_5" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_6" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_7" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_8" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="tr_corsair_9" href="#corsair" x="0" y="0" fill="#de333e"/>
-<use id="al_corsair_1" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_2" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_3" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_4" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_5" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_6" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_7" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_8" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="al_corsair_9" href="#corsair" x="0" y="0" fill="#f39331"/>
-<use id="us_marine_1" href="#infantry" x="0" y="0" fill="#0977bd"/>
-<use id="us_marine_2" href="#infantry" x="0" y="0" fill="#0977bd"/>
-<use id="us_marine_3" href="#infantry" x="0" y="0" fill="#0977bd"/>
-<use id="us_marine_4" href="#infantry" x="0" y="0" fill="#0977bd"/>
-<use id="ar_infantry_1" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_2" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_3" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_4" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_5" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_6" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_7" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_8" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_9" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="ar_infantry_10" href="#infantry" x="0" y="0" fill="#e0e0e0"/>
-<use id="tr_infantry_1" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_2" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_3" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_4" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_5" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_6" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_7" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_8" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_9" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_10" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_11" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_12" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_13" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_14" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_15" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_16" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_17" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_18" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_19" href="#infantry" x="0" y="0" fill="#de333e"/>
-<use id="tr_infantry_20" href="#infantry" x="0" y="0" fill="#de333e"/>
+<use id="us_frigate_1" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_2" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_3" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_4" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_5" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_6" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_7" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="us_frigate_8" href="#frigate" x="0" y="0" class="us_frigate"/>
+<use id="tr_frigate_1" href="#frigate" x="0" y="0" class="tr_frigate"/>
+<use id="tr_frigate_2" href="#frigate" x="0" y="0" class="tr_frigate"/>
+<use id="se_frigate_1" href="#frigate" x="0" y="0" class="se_frigate"/>
+<use id="se_frigate_2" href="#frigate" x="0" y="0" class="se_frigate"/>
+<use id="us_gunboat_1" href="#corsair" x="0" y="0" class="us_gunboat"/>
+<use id="us_gunboat_2" href="#corsair" x="0" y="0" class="us_gunboat"/>
+<use id="us_gunboat_3" href="#corsair" x="0" y="0" class="us_gunboat"/>
+<use id="tr_corsair_1" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_2" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_3" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_4" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_5" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_6" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_7" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_8" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="tr_corsair_9" href="#corsair" x="0" y="0" class="tr_corsair"/>
+<use id="al_corsair_1" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_2" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_3" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_4" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_5" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_6" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_7" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_8" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="al_corsair_9" href="#corsair" x="0" y="0" class="al_corsair"/>
+<use id="us_marine_1" href="#infantry" x="0" y="0" class="us_marine"/>
+<use id="us_marine_2" href="#infantry" x="0" y="0" class="us_marine"/>
+<use id="us_marine_3" href="#infantry" x="0" y="0" class="us_marine"/>
+<use id="us_marine_4" href="#infantry" x="0" y="0" class="us_marine"/>
+<use id="ar_infantry_1" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_2" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_3" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_4" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_5" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_6" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_7" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_8" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_9" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="ar_infantry_10" href="#infantry" x="0" y="0" class="ar_infantry"/>
+<use id="tr_infantry_1" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_2" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_3" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_4" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_5" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_6" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_7" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_8" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_9" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_10" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_11" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_12" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_13" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_14" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_15" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_16" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_17" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_18" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_19" href="#infantry" x="0" y="0" class="tr_infantry"/>
+<use id="tr_infantry_20" href="#infantry" x="0" y="0" class="tr_infantry"/>
</g>
</svg>
diff --git a/rules.js b/rules.js
index 62f8693..4717160 100644
--- a/rules.js
+++ b/rules.js
@@ -1,5 +1,22 @@
"use strict";
+// Battle Card timing:
+
+// us: before interception roll
+// tr: after interception roll
+// tr: before pirate raid
+// tr: after pirate raid
+
+// us: before naval battle
+// tr: before naval battle
+// us: before land battle
+// tr: before land battle
+
+// us: own reaction: burn the philadelphia
+// us: own reaction: launch the intrepid
+// us: own reaction: assault on tripoli
+// tr: own reaction: philadelphia runs aground
+
const US = "United States";
const TR = "Tripolitania";
@@ -83,6 +100,16 @@ const FRIGATE_SPACES = [
TUNIS_PATROL_ZONE,
];
+const BATTLE_SPACES = [
+ ALEXANDRIA_HARBOR,
+ ALGIERS_HARBOR,
+ BENGHAZI_HARBOR,
+ DERNE_HARBOR,
+ TANGIER_HARBOR,
+ TRIPOLI_HARBOR,
+ TUNIS_HARBOR,
+];
+
const THOMAS_JEFFERSON = 1;
const SWEDISH_FRIGATES_ARRIVE = 2;
const HAMETS_ARMY_CREATED = 3;
@@ -104,6 +131,7 @@ const BURN_THE_PHILADELPHIA = 18;
const LAUNCH_THE_INTREPID = 19;
const GENERAL_EATON_ATTACKS_DERNE = 20;
const GENERAL_EATON_ATTACKS_BENGHAZI = 21;
+
const LIEUTENANT_STERETT_IN_PURSUIT = 22;
const PREBLES_BOYS_TAKE_AIM = 23;
const THE_DARING_STEPHEN_DECATUR = 24;
@@ -132,6 +160,7 @@ const THE_PHILADELPHIA_RUNS_AGROUND = 45;
const ALGIERS_DECLARES_WAR = 46;
const MOROCCO_DECLARES_WAR = 47;
const TUNIS_DECLARES_WAR = 48;
+
const US_SIGNAL_BOOKS_OVERBOARD = 49;
const UNCHARTED_WATERS = 50;
const MERCHANT_SHIP_CONVERTED = 51;
@@ -388,14 +417,26 @@ function count_american_frigates(where) {
return count_pieces(US_FRIGATES, where);
}
+function count_american_gunboats(where) {
+ return count_pieces(US_GUNBOATS, where);
+}
+
function count_tripolitan_corsairs(where) {
return count_pieces(TR_CORSAIRS, where);
}
+function count_tripolitan_frigates(where) {
+ return count_pieces(TR_FRIGATES, where);
+}
+
function count_allied_corsairs(where) {
return count_pieces(AL_CORSAIRS, where);
}
+function count_tripolitan_infantry(where) {
+ return count_pieces(TR_INFANTRY, where);
+}
+
function can_play_thomas_jefferson() {
return game.us.core.includes(THOMAS_JEFFERSON);
}
@@ -441,6 +482,8 @@ function start_of_year() {
log("");
log("Start of " + game.year + ".");
+ game.season = SPRING;
+
move_all_pieces(US_FRIGATES, TRACK_YEAR[game.year], GIBRALTAR_HARBOR);
move_all_pieces(TR_FRIGATES, TRACK_YEAR[game.year], TRIPOLI_HARBOR);
@@ -466,11 +509,13 @@ function start_of_year() {
}
function end_american_play() {
+ clear_undo();
game.active = TR;
game.state = 'tripolitan_play';
}
function end_tripolitan_play() {
+ clear_undo();
end_of_season();
}
@@ -559,7 +604,6 @@ states.tripolitan_play = {
gen_action(view, 'card_event', CONSTANTINOPLE_SENDS_AID);
let build = can_build_corsair_in_tripoli();
let raid = can_pirate_raid_from_tripoli();
- console.log("tripolitan_play", build, raid);
for (let c of game.tr.hand) {
if (build)
gen_action(view, 'card_build_corsair', c);
@@ -594,6 +638,35 @@ function goto_move_up_to_n_american_frigates(n) {
push_undo();
}
+function is_naval_battle_location(space) {
+ let n_us = count_american_frigates(space);
+ let n_tr = count_tripolitan_corsairs(space) + count_allied_corsairs(space);
+ return (n_us > 0 && n_tr > 0);
+}
+
+function is_naval_bombardment_location(space) {
+ let n_us = count_american_frigates(space);
+ let n_tr = count_tripolitan_infantry(space);
+ return (n_us > 0 && n_tr > 0);
+}
+
+function is_naval_battle_or_bombardment_location(space) {
+ let n_us = count_american_frigates(space);
+ let n_tr_ships = count_tripolitan_corsairs(space) + count_allied_corsairs(space);
+ let n_tr_infantry = count_tripolitan_infantry(space);
+ return (n_us > 0 && (n_tr_ships > 0 || n_tr_infantry > 0));
+}
+
+function count_naval_battle_or_bombardment_locations() {
+ let n = 0;
+ for (let space of BATTLE_SPACES)
+ if (is_naval_battle_or_bombardment_location(space))
+ ++n;
+ return n;
+}
+
+// TODO: click 'from' location to go back to selecting source?
+
states.move_us_frigate_from = {
prompt: function (view, current) {
if (is_inactive_player(current))
@@ -601,9 +674,9 @@ states.move_us_frigate_from = {
view.prompt = "United States: Move up to " + game.moves + " frigates."
if (game.moves > 0) {
view.prompt += " Select a frigate to move.";
- for (let s of FRIGATE_SPACES) {
- if (count_american_frigates(s) > 0)
- gen_action(view, 'space', s);
+ for (let space of FRIGATE_SPACES) {
+ if (count_american_frigates(space) > 0)
+ gen_action(view, 'space', space);
}
}
gen_action(view, 'next');
@@ -611,13 +684,14 @@ states.move_us_frigate_from = {
},
space: function (space) {
push_undo();
- game.from = space;
+ game.where = space;
game.state = 'move_us_frigate_to'
},
next: function () {
- // resolve naval battles
- // resolve bombardment
- end_american_play();
+ if (count_naval_battle_or_bombardment_locations() > 0)
+ goto_allocate_gunboats();
+ else
+ end_american_play();
},
undo: pop_undo
}
@@ -627,20 +701,312 @@ states.move_us_frigate_to = {
if (is_inactive_player(current))
return view.prompt = "United States: Move up to " + game.moves + " frigates.";
view.prompt = "United States: Move up to " + game.moves + " frigates. Select a destation.";
- for (let s of FRIGATE_SPACES)
- if (s != game.from)
- gen_action(view, 'space', s);
+ for (let space of FRIGATE_SPACES)
+ if (space != game.where)
+ gen_action(view, 'space', space);
gen_action_undo(view);
},
space: function (space) {
- move_one_piece(US_FRIGATES, game.from, space);
+ log(game.active + " moves a frigate from " + SPACES[game.where] + " to " + SPACES[space] + ".");
+ move_one_piece(US_FRIGATES, game.where, space);
--game.moves;
- game.from = null;
+ game.where = null;
game.state = 'move_us_frigate_from'
},
undo: pop_undo
}
+function goto_allocate_gunboats() {
+ if (count_american_gunboats(MALTA_HARBOR) == 0)
+ return goto_select_combat();
+ game.where = MALTA_HARBOR;
+ game.state = 'allocate_gunboats';
+}
+
+states.allocate_gunboats = {
+ prompt: function (view, current) {
+ view.prompt = "United States: Allocate gunboats to battle locations.";
+ if (is_inactive_player(current))
+ return view.prompt;
+ if (count_american_gunboats(MALTA_HARBOR) > 0) {
+ for (let space of BATTLE_SPACES)
+ if (is_naval_battle_or_bombardment_location(space))
+ gen_action(view, 'space', space);
+ }
+ gen_action(view, 'next');
+ gen_action_undo(view);
+ },
+ space: function (space) {
+ push_undo();
+ log(game.active + " moves a gunboat to " + SPACES[space] + ".");
+ move_one_piece(US_GUNBOATS, MALTA_HARBOR, space);
+ },
+ next: function () {
+ game.where = null;
+ goto_select_combat();
+ },
+ undo: pop_undo
+}
+
+function goto_select_combat() {
+ clear_undo();
+ // TODO: auto-select if only one location?
+ if (count_naval_battle_or_bombardment_locations() > 0)
+ game.state = 'select_combat';
+ else
+ end_american_play();
+}
+
+states.select_combat = {
+ prompt: function (view, current) {
+ view.prompt = "United States: Pick the next naval combat or bombardment."
+ if (is_inactive_player(current))
+ return view.prompt;
+ for (let space of BATTLE_SPACES)
+ if (is_naval_battle_or_bombardment_location(space))
+ gen_action(view, 'space', space);
+ },
+ space: function (space) {
+ game.where = space;
+ if (is_naval_battle_location(space))
+ goto_naval_battle();
+ else
+ goto_naval_bombardment();
+ },
+}
+
+function fire(space, what, n_dice) {
+ let hits = 0;
+ for (let i = 0; i < n_dice; ++i) {
+ let roll = roll_d6();
+ if (roll == 6)
+ ++hits;
+ log(what + " fires " + roll + ".");
+ }
+ return hits;
+}
+
+function goto_naval_battle() {
+ log("Naval battle in " + SPACES[game.where] + ".");
+ // TODO: battle cards
+ naval_battle_round();
+}
+
+function naval_battle_round() {
+ let n_us_frigates = count_american_frigates(game.where);
+ let n_us_gunboats = count_american_gunboats(game.where);
+ let us_hitpoints = n_us_frigates * 2 + n_us_gunboats;
+ let n_tr_frigates = count_tripolitan_frigates(game.where);
+ let n_tr_corsairs = count_tripolitan_corsairs(game.where);
+ let n_al_corsairs = count_allied_corsairs(game.where);
+ let tr_hitpoints = n_tr_frigates * 2 + n_tr_corsairs + n_al_corsairs;
+
+ game.n_tr_hits = 0;
+ game.n_tr_hits += fire(game.where, "US frigate", 2 * n_us_frigates);
+ game.n_tr_hits += fire(game.where, "US gunboat", 1 * n_us_gunboats);
+ if (game.n_tr_hits > tr_hitpoints)
+ game.n_tr_hits = tr_hitpoints;
+
+ game.n_us_hits = 0;
+ game.n_us_hits += fire(game.where, "Tripolitan frigate", 2 * n_tr_frigates);
+ game.n_us_hits += fire(game.where, "Tripolitan corsair", 1 * n_tr_corsairs);
+ game.n_us_hits += fire(game.where, "Allied corsair", 1 * n_al_corsairs);
+ if (game.n_us_hits > us_hitpoints)
+ game.n_us_hits = us_hitpoints;
+
+ if (game.active == US)
+ goto_allocate_american_hits();
+ else
+ goto_allocate_tripolitan_hits();
+}
+
+function goto_allocate_american_hits() {
+ if (game.n_us_hits > 0) {
+ game.active = US;
+ game.state = 'allocate_us_hits';
+ } else if (game.n_tr_hits > 0) {
+ game.active = TR;
+ game.state = 'allocate_tr_hits';
+ } else {
+ end_naval_battle();
+ }
+}
+
+function goto_allocate_tripolitan_hits() {
+ if (game.n_tr_hits > 0) {
+ game.active = TR;
+ game.state = 'allocate_tr_hits';
+ } else if (game.n_us_hits > 0) {
+ game.active = US;
+ game.state = 'allocate_us_hits';
+ } else {
+ end_naval_battle();
+ }
+}
+
+states.allocate_us_hits = {
+ prompt: function (view, current) {
+ view.prompt = "United States: Allocate " + game.n_us_hits + " hits in " + SPACES[game.where] + ".";
+ if (is_inactive_player(current))
+ return view.prompt;
+ gen_action_undo(view);
+ if (game.n_us_hits > 0) {
+ for (let p of US_FRIGATES)
+ if (game.location[p] == game.where)
+ gen_action(view, 'piece', p);
+ for (let p of US_GUNBOATS)
+ if (game.location[p] == game.where)
+ gen_action(view, 'piece', p);
+ } else {
+ gen_action(view, 'next');
+ }
+ },
+ piece: function (p) {
+ push_undo();
+ --game.n_us_hits;
+ if (US_FRIGATES.includes(p)) {
+ if (game.damaged.includes(p)) {
+ log("US frigate sinks!");
+ game.location[p] = TRIPOLITAN_SUPPLY;
+ remove_from_array(game.damaged, p);
+ // TODO: check victory
+ } else {
+ log("US frigate is damaged.");
+ game.damaged.push(p);
+ }
+ }
+ if (US_GUNBOATS.includes(p)) {
+ log("US gunboat sinks.");
+ move_one_piece(US_GUNBOATS, game.where, UNITED_STATES_SUPPLY);
+ }
+ },
+ next: function () {
+ clear_undo();
+ if (game.n_tr_hits > 0) {
+ game.active = TR;
+ game.state = 'allocate_tr_hits';
+ } else {
+ end_naval_battle();
+ }
+ },
+ undo: pop_undo
+}
+
+states.allocate_tr_hits = {
+ prompt: function (view, current) {
+ view.prompt = "Tripolitania: Allocate " + game.n_tr_hits + " hits in " + SPACES[game.where] + ".";
+ if (is_inactive_player(current))
+ return view.prompt;
+ gen_action_undo(view);
+ if (game.n_tr_hits > 0) {
+ for (let p of TR_FRIGATES)
+ if (game.location[p] == game.where)
+ gen_action(view, 'piece', p);
+ for (let p of TR_CORSAIRS)
+ if (game.location[p] == game.where)
+ gen_action(view, 'piece', p);
+ for (let p of AL_CORSAIRS)
+ if (game.location[p] == game.where)
+ gen_action(view, 'piece', p);
+ } else {
+ gen_action(view, 'next');
+ }
+ },
+ piece: function (p) {
+ push_undo();
+ --game.n_tr_hits;
+ if (TR_FRIGATES.includes(p)) {
+ if (game.damaged.includes(p)) {
+ log("Tripolitan frigate sinks!");
+ game.location[p] = TRIPOLITAN_SUPPLY;
+ remove_from_array(game.damaged, p);
+ } else {
+ log("Tripolitan frigate is damaged.");
+ game.damaged.push(p);
+ }
+ }
+ if (TR_CORSAIRS.includes(p)) {
+ log("Tripolitan corsair sinks.");
+ move_one_piece(TR_CORSAIRS, game.where, TRIPOLITAN_SUPPLY);
+ }
+ if (AL_CORSAIRS.includes(p)) {
+ log("Allied corsair sinks.");
+ move_one_piece(TR_CORSAIRS, game.where, TRIPOLITAN_SUPPLY);
+ }
+ },
+ next: function () {
+ clear_undo();
+ if (game.n_us_hits > 0) {
+ game.active = US;
+ game.state = 'allocate_us_hits';
+ } else {
+ end_naval_battle();
+ }
+ },
+ undo: pop_undo
+}
+
+function move_damaged_frigate_to_year_track(p, supply) {
+ if (game.year == 1806)
+ game.location[p] = supply;
+ else
+ game.location[p] = TRACK_YEAR[game.year + 1];
+ remove_from_array(game.damaged, p);
+}
+
+function remove_damaged_frigates() {
+ for (let p of US_FRIGATES)
+ if (game.damaged.includes(p))
+ move_damaged_frigate_to_year_track(p, UNITED_STATES_SUPPLY);
+ for (let p of TR_FRIGATES)
+ if (game.damaged.includes(p))
+ move_damaged_frigate_to_year_track(p, TRIPOLITAN_SUPPLY);
+}
+
+function end_naval_battle() {
+ remove_damaged_frigates()
+
+ move_all_pieces(US_FRIGATES, game.where, MALTA_HARBOR);
+ move_all_pieces(US_GUNBOATS, game.where, MALTA_HARBOR);
+
+ if (game.where == TRIPOLI_PATROL_ZONE) {
+ move_all_pieces(TR_FRIGATES, game.where, TRIPOLI_HARBOR);
+ move_all_pieces(TR_CORSAIRS, game.where, TRIPOLI_HARBOR);
+ }
+
+ game.where = null;
+ goto_select_combat();
+}
+
+function goto_naval_bombardment() {
+ log("Naval bombardment in " + SPACES[game.where] + ".");
+ naval_bombardment_round();
+ end_naval_bombardment();
+}
+
+function naval_bombardment_round() {
+ let n_frigates = count_american_frigates(game.where);
+ let n_gunboats = count_american_gunboats(game.where);
+ let n_infantry = count_tripolitan_infantry(game.where);
+
+ let n_hits = 0;
+ n_hits += fire(game.where, "US frigate", 2 * n_frigates);
+ n_hits += fire(game.where, "US gunboat", 1 * n_gunboats);
+ if (n_hits > n_infantry)
+ n_hits = n_infantry;
+
+ log(n_hits + " Tripolitan infantry eliminated.");
+ for (let i = 0; i < n_hits; ++i)
+ move_one_piece(TR_INFANTRY, game.where, TRIPOLITAN_SUPPLY);
+}
+
+function end_naval_bombardment() {
+ move_all_pieces(US_FRIGATES, game.where, MALTA_HARBOR);
+ move_all_pieces(US_GUNBOATS, game.where, MALTA_HARBOR);
+ game.where = null;
+ goto_select_combat();
+}
+
states.game_over = {
prompt: function (view, current) {
return view.prompt = game.victory;
@@ -656,6 +1022,7 @@ exports.setup = function (scenario, players) {
season: 0,
log: [],
location: [],
+ damaged: [],
us: {
core: [],
hand: [],
@@ -670,6 +1037,7 @@ exports.setup = function (scenario, players) {
coins: 0,
},
derne_captured: 0,
+ where: null,
undo: [],
};
@@ -776,6 +1144,7 @@ exports.view = function(state, current) {
year: game.year,
season: game.season,
location: game.location,
+ damaged: game.damaged,
active: game.active,
prompt: null,
actions: null,
@@ -792,6 +1161,7 @@ exports.view = function(state, current) {
discard: game.us.discard.length,
hand: game.us.hand.length,
},
+ where: game.where,
};
states[game.state].prompt(view, current);
diff --git a/ui.js b/ui.js
index b9b8928..205d42c 100644
--- a/ui.js
+++ b/ui.js
@@ -12,6 +12,17 @@ function get_space_id(name) {
return SPACES.indexOf(name);
}
+function create_piece_list(n, name) {
+ let list = [];
+ for (let i = 1; i <= n; ++i)
+ list.push(get_piece_id(name + i));
+ return list;
+}
+
+const US_FRIGATES = create_piece_list(8, 'us_frigate_');
+const TR_FRIGATES = create_piece_list(2, 'tr_frigate_');
+const FRIGATES = US_FRIGATES.concat(TR_FRIGATES);
+
const ALEXANDRIA_HARBOR = get_space_id("Alexandria Harbor");
const ALGIERS_HARBOR = get_space_id("Algiers Harbor");
const ALGIERS_PATROL_ZONE = get_space_id("Algiers Patrol Zone");
@@ -44,12 +55,12 @@ let ui = {
}
function on_focus_space(evt) {
- let where = evt.target.space;
+ let where = SPACES[evt.target.space];
document.getElementById("status").textContent = where;
}
function on_focus_piece(evt) {
- let who = evt.target.piece;
+ let who = PIECES[evt.target.piece];
document.getElementById("status").textContent = who;
}
@@ -199,15 +210,27 @@ function update_pieces() {
layout_space(game.location, TANGIER_PATROL_ZONE, 125, 410, 3);
layout_space(game.location, TRIPOLI_PATROL_ZONE, 1575, 390, 6);
layout_space(game.location, TUNIS_PATROL_ZONE, 1300, 130, 3);
+
+ for (let p of FRIGATES) {
+ if (game.damaged.includes(p))
+ ui.pieces[p].classList.add("damaged");
+ else
+ ui.pieces[p].classList.remove("damaged");
+ }
}
function update_spaces() {
- for (let space in ui.spaces)
- ui.spaces[space].classList.remove('enabled');
+ for (let space in ui.spaces) {
+ ui.spaces[space].classList.remove('highlight');
+ ui.spaces[space].classList.remove('where');
+ }
+ if (game.where != null) {
+ ui.spaces[game.where].classList.add('where');
+ }
if (game.actions && game.actions.space) {
for (let space of game.actions.space) {
console.log("enable space " + space);
- ui.spaces[space].classList.add('enabled');
+ ui.spaces[space].classList.add('highlight');
}
}
}