From 3664a02ae1a5ca368d6e9cb3a8e016177d528238 Mon Sep 17 00:00:00 2001
From: Tor Andersson <tor@ccxvii.net>
Date: Wed, 23 Oct 2024 23:58:09 +0200
Subject: annexation, expeditionary corps, set-aside markers

---
 play.js  |  26 ++++
 rules.js | 445 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 433 insertions(+), 38 deletions(-)

diff --git a/play.js b/play.js
index 5073ee0..578156b 100644
--- a/play.js
+++ b/play.js
@@ -51,6 +51,9 @@ const P_AUSTRIA = 3
 const P_BAVARIA = 4
 const P_SAXONY = 5
 
+const SET_ASIDE_FRANCE = 4
+const SET_ASIDE_PRUSSIA = 5
+
 const power_name = [ "France", "Prussia", "Pragmatic Army", "Austria", "Bavaria", "Saxony" ]
 const power_class = [ "france", "prussia", "pragmatic", "austria", "bavaria", "saxony" ]
 
@@ -957,10 +960,15 @@ function layout_retro(s, pow) {
 
 function layout_victory_pool(pow, max, x, y) {
 	let n = view.vp[pow]
+	let m = 0
 	if (pow === P_PRUSSIA) {
+		m = view.vp[SET_ASIDE_PRUSSIA]
+		n += m
 		if (view.flags & F_SILESIA_ANNEXED) ++n
 	}
 	if (pow === P_FRANCE) {
+		m = view.vp[SET_ASIDE_FRANCE]
+		n += m
 		if (view.flags & F_ITALY_FRANCE) ++n
 		if (view.flags & F_EMPEROR_FRANCE) ++n
 	}
@@ -971,12 +979,25 @@ function layout_victory_pool(pow, max, x, y) {
 	for (let i = 0; i < view.victory.length; i += 2)
 		if (view.victory[i+1] === pow)
 			++n
+
 	for (let i = 0; i < max - n; ++i) {
 		let e = ui.victory[pow][used_victory[pow]++]
 		e.style.left = (x - 16 + (i%5) * 20) + "px"
 		e.style.top = (y - 16 + (i/5|0) * 20) + "px"
 		ui.markers_element.appendChild(e)
 	}
+
+	for (let i = 0; i < m; ++i) {
+		let e = ui.victory[pow][used_victory[pow]++]
+		if (pow === P_FRANCE) {
+			e.style.left = (x - 16 + i * 20) + "px"
+			e.style.top = (1635 - 16) + "px"
+		} else {
+			e.style.left = (x + 80 - 16 - i * 20) + "px"
+			e.style.top = (19 - 16) + "px"
+		}
+		ui.markers_element.appendChild(e)
+	}
 }
 
 function layout_elector(s, pow) {
@@ -1207,6 +1228,11 @@ function on_update() {
 	for (let pow of all_powers)
 		action_button_with_argument("power", pow, power_name[pow])
 
+	action_button("reduce", "Reduce")
+	action_button("offer", "Offer")
+	action_button("accept", "Accept")
+	action_button("deny", "Deny")
+
 	action_button("re_enter", "Re-enter")
 	action_button("force_march", "Force march")
 
diff --git a/rules.js b/rules.js
index d1c1e75..960a50a 100644
--- a/rules.js
+++ b/rules.js
@@ -4,8 +4,11 @@
 
 /* TODO
 
+check inactive prompts
 check push_undo/clear_undo for political phase and political changes
 
+add undo steps and pauses for saxony neutral return pieces
+
 show who controls which power in player list
 
 set-aside victory marker areas
@@ -81,6 +84,9 @@ const P_AUSTRIA = 3
 const P_BAVARIA = 4
 const P_SAXONY = 5
 
+const SET_ASIDE_FRANCE = 4
+const SET_ASIDE_PRUSSIA = 5
+
 const power_name = [ "France", "Prussia", "Pragmatic Army", "Austria", "Bavaria", "Saxony" ]
 const power_class = [ "france", "prussia", "pragmatic", "austria", "bavaria", "saxony" ]
 
@@ -92,6 +98,8 @@ const F_SILESIA_ANNEXED = 16
 const F_IMPERIAL_ELECTION = 32 // imperial election card revealed!
 const F_WAR_OF_JENKINS_EAR = 64 // -1 France TC for one turn
 const F_SAXONY_TC_ONCE = 128 // only draw TCs once per turn
+const F_FRANCE_REDUCED = 256
+const F_PRUSSIA_NEUTRAL = 512
 
 const SPADES = 0
 const CLUBS = 1
@@ -128,6 +136,15 @@ const all_power_trains = [
 	[ 29 ],
 ]
 
+const all_power_pieces = [
+	[ ...all_power_generals[0], ...all_power_trains[0] ],
+	[ ...all_power_generals[1], ...all_power_trains[1] ],
+	[ ...all_power_generals[2], ...all_power_trains[2] ],
+	[ ...all_power_generals[3], ...all_power_trains[3] ],
+	[ ...all_power_generals[4], ...all_power_trains[4] ],
+	[ ...all_power_generals[5], ...all_power_trains[5] ],
+]
+
 const last_piece = 29
 
 const all_hussars = [ 30, 31 ]
@@ -299,6 +316,10 @@ const COSEL = find_city("Cosel")
 const MUNCHEN = find_city("München")
 const DRESDEN = find_city("Dresden")
 
+const WOLDENBURG = find_city("Woldenburg")
+const OMANS = find_city("Omans")
+const STEINAMANGER = find_city("Steinamanger")
+
 const ENGLAND = find_city("England")
 const EAST_PRUSSIA = find_city("East Prussia")
 const AUSTRIAN_ITALY_BOX = data.sectors.ItalyA[0]
@@ -694,6 +715,7 @@ all_home_country_fortresses[P_PRAGMATIC] = set_intersect(all_fortresses, data.co
 all_home_country_fortresses[P_AUSTRIA] = set_intersect(all_fortresses, data.country.Austria)
 all_home_country_fortresses[P_BAVARIA] = set_intersect(all_fortresses, data.country.Bavaria)
 all_home_country_fortresses[P_SAXONY] = set_intersect(all_fortresses, data.country.Saxony)
+const all_silesian_fortresses = set_intersect(all_fortresses, data.country.Silesia)
 
 const all_home_country_major_fortresses = []
 all_home_country_major_fortresses[P_FRANCE] = set_intersect(data.type.major_fortress, data.country.France)
@@ -703,13 +725,22 @@ all_home_country_major_fortresses[P_AUSTRIA] = set_intersect(data.type.major_for
 all_home_country_major_fortresses[P_BAVARIA] = set_intersect(data.type.major_fortress, data.country.Bavaria)
 all_home_country_major_fortresses[P_SAXONY] = set_intersect(data.type.major_fortress, data.country.Saxony)
 
+const all_core_austria_cities = data.country.Austria.filter(is_bohemia_space)
+const all_core_austria_fortresses = all_home_country_fortresses[P_AUSTRIA].filter(is_bohemia_space)
+
+const all_prussian_and_silesian_fortresses = set_union(
+	all_home_country_fortresses[P_PRUSSIA],
+	all_silesian_fortresses
+)
+
+const all_prussian_and_silesian_cities = set_union(data.country.Prussia, data.country.Silesia)
+const all_prussian_and_silesian_and_polish_cities = set_union(data.country.Prussia, data.country.Silesia, data.country.Poland)
+
 const all_arenberg_major_fortresses = set_union(
 	all_home_country_major_fortresses[P_PRAGMATIC],
 	all_home_country_major_fortresses[P_AUSTRIA]
 )
 
-const all_silesian_fortresses = set_intersect(all_fortresses, data.country.Silesia)
-
 const protect_range = []
 for (let s of all_fortresses)
 	make_protect_range(protect_range[s] = [], s, s, 3)
@@ -724,10 +755,12 @@ function make_protect_range(result, start, here, range) {
 }
 
 function is_home_country(s) {
-	// TODO: Silesia
 	switch (game.power) {
 	case P_FRANCE: return set_has(data.country.France, s)
-	case P_PRUSSIA: return set_has(data.country.Prussia, s)
+	case P_PRUSSIA:
+		if (has_prussia_annexed_silesia())
+			return set_has(all_prussian_and_silesian_cities, s)
+		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)
@@ -736,10 +769,12 @@ 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_PRUSSIA:
+		if (has_prussia_annexed_silesia())
+			return set_has(all_prussian_and_silesian_cities, s)
+		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)
@@ -797,6 +832,11 @@ function set_control_of_fortress(s, pow) {
 		return
 	}
 
+	if (pow === P_FRANCE && game.vp[SET_ASIDE_FRANCE] > 0) {
+		if (set_has(all_core_austria_fortresses, s))
+			return_set_aside_markers(P_FRANCE, SET_ASIDE_FRANCE)
+	}
+
 	if (is_enemy_home_country(s) || is_friendly_minor_home_country(s) || set_has(all_silesian_fortresses, s))
 		map_set(game.victory, s, pow)
 	else
@@ -955,11 +995,11 @@ function count_used_troops() {
 	return current
 }
 
-function has_general_of_power(to, power) {
-	for (let p of all_power_generals[power])
-		if (game.pos[p] === to)
-			return true
-	return false
+function find_general_of_power(s, pow) {
+	for (let p of all_power_generals[pow])
+		if (game.pos[p] === s)
+			return p
+	return -1
 }
 
 function has_any_piece(to) {
@@ -1128,9 +1168,30 @@ function goto_action_stage() {
 function end_action_stage() {
 	clear_undo()
 
+	set_active_to_current_action_stage()
+
 	if (check_instant_victory())
 		return
 
+	if (game.power === P_PRUSSIA && has_prussia_conquered_silesia()) {
+		game.state = "offer_peace"
+		return
+	}
+
+	if (
+		game.power === P_FRANCE &&
+		!(game.flags & F_FRANCE_REDUCED) &&
+		count_french_vp_markers_in_core_austria() > 0 &&
+		france_has_no_generals_in_core_austria()
+	) {
+		game.state = "france_reduces_military_objectives"
+		return
+	}
+
+	end_action_stage_2()
+}
+
+function end_action_stage_2() {
 	if (++game.stage === 4)
 		goto_end_turn()
 	else
@@ -1822,11 +1883,9 @@ function can_train_move_anywhere(p) {
 
 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(p, from, to))
 			return true
-	console.log(" fail")
 	return false
 }
 
@@ -1836,10 +1895,7 @@ 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)
@@ -2010,6 +2066,11 @@ function move_general_to(to, is_force_march) {
 		}
 	}
 
+	// return set-aside prussian victory markers when leaving prussia
+	if (pow === P_PRUSSIA && game.vp[SET_ASIDE_PRUSSIA] > 0)
+		if (!set_has(all_prussian_and_silesian_cities, to))
+			return_set_aside_markers(P_PRUSSIA, SET_ASIDE_PRUSSIA)
+
 	return stop
 }
 
@@ -2640,9 +2701,17 @@ states.recruit = {
 	},
 }
 
+function enter_piece_at(p, s) {
+	if (is_general(game.selected))
+		enter_general_at(game.selected, s)
+	else
+		enter_train_at(game.selected, s)
+}
+
 function enter_general_at(p, s) {
 	game.pos[p] = s
-	game.troops[p] = 1
+	if (game.troops[p] < 1)
+		game.troops[p] = 1
 
 	// remove hussars
 	for (let p of all_hussars) {
@@ -2659,6 +2728,11 @@ function enter_general_at(p, s) {
 			game.pos[p] = ELIMINATED
 		}
 	}
+
+	// return set-aside prussian victory markers when leaving prussia
+	if (piece_power[p] === P_PRUSSIA && game.vp[SET_ASIDE_PRUSSIA] > 0)
+		if (!set_has(all_prussian_and_silesian_cities, to))
+			return_set_aside_markers(P_PRUSSIA, SET_ASIDE_PRUSSIA)
 }
 
 function enter_train_at(p, s) {
@@ -2832,6 +2906,7 @@ states.combat_target = {
 
 		game.defender = game.pos[p]
 
+		// TODO: map_filter
 		for (let i = 0; i < game.combat.length; i += 2) {
 			if (game.combat[i] === game.attacker && game.combat[i+1] === game.defender) {
 				array_remove_pair(game.combat, i)
@@ -3404,6 +3479,11 @@ function goto_retroactive_conquest() {
 
 const power_victory_target = [ 11, 13, 8, 8 ]
 
+function return_set_aside_markers(pow, ix) {
+	log(power_name[pow] + " returned " + game.vp[ix] + " victory markers to pool.")
+	game.vp[ix] = 0
+}
+
 function elector_majority() {
 	let elector_france = 0
 	let elector_pragmatic = 0
@@ -3423,9 +3503,11 @@ function elector_majority() {
 function count_vp_markers(pow) {
 	let n = game.vp[pow]
 	if (pow === P_PRUSSIA) {
+		n += game.vp[SET_ASIDE_PRUSSIA]
 		if (game.flags & F_SILESIA_ANNEXED) ++n
 	}
 	if (pow === P_FRANCE) {
+		n += game.vp[SET_ASIDE_FRANCE]
 		if (game.flags & F_ITALY_FRANCE) ++n
 		if (game.flags & F_EMPEROR_FRANCE) ++n
 		if (elector_majority() === P_FRANCE) ++n
@@ -3924,7 +4006,9 @@ states.political_troops_place = {
 }
 
 function goto_mannheim_to_french_control() {
-	throw "TODO"
+	log("Mannheim to French control.")
+	map_set(game.elector, MANNHEIM, P_FRANCE)
+	next_execute_political_card()
 }
 
 function goto_pragmatic_general_to_england() {
@@ -4002,13 +4086,16 @@ function goto_adjust_political_tracks() {
 }
 
 function check_expeditionary_corps(power, space, active) {
-	if (active && !has_general_of_power(space, power)) {
+	let p = find_general_of_power(space, power)
+	if (active && p < 0) {
 		set_active_to_power(power)
+		game.selected = space
 		game.state = "send_expeditionary_corps_off_map"
 		return 1
 	}
-	if (!active && has_general_of_power(space, power)) {
+	if (!active && p >= 0) {
 		set_active_to_power(power)
+		game.selected = p
 		game.state = "bring_expeditionary_corps_on_map"
 		return 1
 	}
@@ -4038,6 +4125,51 @@ function goto_saxony_defection() {
 	end_adjust_political_tracks()
 }
 
+/* EXPEDITIONARY CORPS */
+
+states.bring_expeditionary_corps_on_map = {
+	inactive: "bring expeditionary corps onto map",
+	prompt() {
+		prompt("Bring expeditionary corps onto map.")
+		view.selected = game.selected
+		let entry = -1
+		if (game.power === P_PRUSSIA)
+			entry = WOLDENBURG
+		if (game.power === P_FRANCE)
+			entry = OMANS
+		if (game.power === P_AUSTRIA)
+			entry = STEINAMANGER
+		if (can_move_piece_to(game.selected, ELIMINATED, entry))
+			gen_action_space(entry)
+		else
+			for (let s of search_nearest_city(entry))
+				gen_action_space(s)
+	},
+	space(s) {
+		log("P" + game.selected + " to S" + s + ".")
+		enter_piece_at(game.selected, s)
+		game.selected = -1
+		goto_expeditionary_corps()
+	},
+}
+
+states.send_expeditionary_corps_off_map = {
+	inactive: "send expeditionary corps off map",
+	prompt() {
+		prompt("Send expeditionary corps off map.")
+		for (let p of all_power_generals[game.power])
+			if (is_piece_on_map_or_eliminated(p))
+				gen_action_piece(p)
+	},
+	piece(p) {
+		if (game.troops[p] === 0)
+			throw "TODO - pay for 2 troops minimum"
+		game.pos[p] = game.selected
+		game.selected = -1
+		goto_expeditionary_corps()
+	},
+}
+
 /* SAXONY'S DEFECTION */
 
 function search_nearest_city(p) {
@@ -4150,10 +4282,7 @@ 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])
+		for (let p of all_power_pieces[game.power])
 			if (set_has(data.country.Saxony, game.pos[p]))
 				gen_action_piece(p)
 	},
@@ -4173,10 +4302,7 @@ states.saxony_return_foreign_where = {
 	},
 	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)
+		enter_piece_at(game.selected, s)
 		game.selected = -1
 		goto_saxony_return_foreign_pieces()
 	},
