diff options
-rw-r--r-- | rules.js | 819 |
1 files changed, 455 insertions, 364 deletions
@@ -89,6 +89,8 @@ function is_gray_indian(p) { return (p >= 16 && p <= 20) || (p >= 108 && p <= 11 function is_cherokee(p) { return (p >= 14 && p <= 15) } function is_mohawk(p) { return (p >= 21 && p <= 22) } function is_british_iroquois_or_mohawk(p) { return (p >= 16 && p <= 22) } +function is_british_leader(p) { return p >= 1 && p <= 13 } +function is_french_leader(p) { return p >= 87 && p <= 96 } const AMHERST = 1 const BRADDOCK = 2 const ABERCROMBY = 3 @@ -200,10 +202,6 @@ for (let s = first_space; s <= last_space; ++s) { spaces.forEach(ss => ss.nb_name = ss.name.replace(/ /g, '\xa0')) // GLOBALS -pieces.forEach(pp => { - if (pp.desc) pp.nb_desc = pp.desc.replace(/ /g, '\xa0') - if (pp.rdesc) pp.nb_rdesc = pp.rdesc.replace(/ /g, '\xa0') -}) let game let view = null @@ -239,20 +237,15 @@ function random(n) { return (game.seed = game.seed * 200105 % 34359738337) % n } -function roll_die(reason) { - let die = random(6) + 1 - if (reason) - log(`Rolled ${die} ${reason}.`) - else - log(`Rolled ${die}.`) - return die +function roll_die() { + return random(6) + 1 } function modify(die, drm, why) { if (drm >= 0) - log(`+${drm} ${why}.`) + log(`>+${drm} ${why}`) else if (drm < 0) - log(`${drm} ${why}.`) + log(`>\u2212${-drm} ${why}`) return die + drm } @@ -275,84 +268,96 @@ function log(msg) { game.log.push(msg) } -function push_summary(summary, p) { - let s = piece_space(p) - if (!(s in summary)) - summary[s] = [] - summary[s].push(piece_name(p)) +function map_get_set(map, key) { + let set = map_get(map, key) + if (!set) + map_set(map, key, set = []) + return set } -function print_plain_summary(verb, list) { - if (game.summary) { - if (game.summary[list].length > 0) - log(verb + "\n" + game.summary[list].sort((a,b)=>a-b).map(piece_name).join(",\n") + ".") - delete game.summary[list] - } +function push_summary(summary, p) { + set_add(map_get_set(summary, piece_space(p)), p) } -function print_summary(summary, verb) { - for (let s in summary) - log(verb + "%" + s + "\n" + summary[s].join(",\n") + ".") +function push_retreat_summary(p, s) { + set_add(map_get_set(game.summary.retreat, s), p) } -function flush_summary() { - if (game.summary) { - print_summary(game.summary.placed, "Placed at ") - print_summary(game.summary.restored, "Restored at ") - print_summary(game.summary.reduced, "Reduced at ") - print_summary(game.summary.eliminated, "Eliminated at ") - game.summary.placed = {} - game.summary.restored = {} - game.summary.reduced = {} - game.summary.eliminated = {} +function make_summary(list) { + let result = [] + let current = 0 + let n = 0 + for (let p of list) { + if (is_leader(p)) { + // leaders are always first + result.push("P" + p) + continue + } + let name = piece_log_name(p) + if (name !== current) { + if (n > 0) + result.push(n + " " + current) + current = name + n = 1 + } else { + ++n + } } + if (n > 0) + result.push(n + " " + current) + return result.join("\n") } -function init_retreat_summary() { - if (game.summary) - game.summary.retreat = {} +function print_plain_summary(verb, list) { + if (game.summary[list].length > 0) + log(verb + "\n" + make_summary(game.summary[list])) + delete game.summary[list] } -function push_retreat_summary(p, s) { - if (game.summary) { - if (!(s in game.summary.retreat)) - game.summary.retreat[s] = [] - game.summary.retreat[s].push(p) - } else { - // log(piece_name(p) + " retreated " + s + ".") - log(piece_name(p) + " " + s + ".") +function print_summary(summary, verb) { + if (summary.length > 0) { + log(verb) + map_for_each(summary, (s, list) => { + log(">%" + s + "\n" + make_summary(list)) + }) } } -function flush_retreat_summary() { - if (game.summary) { - for (let s in game.summary.retreat) - log("Retreated " + s + "\n" + game.summary.retreat[s].map(piece_name).join(",\n") + ".") - delete game.summary.retreat - } +function print_summary_no_space(summary, verb) { + if (summary.length === 2) + log(verb + "\n" + make_summary(summary[1])) + else + print_summary(summary, verb) } -function init_go_home_summary() { - if (game.summary) - game.summary.go_home = {} +function flush_summary() { + print_summary(game.summary.placed, "Placed") + print_summary(game.summary.restored, "Restored") + print_summary(game.summary.reduced, "Reduced") + print_summary(game.summary.eliminated, "Eliminated") + game.summary.placed = [] + game.summary.restored = [] + game.summary.reduced = [] + game.summary.eliminated = [] } -function push_go_home_summary(p, s) { - if (game.summary) { - if (!(s in game.summary.go_home)) - game.summary.go_home[s] = [] - game.summary.go_home[s].push(log_piece_name_and_place(p)) - } else { - // log(log_piece_name_and_place(p) + " went home to %" + s + ".") - log(log_piece_name_and_place(p) + " home to %" + s + ".") - } +function flush_losses_summary() { + print_summary_no_space(game.summary.reduced, "Reduced") + print_summary_no_space(game.summary.eliminated, "Eliminated") + game.summary.reduced = [] + game.summary.eliminated = [] +} + +function flush_retreat_summary() { + map_for_each(game.summary.retreat, (s, list) => { + log(s + "\n" + make_summary(list)) + }) + delete game.summary.retreat } function flush_go_home_summary() { - if (game.summary) { - print_summary(game.summary.go_home, "Went home to ") - delete game.summary.go_home - } + print_summary(game.summary.go_home, "Went home") + delete game.summary.go_home } // CURRENT PLAYER ALIASES @@ -723,7 +728,7 @@ function draw_leader_from_pool() { let i = random(game.british.pool.length) let p = game.british.pool[i] - log(`Drew ${piece_name(p)} from pool.`) + log(`Drew P${p} from pool.`) // 5.55 If both on-map 7 leaders are besieged, return the third to the pool without substitution. if (is_seven_command_leader(p)) { @@ -733,7 +738,7 @@ function draw_leader_from_pool() { if (is_piece_on_map(BRADDOCK) && is_piece_inside(BRADDOCK)) ++n if (is_piece_on_map(LOUDOUN) && is_piece_inside(LOUDOUN)) ++n if (n >= 2) { - log(`Returned ${piece_name(p)} to pool.`) + log(`Returned P${p} to pool.`) return 0 } } @@ -933,20 +938,16 @@ function is_originally_enemy(s) { return is_originally_british(s) } -function piece_name(p) { - if (is_unit_reduced(p)) - return pieces[p].nb_rdesc - return pieces[p].nb_desc +function piece_log_name(p) { + return pieces[p].log_name } -function log_piece_name_and_place(p) { - // return piece_name(p) + " at %" + piece_space(p) - return piece_name(p) + " (%" + piece_space(p) + ")" +function piece_name(p) { + return pieces[p].name || pieces[p].log_name } function piece_name_and_place(p) { - // return piece_name(p) + " at %" + space_name(piece_space(p)) - return piece_name(p) + " (" + space_name(piece_space(p)) + ")" + return piece_name(p) + " at " + space_name(piece_space(p)) } function piece_movement(p) { @@ -977,6 +978,10 @@ function leader_tactics(p) { return pieces[p].tactics } +function leader_tactics_text(who) { + return is_british_leader(who) ? "RB" + leader_tactics(who) : "RF" + leader_tactics(who) +} + // DYNAMIC PROPERTIES function piece_node(p) { @@ -1143,20 +1148,15 @@ function has_fieldworks(s) { } function place_fieldworks(s) { - log(`Placed fieldworks at %${s}.`) set_add(game.fieldworks, s) } function remove_fieldworks(s) { - if (set_has(game.fieldworks, s)) { - // log(`Fieldworks (%${s}) removed.`) - log(`Removed fieldworks at %${s}.`) - set_delete(game.fieldworks, s) - } + set_delete(game.fieldworks, s) } function place_friendly_raided_marker(s) { - log(`Placed raided marker at %${s}.`) + log("Placed raided marker.") player.raids.push(s) player.raids.sort((a,b)=>a-b) } @@ -1676,14 +1676,14 @@ function find_enemy_commanding_leader_in_space(s) { function log_vp(n) { if (game.active === FRANCE) { if (n < 0) - log(`France lost ${-n} VP.`) + log(`France \u2212${-n} VP.`) else - log(`France gained ${n} VP.`) + log(`France +${n} VP.`) } else { if (n < 0) - log(`Britain gained ${-n} VP.`) + log(`Britain +${-n} VP.`) else - log(`Britain lost ${n} VP.`) + log(`Britain \u2212${n} VP.`) } } @@ -1751,39 +1751,21 @@ function unstack_piece_from_force(p) { function restore_unit(p) { set_unit_reduced(p, 0) - if (game.summary && game.summary.restored) - push_summary(game.summary.restored, p) - else - // log(`Restored ${log_piece_name_and_place(p)}.`) - log(`${log_piece_name_and_place(p)} restored.`) + push_summary(game.summary.restored, p) } -function reduce_unit(p, verbose=true) { +function reduce_unit(p) { if (is_unit_reduced(p) || is_one_step_marine_detachment(p)) { - eliminate_piece(p, verbose) + eliminate_piece(p) return true } - if (game.summary && game.summary.reduced) - push_summary(game.summary.reduced, p) - else if (verbose) - // log(`Reduced ${log_piece_name_and_place(p)}.`) - log(`${log_piece_name_and_place(p)} reduced.`) - else - // log(`Reduced ${piece_name(p)}.`) - log(`${piece_name(p)} reduced.`) + push_summary(game.summary.reduced, p) set_unit_reduced(p, 1) return false } -function eliminate_piece(p, verbose=true) { - if (game.summary && game.summary.eliminated) - push_summary(game.summary.eliminated, p) - else if (verbose) - // log(`Eliminated ${log_piece_name_and_place(p)}.`) - log(`${log_piece_name_and_place(p)} eliminated.`) - else - // log(`Eliminated ${piece_name(p)}.`) - log(`${piece_name(p)} eliminated.`) +function eliminate_piece(p) { + push_summary(game.summary.eliminated, p) unstack_force(p) set_unit_reduced(p, 0) game.location[p] = 0 @@ -1801,6 +1783,12 @@ function eliminate_piece(p, verbose=true) { } } +function eliminate_leader(p) { + unstack_force(p) + set_unit_reduced(p, 0) + game.location[p] = 0 +} + function eliminate_indian_tribe(s) { for (let p of indians.pieces_from_space[s]) if (is_piece_unbesieged(p)) @@ -1825,11 +1813,7 @@ function is_seven_command_leader(who) { function place_piece(who, to) { game.location[who] = to - if (game.summary && game.summary.placed) - push_summary(game.summary.placed, who) - else - // log(`Placed ${log_piece_name_and_place(who)}.`) - log(`${log_piece_name_and_place(who)} placed.`) + push_summary(game.summary.placed, who) // remember last placed 7-command leader(s) if (is_seven_command_leader(who)) { @@ -1885,34 +1869,34 @@ function recapture_british_fortress(s) { } function capture_enemy_fort_intact(s) { - log(`Captured intact fort at %${s}.`) + log(`Captured intact fort.`) set_delete(enemy_player.forts, s) set_add(player.forts, s) award_vp(2) } function capture_enemy_fort(s) { - log(`Captured fort at %${s}.`) + log(`Captured fort.`) set_delete(enemy_player.forts, s) set_add(player.forts_uc, s) award_vp(2) } function capture_enemy_stockade(s) { - log(`Captured stockade at %${s}.`) + log(`Captured stockade.`) set_delete(enemy_player.stockades, s) set_add(player.stockades, s) award_vp(1) } function destroy_enemy_stockade_after_battle(s) { - log(`Destroyed stockade at %${s}.`) + log(`Destroyed stockade.`) set_delete(enemy_player.stockades, s) award_vp(1) } function destroy_enemy_stockade_in_raid(s) { - log(`Destroyed stockade at %${s}.`) + log(`Destroyed stockade.`) set_delete(enemy_player.stockades, s) } @@ -2277,24 +2261,41 @@ function gen_card_menu(card) { } function card_name(card) { - return `#${card} ${cards[card].name} [${cards[card].activation}]` + return `(${cards[card].activation}) ${cards[card].name}` } -function play_card(card) { - log(`${game.active} played\n${card_name(card)}.`) +function play_card_for_event(card) { + log(`Played C${card} for event.`) remove_from_array(player.hand, card) game.last_card = card - if (cards[card].special === 'remove') + if (cards[card].special === 'remove') { + log("Removed card.") game.removed.push(card) - else + } else { + game.discard.push(card) + } +} + +function play_card_for_response(card) { + log(`${game.active} played C${card}.`) + remove_from_array(player.hand, card) + game.last_card = card + if (cards[card].special === 'remove') { + log("Removed card.") + game.removed.push(card) + } else { game.discard.push(card) + } +} + +function play_card_for_value(card, reason) { + log(`Played C${card} ${reason}.`) + remove_from_array(player.hand, card) + game.last_card = card + game.discard.push(card) } function discard_card(card, reason) { - if (reason) - log(`${game.active} discarded\n${card_name(card)}\n${reason}.`) - else - log(`${game.active} discarded\n${card_name(card)}.`) remove_from_array(player.hand, card) game.last_card = card game.discard.push(card) @@ -2318,7 +2319,8 @@ states.action_phase = { push_undo() player.did_construct = 0 logbr() - play_card(card) + play_card_for_event(card) + logbr() events[cards[card].event].play(card) }, activate_force(card) { @@ -2340,12 +2342,13 @@ states.action_phase = { discard(card) { logbr() player.did_construct = 0 + log(`Discarded C${card}.`) discard_card(card) end_action_phase() }, pass() { logbr() - log(game.active + " passed.") + log("Passed.") player.passed = 1 end_action_phase() }, @@ -2356,7 +2359,7 @@ states.action_phase = { function goto_activate_individually(card) { push_undo() player.did_construct = 0 - discard_card(card, "to activate units individually") + play_card_for_value(card, "to activate units individually") game.state = 'activate_individually' game.activation_value = 0 game.count = cards[card].activation @@ -2366,7 +2369,7 @@ function goto_activate_individually(card) { function goto_activate_force(card) { push_undo() player.did_construct = 0 - discard_card(card, "to activate a force") + play_card_for_value(card, "to activate a force") game.state = 'activate_force' game.activation_value = cards[card].activation } @@ -2389,14 +2392,14 @@ states.activate_individually = { view.actions.end_activations = 1 if (game.count >= 1) { for (let p = first_friendly_leader; p <= last_friendly_leader; ++p) { - if (is_piece_on_map(p) && !game.activation.includes(p)) { + if (is_piece_on_map(p) && !set_has(game.activation, p)) { gen_action_piece(p) } } } if (game.count > 0) { for (let p = first_friendly_unit; p <= last_friendly_unit; ++p) { - if (is_piece_on_map(p) && !game.activation.includes(p)) { + if (is_piece_on_map(p) && !set_has(game.activation, p)) { if (game.count >= 0.5) { if (is_indian(p)) gen_action_piece(p) @@ -2416,7 +2419,7 @@ states.activate_individually = { }, piece(p) { push_undo() - game.activation.push(p) + set_add(game.activation, p) if (is_drilled_troops(p)) game.count = 0 else if (is_indian(p)) @@ -2474,7 +2477,7 @@ states.select_campaign_1 = { view.prompt = "Campaign: Select the first leader." for (let p = first_friendly_leader; p <= last_friendly_leader; ++p) { if (is_piece_on_map(p)) - if (!game.activation.includes(p)) + if (!set_has(game.activation, p)) if (can_activate_force(p)) gen_action_piece(p) } @@ -2495,7 +2498,7 @@ states.select_campaign_2 = { view.prompt = "Campaign: Select the second leader." for (let p = first_friendly_leader; p <= last_friendly_leader; ++p) { if (is_piece_on_map(p) && !is_piece_in_force(p, game.activation[0])) - if (!game.activation.includes(p)) + if (!set_has(game.activation, p)) if (can_activate_force(p)) gen_action_piece(p) } @@ -2513,7 +2516,8 @@ states.select_campaign_2 = { function goto_pick_first_move() { if (game.activation.length > 1) { logbr() - log("Selected\n" + game.activation.map(log_piece_name_and_place).join(",\n") + ".") + let summary = map_group_by(game.activation, piece_space) + print_summary(summary, "Activated") game.state = 'pick_move' } else if (game.activation.length > 0) { goto_move_piece(game.activation.pop()) @@ -2541,7 +2545,7 @@ states.pick_move = { game.activation.forEach(gen_action_piece) }, piece(p) { - remove_from_array(game.activation, p) + set_delete(game.activation, p) goto_move_piece(p) }, } @@ -2603,7 +2607,7 @@ states.designate_force = { for_each_friendly_leader_in_node(where, p => { if (game.force.reason === 'avoid' && is_piece_inside(p)) return // continue - if (game.activation && game.activation.includes(p)) + if (game.activation && set_has(game.activation, p)) return // continue if (p !== commander && leader_command(p) <= leader_command(commander)) { can_pick_up = true @@ -2678,7 +2682,7 @@ states.designate_force = { for_each_friendly_leader_in_node(where, p => { if (game.force.reason === 'avoid' && is_piece_inside(p)) return // continue - if (game.activation && game.activation.includes(p)) + if (game.activation && set_has(game.activation, p)) return // continue if (p !== commander && leader_command(p) <= leader_command(commander)) move_piece_to(p, box) @@ -2730,11 +2734,11 @@ function end_designate_force() { delete game.force switch (reason) { case 'campaign_1': - game.activation.push(commander) + set_add(game.activation, commander) game.state = 'select_campaign_2' break case 'campaign_2': - game.activation.push(commander) + set_add(game.activation, commander) goto_pick_first_move() break case 'move': @@ -2809,22 +2813,32 @@ states.designate_force_lone_ax = { // MOVE -function describe_force(force, verbose) { +function log_force_with(force) { if (is_leader(force) && count_pieces_in_force(force) > 1) { - let desc = verbose ? log_piece_name_and_place(force) : piece_name(force) + let list = [] for_each_piece_in_force(force, p => { if (p !== force) - desc += ",\n" + piece_name(p) + list.push(p) }) - return desc + log(">with\n" + make_summary(list)) + } +} + +function log_force(force) { + if (is_leader(force) && count_pieces_in_force(force) > 1) { + let list = [] + for_each_piece_in_force(force, p => { + if (p !== force) + list.push(p) + }) + log(">with P" + force + "\n" + make_summary(list)) } else { - return verbose ? log_piece_name_and_place(force) : piece_name(force) + log(">with P" + force) } } function goto_move_piece(who) { logbr() - log(`Activated\n${describe_force(who, true)}.`) let from = piece_space(who) game.state = 'move' @@ -2849,16 +2863,13 @@ function goto_move_piece(who) { from: {}, aux: list_auxiliary_units_in_force(who) } - start_move() -} -function start_move() { if (has_besieged_enemy_fortifications(moving_piece_space())) { game.state = 'siege_or_move' } else if (is_piece_inside(moving_piece())) { goto_break_siege() } else { - resume_move() + start_move() } } @@ -2889,25 +2900,35 @@ states.siege_or_move = { }, siege() { push_undo() + log(`Activated P${game.move.moving}`) + log(`>at %${game.move.where}`) + log_force_with(game.move.moving) goto_siege(moving_piece_space()) }, assault() { push_undo() + log(`Activated P${game.move.moving}`) + log(`>at %${game.move.where}`) + log_force_with(game.move.moving) goto_assault(moving_piece_space()) }, play_event(c) { push_undo() game.siege_where = moving_piece_space() - play_card(c) + play_card_for_response(c) goto_surrender() }, move() { push_undo() - resume_move() + start_move() }, } function goto_break_siege() { + log(`Moved P${game.move.moving}`) + log(`>from %${game.move.where}`) + log_force_with(game.move.moving) + let here = moving_piece_space() game.move.came_from = here goto_avoid_battle() @@ -2952,6 +2973,13 @@ function max_movement_cost(type) { } } +function start_move() { + log(`Moved P${game.move.moving}`) + log(`>from %${game.move.where}`) + log_force_with(game.move.moving) + resume_move() +} + function resume_move() { let who = moving_piece() let where = moving_piece_space() @@ -3304,9 +3332,9 @@ function apply_move(to) { } if (game.move.infiltrated) - log(`Infiltrated %${to}.`) + log(`>to %${to} *`) else - log(`Moved to %${to}.`) + log(`>to %${to}`) move_piece_to(who, to) lift_sieges_and_amphib() @@ -3413,7 +3441,7 @@ states.move = { }, play_event(card) { push_undo() - play_card(card) + play_card_for_response(card) if (card === GEORGE_CROGHAN) { game.events.george_croghan = 1 resume_move() @@ -3445,8 +3473,7 @@ states.move = { }, drop_off() { push_undo() - if (game.summary) - game.summary.drop_off = [] + game.summary.drop_off = [] game.state = 'drop_off' }, siege() { @@ -3508,15 +3535,12 @@ states.drop_off = { }, piece(who) { push_undo() - if (game.summary) - game.summary.drop_off.push(who) - else - log(`Dropped off ${piece_name(who)}.`) + set_add(game.summary.drop_off, who) move_piece_to(who, moving_piece_space()) }, next() { push_undo() - print_plain_summary("Dropped off", 'drop_off') + print_plain_summary(">>dropped", 'drop_off') resume_move() }, } @@ -3543,10 +3567,10 @@ states.foul_weather = { view.who = p view.where = moving_piece_space() if (player.hand.includes(FOUL_WEATHER)) { - view.prompt = `${piece_name_and_place(p)} is about to move. You may play "Foul Weather".` + view.prompt = `${piece_name_and_place(p)} to move. You may play "Foul Weather".` gen_action('play_event', FOUL_WEATHER) } else { - view.prompt = `${piece_name_and_place(p)} is about to move. You don't have "Foul Weather".` + view.prompt = `${piece_name_and_place(p)} to move. You don't have "Foul Weather".` } view.actions.pass_fw_season = 1 if (game.activation && game.activation.length > 0) @@ -3558,7 +3582,7 @@ states.foul_weather = { // console.log("RETRO STAY") delete game.retro_foul_weather } - play_card(c) + play_card_for_response(c) game.events.foul_weather = 1 game.move.used = 0 set_active_enemy() @@ -3604,15 +3628,15 @@ states.lake_schooner = { view.who = who view.where = to if (player.hand.includes(LAKE_SCHOONER)) { - view.prompt = `${piece_name(who)} is about to move from ${space_name(from)} to ${space_name(to)}. You may play "Lake Schooner".` + view.prompt = `${piece_name(who)} to move from ${space_name(from)} to ${space_name(to)}. You may play "Lake Schooner".` gen_action('play_event', LAKE_SCHOONER) } else { - view.prompt = `${piece_name(who)} is about to move from ${space_name(from)} to ${space_name(to)}. You don't have "Lake Schooner".` + view.prompt = `${piece_name(who)} to move from ${space_name(from)} to ${space_name(to)}. You don't have "Lake Schooner".` } view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) let who = moving_piece() let from = moving_piece_space() let to = game.move.lake_schooner @@ -3620,7 +3644,7 @@ states.lake_schooner = { set_active_enemy() - log(`${piece_name(who)} stopped at %${from}.`) + log(`Stopped at %${from}.`) if (would_be_infiltration_move(who, from, to)) { // 6.63 eliminate if forced back into enemy-occupied space during infiltration @@ -3650,7 +3674,7 @@ states.amphibious_landing = { prompt() { let who = moving_piece() let from = moving_piece_space() - view.prompt = `Amphibious Landing: Select a destination for ${piece_name_and_place(who)}.` + view.prompt = `Amphibious Landing: Select a destination for ${piece_name(who)}.` view.who = who if (from === HALIFAX) { gen_action_move(from, LOUISBOURG) @@ -3675,7 +3699,7 @@ function remove_siege_marker(where) { } function place_siege_marker(where) { - log(`Started siege at %${where}.`) + log(`Placed siege marker.`) game.sieges[where] = 0 } @@ -3963,14 +3987,25 @@ function attempt_intercept() { } game.move.did_attempt_intercept = 1 - let die = roll_die("to intercept with\n" + describe_force(who, true)) - if (is_leader(who)) - die = modify(die, leader_tactics(who), "leader tactics") - if (die >= 4) { - log("Intercepted!") + logbr() + log("Intercept") + log(">from %" + piece_space(who)) + log_force(who) + + let die = roll_die() + let drm = 0 + let drm_text = "" + if (is_leader(who)) { + drm = leader_tactics(who) + drm_text = " + " + leader_tactics_text(who) + } + + if (die + drm >= 4) { + log(`>D${die}${drm_text} vs 4 \u2013 success`) end_intercept_success() } else { - log("Failed.") + log(`>D${die}${drm_text} vs 4 \u2013 failed`) + logbr() end_intercept_fail() } } @@ -4006,8 +4041,7 @@ function goto_designate_inside() { if (has_enemy_fortress(where) || has_enemy_fort(where)) { set_active_enemy() game.state = 'designate_inside' - if (game.summary) - game.summary.inside = [] + game.summary.inside = [] return goto_retroactive_foul_weather() } } @@ -4031,21 +4065,13 @@ states.designate_inside = { }, piece(p) { push_undo() - if (game.summary) { - game.summary.inside.push(p) - } else { - if (is_fortress(moving_piece_space())) - log(`${piece_name(p)} withdrew into fortress.`) - else - log(`${piece_name(p)} withdrew into fort.`) - } + set_add(game.summary.inside, p) set_piece_inside(p) }, end_withdraw() { - if (is_fortress(moving_piece_space())) - print_plain_summary("Withdrew into fortress", 'inside') - else - print_plain_summary("Withdrew into fort", 'inside') + logbr() + print_plain_summary("Withdrew", 'inside') + logbr() set_active_enemy() goto_avoid_battle() }, @@ -4113,24 +4139,37 @@ states.avoid_who = { } function attempt_avoid_battle() { - let avoiding = avoiding_piece() + let who = avoiding_piece() let moving = moving_piece() let from = moving_piece_space() + logbr() + // 6.8 Exception: Auxiliary and all-Auxiliary forces automatically succeed. - if (is_wilderness_or_mountain(from) && force_has_only_auxiliary_units(avoiding) && !force_has_auxiliary(moving)) { - log("Auxiliaries avoided battle\n" + describe_force(avoiding, false) + ".") + if (is_wilderness_or_mountain(from) && force_has_only_auxiliary_units(who) && !force_has_auxiliary(moving)) { + log("Avoid battle by auxiliaries") + log_force(who) game.state = 'avoid_to' return } - let die = roll_die("to avoid battle\n" + describe_force(avoiding, false)) - if (is_leader(avoiding)) - die = modify(die, leader_tactics(avoiding), "leader tactics") - if (die >= 4) { - game.state = 'avoid_to' + log("Avoid battle") + log_force(who) + + let die = roll_die() + let drm = 0 + let drm_text = "" + if (is_leader(who)) { + drm = leader_tactics(who) + drm_text = " + " + leader_tactics_text(who) + } + + if (die + drm >= 4) { + log(`>D${die}${drm_text} vs 4`) + game.state = "avoid_to" } else { - log("Failed.") + log(`>D${die}${drm_text} vs 4 \u2013 failed`) + logbr() end_avoid_battle() } } @@ -4179,17 +4218,14 @@ states.avoid_to = { } }, space(to) { - log(`Avoided to %${to}.`) - end_avoid_battle_success(to) + let who = avoiding_piece() + move_piece_to(who, to) + log(`>>to %${to}`) + logbr() + end_avoid_battle() }, } -function end_avoid_battle_success(to) { - let who = avoiding_piece() - move_piece_to(who, to) - end_avoid_battle() -} - function end_avoid_battle() { let who = avoiding_piece() if (who) @@ -4291,16 +4327,16 @@ function combat_result(die, str, shift) { let k = clamp(i + shift, 0, COMBAT_RESULT_TABLE.length-1) let r = COMBAT_RESULT_TABLE[k][1][die] if (k === 0) - log(`Lookup ${die} on column 0.`) + log(`Combat ${die} \xd7 0: ${r}`) else if (k === COMBAT_RESULT_TABLE.length - 1) - log(`Lookup ${die} on column >= 28.`) + log(`Combat ${die} \xd7 28+: ${r}`) else { let a = COMBAT_RESULT_TABLE[k-1][0] + 1 let b = COMBAT_RESULT_TABLE[k][0] if (a === b) - log(`Lookup ${die} on column ${b}.`) + log(`Combat ${die} \xd7 ${b}: ${r}`) else - log(`Lookup ${die} on column ${a}-${b}.`) + log(`Combat ${die} \xd7 ${a}-${b}: ${r}`) } return r } @@ -4353,13 +4389,18 @@ function goto_battle(where, is_assault) { // 5.36 unit or leader may not be activated if it participated in combat or assault. if (game.activation) { + let list = [] for_each_attacking_piece(p => { - if (game.activation.includes(p)) { - log(`Deactivated ${log_piece_name_and_place(p)}.`) - remove_from_array(game.activation, p) + if (set_has(game.activation, p)) { + list.push(p) + set_delete(game.activation, p) unstack_force(p) } }) + if (list.length > 0) { + list = map_group_by(list, piece_space) + print_summary(list, "Deactivated") + } } if (game.raid) @@ -4425,8 +4466,7 @@ function goto_battle_militia() { if (has_enemy_raided_marker(s)) return goto_battle_sortie() game.state = 'militia_in_battle' - if (game.summary) - game.summary.deploy = [] + game.summary.deploy = [] } else { goto_battle_sortie() } @@ -4454,10 +4494,7 @@ states.militia_in_battle = { move_piece_to(p, game.battle.where) if (game.active === game.battle.attacker) game.battle.atk_pcs.push(p) - if (game.summary) - game.summary.deploy.push(p) - else - log(`Deployed ${piece_name(p)}.`) + set_add(game.summary.deploy, p) }, end_militia() { print_plain_summary("Deployed", 'deploy') @@ -4469,25 +4506,21 @@ function goto_battle_sortie() { set_active(game.battle.attacker) if (has_besieged_friendly_units(game.battle.where)) { game.state = 'sortie' - if (game.summary) - game.summary.sortie = [] + game.summary.sortie = [] } else { goto_battle_attacker_events() } } function sortie_with_piece(p) { - if (game.summary) - game.summary.sortie.push(p) - else - log(`${piece_name(p)} sortied.`) + set_add(game.summary.sortie, p) game.battle.atk_pcs.push(p) // 5.36 unit or leader may not be activated if it participated in combat or assault. unstack_piece_from_force(p) if (game.activation) - remove_from_array(game.activation, p) + set_delete(game.activation, p) } states.sortie = { @@ -4703,7 +4736,7 @@ states.attacker_events = { }, play_event(c) { push_undo() - play_card(c) + play_card_for_response(c) switch (c) { case AMBUSH_1: case AMBUSH_2: @@ -4714,6 +4747,7 @@ states.attacker_events = { break case FIELDWORKS_1: case FIELDWORKS_2: + log("Removed fieldworks.") remove_fieldworks(game.battle.where) break } @@ -4778,7 +4812,7 @@ states.defender_events = { }, play_event(c) { push_undo() - play_card(c) + play_card_for_response(c) switch (c) { case AMBUSH_1: case AMBUSH_2: @@ -4792,6 +4826,7 @@ states.defender_events = { break case FIELDWORKS_1: case FIELDWORKS_2: + log("Placed fieldworks.") place_fieldworks(game.battle.where) break } @@ -4842,7 +4877,7 @@ function end_def_fire() { } function end_step_losses() { - flush_summary() + flush_losses_summary() if (game.active === game.battle.attacker) goto_atk_leader_check() else @@ -4850,7 +4885,7 @@ function end_step_losses() { } function end_leader_check() { - flush_summary() + flush_losses_summary() delete game.battle.leader_check if (game.events.ambush === game.battle.attacker) { if (game.active === game.battle.defender) @@ -4887,27 +4922,21 @@ function goto_atk_fire() { logbr() log(".b Attacker") - let str = attacker_combat_strength() let shift = 0 - if (game.events.ambush === game.battle.attacker) { - log(`Strength ${str} \xd7 2 for ambush.`) - str *= 2 - } else { - log(`Strength ${str}.`) - } let die = game.battle.atk_die = roll_die() if (is_leader(game.battle.atk_commander)) { - die = modify(die, leader_tactics(game.battle.atk_commander), "leader tactics") + log(">D" + die + " + " + leader_tactics_text(game.battle.atk_commander)) + die += leader_tactics(game.battle.atk_commander) + } else { + log(">D" + die) } + if (game.events.coehorns === game.battle.attacker) { die = modify(die, 2, "for coehorns") } - if (game.battle.assault) { - log(`1 column left for assaulting`) - shift -= 1 - } else { + if (!game.battle.assault) { if (is_wilderness_or_mountain(game.battle.where)) { let atk_has_ax = some_attacking_piece(p => is_auxiliary(p) || is_light_infantry(p)) let def_has_ax = some_defending_piece(p => is_auxiliary(p) || is_light_infantry(p)) @@ -4926,15 +4955,28 @@ function goto_atk_fire() { if (has_enemy_stockade(game.battle.where)) { die = modify(die, -1, "vs stockade") } + } + + let str = attacker_combat_strength() + if (game.events.ambush === game.battle.attacker) { + log(`Strength ${str} \xd7 2 for ambush`) + str *= 2 + } else { + log(`Strength ${str}`) + } + + if (game.battle.assault) { + log(`>1L for assaulting`) + shift -= 1 + } else { if (has_fieldworks(game.battle.where)) { // NOTE: Ignore fieldworks during assault, as they belong to the besieging forces. - log(`1 column left vs fieldworks`) + log(`>1L vs fieldworks`) shift -= 1 } } game.battle.atk_result = combat_result(die, str, shift) - log(`Attacker result: ${game.battle.atk_result}.`) end_atk_fire() } @@ -4954,20 +4996,15 @@ function goto_def_fire() { logbr() log(".b Defender") - let str = defender_combat_strength() - let shift = 0 - if (game.events.ambush === game.battle.defender) { - log(`Strength ${str} \xd7 2 for ambush.`) - str *= 2 - } else { - log(`Strength ${str}.`) - } - let die = game.battle.def_die = roll_die() let p = find_friendly_commanding_leader_in_space(game.battle.where) if (p) { - die = modify(die, leader_tactics(p), "leader tactics") + log(">D" + die + " + " + leader_tactics_text(p)) + die += leader_tactics(p) + } else { + log(">D" + die) } + if (game.events.coehorns === game.battle.defender) { die = modify(die, 2, "for coehorns") } @@ -4987,8 +5024,15 @@ function goto_def_fire() { } } - game.battle.def_result = combat_result(die, str, shift) - log(`Defender result: ${game.battle.def_result}.`) + let str = defender_combat_strength() + if (game.events.ambush === game.battle.defender) { + log(`Strength ${str} \xd7 2 for ambush`) + str *= 2 + } else { + log(`Strength ${str}`) + } + + game.battle.def_result = combat_result(die, str, 0) end_def_fire() } @@ -5101,7 +5145,7 @@ states.step_losses = { --game.battle.step_loss if (game.battle.dt_loss > 0 && is_drilled_troops(p)) --game.battle.dt_loss - if (reduce_unit(p, false)) { + if (reduce_unit(p)) { remove_from_array(game.battle.atk_pcs, p) remove_from_array(game.battle.units, p) } @@ -5154,11 +5198,11 @@ states.raid_step_losses = { piece(p) { push_undo() --game.raid.step_loss - if (reduce_unit(p, false)) + if (reduce_unit(p)) remove_from_array(game.raid.units, p) }, end_losses() { - flush_summary() + flush_losses_summary() goto_raid_leader_check() }, } @@ -5175,6 +5219,7 @@ function goto_atk_leader_check() { }) } if (game.battle.leader_check.length > 0) { + log("Leaders") game.state = 'leader_check' } else { end_leader_check() @@ -5191,7 +5236,7 @@ function goto_def_leader_check() { }) } if (game.battle.leader_check.length > 0) { - log(`Leader loss check.`) + log("Leaders") game.state = 'leader_check' } else { end_leader_check() @@ -5206,11 +5251,14 @@ states.leader_check = { gen_action_piece(game.battle.leader_check[i]) }, piece(p) { - let die = roll_die("for " + piece_name(p)) + let die = roll_die() if (die === 1) { + log(">D" + die + " P" + p + " \u2013 eliminated") if (game.battle) remove_from_array(game.battle.atk_pcs, p) - eliminate_piece(p, false) + eliminate_leader(p) + } else { + log(">D" + die + " P" + p) } remove_from_array(game.battle.leader_check, p) if (game.battle.leader_check.length === 0) @@ -5225,7 +5273,7 @@ function goto_raid_leader_check() { game.raid.leader_check.push(p) }) if (game.raid.leader_check.length > 0) { - log(`Leader loss check.`) + log("Leader") game.state = 'raid_leader_check' } else { delete game.raid.leader_check @@ -5244,13 +5292,16 @@ states.raid_leader_check = { gen_action_piece(game.raid.leader_check[i]) }, piece(p) { - let die = roll_die("for " + piece_name(p)) - if (die === 1) - eliminate_piece(p, false) + let die = roll_die() + log(">D" + die + " P" + p) + if (die === 1) { + log(">>eliminated") + eliminate_leader(p) + } remove_from_array(game.raid.leader_check, p) if (game.raid.leader_check.length === 0) { delete game.raid.leader_check - flush_summary() + flush_losses_summary() goto_raiders_go_home() } }, @@ -5261,16 +5312,12 @@ states.raid_leader_check = { function return_militia(where) { let box = department_militia(where) if (box) { - let n = 0 - for (let p = 1; p <= last_piece; ++p) { - if (is_militia(p) && is_piece_in_space(p, where)) { + for (let p = first_british_militia; p <= last_british_militia; ++p) + if (is_piece_in_space(p, where)) + move_piece_to(p, box) + for (let p = first_french_militia; p <= last_french_militia; ++p) + if (is_piece_in_space(p, where)) move_piece_to(p, box) - ++n - } - } - if (n > 0) { - log(`${n} Militia units returned to their box.`) - } } } @@ -5333,10 +5380,14 @@ function determine_winner_battle() { award_british_vp(1) } - return_militia(game.battle.where) + return_militia(where) - if (victor === game.battle.attacker) - remove_fieldworks(where) + if (victor === game.battle.attacker) { + if (has_fieldworks(where)) { + log("Removed fieldworks.") + remove_fieldworks(where) + } + } logbr() @@ -5396,7 +5447,7 @@ function determine_winner_battle() { function eliminate_enemy_pieces_inside(where) { for (let p = first_enemy_piece; p <= last_enemy_piece; ++p) if (is_piece_besieged_in_space(p, where)) - eliminate_piece(p, false) + eliminate_piece(p) } function determine_winner_assault() { @@ -5413,6 +5464,7 @@ function determine_winner_assault() { if (victor === game.battle.attacker) { log(".b Attacker Won Assault") eliminate_enemy_pieces_inside(where) + flush_losses_summary() remove_siege_marker(where) if (has_enemy_fortress(where)) { capture_enemy_fortress(where) @@ -5474,15 +5526,15 @@ states.retreat_attacker = { let to = game.retreat.to // NOTE: Besieged pieces that sortie out are 'inside' so not affected by the code below. - init_retreat_summary() log(".b Attacker Retreat") + game.summary.retreat = [] for_each_friendly_piece_in_space(from, p => { if (is_piece_unbesieged(p)) { if (can_attacker_retreat_from_to(p, from, to)) { push_retreat_summary(p, "to %" + to) move_piece_to(p, to) } else { - eliminate_piece(p, false) + eliminate_piece(p) } } else { if (is_fortress(to)) @@ -5492,7 +5544,7 @@ states.retreat_attacker = { } }) flush_retreat_summary() - flush_summary() + flush_losses_summary() logbr() end_retreat_attacker(to) } @@ -5520,7 +5572,7 @@ function end_retreat_attacker(to) { function goto_retreat_defender() { set_active(game.battle.defender) game.state = 'retreat_defender' - init_retreat_summary() + game.summary.retreat = [] } function can_defender_retreat_from_to(p, from, to) { @@ -5627,10 +5679,10 @@ states.retreat_defender = { let from = game.battle.where for_each_friendly_piece_in_space(from, p => { if (is_piece_unbesieged(p)) - eliminate_piece(p, false) + eliminate_piece(p) }) flush_retreat_summary() - flush_summary() + flush_losses_summary() logbr() end_retreat() }, @@ -5787,12 +5839,12 @@ states.retreat_lone_leader = { let who = pick_unbesieged_leader(from) if (from === to) { if (is_fortress(to)) - log(`${piece_name(who)} retreated into fortress.`) + log(`P${who} retreated into fortress.`) else - log(`${piece_name(who)} retreated into fort.`) + log(`P${who} retreated into fort.`) set_piece_inside(who) } else { - log(`${piece_name(who)} retreated to %${to}.`) + log(`P${who} retreated to %${to}.`) move_piece_to(who, to) } resume_retreat_lone_leader(from) @@ -5883,7 +5935,7 @@ states.siege_coehorns_attacker = { view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) game.events.coehorns = game.active end_siege_coehorns_attacker() }, @@ -5912,7 +5964,7 @@ states.siege_coehorns_defender = { view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) game.events.coehorns = game.active end_siege_coehorns_defender() }, @@ -5945,7 +5997,7 @@ states.siege_surrender = { view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) goto_surrender() }, pass() { @@ -5985,7 +6037,7 @@ function goto_surrender_place() { } states.surrender = { - inactive: "to play Surrender", + inactive: "to execute event", prompt() { view.prompt = "Surrender! Place defenders at the closest unbesieged fortification." view.where = game.siege_where @@ -6016,32 +6068,45 @@ function end_surrender() { } const SIEGE_TABLE_RESULT = { - 0: "No effect.", - 1: "Siege +1.", - 2: "Siege +2." + 0: "NE", + 1: "+1", + 2: "+2" } function resolve_siege() { let where = game.siege_where + delete game.siege_where logbr() log(".siege %" + where) logbr() let att_leader = find_friendly_commanding_leader_in_space(where) let def_leader = find_enemy_commanding_leader_in_space(where) - let die = roll_die("for siege") - die = modify(die, leader_tactics(att_leader), "besieging leader") + + let die = roll_die() + + log(".b Siege") + if (def_leader) { + log(`>D${die} + ${leader_tactics_text(att_leader)} \u2212 ${leader_tactics_text(def_leader)}`) + die += leader_tactics(att_leader) + die -= leader_tactics(def_leader) + } else { + log(`>D${die} + ${leader_tactics_text(att_leader)}`) + die += leader_tactics(att_leader) + } + if (game.events.coehorns) die = modify(die, game.events.coehorns === game.active ? 2 : -2, "for coehorns") - if (def_leader) - die = modify(die, -leader_tactics(def_leader), "defending leader") + if (where === LOUISBOURG) die = modify(die, -1, "for Louisbourg") - let result = SIEGE_TABLE[clamp(die, 0, 7)] - log(`Lookup ${die} on siege table.`) - log(SIEGE_TABLE_RESULT[result]) + + die = clamp(die, 0, 7) + let result = SIEGE_TABLE[die] + + log(`Siege ${die}: ${SIEGE_TABLE_RESULT[result]}`) if (result > 0) { let level = change_siege_marker(where, result) - log("Siege level " + level + ".") + log(">to level " + level) } goto_assault_possible(where) } @@ -6163,7 +6228,7 @@ states.militia_against_raid = { piece(p) { push_undo() move_piece_to(p, game.raid.where) - log(`Deployed ${piece_name(p)}.`) + log(`Deployed militia.`) game.count -- }, end_militia() { @@ -6180,6 +6245,11 @@ const RAID_TABLE = { cultivated: [ 2, 0, 0, 0, 1, 1, 0, 0 ], } +const RAID_TABLE_TEXT = { + stockade: [ "2", "1", "1", "NE", "2", "Success, 1", "Success", "Success" ], + cultivated: [ "2", "NE", "NE", "NE", "1", "Success, 1", "Success", "Success" ], +} + function goto_raid_events() { if (is_enemy_card_available(BLOCKHOUSES) && !enemy_player.pass_bh) { set_active_enemy() @@ -6202,7 +6272,7 @@ states.raid_blockhouses = { view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) game.events.blockhouses = game.active set_active_enemy() resolve_raid() @@ -6222,12 +6292,17 @@ function resolve_raid() { let x_stockade = has_enemy_stockade(where) let x_allied = has_enemy_allied_settlement(where) - let natural_die = roll_die("for raid") + let natural_die = roll_die() let die = natural_die let commander = find_friendly_commanding_leader_in_space(where) - if (commander) - die = modify(die, leader_tactics(commander), "leader") + if (commander) { + log("Raid D" + natural_die + " + " + leader_tactics_text(commander)) + die += leader_tactics(commander) + } else { + log("Raid D" + natural_die) + } + if (has_friendly_rangers(where)) die = modify(die, 1, "for rangers") if (enemy_department_has_at_least_n_militia(where, 2)) @@ -6238,32 +6313,27 @@ function resolve_raid() { column = 'stockade' if (game.events.blockhouses === enemy()) { column = 'stockade' - log("vs enemy blockhouses") + log(">vs enemy blockhouses") } let result = clamp(die, 0, 7) let success = result >= 5 let losses = RAID_TABLE[column][result] + let effect = RAID_TABLE_TEXT[column][result] + + if (column === 'stockade') + log(`vs Stockade ${die}: ${effect}`) + else + log(`vs Cultivated ${die}: ${effect}`) + logbr() - log(`Lookup ${die} vs ${column}.`) if (success) { - if (losses === 0) - log(`Success.`) - else - log(`Success with one step loss.`) if (x_stockade || x_allied || !has_friendly_raided_marker(where)) place_friendly_raided_marker(where) if (x_stockade) destroy_enemy_stockade_in_raid(where) if (x_allied) eliminate_indian_tribe(where) - } else { - if (losses === 0) - log(`No effect.`) - else if (losses === 1) - log(`Failure with one step loss.`) - else - log(`Failure with two step losses.`) } game.raid.step_loss = losses @@ -6307,7 +6377,7 @@ function goto_raiders_go_home() { to: 0, follow: {}, } - init_go_home_summary() + game.summary.go_home = [] } else { end_raiders_go_home() } @@ -6333,10 +6403,11 @@ function resume_indians_and_leaders_go_home() { to: 0, follow: {}, } - init_go_home_summary() + game.summary.go_home = [] } function end_indians_and_leaders_go_home() { + logbr() flush_go_home_summary() logbr() if (game.active === FRANCE) { @@ -6485,8 +6556,10 @@ states.go_home_to = { space(to) { let who = game.go_home.who let from = game.go_home.from - push_go_home_summary(who, to) + move_piece_to(who, to) + push_summary(game.summary.go_home, who) + if (is_indian(who)) { let home = indians.space_from_piece[who] game.count = 0 @@ -6562,8 +6635,9 @@ states.go_home_with_indians = { let from = game.go_home.from let to = game.go_home.to - push_go_home_summary(p, to) move_piece_to(p, to) + push_summary(game.summary.go_home, p) + if (game.count > 0 && is_leader(p)) game.count = 0 @@ -6704,7 +6778,7 @@ states.winter_attrition = { push_undo() if (is_unit_reduced_for_winter(p)) stack.n-- - reduce_unit(p, true) + reduce_unit(p) remove_from_array(stack.dt, p) }, end_attrition() { @@ -6913,6 +6987,7 @@ states.demolish_fieldworks = { gen_action_space(s) }, space(s) { + log(`Demolished fieldworks at %${s}.`) remove_fieldworks(s) end_demolish() } @@ -6928,7 +7003,7 @@ function format_remain(n) { function goto_construct_stockades(card) { push_undo() - discard_card(card, "to construct stockades") + play_card_for_value(card, "to construct") player.did_construct = 1 game.state = 'construct_stockades' game.count = cards[card].activation @@ -6978,7 +7053,7 @@ states.construct_stockades = { function goto_construct_forts(card) { push_undo() - discard_card(card, "to construct forts") + play_card_for_value(card, "to construct") player.did_construct = 1 game.state = 'construct_forts' game.count = cards[card].activation @@ -7124,7 +7199,7 @@ states.massacre_1 = { view.actions.pass = 1 }, play_event(c) { - play_card(c) + play_card_for_response(c) award_vp(1) game.state = 'massacre_2' set_active_enemy() @@ -7154,7 +7229,7 @@ states.massacre_2 = { } }, piece(p) { - eliminate_piece(p, false) + eliminate_piece(p) }, end_event() { set_active_enemy() @@ -7308,10 +7383,13 @@ states.indian_alliance_roll = { }, roll() { let roll = roll_die() - if (game.vp > 4) + if (game.vp > 4) { game.count = roll - else + log(`Full D${roll} indians.`) + } else { game.count = Math.ceil(roll / 2) + log(`Half D${roll} indians.`) + } game.state = 'indian_alliance' }, } @@ -7324,6 +7402,7 @@ states.indian_alliance_roll_gray = { roll() { let roll = roll_die() game.count = roll + log(`Roll D${roll} indians.`) game.state = 'indian_alliance' }, } @@ -7691,11 +7770,12 @@ states.louisbourg_squadrons = { }, roll() { let roll = roll_die() + log("Roll D" + roll + " vs 1-3.") log("No amphibious landings this year.") if (roll <= 3) { log("No French naval moves ever.") log("British may play Quiberon.") - log("Card removed.") + log("Removed card.") game.events.no_fr_naval = 1 remove_card(LOUISBOURG_SQUADRONS) } @@ -7749,8 +7829,8 @@ states.governor_vaudreuil_interferes = { let p_loc = piece_space(p) move_piece_to(a, p_loc) move_piece_to(p, a_loc) - log(`${piece_name(a)} moved to %${p_loc}.`) - log(`${piece_name(p)} moved to %${a_loc}.`) + log(`P${a} moved to %${p_loc}.`) + log(`P${p} moved to %${a_loc}.`) game.count = 0 } else { push_undo() @@ -7791,16 +7871,18 @@ states.small_pox = { states.small_pox_roll = { prompt() { view.prompt = "Small Pox: Roll a die." + view.where = game.small_pox view.actions.roll = 1 }, roll() { let roll = roll_die() if (count_units_in_space(game.small_pox) > 8) { + log("Full D" + roll + " steps.") game.count = roll } else { + log("Half D" + roll + " steps.") game.count = Math.ceil(roll / 2) } - log(`Must eliminate ${game.count} steps.`) game.state = 'small_pox_eliminate_steps' set_active_enemy() }, @@ -7808,6 +7890,7 @@ states.small_pox_roll = { states.small_pox_eliminate_steps = { prompt() { + view.where = game.small_pox let done = true if (game.count > 0) { for_each_friendly_unit_in_space(game.small_pox, p => { @@ -7832,10 +7915,11 @@ states.small_pox_eliminate_steps = { }, piece(p) { push_undo() + reduce_unit(p) game.count -- - reduce_unit(p, false) }, next() { + flush_losses_summary() if (has_any_indians(game.small_pox)) { game.state = 'small_pox_remove_indians' } else { @@ -7847,19 +7931,21 @@ states.small_pox_eliminate_steps = { states.small_pox_remove_indians = { prompt() { view.prompt = `Small Pox at ${space_name(game.small_pox)}: Remove all indians.` + view.where = game.small_pox for_each_unit_in_space(game.small_pox, p => { if (is_indian(p)) gen_action_piece(p) }) }, piece(p) { - eliminate_piece(p, false) + eliminate_piece(p) if (!has_any_indians(game.small_pox)) end_small_pox() }, } function end_small_pox() { + flush_losses_summary() delete game.small_pox set_active_enemy() end_action_phase() @@ -7881,12 +7967,13 @@ states.courier_intercepted_roll = { }, roll() { let roll = roll_die() + log("Roll D" + roll + " vs 3-6.") if (roll >= 3) { let i = random(enemy_player.hand.length) let c = enemy_player.hand[i] enemy_player.hand.splice(i, 1) player.hand.push(c) - log(`Stole ${card_name(c)}.`) + log(`Took C${c} from opponent.`) game.state = "courier_intercepted_show" } else { log("No effect.") @@ -7898,7 +7985,7 @@ states.courier_intercepted_roll = { states.courier_intercepted_show = { prompt() { let c = player.hand[player.hand.length-1] - view.prompt = `Courier Intercepted: You stole ${card_name(c)}.` + view.prompt = `Courier Intercepted: You took ${card_name(c)}.` view.selected_card = c view.actions.end_event = 1 }, @@ -7925,7 +8012,7 @@ states.francois_bigot_draw = { set_active_enemy() let i = random(player.hand.length) game.bigot = player.hand[i] - log(`France discarded ${card_name(game.bigot)}.`) + log(`France discarded C${game.bigot}.`) game.state = "francois_bigot_show" }, } @@ -7995,6 +8082,7 @@ states.british_ministerial_crisis = { card(c) { push_undo() game.count = 0 + log(`Britain discarded C${card}.`) discard_card(c) }, end_event() { @@ -8112,8 +8200,8 @@ states.stingy_provincial_assembly = { }, piece(p) { push_undo() - game.count = 0 eliminate_piece(p) + game.count = 0 }, end_event() { set_active_enemy() @@ -8349,8 +8437,8 @@ states.raise_provincial_regiments = { push_undo() let p = find_unused_provincial(game.department) place_piece(p, s) - game.count -- game.did_raise = 1 + game.count -- }, end_event() { delete game.did_raise @@ -8463,6 +8551,7 @@ states.colonial_recruits_roll = { }, roll() { let roll = roll_die() + log("Restore D" + roll + " units.") game.state = 'colonial_recruits' game.count = roll }, @@ -8548,8 +8637,10 @@ states.victories_roll = { view.actions.roll = 1 }, roll() { - game.count = roll_die() + let roll = roll_die() + log("Restore D" + roll + " units.") game.state = 'restore_regular_or_light_infantry_units' + game.count = roll }, } @@ -9210,7 +9301,7 @@ states.william_pitt = { }, card(c) { push_undo() - log(`Drew ${card_name(c)} from discard.`) + log(`Drew C${c} from discard.`) remove_from_array(game.discard, c) player.hand.push(c) game.count = 0 @@ -9252,7 +9343,7 @@ states.diplomatic_revolution = { }, card(c) { push_undo() - log(`Drew ${card_name(c)} from discard.`) + log(`Drew C${c} from discard.`) remove_from_array(game.discard, c) player.hand.push(c) game.count = 0 @@ -9295,7 +9386,7 @@ states.draw_regulars = { remove_from_array(game.discard, c) player.hand.push(c) - log(`Exchanged ${card_name(x)} for ${card_name(c)} in discard.`) + log(`Exchanged C${x} for C${c} in discard.`) start_action_phase() }, @@ -9713,10 +9804,10 @@ exports.setup = function (seed, scenario, options) { // go_home: {}, summary: { - placed: {}, - restored: {}, - reduced: {}, - eliminated: {}, + placed: [], + restored: [], + reduced: [], + eliminated: [], }, undo: [], |