diff options
-rw-r--r-- | play.js | 4 | ||||
-rw-r--r-- | rules.js | 264 |
2 files changed, 247 insertions, 21 deletions
@@ -1022,6 +1022,10 @@ function on_update() { for (let p = 0; p < 20; ++p) action_button_with_argument("supreme", p, power_name[piece_power[p]]) + for (let pow of all_powers) + action_button_with_argument("power", pow, power_name[pow]) + + action_button("re_enter", "Re-enter") action_button("force_march", "Force march") action_button("take", "Take") @@ -4,10 +4,7 @@ /* TODO -re-entering supply trains during movement (10.2) - supply payment - -winter +winter recruitment tc draw no TCs for minor power if major fortress enemy controlled @@ -69,6 +66,15 @@ function is_map_space(s) { return s >= 0 && s <= 618 } +function is_piece_on_map(p) { + return is_map_space(game.pos[p]) +} + +function is_piece_on_map_or_eliminated(p) { + let s = game.pos[p] + return is_map_space(s) || s === ELIMINATED +} + function find_city(city) { let n = data.cities.name.length let x = -1 @@ -104,6 +110,9 @@ const P_AUSTRIA = 5 const POWER_NAME = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic Army", "Austria" ] +const is_major_power = [ 1, 0, 1, 0, 1, 1 ] +const is_minor_power = [ 0, 1, 0, 1, 0, 0 ] + const SPADES = 0 const CLUBS = 1 const HEARTS = 2 @@ -136,15 +145,9 @@ const max_power_troops = [ 5*8, 1*8, 4*8, 1*8, 3*8, 6*8 ] const all_powers = [ 0, 1, 2, 3, 4, 5 ] -const all_home_or_depot_cities = [ -] - const all_power_depots = [ ] -const all_power_re_entry_cities = [ -] - const all_power_generals = [ [ 0, 1, 2, 3, 4 ], [ 5 ], @@ -288,6 +291,8 @@ 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_pragmatic = [ P_PRAGMATIC ] +const all_powers_austria = [ P_AUSTRIA ] const all_powers_prussia_saxony = [ P_PRUSSIA, P_SAXONY ] const all_powers_saxony = [ P_SAXONY ] const all_powers_bavaria_saxony = [ P_BAVARIA, P_SAXONY ] @@ -300,6 +305,14 @@ function coop_major_power(pow) { return pow } +function coop_minor_power(pow) { + if (pow === P_FRANCE) + return P_BAVARIA + if (pow === P_PRUSSIA) + return P_SAXONY + return pow +} + function all_friendly_minor_powers(pow) { switch (pow) { case P_FRANCE: @@ -327,6 +340,21 @@ function all_coop_powers(pow) { } } +function all_controlled_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: + return all_powers_pragmatic + case P_AUSTRIA: + return all_powers_austria + } +} + function is_hostile_to_austria() { switch (game.power) { case P_FRANCE: @@ -532,6 +560,14 @@ all_home_country_fortresses[P_SAXONY] = set_intersect(all_fortresses, data.count all_home_country_fortresses[P_AUSTRIA] = set_intersect(all_fortresses, data.country.Austria) all_home_country_fortresses[P_PRAGMATIC] = set_intersect(all_fortresses, data.country.Netherlands) +const all_home_country_major_fortresses = [] +all_home_country_major_fortresses[P_FRANCE] = set_intersect(data.type.major_fortress, data.country.France) +all_home_country_major_fortresses[P_BAVARIA] = set_intersect(data.type.major_fortress, data.country.Bavaria) +all_home_country_major_fortresses[P_PRUSSIA] = set_intersect(data.type.major_fortress, data.country.Prussia) +all_home_country_major_fortresses[P_SAXONY] = set_intersect(data.type.major_fortress, data.country.Saxony) +all_home_country_major_fortresses[P_AUSTRIA] = set_intersect(data.type.major_fortress, data.country.Austria) +all_home_country_major_fortresses[P_PRAGMATIC] = set_intersect(data.type.major_fortress, data.country.Netherlands) + const all_silesian_fortresses = set_intersect(all_fortresses, data.country.Silesia) const protect_range = [] @@ -591,6 +627,10 @@ function is_enemy_controlled_fortress(s) { return false } +function is_friendly_controlled_fortress(s) { + return !is_enemy_controlled_fortress(s) +} + function is_power_controlled_fortress(pow, s) { let owner = map_get(game.elector, s, -1) if (owner < 0) @@ -741,7 +781,7 @@ function count_used_troops() { function count_unused_troops_on_map() { let n = 0 for (let p of all_power_generals[game.power]) - if (is_map_space(game.pos[p])) + if (is_piece_on_map(p)) n += 8 - game.troops[p] return n } @@ -1262,7 +1302,7 @@ function goto_supply() { } for (let p of all_controlled_generals(game.power)) { - if (!is_map_space(game.pos[p])) + if (!is_piece_on_map(p)) continue if (is_hostile_to_austria()) { @@ -1532,6 +1572,7 @@ function goto_movement() { log_br() + game.move_re_entered = 0 game.move_conq = [] } @@ -1558,7 +1599,7 @@ states.movement = { let done_trains = true for (let p of all_controlled_generals(game.power)) { - if (!set_has(game.moved, p) && is_map_space(game.pos[p])) { + if (!set_has(game.moved, p) && is_piece_on_map(p)) { if (can_general_move_anywhere(p)) { gen_action_piece(p) done_generals = false @@ -1567,10 +1608,15 @@ states.movement = { } for (let p of all_controlled_trains(game.power)) { - if (!set_has(game.moved, p) && is_map_space(game.pos[p])) { - if (can_train_move_anywhere(p)) { - gen_action_piece(p) - done_trains = false + if ((game.move_re_entered & (1 << piece_power[p])) === 0) + if (can_train_re_enter(p)) + view.actions.re_enter = 1 + if (!set_has(game.moved, p)) { + if (is_piece_on_map(p)) { + if (can_train_move_anywhere(p)) { + gen_action_piece(p) + done_trains = false + } } } } @@ -1589,6 +1635,10 @@ states.movement = { else view.actions.end_movement = 1 }, + re_enter() { + push_undo() + goto_re_enter_train() + }, piece(p) { push_undo() @@ -1626,6 +1676,7 @@ states.movement = { log_conquest(game.move_conq) delete game.move_conq + delete game.move_re_entered goto_combat() }, @@ -2020,6 +2071,178 @@ states.move_supreme = { }, } +/* RE-ENTER SUPPLY TRAIN */ + +/* + TODO: move to end of movement (like friedrich recruitment)? +*/ + +function goto_re_enter_train() { + if (all_controlled_powers(game.power).length > 1) + game.state = "re_enter_train_power" + else + game.state = "re_enter_train" + game.re_enter = { + pool: [], + used: [], + pieces: [], + } + game.count = 0 +} + +function can_train_re_enter(p) { + return ( + (is_piece_on_map(p) || game.pos[p] === ELIMINATED) && + !set_has(game.moved, p) && + train_has_re_entry_city(p) + ) +} + +function train_has_re_entry_city(p) { + let pow = piece_power[p] + if (coop_minor_power(pow) !== pow) + return can_re_enter_train_at_power_fortress(pow) || can_re_enter_train_at_power_fortress(coop_minor_power(pow)) + else + return can_re_enter_train_at_power_fortress(pow) +} + +function gen_re_enter_train_at_power_fortress(pow) { + for (let s of all_home_country_major_fortresses[pow]) + if (is_friendly_controlled_fortress(s) && !has_any_piece(s)) + gen_action_space(s) +} + +function can_re_enter_train_at_power_fortress(pow) { + for (let s of all_home_country_major_fortresses[pow]) + if (is_friendly_controlled_fortress(s) && !has_any_piece(s)) + return true + return false +} + +states.re_enter_train_power = { + inactive: "move", + prompt() { + prompt("Re-enter supply train from which power?") + view.actions.power = [] + for (let pow of all_controlled_powers(game.power)) { + if (game.move_re_entered & (1 << pow)) + continue + for (let p of all_power_trains[pow]) { + if (can_train_re_enter(p)) { + view.actions.power.push(pow) + break + } + } + } + }, + power(pow) { + game.move_re_entered |= (1 << pow) + set_active_to_power(pow) + game.state = "re_enter_train" + }, +} + +states.re_enter_train = { + inactive: "move", + prompt() { + let str + + let paid = game.count + sum_card_values(game.re_enter.pool) + + let av_trains = 0 + for (let p of all_power_trains[game.power]) { + if (can_train_re_enter(p)) { + if (paid >= 4) + gen_action_piece(p) + av_trains += 1 + } + } + + if (paid / 4 < av_trains) { + for (let c of game.hand[game.power]) + gen_action_card(c) + } + + if (game.re_enter.used.length > 0) + view.actions.next = 1 + + if (av_trains > 0) { + str = "Re-enter supply trains for 4 each" + if (paid > 1) + str += " \u2014 " + paid + " points." + else if (paid === 1) + str += " \u2014 1 point." + else + str += "." + } else { + str = "Re-enter supply trains done." + } + + prompt(str) + + view.draw = game.re_enter.pool + }, + piece(p) { + push_undo() + spend_card_value(game.re_enter.pool, game.re_enter.used, 4) + set_add(game.moved, p) + map_set(game.re_enter.pieces, p, game.pos[p]) + game.state = "re_enter_train_where" + game.selected = p + }, + card(c) { + push_undo() + set_delete(game.hand[game.power], c) + set_add(game.re_enter.pool, c) + }, + next() { + push_undo() + end_re_enter_train() + }, +} + +states.re_enter_train_where = { + inactive: "move", + prompt() { + prompt("Re-enter supply train at a major fortress.") + + view.selected = game.selected + view.draw = game.re_enter.pool + + gen_re_enter_train_at_power_fortress(game.power) + if (coop_minor_power(game.power) !== game.power) + gen_re_enter_train_at_power_fortress(coop_minor_power(game.power)) + }, + space(s) { + game.pos[game.selected] = s + game.selected = -1 + game.state = "re_enter_train" + }, +} + +function end_re_enter_train() { + if (game.re_enter.used.length > 0) { + log_br() + log(POWER_NAME[game.power] + " spent " + game.re_enter.used.map(format_card).join(", ") + ".") + map_for_each(game.re_enter.pieces, (p, s) => { + if (s !== ELIMINATED) + log("Re-entered P" + p + " from S" + s + " at S" + game.pos[p] + ".") + else + log("Re-entered P" + p + " at S" + game.pos[p] + ".") + }) + log_br() + } + + // put back into hand unused cards + for (let c of game.re_enter.pool) + set_add(game.hand[game.power], c) + + delete game.re_enter + + set_active_to_current_action_stage() + game.state = "movement" +} + /* RECRUITMENT */ function troop_cost() { @@ -2144,7 +2367,6 @@ states.recruit = { str = "Nothing to recruit" let paid = game.count + sum_card_values(game.recruit.pool) - if (paid > 1) str += " \u2014 " + paid + " points." else if (paid === 1) @@ -2216,7 +2438,7 @@ function end_recruit() { else log("Recruited with " + game.recruit.used.map(format_card).join(", ") + ".") map_for_each(game.recruit.pieces, (p,s) => { - log("Re-entered P" + p + " at S" + s + ".") + log("Re-entered P" + p + " to S" + s + ".") }) if (game.recruit.troops) log(">" + game.recruit.troops + " troops") @@ -2271,11 +2493,11 @@ function goto_combat() { let to = [] for (let p of all_controlled_generals(game.power)) - if (is_map_space(game.pos[p])) + if (is_piece_on_map(p)) set_add(from, game.pos[p]) for (let p of all_enemy_generals(game.power)) - if (is_map_space(game.pos[p])) + if (is_piece_on_map(p)) set_add(to, game.pos[p]) game.combat = [] |