"use strict" // TODO: battle dialog popup for rolling and assigning hits! // TODO: show killed leaders taken for bonus purchase // === SYNC with rules.js === const LEGION_COUNT = 33 const BARBARIAN_COUNT = 53 const CARD_M1 = [ 0, 11 ] const CARD_S1 = [ 12, 23 ] const CARD_P1 = [ 24, 35 ] const CARD_M2 = [ 36, 44 ] const CARD_S2 = [ 45, 53 ] const CARD_P2 = [ 54, 62 ] const CARD_M2X = [ 63, 71 ] const CARD_S2X = [ 72, 80 ] const CARD_P2X = [ 81, 89 ] const CARD_M3 = [ 90, 97 ] const CARD_S3 = [ 98, 105 ] const CARD_P3 = [ 106, 113 ] const CARD_M3X = [ 114, 121 ] const CARD_S3X = [ 122, 129 ] const CARD_P3X = [ 130, 137 ] const CARD_M4 = [ 138, 143 ] const CARD_S4 = [ 144, 149 ] const CARD_S4B = [ 150, 155 ] const CARD_P4 = [ 156, 161 ] const CARD_M4X = [ 162, 167 ] const CARD_S4X = [ 168, 173 ] const CARD_P4X = [ 174, 179 ] const CARD_INDEX = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, ] const CARD_INFO = [ { name: "M1", type: 0, cost: 1, event: "None" }, { name: "S1", type: 1, cost: 1, event: "None" }, { name: "P1", type: 2, cost: 1, event: "None" }, { name: "M2", type: 0, cost: 2, event: "Castra" }, { name: "S2", type: 1, cost: 2, event: "Tribute" }, { name: "P2", type: 2, cost: 2, event: "Quaestor" }, { name: "M2X", type: 0, cost: 2, event: "Cavalry" }, { name: "S2X", type: 1, cost: 2, event: "Princeps Senatus" }, { name: "P2X", type: 2, cost: 2, event: "Ambitus" }, { name: "M3", type: 0, cost: 3, event: "Flanking Maneuver" }, { name: "S3", type: 1, cost: 3, event: "Foederati" }, { name: "P3", type: 2, cost: 3, event: "Mob" }, { name: "M3X", type: 0, cost: 3, event: "Force March" }, { name: "S3X", type: 1, cost: 3, event: "Frumentarii" }, { name: "P3X", type: 2, cost: 3, event: "Mobile Vulgus" }, { name: "M4", type: 0, cost: 4, event: "Praetorian Guard" }, { name: "S4", type: 1, cost: 4, event: "Damnatio Memoriae" }, { name: "S4B", type: 1, cost: 4, event: "Damnatio Memoriae (exp)" }, { name: "P4", type: 2, cost: 4, event: "Pretender" }, { name: "M4X", type: 0, cost: 4, event: "Spiculum" }, { name: "S4X", type: 1, cost: 4, event: "Triumph" }, { name: "P4X", type: 2, cost: 4, event: "Demagogue" }, ] const ITALIA = 0 const ASIA = 1 const GALLIA = 2 const MACEDONIA = 3 const PANNONIA = 4 const THRACIA = 5 const AEGYPTUS = 6 const AFRICA = 7 const HISPANIA = 8 const BRITANNIA = 9 const GALATIA = 10 const SYRIA = 11 const ALAMANNI = 0 const FRANKS = 1 const GOTHS = 2 const NOMADS = 3 const SASSANIDS = 4 const ALAMANNI_HOMELAND = 12 const FRANKS_HOMELAND = 13 const GOTHS_HOMELAND = 14 const NOMADS_HOMELAND = 15 const SASSANIDS_HOMELAND = 16 const MARE_OCCIDENTALE = 17 const MARE_ORIENTALE = 18 const OCEANUS_ATLANTICUS = 19 const PONTUS_EUXINUS = 20 const AVAILABLE = 21 const UNAVAILABLE = 22 const ARMY = 23 const first_barbarian = [ 0, 10, 20, 31, 41 ] const last_barbarian = [ 9, 19, 30, 40, 52 ] const first_governor = [ 0, 6, 12, 18 ] const first_general = [ 0, 6, 12, 18 ] const CNIVA = first_barbarian[GOTHS] + 0 const ARDASHIR = first_barbarian[SASSANIDS] + 0 const SHAPUR = first_barbarian[SASSANIDS] + 1 const REGION_NAME = [ "Italia", "Asia", "Gallia", "Macedonia", "Pannonia", "Thracia", "Aegyptus", "Africa", "Hispania", "Britannia", "Galatia", "Syria", "Alamanni Homeland", "Franks Homeland", "Goths Homeland", "Nomads Homeland", "Sassanids Homeland", "Mare Occidentale", "Mare Orientale", "Oceanus Atlanticus", "Pontus Euxinus", "Available", "Unavailable", ] function is_no_place_governor(province) { return province >= view.support.length } function get_support(province) { return view.support[province] } function get_rival_emperor_location(id) { return view.rival_emperors[id] } function get_barbarian_location(id) { return view.barbarians[id] & 63 } function is_barbarian_inactive(id) { return view.barbarians[id] & 64 } function get_legion_location(ix) { return view.legions[ix] & 63 } function is_legion_reduced(ix) { return view.legions[ix] & 64 } function is_legion_unused(ix) { return view.legions[ix] === AVAILABLE } function get_governor_location(id, loc) { return view.governors[id] & 63 } function get_general_location(id) { return view.generals[id] & 63 } function is_general_inside_capital(id) { return view.generals[id] & 64 } function has_general_castra(id) { return view.castra & (1 << id) } function has_militia_castra(province) { return view.mcastra & (1 << province) } function has_quaestor(province) { return view.quaestor & (1 << province) } function has_militia(province) { return view.militia & (1 << province) } function get_mobs(province) { return view.mobs[province] } function has_amphitheater(province) { return view.amphitheater & (1 << province) } function has_basilica(province) { return view.basilica & (1 << province) } function has_limes(province) { return view.limes & (1 << province) } function is_breakaway(province) { return view.breakaway & (1 << province) } function is_seat_of_power(province) { return view.seat_of_power & (1 << province) } function find_governor(f) { let n = view.legacy.length * 6 for (let id = 0; id < n; ++id) if (f(id, get_governor_location(id))) return id return -1 } function get_province_governor(where) { return find_governor((id, loc) => loc === where) } // === END SYNC === function set_has(set, item) { let a = 0 let b = set.length - 1 while (a <= b) { let m = (a + b) >> 1 let x = set[m] if (item < x) b = m - 1 else if (item > x) a = m + 1 else return true } return false } const PLAYER_CLASS = [ "red", "blue", "yellow", "green" ] const BARBARIAN_CLASS = [ "alamanni", "franks", "goths", "nomads", "sassanids" ] const BOXES = { "Thracia Support": [ 1502, 720, 258, 52 ], "Syria Support": [ 2034, 1280, 258, 52 ], "Pannonia Support": [ 1154, 626, 258, 53 ], "Macedonia Support": [ 1384, 936, 258, 53 ], "Hispania Support": [ 154, 980, 258, 53 ], "Gallia Support": [ 460, 507, 258, 53 ], "Galatia Support": [ 1954, 931, 258, 53 ], "Britannia Support": [ 231, 260, 258, 52 ], "Asia Support": [ 1679, 1000, 258, 52 ], "Africa Support": [ 647, 1290, 258, 53 ], "Aegyptus Support": [ 1700, 1468, 258, 53 ], "Italia Support 2": [ 1054, 887, 258, 52 ], "Italia Support 1": [ 1028, 835, 258, 52 ], "Thracia Capital": [ 1594, 631, 70, 70 ], "Syria Capital": [ 2174, 1193, 70, 70 ], "Pannonia Capital": [ 1214, 536, 70, 70 ], "Macedonia Capital": [ 1477, 850, 70, 70 ], "Italia Capital": [ 1038, 743, 70, 70 ], "Hispania Capital": [ 249, 892, 70, 70 ], "Gallia Capital": [ 554, 418, 70, 70 ], "Galatia Capital": [ 2048, 842, 70, 70 ], "Britannia Capital": [ 325, 177, 70, 70 ], "Asia Capital": [ 1790, 908, 70, 70 ], "Africa Capital": [ 741, 1204, 70, 70 ], "Aegyptus Capital": [ 1793, 1380, 70, 70 ], "Pontus Euxinus XY": [ 1880, 580, 60, 60 ], "Mare Orientale XY": [ 1480, 1160, 60, 60 ], "Mare Occidentale XY": [ 720, 900, 60, 60 ], "Oceanus Atlanticus XY": [ 180, 500, 60, 60 ], "Nomads XY": [ 520, 1460, 60, 60 ], "Sassanids XY": [ 2440, 820, 60, 60 ], "Goths XY": [ 2020, 360, 60, 60 ], "Alamanni XY": [ 1540, 280, 60, 60 ], "Franks XY": [ 1160, 300, 60, 60 ], "Franks Dice": [785,160,100,50], "Alamanni Dice": [1265,160,100,50], "Goths Dice": [1730,195,100,50], "Sassanids Dice": [2380,895,100,50], "Nomads Dice": [570,1520,100,50], } const LAYOUT_XY = [ BOXES["Italia Capital"], BOXES["Asia Capital"], BOXES["Gallia Capital"], BOXES["Macedonia Capital"], BOXES["Pannonia Capital"], BOXES["Thracia Capital"], BOXES["Aegyptus Capital"], BOXES["Africa Capital"], BOXES["Hispania Capital"], BOXES["Britannia Capital"], BOXES["Galatia Capital"], BOXES["Syria Capital"], BOXES["Alamanni XY"], BOXES["Franks XY"], BOXES["Goths XY"], BOXES["Nomads XY"], BOXES["Sassanids XY"], BOXES["Mare Occidentale XY"], BOXES["Mare Orientale XY"], BOXES["Oceanus Atlanticus XY"], BOXES["Pontus Euxinus XY"], ] const LAYOUT_SUPPORT = [ BOXES["Italia Support 1"], BOXES["Asia Support"], BOXES["Gallia Support"], BOXES["Macedonia Support"], BOXES["Pannonia Support"], BOXES["Thracia Support"], BOXES["Aegyptus Support"], BOXES["Africa Support"], BOXES["Hispania Support"], BOXES["Britannia Support"], BOXES["Galatia Support"], BOXES["Syria Support"], ] const LAYOUT_DICE = [ BOXES["Alamanni Dice"], BOXES["Franks Dice"], BOXES["Goths Dice"], BOXES["Nomads Dice"], BOXES["Sassanids Dice"], ] const LAYOUT_QUAESTOR = [ [ 971, 829 ], [ 1622, 994 ], [ 403, 501 ], [ 1327, 930 ], [ 1097, 620 ], [ 1445, 714 ], [ 1643, 1462 ], [ 590, 1284 ], [ 97, 974 ], [ 174, 254 ], [ 1897, 925 ], [ 1977, 1274 ], ] const LAYOUT_SEA = [ [ 0, 0 ] ] const LAYOUT_HOMELAND = [ [ 0, 0 ], [ -1, 0 ], [ -2, 0 ], [ -3, 0 ], [ -3, -1 ], [ -2, -1 ], [ -1, -1 ], [ 0, -1 ], ] const LAYOUT_ALAMANNI = [ [ 0, 0 ], [ -1, 0 ], [ -2, 0 ], [ -2, 1 ], [ -2, -1 ], ] const LAYOUT_SASSANIDS = [ [ 0, 0 ], [ -1, 0 ], [ -2, 0 ], [ 0, -1 ], [ -1, -1 ], [ -2, -1 ], [ -2, 1 ], [ -1, 1 ], [ 0, 1 ], [ -2, 2 ], [ -1, 2 ], [ 0, 2 ], ] const LAYOUT_NOMADS = [ [ 0, 0 ], [ 1, 0 ], [ 4, 0 ], [ 5, 0 ], [ -1, -1 ], [ 2, 0 ], [ 3, 0 ], [ -2, -1 ], [ -3, -1 ], [ 0, 1 ], ] const LAYOUT_ITALIA = [ //[ 0, -2 ], [ -1, -2 ], [ -2, -2 ], [ -3, -2 ], [ -2, -1 ], [ -1, -1 ], [ 0, -1 ], [ 1, 0 ], [ 2, 0 ], [ -1, 0 ], [ 0, 3 ], ] const LAYOUT_ASIA = [ [ -1, 0 ], [ 1, 0 ], [ -1, -1 ], [ 0, -1 ], [ 1, -1 ], [ 1, -2 ], [ 0, -2 ], [ 1, -3 ], [ -1, 3 ], ] const LAYOUT_GALLIA = [ [ -1, 0 ], [ 1, 0 ], [ 2, 0 ], [ 2, -1 ], [ 2, -2 ], [ -2, 0 ], [ 1, -1 ], [ 1, -2 ], [ 0, -1 ], [ 0, -2 ], [ -1, 3 ], ] const LAYOUT_MACEDONIA = [ [ -1, 0 ], [ -2, 0 ], [ 1, 0 ], [ 0, -1 ], [ -1, -1 ], [ -2, -1 ], [ -1, 3 ], ] const LAYOUT_PANNONIA = [ [ -3.5, -1.4 ], [ -2.5, -1.4 ], [ -1.5, -1.4 ], [ -0.5, -1.4 ], [ +0.5, -1.4 ], [ -4.5, -1.4 ], [ -1, 0 ], [ 1, 0 ], ] const LAYOUT_THRACIA = [ [ -2, -1 ], [ -2, -2 ], [ -1, 0 ], [ 1, 0 ], [ 1, -1 ], [ -2, -3 ], [ 0, -1 ], [ -1, -1 ], [ -1, -2 ], ] const LAYOUT_AEGYPTUS = [ [ -1, 0 ], [ -2, 0 ], [ -3, 0 ], [ -4, 0 ], [ 1, 0 ], [ -4, -1 ], [ -4, 1 ], [ -3, 1 ], [ -4, 2 ], [ -3, 2 ], [ 3, 2 ], ] const LAYOUT_AFRICA = [ [ -1, 0 ], [ -2, 0 ], [ -3, 0 ], [ -4, 0 ], [ 1, 0 ], [ 2, 0 ], [ 2, -1 ], [ 1, -1 ], [ -4, 1 ], [ -3, 1 ], [ 3, 2 ], [ 4, 2 ], [ 5, 2 ], [ 6, 2 ], [ 5, 3 ], ] const LAYOUT_HISPANIA = [ [ -1, 0 ], [ 1, 0 ], [ 2, 0 ], [ 3, -1 ], [ -2, 0 ], [ 2, -1 ], [ 1, -1 ], [ 0, -1 ], [ -1, -1 ], [ -1, -2 ], [ 0, -2 ], [ 1, -2 ], [ -1, 3 ], ] const LAYOUT_BRITANNIA = [ [ -1, 0 ], [ 1, 0 ], [ 2, 0 ], [ -2, 0 ], [ -3, 0 ], [ -3, 1 ], [ -3, 2 ], [ -2, 1 ], [ -2, 2 ], [ -3, 3 ], ] const LAYOUT_GALATIA = [ [ -1, 0 ], [ 1, 0 ], [ 1, -1 ], [ 0, -1 ], [ -1, -1 ], [ 1, -2 ], [ 0, -2 ], [ -1, -2 ], [ -1, -3 ], ] const LAYOUT_SYRIA = [ [ -1, 0 ], [ 1, 0 ], [ 1, -1 ], [ 0, -1 ], [ -1, -1 ], [ -2, 0 ], [ 2, 0 ], [ -2, -1 ], [ 2, -1 ], [ 0, -2 ], [ -1, -2 ], [ 1, -2 ], ] const LAYOUT_PATTERN = [ LAYOUT_ITALIA, LAYOUT_ASIA, LAYOUT_GALLIA, LAYOUT_MACEDONIA, LAYOUT_PANNONIA, LAYOUT_THRACIA, LAYOUT_AEGYPTUS, LAYOUT_AFRICA, LAYOUT_HISPANIA, LAYOUT_BRITANNIA, LAYOUT_GALATIA, LAYOUT_SYRIA, LAYOUT_ALAMANNI, LAYOUT_HOMELAND, LAYOUT_HOMELAND, LAYOUT_NOMADS, LAYOUT_SASSANIDS, LAYOUT_SEA, LAYOUT_SEA, LAYOUT_SEA, LAYOUT_SEA, ] let ui = { cards: [], event_cards: [], militia: [], body: document.querySelector("body"), header: document.querySelector("header"), hand: document.getElementById("hand"), draw: document.getElementById("draw"), active_event: document.getElementById("active_event"), discard: document.getElementById("discard"), played: document.getElementById("played"), market: document.getElementById("market"), pieces: document.getElementById("pieces"), legacy: [ document.getElementById("red_legacy"), document.getElementById("blue_legacy"), document.getElementById("yellow_legacy"), document.getElementById("green_legacy"), ], emperor_turns: [ document.getElementById("red_emperor_turns"), document.getElementById("blue_emperor_turns"), document.getElementById("yellow_emperor_turns"), document.getElementById("green_emperor_turns"), ], regions: [ document.getElementById("mapsvg").getElementById("region_italia"), document.getElementById("mapsvg").getElementById("region_asia"), document.getElementById("mapsvg").getElementById("region_gallia"), document.getElementById("mapsvg").getElementById("region_macedonia"), document.getElementById("mapsvg").getElementById("region_pannonia"), document.getElementById("mapsvg").getElementById("region_thracia"), document.getElementById("mapsvg").getElementById("region_aegyptus"), document.getElementById("mapsvg").getElementById("region_africa"), document.getElementById("mapsvg").getElementById("region_hispania"), document.getElementById("mapsvg").getElementById("region_britannia"), document.getElementById("mapsvg").getElementById("region_galatia"), document.getElementById("mapsvg").getElementById("region_syria"), document.getElementById("mapsvg").getElementById("region_alamanni"), document.getElementById("mapsvg").getElementById("region_franks"), document.getElementById("mapsvg").getElementById("region_goths"), document.getElementById("mapsvg").getElementById("region_nomads"), document.getElementById("mapsvg").getElementById("region_sassanids"), document.getElementById("mapsvg").getElementById("region_mare_occidentale"), document.getElementById("mapsvg").getElementById("region_mare_orientale"), document.getElementById("mapsvg").getElementById("region_oceanus_atlanticus"), document.getElementById("mapsvg").getElementById("region_pontus_euxinus"), ], capital: [], quaestor: [], amphitheater: [], basilica: [], limes: [], dice: [ document.getElementById("crisis_die_1"), document.getElementById("crisis_die_2"), document.getElementById("barbarian_die_1"), document.getElementById("barbarian_die_2"), ], neutral_governors: [], barbarian_leaders: [], rival_emperors: [], seat_of_power: [], breakaway: [], legions: [], barbarians: [ [], [], [], [], [] ], generals: [ [], [], [], [] ], governors: [ [], [], [], [] ], castra: [ [], [], [], [] ], mcastra: [], mobs: [], } function get_province_governor_player(where) { let np = view.legacy.length for (let p = 0; p < np; ++p) for (let i = 0; i < 6; ++i) if (get_governor_location(first_governor[p] + i) === where) return p return -1 } function is_neutral_province(where) { if (is_no_place_governor(where)) return false return get_province_governor_player(where) < 0 } function show(elt) { elt.classList.remove("hide") } function hide(elt) { elt.classList.add("hide") } function toggle_pieces() { ui.pieces.classList.toggle("hide") } function create(t, p, ...c) { let e = document.createElement(t) Object.assign(e, p) e.append(c) if (p.my_action) register_action(e, p.my_action, p.my_id) return e } function create_thing(p) { let e = create("div", p) ui.pieces.appendChild(e) return e } function create_piece(id, action, css_class, dom_id) { if (dom_id) return create_thing({ className: css_class + " hide", id: dom_id, my_action: action, my_id: id }) return create_thing({ className: css_class + " hide", my_action: action, my_id: id }) } let action_register = [] function register_action(target, action, id) { target.my_action = action target.my_id = id target.onmousedown = (evt) => on_click_action(evt, target) action_register.push(target) } function on_click_action(evt, target) { if (evt.button === 0) if (send_action(target.my_action, target.my_id)) evt.stopPropagation() } function create_building(region, className, xoff, yoff) { let [ x, y, w, h ] = LAYOUT_SUPPORT[region] if (region === ITALIA) y += 52 let e = create_thing({ className }) e.style.left = x + (w >> 1) + xoff - 46 + "px" e.style.top = y + h + yoff + "px" return e } function create_support_buttons(region) { let [ x0, y0 ] = LAYOUT_SUPPORT[region] for (let i = 0; i <= 4; ++i) { let x = Math.floor(-1 + x0 + i * 51.6666) let y = (-1 + y0) let e = create_thing({ className: "support s" + i, my_action: i > 0 ? "support" : "recall", my_id: i > 0 ? (region << 3) + i : region }) e.style.top = y + "px" e.style.left = x + "px" } } function is_action(action, arg) { if (arg === undefined) return !!(view.actions && view.actions[action] === 1) return !!(view.actions && view.actions[action] && set_has(view.actions[action], arg)) } function on_init() { for (let c = 0; c < CARD_INDEX.length; ++c) { let name = CARD_INFO[CARD_INDEX[c]].name ui.cards[c] = create("div", { className: "card influence influence_" + name.toLowerCase(), my_action: "card", my_id: c }) } for (let e = 0; e <= 15; ++e) { ui.event_cards[e] = create("div", { className: "card event event_" + e }) } for (let i = 0; i < LEGION_COUNT; ++i) ui.legions[i] = create_piece(i, "legion", "legion", "legion_" + i) for (let p = 0; p < 4; ++p) { for (let i = 0; i < 12; ++i) { ui.seat_of_power[p * 12 + i] = create_thing({ className: PLAYER_CLASS[p] + " seat_of_power hide" }) ui.seat_of_power[p * 12 + i].style.left = (LAYOUT_QUAESTOR[i][0] + 16) + "px" ui.seat_of_power[p * 12 + i].style.top = (LAYOUT_QUAESTOR[i][1]) + "px" ui.breakaway[p * 12 + i] = create_thing({ className: PLAYER_CLASS[p] + " breakaway hide" }) ui.breakaway[p * 12 + i].style.left = (LAYOUT_QUAESTOR[i][0] + 16) + "px" ui.breakaway[p * 12 + i].style.top = (LAYOUT_QUAESTOR[i][1]) + "px" } } ui.rival_emperors[0] = create_piece(0, "rival_emperor", "rival_emperor", "postumus") ui.rival_emperors[1] = create_piece(1, "rival_emperor", "rival_emperor", "priest_king") ui.rival_emperors[2] = create_piece(2, "rival_emperor", "rival_emperor", "zenobia") for (let tribe = 0; tribe < 5; ++tribe) for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) ui.barbarians[id] = create_piece(id, "barbarian", BARBARIAN_CLASS[tribe]) ui.barbarians[CNIVA].id = "cniva" ui.barbarians[SHAPUR].id = "shapur" ui.barbarians[ARDASHIR].id = "ardashir" for (let p = 0; p < 4; ++p) { for (let g = 0; g < 6; ++g) { ui.castra[p][g] = create_thing({ className: "castra hide" }) ui.governors[p][g] = create_piece(p * 6 + g, "governor", PLAYER_CLASS[p] + " governor n" + g) ui.generals[p][g] = create_piece(p * 6 + g, "general", PLAYER_CLASS[p] + " general n" + g) } } for (let region = 0; region < 12; ++region) { ui.mcastra[region] = create_thing({ className: "castra hide" }) ui.militia[region] = create_thing({ className: "militia hide", my_action: "militia", my_id: region }) ui.capital[region] = document.getElementById(REGION_NAME[region] + "_Capital") ui.quaestor[region] = create_thing({ className: "quaestor hide" }) ui.quaestor[region].style.left = (LAYOUT_QUAESTOR[region][0] + 16) + "px" ui.quaestor[region].style.top = (LAYOUT_QUAESTOR[region][1]) + "px" // at most 3 mobs per province ui.mobs[region * 3 + 0] = create_piece(region, "mob", "mob") ui.mobs[region * 3 + 1] = create_piece(region, "mob", "mob") ui.mobs[region * 3 + 2] = create_piece(region, "mob", "mob") if (true) { ui.amphitheater[region] = create_building(region, "amphitheater hide", -48 - 3, 6) ui.basilica[region] = create_building(region, "basilica hide", 48 + 3, 6) ui.limes[region] = create_building(region, "limes hide", 0, 6 + 25) } else { ui.amphitheater[region] = create_building(region, "amphitheater hide", -96 - 5, 6) ui.basilica[region] = create_building(region, "basilica hide", 0, 6) ui.limes[region] = create_building(region, "limes hide", 96 + 5, 6) } register_action(ui.capital[region], "capital", region) register_action(ui.regions[region], "region", region) create_support_buttons(region) ui.neutral_governors[region] = create_thing({ className: "neutral governor hide" }) } for (let region = 12; region < 21; ++region) { register_action(ui.regions[region], "region", region) } } let stack_count = new Array(21).fill(0) function layout_stack(id, list, region, in_capital, dx, dy) { let [ x, y, w, h ] = LAYOUT_XY[region] let z = 1 x += w >> 1 y += h >> 1 x -= 30 y -= 30 if (!in_capital) { let step = (region < 12) ? 80 : 100 let sc = stack_count[region] if (sc >= LAYOUT_PATTERN[region].length) sc = LAYOUT_PATTERN[region].length - 1 let xo = LAYOUT_PATTERN[region][sc][0] * step let yo = LAYOUT_PATTERN[region][sc][1] * step if (stack_count[region] > sc) xo += (stack_count[region] - sc) * step x += xo y += yo stack_count[region] += 1 } for (let i = list.length - 1; i >= 0; --i) { let item = list[i] item.style.left = x + "px" item.style.top = y + "px" item.style.zIndex = z item.my_stack = id x -= dx y -= dy z += 1 } } function layout_available(list, dx, x0, y0) { let y = 1650 + 45 - y0 let x = 25 + x0 let z = 7 for (let item of list) { item.style.left = x + "px" item.style.top = y + "px" item.style.zIndex = z item.my_stack = 0 x += dx z -= 1 } } function layout_governor(e, color, region) { e.className = color + " governor s" + get_support(region) e.style.left = LAYOUT_SUPPORT[region][0] - 1 + "px" e.style.top = LAYOUT_SUPPORT[region][1] - 1 + "px" } function layout_governor_available(e, color) { e.className = color + " governor" } function layout_governor_unavailable(e, color, ix) { e.className = color + " governor n" + ix } function layout_mob(region, i, e, visible, x2) { if (visible) { let [ x, y, w, h ] = LAYOUT_SUPPORT[region] e.className = x2 ? "mob_x2" : "mob" e.style.top = (y - 36) + "px" e.style.left = (x + 26 + 26 * i) + "px" } else { e.className = "hide" } } function layout_barbarian_dice(black, white, tribe) { if (tribe >= 0) { show(black) show(white) let [ x, y, w, h ] = LAYOUT_DICE[tribe] black.style.top = (y + 4) + "px" white.style.top = (y + 4) + "px" black.style.left = (x + 0) + "px" white.style.left = (x + 50) + "px" } else { hide(black) hide(white) } } function on_update() { let player_count = view.legacy.length ui.body.classList.toggle("p2", player_count === 2) ui.body.classList.toggle("p3", player_count === 3) ui.body.classList.toggle("p4", player_count === 4) ui.header.classList.toggle("player_red", view.current === 0) ui.header.classList.toggle("player_blue", view.current === 1) ui.header.classList.toggle("player_yellow", view.current === 2) ui.header.classList.toggle("player_green", view.current === 3) if (player_count < 4) hide(document.getElementById("role_Green")) else show(document.getElementById("role_Green")) if (player_count < 3) hide(document.getElementById("role_Yellow")) else show(document.getElementById("role_Yellow")) ui.hand.replaceChildren() ui.draw.replaceChildren() ui.discard.replaceChildren() ui.market.replaceChildren() for (let pi = 0; pi < player_count; ++pi) { let legacy = view.legacy[pi] let turns = view.emperor_turns[pi] if (legacy > 40) { legacy -= 40 ui.legacy[pi].classList.toggle("legacy_40", true) } else { ui.legacy[pi].classList.toggle("legacy_40", false) } let y = 30 for (let k = 0; k < pi; ++k) { let k_legacy = view.legacy[k] if (k_legacy > 40) k_legacy -= 40 if (legacy === k_legacy) y += 20 } show(ui.legacy[pi]) ui.legacy[pi].style.left = Math.round(43 + legacy * 60.2) + "px" ui.legacy[pi].style.top = 2 + y + "px" y = 30 for (let k = 0; k < pi; ++k) { let k_turns = view.emperor_turns[k] if (turns === k_turns) y += 20 } show(ui.emperor_turns[pi]) ui.emperor_turns[pi].style.left = Math.round(41 + turns * 60.2) + "px" ui.emperor_turns[pi].style.top = 0 + y + "px" } for (let pi = player_count; pi < 4; ++pi) { hide(ui.legacy[pi]) hide(ui.emperor_turns[pi]) } for (let region = 0; region < 12; ++region) { for (let p = 0; p < 4; ++p) { let gov = get_province_governor(region) if (gov > 0 && (gov/6|0) === p) { if (is_seat_of_power(region)) show(ui.seat_of_power[region + p * 12]) else hide(ui.seat_of_power[region + p * 12]) if (is_breakaway(region)) show(ui.breakaway[region + p * 12]) else hide(ui.breakaway[region + p * 12]) } else { hide(ui.seat_of_power[region + p * 12]) hide(ui.breakaway[region + p * 12]) } } if (has_quaestor(region)) show(ui.quaestor[region]) else hide(ui.quaestor[region]) if (has_amphitheater(region)) show(ui.amphitheater[region]) else hide(ui.amphitheater[region]) if (has_basilica(region)) show(ui.basilica[region]) else hide(ui.basilica[region]) if (has_limes(region)) show(ui.limes[region]) else hide(ui.limes[region]) if (has_militia(region)) show(ui.militia[region]) else hide(ui.militia[region]) } for (let i = 0; i < LEGION_COUNT; ++i) { if (is_legion_unused(i)) hide(ui.legions[i]) else show(ui.legions[i]) if (is_legion_reduced(i)) ui.legions[i].classList.toggle("reduced", true) else ui.legions[i].classList.toggle("reduced", false) } for (let id = 0; id < BARBARIAN_COUNT; ++id) { let loc = get_barbarian_location(id) if (loc === UNAVAILABLE) hide(ui.barbarians[id]) else show(ui.barbarians[id]) if (is_barbarian_inactive(id)) ui.barbarians[id].classList.toggle("inactive", true) else ui.barbarians[id].classList.toggle("inactive", false) } stack_count.fill(0) for (let region = 0; region < 12 + 5; ++region) { for (let tribe = 0; tribe < 5; ++tribe) { let active_barbarians = [] let inactive_barbarians = [] for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { if (id === CNIVA || id === ARDASHIR || id === SHAPUR) continue let loc = get_barbarian_location(id) let inactive = is_barbarian_inactive(id) if (loc === region) { if (inactive) inactive_barbarians.push(ui.barbarians[id]) else active_barbarians.push(ui.barbarians[id]) } } if (inactive_barbarians.length > 0) if (region >= 12) layout_stack(-1, inactive_barbarians, region, false, 4, 4) else layout_stack(-1, inactive_barbarians, region, false, 8, 8) if (active_barbarians.length > 0) layout_stack(-1, active_barbarians, region, false, 8, 8) } } for (let id of [ CNIVA, ARDASHIR, SHAPUR ]) { let loc = get_barbarian_location(id) if (loc !== UNAVAILABLE) 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) hide(ui.rival_emperors[id]) else { show(ui.rival_emperors[id]) layout_stack(-1, [ ui.rival_emperors[id] ], loc, false, 8, 8) } } for (let region = 0; region < 21; ++region) { ui.regions[region].classList.toggle("selected", view.selected_region === region) } for (let region = 0; region < 12; ++region) { if (has_militia_castra(region)) show(ui.mcastra[region]) else hide(ui.mcastra[region]) if (has_militia(region)) { let lone_militia = true for (let pi = 0; pi < player_count; ++pi) { for (let i = 0; i < 6; ++i) { let loc = get_general_location(first_general[pi] + i) let inside = is_general_inside_capital(first_general[pi] + i) if (loc === region && inside) lone_militia = false } } if (lone_militia) { if (has_militia_castra(region)) layout_stack(-1, [ ui.mcastra[region], ui.militia[region] ], region, true, 16, 16) else layout_stack(-1, [ ui.militia[region] ], region, true, 16, 16) } } if (is_no_place_governor(region)) { hide(ui.neutral_governors[region]) } else { if (is_neutral_province(region)) { show(ui.neutral_governors[region]) layout_governor(ui.neutral_governors[region], "neutral", region) } else { hide(ui.neutral_governors[region]) } } let n = view.mobs[region] layout_mob(region, 0, ui.mobs[region * 3 + 0], n >= 1, n >= 2) layout_mob(region, 1, ui.mobs[region * 3 + 1], n >= 3, n >= 4) layout_mob(region, 2, ui.mobs[region * 3 + 2], n >= 5, n >= 6) } for (let pi = 0; pi < player_count; ++pi) { let avail_stack = [] for (let ai = 0; ai < 6; ++ai) { let army = ARMY + first_general[pi] + ai let region = get_general_location(first_general[pi] + ai) let inside = is_general_inside_capital(first_general[pi] + ai) let castra = has_general_castra(first_general[pi] + ai) let e = ui.generals[pi][ai] show(e) if (region < 21) { let stack = [] if (castra) { show(ui.castra[pi][ai]) stack.push(ui.castra[pi][ai]) } else { hide(ui.castra[pi][ai]) } if (has_militia_castra(region) && inside) stack.push(ui.mcastra[region]) stack.push(e) for (let i = 0; i < LEGION_COUNT; ++i) { let loc = get_legion_location(i) if (loc === army) stack.push(ui.legions[i]) } for (let tribe = 0; tribe < 5; ++tribe) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { let loc = get_barbarian_location(id) if (loc === army) stack.push(ui.barbarians[id]) } } if (has_militia(region) && inside) stack.push(ui.militia[region]) if (inside) layout_stack(pi * 6 + ai, stack, region, true, 16, 16) else layout_stack(pi * 6 + ai, stack, region, false, 16, 16) } else { avail_stack.push(e) } e.classList.toggle("unavailable", region === UNAVAILABLE) e.classList.toggle("selected", view.selected_general === pi * 6 + ai) } if (avail_stack.length >= 6) layout_available(avail_stack, 48, pi * 625 + 0, 30) else if (avail_stack.length >= 5) layout_available(avail_stack, 63, pi * 625 + 0, 30) else layout_available(avail_stack, 69, pi * 625 + 0, 30) } for (let pi = 0; pi < player_count; ++pi) { let avail_stack = [] for (let ai = 0; ai < 6; ++ai) { let region = get_governor_location(first_governor[pi] + ai) let e = ui.governors[pi][ai] if (region < 12) { layout_governor(e, PLAYER_CLASS[pi], region) } else { if (region === AVAILABLE) layout_governor_available(e, PLAYER_CLASS[pi]) else layout_governor_unavailable(e, PLAYER_CLASS[pi], ai) avail_stack.push(e) } e.classList.toggle("unavailable", region === UNAVAILABLE) e.classList.toggle("selected", view.selected_governor === pi * 6 + ai) } if (avail_stack.length >= 6) layout_available(avail_stack, 43, pi * 625 + 325, 27) else if (avail_stack.length >= 5) layout_available(avail_stack, 58, pi * 625 + 325, 27) else layout_available(avail_stack, 64, pi * 625 + 325, 27) } ui.body.classList.toggle("military", view.color === 0) ui.body.classList.toggle("senate", view.color === 1) ui.body.classList.toggle("populace", view.color === 2) ui.dice[0].className = "dice black d" + view.dice[0] ui.dice[1].className = "dice white d" + view.dice[1] ui.dice[2].className = "dice black d" + view.dice[2] ui.dice[3].className = "dice white d" + view.dice[3] layout_barbarian_dice(ui.dice[2], ui.dice[3], view.crisis) ui.active_event.replaceChildren() ui.active_event.appendChild(ui.event_cards[view.event]) ui.played.replaceChildren() if (view.played) { for (let c of view.played) { ui.played.appendChild(ui.cards[c]) ui.cards[c].classList.toggle("used", set_has(view.used, c)) } } ui.hand.replaceChildren() if (view.hand) { for (let c of view.hand) { ui.hand.appendChild(ui.cards[c]) ui.cards[c].classList.remove("used") } } ui.draw.replaceChildren() if (view.draw) { for (let c of view.draw) { ui.draw.appendChild(ui.cards[c]) ui.cards[c].classList.remove("used") } } ui.discard.replaceChildren() if (view.discard) { for (let c of view.discard) { ui.discard.appendChild(ui.cards[c]) ui.cards[c].classList.remove("used") } } ui.market.replaceChildren() for (let c of view.market) { if (c > 0) { ui.market.appendChild(ui.cards[c]) ui.cards[c].classList.remove("used") } } for (let e of action_register) e.classList.toggle("action", is_action(e.my_action, e.my_id)) action_button("enter", "Enter Capital") action_button("leave", "Leave Capital") action_button("spend_military", "Spend Military") action_button("spend_senate", "Spend Senate") action_button("reroll", "Reroll") action_button("roll", "Roll") action_button("disperse_mob", "Disperse Mob") action_button("train_legions", "Train Legions") action_button("add_legion_to_army", "Add Legion to Army") action_button("hold_games", "Hold Games") action_button("place_militia", "Place Militia") action_button("amphitheater", "Amphitheater") action_button("basilica", "Basilica") action_button("limes", "Limes") action_button("recruit_general", "Recruit General") action_button("recruit_governor", "Recruit Governor") action_button("end_actions", "End Actions") action_button("save", "Save") action_button("pass", "Pass") action_button("done", "Done") action_button("undo", "Undo") } on_init() scroll_with_middle_mouse("main")