diff options
-rw-r--r-- | play.js | 13 | ||||
-rw-r--r-- | rules.js | 240 |
2 files changed, 143 insertions, 110 deletions
@@ -173,7 +173,7 @@ function is_unit_moved(u) { } function is_unit_fired(u) { - return set_has(view.fired, u) + return set_has(view.fired, u) || set_has(view.retreat, u) } function is_unit_revealed(u) { @@ -200,8 +200,12 @@ function is_unit_selected(unit) { return view.selected === unit } -function is_hex_action(hex) { - return !!(view.actions && view.actions.hex && view.actions.hex.includes(hex)) +function is_any_hex_action(hex) { + if (view.actions && view.actions.hex && view.actions.hex.includes(hex)) + return true + if (is_hex_forced_march_action(hex)) + return true + return false } function is_hex_forced_march_action(hex) { @@ -847,7 +851,7 @@ function update_map() { } if (ui.hexes[hex]) { - ui.hexes[hex].classList.toggle("action", is_hex_action(hex) || is_hex_forced_march_action(hex)) + ui.hexes[hex].classList.toggle("action", is_any_hex_action(hex)) ui.hexes[hex].classList.toggle("forced_march", is_hex_forced_march_action(hex)) ui.hexes[hex].classList.toggle("selected", is_hex_selected(hex)) ui.hexes[hex].classList.toggle("axis_control", is_hex_axis_controlled(hex)) @@ -993,6 +997,7 @@ function on_update() { action_button("eliminate", "Eliminate") action_button("overrun", "Overrun") + action_button("refuse", "Refuse") action_button("retreat", "Retreat") action_button("probe", "Probe") @@ -4,8 +4,6 @@ const max = Math.max const min = Math.min const abs = Math.abs -const AUTOSELECT = false - var states = {} var game = null var view = null @@ -2274,13 +2272,13 @@ states.turn_option = { inactive: "turn option", prompt() { if (game.commit[0] === 0 && game.commit[1] === 0) - view.prompt = `Turn Option: committed zero supply.` + view.prompt = `Turn Option: Committed zero supply.` else if (game.commit[1] === 0) - view.prompt = `Turn Option: committed ${game.commit[0]} real supply.` + view.prompt = `Turn Option: Committed ${game.commit[0]} real supply.` else if (game.commit[0] === 0) - view.prompt = `Turn Option: committed ${game.commit[1]} dummy supply.` + view.prompt = `Turn Option: Committed ${game.commit[1]} dummy supply.` else - view.prompt = `Turn Option: committed ${game.commit[0]} real and ${game.commit[1]} dummy supply.` + view.prompt = `Turn Option: Committed ${game.commit[0]} real and ${game.commit[1]} dummy supply.` let hand = player_hand() if (game.commit[0] + game.commit[1] < 3) { @@ -2726,19 +2724,14 @@ function goto_initial_supply_check_recover() { } function goto_initial_supply_check_rout() { - let n = 0, where = 0 - for (let x of all_hexes) { - if (is_friendly_rout_hex(x)) { - where = x - n++ - } - } - if (n === 0) - goto_turn_option() - else if (AUTOSELECT && n === 1) - goto_rout(where, false, goto_initial_supply_check_rout) - else + let rout = false + for (let x of all_hexes) + if (is_friendly_rout_hex(x)) + rout = true + if (rout) game.state = 'initial_supply_check_rout' + else + goto_turn_option() } states.initial_supply_check_rout = { @@ -2812,19 +2805,14 @@ function goto_final_supply_check_disrupt() { } function goto_final_supply_check_rout() { - let n = 0, where = 0 - for (let x of all_hexes) { - if (is_friendly_rout_hex(x)) { - where = x - n++ - } - } - if (n === 0) - end_player_turn() - else if (AUTOSELECT && n === 1) - goto_rout(where, false, goto_final_supply_check_rout) - else + let rout = false + for (let x of all_hexes) + if (is_friendly_rout_hex(x)) + rout = true + if (rout) game.state = 'final_supply_check_rout' + else + end_player_turn() } states.final_supply_check_rout = { @@ -2971,18 +2959,18 @@ function has_valid_regroup_move_left() { } states.select_moves = { - inactive: "move phase", + inactive: "movement", prompt() { show_move_commands() if (game.turn_option === 'offensive') { if (game.from1) - view.prompt = `Designate second offensive move.` + view.prompt = `Movement: Designate second offensive move.` else - view.prompt = `Designate first offensive move.` + view.prompt = `Movement: Designate first offensive move.` } else if (game.turn_option === 'blitz') { - view.prompt = `Designate first blitz move.` + view.prompt = `Movement: Designate first blitz move.` } else { - view.prompt = `Designate ${game.turn_option} move.` + view.prompt = `Movement: Designate ${game.turn_option} move.` } let can_group_move = has_valid_group_move_left() @@ -3089,10 +3077,10 @@ function list_valid_regroup_moves() { } states.group_move_from = { - inactive: "move phase", + inactive: "movement", prompt() { show_move_commands() - view.prompt = `Group Move: Select hex to move from.` + view.prompt = `Group Move: Select group to move.` for (let x of game.group) if (x !== game.from1 || game.to1) @@ -3148,10 +3136,10 @@ states.group_move_from = { } states.regroup_move_command_point = { - inactive: "move phase", + inactive: "movement", prompt() { show_move_commands() - view.prompt = `Regroup Move: Designate the command point hex.` + view.prompt = `Regroup Move: Select the command point hex.` if (game.turn_option !== 'pass') { for (let x of game.regroup) gen_action_hex(x) @@ -3173,10 +3161,10 @@ states.regroup_move_command_point = { } states.regroup_move_destination = { - inactive: "move phase", + inactive: "movement", prompt() { show_move_commands() - view.prompt = `Regroup Move: Select destination hex.` + view.prompt = `Regroup Move: Select the destination hex.` let cp, rommel if (game.from2 === 0) cp = game.from1, rommel = (game.rommel === 1 ? 1 : 0) @@ -3313,19 +3301,20 @@ function can_end_move() { } states.move = { - inactive: "move phase", + inactive: "movement", prompt() { let rommel1 = (game.rommel === 1) ? 1 : 0 let rommel2 = (game.rommel === 2) ? 1 : 0 if (game.selected < 0) { - view.prompt = `Move: Select unit to move.` + let can_move = false // Select Group Move 1 if (!game.to1 && game.from1) { if (!is_battle_hex(game.from1)) { for_each_undisrupted_and_unmoved_friendly_unit_in_hex(game.from1, u => { gen_action_unit(u) + can_move = true }) } } @@ -3335,6 +3324,7 @@ states.move = { if (!is_battle_hex(game.from2)) { for_each_undisrupted_and_unmoved_friendly_unit_in_hex(game.from2, u => { gen_action_unit(u) + can_move = true }) } } @@ -3347,8 +3337,10 @@ states.move = { if (fastest >= 0) { search_current_move(fastest, false) for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => { - if (can_move_to(game.to1, unit_speed[u] + 1 + rommel1)) + if (can_move_to(game.to1, unit_speed[u] + 1 + rommel1)) { gen_action_unit(u) + can_move = true + } }) } } @@ -3363,18 +3355,16 @@ states.move = { if (fastest >= 0) { search_current_move(fastest, false) for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => { - if (can_move_to(game.to2, unit_speed[u] + 1 + rommel2)) + if (can_move_to(game.to2, unit_speed[u] + 1 + rommel2)) { gen_action_unit(u) + can_move = true + } }) } } }) } - // Retreat - if (can_select_retreat_hex()) - gen_action('retreat') - // Overrun let has_overrun_hex = false for (let x of all_hexes) { @@ -3383,15 +3373,33 @@ states.move = { break } } - if (has_overrun_hex) { + if (can_move) + view.prompt = `Movement: Select unit to move, or hex to overrun.` + else + view.prompt = `Movement: Select hex to overrun.` gen_action('overrun') } else { - if (can_end_move()) - gen_action('end_move') + if (can_select_retreat_hex()) { + if (can_move) + view.prompt = `Movement: Select unit to move, or retreat after all other movement.` + else + view.prompt = `Movement: You may retreat.` + gen_action('retreat') + if (can_end_move()) + gen_action('end_move') + } else { + if (can_end_move()) { + if (can_move) + view.prompt = `Movement: Select unit to move.` + else + view.prompt = `Movement: Done.` + gen_action('end_move') + } + } } } else { - view.prompt = `Move: Select destination hex.` + view.prompt = `Movement: Select destination.` // Deselect gen_action_unit(game.selected) @@ -3417,7 +3425,8 @@ states.move = { }, retreat() { push_undo() - goto_retreat() + log_br() + game.state = 'retreat_from' }, overrun() { push_undo() @@ -3443,9 +3452,9 @@ states.move = { } states.overrun = { - inactive: "move phase", + inactive: "movement", prompt() { - view.prompt = `Overrun!` + view.prompt = `Movement: Select hex to overrun.` for (let x of all_hexes) if (is_enemy_rout_hex(x)) gen_action_hex(x) @@ -3674,9 +3683,9 @@ function move_unit(who, to, speed, move) { } states.forced_march_via = { - inactive: "move phase", + inactive: "movement", prompt() { - view.prompt = `Move: Select which path to take.` + view.prompt = `Movement: Select which path to take.` view.selected = game.hexside.who view.selected_hexes = game.hexside.to @@ -3705,9 +3714,9 @@ states.forced_march_via = { } states.engage_via = { - inactive: "move phase", + inactive: "movement", prompt() { - view.prompt = `Move: Select which hex side to cross.` + view.prompt = `Movement: Select which hex side to cross.` view.selected = game.hexside.who view.selected_hexes = game.hexside.to @@ -3876,26 +3885,17 @@ function end_forced_marches() { } function goto_forced_marches_rout() { - let friend_count = 0, friend_where = 0 - let enemy_count = 0, enemy_where = 0 + let rout = false for (let x of all_hexes) { - if (is_friendly_rout_hex(x)) { - friend_where = x - friend_count++ - } - if (is_enemy_rout_hex(x)) { - enemy_where = x - enemy_count++ - } + if (is_friendly_rout_hex(x)) + rout = true + if (is_enemy_rout_hex(x)) + rout = true } - if (friend_count + enemy_count === 0) - goto_refuse_battle() - else if (friend_count === 1 && enemy_count === 0) - goto_rout(friend_where, false, goto_forced_marches_rout) - else if (friend_count === 0 && enemy_count === 1) - goto_rout(enemy_where, true, goto_forced_marches_rout) - else + if (rout) game.state = 'forced_marches_rout' + else + goto_refuse_battle() } states.forced_marches_rout = { @@ -4011,15 +4011,6 @@ function can_select_retreat_hex() { return false } -function goto_retreat() { - log_br() - game.state = 'retreat_from' - - // only one valid retreat hex - if (!game.to1 && !game.from2 && is_valid_retreat_hex(game.from1)) - goto_retreat_who(game.from1) -} - states.retreat_from = { inactive: "retreat", prompt() { @@ -4064,14 +4055,16 @@ states.retreat_from = { function goto_retreat_who(from) { game.retreat = from - game.state = 'retreat_who' - game.retreat_units = [] if (game.turn_option === 'pass') { + game.state = 'retreat_full' + game.retreat_units = [] for_each_undisrupted_and_unmoved_friendly_unit_in_hex(game.retreat, u => { if (can_unit_retreat(u)) game.retreat_units.push(u) }) - apply_retreat() + } else { + game.state = 'retreat_who' + game.retreat_units = [] } } @@ -4113,6 +4106,18 @@ states.retreat_who = { }, } +states.retreat_full = { + inactive: "retreat", + prompt() { + view.prompt = `Retreat: All units must retreat.` + view.selected = game.retreat_units + gen_action('retreat') + }, + retreat() { + apply_retreat() + }, +} + function apply_retreat() { let full_retreat = true for (let u of game.retreat_units) @@ -4189,7 +4194,6 @@ function goto_retreat_move() { states.retreat_move = { inactive: "retreat", prompt() { - view.prompt = `Retreat!` if (game.selected < 0) { let done = true for (let u of game.retreat_units) { @@ -4198,9 +4202,14 @@ states.retreat_move = { done = false } } - if (done) + if (done) { + view.prompt = `Retreat: Done.` gen_action('end_retreat') + } else { + view.prompt = `Retreat: Select unit to withdraw.` + } } else { + view.prompt = `Retreat: Select destination.` let rommel1 = (game.rommel === 1) ? 1 : 0 let rommel2 = (game.rommel === 2) ? 1 : 0 gen_action_unit(game.selected) @@ -4283,17 +4292,26 @@ function goto_refuse_battle() { states.refuse_battle = { inactive: "refuse battle", prompt() { - view.prompt = `You may Refuse Battle from hexes just attacked.` + view.prompt = `Refuse Battle: You may retreat from hexes just attacked.` + view.selected_hexes = game.selected for (let x of game.new_battles) if (can_all_undisrupted_units_disengage_and_withdraw(x)) gen_action_hex(x) + if (game.selected >= 0) + gen_action('refuse') gen_action('pass') }, hex(x) { - log_h4(`Refused battle at #${x}`) - game.refuse = x - set_delete(game.new_battles, x) - goto_pursuit_fire_during_refuse_battle(x) + if (x === game.selected) + game.selected = -1 + else + game.selected = x + }, + refuse() { + game.refuse = pop_selected() + log_h4(`Refused battle at #${game.refuse}`) + set_delete(game.new_battles, game.refuse) + goto_pursuit_fire_during_refuse_battle(game.refuse) }, pass() { goto_combat_phase() @@ -4313,16 +4331,20 @@ function goto_refuse_battle_move() { states.refuse_battle_move = { inactive: "refuse battle", prompt() { - view.prompt = `Refuse Battle: Withdraw units.` if (game.selected < 0) { let done = true for_each_undisrupted_friendly_unit_in_hex(game.refuse, u => { gen_action_unit(u) done = false }) - if (done) + if (done) { + view.prompt = `Refuse Battle: Done.` gen_action('end_retreat') + } else { + view.prompt = `Refuse Battle: Select unit to withdraw.` + } } else { + view.prompt = `Refuse Battle: Select destination.` let speed = unit_speed[game.selected] gen_action_unit(game.selected) search_withdraw_retreat(game.selected, 0) @@ -4450,16 +4472,20 @@ function goto_rout_move() { states.rout_move = { inactive: "rout", prompt() { - view.prompt = `Rout: Withdraw units.` if (game.selected < 0) { let done = true for_each_friendly_unit_in_hex(game.rout.from, u => { gen_action_unit(u) done = false }) - if (done) + if (done) { + view.prompt = `Rout: Done.` gen_action('end_rout') + } else { + view.prompt = `Rout: Select unit to withdraw.` + } } else { + view.prompt = `Rout: Select destination.` let speed = unit_speed[game.selected] let eliminate = true search_withdraw_retreat(game.selected, 0) @@ -4572,7 +4598,7 @@ function goto_combat_phase() { states.select_active_battles = { inactive: "combat phase", prompt() { - view.prompt = `Select active battles.` + view.prompt = `Combat: Select active battles.` view.selected_hexes = game.active_battles for (let x of all_hexes) { if (!set_has(game.active_battles, x) && is_battle_hex(x)) @@ -4611,7 +4637,7 @@ states.select_active_battles = { states.select_assault_battles = { inactive: "combat phase", prompt() { - view.prompt = `Select assault battles.` + view.prompt = `Combat: Select assault battles.` view.selected_hexes = game.assault_battles for (let x of game.active_battles) gen_action_hex(x) @@ -4665,7 +4691,7 @@ function goto_select_battle() { states.select_battle = { inactive: "combat phase", prompt() { - view.prompt = `Select next battle to resolve.` + view.prompt = `Combat: Select next battle.` view.active_battles = game.active_battles view.assault_battles = game.assault_battles for (let x of game.active_battles) @@ -5306,7 +5332,7 @@ states.pursuit_fire = { inactive: "pursuit fire", show_pursuit: true, prompt() { - view.prompt = `Pursuit Fire.` + view.prompt = `Pursuit Fire: Fire or withhold.` if (game.hits < count_hp_in_pursuit()) { let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { @@ -5331,7 +5357,7 @@ states.rout_fire = { inactive: "rout fire", show_pursuit: true, prompt() { - view.prompt = `Pursuit Fire (Rout).` + view.prompt = `Pursuit Fire: Fire or withhold.` if (game.hits < count_hp_in_rout()) { let slowest = slowest_enemy_unit_speed(game.pursuit) for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { @@ -7042,8 +7068,10 @@ exports.view = function(state, current) { if (current === game.active) view.selected = game.selected - if (states[game.state].show_battle) + if (states[game.state].show_battle) { view.battle = game.battle + view.retreat = game.retreat_units + } if (states[game.state].show_pursuit) view.pursuit = game.pursuit if (view.battle || view.pursuit) { |