summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-06-23 15:23:22 +0200
committerTor Andersson <tor@ccxvii.net>2024-08-21 00:28:20 +0200
commit59992c96a30db765689a713ebc373c67c06bc26f (patch)
tree4921ea3cd49eb88189f0db35d7551c9fda61cee4
parentd34beb24b0202b00bb2523a16ab7275534bcedc9 (diff)
downloadwashingtons-war-59992c96a30db765689a713ebc373c67c06bc26f.tar.gz
move
-rw-r--r--play.js101
-rw-r--r--rules.js370
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)