diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 233 |
1 files changed, 137 insertions, 96 deletions
@@ -1,7 +1,5 @@ "use strict" -// TOOD: reveal/hide blocks (hexes) - // TODO: fortress supply // TODO: oasis supply @@ -17,6 +15,7 @@ // RULES: for sea redeployment, can bases be "besieged"? (yes) // RULES: may units redeploying out of battle hex leave disrupted units behind to be routed? (no) // RULES: may units redeploying out of battle hex cross enemy controlled hexsides? (yes / doesn't matter) +// RULES: may units returning for refit enter enemy supply network? (no) // RULES: if disrupted units are routed again during their "full enemy turn", can they still recover? // RULES: may oasis supplied units refuse battle or withdraw to base? @@ -38,9 +37,9 @@ var view = null var after_rout_table = {} -const { +const { all_hexes, hex_exists, hex_road, side_road, side_limit, hex_name, regions, - unit_name, unit_appearance, unit_class, unit_speed, unit_start_steps, + unit_name, unit_appearance, unit_elite, unit_class, unit_speed, unit_start_steps, } = require("./data") function debug_hexes3(n, list) { @@ -412,6 +411,7 @@ function set_unit_fired(u) { function eliminate_unit(u) { set_unit_hex(u, 0) set_unit_lost_steps(u, 0) + hide_unit(u) } function reduce_unit(u) { @@ -429,6 +429,22 @@ function replace_unit(u) { set_unit_lost_steps(u, lost - 1) } +function reveal_units_in_hex(x) { + for (let u = 0; u < unit_count; ++u) + if (unit_hex(u) === x) + set_add(game.revealed, u) +} + +function hide_units_in_hex(x) { + for (let u = 0; u < unit_count; ++u) + if (unit_hex(u) === x) + set_delete(game.revealed, u) +} + +function hide_unit(u) { + set_delete(game.revealed, u) +} + // === UNIT DATA === function find_unit(name) { @@ -892,11 +908,14 @@ function for_each_undisrupted_enemy_unit_in_hex(x, fn) { fn(u) } -function count_battle_hexes() { +function count_and_reveal_battle_hexes() { let n = 0 - for (let x of all_hexes) - if (is_battle_hex(x)) + for (let x of all_hexes) { + if (is_battle_hex(x)) { + reveal_units_in_hex(x) ++n + } + } return n } @@ -1371,10 +1390,10 @@ function search_withdraw_retreat(who, bonus) { } function search_redeploy(start) { - search_path_redeploy_bfs(path_cost[0], start, 0, 100) - search_path_redeploy_bfs(path_cost[1], start, 1, 100) - search_path_redeploy_bfs(path_cost[2], start, 2, 100) - search_path_redeploy_bfs(path_cost[4], start, 4, 100) + search_path_redeploy_bfs(path_cost[0], start, 0) + search_path_redeploy_bfs(path_cost[1], start, 1) + search_path_redeploy_bfs(path_cost[2], start, 2) + search_path_redeploy_bfs(path_cost[4], start, 4) } // Breadth First Search @@ -2726,6 +2745,8 @@ states.retreat_who = { retreat() { clear_undo() let full_retreat = true + for (let u of game.retreat_units) + hide_unit(u) for_each_undisrupted_friendly_unit_in_hex(game.retreat, u => { if (!set_has(game.retreat_units, u)) full_retreat = false @@ -2831,8 +2852,10 @@ states.retreat_move = { } function end_retreat() { - if (!is_battle_hex(game.retreat)) + if (!is_battle_hex(game.retreat)) { release_hex_control(game.retreat) + hide_units_in_hex(game.retreat) + } game.retreat_units = null // no shielding units remain @@ -3114,7 +3137,7 @@ function goto_combat_phase() { set_add(game.active_battles, TOBRUK) } - let n = count_battle_hexes() + let n = count_and_reveal_battle_hexes() if (n > 0) { if (n > game.active_battles.length) return game.state = 'select_active_battles' @@ -3306,6 +3329,11 @@ function goto_battle(x) { } function end_battle() { + if (!is_battle_hex(game.battle)) { + release_hex_control(game.battle) + hide_units_in_hex(game.battle) + } + set_delete(game.active_battles, game.battle) set_delete(game.assault_battles, game.battle) @@ -3489,9 +3517,9 @@ states.battle_fire = { states.battle_hits = { prompt() { if (game.active === game.phasing) - view.prompt = `Battle: Apply hits from Defensive Fire.` + view.prompt = `Battle: ${format_allocate_hits()} from Defensive Fire.` else - view.prompt = `Battle: Apply hits from Offensive Fire.` + view.prompt = `Battle: ${format_allocate_hits()} from Offensive Fire.` gen_battle_hits() }, unit(who) { @@ -3547,9 +3575,9 @@ states.probe_fire = { states.probe_hits = { prompt() { if (game.active !== game.phasing) - view.prompt = `Probe: Apply hits from Defensive Fire.` + view.prompt = `Probe: ${format_allocate_hits()} from Defensive Fire.` else - view.prompt = `Probe: Apply hits from Offensive Fire.` + view.prompt = `Probe: ${format_allocate_hits()} from Offensive Fire.` gen_battle_hits() }, unit(who) { @@ -3589,38 +3617,70 @@ function end_probe_hits() { // routing apply hits // routing moves +function slowest_enemy_unit_speed(where) { + let r = 4 + for_each_enemy_unit_in_hex(where, u => { + let s = unit_speed[u] + if (s < r) + r = s + }) + return r +} + +function slowest_undisrupted_enemy_unit_speed(where) { + let r = 4 + for_each_undisrupted_enemy_unit_in_hex(where, u => { + let s = unit_speed[u] + if (s < r) + r = s + }) + return r +} + function goto_rout_fire(where) { set_enemy_player() game.hits = 0 game.pursuit = where - if (can_rout_fire(true)) - game.state = 'rout_fire' - else - goto_rout_hits() + let slowest = slowest_enemy_unit_speed(game.pursuit) + log(`Slowest was ${speed_name[slowest]} unit.`) + game.state = 'rout_fire' } function goto_pursuit_fire_during_retreat(where) { set_passive_player() game.hits = 0 game.pursuit = where - if (can_pursuit_fire(true)) - game.state = 'pursuit_fire' - else - end_pursuit_fire() + let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) + log(`Slowest was ${speed_name[slowest]} unit.`) + game.state = 'pursuit_fire' } function goto_pursuit_fire_during_refuse_battle(where) { set_active_player() game.hits = 0 game.pursuit = where - if (can_pursuit_fire(true)) - game.state = 'pursuit_fire' + let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) + log(`Slowest was ${speed_name[slowest]} unit.`) + game.state = 'pursuit_fire' +} + +function format_allocate_hits() { + let hits = 0 + if (typeof game.hits === 'number') + hits = game.hits else - end_pursuit_fire() + hits = game.hits[1] + game.hits[2] + game.hits[3] + if (hits === 0) + return `Allocate zero hits` + else if (hits === 1) + return `Allocate 1 hit` + else + return `Allocate ${hits} hits` } function goto_rout_hits() { set_enemy_player() + game.flash = "" if (game.hits > 0) game.state = 'rout_hits' else @@ -3629,57 +3689,13 @@ function goto_rout_hits() { function goto_pursuit_hits() { set_enemy_player() - // XXX if (true) + game.flash = "" if (game.hits > 0) game.state = 'pursuit_hits' else end_pursuit_fire() } -function slowest_enemy_unit_speed(where) { - let r = 4 - for_each_enemy_unit_in_hex(where, u => { - let s = unit_speed[u] - if (s < r) - r = s - }) - return r -} - -function slowest_undisrupted_enemy_unit_speed(where) { - let r = 4 - for_each_undisrupted_enemy_unit_in_hex(where, u => { - let s = unit_speed[u] - if (s < r) - r = s - }) - return r -} - -function can_rout_fire(verbose) { - let result = false - let slowest = slowest_enemy_unit_speed(game.pursuit) - if (verbose) - log(`Slowest was ${speed_name[slowest]} unit.`) - for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { - if (unit_speed[u] >= slowest && !is_unit_fired(u)) - result = true - }) - return result -} - -function can_pursuit_fire(verbose) { - let result = false - let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) - if (verbose) - log(`Slowest was ${speed_name[slowest]} unit.`) - for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { - if (unit_speed[u] >= slowest && !is_unit_fired(u)) - result = true - }) - return result -} - function roll_pursuit_fire_imp(who, n, hp) { let speed = unit_speed[who] if (n === 2) { @@ -3727,43 +3743,63 @@ states.pursuit_fire = { inactive: "pursuit fire (fire)", prompt() { view.prompt = `Pursuit Fire.` - let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) - for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { - if (unit_speed[u] >= slowest && !is_unit_fired(u)) - gen_action_unit(u) - }) - // allow saving fire if there are shielded enemy units - if (has_disrupted_enemy_unit(game.pursuit)) - gen_action('next') + let done = true + 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 => { + if (unit_speed[u] >= slowest && !is_unit_fired(u)) { + gen_action_unit(u) + done = false + } + }) + } + if (done) + gen_action('end_fire') + else + gen_action('withhold') }, unit(who) { let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) + roll_pursuit_fire(who, (unit_speed[who] > slowest ? 2 : 1)) set_unit_fired(who) - let done = roll_pursuit_fire(who, (unit_speed[who] > slowest ? 2 : 1)) - if (done || !can_pursuit_fire(false)) - goto_pursuit_hits() }, - next() { + withhold() { goto_pursuit_hits() - } + }, + end_fire() { + goto_pursuit_hits() + }, } states.rout_fire = { inactive: "rout fire (fire)", prompt() { view.prompt = `Pursuit Fire (Rout).` - let slowest = slowest_enemy_unit_speed(game.pursuit) - for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { - if (unit_speed[u] >= slowest && !is_unit_fired(u)) - gen_action_unit(u) - }) + let done = true + 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 => { + if (unit_speed[u] >= slowest && !is_unit_fired(u)) { + gen_action_unit(u) + done = false + } + }) + } + if (done) + gen_action('end_fire') + else + gen_action('withhold') }, unit(who) { let slowest = slowest_enemy_unit_speed(game.pursuit) + roll_rout_fire(who, (unit_speed[who] > slowest ? 2 : 1)) set_unit_fired(who) - let done = roll_rout_fire(who, (unit_speed[who] > slowest ? 2 : 1)) - if (done || !can_rout_fire(false)) - goto_rout_hits() + }, + withhold() { + goto_rout_hits() + }, + end_fire() { + goto_rout_hits() }, } @@ -3795,7 +3831,7 @@ function gen_pursuit_hits(normal_steps, elite_steps, iterate) { states.pursuit_hits = { inactive: "pursuit fire (hits)", prompt() { - view.prompt = `Pursuit Fire: Apply ${game.hits} hits.` + view.prompt = "Pursuit Fire: " + format_allocate_hits() + "." let normal_steps = count_normal_steps_in_pursuit() let elite_steps = count_elite_steps_in_pursuit() gen_pursuit_hits(normal_steps, elite_steps, for_each_undisrupted_friendly_unit_in_hex) @@ -3813,7 +3849,7 @@ states.pursuit_hits = { states.rout_hits = { inactive: "rout fire (hits)", prompt() { - view.prompt = `Pursuit Fire (Rout): Apply ${game.hits} hits.` + view.prompt = "Pursuit Fire: " + format_allocate_hits() + "." let normal_steps = count_normal_steps_in_rout() let elite_steps = count_elite_steps_in_rout() gen_pursuit_hits(normal_steps, elite_steps, for_each_friendly_unit_in_hex) @@ -4270,6 +4306,7 @@ states.spending_bps = { }, refit() { log(`Returned for Refit.`) + hide_unit(game.selected) set_unit_hex(pop_selected(), friendly_refit()) }, hex(to) { @@ -4279,6 +4316,7 @@ states.spending_bps = { game.selected = -1 } else if (to === friendly_refit()) { log(`Returned for Refit.`) + hide_unit(game.selected) set_unit_hex(pop_selected(), friendly_refit()) } else { search_redeploy(from) @@ -4299,6 +4337,7 @@ states.spending_bps = { game.buildup.tobruk-- } set_unit_hex(who, to) + hide_unit(who) } }, end_buildup() { @@ -5090,6 +5129,7 @@ exports.setup = function (seed, scenario, options) { allied_bps: 0, units: new Array(unit_count).fill(0), + revealed: [], moved: [], fired: [], recover: [], @@ -5154,6 +5194,7 @@ exports.view = function(state, current) { view = { month: game.month, units: game.units, + revealed: game.revealed, moved: game.moved, fortress: game.fortress, axis_hand: game.axis_hand[REAL] + game.axis_hand[DUMMY], |