summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js160
1 files changed, 138 insertions, 22 deletions
diff --git a/rules.js b/rules.js
index 7cd0ac4..b6f3ee1 100644
--- a/rules.js
+++ b/rules.js
@@ -1,8 +1,25 @@
"use strict"
+// TODO: fortress battles mandatory combat!
+// TODO: claim fortress control
+
+// TODO: FORCED MARCHES
+// TODO: BLITZ TURN
+// TODO: FINAL SUPPLY CHECK
+// TODO: SUPPLY CARDS (playing and revealing and choosing turn option)
+// TODO: BUILDUP
+
// TODO: first_friendly_unit / for_each_friendly_unit
-// TODO: be smart about caching update_supply_networks calls
+// TODO: when is "fired" status cleared?
+
+// TODO: cache_valid, cache_axis, cache_allied (presence and disruption per hex)
+// instead of iterating units in is_axis_hex, etc.
+// invalidate when loading state
+// invalidate when disrupting, recovering, eliminating or moving units
+
+// TODO: cache supply lines
+// same as presence cache
// RULES: disrupted units routed again in second enemy turn, will they still recover?
// assume yes, easy to change (remove from game.recover set if routed)
@@ -573,7 +590,7 @@ function is_new_battle_hex(a) {
}
function claim_hexside_control(side) {
- if (game.active === AXIS) {
+ if (is_axis_player()) {
set_add(game.axis_sides, side)
set_delete(game.allied_sides, side)
} else {
@@ -598,7 +615,7 @@ function release_hex_control(a) {
function claim_hex_control_for_defender(a) {
// a new battle hex: claim hex and hexsides for defender
- if (game.active === AXIS)
+ if (is_axis_player())
set_add(game.allied_hexes, a)
else
set_add(game.axis_hexes, a)
@@ -606,7 +623,7 @@ function claim_hex_control_for_defender(a) {
for_each_adjacent_hex(a, b => {
let side = to_side_id(a, b)
if (side_limit[side] > 0) {
- if (game.active === AXIS) {
+ if (is_axis_player()) {
if (!set_has(game.axis_sides, side))
set_add(game.allied_sides, side)
} else {
@@ -617,6 +634,27 @@ function claim_hex_control_for_defender(a) {
})
}
+function capture_fortress(fortress, capacity, control_prop, captured_prop) {
+ if (game[control_prop] !== game.active) {
+ if (has_undisrupted_friendly_unit(fortress) && !has_enemy_unit(fortress)) {
+ log(`Captured #${fortress}!`)
+ game[control_prop] = game.active
+ if (!game[captured_prop]) {
+ game[captured_prop] = 1
+ if (is_axis_player()) {
+ let award = capacity
+ log(`Awarded ${award} supply cards.`)
+ deal_axis_supply_cards(award)
+ } else {
+ let award = Math.floor(capacity / 2)
+ log(`Awarded ${award} supply cards.`)
+ deal_allied_supply_cards(award)
+ }
+ }
+ }
+ }
+}
+
// === ITERATORS ===
function for_each_adjacent_hex(here, fn) {
@@ -1124,8 +1162,8 @@ function search_move_retreat(start, speed) {
function search_withdraw(start, speed) {
update_supply_networks()
- let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line
- let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
+ let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line
+ let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
search_init()
search_move_bfs(path_from[0], path_cost[0], start, 0, speed, false, sline, sdist)
@@ -1136,8 +1174,8 @@ function search_withdraw(start, speed) {
function search_withdraw_retreat(start, speed) {
update_supply_networks()
- let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line
- let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
+ let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line
+ let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
search_init()
search_move_bfs(path_from[0], path_cost[0], start, 0, speed, true, sline, sdist)
@@ -1162,7 +1200,7 @@ function search_init() {
// Breadth First Search
function search_move_bfs(from, cost, start, road, max_cost, retreat, sline, sdist) {
- let friendly_sides = (game.active === AXIS) ? game.axis_sides : game.allied_sides
+ let friendly_sides = (is_axis_player()) ? game.axis_sides : game.allied_sides
from.fill(0)
cost.fill(15)
@@ -1325,6 +1363,22 @@ function set_active_player() {
game.active = game.phasing
}
+function is_active_player() {
+ return game.active === game.phasing
+}
+
+function is_passive_player() {
+ return game.active !== game.phasing
+}
+
+function is_axis_player() {
+ return game.active === AXIS
+}
+
+function is_allied_player() {
+ return game.active === ALLIED
+}
+
function set_passive_player() {
if (game.phasing === AXIS)
game.active = ALLIED
@@ -1372,6 +1426,9 @@ function goto_initial_supply_check() {
let snet = game.phasing === AXIS ? game.axis_supply : game.allied_supply
let ssrc = game.phasing === AXIS ? EL_AGHEILA : ALEXANDRIA
+ // TODO: fortress supply
+ // TODO: assign fortress supply
+
for_each_friendly_unit(u => {
let x = unit_hex(u)
if (snet[x]) {
@@ -1390,9 +1447,40 @@ function goto_initial_supply_check() {
set_add(game.recover, u)
})
+ // TODO: check for enemy routs
+
goto_turn_option()
}
+function goto_final_supply_check() {
+ set_active_player()
+
+ capture_fortress(BARDIA, 2, "bardia", "bardia_captured")
+ capture_fortress(BENGHAZI, 2, "benghazi", "benghazi_captured")
+ capture_fortress(TOBRUK, 5, "tobruk", "tobruk_captured")
+
+ update_supply_networks()
+ let snet = game.phasing === AXIS ? game.axis_supply : game.allied_supply
+ let ssrc = game.phasing === AXIS ? EL_AGHEILA : ALEXANDRIA
+
+ // TODO: fortress supply
+ // TODO: assign unused fortress supply
+
+ for_each_friendly_unit(u => {
+ let x = unit_hex(u)
+ if (!snet[x]) {
+ if (!is_unit_disrupted(u) && !is_unit_supplied(u)) {
+ log(`Disrupted at #${x}`)
+ set_unit_disrupted(u)
+ }
+ }
+ })
+
+ // TODO: rout friendly units
+
+ end_player_turn()
+}
+
function goto_turn_option() {
game.state = 'turn_option'
}
@@ -1472,7 +1560,7 @@ states.select_moves = {
},
end_turn() {
clear_undo()
- end_player_turn()
+ goto_final_supply_check()
}
}
@@ -1951,8 +2039,8 @@ states.retreat_from = {
view.prompt = `Retreat: Select hex to retreat from.`
update_supply_networks()
- let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line
- let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
+ let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line
+ let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
if (!game.to1 && game.from1) {
if (can_retreat_with_group_move(game.from1, sline, sdist))
@@ -2173,8 +2261,8 @@ states.refuse_battle = {
view.prompt = `You may Refuse Battle.`
update_supply_networks()
- let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line
- let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
+ let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line
+ let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA]
for (let x of game.active_battles)
if (can_disengage_and_withdraw(x, sline, sdist))
@@ -2459,9 +2547,7 @@ states.select_battle = {
function end_combat_phase() {
// TODO: blitz
- // TODO: final supply check
- // TODO: supply cards revealed
- end_player_turn()
+ goto_final_supply_check()
}
// === BATTLES ===
@@ -2478,9 +2564,32 @@ function is_unit_retreating(u) {
return false
}
+function is_assault_battle() {
+ return set_has(game.assault_battles, game.battle)
+}
+
+function is_fortress_defender() {
+ if ((game.state === 'battle_fire' && is_passive_player()) || (game.state === 'probe_fire' && is_active_player())) {
+ if (game.battle === BENGHAZI)
+ return game.benghazi === game.active
+ if (game.battle === TOBRUK)
+ return game.tobruk === game.active
+ if (game.battle === BARDIA)
+ return game.tobruk === game.active
+ }
+ return false
+}
+
function roll_battle_fire(who, tc) {
let fc = unit_class(who)
let cv = unit_cv(who)
+
+ // Double dice during assault!
+ if (is_assault_battle())
+ cv += cv
+ else if (fc !== ARMOR && is_fortress_defender())
+ cv += cv
+
console.log("FIRE", unit_name(who), cv)
let fp = FIREPOWER_MATRIX[fc][tc]
let result = []
@@ -3010,6 +3119,14 @@ function end_rout_fire() {
goto_rout_move()
}
+// === BUILD-UP ===
+
+function end_month() {
+ delete game.bardia_captured
+ delete game.benghazi_captured
+ delete game.tobruk_captured
+}
+
// === DEPLOYMENT ===
states.free_deployment = {
@@ -3053,8 +3170,8 @@ states.free_deployment = {
},
next() {
clear_undo()
- if (game.active === AXIS) {
- game.active = ALLIED
+ if (is_axis_player()) {
+ set_enemy_player()
log_h2("Allied Deployment")
} else {
end_free_deployment()
@@ -3063,8 +3180,8 @@ states.free_deployment = {
}
function end_free_deployment() {
- game.phasing = game.first_player_turn
- game.active = game.phasing
+ game.phasing = AXIS
+ set_active_player()
let scenario = SCENARIOS[game.scenario]
deal_axis_supply_cards(scenario.axis_initial_supply)
@@ -3546,7 +3663,6 @@ exports.setup = function (seed, scenario, options) {
scenario: scenario,
month: 0,
- first_player_turn: AXIS,
draw_pile: [ DUMMY_SUPPLY_COUNT, REAL_SUPPLY_COUNT ],
axis_hand: [ 0, 0 ],