diff options
-rw-r--r-- | play.js | 12 | ||||
-rw-r--r-- | rules.js | 624 |
2 files changed, 325 insertions, 311 deletions
@@ -242,19 +242,19 @@ function build_map() { } function get_space_pc(space) { - return view.spc[space] & 3 + return view.cupc[space] & 3 } function count_british_cu(s) { - return (view.spc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT + return (view.cupc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT } function count_american_cu(s) { - return (view.spc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT + return (view.cupc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT } function count_french_cu(s) { - return (view.spc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT + return (view.cupc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT } function update_units() { @@ -329,7 +329,7 @@ function update_units() { let offset = [] for (let g in GENERALS) { let e = ui.generals[g] - let loc = view.loc[g] + let loc = view.loca[g] let space = SPACES[loc] || BOXES[loc] if (space) { let o = offset[loc] | 0 @@ -359,7 +359,7 @@ function update_units() { } } clear_group("cu") - for (let s = 0; s < view.spc.length; ++s) { + for (let s = 0; s < view.cupc.length; ++s) { add_cu_marker(s, count_british_cu(s), [ "cu", "british" ]) add_cu_marker(s, count_american_cu(s), [ "cu", "american" ]) add_cu_marker(s, count_french_cu(s), [ "cu", "french" ]) @@ -1,13 +1,14 @@ "use strict" +// TODO: capture washington is messy // TODO: campaign messed up who is who after battle // TODO: retreat with 0 CU after battle -// TODO: bit flags for regulars, european_war, french alliance triggered +// TODO: bit flags for regulars, european_war, french alliance triggered, etc /* DATA */ -const data = require("./data") +const data = require("./data.js") const CARDS = data.cards const SPACES = data.spaces @@ -88,8 +89,8 @@ const SOUTH_OF_WINTER_ATTRITION_LINE = [ 11, 12, 13 ] const CAMPAIGN_CARDS = [ 67, 68, 69, 70 ] const DECLARATION_OF_INDEPENDENCE = 99 const BARON_VON_STEUBEN = 86 -const WAR_ENDS_1779 = 71 -const BENJAMIN_FRANKLIN = 101 +// const WAR_ENDS_1779 = 71 +// const BENJAMIN_FRANKLIN = 101 const general_count = data.generals.length const space_count = 66 @@ -97,11 +98,11 @@ const all_spaces = new Array(space_count).fill(0).map((_,i)=>i) const ENEMY = { [P_AMERICA]: P_BRITAIN, [P_BRITAIN]: P_AMERICA } -let states = {} -let events = {} +var states = {} +var events = {} -let game -let view +var game +var view /* SETUP */ @@ -115,9 +116,13 @@ function setup_game(seed) { // tracks year: 1775, + french_alliance_triggered: false, european_war: false, regulars: true, + congress_was_dispersed: false, + pennsylvania_and_new_jersey_line_mutinies: false, + reshuffle: false, // played cards war_ends: 0, @@ -127,8 +132,8 @@ function setup_game(seed) { french_alliance: 0, congress: PHILADELPHIA, french_navy: -1, - loc: new Array(general_count).fill(NOWHERE), - spc: new Array(space_count + 2).fill(PC_NONE), + loca: new Array(general_count).fill(NOWHERE), + cupc: new Array(space_count + 2).fill(PC_NONE), // hands & card deck a_hand: [], @@ -136,13 +141,15 @@ function setup_game(seed) { a_queue: 0, b_queue: 0, - reshuffle: false, + last_played: 0, + did_discard_event: 0, deck: null, removed: [], - // transient state - moved: [], - mcu: [], + mv: [], + mvcu: [], + + count: 0, } set_space_pc(QUEBEC, PC_BRITISH) @@ -273,7 +280,7 @@ function discard_card(c, reason) { } function can_exchange_for_discard(c) { - if (game.did_discard_event > 0) { + if (game.did_discard_event) { if (game.active === P_BRITAIN) return true return CARDS[c].count > 1 @@ -362,7 +369,7 @@ function is_american_militia(space) { } function is_american_winter_offensive() { - if (game.who === WASHINGTON && game.a_hand.length === 0) + if (game.move.who === WASHINGTON && game.a_hand.length === 0) return true return false } @@ -370,12 +377,12 @@ function is_american_winter_offensive() { /* PC */ function set_space_pc(space, pc) { - game.spc[space] &= ~3 - game.spc[space] |= pc + game.cupc[space] &= ~3 + game.cupc[space] |= pc } function get_space_pc(space) { - return game.spc[space] & 3 + return game.cupc[space] & 3 } function has_no_pc(space) { @@ -468,34 +475,34 @@ function get_colony_control(c) { /* CU */ function reset_moved_cu() { - map_clear(game.mcu) + map_clear(game.mvcu) } function count_british_cu(s) { - return (game.spc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT + return (game.cupc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT } function count_american_cu(s) { - return (game.spc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT + return (game.cupc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT } function count_french_cu(s) { - return (game.spc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT + return (game.cupc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT } function set_british_cu(s, n) { - game.spc[s] &= ~CU_BRITISH_MASK - game.spc[s] |= n << CU_BRITISH_SHIFT + game.cupc[s] &= ~CU_BRITISH_MASK + game.cupc[s] |= n << CU_BRITISH_SHIFT } function set_american_cu(s, n) { - game.spc[s] &= ~CU_AMERICAN_MASK - game.spc[s] |= n << CU_AMERICAN_SHIFT + game.cupc[s] &= ~CU_AMERICAN_MASK + game.cupc[s] |= n << CU_AMERICAN_SHIFT } function set_french_cu(s, n) { - game.spc[s] &= ~CU_FRENCH_MASK - game.spc[s] |= n << CU_FRENCH_SHIFT + game.cupc[s] &= ~CU_FRENCH_MASK + game.cupc[s] |= n << CU_FRENCH_SHIFT } function has_british_cu(space) { @@ -526,21 +533,21 @@ function has_enemy_cu(where) { } function count_unmoved_british_cu(where) { - return count_british_cu(where) - map_get(game.mcu, where * 3 + 0, 0) + return count_british_cu(where) - map_get(game.mvcu, where * 3 + 0, 0) } function count_unmoved_american_cu(where) { - return count_american_cu(where) - map_get(game.mcu, where * 3 + 1, 0) + return count_american_cu(where) - map_get(game.mvcu, where * 3 + 1, 0) } function count_unmoved_french_cu(where) { - return count_french_cu(where) - map_get(game.mcu, where * 3 + 2, 0) + return count_french_cu(where) - map_get(game.mvcu, where * 3 + 2, 0) } function mark_moved_cu_imp(offset, where, moved) { if (moved > 0) { - let old = map_get(game.mcu, where * 3 + offset, 0) - map_set(game.mcu, where * 3 + offset, old + moved) + let old = map_get(game.mvcu, where * 3 + offset, 0) + map_set(game.mvcu, where * 3 + offset, old + moved) } } @@ -609,11 +616,11 @@ function move_french_cu(from, to, count) { /* GENERALS */ function location_of_general(g) { - return game.loc[g] + return game.loca[g] } function set_general_location(g, s) { - game.loc[g] = s + game.loca[g] = s } function is_general_at_location(g, s) { @@ -625,15 +632,15 @@ function is_general_on_map(g) { } function has_general_moved(g) { - return set_has(game.moved, g) + return set_has(game.mv, g) } function set_general_moved(g) { - set_add(game.moved, g) + set_add(game.mv, g) } function reset_moved_generals() { - set_clear(game.moved) + set_clear(game.mv) } function find_british_general(where) { @@ -853,46 +860,46 @@ function place_french_reinforcements(who, where) { move_french_cu(AMERICAN_REINFORCEMENTS, where, count_french_cu(AMERICAN_REINFORCEMENTS)) } -function pickup_max_british_cu(where) { - game.carry_british = count_unmoved_british_cu(where) - if (game.carry_british > 5) - game.carry_british = 5 - game.carry_american = 0 - game.carry_french = 0 +function pickup_max_british_cu(move, where) { + move.carry_british = count_unmoved_british_cu(where) + if (move.carry_british > 5) + move.carry_british = 5 + move.carry_american = 0 + move.carry_french = 0 } -function pickup_max_american_cu(where) { - game.carry_british = 0 - game.carry_french = count_unmoved_french_cu(where) - game.carry_american = count_unmoved_american_cu(where) - if (game.carry_french > 5) - game.carry_french = 5 - if (game.carry_american + game.carry_french > 5) - game.carry_american = 5 - game.carry_french +function pickup_max_american_cu(move, where) { + move.carry_british = 0 + move.carry_french = count_unmoved_french_cu(where) + move.carry_american = count_unmoved_american_cu(where) + 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.count -= movement_cost(from, to) - if (game.mobility && has_enemy_cu(to)) { - game.mobility = false - game.count -= 1 - } - if (game.carry_british > 0) - move_british_cu(from, to, game.carry_british) - if (game.carry_american > 0) - move_american_cu(from, to, game.carry_american) - if (game.carry_french > 0) - move_french_cu(from, to, game.carry_french) + 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.carry_british > 0) - move_british_cu(from, to, game.carry_british) - if (game.carry_american > 0) - move_american_cu(from, to, game.carry_american) - if (game.carry_french > 0) - move_french_cu(from, to, game.carry_french) + 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) } @@ -935,7 +942,7 @@ function surrender_british_army(where) { let g = find_british_general(where) if (g) capture_british_general(where) - game.british_losses += count_british_cu(where) + game.combat.british_losses += count_british_cu(where) remove_british_cu(where, count_british_cu(where)) } @@ -1031,27 +1038,28 @@ function goto_committees_of_correspondence() { logbr() game.active = P_AMERICA game.state = "committees_of_correspondence" - game.coc = THE_13_COLONIES.slice() + game.colonies = THE_13_COLONIES.slice() } states.committees_of_correspondence = { inactive: "Committees of Correspondence", prompt() { view.prompt = - "Committees of Correspondence: Place 1 PC marker in each of the 13 colonies. " + game.coc.length + " left." - if (game.coc.length > 0) - gen_place_american_pc_in(game.coc) + "Committees of Correspondence: Place 1 PC marker in each of the 13 colonies. " + game.colonies.length + " left." + if (game.colonies.length > 0) + gen_place_american_pc_in(game.colonies) else gen_pass() }, place_american_pc(space) { push_undo() let colony = SPACES[space].colony - set_delete(game.coc, colony) + set_delete(game.colonies, colony) place_american_pc(space) }, pass() { clear_undo() + delete game.colonies goto_for_the_king() }, } @@ -1060,7 +1068,6 @@ function goto_for_the_king() { logbr() log(".h2.british For the King") logbr() - delete game.coc game.active = P_BRITAIN game.state = "for_the_king" game.count = 3 @@ -1196,7 +1203,7 @@ states.british_declare_first = { } }, card_campaign(c) { - delete game.congress_was_dispersed + game.congress_was_dispersed = false logp("went first by playing a campaign card") game.active = P_BRITAIN goto_campaign(c) @@ -1207,7 +1214,7 @@ states.british_declare_first = { else game.active = P_AMERICA game.state = "choose_first_player" - delete game.congress_was_dispersed + game.congress_was_dispersed = false }, } @@ -1259,7 +1266,7 @@ states.strategy_phase = { }, card_discard_event(c) { push_undo() - game.did_discard_event = 0 + game.did_discard_event = c clear_queue() discard_card(c, "PC action") game.state = "discard_event_pc_action" @@ -1294,7 +1301,7 @@ states.strategy_phase = { exchange_for_discard(c) { let d = game.did_discard_event = 0 game.did_discard_event = 0 - discard_card(c) + discard_card(c, "exchange") set_add(active_hand(), d) logp("picked up up #" + d) }, @@ -1321,7 +1328,6 @@ function end_strategy_card() { log("The French signed an alliance with the Americans!") game.french_alliance_triggered = true if (game.french_navy === -1) { - game.save_active = game.active game.active = P_AMERICA game.state = "place_french_navy_trigger" return @@ -1572,7 +1578,7 @@ states.ops_british_reinforcements_who = { pass() { push_undo() game.state = "ops_british_reinforcements_where" - game.who = NOBODY + delete game.who }, } @@ -1591,7 +1597,7 @@ states.ops_british_reinforcements_where = { place_reinforcements(space) { place_british_reinforcements(game.who, game.count, space) end_strategy_card() - game.who = NOBODY + delete game.who }, } @@ -1609,7 +1615,7 @@ states.ops_american_reinforcements_who = { pass() { push_undo() game.state = "ops_american_reinforcements_where" - game.who = NOBODY + delete game.who }, } @@ -1624,7 +1630,7 @@ states.ops_american_reinforcements_where = { else place_american_reinforcements(game.who, game.count, space) end_strategy_card() - game.who = NOBODY + delete game.who }, } @@ -1676,7 +1682,7 @@ function gen_american_reinforcements_where(general) { /* PLAY OPS CARD TO MOVE A GENERAL */ function goto_ops_general(c) { - play_card(c, " to activate a general") + play_card(c, "to activate a general") if (game.active === P_BRITAIN) { game.count = CARDS[c].count + game.b_queue game.b_queue = 0 @@ -1829,52 +1835,65 @@ function gen_remove_american_general() { function goto_ops_general_move(g, marblehead) { game.state = "ops_general_move" - game.who = g + + let where = location_of_general(g) + + game.move = { + who: g, + from: where, + to: where, + count: 0, + mobility: false, + carry_british: 0, + carry_american: 0, + carry_french: 0 + } + if (marblehead) { - game.mobility = false - game.count = 6 + game.move.mobility = false + game.move.count = 6 } else { if (game.active === P_BRITAIN) { - game.mobility = false - game.count = 4 + game.move.mobility = false + game.move.count = 4 } else { - game.mobility = true - game.count = 5 + game.move.mobility = true + game.move.count = 5 } } - let where = location_of_general(g) + if (game.active === P_BRITAIN) - pickup_max_british_cu(where) + pickup_max_british_cu(game.move, where) else - pickup_max_american_cu(where) + pickup_max_american_cu(game.move, where) } states.ops_general_move = { prompt() { - view.prompt = "Move " + game.who + " with " - if (game.carry_british > 0) { - view.prompt += game.carry_british + " British CU." - } else if (game.carry_french + game.carry_american > 0) { - if (game.carry_french > 0) { - if (game.carry_american > 0) { - view.prompt += game.carry_french + " French CU and " - view.prompt += game.carry_american + " American CU." + view.prompt = "Move " + game.move.who + " with " + if (game.move.carry_british > 0) { + view.prompt += game.move.carry_british + " 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.carry_french + " French CU." + view.prompt += game.move.carry_french + " French CU." } } else { - view.prompt += game.carry_american + " American CU." + view.prompt += game.move.carry_american + " American CU." } } else { - view.prompt += game.carry_american + " no CU." + view.prompt += game.move.carry_american + " no CU." } if (game.count === 1) - view.prompt += " " + game.count + " move left." + view.prompt += " " + game.move.count + " move left." else if (game.count > 1) - view.prompt += " " + game.count + " moves left." + view.prompt += " " + game.move.count + " moves left." // Cannot stop on enemy general - if (!has_enemy_general(location_of_general(game.who))) + if (!has_enemy_general(location_of_general(game.move.who))) gen_pass() gen_carry_cu() @@ -1882,40 +1901,40 @@ states.ops_general_move = { }, pickup_british_cu() { - ++game.carry_british + ++game.move.carry_british }, pickup_american_cu() { - ++game.carry_american + ++game.move.carry_american }, pickup_french_cu() { - ++game.carry_french + ++game.move.carry_french }, drop_british_cu() { push_undo() - --game.carry_british - if (has_general_moved(game.who)) - mark_moved_british_cu(location_of_general(game.who), 1) + --game.move.carry_british + if (has_general_moved(game.move.who)) + mark_moved_british_cu(location_of_general(game.move.who), 1) }, drop_american_cu() { push_undo() - --game.carry_american - if (has_general_moved(game.who)) - mark_moved_american_cu(location_of_general(game.who), 1) + --game.move.carry_american + if (has_general_moved(game.move.who)) + mark_moved_american_cu(location_of_general(game.move.who), 1) }, drop_french_cu() { push_undo() - --game.carry_french - if (has_general_moved(game.who)) - mark_moved_french_cu(location_of_general(game.who), 1) + --game.move.carry_french + if (has_general_moved(game.move.who)) + mark_moved_french_cu(location_of_general(game.move.who), 1) }, move(to) { push_undo() - set_general_moved(game.who) - let from = location_of_general(game.who) - let cu = game.carry_british + game.carry_american + game.carry_french + set_general_moved(game.move.who) + let from = location_of_general(game.move.who) + let cu = game.move.carry_british + game.move.carry_american + game.move.carry_french let intercept = false if (game.active === P_BRITAIN) { @@ -1924,7 +1943,10 @@ states.ops_general_move = { intercept = can_intercept_to(to) } - move_army(game.who, from, to) + game.move.from = from + game.move.to = to + + move_army(game.move.who, from, to) if (cu > 0) { if (has_enemy_general(to) && !has_enemy_cu(to)) { @@ -1939,13 +1961,13 @@ states.ops_general_move = { } if (intercept) - goto_intercept(from, to) + goto_intercept() else - resume_moving(from, to) + resume_moving() }, pass() { clear_undo() - let where = location_of_general(game.who) + let where = location_of_general(game.move.who) end_move() if (count_friendly_generals(where) > 1) goto_remove_general(where) @@ -1954,10 +1976,10 @@ states.ops_general_move = { }, } -function resume_moving(from, to) { - if (has_enemy_cu(to)) { +function resume_moving() { + if (has_enemy_cu(game.move.to)) { end_move() - goto_start_battle(from, to) + goto_start_battle() } } @@ -1973,7 +1995,7 @@ function can_intercept_to(to) { } function gen_intercept() { - for (let space of SPACES[game.where].adjacent) { + for (let space of SPACES[game.move.to].adjacent) { if (has_american_army(space)) { let g = find_american_or_french_general(space) if (g && !has_general_moved(g)) @@ -1982,19 +2004,15 @@ function gen_intercept() { } } -function goto_intercept(from, where) { +function goto_intercept() { clear_undo() - game.save_who = game.who - game.who = NOBODY - game.from = from - game.where = where game.active = P_AMERICA game.state = "intercept" } states.intercept = { prompt() { - view.prompt = "Intercept " + game.save_who + " in " + game.where + "?" + view.prompt = "Intercept " + game.move.who + " in " + game.move.to + "?" gen_pass() gen_intercept() }, @@ -2003,26 +2021,28 @@ states.intercept = { let die = roll_d6() if (die <= GENERALS[g].agility) { log(g + " intercepted (" + die + " <= " + GENERALS[g].agility + ")") - game.did_intercept = 1 - - let save_carry_british = game.carry_british - let save_carry_american = game.carry_american - let save_carry_french = game.carry_french - - pickup_max_american_cu(location_of_general(g)) - intercept_army(g, location_of_general(g), game.where) + 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, + } - game.carry_british = save_carry_british - game.carry_american = save_carry_american - game.carry_french = save_carry_french + pickup_max_american_cu(game.intercept, location_of_general(g)) + intercept_army(g, location_of_general(g), game.move.to) - if (count_friendly_generals(game.where) > 1) + if (count_friendly_generals(game.move.to) > 1) goto_remove_general_after_intercept() else end_intercept() } else { log(g + " failed to intercept (" + die + " > " + GENERALS[g].agility + ")") - if (!can_intercept_to(game.where)) + delete game.intercept + if (!can_intercept_to(game.move.to)) end_intercept() } }, @@ -2034,24 +2054,18 @@ states.intercept = { function end_intercept() { game.active = P_BRITAIN game.state = "ops_general_move" - game.who = game.save_who - delete game.save_who - resume_moving(game.from, game.where) - delete game.from + delete game.intercept + resume_moving() } function end_move() { - let where = location_of_general(game.who) - if (has_general_moved(game.who)) { - mark_moved_british_cu(where, game.carry_british) - mark_moved_american_cu(where, game.carry_american) - mark_moved_french_cu(where, game.carry_french) + let where = location_of_general(game.move.who) + if (has_general_moved(game.move.who)) { + mark_moved_british_cu(where, game.move.carry_british) + mark_moved_american_cu(where, game.move.carry_american) + mark_moved_french_cu(where, game.move.carry_french) } - game.who = NOBODY - delete game.mobility - delete game.carry_british - delete game.carry_american - delete game.carry_french + delete game.move } function path_type(from, to) { @@ -2063,21 +2077,21 @@ function path_type(from, to) { } function gen_carry_cu() { - let where = location_of_general(game.who) + let where = location_of_general(game.move.who) if (game.active === P_BRITAIN) { - if (game.carry_british > 0) + if (game.move.carry_british > 0) gen_action("drop_british_cu") - if (game.carry_british < 5 && game.carry_british < count_unmoved_british_cu(where)) + if (game.move.carry_british < 5 && game.move.carry_british < count_unmoved_british_cu(where)) gen_action("pickup_british_cu") } else { - let carry_total = game.carry_french + game.carry_american - if (game.carry_french > 0) + let carry_total = game.move.carry_french + game.move.carry_american + if (game.move.carry_french > 0) gen_action("drop_french_cu") - if (game.carry_american > 0) + if (game.move.carry_american > 0) gen_action("drop_american_cu") - if (carry_total < 5 && game.carry_french < count_unmoved_french_cu(where)) + if (carry_total < 5 && game.move.carry_french < count_unmoved_french_cu(where)) gen_action("pickup_french_cu") - if (carry_total < 5 && game.carry_american < count_unmoved_american_cu(where)) + if (carry_total < 5 && game.move.carry_american < count_unmoved_american_cu(where)) gen_action("pickup_american_cu") } } @@ -2096,13 +2110,13 @@ function movement_cost(from, to) { } function gen_move_general() { - let from = location_of_general(game.who) - let alone = game.carry_british + game.carry_american + game.carry_french === 0 + 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.who !== ARNOLD) + if (game.move.who !== ARNOLD) continue mp = 3 } @@ -2117,11 +2131,11 @@ function gen_move_general() { continue } - if (game.mobility && has_enemy_cu(to)) { - if (game.count - mp >= 1) + if (game.move.mobility && has_enemy_cu(to)) { + if (game.move.count - mp >= 1) gen_action("move", to) } else { - if (game.count - mp >= 0) + if (game.move.count - mp >= 0) gen_action("move", to) } } @@ -2409,7 +2423,7 @@ events.lord_sandwich_coastal_raids = function (c, card) { play_card(c) game.state = "lord_sandwich_coastal_raids" game.count = 2 - game.where = NOWHERE + delete game.where } states.lord_sandwich_coastal_raids = { @@ -2507,7 +2521,7 @@ events.pennsylvania_and_new_jersey_line_mutinies = function (c, card) { game.pennsylvania_and_new_jersey_line_mutinies = true game.state = "pennsylvania_and_new_jersey_line_mutinies" game.count = 2 - game.where = NOWHERE + delete game.where } states.pennsylvania_and_new_jersey_line_mutinies = { @@ -2556,24 +2570,24 @@ states.john_glovers_marblehead_regiment_who = { events.declaration_of_independence = function (c, card) { play_card(c) - game.last_active = game.active + game.save = game.active game.active = P_AMERICA - game.doi = THE_13_COLONIES.slice() + game.colonies = THE_13_COLONIES.slice() game.state = "declaration_of_independence" } states.declaration_of_independence = { prompt() { view.prompt = "Declaration of Independence: Place 1 PC marker in each of the 13 colonies. " - view.prompt += game.doi.length + " left." + view.prompt += game.colonies.length + " left." gen_pass() - gen_place_american_pc_in(game.doi) + gen_place_american_pc_in(game.colonies) }, place_american_pc(space) { let colony = SPACES[space].colony - set_delete(game.doi, colony) + set_delete(game.colonies, colony) place_american_pc(space) - if (game.doi.length === 0) + if (game.colonies.length === 0) end_declaration_of_independence() }, pass() { @@ -2582,17 +2596,21 @@ states.declaration_of_independence = { } function end_declaration_of_independence() { - game.active = game.last_active - delete game.last_active - delete game.doi + game.active = game.save + delete game.save + delete game.colonies end_strategy_card() } function goto_george_washington_captured() { + // TODO: this is not robust enough! figure out exactly where we are called. + /* Save all the state we clobber during the interrupt. */ - game.last_state = game.state - game.last_active = game.active - game.last_count = game.count + game.save = { + state: game.state, + active: game.active, + count: game.count + } game.state = "george_washington_captured" game.active = P_BRITAIN @@ -2618,12 +2636,10 @@ states.george_washington_captured = { function end_george_washington_captured() { /* Restore previous state. */ - game.state = game.last_state - game.count = game.last_count - game.active = game.last_active - delete game.last_state - delete game.last_active - delete game.last_count + game.state = game.save.state + game.count = game.save.count + game.active = game.save.active + delete game.save } function do_event(c) { @@ -2636,25 +2652,29 @@ function do_event(c) { /* BATTLE */ -function can_retreat_before_battle(where) { - if (game.did_intercept) +function can_retreat_before_battle() { + if (game.move.did_intercept) return false // can't retreat if attempted (successful or not) interception! - let g = find_american_or_french_general(where) + let g = find_american_or_french_general(game.move.to) if (g && !has_general_moved(g)) return true return false } -function goto_start_battle(from, where) { +function goto_start_battle() { clear_undo() - game.attacker = game.active - game.attack_from = from - game.british_losses = 0 - game.where = where - game.a_bonus = 0 - game.b_bonus = 0 - if (game.active === P_BRITAIN && can_retreat_before_battle(where)) + + game.combat = { + attacker: game.active, + a_bonus: 0, + b_bonus: 0, + b_draw_after_battle: false, + a_draw_after_battle: false, + british_losses: 0, + } + + if (game.active === P_BRITAIN && can_retreat_before_battle()) goto_retreat_before_battle() else goto_play_attacker_battle_card() @@ -2662,7 +2682,7 @@ function goto_start_battle(from, where) { function goto_retreat_before_battle() { game.active = P_AMERICA - game.who = find_american_or_french_general(game.where) + game.who = find_american_or_french_general(game.move.to) game.state = "retreat_before_battle" } @@ -2679,8 +2699,8 @@ states.retreat_before_battle = { let roll = roll_d6() if (roll <= agility) { logp("successfully retreated before battle: " + roll + " <= " + agility) - pickup_max_american_cu(game.where) - move_army(game.who, game.where, to) + pickup_max_american_cu(game.move.to) + move_army(game.who, game.move.to, to) goto_remove_general_after_retreat_before_battle(to) } else { logp("failed to retreat before battle: " + roll + " > " + agility) @@ -2692,11 +2712,10 @@ states.retreat_before_battle = { }, } -function goto_remove_general_after_retreat_before_battle(to) { - if (count_friendly_generals(to) > 1) { +function goto_remove_general_after_retreat_before_battle(where) { + if (count_friendly_generals(where) > 1) { game.state = "remove_general_after_retreat_before_battle" - game.save_where = game.where - game.where = to + game.where = where } else { end_remove_general_after_retreat_before_battle() } @@ -2712,19 +2731,17 @@ states.remove_general_after_retreat_before_battle = { move_general(g, BRITISH_REINFORCEMENTS) else move_general(g, AMERICAN_REINFORCEMENTS) - game.where = game.save_where - delete game.save_where end_remove_general_after_retreat_before_battle() }, } function end_remove_general_after_retreat_before_battle() { - let b_cu = count_british_cu(game.where) - let a_cu = count_american_and_french_cu(game.where) + 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() } else if (b_cu >= 4 && a_cu === 1) { - overrun(game.where) + overrun(game.move.to) end_battle() } else { end_retreat_before_battle() @@ -2732,7 +2749,7 @@ function end_remove_general_after_retreat_before_battle() { } function can_defender_retreat(to) { - if (to === game.attack_from) + if (to === game.move.from) return false if (has_enemy_pc(to)) return false @@ -2742,7 +2759,7 @@ function can_defender_retreat(to) { } function can_attacker_retreat() { - let to = game.attack_from + let to = game.move.from if (has_enemy_pc(to)) return false if (has_enemy_cu(to)) @@ -2751,7 +2768,7 @@ function can_attacker_retreat() { } function gen_defender_retreat() { - let from = game.where + let from = game.move.to for (let to of SPACES[from].adjacent) { if (can_defender_retreat(to)) gen_action("move", to) @@ -2780,16 +2797,16 @@ function gen_defender_retreat() { function gen_attacker_retreat() { if (can_attacker_retreat()) - gen_action("move", game.attack_from) + gen_action("move", game.move.from) } function end_retreat_before_battle() { - game.who = NOBODY + delete game.who goto_play_attacker_battle_card() } function goto_play_attacker_battle_card() { - game.active = game.attacker + game.active = game.combat.attacker game.state = "play_attacker_battle_card" } @@ -2804,22 +2821,22 @@ states.play_attacker_battle_card = { if (game.active === P_BRITAIN) { if (CARDS[c].event === "remove_benedict_arnold") remove_benedict_arnold() - game.b_draw_after_battle = true - game.b_bonus += 2 + game.combat.b_draw_after_battle = true + game.combat.b_bonus += 2 } else { - game.a_draw_after_battle = true - game.a_bonus += 2 + game.combat.a_draw_after_battle = true + game.combat.a_bonus += 2 } goto_play_defender_battle_card() }, card_battle_discard(c) { discard_card(c, "for +1 DRM") if (game.active === P_BRITAIN) { - game.b_draw_after_battle = true - game.b_bonus += 1 + game.combat.b_draw_after_battle = true + game.combat.b_bonus += 1 } else { - game.a_draw_after_battle = true - game.a_bonus += 1 + game.combat.a_draw_after_battle = true + game.combat.a_bonus += 1 } goto_play_defender_battle_card() }, @@ -2830,7 +2847,7 @@ states.play_attacker_battle_card = { function goto_play_defender_battle_card() { game.state = "play_defender_battle_card" - game.active = ENEMY[game.attacker] + game.active = ENEMY[game.combat.attacker] } states.play_defender_battle_card = { @@ -2844,22 +2861,22 @@ states.play_defender_battle_card = { if (game.active === P_BRITAIN) { if (CARDS[c].event === "remove_benedict_arnold") remove_benedict_arnold() - game.b_draw_after_battle = true - game.b_bonus += 2 + game.combat.b_draw_after_battle = true + game.combat.b_bonus += 2 } else { - game.a_draw_after_battle = true - game.a_bonus += 2 + game.combat.a_draw_after_battle = true + game.combat.a_bonus += 2 } resolve_battle() }, card_battle_discard(c) { discard_card(c, "for +1 DRM") if (game.active === P_BRITAIN) { - game.b_draw_after_battle = true - game.b_bonus += 1 + game.combat.b_draw_after_battle = true + game.combat.b_bonus += 1 } else { - game.a_draw_after_battle = true - game.a_bonus += 1 + game.combat.a_draw_after_battle = true + game.combat.a_bonus += 1 } resolve_battle() }, @@ -2944,20 +2961,20 @@ function roll_winner_combat_losses(log, losing_general) { } function apply_british_combat_losses(max) { - let n = Math.min(count_british_cu(game.where), max) - remove_british_cu(game.where, n) + let n = Math.min(count_british_cu(game.move.to), max) + remove_british_cu(game.move.to, n) return n } function apply_american_combat_losses(max) { - let n = Math.min(count_american_cu(game.where), max) - remove_american_cu(game.where, n) + let n = Math.min(count_american_cu(game.move.to), max) + remove_american_cu(game.move.to, n) return n } function apply_french_combat_losses(max) { - let n = Math.min(count_french_cu(game.where), max) - remove_french_cu(game.where, n) + let n = Math.min(count_french_cu(game.move.to), max) + remove_french_cu(game.move.to, n) return n } @@ -2973,10 +2990,10 @@ function resolve_battle() { let b_log = [] game.active = ENEMY[game.active] - let b_g = find_british_general(game.where) - let b_cu = count_british_cu(game.where) - let a_g = find_american_or_french_general(game.where) - let a_cu = count_american_and_french_cu(game.where) + let b_g = find_british_general(game.move.to) + let b_cu = count_british_cu(game.move.to) + let a_g = find_american_or_french_general(game.move.to) + let a_cu = count_american_and_french_cu(game.move.to) let b_br = 0 let a_br = 0 @@ -3008,14 +3025,14 @@ function resolve_battle() { b_log.push("+" + b_br + " Actual Battle Rating") a_log.push("+" + a_br + " Actual Battle Rating") - let b_drm = b_cu + b_br + game.b_bonus + let b_drm = b_cu + b_br + game.combat.b_bonus if (game.regulars) { b_log.push("+1 British Regulars' Advantage") b_drm += 1 } - if (is_non_blockaded_port(game.where)) { - if (is_fortified_port(game.where)) { - if (has_british_pc(game.where)) { + if (is_non_blockaded_port(game.move.to)) { + if (is_fortified_port(game.move.to)) { + if (has_british_pc(game.move.to)) { b_log.push("+1 Royal Navy Support") b_drm += 1 } @@ -3024,17 +3041,17 @@ function resolve_battle() { b_drm += 1 } } - if (is_british_militia(game.where)) { + if (is_british_militia(game.move.to)) { b_log.push("+1 Militia") b_drm += 1 } - if (game.b_bonus === 2) + if (game.combat.b_bonus === 2) b_log.push("+2 Battle Card") - else if (game.b_bonus === 1) + else if (game.combat.b_bonus === 1) b_log.push("+1 Discard of an Event Card") - let a_drm = a_cu + a_br + game.a_bonus - if (is_american_militia(game.where)) { + let a_drm = a_cu + a_br + game.combat.a_bonus + if (is_american_militia(game.move.to)) { a_log.push("+1 Militia") a_drm += 1 } @@ -3042,11 +3059,11 @@ function resolve_battle() { a_log.push("+2 American Winter Offensive") a_drm += 2 } - if (game.a_bonus === 2) + if (game.combat.a_bonus === 2) a_log.push("+2 Battle Card") - else if (game.a_bonus === 1) + else if (game.combat.a_bonus === 1) a_log.push("+1 Discard of an Event Card") - if (game.did_intercept) { + if (game.move.did_intercept) { a_log.push("+1 Interception") a_drm += 1 } @@ -3074,24 +3091,24 @@ function resolve_battle() { a_lost_cu = roll_winner_combat_losses(a_log, b_g) } - game.british_losses = apply_british_combat_losses(b_lost_cu) + game.combat.british_losses = apply_british_combat_losses(b_lost_cu) let american_losses = apply_american_and_french_combat_losses(a_lost_cu) - b_log.push("Losses: " + game.british_losses + " CU") + b_log.push("Losses: " + game.combat.british_losses + " CU") a_log.push("Losses: " + american_losses + " CU") // Special case: winning general with no CU on enemy PC is captured if (victor === P_BRITAIN) { - if (b_g && count_british_cu(game.where) === 0 && has_american_pc(game.where)) - capture_british_general(game.where) + if (b_g && count_british_cu(game.move.to) === 0 && has_american_pc(game.move.to)) + capture_british_general(game.move.to) } else { - if (a_g && count_american_and_french_cu(game.where) === 0 && has_british_pc(game.where)) - capture_american_or_french_general(game.where) + if (a_g && count_american_and_french_cu(game.move.to) === 0 && has_british_pc(game.move.to)) + capture_american_or_french_general(game.move.to) } log("BRITISH BATTLE REPORT:\n" + b_log.join("\n")) log("AMERICAN BATTLE REPORT:\n" + a_log.join("\n")) - log(victor + " victory in " + game.where + "!") + log(victor + " victory in " + game.move.to + "!") if (victor === P_AMERICA) advance_french_alliance(1) @@ -3101,12 +3118,12 @@ function resolve_battle() { function goto_retreat_after_battle(victor) { if (victor === P_BRITAIN) { - game.who = find_american_or_french_general(game.where) - if (game.who === NOBODY && count_american_and_french_cu(game.where) === 0) + game.who = find_american_or_french_general(game.move.to) + if (game.who === NOBODY && count_american_and_french_cu(game.move.to) === 0) return end_battle() } else { - game.who = find_british_general(game.where) - if (game.who === NOBODY && count_british_cu(game.where) === 0) + game.who = find_british_general(game.move.to) + if (game.who === NOBODY && count_british_cu(game.move.to) === 0) return end_battle() } game.active = ENEMY[victor] @@ -3117,7 +3134,7 @@ states.retreat_after_battle = { prompt() { view.prompt = "Retreat after battle." gen_action("surrender") - if (game.active === game.attacker) + if (game.active === game.combat.attacker) gen_attacker_retreat() else gen_defender_retreat() @@ -3125,9 +3142,9 @@ states.retreat_after_battle = { move(to) { logp("retreated to " + to) if (game.active === P_BRITAIN) - retreat_british_army(game.where, to) + retreat_british_army(game.move.to, to) else - retreat_american_army(game.where, to) + retreat_american_army(game.move.to, to) if (count_friendly_generals(to) > 1) goto_remove_general_after_retreat(to) else @@ -3136,42 +3153,34 @@ states.retreat_after_battle = { surrender() { // End battle here, so if Washington is captured we can handle the interrupt state. let active = game.active - let where = game.where + // TODO: ugly clean this up end_battle() logp("surrendered") if (active === P_BRITAIN) - surrender_british_army(where) + surrender_british_army(game.move.to) else - surrender_american_army(where) + surrender_american_army(game.move.to) }, } function end_battle() { - game.active = game.attacker + game.active = game.combat.attacker - if (game.active === P_BRITAIN && game.congress === game.where) + if (game.active === P_BRITAIN && game.congress === game.move.to) disperse_continental_congress() - if (game.british_losses >= 3) + if (game.combat.british_losses >= 3) lose_regular_advantage() // TODO: delay until end of campaign - if (game.b_draw_after_battle) + if (game.combat.b_draw_after_battle) set_add(game.b_hand, deal_card()) - if (game.a_draw_after_battle) + if (game.combat.a_draw_after_battle) set_add(game.a_hand, deal_card()) - delete game.did_intercept - delete game.b_bonus - delete game.a_bonus - delete game.b_draw_after_battle - delete game.a_draw_after_battle - delete game.attack_from - delete game.british_losses - delete game.attacker - game.where = NOWHERE - game.who = NOBODY + delete game.combat + delete game.move end_strategy_card() } @@ -3250,6 +3259,7 @@ function goto_winter_attrition_phase() { function goto_french_naval_phase() { if (game.french_navy !== -1) { + game.save = game.active game.active = P_AMERICA game.state = "place_french_navy" } else { @@ -3275,8 +3285,8 @@ states.place_french_navy_trigger = { place_navy(zone) { logp("placed French Navy.") game.french_navy = zone - game.active = game.save_active - delete game.save_active + game.active = game.save + delete game.save end_strategy_card() }, } @@ -3516,6 +3526,10 @@ function gen_action(action, argument) { } } +function gen_pass() { + gen_action("pass") +} + exports.scenarios = [ "Standard" ] exports.roles = [ P_BRITAIN, P_AMERICA ] @@ -3559,8 +3573,8 @@ exports.view = function (state, current) { french_navy: state.french_navy, regulars: state.regulars, - loc: state.loc, - spc: state.spc, + loca: state.loca, + cupc: state.cupc, a_cards: state.a_hand.length, b_cards: state.b_hand.length, |