@@ -4187,7 +4313,7 @@ states.saxony_return_home = {
 	prompt() {
 		prompt("Return pieces to their set-up cities.")
 		let done = true
-		for (let p of [ SAXONY_GENERAL, SAXONY_TRAIN ]) {
+		for (let p of all_power_pieces[P_SAXONY]) {
 			if (is_piece_on_map(p) && game.pos[p] !== setup_piece_position[p]) {
 				gen_action_piece(p)
 				done = false
@@ -4221,7 +4347,8 @@ function goto_saxony_becomes_austrian_ally() {
 		return
 	}
 
-	if (has_general_of_power(game.pos[SAXONY_GENERAL], P_PRUSSIA))
+	// if stacked with prussian general
+	if (find_general_of_power(game.pos[SAXONY_GENERAL], P_PRUSSIA) >= 0)
 		game.state = "saxony_move_general"
 	else
 		end_saxony_neutral()
@@ -4244,17 +4371,259 @@ states.saxony_move_general = {
 
 function end_saxony_neutral() {
 	game.selected = -1
-	if (game.stage === 100)
+
+	// Silesia annexed!
+	if (game.stage > 100) {
+		game.stage -= 100
+		goto_annex_silesia_return_austrian_pieces()
+		return
+	}
+
+	// Political Card event shift
+	if (game.stage === 100) {
 		end_adjust_political_tracks()
-	else
-		next_combat()
+		return
+	}
+
+	// Battle Victory shift
+	next_combat()
 }
 
-/* NEUTRALITY */
+/* PRUSSIA ANNEXES SILESIA */
 
 function is_prussia_neutral() {
-	// TODO
-	return false
+	return game.flags & F_PRUSSIA_NEUTRAL
+}
+
+function has_prussia_annexed_silesia() {
+	return !!(game.flags & F_SILESIA_ANNEXED)
+}
+
+function has_prussia_conquered_silesia() {
+	if (has_prussia_annexed_silesia())
+		return false
+	for (let s of all_silesian_fortresses) {
+		let pow = map_get(game.victory, s)
+		if (pow !== P_PRUSSIA)
+			return false
+	}
+	return true
+}
+
+states.offer_peace = {
+	inactive: "offer peace",
+	prompt() {
+		prompt("Annex Silesia and offer temporary peace with Austria?")
+		view.actions.offer = 1
+		view.actions.pass = 1
+	},
+	offer() {
+		set_active_to_power(P_AUSTRIA)
+		game.state = "accept_peace"
+	},
+	pass() {
+		end_action_stage_2()
+	},
+}
+
+states.accept_peace = {
+	inactive: "accept peace",
+	prompt() {
+		prompt("Accept Prussia's offer of temporary peace to annex Silesia?")
+		view.actions.accept = 1
+		view.actions.deny = 1
+	},
+	accept() {
+		goto_annex_silesia()
+	},
+	deny() {
+		end_action_stage_2()
+	},
+}
+
+function goto_annex_silesia() {
+	log("Silesia Annexed")
+
+	game.flags |= F_SILESIA_ANNEXED
+	game.flags |= F_PRUSSIA_NEUTRAL
+
+	// remove all austrian markers in prussia
+	// set aside half prussian markers in prussia
+	let n = 0
+	for (let s of all_prussian_and_silesian_fortresses) {
+		let pow = map_get(game.victory, s, -1)
+		if (pow === P_AUSTRIA) {
+			map_delete(game.victory, s)
+		}
+		if (pow === P_PRUSSIA) {
+			map_delete(game.victory, s)
+			++n
+		}
+	}
+	n = (n + 1) >> 1
+	log("Removed all Austrian victory markers.")
+	log("Set aside " + n + " Prussian victory markers.")
+	game.vp[SET_ASIDE_PRUSSIA] = n
+
+	if (is_saxony_prussian()) {
+		log("Saxony shifted to neutral.")
+		game.stage += 100
+		game.saxony = 3
+		goto_saxony_becomes_neutral()
+		return
+	}
+
+	goto_annex_silesia_return_austrian_pieces()
+
+	// return austrian pieces in prussia or Poland
+	// return prussian pieces outside prussia
+	// enter second supply train
+	// prussia is neutral until actions stage after next
+
+	// return markers if prussian piece leaves prussia
+}
+
+function goto_annex_silesia_return_austrian_pieces() {
+	set_active_to_power(P_AUSTRIA)
+	if (power_has_any_piece_in_list(P_AUSTRIA, all_prussian_and_silesian_and_polish_cities))
+		game.state = "silesia_return_austrian_who"
+	else
+		goto_annex_silesia_return_prussian_pieces()
+}
+
+states.silesia_return_austrian_who = {
+	inactive: "return Austrian pieces from Prussia and Poland",
+	prompt() {
+		prompt("Return pieces to the nearest city in their home country.")
+		for (let p of all_power_pieces[P_AUSTRIA])
+			if (set_has(all_prussian_and_silesian_and_polish_cities, game.pos[p]))
+				gen_action_piece(p)
+	},
+	piece(p) {
+		game.selected = p
+		game.state = "silesia_return_austrian_where"
+	},
+}
+
+states.silesia_return_austrian_where = {
+	inactive: "return Austrian pieces from Prussia and Poland",
+	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)
+		enter_piece_at(game.selected, s)
+		game.selected = -1
+		goto_annex_silesia_return_austrian_pieces()
+	},
+}
+
+function goto_annex_silesia_return_prussian_pieces() {
+	set_active_to_power(P_PRUSSIA)
+	game.state = "silesia_return_prussian_who"
+}
+
+states.silesia_return_prussian_who = {
+	inactive: "return Austrian pieces from Prussia and Poland",
+	prompt() {
+		prompt("Return pieces to the nearest city in their home country.")
+		let done = true
+		for (let p of all_power_pieces[P_PRUSSIA]) {
+			if (is_piece_on_map(p) && !set_has(all_prussian_and_silesian_cities, game.pos[p])) {
+				gen_action_piece(p)
+				done = false
+			}
+		}
+		if (done)
+			view.actions.next = 1
+	},
+	piece(p) {
+		push_undo()
+		game.selected = p
+		game.state = "silesia_return_prussian_where"
+	},
+	next() {
+		clear_undo()
+		end_action_stage_2()
+	},
+}
+
+states.silesia_return_prussian_where = {
+	inactive: "return Austrian pieces from Prussia and Poland",
+	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)
+		enter_piece_at(game.selected, s)
+		game.selected = -1
+		game.state = "silesia_return_prussian_who"
+	},
+}
+
+/* FRANCE REDUCES MILITARY OBJECTIVES */
+
+function count_french_vp_markers_in_core_austria() {
+	let n = 0
+	map_for_each(game.victory, (s, pow) => {
+		if (pow === P_FRANCE && set_has(all_core_austria_cities, s))
+			++n
+	})
+	return n
+}
+
+function remove_french_vp_markers_in_core_austria() {
+	// TODO: map_filter
+	for (let i = 0; i < game.victory.length; i += 2) {
+		if (game.victory[i+1] === P_FRANCE) {
+			if (set_has(all_core_austria_fortresses, game.victory[i])) {
+				array_remove_pair(game.victory, i)
+				i -= 2
+			}
+		}
+	}
+}
+
+function france_has_no_generals_in_core_austria() {
+	for (let p of all_power_generals[P_FRANCE]) {
+		let s = game.pos[p]
+		if (is_bohemia_space(s) && set_has(data.country.Austria, s))
+			return false
+	}
+	return true
+}
+
+states.france_reduces_military_objectives = {
+	inactive: "reduce military objectives",
+	prompt() {
+		prompt("Reduce military objectives?")
+		view.actions.reduce = 1
+		view.actions.pass = 1
+	},
+	reduce() {
+		game.flags |= F_FRANCE_REDUCED
+		let n = 0
+		for (let s of all_core_austria_fortresses) {
+			let pow = map_get(game.victory, s, -1)
+			if (pow === P_FRANCE) {
+				map_delete(game.victory, s)
+				++n
+			}
+		}
+		n = (n + 1) >> 1
+		log("France set aside " + n + " victory markers.")
+		game.vp[SET_ASIDE_FRANCE] = n
+		end_action_stage_2()
+	},
+	pass() {
+		end_action_stage_2()
+	},
 }
 
 /* SETUP */
@@ -4367,7 +4736,7 @@ const setup_piece_position = [
 
 function make_political_deck() {
 	let deck41 = [ 0, 1, 2, 3, 4, 5 ]
-	let deck42 = [ 6, 7, 8, 9, 10, 11, 25 ]
+	let deck42 = [ 6, 7, 8, 9, 10, 11, 24 ]
 	let deck43 = [ 12, 13, 14, 15, 16, 17 ]
 	let deck44 = [ 18, 19, 20, 21, 22, 23 ]
 	shuffle_bigint(deck41)
@@ -4417,7 +4786,7 @@ exports.setup = function (seed, _scenario, _options) {
 		turn: 0,
 		stage: 0,
 
-		vp: [ 0, 0, 0, 0 ], // battle victory points
+		vp: [ 0, 0, 0, 0, 0, 0 ], // battle victory points, set-aside VP
 		saxony: 2, // political track
 		russia: 6, // political track
 		italy: 5, // political track
-- 
cgit v1.2.3