diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 305 |
1 files changed, 198 insertions, 107 deletions
@@ -3,6 +3,9 @@ // TODO: partial moves during regroup (to allow deciding entry hex-side) // TODO: first_friendly_unit / for_each_friendly_unit +// RULES: can refuse battle from hex with mixed undisrupted and disrupted units? +// assume yes, followed by rout + // unit state: location (8 bits), supply source (3 bits), steps (2 bits), disrupted (1 bit) const max = Math.max @@ -491,6 +494,131 @@ function claim_hex_control_for_defender(a) { }) } +// === ITERATORS === + +function for_each_adjacent_hex(here, fn) { + for (let s = 0; s < 6; ++s) { + let next = here + hexnext[s] + if (is_map_hex(next)) + fn(next) + } +} + +function for_each_hex_and_adjacent_hex(here, fn) { + fn(here) + for (let s = 0; s < 6; ++s) { + let next = here + hexnext[s] + if (is_map_hex(next)) + fn(next) + } +} + +function for_each_friendly_unit_in_hex(x, fn) { + // TODO: first/last_enemy_unit + for (let u = 0; u < units.length; ++u) + if (is_friendly_unit(u) && unit_hex(u) === x) + fn(u) +} + +function for_each_undisrupted_friendly_unit_in_hex(x, fn) { + // TODO: first/last_enemy_unit + for (let u = 0; u < units.length; ++u) + if (is_friendly_unit(u) && !is_unit_disrupted(u) && unit_hex(u) === x) + fn(u) +} + +function for_each_enemy_unit_in_hex(x, fn) { + // TODO: first/last_enemy_unit + for (let u = 0; u < units.length; ++u) + if (is_enemy_unit(u) && unit_hex(u) === x) + fn(u) +} + +function for_each_undisrupted_enemy_unit_in_hex(x, fn) { + // TODO: first/last_enemy_unit + for (let u = 0; u < units.length; ++u) + if (is_enemy_unit(u) && !is_unit_disrupted(u) && unit_hex(u) === x) + fn(u) +} + +function count_normal_steps_in_battle() { + let steps = [ 0, 0, 0, 0 ] + for_each_undisrupted_enemy_unit_in_hex(game.battle, u => { + if (!is_unit_elite(u)) + steps[unit_class(u)] += unit_steps(u) + }) + return steps +} + +function count_elite_steps_in_battle() { + let steps = [ 0, 0, 0, 0 ] + for_each_undisrupted_enemy_unit_in_hex(game.battle, u => { + if (is_unit_elite(u)) + steps[unit_class(u)] += unit_steps(u) + }) + return steps +} + +function count_hp_in_battle() { + let hp = [ 0, 0, 0, 0 ] + for_each_undisrupted_enemy_unit_in_hex(game.battle, u => { + hp[unit_class(u)] += unit_hp(u) + }) + return hp +} + +function count_normal_steps_in_pursuit() { + let steps = 0 + for_each_undisrupted_enemy_unit_in_hex(game.pursuit, u => { + if (!is_unit_elite(u)) + steps += unit_steps(u) + }) + return steps +} + +function count_elite_steps_in_pursuit() { + let steps = 0 + for_each_undisrupted_enemy_unit_in_hex(game.pursuit, u => { + if (is_unit_elite(u)) + steps += unit_steps(u) + }) + return steps +} + +function count_hp_in_pursuit() { + let hp = 0 + for_each_undisrupted_enemy_unit_in_hex(game.pursuit, u => { + hp += unit_hp(u) + }) + return hp +} + +function count_normal_steps_in_rout() { + let steps = 0 + for_each_enemy_unit_in_hex(game.pursuit, u => { + if (!is_unit_elite(u)) + steps += unit_steps(u) + }) + return steps +} + +function count_elite_steps_in_rout() { + let steps = 0 + for_each_enemy_unit_in_hex(game.pursuit, u => { + if (is_unit_elite(u)) + steps += unit_steps(u) + }) + return steps +} + +function count_hp_in_rout() { + let hp = 0 + for_each_enemy_unit_in_hex(game.pursuit, u => { + hp += unit_hp(u) + }) + return hp +} + // === SUPPLY CARDS === function draw_supply_card(pile) { @@ -995,51 +1123,6 @@ function adjacent_hex_has_undisrupted_friendly_unit(here) { return false } -function for_each_adjacent_hex(here, fn) { - for (let s = 0; s < 6; ++s) { - let next = here + hexnext[s] - if (is_map_hex(next)) - fn(next) - } -} - -function for_each_hex_and_adjacent_hex(here, fn) { - fn(here) - for (let s = 0; s < 6; ++s) { - let next = here + hexnext[s] - if (is_map_hex(next)) - fn(next) - } -} - -function for_each_friendly_unit_in_hex(x, fn) { - // TODO: first/last_enemy_unit - for (let u = 0; u < units.length; ++u) - if (is_friendly_unit(u) && unit_hex(u) === x) - fn(u) -} - -function for_each_undisrupted_friendly_unit_in_hex(x, fn) { - // TODO: first/last_enemy_unit - for (let u = 0; u < units.length; ++u) - if (is_friendly_unit(u) && !is_unit_disrupted(u) && unit_hex(u) === x) - fn(u) -} - -function for_each_enemy_unit_in_hex(x, fn) { - // TODO: first/last_enemy_unit - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === x) - fn(u) -} - -function for_each_undisrupted_enemy_unit_in_hex(x, fn) { - // TODO: first/last_enemy_unit - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && !is_unit_disrupted(u) && unit_hex(u) === x) - fn(u) -} - function max_speed_of_undisrupted_friendly_unit_in_hex(from) { let max = 0 for_each_undisrupted_friendly_unit_in_hex(from, u => { @@ -1076,11 +1159,41 @@ function set_passive_player() { // Supply check // Turn option + // Movement +// declare moves +// normal moves +// rout +// retreats +// declare full/partial retreats +// probe combat +// pursuit fire +// withdraw +// rout +// pursuit fire +// withdraw +// forced marches +// rout +// pursuit fire +// withdraw +// refuse battle +// pursuit fire +// withdraw +// rout +// pursuit fire +// withdraw // Combat +// declare active +// declare assault +// resolve +// defensive fire +// rout +// offensive fire +// rout // Blitz Movement // Blitz Combat // Final supply check +// rout // Reveal supply cards -> next player function end_player_turn() { @@ -1630,6 +1743,7 @@ states.refuse_battle = { prompt() { view.prompt = `You may Refuse Battle.` for (let x of game.active_battles) + // TODO: check if there are possible retreat paths gen_action_hex(x) gen_action('next') }, @@ -1698,6 +1812,15 @@ states.refuse_battle_to = { function goto_combat_phase() { clear_undo() set_active_player() + if (game.active_battles.length > 0) + return goto_select_battles() + for (let x of all_hexes) + if (is_battle_hex(x)) + return goto_select_battles() + end_combat_phase() +} + +function goto_select_battles() { game.state = 'select_active_battles' } @@ -1717,10 +1840,14 @@ states.select_active_battles = { }, next() { push_undo() - if (game.turn_option === 'assault') - game.state = 'select_assault_battles' - else - game.state = 'select_battle' + if (game.active_battles.length > 0) { + if (game.turn_option === 'assault') + game.state = 'select_assault_battles' + else + game.state = 'select_battle' + } else { + end_combat_phase() + } } } @@ -1753,17 +1880,12 @@ states.select_battle = { view.assault_battles = game.assault_battles for (let x of game.active_battles) gen_action_hex(x) - if (game.active_battles.length === 0) - gen_action('end_combat') }, hex(x) { clear_undo() game.battle = x goto_defensive_fire() }, - end_combat() { - end_combat_phase() - } } function goto_defensive_fire() { @@ -1807,54 +1929,6 @@ const xxx_fire = { }, } -function count_normal_steps_in_battle() { - let steps = [ 0, 0, 0, 0 ] - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === game.battle) - steps[unit_class(u)] += unit_steps(u) - return steps -} - -function count_elite_steps_in_battle() { - let steps = [ 0, 0, 0, 0 ] - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === game.battle) - steps[unit_class(u)] += unit_steps(u) - return steps -} - -function count_hp_in_battle() { - let hp = [ 0, 0, 0, 0 ] - for_each_undisrupted_enemy_unit_in_hex(game.battle, u => { - hp[unit_class(u)] += unit_hp(u) - }) - return hp -} - -function count_normal_steps_in_pursuit() { - let steps = 0 - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === game.pursuit) - steps += unit_steps(u) - return steps -} - -function count_elite_steps_in_pursuit() { - let steps = 0 - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === game.pursuit) - steps += unit_steps(u) - return steps -} - -function count_hp_in_pursuit() { - let hp = 0 - for (let u = 0; u < units.length; ++u) - if (is_enemy_unit(u) && unit_hex(u) === game.pursuit) - hp += unit_hp(u) - return hp -} - const xxx_fire_target = { prompt() { view.prompt = `Select a target class.` @@ -1961,7 +2035,7 @@ const xxx_fire_hits = { if (game.state === 'defensive_fire_hits') { goto_offensive_fire() } else { - end_battle() + end_combat() } }, } @@ -2015,13 +2089,16 @@ function goto_fire_hits() { } } -function end_battle() { +function end_combat() { clear_undo() set_active_player() set_delete(game.active_battles, game.battle) set_delete(game.assault_battles, game.battle) - game.state = 'select_battle' game.battle = 0 + if (game.active_battles.length > 0) + game.state = 'select_battle' + else + end_combat_phase() } states.defensive_fire = xxx_fire @@ -2043,8 +2120,11 @@ function end_combat_phase() { function goto_pursuit_fire() { clear_undo() set_active_player() - game.state = 'pursuit_fire' game.hits = 0 + if (can_pursuit_fire()) + game.state = 'pursuit_fire' + else + goto_pursuit_hits() } function slowest_undisrupted_enemy_unit_speed(where) { @@ -2057,6 +2137,17 @@ function slowest_undisrupted_enemy_unit_speed(where) { return r } +function can_pursuit_fire() { + let result = false + let slowest = slowest_undisrupted_enemy_unit_speed(game.pursuit) + log(`Slowest enemy speed is ${slowest}.`) + 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(n) { for (let i = 0; i < n; ++i) { let roll = random(6) + 1 @@ -2111,7 +2202,7 @@ states.pursuit_hits = { let elite_steps = count_elite_steps_in_pursuit() let done = true - for_each_friendly_unit_in_hex(game.pursuit, u => { + for_each_undisrupted_friendly_unit_in_hex(game.pursuit, u => { if (is_unit_elite(u)) { if (game.hits >= 2) { gen_action_unit(u) |