summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.css7
-rw-r--r--play.js106
-rw-r--r--rules.js411
3 files changed, 405 insertions, 119 deletions
diff --git a/play.css b/play.css
index 92e7420..8e8cdf5 100644
--- a/play.css
+++ b/play.css
@@ -36,6 +36,9 @@ body {
);
}
+#role_Player_A { background-color: thistle; }
+#role_Player_B { background-color: whitesmoke; }
+
.role.active span { font-weight: bold }
.role_stat {
@@ -68,6 +71,10 @@ button:has(.saxony):active:hover { border-color: #005e17 #3e9d54 #3e9d54 #005e17
border: 1px solid black;
}
+#role_Player_A .role_marker {
+ width: 24px;
+}
+
.role_marker.france { background-color: #ed1c24 }
.role_marker.prussia { background-color: #005988 }
.role_marker.pragmatic { background-color: #5f5c5c }
diff --git a/play.js b/play.js
index cef2bdd..0e885e3 100644
--- a/play.js
+++ b/play.js
@@ -43,6 +43,9 @@ const R_LOUIS_XV = "Louis XV"
const R_FREDERICK = "Frederick"
const R_MARIA_THERESA = "Maria Theresa"
+const R_PLAYER_A = "Player A"
+const R_PLAYER_B = "Player B"
+
/* DATA (SHARED) */
const deck_name = [ "red", "green", "blue", "yellow" ]
@@ -80,6 +83,8 @@ const F_SAXONY_TC_ONCE = 128 // only draw TCs once per turn
const F_FRANCE_REDUCED = 256
const F_PRUSSIA_NEUTRAL = 512
const F_MOVE_FLANDERS = 1024
+const F_INTRODUCTORY = 2048
+const F_TWO_PLAYER = 4096
const SPADES = 0
const CLUBS = 1
@@ -265,6 +270,10 @@ function find_city(city) {
return data.cities.name.indexOf(city)
}
+function is_bohemia_space(s) {
+ return s >= 0 && s <= 401
+}
+
const all_electoral_colleges = [
// find_city("England"),
find_city("Prag"),
@@ -276,6 +285,7 @@ const all_electoral_colleges = [
find_city("Dresden"),
find_city("Berlin"),
]
+
const all_fortresses = []
set_add_all(all_fortresses, data.type.major_fortress)
set_add_all(all_fortresses, data.type.minor_fortress)
@@ -288,6 +298,30 @@ all_home_country_fortresses[P_AUSTRIA] = set_intersect(all_fortresses, data.coun
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_core_austria_fortresses = all_home_country_fortresses[P_AUSTRIA].filter(is_bohemia_space)
+const all_core_austria_and_silesia_fortresses = set_union(
+ all_core_austria_fortresses,
+ all_silesian_fortresses
+)
+
+function is_intro() {
+ return view.flags & F_INTRODUCTORY
+}
+
+function is_two_player() {
+ return view.flags & F_TWO_PLAYER
+}
+
+function count_victory_markers_in_country(pow, country) {
+ let n = 0
+ map_for_each(view.victory, (vspace, vpow) => {
+ if (vpow === pow && set_has(country, vspace))
+ ++n
+ })
+ return n
+}
+
/* SHOW PATHS */
const svgNS = "http://www.w3.org/2000/svg"
@@ -1162,15 +1196,37 @@ function is_saxony_prussian() {
}
function player_from_power(pow) {
+ if (is_two_player()) {
+ switch (pow) {
+ case P_FRANCE:
+ return R_PLAYER_A
+ case P_PRUSSIA:
+ return R_PLAYER_A
+ case P_PRAGMATIC:
+ if (is_intro())
+ return "NONE"
+ return R_PLAYER_B
+ case P_AUSTRIA:
+ return R_PLAYER_B
+ case P_SAXONY:
+ if (is_saxony_prussian())
+ return R_PLAYER_A
+ else
+ return R_PLAYER_B
+ case P_BAVARIA:
+ return R_PLAYER_A
+ }
+ }
+
switch (pow) {
case P_FRANCE:
return R_LOUIS_XV
case P_PRUSSIA:
return R_FREDERICK
case P_PRAGMATIC:
- if (view.pol_deck)
- return R_FREDERICK
- return "NONE"
+ if (is_intro())
+ return "NONE"
+ return R_FREDERICK
case P_AUSTRIA:
return R_MARIA_THERESA
case P_SAXONY:
@@ -1273,9 +1329,14 @@ function on_update() {
ui.header.classList.toggle("pragmatic", view.power === P_PRAGMATIC)
ui.header.classList.toggle("austria", view.power === P_AUSTRIA)
- update_player_power_list(R_MARIA_THERESA)
- update_player_power_list(R_FREDERICK)
- update_player_power_list(R_LOUIS_XV)
+ if (is_two_player()) {
+ update_player_power_list(R_PLAYER_A)
+ update_player_power_list(R_PLAYER_B)
+ } else {
+ update_player_power_list(R_MARIA_THERESA)
+ update_player_power_list(R_FREDERICK)
+ update_player_power_list(R_LOUIS_XV)
+ }
used_victory.fill(0)
used_elector.fill(0)
@@ -1284,7 +1345,7 @@ function on_update() {
back.fill(0)
sort_power_panel(true)
- if (!view.pol_deck) {
+ if (is_intro()) {
ui.power_panel[P_PRAGMATIC].style.display = "none"
ui.power_panel[6].style.display = "none"
}
@@ -1309,6 +1370,18 @@ function on_update() {
banner += " \u2014 Neutral"
if (pow === P_FRANCE && (view.flags & F_WAR_OF_JENKINS_EAR))
banner += " \u2014 receives 1 TC less"
+
+ if (is_intro()) {
+ if (pow === P_FRANCE) {
+ let n = count_victory_markers_in_country(P_FRANCE, all_core_austria_fortresses)
+ banner += ` \u2014 ${n} / 9 fortresses`
+ }
+ if (pow === P_PRUSSIA) {
+ let n = count_victory_markers_in_country(P_PRUSSIA, all_core_austria_and_silesia_fortresses)
+ banner += ` \u2014 ${n} / 12 fortresses`
+ }
+ }
+
ui.power_header[pow].innerHTML = banner
ui.hand[pow].replaceChildren()
@@ -1328,9 +1401,6 @@ function on_update() {
ui.hand[view.power].appendChild(ui.tc[c])
}
- if (view.pol_deck)
- update_political()
-
for (let deck = 0; deck < 4; ++deck) {
ui.discard[deck].replaceChildren()
for (let i = 0; i < view.discard[deck]; ++i)
@@ -1344,7 +1414,9 @@ function on_update() {
for (let i = 0; i < view.retro.length; i += 2)
layout_retro(view.retro[i], view.retro[i+1])
- if (view.pol_deck) {
+ if (!is_intro()) {
+ update_political()
+
for (let i = 0; i < view.elector.length; i += 2)
layout_elector(view.elector[i], view.elector[i+1])
@@ -1418,7 +1490,10 @@ function on_update() {
action_button_with_argument("shift", -1, "\u2b05")
action_button_with_argument("shift", +1, "\u27a1")
action_button_with_argument("shift", +2, "\u27a1\u27a1")
+ action_button_with_argument("shift", +3, "\u27a1\u27a1\u27a1")
action_button_with_argument("shift", +4, "\u27a1\u27a1\u27a1\u27a1")
+ action_button_with_argument("shift", +5, "\u27a1\u27a1\u27a1\u27a1")
+ action_button_with_argument("shift", +6, "\u27a1\u27a1\u27a1\u27a1")
for (let v = 16; v >= 0; --v)
action_button_with_argument("value", v, v)
@@ -1603,6 +1678,15 @@ function set_add_all(set, other) {
set_add(set, item)
}
+function set_union(one, two) {
+ let set = []
+ for (let item of one)
+ set_add(set, item)
+ for (let item of two)
+ set_add(set, item)
+ return set
+}
+
function set_intersect(one, two) {
let set = []
for (let item of one)
diff --git a/rules.js b/rules.js
index 9a1395b..8b4303e 100644
--- a/rules.js
+++ b/rules.js
@@ -20,22 +20,27 @@ const R_MARIA_THERESA = "Maria Theresa"
const R_PLAYER_A = "Player A"
const R_PLAYER_B = "Player B"
-exports.roles = [
- R_MARIA_THERESA,
- R_FREDERICK,
- R_LOUIS_XV
-]
-
-const SCENARIO_INDEX = {
- "Advanced": 0,
- "Introductory": 1,
-}
+const ROLES_3P = [ R_MARIA_THERESA, R_FREDERICK, R_LOUIS_XV ]
+const ROLES_2P = [ R_PLAYER_A, R_PLAYER_B ]
exports.scenarios = [
"Advanced",
+ "Advanced 2P",
"Introductory",
+ "Introductory 2P",
]
+exports.roles = function (scenario, _options) {
+ switch (scenario) {
+ case "Advanced":
+ case "Introductory":
+ return ROLES_3P
+ case "Advanced 2P":
+ case "Introductory 2P":
+ return ROLES_2P
+ }
+}
+
const data = require("./data.js")
const political_cards = require("./cards.js")
@@ -61,7 +66,13 @@ 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" ]
+const power_class = [
+ "france", "prussia", "pragmatic", "austria", "bavaria", "saxony",
+ "france bavaria",
+ "prussia saxony",
+ "austria pragmatic",
+ "austria pragmatic saxony",
+]
const F_EMPEROR_FRANCE = 1
const F_EMPEROR_AUSTRIA = 2
@@ -74,6 +85,10 @@ const F_SAXONY_TC_ONCE = 128 // only draw TCs once per turn
const F_FRANCE_REDUCED = 256
const F_PRUSSIA_NEUTRAL = 512
const F_MOVE_FLANDERS = 1024
+const F_INTRODUCTORY = 2048
+const F_TWO_PLAYER = 4096
+const F_PLAYER_A_PICK = 8192 // picked PC card
+const F_PLAYER_B_PICK = 16384
const SPADES = 0
const CLUBS = 1
@@ -87,11 +102,11 @@ const ELIMINATED = data.cities.name.length
const ARENBERG = 17
const SAXONY_GENERAL = 19
-const SAXONY_TRAIN = 29
const PRUSSIAN_TRAIN_2 = 23
const all_powers = [ 0, 1, 2, 3, 4, 5 ]
const all_major_powers = [ 0, 1, 2, 3 ]
+const all_minor_powers = [ 4, 5 ]
const all_power_generals = [
[ 0, 1, 2, 3, 4 ],
@@ -462,6 +477,11 @@ function all_controlled_powers(pow) {
case P_PRAGMATIC:
return all_powers_pragmatic
case P_AUSTRIA:
+ if (is_two_player() && !is_intro()) {
+ if (is_saxony_austrian())
+ return all_powers_pragmatic_austria_saxony
+ return all_powers_pragmatic_austria
+ }
if (is_saxony_austrian())
return all_powers_austria_saxony
return all_powers_austria
@@ -479,6 +499,11 @@ function all_controlled_generals(pow) {
case P_PRAGMATIC:
return all_power_generals[P_PRAGMATIC]
case P_AUSTRIA:
+ if (is_two_player() && !is_intro()) {
+ if (is_saxony_austrian())
+ return all_austria_pragmatic_saxony_generals
+ return all_austria_pragmatic_generals
+ }
if (is_saxony_austrian())
return all_austria_saxony_generals
return all_power_generals[P_AUSTRIA]
@@ -496,6 +521,11 @@ function all_controlled_trains(pow) {
case P_PRAGMATIC:
return all_power_trains[P_PRAGMATIC]
case P_AUSTRIA:
+ if (is_two_player() && !is_intro()) {
+ if (is_saxony_austrian())
+ return all_austria_pragmatic_saxony_trains
+ return all_austria_pragmatic_trains
+ }
if (is_saxony_austrian())
return all_austria_saxony_trains
return all_power_trains[P_AUSTRIA]
@@ -726,6 +756,10 @@ const all_silesian_major_fortresses = set_intersect(data.type.major_fortress, da
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_core_austria_and_silesia_fortresses = set_union(
+ all_core_austria_fortresses,
+ all_silesian_fortresses
+)
const all_prussian_and_silesian_fortresses = set_union(
all_home_country_fortresses[P_PRUSSIA],
@@ -847,7 +881,6 @@ function set_control_of_fortress(s, pow) {
map_delete(game.victory, s)
}
-
function is_space_protected_by_piece(s, p) {
return set_has(protect_range[s], game.pos[p])
}
@@ -867,11 +900,11 @@ function is_protected_from_conquest(s) {
/* STATE */
function is_intro() {
- return game.scenario & 1
+ return game.flags & F_INTRODUCTORY
}
function is_two_player() {
- return game.scenario & 2
+ return game.flags & F_TWO_PLAYER
}
const tc_per_turn_table = [ 5, 3, 3, 5, 1, 1 ]
@@ -925,6 +958,26 @@ function tc_per_turn() {
}
function player_from_power(pow) {
+ if (is_two_player()) {
+ switch (pow) {
+ case P_FRANCE:
+ return R_PLAYER_A
+ case P_PRUSSIA:
+ return R_PLAYER_A
+ case P_PRAGMATIC:
+ return R_PLAYER_B
+ case P_AUSTRIA:
+ return R_PLAYER_B
+ case P_SAXONY:
+ if (is_saxony_prussian())
+ return R_PLAYER_A
+ else
+ return R_PLAYER_B
+ case P_BAVARIA:
+ return R_PLAYER_A
+ }
+ }
+
switch (pow) {
case P_FRANCE:
return R_LOUIS_XV
@@ -1045,7 +1098,7 @@ function has_enemy_piece(to) {
function has_non_cooperative_general(to) {
for (let other of all_non_coop_powers(game.power))
- for (let p of all_controlled_generals(other))
+ for (let p of all_power_generals[other])
if (game.pos[p] === to)
return true
return false
@@ -1139,6 +1192,7 @@ const advanced_sequence_of_play = [
{ power: P_FRANCE, action: goto_tactical_cards },
{ power: P_BAVARIA, action: goto_tactical_cards },
{ power: P_FRANCE, action: goto_supply },
+ { power: P_BAVARIA, action: goto_supply },
{ power: P_FRANCE, action: init_movement },
{ power: P_FRANCE, action: goto_movement_global },
{ power: P_FRANCE, action: end_movement },
@@ -1148,16 +1202,9 @@ const advanced_sequence_of_play = [
{ power: P_PRUSSIA, action: start_action_stage },
{ power: P_PRUSSIA, action: goto_tactical_cards },
- {
- power: P_SAXONY,
- action() {
- if (is_saxony_prussian())
- goto_tactical_cards()
- else
- next_sequence_of_play()
- },
- },
+ { power: P_SAXONY, action: goto_tactical_cards, condition: is_saxony_prussian },
{ power: P_PRUSSIA, action: goto_supply },
+ { power: P_SAXONY, action: goto_supply, condition: is_saxony_prussian },
{ power: P_PRUSSIA, action: init_movement },
{ power: P_PRUSSIA, action: goto_movement_global },
{ power: P_PRUSSIA, action: end_movement },
@@ -1167,18 +1214,10 @@ const advanced_sequence_of_play = [
{ power: P_PRUSSIA, action: goto_prussia_annexes_silesia },
{ power: P_AUSTRIA, action: start_action_stage },
-
{ power: P_AUSTRIA, action: goto_tactical_cards },
- {
- power: P_SAXONY,
- action() {
- if (is_saxony_prussian())
- next_sequence_of_play()
- else
- goto_tactical_cards()
- },
- },
+ { power: P_SAXONY, action: goto_tactical_cards, condition: is_saxony_austrian },
{ power: P_AUSTRIA, action: goto_supply },
+ { power: P_SAXONY, action: goto_supply, condition: is_saxony_austrian },
{ power: P_PRAGMATIC, action: goto_tactical_cards },
{ power: P_PRAGMATIC, action: goto_supply },
@@ -1202,6 +1241,55 @@ const advanced_sequence_of_play = [
{ power: P_FRANCE, action: goto_end_turn },
]
+const two_player_sequence_of_play = [
+ { power: P_AUSTRIA, action: goto_place_hussars },
+
+ { power: P_FRANCE, action: start_action_stage },
+ { power: P_FRANCE, action: goto_tactical_cards },
+ { power: P_BAVARIA, action: goto_tactical_cards },
+ { power: P_FRANCE, action: goto_supply },
+ { power: P_BAVARIA, action: goto_supply },
+ { power: P_FRANCE, action: init_movement },
+ { power: P_FRANCE, action: goto_movement_global },
+ { power: P_FRANCE, action: end_movement },
+ { power: P_FRANCE, action: goto_combat },
+ { power: P_FRANCE, action: end_action_stage },
+ { power: P_FRANCE, action: goto_france_reduces_military_objectives },
+
+ { power: P_PRUSSIA, action: start_action_stage },
+ { power: P_PRUSSIA, action: goto_tactical_cards },
+ { power: P_SAXONY, action: goto_tactical_cards, condition: is_saxony_prussian },
+ { power: P_PRUSSIA, action: goto_supply },
+ { power: P_SAXONY, action: goto_supply, condition: is_saxony_prussian },
+ { power: P_PRUSSIA, action: init_movement },
+ { power: P_PRUSSIA, action: goto_movement_global },
+ { power: P_PRUSSIA, action: end_movement },
+ { power: P_PRUSSIA, action: goto_combat },
+ { power: P_PRUSSIA, action: end_action_stage },
+ { power: P_PRUSSIA, action: goto_prussia_ends_neutrality },
+ { power: P_PRUSSIA, action: goto_prussia_annexes_silesia },
+
+ { power: P_AUSTRIA, action: start_action_stage },
+ { power: P_AUSTRIA, action: goto_tactical_cards },
+ { power: P_SAXONY, action: goto_tactical_cards, condition: is_saxony_austrian },
+ { power: P_PRAGMATIC, action: goto_tactical_cards },
+ { power: P_AUSTRIA, action: goto_supply },
+ { power: P_SAXONY, action: goto_supply, condition: is_saxony_austrian },
+ { power: P_PRAGMATIC, action: goto_supply },
+
+ // austria and pragmatic share movement (like with minor power)
+ { power: P_AUSTRIA, action: init_movement },
+ { power: P_AUSTRIA, action: goto_movement_global },
+ { power: P_AUSTRIA, action: end_movement },
+
+ // austria and pragmatic share combat (like with minor power)
+ { power: P_AUSTRIA, action: goto_combat },
+
+ { power: P_AUSTRIA, action: end_action_stage },
+
+ { power: P_FRANCE, action: goto_end_turn },
+]
+
const intro_sequence_of_play = [
{ power: P_AUSTRIA, action: goto_place_hussars },
@@ -1209,6 +1297,7 @@ const intro_sequence_of_play = [
{ power: P_FRANCE, action: goto_tactical_cards },
{ power: P_BAVARIA, action: goto_tactical_cards },
{ power: P_FRANCE, action: goto_supply },
+ { power: P_BAVARIA, action: goto_supply },
{ power: P_FRANCE, action: init_movement },
{ power: P_FRANCE, action: goto_movement_global },
{ power: P_FRANCE, action: end_movement },
@@ -1217,16 +1306,9 @@ const intro_sequence_of_play = [
{ power: P_PRUSSIA, action: start_action_stage },
{ power: P_PRUSSIA, action: goto_tactical_cards },
- {
- power: P_SAXONY,
- action() {
- if (is_saxony_prussian())
- goto_tactical_cards()
- else
- next_sequence_of_play()
- },
- },
+ { power: P_SAXONY, action: goto_tactical_cards },
{ power: P_PRUSSIA, action: goto_supply },
+ { power: P_SAXONY, action: goto_supply },
{ power: P_PRUSSIA, action: init_movement },
{ power: P_PRUSSIA, action: goto_movement_global },
{ power: P_PRUSSIA, action: end_movement },
@@ -1235,15 +1317,6 @@ const intro_sequence_of_play = [
{ power: P_AUSTRIA, action: start_action_stage },
{ power: P_AUSTRIA, action: goto_tactical_cards },
- {
- power: P_SAXONY,
- action() {
- if (is_saxony_prussian())
- next_sequence_of_play()
- else
- goto_tactical_cards()
- },
- },
{ power: P_AUSTRIA, action: goto_supply },
{ power: P_AUSTRIA, action: init_movement },
{ power: P_AUSTRIA, action: goto_movement_global },
@@ -1262,7 +1335,9 @@ function start_sequence_of_play() {
function current_sequence_of_play() {
if (is_intro())
return intro_sequence_of_play[game.stage]
- return sequence_of_play[game.stage]
+ if (is_two_player())
+ return two_player_sequence_of_play[game.stage]
+ return advanced_sequence_of_play[game.stage]
}
function set_active_to_current_sequence_of_play() {
@@ -1273,7 +1348,10 @@ function next_sequence_of_play() {
++game.stage
let row = current_sequence_of_play()
set_active_to_power(row.power)
- row.action()
+ if (row.condition && !row.condition())
+ next_sequence_of_play()
+ else
+ row.action()
}
function start_action_stage() {
@@ -1725,13 +1803,15 @@ function search_supply_path(who) {
}
function goto_supply() {
+ game.count = 0 // to track saxony shift to oos
+
game.supply = {
hussars: [],
restore: [],
suffer: [],
}
- for (let p of all_controlled_generals(game.power)) {
+ for (let p of all_power_generals[game.power]) {
if (!is_piece_on_map(p))
continue
@@ -1796,6 +1876,7 @@ function goto_supply_suffer() {
log_br()
log("Out of supply")
game.state = "supply_suffer"
+ game.count = 0 // number of lost VP
}
states.supply_hussars = {
@@ -1910,19 +1991,22 @@ states.supply_suffer = {
gen_action_piece(p)
},
piece(p) {
- log(`>P${p} at S${game.pos[p]}`)
+ let s = game.pos[p]
+ log(`>P${p} at S${s}`)
set_delete(game.supply.suffer, p)
if (is_out_of_supply(p)) {
- //log(`>P${p} at S${game.pos[p]} -2 troops`)
+ //log(`>P${p} at S${s} -2 troops`)
game.troops[p] -= 2
} else {
- //log(`>P${p} at S${game.pos[p]} -1 troop`)
+ //log(`>P${p} at S${s} -1 troop`)
set_out_of_supply(p)
game.troops[p] -= 1
}
if (game.troops[p] <= 0) {
eliminate_general(p, true)
lose_vp(coop_major_power(game.power), 1)
+ if (is_bohemia_space(s))
+ game.count += 1 // for saxony shift
}
if (game.supply.suffer.length === 0)
resume_supply()
@@ -1941,7 +2025,13 @@ states.supply_done = {
}
function end_supply() {
+ if (game.count > 0 && should_saxony_shift_after_supply(coop_major_power(game.power))) {
+ game.state = "saxony_shift_supply"
+ return
+ }
+
delete game.supply
+
next_sequence_of_play()
}
@@ -2039,15 +2129,13 @@ function goto_movement_bohemia() {
next_sequence_of_play()
}
-function goto_movement() {
- game.state = "movement"
-}
-
function resume_movement() {
+ if (is_two_player() && game.power === P_PRAGMATIC)
+ game.power = P_AUSTRIA
set_active_to_power(coop_major_power(game.power))
game.selected = -1
if (game.flags & F_MOVE_FLANDERS) {
- let row = sequence_of_play[game.stage+1]
+ let row = advanced_sequence_of_play[game.stage+1]
if (row.action === goto_movement_flanders) {
let next = game.power === P_PRAGMATIC ? P_AUSTRIA : P_PRAGMATIC
if (!has_unmoved_piece_on_flanders_map(next)) {
@@ -2067,7 +2155,7 @@ function resume_movement() {
function resume_movement_after_flanders_stacking() {
set_active_to_power(coop_major_power(game.power))
game.selected = -1
- let row = sequence_of_play[game.stage+1]
+ let row = advanced_sequence_of_play[game.stage+1]
if (row.action === goto_movement_flanders) {
next_sequence_of_play()
} else {
@@ -2810,13 +2898,13 @@ states.confirm_flanders_stack_3 = {
//view.selected = game.selected
//view.actions.next = 1
},
- piece(p) {
+ piece(_) {
this.next()
},
next() {
push_undo()
- let s = game.forbidden
/*
+ let s = game.forbidden
if (game.power === P_AUSTRIA)
log("Pragmatic Army refused to stack at S" + s + ".")
else
@@ -3021,6 +3109,13 @@ function goto_winter_turn() {
for (let pow of all_major_powers)
game.score[pow].push(count_vp_markers_in_pool(pow))
+ if (is_intro()) {
+ if (game.turn === 10) {
+ goto_game_over(player_from_power(P_AUSTRIA), "Austria survived 9 turns!")
+ return
+ }
+ }
+
if (game.turn === 16) {
log("Winter Scores")
let best_score = 1000
@@ -3035,7 +3130,7 @@ function goto_winter_turn() {
best_power = pow
}
}
- goto_game_over(player_from_power(best_power), power_name[best] + " won!")
+ goto_game_over(player_from_power(best_power), power_name[best_power] + " won!")
return
}
@@ -3230,7 +3325,7 @@ function enter_general_at(p, s) {
// 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))
+ if (!set_has(all_prussian_and_silesian_cities, s))
return_set_aside_markers(P_PRUSSIA, SET_ASIDE_PRUSSIA)
}
@@ -3359,7 +3454,6 @@ function next_combat() {
goto_retroactive_conquest()
}
-
states.combat = {
inactive: "attack",
prompt() {
@@ -3926,10 +4020,10 @@ function finish_combat() {
delete game.lost_generals
delete game.lost_troops
- if (game.loser_power === P_PRUSSIA && n > 0 && game.saxony < 5) {
+ if (should_shift_saxony_after_battle(n)) {
game.count = n
set_active_to_power(P_AUSTRIA)
- game.state = "saxony_shift"
+ game.state = "saxony_shift_battle"
return
}
@@ -4033,7 +4127,43 @@ function count_vp_markers_in_pool(pow) {
return Math.max(0, power_victory_target[pow] - count_vp_markers(pow))
}
+function count_victory_markers_in_country(pow, country) {
+ let n = 0
+ map_for_each(game.victory, (vspace, vpow) => {
+ if (vpow === pow && set_has(country, vspace))
+ ++n
+ })
+ return n
+}
+
+function check_instant_victory_intro() {
+ let win_france = count_victory_markers_in_country(P_FRANCE, all_core_austria_fortresses) >= 9
+ let win_prussia = count_victory_markers_in_country(P_PRUSSIA, all_core_austria_and_silesia_fortresses) >= 12
+
+ if (is_two_player()) {
+ if (win_france && win_prussia) {
+ goto_game_over(R_PLAYER_A, "France controls 9 fortresses and Prussia controls 12!")
+ return true
+ }
+ } else {
+ if (win_france) {
+ goto_game_over(player_from_power(P_FRANCE), "France controls 9 fortresses in Austria!")
+ return true
+ }
+ if (win_prussia) {
+ goto_game_over(player_from_power(P_PRUSSIA), "Prussia controls 12 fortresses in Austria and Silesia!")
+ return true
+ }
+ }
+
+ return false
+}
+
function check_instant_victory() {
+
+ if (is_intro())
+ return check_instant_victory_intro()
+
let margin = [
count_vp_markers(P_FRANCE) - 11,
count_vp_markers(P_PRUSSIA) - 13,
@@ -4245,13 +4375,32 @@ function goto_select_political_card() {
game.stage |= 1 << pow
set_active_to_power(pow)
log("=" + game.power)
+
game.state = "select_political_card"
+ if (is_two_player()) {
+ if ((game.flags & F_PLAYER_A_PICK) && (game.power === P_FRANCE || game.power === P_PRUSSIA))
+ game.state = "must_save_tc"
+ if ((game.flags & F_PLAYER_B_PICK) && (game.power === P_PRAGMATIC || game.power === P_AUSTRIA))
+ game.state = "must_save_tc"
+ }
}
} else {
end_politics()
}
}
+states.must_save_tc = {
+ inactive: "select a political card",
+ prompt() {
+ prompt(`Save your TC.`)
+ view.actions.save = 1
+ },
+ save() {
+ log(power_name[game.power] + " saved her TC.")
+ goto_select_political_card()
+ },
+}
+
states.select_political_card = {
inactive: "select a political card",
prompt() {
@@ -4268,12 +4417,19 @@ states.select_political_card = {
// face-up TCs to discard
game.face_up[game.power] = []
+ if (is_two_player()) {
+ if (game.power === P_FRANCE || game.power === P_PRUSSIA)
+ game.flags |= F_PLAYER_A_PICK
+ if (game.power === P_PRAGMATIC || game.power === P_AUSTRIA)
+ game.flags |= F_PLAYER_B_PICK
+ }
+
game.pc = pc
game.pcx = -1
game.state = "political_card_discard_or_execute"
},
save() {
- log(power_name[game.power] + " saved its TC.")
+ log(power_name[game.power] + " saved her TC.")
goto_select_political_card()
},
}
@@ -4303,6 +4459,11 @@ function end_politics() {
game.trump = -1
+ if (is_two_player()) {
+ game.flags &= ~F_PLAYER_A_PICK
+ game.flags &= ~F_PLAYER_B_PICK
+ }
+
// did not take a political turn; flip all cards face-down
for (let pow of all_major_powers) {
if (!(game.stage & (1 << pow))) {
@@ -4462,6 +4623,7 @@ states.political_troop_power = {
game.count = info.troops
},
pass() {
+ let info = event_troops[current_political_effect()]
if (info.tcs > 0)
log("Did not receive TC and troops.")
else
@@ -4574,7 +4736,23 @@ function is_saxony_austrian_ally() {
return game.saxony === 5
}
-states.saxony_shift = {
+function should_shift_saxony_after_battle(n) {
+ if (n > 0 && game.saxony < 5) {
+ if (game.loser_power === P_PRUSSIA)
+ return true
+ if (is_two_player() && game.loser_power === P_FRANCE && is_bohemia_space(game.attacker))
+ return true
+ }
+}
+
+function should_saxony_shift_after_supply(pow) {
+ if (pow === P_PRUSSIA)
+ return true
+ if (is_two_player() && pow === P_FRANCE)
+ return true
+}
+
+states.saxony_shift_battle = {
inactive: "shift Saxony marker",
prompt() {
prompt(`Shift Saxony marker ${game.count} to the right.`)
@@ -4602,6 +4780,36 @@ states.saxony_shift = {
},
}
+states.saxony_shift_supply = {
+ inactive: "shift Saxony marker",
+ prompt() {
+ prompt(`Shift Saxony marker up to ${game.count} to the right.`)
+ view.actions.shift = []
+ for (let i = 1; i <= game.count; ++i)
+ view.actions.shift.push(i)
+ view.actions.pass = 1
+ },
+ shift(n) {
+ log("Saxony marker " + n + " right.")
+ let save_saxony = game.saxony
+ game.saxony = Math.max(1, Math.min(5, game.saxony + n))
+ game.count = 0
+
+ // Saxony defection
+ if (save_saxony < 3 && is_saxony_neutral())
+ goto_saxony_becomes_neutral()
+ else if (save_saxony < 5 && is_saxony_austrian_ally())
+ goto_saxony_becomes_austrian_ally()
+ else
+ resume_supply()
+ },
+ pass() {
+ log("Did not shift Saxony marker.")
+ game.count = 0
+ resume_supply()
+ },
+}
+
function goto_adjust_political_tracks() {
// clamp final values to track
game.saxony = Math.max(1, Math.min(5, game.saxony))
@@ -4898,7 +5106,6 @@ function goto_saxony_becomes_austrian_ally() {
if (game.pos[SAXONY_GENERAL] === ELIMINATED) {
game.special_saxony_recruit = 1
goto_recruit()
- throw "STOP TEST"
return
}
@@ -4940,6 +5147,12 @@ function end_saxony_neutral() {
return
}
+ // Out of Supply shift
+ if (game.supply) {
+ end_supply()
+ return
+ }
+
// Battle Victory shift
next_combat()
}
@@ -5205,18 +5418,6 @@ function count_french_vp_markers_in_core_austria() {
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]
@@ -5388,7 +5589,7 @@ states.propose_subsidy_to = {
let player = game.power
let from = game.proposal.from
prompt(`Subsidy contract between ${power_name[from]} and who?`)
- for (let to of all_powers) {
+ for (let to of (is_two_player() ? all_minor_powers : all_powers)) {
if (from !== to && is_allied_power(from, to)) {
if (is_controlled_power(player, from) || is_controlled_power(player, to))
gen_action_power(to)
@@ -5455,7 +5656,7 @@ function may_cancel_subsidy() {
let player = game.power
let result = false
for (let from of all_major_powers) {
- map_for_each(game.contracts[from], (to, n) => {
+ map_for_each(game.contracts[from], (to, _n) => {
// cannot cancel initial contract!
if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3)
return
@@ -5477,7 +5678,7 @@ states.cancel_subsidy_from = {
let player = game.power
prompt("Cancel which subsidy contract?")
for (let from of all_major_powers) {
- map_for_each(game.contracts[from], (to, n) => {
+ map_for_each(game.contracts[from], (to, _n) => {
// cannot cancel initial contract!
if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3)
return
@@ -5498,7 +5699,7 @@ states.cancel_subsidy_to = {
let player = game.power
let from = game.proposal.from
prompt(`Cancel subsidy contract between ${power_name[from]} and who?`)
- map_for_each(game.contracts[from], (to, n) => {
+ map_for_each(game.contracts[from], (to, _n) => {
// cannot cancel initial contract!
if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3)
return
@@ -5534,9 +5735,8 @@ states.cancel_subsidy_approve = {
map_delete(game.contracts[from], to)
end_cancel_subsidy()
},
- refuse(n) {
+ refuse() {
let from = game.proposal.from
- let to = game.proposal.to
log(power_name[game.power] + ` refused to cancel subsidy contract from ${power_name[from]}.`)
end_cancel_subsidy()
},
@@ -5709,7 +5909,6 @@ function make_tactics_discard(n) {
exports.setup = function (seed, scenario, _options) {
game = {
seed: seed,
- scenario: SCENARIO_INDEX[scenario],
undo: [],
log: [],
@@ -5762,8 +5961,10 @@ exports.setup = function (seed, scenario, _options) {
count: 0,
}
- if (is_intro()) {
- log("Introductory")
+ if (scenario.startsWith("Introductory")) {
+ log("# Introductory")
+
+ game.flags |= F_INTRODUCTORY
game.troops[ARENBERG] = 4
game.troops[0] = 3
@@ -5771,6 +5972,12 @@ exports.setup = function (seed, scenario, _options) {
game.troops[2] = 8
game.troops[3] = 3
game.troops[4] = 4
+ } else {
+ log("# Advanced")
+ }
+
+ if (scenario.endsWith("2P")) {
+ game.flags |= F_TWO_PLAYER
}
if (!is_intro())
@@ -5964,17 +6171,6 @@ function is_trump_card(c) {
return (game.trump >= 0) && (is_reserve(c) || to_suit(c) === game.trump)
}
-function total_troops_list() {
- let list = []
- for (let pow of all_powers) {
- let n = 0
- for (let p of all_power_generals[pow])
- n += game.troops[p]
- list[pow] = n
- }
- return list
-}
-
exports.view = function (state, player) {
game = state
view = {
@@ -5997,7 +6193,6 @@ exports.view = function (state, player) {
troops: mask_troops(player),
hand1: mask_hand1(player),
hand2: mask_hand2(player),
- // pt: total_troops_list(),
discard: total_discard_list(),
contracts: game.contracts,