diff options
-rw-r--r-- | play.js | 69 | ||||
-rw-r--r-- | rules.js | 637 |
2 files changed, 504 insertions, 202 deletions
@@ -1,7 +1,5 @@ "use strict" -// TODO: intermediate regroup moves - // https://www.redblobgames.com/grids/hexagons/ const svgNS = "http://www.w3.org/2000/svg" @@ -16,6 +14,13 @@ const hex_special = [ 48, 49, 102, 127, MALTA ] const unit_count = 94 +const SS_NONE = 0 +const SS_BASE = 1 +const SS_BARDIA = 2 +const SS_BENGHAZI = 3 +const SS_TOBRUK = 4 +const SS_OASIS = 5 + const ARMOR = 0 const INFANTRY = 1 const ANTITANK = 2 @@ -29,9 +34,9 @@ function is_allied_unit(u) { return (u >= 34 && u <= 93) } function is_elite_unit(u) { return unit_elite[u] } function is_armor_unit(u) { return unit_class[u] === ARMOR } -function is_infantry_unit(u) { unit_class[u] === INFANTRY } -function is_antitank_unit(u) { unit_class[u] === ANTITANK } -function is_artillery_unit(u) { unit_class[u] === ARTILLERY } +function is_infantry_unit(u) { return unit_class[u] === INFANTRY } +function is_antitank_unit(u) { return unit_class[u] === ANTITANK } +function is_artillery_unit(u) { return unit_class[u] === ARTILLERY } function is_recon_unit(u) { return unit_speed[u] === 4 } function is_mechanized_unit(u) { return unit_speed[u] === 3 } @@ -103,6 +108,10 @@ let ui = { const AXIS = 'Axis' const ALLIED = 'Allied' +function is_map_hex(x) { + return x < hex_exists.length && hex_exists[x] === 1 +} + // === UNIT STATE === const UNIT_DISRUPTED_SHIFT = 0 @@ -121,56 +130,32 @@ function is_unit_disrupted(u) { return (view.units[u] & UNIT_DISRUPTED_MASK) === UNIT_DISRUPTED_MASK } -function set_unit_disrupted(u) { - view.units[u] |= UNIT_DISRUPTED_MASK -} - -function clear_unit_disrupted(u) { - view.units[u] &= ~UNIT_DISRUPTED_MASK -} - function unit_hex(u) { return (view.units[u] & UNIT_HEX_MASK) >> UNIT_HEX_SHIFT } -function set_unit_hex(u, x) { - view.units[u] = (view.units[u] & ~UNIT_HEX_MASK) | (x << UNIT_HEX_SHIFT) -} - function is_unit_supplied(u) { return ((view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT) !== 0 } function unit_supply(u) { - let src = (view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT - return hex_from_supply_source[src] + return (view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT } function is_unit_unsupplied(u) { - return ((view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT) === 0 -} - -function set_unit_supply(u, hex) { - let src = supply_source_from_hex(hex) - view.units[u] = (view.units[u] & ~UNIT_SUPPLY_MASK) | (src << UNIT_SUPPLY_SHIFT) + if (is_map_hex(unit_hex(u))) + return ((view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT) === 0 + return false } function unit_lost_steps(u) { return (view.units[u] & UNIT_STEPS_MASK) >> UNIT_STEPS_SHIFT } -function set_unit_lost_steps(u, n) { - view.units[u] = (view.units[u] & ~UNIT_STEPS_MASK) | (n << UNIT_STEPS_SHIFT) -} - function unit_steps(u) { return unit_start_steps[u] - unit_lost_steps(u) } -function set_unit_steps(u, n) { - set_unit_lost_steps(u, unit_start_steps[u] - n) -} - function is_unit_moved(u) { return set_has(view.moved, u) } @@ -340,13 +325,23 @@ for (let u = 0; u <= 93; ++u) { function on_focus_unit(evt) { let u = evt.target.unit - let t = "(" + u + ")" - t += " " + unit_description[u] - t += " \"" + unit_name[u] + "\"" + let t = "" + if (is_unit_revealed(u)) { + t += " " + unit_description[u] + t += " \"" + unit_name[u] + "\"" + } else { + t = is_allied_unit(u) ? "Allied unit" : "Axis unit" + } if (is_unit_disrupted(u)) t += " - disrupted" if (is_unit_unsupplied(u)) t += " - unsupplied" + else { + if (unit_supply(u) === SS_BARDIA) t += " - Bardia supply" + if (unit_supply(u) === SS_TOBRUK) t += " - Tobruk supply" + if (unit_supply(u) === SS_BENGHAZI) t += " - Benghazi supply" + if (unit_supply(u) === SS_OASIS) t += " - Oasis supply" + } if (is_unit_moved(u)) t += " - moved" if (is_unit_fired(u)) @@ -663,7 +658,7 @@ function update_map() { e.classList.toggle("selected", !view.battle && is_unit_selected(u)) e.classList.toggle("disrupted", is_unit_disrupted(u)) e.classList.toggle("moved", is_unit_moved(u)) - // e.classList.toggle("unsupplied", is_unit_unsupplied(u)) + e.classList.toggle("unsupplied", is_unit_unsupplied(u)) e.classList.toggle("revealed", is_unit_revealed(u)) } @@ -1,8 +1,5 @@ "use strict" -// TODO: fortress supply -// TODO: oasis supply - // TODO: RAIDERS // TODO: MINEFIELDS // TODO: legal pass withdrawal moves (reduce supply net, withdraw from fortress attack) @@ -13,14 +10,19 @@ // TODO: log summaries (deploy, rebuild, move, etc) // TODO: put initial deployment stack somewhere more accessible (spread out along the top?) +// TODO: multi-select deployment + +// RULES: may units redeploying out of battle hex cross enemy controlled hexsides? (yes / doesn't matter) + +// RULES: can fortress supplied units be part of supply lines for non-fortress supplied units for withdrawals to base? +// RULES: can non-fortress supplied units be part of supply lines for fortress supplied units for withdrawals? // RULES: for sea redeployment, can bases be "besieged"? (yes) // RULES: may units redeploying out of battle hex leave disrupted units behind to be routed? (no) -// RULES: may units redeploying out of battle hex cross enemy controlled hexsides? (yes / doesn't matter) // RULES: may units returning for refit enter enemy supply network? (no) // RULES: if disrupted units are routed again during their "full enemy turn", can they still recover? -// RULES: may oasis supplied units refuse battle or withdraw to base? +// RULES: may oasis supplied units refuse battle or withdraw to base? (yes) // RULES: when is "fired" status cleared? // RULES: are minefields moved through (but not stopped at) revealed? @@ -135,25 +137,11 @@ const SIWA_OASIS = 213 const BARDIA_FT_CAPUZZO = 122 const SS_NONE = 0 -const SS_EL_AGHEILA = 1 -const SS_ALEXANDRIA = 2 -const SS_BARDIA = 3 -const SS_BENGHAZI = 4 -const SS_TOBRUK = 5 -const SS_OASIS = 6 - -const hex_from_supply_source = [ 0, EL_AGHEILA, ALEXANDRIA, BARDIA, BENGHAZI, TOBRUK ] - -function supply_source_from_hex(hex) { - switch (hex) { - case 0: return 0 - case EL_AGHEILA: return 1 - case ALEXANDRIA: return 2 - case BARDIA: return 3 - case BENGHAZI: return 4 - case TOBRUK: return 5 - } -} +const SS_BASE = 1 +const SS_BARDIA = 2 +const SS_BENGHAZI = 3 +const SS_TOBRUK = 4 +const SS_OASIS = 5 const region_egypt = regions["Egypt"] const region_egypt_and_libya = regions["Libya"].concat(regions["Egypt"]) @@ -358,6 +346,7 @@ function is_unit_disrupted(u) { } function set_unit_disrupted(u) { + invalidate_caches() game.units[u] |= UNIT_DISRUPTED_MASK } @@ -384,12 +373,10 @@ function is_unit_unsupplied(u) { } function unit_supply(u) { - let src = (game.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT - return hex_from_supply_source[src] + return (game.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT } -function set_unit_supply(u, hex) { - let src = supply_source_from_hex(hex) +function set_unit_supply(u, src) { game.units[u] = (game.units[u] & ~UNIT_SUPPLY_MASK) | (src << UNIT_SUPPLY_SHIFT) } @@ -772,26 +759,38 @@ function capture_fortress(fortress, capacity) { // === FORTRESSES === -const FORTRESS_BIT = { - [BARDIA]: 1, - [BENGHAZI]: 2, - [TOBRUK]: 4, +function fortress_bit(fortress) { + if (fortress === BARDIA) return 1 + if (fortress === BENGHAZI) return 2 + if (fortress === TOBRUK) return 4 + return 0 +} + +function fortress_src(fortress) { + if (fortress === BARDIA) return SS_BARDIA + if (fortress === BENGHAZI) return SS_BENGHAZI + if (fortress === TOBRUK) return SS_TOBRUK + return SS_OASIS +} + +function is_fortress_allied_controlled(fortress) { + return (game.fortress & fortress_bit(fortress)) === 1 } function is_fortress_axis_controlled(fortress) { - return (game.fortress & FORTRESS_BIT[fortress]) === 0 + return (game.fortress & fortress_bit(fortress)) === 0 } function set_fortress_axis_controlled(fortress) { - game.fortress &= ~FORTRESS_BIT[fortress] + game.fortress &= ~fortress_bit(fortress) } function set_fortress_allied_controlled(fortress) { - game.fortress |= FORTRESS_BIT[fortress] + game.fortress |= fortress_bit(fortress) } function set_fortress_captured(fortress) { - let bit = FORTRESS_BIT[fortress] << 3 + let bit = fortress_bit(fortress) << 3 if (game.fortress & bit) return false game.fortress |= bit @@ -818,7 +817,7 @@ function set_fortress_friendly_controlled(fortress) { function is_fortress_besieged(fortress) { let result = false - let besieged = is_fortress_axis_controlled() ? has_allied_unit : has_axis_unit + let besieged = is_fortress_axis_controlled(fortress) ? has_allied_unit : has_axis_unit for_each_adjacent_hex(fortress, x => { if (besieged(x)) result = true @@ -1077,13 +1076,11 @@ function ind(d, msg, here, ...extra) { //console.log(new Array(d).fill("-").join("") + msg, here, "("+hex_name[here]+")", extra.join(" ")) } -var supply_defender, supply_defender_sides, supply_friendly, supply_enemy, supply_net, supply_line +var supply_defender, supply_defender_sides, supply_friendly, supply_enemy, supply_net, supply_line, supply_fortress_enemy var supply_visited = new Array(hexcount).fill(0) var supply_src = new Array(hexcount).fill(0) var trace_total -var trace_highway -var trace_chain function is_supply_line_blocked(here, next, side) { // impassable hexside @@ -1102,6 +1099,11 @@ function is_supply_line_blocked(here, next, side) { } } + // enemy controlled fortresses block supply lines + if (next === BARDIA || next === BENGHAZI || next === TOBRUK) + if (supply_fortress_enemy(next)) + return true + // cannot trace through enemy hexsides if (supply_friendly[here] && supply_enemy[here]) if (!set_has(supply_defender_sides, side)) @@ -1243,7 +1245,7 @@ function trace_supply_chain(here, d, n, range) { return has_supply } -function trace_supply_network(start) { +function trace_supply_network(start, ss) { supply_visited.fill(0) supply_src.fill(0) supply_net.fill(0) @@ -1258,27 +1260,52 @@ function trace_supply_network(start) { trace_supply_chain(x, 0, 0, 3) console.log("SUPPLY VISITS", trace_total) - //debug_hexes("SS", supply_src) - //debug_hexes("SN", supply_net) } -// For repeated supplied hex checks during deployment and fortress assignment -function init_trace_supply_to_base_or_fortress(net, line) { +function trace_fortress_network(fortress, ss) { + supply_visited.fill(0) + supply_src.fill(0) + supply_net.fill(0) + supply_line.fill(0) + + supply_src[fortress] = 1 + supply_net[fortress] = 1 + + trace_total = 0 + for (let u = 0; u < unit_count; ++u) { + let x = unit_hex(u) + if (is_map_hex(x) && unit_supply(u) === ss) { + if (!supply_visited[x]) + trace_supply_chain(x, 0, 0, 3) + } + } + + console.log("FORTRESS SUPPLY VISITS", trace_total) +} + +function init_trace_supply(net, line, friendly) { if (presence_invalid) update_presence() supply_net = net supply_line = line - if (is_axis_player()) { + if (friendly === AXIS) { + supply_fortress_enemy = is_fortress_allied_controlled supply_defender = game.axis_hexes supply_defender_sides = game.axis_sides supply_friendly = presence_axis supply_enemy = presence_allied } else { + supply_fortress_enemy = is_fortress_axis_controlled supply_defender = game.allied_hexes supply_defender_sides = game.allied_sides supply_friendly = presence_allied supply_enemy = presence_axis } +} + +// For repeated supplied hex checks during deployment and fortress assignment +function init_trace_supply_to_base_or_fortress() { + init_trace_supply(supply_temp_network, supply_temp_line, game.active) supply_net.fill(0) } @@ -1291,39 +1318,32 @@ function can_trace_supply_to_base_or_fortress(base, from) { function update_axis_supply() { supply_axis_invalid = false - if (presence_invalid) - update_presence() - - supply_net = supply_axis_network - supply_line = supply_axis_line - supply_defender = game.axis_hexes - supply_defender_sides = game.axis_sides - supply_friendly = presence_axis - supply_enemy = presence_allied - - trace_supply_network(EL_AGHEILA) + init_trace_supply(supply_axis_network, supply_axis_line, AXIS) + trace_supply_network(EL_AGHEILA, 0) } function update_allied_supply() { supply_allied_invalid = false - if (presence_invalid) - update_presence() + init_trace_supply(supply_allied_network, supply_allied_line, ALLIED) + trace_supply_network(ALEXANDRIA, 0) +} - supply_net = supply_allied_network - supply_line = supply_allied_line - supply_defender = game.allied_hexes - supply_defender_sides = game.allied_sides - supply_friendly = presence_allied - supply_enemy = presence_axis +function update_bardia_supply() { + supply_bardia_invalid = false + init_trace_supply(supply_bardia_network, supply_bardia_line, is_fortress_axis_controlled(BARDIA) ? AXIS : ALLIED) + trace_fortress_network(BARDIA, SS_BARDIA) +} - trace_supply_network(ALEXANDRIA) +function update_benghazi_supply() { + supply_benghazi_invalid = false + init_trace_supply(supply_benghazi_network, supply_benghazi_line, is_fortress_axis_controlled(BENGHAZI) ? AXIS : ALLIED) + trace_fortress_network(BENGHAZI, SS_BENGHAZI) } -function update_supply() { - if (supply_axis_invalid) - update_axis_supply() - if (supply_allied_invalid) - update_allied_supply() +function update_tobruk_supply() { + supply_tobruk_invalid = false + init_trace_supply(supply_tobruk_network, supply_tobruk_line, is_fortress_axis_controlled(TOBRUK) ? AXIS : ALLIED) + trace_fortress_network(TOBRUK, SS_TOBRUK) } function axis_supply_line() { @@ -1351,8 +1371,10 @@ function allied_supply_network() { } function bardia_supply_line() { + console.log("X") if (supply_bardia_invalid) update_bardia_supply() + debug_hexes("bardia-line", supply_bardia_line) return supply_bardia_line } @@ -1387,14 +1409,24 @@ function tobruk_supply_network() { } function unit_supply_line(who) { - // TODO: fortress supply + // 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() + case SS_TOBRUK: return tobruk_supply_line() + } if (is_axis_unit(who)) return axis_supply_line() return allied_supply_line() } function unit_supply_distance(who) { - // TODO: fortress supply + // 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] + case SS_TOBRUK: return distance_to[TOBRUK] + } if (is_axis_unit(who)) return distance_to[EL_AGHEILA] return distance_to[ALEXANDRIA] @@ -1828,29 +1860,284 @@ function end_player_turn() { goto_player_turn() } -// === INITIAL & FINAL SUPPLY CHECK === +// === FORTRESS SUPPLY === -function goto_initial_supply_check() { - let snet = friendly_supply_network() - let ssrc = friendly_supply_base() +function union_fortress_network(result, fort) { + for (let i = first_hex; i <= last_hex; ++i) + result[i] |= fort[i] +} - // TODO: fortress supply - // TODO: assign fortress supply +function union_fortress_line(result, fort) { + for (let i = 0; i < sidecount; ++i) + result[i] |= fort[i] +} +function union_axis_network() { + let net = axis_supply_network().slice() + if (net[BARDIA] === 0 && is_fortress_axis_controlled(BARDIA)) + union_fortress_network(net, bardia_supply_network()) + if (net[BENGHAZI] === 0 && is_fortress_axis_controlled(BENGHAZI)) + union_fortress_network(net, benghazi_supply_network()) + if (net[TOBRUK] === 0 && is_fortress_axis_controlled(TOBRUK)) + union_fortress_network(net, tobruk_supply_network()) + return net +} + +function union_allied_network() { + let net = allied_supply_network().slice() + if (net[BARDIA] === 0 && is_fortress_allied_controlled(BARDIA)) + union_fortress_network(net, bardia_supply_network()) + if (net[BENGHAZI] === 0 && is_fortress_allied_controlled(BENGHAZI)) + union_fortress_network(net, benghazi_supply_network()) + if (net[TOBRUK] === 0 && is_fortress_allied_controlled(TOBRUK)) + union_fortress_network(net, tobruk_supply_network()) + return net +} + +function union_axis_line() { + let net = axis_supply_network() + let line = axis_supply_line().slice() + if (net[BARDIA] === 0 && is_fortress_axis_controlled(BARDIA)) + union_fortress_line(line, bardia_supply_line()) + if (net[BENGHAZI] === 0 && is_fortress_axis_controlled(BENGHAZI)) + union_fortress_line(line, benghazi_supply_line()) + if (net[TOBRUK] === 0 && is_fortress_axis_controlled(TOBRUK)) + union_fortress_line(line, tobruk_supply_line()) + return line +} + +function union_allied_line() { + let net = allied_supply_network() + let line = allied_supply_line().slice() + console.log("Xa") + if (net[BARDIA] === 0 && is_fortress_allied_controlled(BARDIA)) + union_fortress_line(line, bardia_supply_line()) + if (net[BENGHAZI] === 0 && is_fortress_allied_controlled(BENGHAZI)) + union_fortress_line(line, benghazi_supply_line()) + if (net[TOBRUK] === 0 && is_fortress_allied_controlled(TOBRUK)) + union_fortress_line(line, tobruk_supply_line()) + return line +} + +const FORTRESS_HEX_LIST = [ BARDIA, BENGHAZI, TOBRUK ] +const FORTRESS_SRC_LIST = [ SS_BARDIA, SS_BENGHAZI, SS_TOBRUK ] + +function all_friendly_unsupplied_units() { + let unsupplied = [] for_each_friendly_unit_on_map(u => { - let x = unit_hex(u) - if (snet[x]) { - set_unit_supply(u, ssrc) - if (is_unit_disrupted(u) && set_has(game.recover, u) && !is_battle_hex(x)) { - log(`Recovered at #${x}`) - set_delete(game.recover, u) - clear_unit_disrupted(u) - } + if (is_unit_unsupplied(u)) + unsupplied.push(u) + }) + return unsupplied +} + +function resume_fortress_supply() { + while (game.assign < 3) { + if (assign_fortress_supply()) + return + game.assign++ + } + end_fortress_supply() +} + +function end_fortress_supply() { + game.assign = 0 + if (game.state === 'initial_fortress_supply') + game.state = 'initial_oasis_supply' + if (game.state === 'final_fortress_supply') + game.state = 'final_oasis_supply' + if (game.state === 'buildup_fortress_supply') + game.state = 'buildup_oasis_supply' + resume_oasis_supply() +} + +function list_fortress_supply_candidates(fortress) { + let dist = distance_to[fortress] + let list = [] + init_trace_supply_to_base_or_fortress() + for_each_friendly_unit_on_map(u => { + if (is_unit_unsupplied(u)) + if (can_trace_supply_to_base_or_fortress(fortress, unit_hex(u))) + list.push(u) + }) + list.sort((a,b) => dist[unit_hex(a)] - dist[unit_hex(b)]) + return list +} + +function auto_assign_fortress_supply(list, fortress, ss, ix) { + let total = 0 + let dist = distance_to[fortress] + while (list.length > 0 && game.capacity[ix] > 0) { + let d0 = dist[unit_hex(list[0])] + let n = 0 + for (let u of list) + if (dist[unit_hex(u)] === d0) + ++n + if (n <= game.capacity[ix]) { + for (let u of list) + if (dist[unit_hex(u)] === d0) + set_unit_supply(u, ss) + game.capacity[ix] -= n + total += n + list = list.slice(n) } else { - set_unit_supply(u, 0) + return true + } + } + return false +} + +function assign_fortress_supply() { + let ix = game.assign + let ss = FORTRESS_SRC_LIST[ix] + let fortress = FORTRESS_HEX_LIST[ix] + let base_net = friendly_supply_network() + + // Isolated friendly fortress with capacity! + if (!base_net[fortress] && is_fortress_friendly_controlled(fortress) && game.capacity[ix] > 0) { + let dist = distance_to[fortress] + let list = list_fortress_supply_candidates(fortress) + if (auto_assign_fortress_supply(list, fortress, ss, ix)) { + return true + } + } + + return false +} + +const xxx_fortress_supply = { + prompt() { + let ix = game.assign + let ss = FORTRESS_SRC_LIST[ix] + let fortress = FORTRESS_HEX_LIST[ix] + view.prompt = `Supply Check: Assign fortress supply to ${hex_name[fortress]} (${game.capacity[ix]} capacity left).` + if (game.capacity[ix] > 0) { + let list = list_fortress_supply_candidates(fortress) + if (list.length > 0) { + let dist = distance_to[fortress] + let d0 = dist[unit_hex(list[0])] + for (let u of list) + if (dist[unit_hex(u)] === d0) + gen_action_unit(u) + } + } + gen_action_next() + }, + unit(who) { + let ix = game.assign + let ss = FORTRESS_SRC_LIST[ix] + push_undo() + game.capacity[ix]-- + set_unit_supply(who, ss) + }, + next() { + push_undo() + game.assign++ + resume_fortress_supply() + }, +} + +states.initial_fortress_supply = xxx_fortress_supply +states.final_fortress_supply = xxx_fortress_supply +states.buildup_fortress_supply = xxx_fortress_supply + +// === OASIS SUPPLY === + +const OASIS_HEX_LIST = [ JALO_OASIS, JARABUB_OASIS, SIWA_OASIS ] + +function resume_oasis_supply() { + while (game.assign < 3) { + if (assign_oasis_supply()) + return + game.assign++ + } + end_oasis_supply() +} + +function end_oasis_supply() { + if (game.state === 'initial_oasis_supply') + goto_initial_supply_check_recover() + if (game.state === 'final_oasis_supply') + goto_final_supply_check_disrupt() + if (game.state === 'buildup_oasis_supply') + end_buildup_supply_check() +} + +function assign_oasis_supply() { + let ix = game.assign + if (game.oasis[ix] > 0) { + let oasis = OASIS_HEX_LIST[ix] + let enemy_battle = is_axis_player() ? game.allied_hexes : game.axis_hexes + if (has_friendly_unit(oasis)) { + if (!set_has(enemy_battle, oasis)) { + let n = count_friendly_units_in_hex(oasis) + if (n > 1) + return true + if (n === 1) { + for_each_friendly_unit_in_hex(oasis, u => { + game.oasis[ix] = 0 + set_unit_supply(u, SS_OASIS) + }) + } + } } + } + return false +} + +const xxx_oasis_supply = { + prompt() { + let ix = game.assign + let oasis = OASIS_HEX_LIST[ix] + view.prompt = `Supply Check: Assign oasis supply to ${hex_name[oasis]}.` + for_each_friendly_unit_in_hex(oasis, u => { + gen_action_unit(u) + }) + }, + unit(who) { + let ix = game.assign + push_undo() + game.oasis[ix] = 0 + set_unit_supply(who, SS_OASIS) + game.assign++ + resume_oasis_supply() + }, +} + +states.initial_oasis_supply = xxx_oasis_supply +states.final_oasis_supply = xxx_oasis_supply +states.buildup_oasis_supply = xxx_oasis_supply + +// === INITIAL SUPPLY CHECK === + +function goto_initial_supply_check() { + let base_net = friendly_supply_network() + for_each_friendly_unit_on_map(u => { + if (base_net[unit_hex(u)]) + set_unit_supply(u, SS_BASE) + else + set_unit_supply(u, SS_NONE) }) + if (is_axis_player()) + game.capacity = [ 1, 1, 2 ] + else + game.capacity = [ 2, 2, 5 ] + game.oasis = [ 1, 1, 1 ] + + game.state = 'initial_fortress_supply' + game.assign = 0 + resume_fortress_supply() +} + +function goto_initial_supply_check_recover() { + for (let u of game.recover) { + if (is_unit_disrupted(u) && is_unit_supplied(u) && !is_battle_hex(unit_hex(u))) { + log(`Recovered at #${unit_hex(u)}`) + clear_unit_disrupted(u) + } + } + set_clear(game.recover) for_each_enemy_unit_on_map(u => { if (is_unit_disrupted(u)) @@ -1888,6 +2175,8 @@ states.initial_supply_check_rout = { } } +// === FINAL SUPPLY CHECK === + function goto_final_supply_check() { set_active_player() @@ -1897,20 +2186,28 @@ function goto_final_supply_check() { capture_fortress(BENGHAZI, 2) capture_fortress(TOBRUK, 5) - let snet = friendly_supply_network() - let ssrc = friendly_supply_base() + game.disrupt = all_friendly_unsupplied_units() - // TODO: fortress supply - // TODO: assign unused fortress supply + // Now in supply! + let base_net = friendly_supply_network() + for (let u of game.disrupt) { + if (base_net[unit_hex(u)]) + set_unit_supply(u, SS_BASE) + } - for_each_friendly_unit_on_map(u => { - let x = unit_hex(u) - if (!snet[x] && !is_unit_disrupted(u) && !is_unit_supplied(u)) { - log(`Disrupted at #${x}`) + game.state = 'final_fortress_supply' + game.assign = 0 + resume_fortress_supply() +} + +function goto_final_supply_check_disrupt() { + for (let u of game.disrupt) { + if (!is_unit_disrupted(u) && !is_unit_supplied(u)) { + log(`Disrupted at #${unit_hex(u)}`) set_unit_disrupted(u) } - }) - + } + delete game.disrupt goto_final_supply_check_rout() } @@ -2244,6 +2541,7 @@ states.move = { } }, end_move() { + push_undo() // XXX clear_undo() log_br() end_movement() @@ -2554,6 +2852,7 @@ states.forced_marches = { gen_action_unit(who) }, unit(who) { + push_undo() // XXX let via = unit_hex(who) let ix = game.forced.findIndex(item => who === item[0]) let to = game.forced[ix][1] @@ -2958,7 +3257,6 @@ function end_retreat() { function end_retreat_2() { if (can_select_retreat_hex()) { - console.log("can_select_retreat_hex") game.state = 'retreat_from' } else @@ -3968,7 +4266,7 @@ function end_rout_fire() { goto_rout_move() } -// === BUILDUP === +// === BUILDUP - SUPPLY CHECK === function end_month() { // Forget captured fortresses (for bonus cards) @@ -3990,6 +4288,7 @@ function goto_buildup() { } function goto_buildup_discard() { + log_br() game.state = 'buildup_discard' let hand = player_hand() if (hand[REAL] + hand[DUMMY] === 0) @@ -4012,74 +4311,52 @@ states.buildup_discard = { }, next() { clear_undo() - end_buildup_discard() + goto_buildup_supply_check() }, } -function end_buildup_discard() { +function goto_buildup_supply_check() { + let base_net = friendly_supply_network() + for_each_friendly_unit_on_map(u => { + if (base_net[unit_hex(u)]) + set_unit_supply(u, SS_BASE) + else + set_unit_supply(u, SS_NONE) + }) + + if (is_axis_player()) + game.capacity = [ 1, 1, 2 ] + else + game.capacity = [ 2, 2, 5 ] + game.oasis = [ 1, 1, 1 ] + + game.state = 'buildup_fortress_supply' + game.assign = 0 + resume_fortress_supply() +} + +function end_buildup_supply_check() { if (is_axis_player()) { set_enemy_player() goto_buildup_discard() } else { - goto_buildup_supply_check() - } -} - -function init_buildup() { - game.buildup = { - // redeployment network - axis_network: axis_supply_network().slice(), - axis_line: axis_supply_line().slice(), - allied_network: allied_supply_network().slice(), - allied_line: allied_supply_line().slice(), - - // extra cards purchased - axis_cards: 0, - allied_cards: 0, - - // remaining port capacity for sea redeployment - bardia: 2, - benghazi: 2, - tobruk: 5, + set_enemy_player() + goto_buildup_supply_check_recover() } } -function goto_buildup_supply_check() { - // TODO: fortress supply - // TODO: assign fortress supply - - init_buildup() - - for_each_axis_unit_on_map(u => { - let x = unit_hex(u) - if (supply_axis_network[x]) - set_unit_supply(u, EL_AGHEILA) - else - set_unit_supply(u, 0) - }) - - for_each_allied_unit_on_map(u => { - let x = unit_hex(u) - if (supply_allied_network[x]) - set_unit_supply(u, ALEXANDRIA) - else - set_unit_supply(u, 0) - }) - - for_each_unit_on_map(u => { +function goto_buildup_supply_check_recover() { + for_each_friendly_unit_on_map(u => { if (is_unit_supplied(u) && is_unit_disrupted(u) && !is_battle_hex(unit_hex(u))) { log(`Recovered at #${unit_hex(u)}.`) clear_unit_disrupted(u) } }) - - log_br() - - resume_buildup_eleminate_unsupplied() + resume_buildup_eliminate_unsupplied() } -function resume_buildup_eleminate_unsupplied() { - game.state = 'buildup_eleminate_unsupplied' +function resume_buildup_eliminate_unsupplied() { + game.state = 'buildup_eliminate_unsupplied' let done = true for_each_friendly_unit_on_map(u => { if (is_unit_unsupplied(u)) @@ -4087,16 +4364,15 @@ function resume_buildup_eleminate_unsupplied() { }) if (done) { if (is_axis_player()) { - log_br() set_enemy_player() - resume_buildup_eleminate_unsupplied() + goto_buildup_supply_check_recover() } else { goto_buildup_point_determination() } } } -states.buildup_eleminate_unsupplied = { +states.buildup_eliminate_unsupplied = { prompt() { view.prompt = `Buildup: Eliminate unsupplied units.` for_each_friendly_unit_on_map(u => { @@ -4107,19 +4383,44 @@ states.buildup_eleminate_unsupplied = { unit(u) { log(`Eliminated at #${unit_hex(u)}`) eliminate_unit(u) - resume_buildup_eleminate_unsupplied() + resume_buildup_eliminate_unsupplied() }, } +// === BUILDUP - POINT DETERMINATION === + +function init_buildup() { + game.buildup = { + // redeployment network + axis_network: union_axis_network(), + axis_line: union_axis_line(), + allied_network: union_allied_network(), + allied_line: union_allied_line(), + + // extra cards purchased + axis_cards: 0, + allied_cards: 0, + + // remaining port capacity for sea redeployment + bardia: 2, + benghazi: 2, + tobruk: 5, + } +} + function goto_buildup_point_determination() { let axis, allied + // take union of supply networks? + + init_buildup() + log_br() if (game.scenario === "1940") { axis = roll_die() allied = roll_die() log(`Axis rolled ${axis}.`) - log(`Allied rolled ${axis}.`) + log(`Allied rolled ${allied}.`) } else { let axis_a = roll_die() let axis_b = roll_die() @@ -4128,7 +4429,7 @@ function goto_buildup_point_determination() { axis = axis_a + axis_b allied = allied_a + allied_b log(`Axis rolled ${axis_a} + ${axis_b}.`) - log(`Allied rolled ${axis_a} + ${axis_b}.`) + log(`Allied rolled ${allied_a} + ${allied_b}.`) } log(`Receive ${axis + allied} BPs.`) @@ -4148,7 +4449,7 @@ function have_scheduled_reinforcements() { let refit = friendly_refit() for (let u = first_friendly_unit; u <= last_friendly_unit; ++u) { let x = unit_hex(u) - if (x === refit || x === game.month || x === game.month + 1) + if (x === refit || x === hexdeploy + game.month || x === hexdeploy + game.month + 1) return true } } @@ -4183,11 +4484,13 @@ function apply_reinforcements() { for_each_friendly_unit_in_hex(friendly_refit(), u => { set_unit_hex(u, base) + set_unit_supply(u, SS_BASE) refitted++ }) for_each_friendly_unit_in_month(game.month, u => { set_unit_hex(u, base) + set_unit_supply(u, SS_BASE) scheduled++ }) @@ -4195,6 +4498,7 @@ function apply_reinforcements() { for_each_friendly_unit_in_month(game.month + 1, u => { if (roll_die() <= 2) { set_unit_hex(u, base) + set_unit_supply(u, SS_BASE) early++ } }) @@ -4265,7 +4569,6 @@ function can_redeploy_from(from) { function is_controlled_port(where) { if (where === BARDIA || where === BENGHAZI || where === TOBRUK) return is_fortress_friendly_controlled(where) - console.log("not a porT", where) return true } @@ -4627,7 +4930,7 @@ function goto_free_deployment() { function is_valid_deployment_hex(base, x) { // we've already seen this hex during a previous supplied hex check this go - if (supply_net[from] > 0) + if (supply_temp_network[x] > 0) return true if (can_trace_supply_to_base_or_fortress(base, x)) return true @@ -4688,6 +4991,7 @@ states.free_deployment = { push_undo() log(`Deployed at #${to}.`) set_unit_hex(who, to) + set_unit_supply(who, SS_BASE) }, next() { clear_undo() @@ -4724,6 +5028,7 @@ states.initial_supply_cards = { gen_action('keep') }, discard() { + push_undo() // XXX if (is_axis_player()) { log(`Axis discarded their hand.`) game.axis_hand[REAL] = 0 @@ -4739,6 +5044,7 @@ states.initial_supply_cards = { } }, keep() { + push_undo() // XXX if (is_axis_player()) set_enemy_player() else @@ -4769,7 +5075,6 @@ function begin_game() { // No initiative first month goto_player_turn() - // goto_buildup() } // === SETUP === @@ -4812,6 +5117,7 @@ function setup_units(where, steps, list) { set_unit_steps(u, unit_start_steps[u] + steps) else if (steps > 0) set_unit_steps(u, steps) + set_unit_supply(u, SS_BASE) } } @@ -5270,6 +5576,7 @@ exports.setup = function (seed, scenario, options) { fortress: 7, axis_award: 0, allied_award: 0, + assign: 0, // battle hexes (defender) axis_hexes: [], @@ -5369,10 +5676,10 @@ exports.query = function (state, current, q) { } } else { return { - axis_supply: axis_supply_network(), - axis_supply_line: axis_supply_line(), - allied_supply: allied_supply_network(), - allied_supply_line: allied_supply_line(), + axis_supply: union_axis_network(), + axis_supply_line: union_axis_line(), + allied_supply: union_allied_network(), + allied_supply_line: union_allied_line(), } } } |