summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rules.js305
1 files changed, 198 insertions, 107 deletions
diff --git a/rules.js b/rules.js
index a500baf..bd86bee 100644
--- a/rules.js
+++ b/rules.js
@@ -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)