diff options
-rw-r--r-- | play.css | 4 | ||||
-rw-r--r-- | play.js | 38 | ||||
-rw-r--r-- | rules.js | 205 |
3 files changed, 139 insertions, 108 deletions
@@ -14,6 +14,10 @@ body.Blue header.your_turn { background-color: skyblue; } body.Yellow header.your_turn { background-color: khaki; } body.Green header.your_turn { background-color: darkseagreen; } +body.Observer #hand_panel, body.Observer #draw_panel, body.Observer #discard_panel { + display: none; +} + #log { background-color: whitesmoke; } #log .h1 { background-color: silver; font-weight: bold; padding-top:2px; padding-bottom:2px; margin: 8px 0; text-align: center; } #log .h1.p_red { background-color: salmon; } @@ -23,7 +23,7 @@ const DICE = { // === SYNC with rules.js === const LEGION_COUNT = 33 -const BARBARIAN_COUNT = 53 +const BARBARIAN_COUNT = [ 36, 46, 56 ] const CARD_M1 = [ 0, 11 ] const CARD_S1 = [ 12, 23 ] @@ -119,8 +119,8 @@ const UNAVAILABLE = 22 const ARMY = 23 -const first_barbarian = [ 0, 10, 20, 31, 43 ] -const last_barbarian = [ 9, 19, 30, 42, 52 ] +const first_barbarian = [ 3, 13, 23, 34, 46 ] +const last_barbarian = [ 12, 22, 33, 45, 55 ] const first_governor = [ 0, 6, 12, 18 ] const first_general = [ 0, 6, 12, 18 ] @@ -596,6 +596,12 @@ let ui = { played: document.getElementById("played"), market: document.getElementById("market"), pieces: document.getElementById("pieces"), + player_info: [ + document.querySelector("#role_Red .role_vp"), + document.querySelector("#role_Blue .role_vp"), + document.querySelector("#role_Yellow .role_vp"), + document.querySelector("#role_Green .role_vp"), + ], legacy: [ document.getElementById("red_legacy"), document.getElementById("blue_legacy"), @@ -924,6 +930,13 @@ function layout_barbarian_dice(black, white, tribe) { function on_update() { let player_count = view.legacy.length + for (let p = 0; p < player_count; ++p) { + let t = view.legacy[p] + " (" + view.emperor_turns[p] + ")" + if (p === view.first) + t = "\u2756 " + t + ui.player_info[p].textContent = t + } + ui.body.classList.toggle("p2", player_count === 2) ui.body.classList.toggle("p3", player_count === 3) ui.body.classList.toggle("p4", player_count === 4) @@ -1041,9 +1054,9 @@ function on_update() { ui.legions[i].classList.toggle("reduced", false) } - for (let id = 0; id < BARBARIAN_COUNT; ++id) { + for (let id = 3; id < BARBARIAN_COUNT[player_count-2]; ++id) { let loc = get_barbarian_location(id) - if (loc === UNAVAILABLE) + if (loc === AVAILABLE) hide(ui.barbarians[id]) else show(ui.barbarians[id]) @@ -1052,6 +1065,8 @@ function on_update() { else ui.barbarians[id].classList.toggle("inactive", false) } + for (let id = BARBARIAN_COUNT[player_count-2]; id < 56; ++id) + hide(ui.barbarians[id]) stack_count.fill(0) @@ -1083,13 +1098,13 @@ function on_update() { for (let id of [ CNIVA, ARDASHIR, SHAPUR ]) { let loc = get_barbarian_location(id) - if (loc !== UNAVAILABLE) + if (loc !== AVAILABLE) layout_stack(-1, [ ui.barbarians[id] ], loc, false, 8, 8) } for (let id = 0; id < 3; ++id) { let loc = get_rival_emperor_location(id) - if (loc === UNAVAILABLE) + if (loc === AVAILABLE) hide(ui.rival_emperors[id]) else { show(ui.rival_emperors[id]) @@ -1307,8 +1322,11 @@ function on_update() { action_button("recruit_general", "Recruit General") action_button("recruit_governor", "Recruit Governor") + action_button("play_all", "Play All") + action_button("end_actions", "End Actions") + action_button("continue", "Continue") action_button("save", "Save") action_button("pass", "Pass") action_button("done", "Done") @@ -1336,7 +1354,7 @@ function on_log(text) { text = text.replace(/</g, "<") text = text.replace(/>/g, ">") - text = text.replace(/S(\d+)/g, sub_region_name) + text = text.replace(/%(\d+)/g, sub_region_name) text = text.replace(/\b[BW]\d\b/g, sub_dice_image) if (text.match(/^.turn/)) { @@ -1364,9 +1382,9 @@ function on_log(text) { text = text.substring(4) p.className = "h1" } - else if (text.match(/^\.h4/)) { + else if (text.match(/^\.h2/)) { text = text.substring(4) - p.className = "h3" + p.className = "h2" } else if (text.match(/^\.h3/)) { text = text.substring(4) @@ -341,9 +341,9 @@ const CARD_INDEX = [ ] const CARD_INFO = [ - { name: "M1", type: 0, value: 1, event: "Military 1" }, - { name: "S1", type: 1, value: 1, event: "Senate 1" }, - { name: "P1", type: 2, value: 1, event: "Populace 1" }, + { name: "M1", type: 0, value: 1, event: null }, + { name: "S1", type: 1, value: 1, event: null }, + { name: "P1", type: 2, value: 1, event: null }, { name: "M2", type: 0, value: 2, event: "Castra" }, { name: "S2", type: 1, value: 2, event: "Tribute" }, { name: "P2", type: 2, value: 2, event: "Quaestor" }, @@ -478,7 +478,7 @@ function get_mobs(where) { } function set_mobs(where, n) { - game.provinces[where] &= 7 << 4 + game.provinces[where] &= ~(7 << 4) game.provinces[where] |= n << 4 } @@ -1005,7 +1005,7 @@ function eliminate_barbarian(id) { log("Shapur killed!") game.battle.killed |= SHAPUR_BONUS } - set_barbarian_location(id, UNAVAILABLE) + set_barbarian_location(id, AVAILABLE) } else { set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) set_barbarian_inactive(id) @@ -1015,7 +1015,7 @@ function eliminate_barbarian(id) { function eliminate_rival_emperor(id) { if (game.battle) game.battle.killed |= (1 << id) - set_rival_emperor_location(id, UNAVAILABLE) + set_rival_emperor_location(id, AVAILABLE) } function eliminate_militia(where) { @@ -1098,7 +1098,7 @@ states.setup_province = { capital(where) { push_undo() - log(PLAYER_NAMES[game.current] + " started in S" + where + ".") + log(PLAYER_NAMES[game.current] + " started in %" + where + ".") set_governor_location(game.current * 6 + 0, where) @@ -1151,24 +1151,18 @@ function goto_start_turn() { } function goto_upkeep() { - // TODO: manually remove Quaestor and Castra? for (let i = 0; i < 6; ++i) { let id = game.current * 6 + i let where = get_governor_location(id) - if (is_province(where) && has_quaestor(where)) { - log("Removed Quaestor from S" + where) - remove_quaestor(where) - } - if (has_militia_castra(where)) { - log("Removed Castra from S" + where) - remove_militia_castra(where) + if (is_province(where)) { + if (has_quaestor(where)) + remove_quaestor(where) + if (has_militia_castra(where)) + remove_militia_castra(where) } - if (has_general_castra(id)) { - log("Removed Castra from S" + get_general_location(id)) + if (has_general_castra(id)) remove_general_castra(id) - } } - goto_crisis() } @@ -1247,6 +1241,8 @@ states.ira_deorum = { // CRISIS: PAX DEORUM +// TODO: Skip players if game end has triggered. + function goto_pax_deorum() { game.count = game.current game.current = prev_player() @@ -1254,31 +1250,36 @@ function goto_pax_deorum() { } function resume_pax_deorum() { - if (game.current === game.count) { - goto_take_actions() - return - } - game.state = "pax_deorum" - game.current = prev_player() if (game.draw[game.current].length === 0) flip_discard_to_available() - - // Skip players if game end has triggered. - if (game.end && game.current < game.count) - resume_pax_deorum() } states.pax_deorum = { - prompt(player) { + prompt() { prompt("Pax Deorum: Draw one card.") - for (let c of game.draw[player]) + for (let c of game.draw[game.current]) gen_action_card(c) }, - card(c, player) { - set_add(game.hand[player], c) - set_delete(game.draw[player], c) - resume_pax_deorum() + card(c) { + set_add(game.hand[game.current], c) + set_delete(game.draw[game.current], c) + game.state = "pax_deorum_done" + }, +} + +states.pax_deorum_done = { + prompt() { + prompt("Pax Deorum: Done.") + view.actions.done = 1 + }, + done() { + if (game.current === game.count) { + goto_take_actions() + } else { + game.current = prev_player() + resume_pax_deorum() + } }, } @@ -1577,6 +1578,9 @@ states.take_actions = { let where = UNAVAILABLE + if (mip + sip + pip === 0 && current_hand().length > 0) + view.actions.play_all = 1 + view.actions.end_actions = 1 // Play cards for IP @@ -1783,11 +1787,23 @@ states.take_actions = { goto_support_check() }, + play_all() { + push_undo() + let hand = current_hand() + while (hand.length > 0) { + let c = hand[0] + log("Played " + card_name(c) + ".") + set_delete(hand, c) + set_add(game.played, c) + add_card_ip(c) + } + }, + card(c) { push_undo() let hand = current_hand() if (set_has(hand, c)) { - log("Played " + card_event_name(c) + ".") + log("Played " + card_name(c) + ".") set_delete(hand, c) set_add(game.played, c) add_card_ip(c) @@ -1846,7 +1862,7 @@ states.take_actions = { hold_games() { push_undo() let where = get_governor_location(game.selected_governor) - log("Held Games in S" + where + ".") + log("Held Games in %" + where + ".") spend_ip(POPULACE, 2) set_mobs(where, get_mobs(where) - 1) }, @@ -1856,7 +1872,7 @@ states.take_actions = { spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_amphitheater(where) - log("Built Amphitheater in S" + where + ".") + log("Built Amphitheater in %" + where + ".") }, basilica() { @@ -1864,7 +1880,7 @@ states.take_actions = { spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_basilica(where) - log("Built Basilica in S" + where + ".") + log("Built Basilica in %" + where + ".") }, limes() { @@ -1872,7 +1888,7 @@ states.take_actions = { spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_limes(where) - log("Built Limes in S" + where + ".") + log("Built Limes in %" + where + ".") }, add_legion_to_army() { @@ -1909,7 +1925,7 @@ states.take_actions = { if (game.selected_general >= 0) n += count_units_in_army(game.selected_general) n = Math.min(get_mobs(where), n) - log("Disperse " + n + " Mobs in S" + where) + log("Disperse " + n + " Mobs in %" + where) set_mobs(where, get_mobs(where) - n) reduce_support(where) }, @@ -1994,7 +2010,7 @@ states.occupy_seat_of_power_1 = { }, region(where) { push_undo() - log("Removed Seat of Power in S" + where) + log("Removed Seat of Power in %" + where) remove_seat_of_power(where) remove_governor(where) resume_occupy_seat_of_power() @@ -2011,7 +2027,7 @@ states.occupy_seat_of_power_2 = { }, region(where) { push_undo() - log("Removed Breakaway in S" + where) + log("Removed Breakaway in %" + where) remove_breakaway(where) resume_occupy_seat_of_power() }, @@ -2025,7 +2041,7 @@ states.occupy_breakaway = { }, region(where) { push_undo() - log("Removed Breakaway in S" + where) + log("Removed Breakaway in %" + where) remove_breakaway(where) remove_governor(where) goto_replace_pretender() @@ -2053,7 +2069,7 @@ states.replace_pretender_governor = { }, governor(id) { push_undo() - log("Placed Governor in S" + game.where) + log("Placed Governor in %" + game.where) set_governor_location(id, game.where) game.state = "take_actions" }, @@ -2068,7 +2084,7 @@ states.replace_pretender_governor = { function improve_support() { let where = get_governor_location(game.selected_governor) let support = get_support(where) - log("Built Support in S" + where + ".") + log("Built Support in %" + where + ".") spend_ip(POPULACE, support + 1) set_support(where, support + 1) } @@ -2077,7 +2093,7 @@ function improve_support() { function recall_governor() { let where = get_governor_location(game.selected_governor) - log("Recalled Governor from S" + where + ".") + log("Recalled Governor from %" + where + ".") spend_ip(SENATE, 2) set_placed_governor(where) remove_governor(where) @@ -2106,7 +2122,7 @@ function increase_support(where) { } function remove_governor(where) { - log("Removed Governor from S" + where) + log("Removed Governor from %" + where) eliminate_militia(where) set_mobs(where, 0) @@ -2237,9 +2253,9 @@ function roll_to_place_governor(pg) { game.count += count_own_basilicas() if (pg) - log("Praetorian Guard in S" + game.where) + log("Praetorian Guard in %" + game.where) else - log("Place Governor in S" + game.where) + log("Place Governor in %" + game.where) if (is_neutral_province(game.where)) have = roll_dice(game.count, 1) @@ -2313,7 +2329,7 @@ states.damnatio_memoriae_mobs = { }, region(where) { push_undo() - log("Added Mob to S" + where) + log("Added Mob to %" + where) set_mobs(where, get_mobs(where) + 1) if (--game.count === 0) game.state = "take_actions" @@ -2363,7 +2379,7 @@ function gen_create_army() { function create_army(where) { spend_ip(MILITARY, 1) - log("Created Army in S" + where + ".") + log("Created Army in %" + where + ".") set_general_location(game.selected_general, where) if (can_enter_capital(where)) set_general_inside_capital(game.selected_general) @@ -2408,7 +2424,7 @@ states.move_army_at_sea = { } function move_army_to(who, to) { - log("Moved Army to S" + to + ".") + log("Moved Army to %" + to + ".") remove_general_castra(who) @@ -2435,7 +2451,7 @@ function can_play_castra() { function play_castra() { let where = get_selected_region() if (game.selected_governor >= 0 && is_province(where)) { - log("Castra in S" + where) + log("Castra in %" + where) add_militia_castra(where) } if (game.selected_general >= 0) { @@ -2455,7 +2471,7 @@ function can_play_quaestor() { function play_quaestor() { let where = get_selected_region() - log("Quaestor in S" + where) + log("Quaestor in %" + where) add_quaestor(where) } @@ -2487,7 +2503,7 @@ states.tribute = { barbarian(target) { let where = get_barbarian_location(target) let tribe = get_barbarian_tribe(target) - log("Tribute " + BARBARIAN_NAME[tribe] + " in S" + where) + log("Tribute " + BARBARIAN_NAME[tribe] + " in %" + where) for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) if (get_barbarian_location(id) === where && is_barbarian_active(id)) set_barbarian_inactive(id) @@ -2553,7 +2569,8 @@ states.foederati = { prompt() { prompt("Foederati: Remove or recruit a barbarian.") view.selected_general = game.selected_general - let from = get_general_location(game.selected_general) + view.selected_governor = game.selected_governor + let from = get_selected_region() gen_foederati(from) for (let to of ADJACENT[from]) gen_foederati(to) @@ -2561,7 +2578,7 @@ states.foederati = { barbarian(id) { let tribe = get_barbarian_tribe(id) let from = get_barbarian_location(id) - log("Foederati " + BARBARIAN_NAME[tribe] + " from S" + from) + log("Foederati " + BARBARIAN_NAME[tribe] + " from %" + from) if (game.selected_general >= 0 && count_legions_in_army(game.selected_general) > count_barbarians_in_army(game.selected_general)) set_barbarian_location(id, ARMY + game.selected_general) else @@ -2592,7 +2609,7 @@ states.mob = { gen_action_region(where) }, region(where) { - log("Mob in S" + where) + log("Mob in %" + where) set_mobs(where, get_mobs(where) + 1) game.state = "take_actions" }, @@ -2602,11 +2619,11 @@ states.mob = { function auto_remove_pretender_empire(seat) { let pretender = get_province_governor(seat) / 6 | 0 - log("Removed Seat of Power in S" + seat) + log("Removed Seat of Power in %" + seat) remove_seat_of_power(seat) for (let where = 1; where < 12; ++where) { if (is_breakaway(where) && (get_province_governor(where) / 6 | 0) === pretender) { - log("Removed Breakaway in S" + where) + log("Removed Breakaway in %" + where) remove_breakaway(where) } } @@ -2638,7 +2655,7 @@ states.pretender_seat_of_power = { }, region(where) { push_undo() - log("Seat of Power in S" + where) + log("Seat of Power in %" + where) add_seat_of_power(where) remove_quaestor(where) // no effect anymore goto_pretender_breakaway() @@ -2667,7 +2684,7 @@ states.pretender_breakaway = { }, region(where) { push_undo() - log("Breakaway in S" + where) + log("Breakaway in %" + where) add_breakaway(where) remove_quaestor(where) // no effect anymore goto_pretender_breakaway() @@ -2681,23 +2698,23 @@ function play_flanking_maneuver() { } function goto_battle_vs_general(where, attacker, target) { - log("Initiate Battle against " + GENERAL_NAME[target] + " in S" + where) + log("Initiate Battle against " + GENERAL_NAME[target] + " in %" + where) goto_battle("general", where, attacker, target) } function goto_battle_vs_barbarian(where, attacker, target) { let tribe = get_barbarian_tribe(target) - log("Initiate Battle against " + BARBARIAN_NAME[tribe] + " in S" + where) + log("Initiate Battle against " + BARBARIAN_NAME[tribe] + " in %" + where) goto_battle("barbarians", where, attacker, tribe) } function goto_battle_vs_rival_emperor(where, attacker, target) { - log("Initiate Battle against " + RIVAL_EMPEROR_NAME[target] + " in S" + where) + log("Initiate Battle against " + RIVAL_EMPEROR_NAME[target] + " in %" + where) goto_battle("rival_emperor", where, attacker, target) } function goto_battle_vs_militia(where, attacker) { - log("Initiated Battle against militia in S" + where) + log("Initiated Battle against militia in %" + where) goto_battle("militia", where, attacker, -1) } @@ -2785,12 +2802,17 @@ function format_hits() { states.flanking_maneuver = { prompt() { prompt("Flanking Maneuver: " + format_hits() + ".") + view.actions.continue = 1 + view.actions.pass = 1 // XXX view.actions.reroll = 1 - view.actions.pass = 1 }, + // XXX pass() { goto_assign_hits() }, + continue() { + goto_assign_hits() + }, reroll() { roll_flanking_maneuver_dice() goto_assign_hits() @@ -2886,9 +2908,9 @@ function roll_attacker_dice() { let n = get_plague_hits() if (game.battle.attacker < 0) - n = roll_militia_dice() + n += roll_militia_dice() else - n = roll_general_dice(game.battle.attacker) + n += roll_general_dice(game.battle.attacker) if (game.battle.type === "militia" && has_militia_castra(game.where)) { log("Castra reduces 1 hit") @@ -2913,16 +2935,16 @@ function roll_defender_dice() { let n = get_plague_hits() switch (game.battle.type) { case "militia": - n = roll_militia_dice() + n += roll_militia_dice() break case "rival_emperor": - n = roll_rival_emperor_dice() + n += roll_rival_emperor_dice() break case "barbarians": - n = roll_barbarian_dice(game.battle.target) + n += roll_barbarian_dice(game.battle.target) break case "general": - n = roll_general_dice(game.battle.target) + n += roll_general_dice(game.battle.target) break } @@ -2932,7 +2954,7 @@ function roll_defender_dice() { // COMBAT: ASSIGN HITS function has_hits_rival_emperor(id) { - return get_rival_emperor_location(id) !== UNAVAILABLE + return get_rival_emperor_location(id) !== AVAILABLE } function gen_hits_rival_emperor(id) { @@ -3266,7 +3288,7 @@ function goto_support_check() { function is_any_rival_emperor_or_pretender() { for (let i = 0; i < 3; ++i) - if (get_rival_emperor_location(i) !== UNAVAILABLE) + if (get_rival_emperor_location(i) !== AVAILABLE) return true for (let where = 0; where < 12; ++where) if (is_seat_of_power(where) && is_enemy_province(where)) @@ -3431,6 +3453,7 @@ function goto_gain_legacy_provinces() { award_legacy(game.current, "Improvements", count_own_improvements()) if (is_emperor_player() && game.legacy[game.current] >= 60) { + log_br() log("Game will end after this round!") game.end = 1 } @@ -3810,7 +3833,7 @@ function setup_market_pile(cards) { function setup_barbarians(tribe, home) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { if (is_barbarian_leader(id)) { - set_barbarian_location(id, UNAVAILABLE) + set_barbarian_location(id, AVAILABLE) } else { set_barbarian_location(id, home) set_barbarian_inactive(id) @@ -3878,19 +3901,17 @@ exports.setup = function (seed, scenario, options) { setup_market_pile(CARD_P4), ] + set_rival_emperor_location(POSTUMUS, AVAILABLE) + set_rival_emperor_location(PRIEST_KING, AVAILABLE) + set_rival_emperor_location(ZENOBIA, AVAILABLE) + setup_barbarians(ALAMANNI, ALAMANNI_HOMELAND) setup_barbarians(FRANKS, FRANKS_HOMELAND) setup_barbarians(GOTHS, GOTHS_HOMELAND) - if (player_count >= 3) setup_barbarians(SASSANIDS, SASSANIDS_HOMELAND) - else - setup_barbarians(SASSANIDS, UNAVAILABLE) - if (player_count >= 4) setup_barbarians(NOMADS, NOMADS_HOMELAND) - else - setup_barbarians(NOMADS, UNAVAILABLE) for (let player = 0; player < player_count; ++player) { game.hand[player] = [] @@ -3942,31 +3963,19 @@ exports.view = function (state, player_name) { prompt: null, provinces: game.provinces, - /* - support: game.support, - mobs: game.mobs, - militia: game.militia, - quaestor: game.quaestor, - castra: game.castra, - mcastra: game.mcastra, - amphitheater: game.amphitheater, - basilica: game.basilica, - limes: game.limes, - seat_of_power: game.seat_of_power, - breakaway: game.breakaway, - */ - governors: game.governors, generals: game.generals, legions: game.legions, barbarians: game.barbarians, + first: game.first, crisis: game.crisis, dice: game.dice, event: game.active_event, + market: game.market.map(m => m[0] | 0), + played: game.played, used: game.used, - market: game.market.map(m => m[0] | 0), legacy: game.legacy, emperor_turns: game.emperor_turns, |