summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.js1
-rw-r--r--rules.js158
2 files changed, 137 insertions, 22 deletions
diff --git a/play.js b/play.js
index c11217b..7b38c9a 100644
--- a/play.js
+++ b/play.js
@@ -630,6 +630,7 @@ function on_update() {
action_button("rommel", "Rommel")
action_button("eliminate", "Eliminate")
+ action_button("overrun", "Overrun")
action_button("retreat", "Retreat")
action_button("probe", "Probe")
diff --git a/rules.js b/rules.js
index e0745b2..a6c7f06 100644
--- a/rules.js
+++ b/rules.js
@@ -452,6 +452,13 @@ function has_undisrupted_axis_unit(x) {
return false
}
+function has_disrupted_axis_unit(x) {
+ for (let u = first_axis_unit; u <= last_axis_unit; ++u)
+ if (is_unit_disrupted(u) && unit_hex(u) === x)
+ return true
+ return false
+}
+
function has_undisrupted_allied_unit(x) {
for (let u = first_allied_unit; u <= last_allied_unit; ++u)
if (!is_unit_disrupted(u) && unit_hex(u) === x)
@@ -459,6 +466,37 @@ function has_undisrupted_allied_unit(x) {
return false
}
+function has_disrupted_allied_unit(x) {
+ for (let u = first_allied_unit; u <= last_allied_unit; ++u)
+ if (is_unit_disrupted(u) && unit_hex(u) === x)
+ return true
+ return false
+}
+
+function has_unshielded_disrupted_axis_unit(x) {
+ let undisrupted = false
+ let disrupted = false
+ for (let u = first_axis_unit; u <= last_axis_unit; ++u)
+ if (unit_hex(u) === x)
+ if (is_unit_disrupted(u))
+ disrupted = true
+ else
+ undisrupted = true
+ return disrupted && !undisrupted
+}
+
+function has_unshielded_disrupted_allied_unit(x) {
+ let undisrupted = false
+ let disrupted = false
+ for (let u = first_allied_unit; u <= last_allied_unit; ++u)
+ if (unit_hex(u) === x)
+ if (is_unit_disrupted(u))
+ disrupted = true
+ else
+ undisrupted = true
+ return disrupted && !undisrupted
+}
+
function is_axis_hex(x) {
let has_axis = has_axis_unit(x)
let has_allied = has_allied_unit(x)
@@ -507,6 +545,16 @@ function has_undisrupted_enemy_unit(x) {
return has_undisrupted_allied_unit(x)
}
+function has_unshielded_disrupted_enemy_unit(x) {
+ if (game.active === ALLIED)
+ return has_unshielded_disrupted_axis_unit(x)
+ return has_unshielded_disrupted_allied_unit(x)
+}
+
+function is_overrun_hex(x) {
+ return has_undisrupted_friendly_unit(x) && has_unshielded_disrupted_enemy_unit(x)
+}
+
function is_new_battle_hex(a) {
if (is_battle_hex(a))
return !set_has(game.axis_hexes, a) && !set_has(game.allied_hexes, a)
@@ -1578,7 +1626,19 @@ states.move = {
if (has_retreat_hex)
gen_action('retreat')
- gen_action('end_move')
+ // Overrun
+ let has_overrun_hex = false
+ for (let x of all_hexes) {
+ if (is_overrun_hex(x)) {
+ has_overrun_hex = true
+ break
+ }
+ }
+
+ if (has_overrun_hex)
+ gen_action('overrun')
+ else
+ gen_action('end_move')
} else {
// Deselect
@@ -1603,6 +1663,22 @@ states.move = {
log_br()
game.state = 'retreat_from'
},
+ overrun() {
+ let n = 0
+ let where = 0
+ for (let x of all_hexes) {
+ if (is_overrun_hex(x)) {
+ n ++
+ where = x
+ }
+ }
+ if (n === 1) {
+ goto_overrun(where)
+ } else {
+ push_undo()
+ game.state = 'overrun'
+ }
+ },
end_move() {
clear_undo()
log_br()
@@ -1610,6 +1686,24 @@ states.move = {
}
}
+states.overrun = {
+ prompt() {
+ view.prompt = `Overrun!`
+ for (let x of all_hexes)
+ if (is_overrun_hex(x))
+ gen_action_hex(x)
+ },
+ hex(where) {
+ goto_overrun(where)
+ },
+}
+
+function goto_overrun(where) {
+ // return to move state afterwards
+ game.state = 'move'
+ goto_rout(where, true)
+}
+
function gen_move(search_fn) {
let rommel1 = (game.rommel === 1) ? 1 : 0
let rommel2 = (game.rommel === 2) ? 1 : 0
@@ -1942,7 +2036,7 @@ states.refuse_battle_move = {
view.prompt = `Refuse Battle: Withdraw units.`
if (game.selected < 0) {
let done = true
- for_each_undisrupted_friendly_unit_in_hex(game.from1, u => {
+ for_each_undisrupted_friendly_unit_in_hex(game.refuse, u => {
gen_action_unit(u)
done = false
})
@@ -1951,9 +2045,9 @@ states.refuse_battle_move = {
} else {
let speed = unit_speed(game.selected)
gen_action_unit(game.selected)
- search_withdraw_retreat(game.from1, speed)
+ search_withdraw_retreat(game.refuse, speed)
for (let to of all_hexes)
- if (to != game.from1 && can_move_to(to, speed))
+ if (to != game.refuse && can_move_to(to, speed))
gen_action_hex(to)
}
},
@@ -1969,8 +2063,8 @@ states.refuse_battle_move = {
},
end_retreat() {
clear_undo()
- release_hex_control(game.from1)
- game.from1 = 0
+ release_hex_control(game.refuse)
+ game.refuse = 0
goto_refuse_battle()
}
}
@@ -1982,7 +2076,7 @@ states.refuse_battle_move = {
// withdraw by group move
// eliminated if cannot
-function goto_rout(from, side) {
+function goto_rout(from, enemy) {
// save old state so we can recover
game.rout = {
state: game.state,
@@ -1991,16 +2085,17 @@ function goto_rout(from, side) {
attrition: [],
}
- if (side !== game.active)
+ if (enemy)
set_enemy_player()
game.pursuit = from
game.state = 'rout_attrition'
+ game.flash = "Rout attrition!"
}
states.rout_attrition = {
prompt() {
- view.prompt = "Rout: Suffer attrition!"
+ view.prompt = "Rout: All units lose one step of rout attrition."
for_each_friendly_unit_in_hex(game.rout.from, u => {
if (!set_has(game.rout.attrition, u))
gen_action_unit(u)
@@ -2015,23 +2110,30 @@ states.rout_attrition = {
done = false
})
if (done) {
- goto_pursuit_fire_during_rout()
delete game.rout.attrition
+ goto_rout_fire(game.rout.from)
}
},
}
+function goto_rout_move() {
+ if (has_friendly_unit(game.rout.from))
+ game.state = 'rout_move'
+ else
+ end_rout()
+}
+
states.rout_move = {
prompt() {
view.prompt = `Rout: Withdraw units.`
if (game.selected < 0) {
let done = true
- for_each_friendly_unit_in_hex(game.from1, u => {
+ for_each_friendly_unit_in_hex(game.rout.from, u => {
gen_action_unit(u)
done = false
})
if (done)
- gen_action('end_retreat')
+ gen_action('end_rout')
} else {
let speed = unit_speed(game.selected)
let eliminate = true
@@ -2063,14 +2165,20 @@ states.rout_move = {
set_unit_disrupted(who)
},
end_rout() {
- clear_undo()
- game.state = game.rout.state
- if (game.active !== game.rout.active)
- set_active_enemy()
- delete game.rout
+ end_rout()
}
}
+function end_rout() {
+ clear_undo()
+ game.state = game.rout.state
+ release_hex_control(game.rout.from)
+ set_delete(game.active_battles, game.rout.from)
+ if (game.active !== game.rout.active)
+ set_enemy_player()
+ delete game.rout
+}
+
// ==== COMBAT PHASE ===
function goto_combat_phase() {
@@ -2469,6 +2577,7 @@ states.probe_hits = {
function goto_rout_fire(where) {
clear_undo()
+ set_enemy_player()
game.hits = 0
game.pursuit = where
if (can_rout_fire(true))
@@ -2690,12 +2799,12 @@ states.rout_hits = {
},
next() {
clear_undo()
- end_pursuit_fire()
+ end_rout_fire()
},
}
function end_pursuit_fire() {
- game.from1 = game.pursuit
+ game.flash = ""
game.pursuit = 0
if (game.retreat) {
game.state = 'retreat_move'
@@ -2705,9 +2814,9 @@ function end_pursuit_fire() {
}
function end_rout_fire() {
- game.from1 = game.pursuit
+ game.flash = ""
game.pursuit = 0
- game.state = 'rout_move'
+ goto_rout_move()
}
// === DEPLOYMENT ===
@@ -3293,6 +3402,12 @@ exports.setup = function (seed, scenario, options) {
retreat: 0,
retreat_units: null,
+ // refuse battle
+ refuse: 0,
+
+ // rout
+ rout: null,
+
// combat
active_battles: [],
assault_battles: [],
@@ -3327,7 +3442,6 @@ exports.view = function(state, current) {
// allied_supply_line: game.allied_supply_line,
}
- if (game.rommel) view.rommel = game.rommel
if (game.from1) view.from1 = game.from1
if (game.from2) view.from2 = game.from2
if (game.to1) view.to1 = game.to1