summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pieces/cylinder_austria_oos.svg9
-rw-r--r--pieces/cylinder_bavaria_oos.svg9
-rw-r--r--pieces/cylinder_france_oos.svg9
-rw-r--r--pieces/cylinder_pragmatic_oos.svg9
-rw-r--r--pieces/cylinder_prussia_oos.svg9
-rw-r--r--pieces/cylinder_saxony_oos.svg9
-rw-r--r--play.css8
-rw-r--r--play.js49
-rw-r--r--rules.js720
-rw-r--r--tools/genpieces.mjs6
10 files changed, 521 insertions, 316 deletions
diff --git a/pieces/cylinder_austria_oos.svg b/pieces/cylinder_austria_oos.svg
new file mode 100644
index 0000000..9c8e35d
--- /dev/null
+++ b/pieces/cylinder_austria_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#dedede"/>
+<stop offset="50%" stop-color="#bebebe"/>
+<stop offset="100%" stop-color="#9e9e9e"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#484848" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#ffffff" stroke="#484848" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/pieces/cylinder_bavaria_oos.svg b/pieces/cylinder_bavaria_oos.svg
new file mode 100644
index 0000000..28f4626
--- /dev/null
+++ b/pieces/cylinder_bavaria_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#e2ac00"/>
+<stop offset="50%" stop-color="#c69100"/>
+<stop offset="100%" stop-color="#ab7600"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#5c2a00" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#ffc825" stroke="#5c2a00" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/pieces/cylinder_france_oos.svg b/pieces/cylinder_france_oos.svg
new file mode 100644
index 0000000..f993d76
--- /dev/null
+++ b/pieces/cylinder_france_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#d70007"/>
+<stop offset="50%" stop-color="#c00000"/>
+<stop offset="100%" stop-color="#aa0000"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#680000" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#ed1c24" stroke="#680000" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/pieces/cylinder_pragmatic_oos.svg b/pieces/cylinder_pragmatic_oos.svg
new file mode 100644
index 0000000..79187e9
--- /dev/null
+++ b/pieces/cylinder_pragmatic_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#524f4f"/>
+<stop offset="50%" stop-color="#454242"/>
+<stop offset="100%" stop-color="#383636"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#161313" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#5f5c5c" stroke="#161313" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/pieces/cylinder_prussia_oos.svg b/pieces/cylinder_prussia_oos.svg
new file mode 100644
index 0000000..7d994d9
--- /dev/null
+++ b/pieces/cylinder_prussia_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#004c7a"/>
+<stop offset="50%" stop-color="#00406d"/>
+<stop offset="100%" stop-color="#003460"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#000f3a" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#005988" stroke="#000f3a" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/pieces/cylinder_saxony_oos.svg b/pieces/cylinder_saxony_oos.svg
new file mode 100644
index 0000000..ea814ac
--- /dev/null
+++ b/pieces/cylinder_saxony_oos.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="42" height="47">
+<linearGradient id="g">
+<stop offset="0%" stop-color="#006e27"/>
+<stop offset="50%" stop-color="#005e17"/>
+<stop offset="100%" stop-color="#004f04"/>
+</linearGradient>
+<path fill="url(#g)" stroke="#002500" d="M 1.5 16 L 1.5 31 A 19.5 14.5 0 0 0 40.5 31 L 40.5 16 z"/>
+<ellipse fill="#157d36" stroke="#002500" cx="21" cy="16" rx="19.5" ry="14.5"/>
+</svg>
diff --git a/play.css b/play.css
index ab9a8f1..c74d0da 100644
--- a/play.css
+++ b/play.css
@@ -336,6 +336,14 @@ span.suit.reserve { color: var(--color-reserve); font-weight: bold; font-family:
.piece.cylinder.prussia_4 { background-image: url(pieces/cylinder_prussia_4.svg) }
.piece.cylinder.saxony_1 { background-image: url(pieces/cylinder_saxony_1.svg) }
+.piece.cylinder.austria.oos { background-image: url(pieces/cylinder_austria_oos.svg) }
+.piece.cylinder.bavaria.oos { background-image: url(pieces/cylinder_bavaria_oos.svg) }
+.piece.cylinder.france.oos { background-image: url(pieces/cylinder_france_oos.svg) }
+.piece.cylinder.pragmatic.oos { background-image: url(pieces/cylinder_pragmatic_oos.svg) }
+.piece.cylinder.prussia.oos { background-image: url(pieces/cylinder_prussia_oos.svg) }
+.piece.cylinder.saxony.oos { background-image: url(pieces/cylinder_saxony_oos.svg) }
+
+
/* MARKERS */
#combat {
diff --git a/play.js b/play.js
index b87f53f..75591c1 100644
--- a/play.js
+++ b/play.js
@@ -445,28 +445,28 @@ function has_removed_all_pieces(pow) {
function on_init() {
ui.pieces = [
- create_piece("piece", 0, "piece cylinder france_1"),
- create_piece("piece", 1, "piece cylinder france_2"),
- create_piece("piece", 2, "piece cylinder france_3"),
- create_piece("piece", 3, "piece cylinder france_4"),
- create_piece("piece", 4, "piece cylinder france_5"),
- create_piece("piece", 5, "piece cylinder bavaria_1"),
-
- create_piece("piece", 6, "piece cylinder prussia_1"),
- create_piece("piece", 7, "piece cylinder prussia_2"),
- create_piece("piece", 8, "piece cylinder prussia_3"),
- create_piece("piece", 9, "piece cylinder prussia_4"),
- create_piece("piece", 10, "piece cylinder saxony_1"),
- create_piece("piece", 11, "piece cylinder pragmatic_1"),
- create_piece("piece", 12, "piece cylinder pragmatic_2"),
- create_piece("piece", 13, "piece cylinder pragmatic_3"),
-
- create_piece("piece", 14, "piece cylinder austria_1"),
- create_piece("piece", 15, "piece cylinder austria_2"),
- create_piece("piece", 16, "piece cylinder austria_3"),
- create_piece("piece", 17, "piece cylinder austria_4"),
- create_piece("piece", 18, "piece cylinder austria_5"),
- create_piece("piece", 19, "piece cylinder austria_6"),
+ create_piece("piece", 0, "piece cylinder france france_1"),
+ create_piece("piece", 1, "piece cylinder france france_2"),
+ create_piece("piece", 2, "piece cylinder france france_3"),
+ create_piece("piece", 3, "piece cylinder france france_4"),
+ create_piece("piece", 4, "piece cylinder france france_5"),
+ create_piece("piece", 5, "piece cylinder bavaria bavaria_1"),
+
+ create_piece("piece", 6, "piece cylinder prussia prussia_1"),
+ create_piece("piece", 7, "piece cylinder prussia prussia_2"),
+ create_piece("piece", 8, "piece cylinder prussia prussia_3"),
+ create_piece("piece", 9, "piece cylinder prussia prussia_4"),
+ create_piece("piece", 10, "piece cylinder saxony saxony_1"),
+ create_piece("piece", 11, "piece cylinder pragmatic pragmatic_1"),
+ create_piece("piece", 12, "piece cylinder pragmatic pragmatic_2"),
+ create_piece("piece", 13, "piece cylinder pragmatic pragmatic_3"),
+
+ create_piece("piece", 14, "piece cylinder austria austria_1"),
+ create_piece("piece", 15, "piece cylinder austria austria_2"),
+ create_piece("piece", 16, "piece cylinder austria austria_3"),
+ create_piece("piece", 17, "piece cylinder austria austria_4"),
+ create_piece("piece", 18, "piece cylinder austria austria_5"),
+ create_piece("piece", 19, "piece cylinder austria austria_6"),
create_piece("piece", 20, "piece cube france"),
create_piece("piece", 21, "piece cube france"),
@@ -1108,7 +1108,10 @@ function on_log(text) {
}
else if (text.startsWith("=")) {
p.className = "h " + power_class[text[1]]
- text = power_name[text[1]]
+ if (text.length === 2)
+ text = power_name[text[1]]
+ else
+ text = text.substring(2)
}
else if (text.startsWith("@")) {
p.className = "move_tip"
diff --git a/rules.js b/rules.js
index f4491a5..c318f40 100644
--- a/rules.js
+++ b/rules.js
@@ -4,9 +4,6 @@
/* TODO
-victory check
-supply phase
- hussar payment
re-entering supply trains during movement (10.2)
supply payment
@@ -116,7 +113,6 @@ const RESERVE = 4
const IMPERIAL_ELECTION = 25
const ELIMINATED = data.cities.name.length
-const REMOVED = ELIMINATED + 1
const TRIER = find_city("Trier")
const MAINZ = find_city("Mainz")
@@ -290,7 +286,9 @@ const all_powers_france_bavaria_prussia_saxony = [ P_FRANCE, P_BAVARIA, P_PRUSSI
const all_powers_pragmatic_austria = [ P_PRAGMATIC, P_AUSTRIA ]
const all_powers_none = []
+const all_powers_france_bavaria = [ P_FRANCE, P_BAVARIA ]
const all_powers_bavaria = [ P_BAVARIA ]
+const all_powers_prussia_saxony = [ P_PRUSSIA, P_SAXONY ]
const all_powers_saxony = [ P_SAXONY ]
const all_powers_bavaria_saxony = [ P_BAVARIA, P_SAXONY ]
@@ -315,6 +313,33 @@ function all_friendly_minor_powers(pow) {
}
}
+function all_coop_powers(pow) {
+ switch (pow) {
+ case P_FRANCE:
+ case P_BAVARIA:
+ return all_powers_france_bavaria
+ case P_PRUSSIA:
+ case P_SAXONY:
+ return all_powers_prussia_saxony
+ case P_PRAGMATIC:
+ case P_AUSTRIA:
+ return all_powers_pragmatic_austria
+ }
+}
+
+function is_hostile_to_austria() {
+ switch (game.power) {
+ case P_FRANCE:
+ case P_BAVARIA:
+ case P_PRUSSIA:
+ case P_SAXONY:
+ return true
+ case P_PRAGMATIC:
+ case P_AUSTRIA:
+ return false
+ }
+}
+
function all_enemy_powers(pow) {
switch (pow) {
case P_FRANCE:
@@ -487,11 +512,6 @@ function format_selected() {
}
}
-function log_move_to(to) {
- let from = game.pos[game.selected]
- log("@" + game.selected + ";" + from + "," + to)
-}
-
function log_move_path() {
if (game.move_path.length > 1)
log("@" + game.selected + ";" + game.move_path.join(","))
@@ -531,6 +551,18 @@ function is_fortress(s) {
return set_has(all_fortresses, s)
}
+function is_home_country(s) {
+ // TODO: Silesia
+ switch (game.power) {
+ case P_FRANCE: return set_has(data.country.France, s)
+ case P_BAVARIA: return set_has(data.country.Bavaria, s)
+ case P_PRUSSIA: return set_has(data.country.Prussia, s)
+ case P_SAXONY: return set_has(data.country.Saxony, s)
+ case P_AUSTRIA: return set_has(data.country.Austria, s)
+ case P_PRAGMATIC: return set_has(data.country.Netherlands, 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))
@@ -560,13 +592,12 @@ function is_enemy_controlled_fortress(s) {
}
function is_power_controlled_fortress(pow, s) {
- if (map_get(game.elector, s, -1) === pow)
- return true
- if (map_get(game.victory, s, -1) === pow)
- return true
- if (set_has(all_home_country_fortresses[pow], s))
- return true
- return false
+ let owner = map_get(game.elector, s, -1)
+ if (owner < 0)
+ owner = map_get(game.victory, s, -1)
+ if (owner < 0)
+ return set_has(all_home_country_fortresses[pow], s)
+ return owner === pow
}
function set_control_of_fortress(s, pow) {
@@ -722,6 +753,13 @@ function has_any_piece(to) {
return false
}
+function has_any_hussar(to) {
+ for (let p of all_hussars)
+ if (game.pos[p] === to)
+ return true
+ return false
+}
+
function has_friendly_supply_train(to) {
for (let p of all_allied_trains(game.power))
if (game.pos[p] === to)
@@ -816,6 +854,13 @@ const POWER_FROM_ACTION_STEP = [
P_PRAGMATIC, // interleave with austria moves on flanders map
]
+const title_from_action_step = [
+ "=0 France and Bavaria",
+ "=2 Prussia and Saxony",
+ "=5 Austria",
+ "=4 Pragmatic Army",
+]
+
function set_active_to_current_action_step() {
set_active_to_power(POWER_FROM_ACTION_STEP[game.step])
}
@@ -845,7 +890,7 @@ function goto_action_stage() {
clear_undo()
- log("=" + game.power)
+ log(title_from_action_step[game.step])
// TODO: minor powers controlled at the same time
@@ -881,13 +926,14 @@ function end_place_hussars() {
}
states.place_hussars = {
- inactive: "place Hussars",
+ inactive: "place hussars",
prompt() {
- prompt("Place the Hussars.")
+ prompt("Place the hussars.")
for (let p of all_hussars)
if (!set_has(game.moved, p))
gen_action_piece(p)
- view.actions.next = 1
+ if (!has_any_hussar(ELIMINATED))
+ view.actions.next = 1
},
piece(p) {
push_undo()
@@ -901,9 +947,9 @@ states.place_hussars = {
}
states.place_hussars_where = {
- inactive: "place Hussars",
+ inactive: "place hussars",
prompt() {
- prompt("Place the Hussar in a city.")
+ prompt("Place the hussar in a city.")
view.selected = game.selected
// bohemia
@@ -935,7 +981,7 @@ function search_hussar_bfs(from) {
continue
if (!is_bohemia_space(next))
continue
- if (!has_any_piece(next))
+ if (!has_any_piece(next) && !has_any_hussar(next))
set_add(seen, next)
if (dist < 4)
queue.push((next << 4) | dist)
@@ -1087,7 +1133,345 @@ function end_tactical_cards() {
// TODO: draw pragmatic cards before austria moves
- // MARIA TODO: supply!
+ goto_supply()
+}
+
+/* PAYMENT */
+
+function sum_card_values(list) {
+ let n = 0
+ for (let c of list)
+ n += to_value(c)
+ return n
+}
+
+function find_largest_card(list) {
+ for (let v = 10; v >= 2; --v) {
+ for (let c of list)
+ if (to_value(c) === v)
+ return c
+ }
+ throw "NO CARDS FOUND IN LIST"
+}
+
+function spend_card_value(pool, used, amount) {
+ if (game.count > 0) {
+ if (amount < game.count) {
+ game.count -= amount
+ amount = 0
+ } else {
+ amount -= game.count
+ game.count = 0
+ }
+ }
+ while (amount > 0) {
+ let c = find_largest_card(pool)
+ let v = to_value(c)
+ set_delete(pool, c)
+ set_add(used, c)
+ if (v > amount) {
+ game.count = v - amount
+ amount = 0
+ } else {
+ amount -= v
+ }
+ }
+}
+
+/* SUPPLY */
+
+function is_out_of_supply(p) {
+ return (game.oos & (1 << p)) !== 0
+}
+
+function set_out_of_supply(p) {
+ return game.oos |= (1 << p)
+}
+
+function set_in_supply(p) {
+ return game.oos &= ~(1 << p)
+}
+
+function search_supply_path_avoid_hussars(who) {
+ let from = game.pos[who]
+ let trains = all_power_trains[piece_power[who]]
+
+ if (is_home_country(from))
+ return 1
+
+ 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]) {
+ for (let p of trains)
+ if (game.pos[p] === next)
+ return dist
+ if (has_any_hussar(next))
+ continue
+ if (set_has(seen, next))
+ continue
+ if (has_enemy_piece(next))
+ continue
+ set_add(seen, next)
+ if (dist < 6)
+ queue.push((next << 4) | dist)
+ }
+ }
+
+ return 0
+}
+
+function search_supply_path(who) {
+ let from = game.pos[who]
+ let trains = all_power_trains[piece_power[who]]
+
+ if (is_home_country(from))
+ return 1
+
+ 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]) {
+ for (let p of trains)
+ if (game.pos[p] === next)
+ return dist
+ if (set_has(seen, next))
+ continue
+ if (has_enemy_piece(next))
+ continue
+ set_add(seen, next)
+ if (dist < 6)
+ queue.push((next << 4) | dist)
+ }
+ }
+
+ return 0
+}
+
+function goto_supply() {
+ game.supply = {
+ hussars: [],
+ restore: [],
+ suffer: [],
+ }
+
+ for (let p of all_controlled_generals(game.power)) {
+ if (!is_map_space(game.pos[p]))
+ continue
+
+ if (is_hostile_to_austria()) {
+ let d = search_supply_path_avoid_hussars(p)
+ if (d > 0) {
+ if (is_out_of_supply(p))
+ set_add(game.supply.restore, p)
+ } else {
+ d = search_supply_path(p)
+ if (d > 0)
+ map_set(game.supply.hussars, p, d)
+ else
+ set_add(game.supply.suffer, p)
+ }
+ } else {
+ let d = search_supply_path(p)
+ if (d > 0) {
+ if (is_out_of_supply(p))
+ set_add(game.supply.restore, p)
+ } else {
+ set_add(game.supply.suffer, p)
+ }
+ }
+ }
+
+ if (game.supply.hussars.length + game.supply.restore.length + game.supply.suffer.length === 0)
+ end_supply()
+ else
+ resume_supply()
+}
+
+function resume_supply() {
+ set_active_to_current_action_step()
+ if (game.supply.hussars.length > 0)
+ goto_supply_hussars()
+ else if (game.supply.restore.length > 0)
+ goto_supply_restore()
+ else if (game.supply.suffer.length > 0)
+ goto_supply_suffer()
+ else
+ end_supply()
+ // TODO: pause
+ // game.state = "supply_done"
+}
+
+function goto_supply_hussars() {
+ log_br()
+ log("Hussars")
+ set_active_to_power(piece_power[game.supply.hussars[0]])
+ game.state = "supply_hussars"
+ game.supply.pool = []
+ game.supply.used = []
+ game.count = 0
+}
+
+function goto_supply_restore() {
+ log_br()
+ log("In supply")
+ game.state = "supply_restore"
+}
+
+function goto_supply_suffer() {
+ log_br()
+ log("Out of supply")
+ game.state = "supply_suffer"
+}
+
+states.supply_hussars = {
+ inactive: "supply",
+ prompt() {
+ let paid = game.count + sum_card_values(game.supply.pool)
+
+ view.selected = []
+
+ let can_pay = false
+ let debt = 0
+ map_for_each(game.supply.hussars, (p, d) => {
+ if (piece_power[p] === game.power) {
+ if (d <= paid) {
+ can_pay = true
+ gen_action_piece(p)
+ } else {
+ view.selected.push(p)
+ }
+ debt += d
+ }
+ })
+
+ let str
+ if (debt > 0)
+ str = `Pay ${debt} for tracing supply through hussars`
+ else
+ str = "Hussar payment done"
+
+ if (paid > 1)
+ str += " \u2014 " + paid + " points."
+ else if (paid === 1)
+ str += " \u2014 1 point."
+ else
+ str += "."
+
+ if (paid < debt)
+ for (let c of game.hand[game.power])
+ gen_action_card(c)
+
+ prompt(str)
+
+ view.draw = game.supply.pool
+
+ if (debt === 0 || (!can_pay && game.hand[game.power].length === 0))
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+
+ let cost = map_get(game.supply.hussars, p)
+
+ spend_card_value(game.supply.pool, game.supply.used, cost)
+
+ map_delete(game.supply.hussars, p)
+ if (is_out_of_supply(p))
+ set_add(game.supply.restore, p)
+
+ log(">P" + p + " paid " + cost)
+ },
+ card(c) {
+ push_undo()
+ set_delete(game.hand[game.power], c)
+ set_add(game.supply.pool, c)
+ },
+ next() {
+ push_undo()
+
+ if (game.supply.used.length > 0)
+ log(">with " + game.supply.used.map(format_card).join(", "))
+ else
+ log(">paid nothing")
+
+ // move generals not paid for to out of supply list
+ map_for_each(game.supply.hussars, (p, _) => {
+ if (piece_power[p] === game.power)
+ set_add(game.supply.suffer, p)
+ })
+ for (let p of game.supply.suffer)
+ map_delete(game.supply.hussars, p)
+
+ // put back into hand unused cards
+ for (let c of game.supply.pool)
+ set_add(game.hand[game.power], c)
+ delete game.supply.pool
+ delete game.supply.used
+
+ resume_supply()
+ },
+}
+
+states.supply_restore = {
+ inactive: "supply",
+ prompt() {
+ prompt("Restore supply to generals with a supply path.")
+ for (let p of game.supply.restore)
+ gen_action_piece(p)
+ },
+ piece(p) {
+ log(`>P${p} at S${game.pos[p]}`)
+ set_delete(game.supply.restore, p)
+ set_in_supply(p)
+ if (game.supply.restore.length === 0)
+ resume_supply()
+ },
+}
+
+states.supply_suffer = {
+ inactive: "supply",
+ prompt() {
+ prompt("Flip and remove troops from generals without a supply path.")
+ for (let p of game.supply.suffer)
+ gen_action_piece(p)
+ },
+ piece(p) {
+ set_delete(game.supply.suffer, p)
+ if (is_out_of_supply(p)) {
+ log(`>P${p} at S${game.pos[p]} -2 troops`)
+ game.troops[p] -= 2
+ } else {
+ log(`>P${p} at S${game.pos[p]} -1 troop`)
+ set_out_of_supply(p)
+ game.troops[p] -= 1
+ }
+ if (game.troops[p] <= 0)
+ eliminate_general(p, true)
+ if (game.supply.suffer.length === 0)
+ resume_supply()
+ },
+}
+
+states.supply_done = {
+ inactive: "supply",
+ prompt() {
+ prompt("Supply done.")
+ view.actions.end_supply = 1
+ },
+ end_supply() {
+ end_supply()
+ },
+}
+
+function end_supply() {
+ delete game.supply
goto_movement()
}
@@ -1141,6 +1525,8 @@ function movement_range() {
}
function goto_movement() {
+ set_active_to_current_action_step()
+
game.state = "movement"
set_clear(game.moved)
@@ -1366,6 +1752,16 @@ states.move_supply_train = {
if (!set_has(data.cities.main_roads[from], to))
game.main = 0
+ // remove hussars
+ for (let p of all_hussars) {
+ if (game.pos[p] === to) {
+ if (!game.move_elim)
+ game.move_elim = []
+ set_add(game.move_elim, p)
+ game.pos[p] = ELIMINATED
+ }
+ }
+
game.pos[who] = to
if (++game.count === 2 + game.main)
@@ -1566,8 +1962,12 @@ function end_move_piece() {
log_move_path()
if (game.move_elim) {
- for (let p of game.move_elim)
- log("P" + p + " eliminated.")
+ for (let p of game.move_elim) {
+ if (is_hussar(p))
+ log("P" + p + " removed.")
+ else
+ log("P" + p + " eliminated.")
+ }
delete game.move_elim
}
@@ -1628,45 +2028,8 @@ function troop_cost() {
return 6
}
-function sum_card_values(list) {
- let n = 0
- for (let c of list)
- n += to_value(c)
- return n
-}
-
-function find_largest_card(list) {
- for (let v = 10; v >= 2; --v) {
- for (let c of list)
- if (to_value(c) === v)
- return c
- }
- throw "NO CARDS FOUND IN LIST"
-}
-
function spend_recruit_cost() {
- let spend = troop_cost()
- if (game.count > 0) {
- if (spend < game.count) {
- game.count -= spend
- spend = 0
- } else {
- spend -= game.count
- game.count = 0
- }
- }
- while (spend > 0) {
- let c = find_largest_card(game.recruit.pool)
- let v = to_value(c)
- set_delete(game.recruit.pool, c)
- set_add(game.recruit.used, c)
- if (v > spend) {
- game.count = v - spend
- spend = 0
- } else {
- spend -= v
- }
- }
+ spend_card_value(game.recruit.pool, game.recruit.used, troop_cost())
}
function has_available_depot() {
@@ -2499,235 +2862,6 @@ function goto_retroactive_conquest() {
end_action_stage()
}
-/* SUPPLY */
-
-function search_supply_bfs(from, range) {
- 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
- if (has_enemy_piece(next))
- continue
- set_add(seen, next)
- if (dist < range)
- queue.push((next << 4) | dist)
- }
- }
- return seen
-}
-
-function search_supply(range) {
- for (let p of all_power_trains[game.power]) {
- let here = game.pos[p]
- if (here >= ELIMINATED)
- continue
- if (!game.supply)
- game.supply = search_supply_bfs(here, range)
- else
- set_add_all(game.supply, search_supply_bfs(here, range))
- }
-}
-
-function is_out_of_supply(p) {
- return (game.oos & (1 << p)) !== 0
-}
-
-function set_out_of_supply(p) {
- return game.oos |= (1 << p)
-}
-
-function set_in_supply(p) {
- return game.oos &= ~(1 << p)
-}
-
-function has_supply_line(p) {
- let s = game.pos[p]
- if (set_has(all_home_or_depot_cities[game.power], s))
- return true
- if (game.supply && set_has(game.supply, s))
- return true
- return false
-}
-
-function should_supply_restore() {
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (is_out_of_supply(p) && has_supply_line(p))
- return true
- }
- return false
-}
-
-function should_supply_eliminate() {
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (is_out_of_supply(p) && !has_supply_line(p))
- return true
- }
- return false
-}
-
-function should_supply_flip() {
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (!is_out_of_supply(p) && !has_supply_line(p))
- return true
- }
- return false
-}
-
-function goto_supply() {
- set_clear(game.moved)
- search_supply(6)
-
- if (should_supply_restore())
- goto_supply_restore()
- else if (should_supply_eliminate())
- goto_supply_eliminate()
- else if (should_supply_flip())
- goto_supply_flip()
- else
- end_supply()
-}
-
-function goto_supply_restore() {
- log_br()
- log("In supply")
- resume_supply_restore()
-}
-
-function goto_supply_eliminate() {
- log_br()
- log("Out of supply")
- resume_supply_eliminate()
-}
-
-function goto_supply_flip() {
- log_br()
- log("Out of supply")
- resume_supply_flip()
-}
-
-function resume_supply_restore() {
- if (should_supply_restore())
- game.state = "supply_restore"
- else if (should_supply_eliminate())
- goto_supply_eliminate()
- else if (should_supply_flip())
- goto_supply_flip()
- else
- game.state = "supply_done"
-}
-
-function resume_supply_eliminate() {
- if (should_supply_eliminate())
- game.state = "supply_eliminate"
- else if (should_supply_flip())
- goto_supply_flip()
- else
- game.state = "supply_done"
-}
-
-function resume_supply_flip() {
- if (should_supply_flip())
- game.state = "supply_flip"
- else
- game.state = "supply_done"
-}
-
-states.supply_restore = {
- inactive: "supply",
- prompt() {
- prompt("Restore supply to generals with a supply line.")
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (is_out_of_supply(p) && has_supply_line(p))
- gen_action_piece(game.pos[p])
- }
- },
- piece(x) {
- let s = game.pos[x]
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] === s) {
- set_add(game.moved, p)
- set_in_supply(p)
- log(">P" + p + " at S" + s)
- }
- }
- resume_supply_restore()
- },
-}
-
-states.supply_eliminate = {
- inactive: "supply",
- prompt() {
- prompt("Eliminate out of supply generals with no supply line.")
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (is_out_of_supply(p) && !has_supply_line(p))
- gen_action_piece(game.pos[p])
- }
- },
- piece(x) {
- let s = game.pos[x]
- for (let p of all_power_generals[game.power])
- if (game.pos[p] === s)
- eliminate_general(p, true)
-
- resume_supply_eliminate()
- },
-}
-
-states.supply_flip = {
- inactive: "supply",
- prompt() {
- prompt("Flip generals with no supply line.")
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] >= ELIMINATED)
- continue
- if (!is_out_of_supply(p) && !has_supply_line(p))
- gen_action_piece(game.pos[p])
- }
- },
- piece(x) {
- let s = game.pos[x]
- for (let p of all_power_generals[game.power]) {
- if (game.pos[p] === s) {
- log(">P" + p + " at S" + s)
- set_out_of_supply(p)
- }
- }
- resume_supply_flip()
- },
-}
-
-states.supply_done = {
- inactive: "supply",
- prompt() {
- prompt("Supply done.")
- view.actions.end_supply = 1
- },
- end_supply() {
- end_supply()
- },
-}
-
-function end_supply() {
- delete game.supply
-
- end_action_stage()
-}
-
/* SETUP */
const POWER_FROM_SETUP_STEP = [
diff --git a/tools/genpieces.mjs b/tools/genpieces.mjs
index d52bf64..0f8268b 100644
--- a/tools/genpieces.mjs
+++ b/tools/genpieces.mjs
@@ -149,6 +149,7 @@ function print_cube(output, c) {
fs.writeFileSync(output, svg.join("\n") + "\n")
}
+print_cylinder("pieces/cylinder_austria_oos.svg", null, color_austria)
print_cylinder("pieces/cylinder_austria_1.svg", "tools/cylinders.2x/face_austria_1.png", color_austria)
print_cylinder("pieces/cylinder_austria_2.svg", "tools/cylinders.2x/face_austria_2.png", color_austria)
print_cylinder("pieces/cylinder_austria_3.svg", "tools/cylinders.2x/face_austria_3.png", color_austria)
@@ -156,23 +157,28 @@ print_cylinder("pieces/cylinder_austria_4.svg", "tools/cylinders.2x/face_austria
print_cylinder("pieces/cylinder_austria_5.svg", "tools/cylinders.2x/face_austria_5.png", color_austria)
print_cylinder("pieces/cylinder_austria_6.svg", "tools/cylinders.2x/face_austria_6.png", color_austria)
+print_cylinder("pieces/cylinder_bavaria_oos.svg", null, color_bavaria)
print_cylinder("pieces/cylinder_bavaria_1.svg", "tools/cylinders.2x/face_bavaria_1.png", color_bavaria)
+print_cylinder("pieces/cylinder_france_oos.svg", null, color_france)
print_cylinder("pieces/cylinder_france_1.svg", "tools/cylinders.2x/face_france_1.png", color_france)
print_cylinder("pieces/cylinder_france_2.svg", "tools/cylinders.2x/face_france_2.png", color_france)
print_cylinder("pieces/cylinder_france_3.svg", "tools/cylinders.2x/face_france_3.png", color_france)
print_cylinder("pieces/cylinder_france_4.svg", "tools/cylinders.2x/face_france_4.png", color_france)
print_cylinder("pieces/cylinder_france_5.svg", "tools/cylinders.2x/face_france_5.png", color_france)
+print_cylinder("pieces/cylinder_pragmatic_oos.svg", null, color_pragmatic)
print_cylinder("pieces/cylinder_pragmatic_1.svg", "tools/cylinders.2x/face_pragmatic_1.png", color_pragmatic)
print_cylinder("pieces/cylinder_pragmatic_2.svg", "tools/cylinders.2x/face_pragmatic_2.png", color_pragmatic)
print_cylinder("pieces/cylinder_pragmatic_3.svg", "tools/cylinders.2x/face_pragmatic_3.png", color_pragmatic)
+print_cylinder("pieces/cylinder_prussia_oos.svg", null, color_prussia)
print_cylinder("pieces/cylinder_prussia_1.svg", "tools/cylinders.2x/face_prussia_1.png", color_prussia)
print_cylinder("pieces/cylinder_prussia_2.svg", "tools/cylinders.2x/face_prussia_2.png", color_prussia)
print_cylinder("pieces/cylinder_prussia_3.svg", "tools/cylinders.2x/face_prussia_3.png", color_prussia)
print_cylinder("pieces/cylinder_prussia_4.svg", "tools/cylinders.2x/face_prussia_4.png", color_prussia)
+print_cylinder("pieces/cylinder_saxony_oos.svg", null, color_saxony)
print_cylinder("pieces/cylinder_saxony_1.svg", "tools/cylinders.2x/face_saxony_1.png", color_saxony)
print_disc("pieces/disc_hussar.svg", "tools/cylinders.2x/face_hussar.png", color_austria)