From 59992c96a30db765689a713ebc373c67c06bc26f Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 23 Jun 2024 15:23:22 +0200 Subject: move --- play.js | 101 +++++++++++++---- rules.js | 370 ++++++++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 316 insertions(+), 155 deletions(-) diff --git a/play.js b/play.js index 4c37ce5..297866a 100644 --- a/play.js +++ b/play.js @@ -288,6 +288,10 @@ function on_init() { ui.a_mcu = build_piece("marker cu american", 60+2, 60+2) ui.f_mcu = build_piece("marker cu french", 60+2, 60+2) + ui.b_rcu = build_piece("marker cu british", 60+2, 60+2) + ui.a_rcu = build_piece("marker cu american", 60+2, 60+2) + ui.f_rcu = build_piece("marker cu french", 60+2, 60+2) + for (let s = 0; s < 7; ++s) { let e = ui.seas[s] = document.createElement("div") let [ x, y, w, h ] = data.layout.sea[s] @@ -377,6 +381,8 @@ function general_offset(g) { for (let i = 0; i < g; ++i) { if (view.move && view.move.who === i) continue + if (view.react && view.react.who === i) + continue if (view.loca[i] === view.loca[g]) ++n } @@ -388,12 +394,40 @@ function general_total(g) { for (let i = 0; i < general_count; ++i) { if (view.move && view.move.who === i) continue + if (view.react && view.react.who === i) + continue if (view.loca[i] === view.loca[g]) ++n } return n } +function get_army_xy_lerp(s1, s2) { + let x, y + if (s1 >= 66) { + x = data.spaces[s1].x + y = data.spaces[s1].y - 40 + } else { + x = (data.spaces[s1].x + data.spaces[s2].x) >> 1 + y = (data.spaces[s1].y + data.spaces[s2].y) >> 1 + } + if (s1 === s2) + y -= 40 + return [ x, y ] +} + +function get_army_xy(s) { + let x, y + if (s >= 66) { + x = data.spaces[s].x + y = data.spaces[s].y - 80 + } else { + x = data.spaces[s].x + y = data.spaces[s].y - 40 + } + return [ x, y ] +} + function on_update() { let e @@ -404,6 +438,9 @@ function on_update() { remember_position(ui.b_mcu) remember_position(ui.a_mcu) remember_position(ui.f_mcu) + remember_position(ui.b_rcu) + remember_position(ui.a_rcu) + remember_position(ui.f_rcu) for (let g = 0; g < general_count; ++g) remember_position(ui.generals[g]) @@ -432,32 +469,49 @@ function on_update() { show_marker_at(ui.french_navy, data.layout.sea[view.french_navy][0]-15, data.layout.sea[view.french_navy][1]+42) for (let s = 0; s < data.spaces.length; ++s) { + let acu = count_american_cu(s) + let fcu = count_french_cu(s) + let bcu = count_british_cu(s) + if (view.move && view.move.to === s) { - toggle_marker_with_number(ui.a_cu[s], count_american_cu(s) - view.move.carry_american) - toggle_marker_with_number(ui.f_cu[s], count_french_cu(s) - view.move.carry_french) - toggle_marker_with_number(ui.b_cu[s], count_british_cu(s) - view.move.carry_british) - } else { - toggle_marker_with_number(ui.a_cu[s], count_american_cu(s)) - toggle_marker_with_number(ui.f_cu[s], count_french_cu(s)) - toggle_marker_with_number(ui.b_cu[s], count_british_cu(s)) + acu -= view.move.carry_american + fcu -= view.move.carry_french + bcu -= view.move.carry_british + } + if (view.react && view.react.from === s) { + acu -= view.react.carry_american + fcu -= view.react.carry_french + bcu -= view.react.carry_british } + toggle_marker_with_number(ui.a_cu[s], acu) + toggle_marker_with_number(ui.f_cu[s], fcu) + toggle_marker_with_number(ui.b_cu[s], bcu) } if (view.move) { - let s = view.move.to - let x = data.spaces[s].x - let y = data.spaces[s].y - if (s >= 66) - y -= 40 + let [ x, y ] = get_army_xy_lerp(view.move.from, view.move.to) if (view.move.carry_american > 0 && view.move.carry_french > 0) { - toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x-15, y - 40) - toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x+15, y - 40) + toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x-15, y) + toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x+15, y) } else { - toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x, y - 40) - toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x, y - 40) + toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x, y) + toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x, y) } - toggle_marker_with_number_at(ui.b_mcu, view.move.carry_british, x, y - 40) + toggle_marker_with_number_at(ui.b_mcu, view.move.carry_british, x, y) + } + + if (view.react) { + let [ x, y ] = get_army_xy_lerp(view.react.from, view.react.to) + + if (view.react.carry_american > 0 && view.react.carry_french > 0) { + toggle_marker_with_number_at(ui.a_rcu, view.react.carry_american, x-15, y) + toggle_marker_with_number_at(ui.f_rcu, view.react.carry_french, x+15, y) + } else { + toggle_marker_with_number_at(ui.a_rcu, view.react.carry_american, x, y) + toggle_marker_with_number_at(ui.f_rcu, view.react.carry_french, x, y) + } + toggle_marker_with_number_at(ui.b_rcu, view.react.carry_british, x, y) } for (let g = 0; g < general_count; ++g) { @@ -466,15 +520,19 @@ function on_update() { continue let { x, y } = data.spaces[s] + if (view.move && view.move.who === g) + [ x, y ] = get_army_xy_lerp(view.move.from, view.move.to) + if (view.react && view.react.who === g) + [ x, y ] = get_army_xy_lerp(view.react.from, view.react.to) if (s !== FRENCH_REINFORCEMENTS && count_french_cu(s) > 0) x += 30 if (view.move && view.move.who === g) { ui.generals[g].classList.add("selected") - if (s >= 66) - y -= 40 - y -= 40 + x += 30 + } else if (view.react && view.react.who === g) { + ui.generals[g].classList.add("selected") x += 30 } else { ui.generals[g].classList.remove("selected") @@ -560,11 +618,14 @@ function on_update() { action_button("drop_british_cu", "Drop CU") action_button("drop_american_cu", "Drop CU") + action_button("no_general", "No General") + action_button("britain_first", "Britain") action_button("america_first", "America") action_button("surrender", "Surrender") action_button("stop", "Stop") + action_button("roll", "Roll") action_button("next", "Next") action_button("done", "Done") action_button("pass", "Pass") diff --git a/rules.js b/rules.js index 01f8bf6..da247c9 100644 --- a/rules.js +++ b/rules.js @@ -1,8 +1,8 @@ "use strict" // TODO: capture washington -// TODO: campaign messed up who is who after battle ? // TODO: retreat with 0 CU after battle ? +// TODO: retreat with 0 CU into lone enemy general ? /* washington's capture - winning general with no CU on enemy PC (no undo anyway) @@ -884,47 +884,32 @@ function place_french_reinforcements(who, where) { move_french_cu(AMERICAN_REINFORCEMENTS, where, count_french_cu(AMERICAN_REINFORCEMENTS)) } -function pickup_max_british_cu(move, where) { - move.carry_british = count_unmoved_british_cu(where) +function pickup_max_british_cu(move) { + move.carry_british = count_unmoved_british_cu(move.from) if (move.carry_british > 5) move.carry_british = 5 move.carry_american = 0 move.carry_french = 0 } -function pickup_max_american_cu(move, where) { +function pickup_max_american_cu(move) { move.carry_british = 0 - move.carry_french = count_unmoved_french_cu(where) - move.carry_american = count_unmoved_american_cu(where) + move.carry_french = count_unmoved_french_cu(move.from) + move.carry_american = count_unmoved_american_cu(move.from) if (move.carry_french > 5) move.carry_french = 5 if (move.carry_american + move.carry_french > 5) move.carry_american = 5 - move.carry_french } -function move_army(who, from, to) { - game.move.count -= movement_cost(from, to) - if (game.move.mobility && has_enemy_cu(to)) { - game.move.mobility = false - game.move.count -= 1 - } - if (game.move.carry_british > 0) - move_british_cu(from, to, game.move.carry_british) - if (game.move.carry_american > 0) - move_american_cu(from, to, game.move.carry_american) - if (game.move.carry_french > 0) - move_french_cu(from, to, game.move.carry_french) - move_general(who, to) -} - -function intercept_army(who, from, to) { - if (game.intercept.carry_british > 0) - move_british_cu(from, to, game.intercept.carry_british) - if (game.intercept.carry_american > 0) - move_american_cu(from, to, game.intercept.carry_american) - if (game.intercept.carry_french > 0) - move_french_cu(from, to, game.intercept.carry_french) - move_general(who, to) +function move_army(army) { + if (army.carry_british > 0) + move_british_cu(army.from, army.to, army.carry_british) + if (army.carry_american > 0) + move_american_cu(army.from, army.to, army.carry_american) + if (army.carry_french > 0) + move_french_cu(army.from, army.to, army.carry_french) + move_general(army.who, army.to) } function overrun(where) { @@ -978,24 +963,11 @@ function disperse_continental_congress() { /* PLACE/REMOVE PC IN COLONY */ -function gen_remove_british_pc_in_colony(list_of_colonies) { - for (let colony of list_of_colonies) { - for (let space of COLONIES[colony]) { - if (has_british_pc(space) && has_no_british_cu(space)) { - gen_action_space(space) - } - } - } -} - function gen_place_american_pc_in_colony(list_of_colonies) { - for (let colony of list_of_colonies) { - for (let space of COLONIES[colony]) { - if (has_no_pc(space) && has_no_british_playing_piece(space)) { + for (let colony of list_of_colonies) + for (let space of COLONIES[colony]) + if (has_no_pc(space) && has_no_british_playing_piece(space)) gen_action_space(space) - } - } - } } /* SETUP PHASE */ @@ -1536,16 +1508,18 @@ function goto_ops_reinforcements(c) { states.ops_british_reinforcements_who = { prompt() { - view.prompt = "Reinforcements: choose an available general or pass to bring only CU." + view.prompt = "Reinforcements: choose an available general." view.prompt += " Carrying " + game.count + " British CU." - view.move = { to: BRITISH_REINFORCEMENTS, who: NOBODY, carry_british: game.count } - view.actions.pass = 1 + view.move = { from: BRITISH_REINFORCEMENTS, to: BRITISH_REINFORCEMENTS, who: NOBODY, carry_british: game.count } + view.actions.no_general = 1 gen_british_reinforcements_who() }, drop_british_cu() { + push_undo() --game.count }, pickup_british_cu() { + push_undo() ++game.count }, general(g) { @@ -1553,7 +1527,7 @@ states.ops_british_reinforcements_who = { game.state = "ops_british_reinforcements_where" game.who = g }, - pass() { + no_general() { push_undo() game.state = "ops_british_reinforcements_where" delete game.who @@ -1564,16 +1538,19 @@ states.ops_british_reinforcements_where = { prompt() { view.prompt = "Reinforcements: choose a port space." view.prompt += " Carrying " + game.count + " British CU." - view.move = { to: BRITISH_REINFORCEMENTS, who: game.who, carry_british: game.count } + view.move = { from: BRITISH_REINFORCEMENTS, to: BRITISH_REINFORCEMENTS, who: game.who, carry_british: game.count } gen_british_reinforcements_where() }, drop_british_cu() { + push_undo() --game.count }, pickup_british_cu() { + push_undo() ++game.count }, space(space) { + push_undo() place_british_reinforcements(game.who, game.count, space) delete game.who // capture george washington happens in end_strategy_card @@ -1583,9 +1560,9 @@ states.ops_british_reinforcements_where = { states.ops_american_reinforcements_who = { prompt() { - view.prompt = "Reinforcements: choose an available general or pass to bring only CU." - view.move = { to: AMERICAN_REINFORCEMENTS, who: NOBODY, carry_american: game.count } - view.actions.pass = 1 + view.prompt = "Reinforcements: choose an available general." + view.move = { from: AMERICAN_REINFORCEMENTS, to: AMERICAN_REINFORCEMENTS, who: NOBODY, carry_american: game.count } + view.actions.no_general = 1 gen_american_reinforcements_who() }, general(g) { @@ -1593,7 +1570,7 @@ states.ops_american_reinforcements_who = { game.state = "ops_american_reinforcements_where" game.who = g }, - pass() { + no_general() { push_undo() game.state = "ops_american_reinforcements_where" delete game.who @@ -1603,7 +1580,7 @@ states.ops_american_reinforcements_who = { states.ops_american_reinforcements_where = { prompt() { view.prompt = "Reinforcements: choose a space." - view.move = { to: AMERICAN_REINFORCEMENTS, who: game.who, carry_american: game.count } + view.move = { from: AMERICAN_REINFORCEMENTS, to: AMERICAN_REINFORCEMENTS, who: game.who, carry_american: game.count } gen_american_reinforcements_where(game.who) }, space(space) { @@ -1709,6 +1686,7 @@ states.ops_general_who = { goto_ops_general_move(g, false) }, pass() { + push_undo() if (game.campaign > 0) game.campaign = 0 end_strategy_card() @@ -1756,6 +1734,8 @@ states.remove_general = { gen_remove_general(game.where) }, general(g) { + push_undo() + delete game.where if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -1774,6 +1754,7 @@ states.remove_general_after_intercept = { gen_remove_general(game.move.to) }, general(g) { + // TODO: allow undo before proceeding to battle cards? if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -1793,11 +1774,13 @@ states.remove_general_after_retreat = { gen_remove_general(game.where) }, general(g) { + push_undo() + delete game.where if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else move_general(g, AMERICAN_REINFORCEMENTS) - end_battle() + goto_end_battle() }, } @@ -1851,30 +1834,34 @@ function goto_ops_general_move(g, marblehead) { } if (game.active === P_BRITAIN) - pickup_max_british_cu(game.move, where) + pickup_max_british_cu(game.move) else - pickup_max_american_cu(game.move, where) -} - -states.ops_general_move = { - prompt() { - view.prompt = "Move " + data.generals[game.move.who].name + " with " - if (game.move.carry_british > 0) { - view.prompt += game.move.carry_british + " CU." - } else if (game.move.carry_french + game.move.carry_american > 0) { - if (game.move.carry_french > 0) { - if (game.move.carry_american > 0) { - view.prompt += game.move.carry_french + " French CU and " - view.prompt += game.move.carry_american + " American CU." - } else { - view.prompt += game.move.carry_french + " CU." - } + pickup_max_american_cu(game.move) +} + +function format_move_prompt() { + view.prompt = "Move " + data.generals[game.move.who].name + " with " + if (game.move.carry_british > 0) { + view.prompt += game.move.carry_british + " CU." + } else if (game.move.carry_french + game.move.carry_american > 0) { + if (game.move.carry_french > 0) { + if (game.move.carry_american > 0) { + view.prompt += game.move.carry_french + " French CU and " + view.prompt += game.move.carry_american + " American CU." } else { - view.prompt += game.move.carry_american + " CU." + view.prompt += game.move.carry_french + " CU." } } else { - view.prompt += game.move.carry_american + " no CU." + view.prompt += game.move.carry_american + " CU." } + } else { + view.prompt += game.move.carry_american + " no CU." + } +} + +states.ops_general_move = { + prompt() { + format_move_prompt() view.prompt += " " + game.move.count + " MP left." // Cannot stop on enemy general @@ -1888,12 +1875,15 @@ states.ops_general_move = { }, pickup_british_cu() { + push_undo() ++game.move.carry_british }, pickup_american_cu() { + push_undo() ++game.move.carry_american }, pickup_french_cu() { + push_undo() ++game.move.carry_french }, @@ -1933,7 +1923,13 @@ states.ops_general_move = { game.move.from = from game.move.to = to - move_army(game.move.who, from, to) + game.move.count -= movement_cost(from, to) + if (game.move.mobility && has_enemy_cu(to)) { + game.move.mobility = false + game.move.count -= 1 + } + + move_army(game.move) // TODO: overrun after intercept? // TODO: disperse continental congress after intercept? @@ -1948,7 +1944,11 @@ states.ops_general_move = { } if (may_intercept) - goto_intercept() + game.state = "confirm_move_intercept" + else if (game.active === P_BRITAIN && has_enemy_cu(game.move.to)) + game.state = "confirm_move_battle" + else if (game.active === P_AMERICA && has_enemy_cu(game.move.to)) + goto_start_battle() else resume_moving() }, @@ -1958,11 +1958,36 @@ states.ops_general_move = { }, } -function resume_moving() { - // TODO: if general moving alone was captured by intercepting army - if (has_enemy_cu(game.move.to)) +states.confirm_move_intercept = { + prompt() { + format_move_prompt() + view.prompt += " You may be intercepted." + view.actions.next = 1 + }, + next() { + goto_intercept() + }, +} + +states.confirm_move_battle = { + prompt() { + format_move_prompt() + view.prompt += " Approach battle?" + view.actions.next = 1 + }, + next() { + clear_undo() goto_start_battle() - else if (game.move.count === 0) + }, +} + +function resume_moving() { + game.move.from = game.move.to + + game.state = "ops_general_move" + + // TODO: auto-end move or require manually stopping at final space? + if (game.move.count === 0) end_move() } @@ -1973,6 +1998,7 @@ function end_move() { mark_moved_american_cu(where, game.move.carry_american) mark_moved_french_cu(where, game.move.carry_french) } + delete game.move if (count_friendly_generals(where) > 1) @@ -2022,15 +2048,18 @@ function movement_cost(from, to) { } } +function is_quebec_falmouth_path(from, to) { + return (from === QUEBEC && to === FALMOUTH) || (to === QUEBEC && from === FALMOUTH) +} + function gen_move_general() { let from = location_of_general(game.move.who) let alone = game.move.carry_british + game.move.carry_american + game.move.carry_french === 0 for (let to of SPACES[from].adjacent) { let mp = 1 if (path_type(from, to) === "wilderness") { - if ((from === QUEBEC && to === FALMOUTH) || (to === QUEBEC && from === FALMOUTH)) - if (game.move.who !== ARNOLD) - continue + if (is_quebec_falmouth_path(from, to) && game.move.who !== ARNOLD) + continue mp = 3 } @@ -2039,7 +2068,7 @@ function gen_move_general() { continue if (has_enemy_pc(to)) continue - // TODO: more robust check for not stopping (or allow undo in case he gets stuck) + // TODO: more robust check for not stopping (or allow undo in case player gets stuck) if (has_enemy_general(to) && game.count - mp === 0) continue } @@ -2061,7 +2090,6 @@ function gen_move_general() { // don't leave alone if (alone && has_enemy_general(to)) continue - // TODO: duplicate action if can move by normal road gen_action_space(to) } } @@ -2077,6 +2105,8 @@ function can_intercept_to(to) { for (let space of SPACES[to].adjacent) { if (has_american_army(space)) { let g = find_american_or_french_general(space) + if (is_quebec_falmouth_path(to, space) && g !== ARNOLD) + continue if (g && !has_general_moved(g)) return true } @@ -2088,6 +2118,8 @@ function gen_intercept() { for (let space of SPACES[game.move.to].adjacent) { if (has_american_army(space)) { let g = find_american_or_french_general(space) + if (is_quebec_falmouth_path(game.move.to, space) && g !== ARNOLD) + continue if (g && !has_general_moved(g)) gen_action_general(g) } @@ -2097,37 +2129,56 @@ function gen_intercept() { function goto_intercept() { clear_undo() game.active = P_AMERICA - game.state = "intercept" + game.state = "intercept_who" } -states.intercept = { +states.intercept_who = { prompt() { view.prompt = "Intercept " + game.move.who + " at " + game.move.to + "?" view.actions.pass = 1 gen_intercept() }, general(g) { + push_undo() set_general_moved(g) + + game.intercept = { + who: g, + from: location_of_general(g), + to: game.move.to, + carry_british: 0, + carry_american: 0, + carry_french: 0, + } + + pickup_max_american_cu(game.intercept) + + game.state = "intercept_roll" + }, + pass() { + end_intercept() + }, +} + +states.intercept_roll = { + prompt() { + view.prompt = "Intercept " + game.move.who + " at " + game.move.to + "?" + view.react = game.intercept + view.actions.roll = 1 + }, + roll() { + let g = game.intercept.who let die = roll_d6() if (die <= GENERALS[g].agility) { log(g + " intercepted (" + die + " <= " + GENERALS[g].agility + ")") game.move.did_intercept = 1 - game.intercept = { - who: g, - from: location_of_general(g), - to: game.move.to, - carry_british: 0, - carry_american: 0, - carry_french: 0, - } - - pickup_max_american_cu(game.intercept, location_of_general(g)) - intercept_army(g, location_of_general(g), game.move.to) + move_army(game.intercept) - if (has_enemy_general(game.move.to) && !has_enemy_cu(game.move.to)) { + /* TODO: IMPOSSIBLE? + if (has_enemy_general(game.move.to) && !has_enemy_cu(game.move.to)) capture_enemy_general(game.move.to) - } + */ if (count_friendly_generals(game.move.to) > 1) goto_remove_general_after_intercept() @@ -2140,16 +2191,15 @@ states.intercept = { end_intercept() } }, - pass() { - end_intercept() - }, } function end_intercept() { game.active = P_BRITAIN - game.state = "ops_general_move" delete game.intercept - resume_moving() + if (has_enemy_cu(game.move.to)) + goto_start_battle() + else + resume_moving() } /* RETREAT BEFORE BATTLE */ @@ -2165,8 +2215,6 @@ function can_retreat_before_battle() { } function goto_start_battle() { - clear_undo() - game.combat = { attacker: game.active, a_bonus: 0, @@ -2194,24 +2242,48 @@ states.retreat_before_battle = { gen_defender_retreat() }, space(to) { - let who = find_american_or_french_general(game.move.to) + push_undo() + game.retreat = { + from: game.move.to, + to, + who: find_american_or_french_general(game.move.to), + carry_american: 0 + } + pickup_max_american_cu(game.retreat) + game.state = "retreat_before_battle_roll" + }, + pass() { + end_retreat_before_battle() + }, +} + +states.retreat_before_battle_roll = { + prompt() { + view.prompt = "Attempt retreat before battle?" + view.react = game.retreat + view.actions.roll = 1 + // TODO: choose which CU to retreat with if mix of french/american + }, + roll() { + let who = game.retreat.who + let to = game.retreat.to let agility = GENERALS[who].agility if (GENERALS[who].bonus) agility += 2 let roll = roll_d6() if (roll <= agility) { logp("successfully retreated before battle: " + roll + " <= " + agility) - pickup_max_american_cu(game.move.to) - move_army(who, game.move.to, to) + move_army(game.retreat) + if (has_enemy_general(to)) + capture_enemy_general(to) + delete game.retreat goto_remove_general_after_retreat_before_battle(to) } else { logp("failed to retreat before battle: " + roll + " > " + agility) + delete game.retreat end_retreat_before_battle() } }, - pass() { - end_retreat_before_battle() - }, } function goto_remove_general_after_retreat_before_battle(where) { @@ -2229,6 +2301,7 @@ states.remove_general_after_retreat_before_battle = { gen_remove_general(game.where) }, general(g) { + delete game.where if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -2238,13 +2311,16 @@ states.remove_general_after_retreat_before_battle = { } function end_remove_general_after_retreat_before_battle() { + game.active = P_BRITAIN let b_cu = count_british_cu(game.move.to) let a_cu = count_american_and_french_cu(game.move.to) if (a_cu === 0) { end_battle() + next_strategy_card() // NOTE: skip pause at end of movement } else if (b_cu >= 4 && a_cu === 1) { overrun(game.move.to) end_battle() + next_strategy_card() // NOTE: skip pause at end of movement } else { end_retreat_before_battle() } @@ -2412,18 +2488,24 @@ function roll_winner_combat_losses(log, losing_general) { function apply_british_combat_losses(max) { let n = Math.min(count_british_cu(game.move.to), max) remove_british_cu(game.move.to, n) + if (game.combat.attacker === P_BRITAIN) + game.move.carry_british -= n return n } function apply_american_combat_losses(max) { let n = Math.min(count_american_cu(game.move.to), max) remove_american_cu(game.move.to, n) + if (game.combat.attacker === P_AMERICA) + game.move.carry_american -= n return n } function apply_french_combat_losses(max) { let n = Math.min(count_french_cu(game.move.to), max) remove_french_cu(game.move.to, n) + if (game.combat.attacker === P_AMERICA) + game.move.carry_french -= n return n } @@ -2567,13 +2649,15 @@ function resolve_battle() { /* RETREAT AFTER BATTLE */ -function can_defender_retreat(to) { +function can_defender_retreat(to, is_lone) { if (to === game.move.from) return false if (has_enemy_pc(to)) return false if (has_enemy_cu(to)) return false + if (is_lone && has_enemy_general(to)) + return false return true } @@ -2586,17 +2670,25 @@ function can_attacker_retreat() { return true } +function is_general_without_cu(s) { + if (game.active === P_AMERICA) + return has_american_or_french_general(s) && !has_american_or_french_cu(s) + else + return has_british_general(s) && !has_british_cu(s) +} + function gen_defender_retreat() { - let from = game.move.to - for (let to of SPACES[from].adjacent) { - if (can_defender_retreat(to)) + let here = game.move.to + let is_lone = is_general_without_cu(here) + for (let to of SPACES[here].adjacent) { + if (can_defender_retreat(to, is_lone)) gen_action_space(to) } if (game.active === P_BRITAIN) { let can_sea_retreat = false - if (is_non_blockaded_port(from)) { - if (is_fortified_port(from)) { - if (has_british_pc(from) && is_non_blockaded_port(from)) + if (is_non_blockaded_port(here)) { + if (is_fortified_port(here)) { + if (has_british_pc(here) && is_non_blockaded_port(here)) can_sea_retreat = true } else { can_sea_retreat = true @@ -2604,7 +2696,7 @@ function gen_defender_retreat() { } if (can_sea_retreat) { for (let to of all_spaces) { - if (to !== from && is_non_blockaded_port(to)) { + if (to !== game.move.from && is_non_blockaded_port(to)) { if (!has_american_pc(to) && !has_american_or_french_cu(to)) { gen_action_space(to) } @@ -2641,31 +2733,39 @@ function goto_retreat_after_battle(victor) { states.retreat_after_battle = { prompt() { view.prompt = "Retreat after battle." - gen_action("surrender") + if (game.active === P_AMERICA) + view.retreat = find_american_or_french_general(game.move.to) + else + view.retreat = find_british_general(game.move.to) + view.actions.surrender = 1 + // TODO: retreat with 0 CU into lone enemy general if (game.active === game.combat.attacker) gen_attacker_retreat() else gen_defender_retreat() }, space(to) { + push_undo() logp("retreated to " + to) if (game.active === P_BRITAIN) retreat_british_army(game.move.to, to) else retreat_american_army(game.move.to, to) + if (has_enemy_general(to)) + // TODO: what if moving without CU? + capture_enemy_general(to) if (count_friendly_generals(to) > 1) goto_remove_general_after_retreat(to) else end_battle() }, surrender() { + push_undo() logp("surrendered") - if (game.active === P_BRITAIN) surrender_british_army(game.move.to) else surrender_american_army(game.move.to) - end_battle() }, } @@ -2807,7 +2907,10 @@ states.remove_british_pc_from = { prompt() { view.prompt = "Remove British PC markers from " + game.where.map(x=>data.colony_name[x]).join(", ") + ". " + game.count + " left." view.actions.pass = 1 - gen_remove_british_pc_in_colony(game.where) + for (let colony of game.where) + for (let space of COLONIES[colony]) + if (has_british_pc(space) && has_no_british_cu(space)) + gen_action_space(space) }, space(where) { push_undo() @@ -3497,9 +3600,9 @@ function goto_political_control_phase() { function gen_place_continental_congress() { let n = 0 for (let space of all_spaces) { - if (SPACES[space].colony !== "CA") { + if (SPACES[space].colony !== Canada) { if (has_american_pc(space) && has_no_british_playing_piece(space)) { - gen_action("place_continental_congress", space) + gen_action_space(space) ++n } } @@ -3513,7 +3616,7 @@ states.return_continental_congress = { if (gen_place_continental_congress() === 0) view.actions.pass = 1 }, - place_continental_congress(where) { + space(where) { game.congress = where goto_place_pc_markers_segment() }, @@ -3728,7 +3831,7 @@ states.remove_isolated_pc_segment = { }, space(s) { log("Removed American PC from " + s) - set_delete(s) + set_delete(game.isolated, s) remove_pc(s) }, next() { @@ -3800,7 +3903,7 @@ function automatic_victory() { let n_british = 0 for (let space of all_spaces) { n_american += count_french_cu(space) + count_american_cu(space) - if (SPACES[space].colony !== "CA") + if (SPACES[space].colony !== Canada) n_british += count_british_cu(space) } if (n_american === 0) { @@ -3883,9 +3986,6 @@ exports.setup = function (seed, scenario, options) { exports.action = function (state, current, action, arg) { game = state - //Object.seal(game) // don't allow adding properties! - - // TODO: check against action list if (current === game.active) { let S = states[game.state] if (action in S) { @@ -3924,7 +4024,7 @@ exports.view = function (state, current) { b_queue: game.b_queue, last_played: game.last_played, - move: game.move + move: game.move, } if (current === P_AMERICA) -- cgit v1.2.3