From d1a4e47292f347556a6506c2b20f6adf8e69d619 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 23 Oct 2024 19:16:27 +0200 Subject: Saxony's defection and neutrality. --- play.js | 19 +- rules.js | 596 ++++++++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 490 insertions(+), 125 deletions(-) diff --git a/play.js b/play.js index beb62e1..5073ee0 100644 --- a/play.js +++ b/play.js @@ -290,13 +290,20 @@ function hide_move_path() { /* PANEL ORDER */ -const panel_order = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA, 6 ] -const panel_start = { +const panel_order_p = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA, 6 ] +const panel_order_a = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_PRAGMATIC, P_AUSTRIA, P_SAXONY, 6 ] +const panel_start_p = { "Observer": 0, "Louis XV": 0, "Frederick": 2, "Maria Theresa": 5, } +const panel_start_a = { + "Observer": 0, + "Louis XV": 0, + "Frederick": 2, + "Maria Theresa": 4, +} function remember_position(e) { if (e.parentElement) { @@ -331,6 +338,9 @@ function animate_position(e) { } function sort_power_panel(animate) { + let panel_order = (!view || view.saxony < 3) ? panel_order_p : panel_order_a + let panel_start = (!view || view.saxony < 3) ? panel_start_p : panel_start_a + let start = panel_start[params.role] | 0 if (animate) @@ -346,6 +356,9 @@ function sort_power_panel(animate) { if (view && view.actions) ui.power_panel_list.prepend(ui.power_panel[view.power]) + if (view && (view.political || (view.actions && view.actions.shift))) + ui.power_panel_list.prepend(ui.power_panel[6]) + if (animate) for (let i = 0; i < 7; ++i) animate_position(ui.power_panel[i]) @@ -927,7 +940,7 @@ function layout_victory_box(pow, col, row) { function layout_political_marker(e, col, row) { let x = 190 + (col-1) * 69 - let y = 72 + row * 86 + 16 + let y = 72 + row * 86 e.style.left = (x - 16) + "px" e.style.top = (y - 16) + "px" ui.pol_tracks.appendChild(e) diff --git a/rules.js b/rules.js index 01532bf..d1c1e75 100644 --- a/rules.js +++ b/rules.js @@ -4,6 +4,8 @@ /* TODO +check push_undo/clear_undo for political phase and political changes + show who controls which power in player list set-aside victory marker areas @@ -26,13 +28,10 @@ tc subsidies minor when major fortress enemy occupied subsidy contracts -arenberg supply exception -arenberg re-entry exception - -victory boxes -politics imperial election +expeditionary corps + political changes prussia annexes silesia france reduces military objectives @@ -105,6 +104,8 @@ const IMPERIAL_ELECTION = 24 const ELIMINATED = data.cities.name.length const ARENBERG = 17 +const SAXONY_GENERAL = 19 +const SAXONY_TRAIN = 29 const all_powers = [ 0, 1, 2, 3, 4, 5 ] const all_major_powers = [ 0, 1, 2, 3 ] @@ -318,9 +319,15 @@ const all_prussia_saxony_generals = [ ...all_power_generals[P_SAXONY], ] -const all_austria_pragmatic_generals = [ +const all_austria_saxony_generals = [ ...all_power_generals[P_AUSTRIA], - ...all_power_generals[P_PRAGMATIC], + ...all_power_generals[P_SAXONY], +] + +const all_austria_pragmatic_saxony_trains = [ + ...all_power_trains[P_AUSTRIA], + ...all_power_trains[P_PRAGMATIC], + ...all_power_trains[P_SAXONY], ] const all_france_bavaria_trains = [ @@ -333,48 +340,70 @@ const all_prussia_saxony_trains = [ ...all_power_trains[P_SAXONY], ] -const all_austria_pragmatic_trains = [ +const all_austria_saxony_trains = [ ...all_power_trains[P_AUSTRIA], - ...all_power_trains[P_PRAGMATIC], + ...all_power_generals[P_SAXONY], ] -const all_france_allied_trains = [ +const all_france_bavaria_prussia_trains = [ + ...all_power_trains[P_FRANCE], + ...all_power_trains[P_BAVARIA], + ...all_power_trains[P_PRUSSIA], +] + +const all_france_bavaria_prussia_saxony_trains = [ ...all_power_trains[P_FRANCE], ...all_power_trains[P_BAVARIA], ...all_power_trains[P_PRUSSIA], ...all_power_trains[P_SAXONY], ] -const all_austria_allied_trains = [ +const all_austria_pragmatic_trains = [ ...all_power_trains[P_AUSTRIA], ...all_power_trains[P_PRAGMATIC], ] -const all_austria_allied_generals = [ +const all_austria_pragmatic_generals = [ ...all_power_generals[P_AUSTRIA], ...all_power_generals[P_PRAGMATIC], ] -const all_france_allied_generals = [ +const all_austria_pragmatic_saxony_generals = [ + ...all_power_generals[P_AUSTRIA], + ...all_power_generals[P_PRAGMATIC], + ...all_power_generals[P_SAXONY], +] + +const all_france_bavaria_prussia_generals = [ ...all_power_generals[P_FRANCE], ...all_power_generals[P_BAVARIA], ...all_power_generals[P_PRUSSIA], - ...all_power_generals[P_SAXONY], ] -const all_powers_prussia_saxony_pragmatic_austria = [ P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA ] -const all_powers_france_bavaria_pragmatic_austria = [ P_FRANCE, P_BAVARIA, P_PRAGMATIC, P_AUSTRIA ] -const all_powers_france_bavaria_prussia_saxony = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY ] -const all_powers_pragmatic_austria = [ P_PRAGMATIC, P_AUSTRIA ] +const all_france_bavaria_prussia_saxony_generals = [ + ...all_power_generals[P_FRANCE], + ...all_power_generals[P_BAVARIA], + ...all_power_generals[P_PRUSSIA], + ...all_power_generals[P_SAXONY], +] const all_powers_none = [] -const all_powers_france_bavaria = [ P_FRANCE, P_BAVARIA ] -const all_powers_bavaria = [ P_BAVARIA ] +const all_powers_prussia = [ P_PRUSSIA ] const all_powers_pragmatic = [ P_PRAGMATIC ] const all_powers_austria = [ P_AUSTRIA ] -const all_powers_prussia_saxony = [ P_PRUSSIA, P_SAXONY ] +const all_powers_bavaria = [ P_BAVARIA ] const all_powers_saxony = [ P_SAXONY ] +const all_powers_france_bavaria = [ P_FRANCE, P_BAVARIA ] +const all_powers_prussia_saxony = [ P_PRUSSIA, P_SAXONY ] +const all_powers_austria_saxony = [ P_AUSTRIA, P_SAXONY ] const all_powers_bavaria_saxony = [ P_BAVARIA, P_SAXONY ] +const all_powers_prussia_saxony_pragmatic_austria = [ P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA ] +const all_powers_france_bavaria_pragmatic_austria = [ P_FRANCE, P_BAVARIA, P_PRAGMATIC, P_AUSTRIA ] +const all_powers_france_bavaria_prussia_pragmatic = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_PRAGMATIC ] +const all_powers_france_bavaria_prussia = [ P_FRANCE, P_BAVARIA, P_PRUSSIA ] +const all_powers_france_bavaria_prussia_saxony = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY ] +const all_powers_pragmatic_austria = [ P_PRAGMATIC, P_AUSTRIA ] +const all_powers_pragmatic_austria_saxony = [ P_PRAGMATIC, P_AUSTRIA, P_SAXONY ] function coop_major_power(pow) { if (pow === P_BAVARIA) @@ -398,58 +427,87 @@ function coop_minor_power(pow) { return pow } -function all_friendly_minor_powers(pow) { - switch (pow) { +function is_hostile_to_austria() { + switch (game.power) { case P_FRANCE: case P_BAVARIA: case P_PRUSSIA: + return true case P_SAXONY: - return all_powers_bavaria_saxony + return is_saxony_prussian() case P_PRAGMATIC: case P_AUSTRIA: - return all_powers_none + return false } } -function all_coop_powers(pow) { - switch (pow) { +// TODO: simplify all these lists and stuff + +function all_controlled_powers(pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: return all_powers_france_bavaria case P_PRUSSIA: - case P_SAXONY: - return all_powers_prussia_saxony + if (is_saxony_prussian()) + return all_powers_prussia_saxony + return all_powers_prussia case P_PRAGMATIC: + return all_powers_pragmatic case P_AUSTRIA: - return all_powers_pragmatic_austria + if (is_saxony_austrian()) + return all_powers_austria_saxony + return all_powers_austria } } -function all_controlled_powers(pow) { - switch (pow) { +function all_controlled_generals(pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: - return all_powers_france_bavaria + return all_france_bavaria_generals case P_PRUSSIA: - case P_SAXONY: - return all_powers_prussia_saxony + if (is_saxony_prussian()) + return all_prussia_saxony_generals + return all_power_generals[P_PRUSSIA] case P_PRAGMATIC: - return all_powers_pragmatic + return all_power_generals[P_PRAGMATIC] case P_AUSTRIA: - return all_powers_austria + if (is_saxony_austrian()) + return all_austria_saxony_generals + return all_power_generals[P_AUSTRIA] } } -function is_hostile_to_austria() { - switch (game.power) { +function all_controlled_trains(pow) { + switch (coop_major_power(pow)) { + case P_FRANCE: + return all_france_bavaria_trains + case P_PRUSSIA: + if (is_saxony_prussian()) + return all_prussia_saxony_trains + return all_power_trains[P_PRUSSIA] + case P_PRAGMATIC: + return all_power_trains[P_PRAGMATIC] + case P_AUSTRIA: + if (is_saxony_austrian()) + return all_austria_saxony_trains + return all_power_trains[P_AUSTRIA] + } +} + +function all_friendly_minor_powers(pow) { + switch (pow) { case P_FRANCE: case P_BAVARIA: case P_PRUSSIA: case P_SAXONY: - return true + if (is_saxony_prussian()) + return all_powers_bavaria_saxony + return all_powers_bavaria case P_PRAGMATIC: case P_AUSTRIA: - return false + if (is_saxony_austrian()) + return all_powers_saxony + return all_powers_none } } @@ -458,10 +516,17 @@ function all_enemy_powers(pow) { case P_FRANCE: case P_BAVARIA: case P_PRUSSIA: + if (is_saxony_prussian()) + return all_powers_pragmatic_austria + return all_powers_pragmatic_austria_saxony case P_SAXONY: + if (is_saxony_prussian()) + return all_powers_france_bavaria_prussia return all_powers_pragmatic_austria case P_PRAGMATIC: case P_AUSTRIA: + if (is_saxony_austrian()) + return all_powers_france_bavaria_prussia return all_powers_france_bavaria_prussia_saxony } } @@ -472,94 +537,89 @@ function all_non_coop_powers(pow) { case P_BAVARIA: return all_powers_prussia_saxony_pragmatic_austria case P_PRUSSIA: - case P_SAXONY: return all_powers_france_bavaria_pragmatic_austria + case P_SAXONY: + if (is_saxony_prussian()) + return all_powers_france_bavaria_pragmatic_austria + return all_powers_france_bavaria_prussia_pragmatic case P_PRAGMATIC: case P_AUSTRIA: + if (is_saxony_austrian()) + return all_powers_france_bavaria_prussia return all_powers_france_bavaria_prussia_saxony } } function all_coop_generals(pow) { - switch (pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: return all_france_bavaria_generals case P_PRUSSIA: - case P_SAXONY: - return all_prussia_saxony_generals + if (is_saxony_prussian()) + return all_prussia_saxony_generals + return all_power_generals[P_PRUSSIA] case P_PRAGMATIC: case P_AUSTRIA: + if (is_saxony_austrian()) + return all_austria_pragmatic_saxony_generals return all_austria_pragmatic_generals } } -function all_controlled_generals(pow) { - switch (pow) { - case P_FRANCE: - case P_BAVARIA: - return all_france_bavaria_generals - case P_PRUSSIA: - case P_SAXONY: - return all_prussia_saxony_generals - case P_PRAGMATIC: - return all_power_generals[P_PRAGMATIC] - case P_AUSTRIA: - return all_power_generals[P_AUSTRIA] - } +function all_france_allied_generals() { + if (is_saxony_prussian()) + return all_france_bavaria_prussia_saxony_generals + return all_france_bavaria_prussia_generals } -function all_controlled_trains(pow) { - switch (pow) { - case P_FRANCE: - case P_BAVARIA: - return all_france_bavaria_trains - case P_PRUSSIA: - case P_SAXONY: - return all_prussia_saxony_trains - case P_PRAGMATIC: - return all_power_trains[P_PRAGMATIC] - case P_AUSTRIA: - return all_power_trains[P_AUSTRIA] - } +function all_france_allied_trains() { + if (is_saxony_prussian()) + return all_france_bavaria_prussia_saxony_trains + return all_france_bavaria_prussia_trains +} + +function all_austria_allied_generals() { + if (is_saxony_austrian()) + return all_austria_pragmatic_saxony_generals + return all_austria_pragmatic_generals +} + +function all_austria_allied_trains() { + if (is_saxony_austrian()) + return all_austria_pragmatic_saxony_trains + return all_austria_pragmatic_trains } function all_allied_trains(pow) { - switch (pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: case P_PRUSSIA: - case P_SAXONY: - return all_france_allied_trains + return all_france_allied_trains() case P_AUSTRIA: case P_PRAGMATIC: - return all_austria_allied_trains + return all_austria_allied_trains() } } function all_enemy_trains(pow) { - switch (pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: case P_PRUSSIA: - case P_SAXONY: - return all_austria_allied_trains + return all_austria_allied_trains() case P_AUSTRIA: case P_PRAGMATIC: - return all_france_allied_trains + return all_france_allied_trains() } } function all_enemy_generals(pow) { - switch (pow) { + switch (coop_major_power(pow)) { case P_FRANCE: - case P_BAVARIA: case P_PRUSSIA: - case P_SAXONY: - return all_austria_allied_generals + return all_austria_allied_generals() case P_AUSTRIA: case P_PRAGMATIC: - return all_france_allied_generals + return all_france_allied_generals() } } @@ -675,6 +735,18 @@ function is_home_country(s) { } } +function is_home_country_for_return(s) { + // TODO: Silesia + switch (game.power) { + case P_FRANCE: return set_has(data.country.France, s) || set_has(data.country.Bavaria, s) + case P_PRUSSIA: return set_has(data.country.Prussia, s) + case P_PRAGMATIC: return set_has(data.country.Netherlands, s) + case P_AUSTRIA: return set_has(data.country.Austria, s) + case P_BAVARIA: return set_has(data.country.Bavaria, s) + case P_SAXONY: return set_has(data.country.Saxony, s) + } +} + function is_enemy_home_country(s) { for (let other of all_enemy_powers(game.power)) if (set_has(all_home_country_fortresses[other], s)) @@ -816,7 +888,10 @@ function player_from_power(pow) { case P_AUSTRIA: return R_MARIA_THERESA case P_SAXONY: - return R_FREDERICK + if (is_saxony_prussian()) + return R_FREDERICK + else + return R_MARIA_THERESA case P_BAVARIA: return R_LOUIS_XV } @@ -1360,8 +1435,9 @@ function set_in_supply(p) { } function search_supply_path_avoid_hussars(who) { + let pow = piece_power[who] let from = game.pos[who] - let trains = all_power_trains[piece_power[who]] + let trains = all_power_trains[pow] if (is_home_country(from)) return 1 @@ -1382,6 +1458,8 @@ function search_supply_path_avoid_hussars(who) { continue if (has_enemy_piece(next)) continue + if (is_forbidden_neutral_space(pow, next)) + continue set_add(seen, next) if (dist < 6) queue.push((next << 4) | dist) @@ -1392,8 +1470,9 @@ function search_supply_path_avoid_hussars(who) { } function search_supply_path(who) { + let pow = piece_power[who] let from = game.pos[who] - let trains = all_power_trains[piece_power[who]] + let trains = all_power_trains[pow] if (who === ARENBERG) { if (set_has(data.country.Netherlands, from)) @@ -1418,6 +1497,8 @@ function search_supply_path(who) { continue if (has_enemy_piece(next)) continue + if (is_forbidden_neutral_space(pow, next)) + continue set_add(seen, next) if (dist < 6) queue.push((next << 4) | dist) @@ -1708,19 +1789,44 @@ function goto_movement() { game.move_conq = [] } +function is_forbidden_neutral_space(pow, to) { + if (is_saxony_neutral()) { + if (pow === P_SAXONY) + return !set_has(data.country.Saxony, to) + else + return set_has(data.country.Saxony, to) + } + if (is_prussia_neutral()) { + // TODO: Silesia home country + if (pow === P_PRUSSIA) + return !set_has(data.country.Prussia, to) + else + return set_has(data.country.Prussia, to) + } + return false +} + +function can_move_piece_to(p, from, to) { + if (is_general(p)) + return can_move_general_to(p, from, to) + return can_move_train_to(p, from, to) +} + function can_train_move_anywhere(p) { let from = game.pos[p] for (let to of data.cities.adjacent[from]) - if (can_move_train_to(to)) + if (can_move_train_to(p, from, to)) return true return false } function can_general_move_anywhere(p) { let from = game.pos[p] + console.log("MOVE CHEK", p, piece_abbr[p]) for (let to of data.cities.adjacent[from]) - if (can_move_general_to(from, to)) + if (can_move_general_to(p, from, to)) return true + console.log(" fail") return false } @@ -1730,7 +1836,10 @@ states.movement = { let done_generals = true let done_trains = true +console.log("MOVE", game.moved, all_controlled_generals(game.power)) + for (let p of all_controlled_generals(game.power)) { + console.log(">P", piece_abbr[p]) if (!set_has(game.moved, p) && is_piece_on_map(p)) { if (can_general_move_anywhere(p)) { gen_action_piece(p) @@ -1821,7 +1930,11 @@ function format_move(max) { return ` up to ${n} cities.` } -function can_move_train_to(to) { +function can_move_train_to(p, from, to) { + if (is_forbidden_neutral_space(piece_power[p], to)) + return false + if (is_illegal_cross_map_move(from, to)) + return false return !has_any_piece(to) } @@ -1834,7 +1947,9 @@ function is_illegal_cross_map_move(from, to) { ) } -function can_move_general_to(from, to) { +function can_move_general_to(p, from, to) { + if (is_forbidden_neutral_space(piece_power[p], to)) + return false if (is_illegal_cross_map_move(from, to)) return false if (has_friendly_supply_train(to)) @@ -1909,11 +2024,11 @@ states.move_supply_train = { if (game.count < 2 + game.main) for (let next of data.cities.main_roads[here]) - if (!has_any_piece(next)) + if (can_move_train_to(who, here, next)) gen_action_space(next) if (game.count < 2) for (let next of data.cities.roads[here]) - if (!has_any_piece(next)) + if (can_move_train_to(who, here, next)) gen_action_space(next) if (game.count > 0) @@ -1983,12 +2098,12 @@ states.move_general = { if (game.count < 3 + game.main) for (let next of data.cities.main_roads[here]) - if (can_move_general_to(here, next)) + if (can_move_general_to(who, here, next)) gen_action_space_or_piece(next) if (game.count < 3) for (let next of data.cities.roads[here]) - if (can_move_general_to(here, next)) + if (can_move_general_to(who, here, next)) gen_action_space_or_piece(next) }, take() { @@ -2034,7 +2149,7 @@ function is_adjacent_to_enemy_piece(here) { return false } -function search_force_march(came_from, start) { +function search_force_march(p, came_from, start) { let seen = [ start ] let queue = [ start << 4 ] while (queue.length > 0) { @@ -2050,7 +2165,7 @@ function search_force_march(came_from, start) { continue if (is_adjacent_to_enemy_piece(next)) continue - if (!can_move_general_to(here, next)) + if (!can_move_general_to(p, here, next)) continue if (came_from) map_set(came_from, next, here) @@ -2071,14 +2186,14 @@ states.force_march = { view.selected = game.selected let here = game.pos[game.selected] - for (let s of search_force_march(null, here)) + for (let s of search_force_march(game.selected, null, here)) gen_action_space(s) }, space(to) { let here = game.pos[game.selected] let came_from = [] - search_force_march(came_from, here) + search_force_march(game.selected, came_from, here) let path = [] while (to !== here) { @@ -2335,17 +2450,9 @@ states.re_enter_train_where = { gen_re_enter_train_at_power_fortress(coop_minor_power(game.power)) }, space(to) { - game.pos[game.selected] = to + enter_train_at(game.selected, to) game.selected = -1 game.state = "re_enter_train" - - // remove hussars - for (let p of all_hussars) { - if (game.pos[p] === to) { - log("P" + p + " removed.") - game.pos[p] = ELIMINATED - } - } }, } @@ -2554,6 +2661,18 @@ function enter_general_at(p, s) { } } +function enter_train_at(p, s) { + game.pos[p] = s + + // remove hussars + for (let p of all_hussars) { + if (game.pos[p] === s) { + log("P" + p + " removed.") + game.pos[p] = ELIMINATED + } + } +} + states.re_enter_general_where = { inactive: "recruit", prompt() { @@ -2614,6 +2733,12 @@ function end_recruit() { log("Recruited nothing.") } + if (game.special_saxony_recruit) { + delete game.special_saxony_recruit + end_saxony_neutral() + return + } + end_winter_stage() } @@ -2623,13 +2748,19 @@ function goto_combat() { let from = [] let to = [] - for (let p of all_controlled_generals(game.power)) + for (let p of all_controlled_generals(game.power)) { + if (piece_power[p] === P_SAXONY && is_saxony_neutral()) + continue if (is_piece_on_map(p)) set_add(from, game.pos[p]) + } - for (let p of all_enemy_generals(game.power)) + for (let p of all_enemy_generals(game.power)) { + if (piece_power[p] === P_SAXONY && is_saxony_neutral()) + continue if (is_piece_on_map(p)) set_add(to, game.pos[p]) + } game.combat = [] for (let a of from) { @@ -3583,6 +3714,8 @@ function end_politics() { } } + game.stage = 100 // hack for saxony's defection return + goto_adjust_political_tracks() } @@ -3677,7 +3810,6 @@ function end_execute_political_card() { } const TRACK_NAME = { saxony: "Saxony marker", russia: "Russia marker", italy: "Italy marker" } -const TRACK_SIZE = { saxony: 5, russia: 9, italy: 9 } states.political_shift = { inactive: "execute political card", @@ -3739,7 +3871,6 @@ states.political_troop_power = { states.political_troops_draw = { inactive: "execute political card", prompt() { - let info = event_troops[current_political_effect()] prompt("Draw " + format_card_list_prompt(game.draw) + ".") view.draw = game.draw view.actions.next = 1 @@ -3759,7 +3890,6 @@ states.political_troops_draw = { states.political_troops_place = { inactive: "execute political card", prompt() { - let info = event_troops[current_political_effect()] if (game.count > 1) prompt("Recieve " + game.count + " troops.") else if (game.count === 1) @@ -3832,6 +3962,7 @@ states.saxony_shift = { view.actions.pass = 1 }, shift(_) { + clear_undo() log("Saxony marker shifted " + game.count + " right.") let save_saxony = game.saxony game.saxony = Math.max(1, Math.min(5, game.saxony + game.count)) @@ -3839,13 +3970,15 @@ states.saxony_shift = { // Saxony defection if (save_saxony < 3 && is_saxony_neutral()) - throw "TODO Saxony became neutral!" + goto_saxony_becomes_neutral() else if (save_saxony < 5 && is_saxony_austrian_ally()) - throw "TODO Saxony became austrian!" + goto_saxony_becomes_austrian_ally() else next_combat() }, pass() { + clear_undo() + log("Saxony marker not shifted.") game.count = 0 next_combat() @@ -3898,13 +4031,232 @@ function goto_saxony_defection() { delete game.save_saxony if (save_saxony < 3 && is_saxony_neutral()) - throw "TODO Saxony became neutral!" + goto_saxony_becomes_neutral() else if (save_saxony < 5 && is_saxony_austrian_ally()) - throw "TODO Saxony became austrian!" + goto_saxony_becomes_austrian_ally() end_adjust_political_tracks() } +/* SAXONY'S DEFECTION */ + +function search_nearest_city(p) { + let from = game.pos[p] + let result = [] + let min_dist = 1000 + + let seen = [ from ] + let queue = [ from << 4 ] + while (queue.length > 0) { + let item = queue.shift() + let here = item >> 4 + let dist = (item & 15) + 1 + for (let next of data.cities.adjacent[here]) { + if (set_has(seen, next)) + continue + set_add(seen, next) + + if (can_move_piece_to(p, from, next)) { + if (dist <= min_dist) { + min_dist = dist + set_add(result, next) + } + } + + if (dist < min_dist) + queue.push((next << 4) | dist) + } + } + + return result +} + +function search_nearest_home_city(p) { + let from = game.pos[p] + let result = [] + let min_dist = 1000 + + let seen = [ from ] + let queue = [ from << 4 ] + while (queue.length > 0) { + let item = queue.shift() + let here = item >> 4 + let dist = (item & 15) + 1 + for (let next of data.cities.adjacent[here]) { + if (set_has(seen, next)) + continue + set_add(seen, next) + + if (is_home_country_for_return(next)) { + if (can_move_piece_to(p, from, next)) { + if (dist <= min_dist) { + min_dist = dist + set_add(result, next) + } + } + } + + if (dist < min_dist) + queue.push((next << 4) | dist) + } + } + + return result +} + +function power_has_any_piece_in_list(pow, list) { + for (let p of all_power_generals[pow]) + if (set_has(list, game.pos[p])) + return true + for (let p of all_power_trains[pow]) + if (set_has(list, game.pos[p])) + return true +} + +function goto_saxony_becomes_neutral() { + set_active_to_power(P_SAXONY) + + log_br() + log("Saxony becomes neutral!") + + // Return all victory markers + log(">Removed victory markers") + for (let s of all_home_country_fortresses[P_SAXONY]) { + let pow = map_get(game.victory, s, -1) + if (pow >= 0) { + map_delete(game.victory, s) + } + } + + goto_saxony_return_foreign_pieces() +} + +function goto_saxony_return_foreign_pieces() { + for (let pow of all_powers) { + if (pow === P_SAXONY) + continue + if (power_has_any_piece_in_list(pow, data.country.Saxony)) { + set_active_to_power(pow) + game.state = "saxony_return_foreign_who" + return + } + } + + set_active_to_power(P_SAXONY) + game.state = "saxony_return_home" +} + +states.saxony_return_foreign_who = { + inactive: "return foreign pieces from Saxony", + prompt() { + prompt("Return pieces to the nearest city in their home country.") + for (let p of all_power_generals[game.power]) + if (set_has(data.country.Saxony, game.pos[p])) + gen_action_piece(p) + for (let p of all_power_trains[game.power]) + if (set_has(data.country.Saxony, game.pos[p])) + gen_action_piece(p) + }, + piece(p) { + game.selected = p + game.state = "saxony_return_foreign_where" + }, +} + +states.saxony_return_foreign_where = { + inactive: "return foreign pieces from Saxony", + prompt() { + prompt("Return pieces to the nearest city in their home country.") + view.selected = game.selected + for (let s of search_nearest_home_city(game.selected)) + gen_action_space(s) + }, + space(s) { + log(">P" + game.selected + " to S" + s) + if (is_general(game.selected)) + enter_general_at(game.selected, s) + else + enter_train_at(game.selected, s) + game.selected = -1 + goto_saxony_return_foreign_pieces() + }, +} + +states.saxony_return_home = { + inactive: "become neutral", + prompt() { + prompt("Return pieces to their set-up cities.") + let done = true + for (let p of [ SAXONY_GENERAL, SAXONY_TRAIN ]) { + if (is_piece_on_map(p) && game.pos[p] !== setup_piece_position[p]) { + gen_action_piece(p) + done = false + } + } + if (done) + view.actions.next = 1 + }, + piece(p) { + let s = setup_piece_position[p] + game.pos[p] = s + log(">P" + p + " to S" + s) + }, + next() { + end_saxony_neutral() + }, +} + +function goto_saxony_becomes_austrian_ally() { + set_active_to_power(P_SAXONY) + + log_br() + log("Saxony becomes Austrian ally!") + + game.selected = SAXONY_GENERAL + + if (game.pos[SAXONY_GENERAL] === ELIMINATED) { + game.special_saxony_recruit = 1 + goto_recruit() + throw "STOP TEST" + return + } + + if (has_general_of_power(game.pos[SAXONY_GENERAL], P_PRUSSIA)) + game.state = "saxony_move_general" + else + end_saxony_neutral() +} + +states.saxony_move_general = { + inactive: "re-enter general", + prompt() { + prompt("Move " + format_selected() + " to the nearest empty city.") + view.selected = game.selected + for (let s of search_nearest_city(game.selected)) + gen_action_space(s) + }, + space(s) { + log(">P" + game.selected + " to S" + s) + enter_general_at(game.selected, s) + end_saxony_neutral() + }, +} + +function end_saxony_neutral() { + game.selected = -1 + if (game.stage === 100) + end_adjust_political_tracks() + else + next_combat() +} + +/* NEUTRALITY */ + +function is_prussia_neutral() { + // TODO + return false +} + /* SETUP */ const POWER_FROM_SETUP_STAGE = [ -- cgit v1.2.3