diff options
author | Tor Andersson <tor@ccxvii.net> | 2024-10-20 21:26:12 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-10-21 00:48:57 +0200 |
commit | 73afac930d3f6671ee6d3d1d57cc889e0ed4dc23 (patch) | |
tree | b753a81906cf81fb7f60bb6a6ca74c6bc4941844 | |
parent | dbbe5ae68ffc6210241bbbb6eb86f9e4d0f16956 (diff) | |
download | maria-73afac930d3f6671ee6d3d1d57cc889e0ed4dc23.tar.gz |
Supply and paying TC for hussars.
-rw-r--r-- | pieces/cylinder_austria_oos.svg | 9 | ||||
-rw-r--r-- | pieces/cylinder_bavaria_oos.svg | 9 | ||||
-rw-r--r-- | pieces/cylinder_france_oos.svg | 9 | ||||
-rw-r--r-- | pieces/cylinder_pragmatic_oos.svg | 9 | ||||
-rw-r--r-- | pieces/cylinder_prussia_oos.svg | 9 | ||||
-rw-r--r-- | pieces/cylinder_saxony_oos.svg | 9 | ||||
-rw-r--r-- | play.css | 8 | ||||
-rw-r--r-- | play.js | 49 | ||||
-rw-r--r-- | rules.js | 720 | ||||
-rw-r--r-- | tools/genpieces.mjs | 6 |
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> @@ -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 { @@ -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" @@ -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) |