summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.html2
-rw-r--r--play.js3
-rw-r--r--rules.js183
3 files changed, 153 insertions, 35 deletions
diff --git a/play.html b/play.html
index fc55ec3..ae183d7 100644
--- a/play.html
+++ b/play.html
@@ -484,7 +484,7 @@ svg .side.allied_supply.axis_supply {
}
.unit.disrupted {
- border-color: dimgray;
+ border-color: black;
}
.unit.unsupplied {
diff --git a/play.js b/play.js
index fa9bd19..43ba4bc 100644
--- a/play.js
+++ b/play.js
@@ -883,6 +883,9 @@ function on_update() {
action_button("end_buildup", "End buildup")
action_button("end_turn", "End turn")
+ confirm_action_button("confirm_end_turn", "End turn",
+ "End turn without performing any moves?")
+
action_button("undo", "Undo")
}
diff --git a/rules.js b/rules.js
index 4feba56..6dae240 100644
--- a/rules.js
+++ b/rules.js
@@ -1445,7 +1445,6 @@ function tobruk_supply_network() {
}
function unit_supply_line(who) {
- // TODO: allow oasis supplied units to trace to base?
switch (unit_supply(who)) {
case SS_BARDIA: return bardia_supply_line()
case SS_BENGHAZI: return benghazi_supply_line()
@@ -1457,7 +1456,6 @@ function unit_supply_line(who) {
}
function unit_supply_distance(who) {
- // TODO: allow oasis supplied units to trace to base?
switch (unit_supply(who)) {
case SS_BARDIA: return distance_to[BARDIA]
case SS_BENGHAZI: return distance_to[BENGHAZI]
@@ -1480,6 +1478,32 @@ function friendly_supply_network() {
return allied_supply_network()
}
+function query_friendly_supply_network(x, y) {
+ // TODO: fortress supply
+ let save_x
+ let save_y
+ if (is_axis_player()) {
+ init_trace_supply(supply_temp_network, supply_temp_line, AXIS)
+ if (x) save_x = presence_axis[x]
+ if (y) save_y = presence_axis[y]
+ if (x) presence_axis[x] = 0
+ if (y) presence_axis[y] = 1
+ trace_supply_network(EL_AGHEILA)
+ if (x) presence_axis[x] = save_x
+ if (y) presence_axis[y] = save_y
+ } else {
+ init_trace_supply(supply_temp_network, supply_temp_line, ALLIED)
+ if (x) save_x = presence_allied[x]
+ if (y) save_y = presence_allied[y]
+ if (x) presence_allied[x] = 0
+ if (y) presence_allied[y] = 1
+ trace_supply_network(ALEXANDRIA)
+ if (x) presence_allied[x] = save_x
+ if (y) presence_allied[y] = save_y
+ }
+ return supply_temp_network
+}
+
// === PATHING ===
const path_from = [ new Array(hexcount), new Array(hexcount), new Array(hexcount), null, new Array(hexcount) ]
@@ -1762,6 +1786,41 @@ function find_valid_regroup_destinations(from, rommel) {
}
}
+// === WITHDRAWAL CHECKS ===
+
+function is_network_reduced(reference, candidate) {
+ for (let x = first_hex; x <= last_hex; ++x)
+ if (reference[x] - candidate[x] === 1)
+ return true
+ return false
+}
+
+function is_legal_withdrawal_from(x) {
+ if (is_battle_hex(x)) {
+ // can retreat, will always reduce supply network
+ return can_all_units_disengage_and_withdraw(x);
+ } else {
+ // non-retreat withdrawal, check if network is reduced after we leave this hex
+ let ref_net = friendly_supply_network()
+ let new_net = query_friendly_supply_network(x, 0)
+ if (is_network_reduced(ref_net, new_net))
+ return true
+ }
+ return false
+}
+
+function is_legal_withdrawal_to(from, to) {
+ if (is_battle_hex(from)) {
+ return true
+ } else {
+ let ref_net = friendly_supply_network()
+ let new_net = query_friendly_supply_network(from, to)
+ if (is_network_reduced(ref_net, new_net))
+ return true
+ }
+ return false
+}
+
// === MINEFIELDS ===
function visit_hex(x) {
@@ -2359,8 +2418,7 @@ states.select_moves = {
view.prompt = `Designate ${game.turn_option} move.`
}
gen_action('group')
- if (game.turn_option !== 'pass')
- gen_action('regroup') // TODO: needs work...
+ gen_action('regroup')
if (game.turn_option === 'pass')
gen_action('end_turn')
},
@@ -2376,6 +2434,9 @@ states.select_moves = {
clear_undo()
reveal_visited_minefields()
goto_final_supply_check()
+ },
+ confirm_end_turn() {
+ this.end_turn()
}
}
@@ -2389,15 +2450,36 @@ states.group_move_from = {
prompt() {
view.prompt = `Group Move: Select hex to move from.`
gen_rommel_move()
- for (let x of all_hexes) {
- if (x === game.from1 && !game.to1)
- continue
- if (has_undisrupted_friendly_unit(x))
- gen_action_hex(x)
- }
- if (game.turn_option !== 'pass')
+ if (game.turn_option !== 'pass') {
+ for (let x of all_hexes) {
+ if (x === game.from1 && !game.to1)
+ continue
+ if (has_undisrupted_friendly_unit(x))
+ gen_action_hex(x)
+ }
if (has_friendly_unit_in_raw_hex(friendly_queue()))
gen_action_hex(friendly_queue())
+ } else {
+ // Withdrawal group move
+ let mandatory = false
+ if (is_mandatory_combat(BARDIA)) {
+ gen_action_hex(BARDIA)
+ mandatory = true
+ }
+ if (is_mandatory_combat(BENGHAZI)) {
+ gen_action_hex(BENGHAZI)
+ mandatory = true
+ }
+ if (is_mandatory_combat(TOBRUK)) {
+ gen_action_hex(TOBRUK)
+ mandatory = true
+ }
+ if (!mandatory) {
+ for (let x of all_hexes)
+ if (has_undisrupted_friendly_unit(x) && is_legal_withdrawal_from(x))
+ gen_action_hex(x)
+ }
+ }
},
rommel() {
push_undo()
@@ -2432,13 +2514,18 @@ states.regroup_move_command_point = {
prompt() {
view.prompt = `Regroup Move: Designate the command point hex.`
gen_rommel_move()
- for (let x of all_hexes) {
- if (!is_enemy_hex(x)) {
- let n = count_hex_or_adjacent_has_undisrupted_and_unmoved_friendly_unit(x)
- // TODO: allow one-hex regroup moves? (failed forced march abuse)
- if (n >= 2)
- gen_action_hex(x)
+ if (game.turn_option !== 'pass') {
+ for (let x of all_hexes) {
+ if (!is_enemy_hex(x)) {
+ let n = count_hex_or_adjacent_has_undisrupted_and_unmoved_friendly_unit(x)
+ // TODO: allow one-hex regroup moves? (failed forced march abuse)
+ if (n >= 2)
+ gen_action_hex(x)
+ }
}
+ } else {
+ // TODO: Withdrawal regroup moves
+ view.prompt = "TODO"
}
},
rommel() {
@@ -2604,8 +2691,14 @@ states.move = {
if (has_overrun_hex)
gen_action('overrun')
- else
- gen_action('end_move')
+ else {
+ if (game.turn_option !== 'pass') {
+ gen_action('end_move')
+ } else {
+ if (!game.to1 && !has_friendly_unit(game.from1))
+ gen_action('end_move')
+ }
+ }
} else {
view.prompt = `Move: Select hex to move to.`
@@ -2613,12 +2706,13 @@ states.move = {
gen_action_unit(game.selected)
// Move
- if (game.turn_option === 'pass')
- search_withdraw(game.selected, 1 + (rommel1 | rommel2))
- else
+ if (game.turn_option !== 'pass') {
search_move(unit_hex(game.selected), unit_speed[game.selected] + 1 + (rommel1 | rommel2))
-
- gen_move()
+ gen_move()
+ } else {
+ search_withdraw(game.selected, 1 + rommel1)
+ gen_withdraw()
+ }
}
},
unit(who) {
@@ -2722,6 +2816,30 @@ function gen_move() {
}
}
+function gen_withdraw() {
+ let rommel1 = (game.rommel === 1) ? 1 : 0
+ let speed = unit_speed[game.selected]
+ let from = unit_hex(game.selected)
+
+ if (!game.to1 && game.from1 === from) {
+ for (let to of all_hexes) {
+ if (to != from) {
+ if (can_move_to(to, speed + rommel1) && is_legal_withdrawal_to(game.from1, to))
+ gen_action_hex(to)
+ else if (can_move_to(to, speed + 1 + rommel1) && is_legal_withdrawal_to(game.from1, to))
+ gen_action_forced_march(to)
+ }
+ }
+ }
+
+ if (game.to1 && is_hex_or_adjacent_to(from, game.from1)) {
+ if (can_move_to(game.to1, speed + rommel1))
+ gen_action_hex(game.to1)
+ else if (can_move_to(game.to1, speed + 1 + rommel1))
+ gen_action_forced_march(game.to1)
+ }
+}
+
function apply_move(to) {
let rommel1 = (game.rommel === 1) ? 1 : 0
let rommel2 = (game.rommel === 2) ? 1 : 0
@@ -3426,7 +3544,7 @@ function end_retreat_2() {
function can_select_refuse_battle_hex() {
for (let x of game.active_battles)
- if (can_all_refuse_battle(x))
+ if (can_all_units_disengage_and_withdraw(x))
return true
return false
}
@@ -3440,25 +3558,21 @@ function goto_refuse_battle() {
}
}
-function can_all_refuse_battle(from) {
+function can_all_units_disengage_and_withdraw(from) {
let result = true
for_each_undisrupted_friendly_unit_in_hex(from, u => {
- if (result === true && !can_unit_refuse_battle(u))
+ if (result === true && !can_unit_disengage_and_withdraw(u))
result = false
})
return result
}
-function can_unit_refuse_battle(who) {
- return can_unit_disengage_and_withdraw(who)
-}
-
states.refuse_battle = {
inactive: "refuse battle",
prompt() {
view.prompt = `You may Refuse Battle.`
for (let x of game.active_battles)
- if (can_all_refuse_battle(x))
+ if (can_all_units_disengage_and_withdraw(x))
gen_action_hex(x)
gen_action('pass')
},
@@ -3538,7 +3652,8 @@ function end_refuse_battle_move_2() {
// withdraw by group move
// eliminated if cannot
-function can_rout_from_hex(from) {
+function can_disengage_from(from) {
+ // TODO: check supply lines too...
let n = 0
for_each_adjacent_hex(from, to => {
let side = to_side_id(from, to)
@@ -3575,7 +3690,7 @@ function goto_rout(from, enemy, after) {
set_delete(game.recover, u)
})
- if (can_rout_from_hex(from))
+ if (can_all_units_disengage_and_withdraw(from))
game.state = 'rout_attrition'
else
game.state = 'rout_elimination'