diff options
author | Tor Andersson <tor@ccxvii.net> | 2021-06-06 22:11:00 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2023-02-18 12:12:42 +0100 |
commit | 5718d3695e0783ce8695aeb51f3af797d6037e60 (patch) | |
tree | d8eb8d09a7f8f617dd61c11fc1618b04d2c67707 | |
parent | 7edd71f76123e10a15daa5918f7f3eae2dfbf062 (diff) | |
download | shores-of-tripoli-5718d3695e0783ce8695aeb51f3af797d6037e60.tar.gz |
tripoli: Naval bombardment and combat.
-rw-r--r-- | play.html | 154 | ||||
-rw-r--r-- | rules.js | 396 | ||||
-rw-r--r-- | ui.js | 33 |
3 files changed, 498 insertions, 85 deletions
@@ -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> @@ -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); @@ -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'); } } } |