summaryrefslogtreecommitdiff
path: root/play.js
diff options
context:
space:
mode:
Diffstat (limited to 'play.js')
-rw-r--r--play.js1247
1 files changed, 1247 insertions, 0 deletions
diff --git a/play.js b/play.js
new file mode 100644
index 0000000..d35e3d3
--- /dev/null
+++ b/play.js
@@ -0,0 +1,1247 @@
+"use strict"
+
+/*
+ global view, data, player, send_action, action_button, scroll_with_middle_mouse
+*/
+
+/* COMMON */
+
+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
+}
+
+function map_get(map, key, missing) {
+ let a = 0
+ let b = (map.length >> 1) - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = map[m << 1]
+ if (key < x)
+ b = m - 1
+ else if (key > x)
+ a = m + 1
+ else
+ return map[(m << 1) + 1]
+ }
+ return missing
+}
+
+/* DATA */
+
+// Factions
+const DS = 0
+const BK = 1
+const VE = 2
+
+const NAME_DS = "DS"
+const NAME_BK = "BK"
+const NAME_VE = "VE"
+
+// Sequence of Play options
+const ELIGIBLE = 0
+const SOP_LIMITED_COMMAND = 1
+const SOP_COMMAND_DECREE = 2
+const SOP_EVENT_OR_COMMAND = 3
+const SOP_PASS = 4
+const INELIGIBLE = 5
+
+// Spaces
+const S_ANDHRA = 0
+const S_BENGAL = 1
+const S_GONDWANA = 2
+const S_GUJARAT = 3
+const S_JAUNPUR = 4
+const S_KARNATAKA = 5
+const S_MADHYADESH = 6
+const S_MAHARASHTRA = 7
+const S_MALWA = 8
+const S_ORISSA = 9
+const S_RAJPUT_KINGDOMS = 10
+const S_SINDH = 11
+const S_TAMILAKAM = 12
+const S_DELHI = 13
+const S_MOUNTAIN_PASSES = 14
+const S_PUNJAB = 15
+const S_MONGOL_INVADERS = 16
+const S_DS_AVAILABLE = 17
+const S_BK_AVAILABLE = 18
+const S_VE_AVAILABLE = 19
+const S_BK_INF_2 = 20
+const S_BK_INF_4 = 21
+const S_VE_INF_1 = 22
+const S_VE_INF_2 = 23
+const S_VE_INF_3 = 24
+const S_VE_INF_4 = 25
+
+const space_name = [
+ "Andhra",
+ "Bengal",
+ "Gondwana",
+ "Gujarat",
+ "Jaunpur",
+ "Karnataka",
+ "Madhyadesh",
+ "Maharashtra",
+ "Malwa",
+ "Orissa",
+ "Rajput Kingdoms",
+ "Sindh",
+ "Tamilakam",
+ "Delhi",
+ "Mountain Passes",
+ "Punjab",
+ "Mongol Invaders",
+ "DS Available",
+ "BK Available",
+ "VE Available",
+]
+
+/* LAYOUT DATA */
+// :r !node tools/parse-layout.js
+const layout = {
+ "circles": {
+ "provinces": {
+ "Sindh": {
+ "cx": 65,
+ "cy": 393,
+ "rx": 34,
+ "ry": 34
+ },
+ "Rajput Kingdoms": {
+ "cx": 329,
+ "cy": 403,
+ "rx": 34,
+ "ry": 34
+ },
+ "Malwa": {
+ "cx": 605,
+ "cy": 572,
+ "rx": 34,
+ "ry": 34
+ },
+ "Jaunpur": {
+ "cx": 894,
+ "cy": 455,
+ "rx": 34,
+ "ry": 34
+ },
+ "Bengal": {
+ "cx": 1192,
+ "cy": 536,
+ "rx": 34,
+ "ry": 34
+ },
+ "Orissa": {
+ "cx": 1034,
+ "cy": 858,
+ "rx": 34,
+ "ry": 34
+ },
+ "Gondwana": {
+ "cx": 913,
+ "cy": 737,
+ "rx": 34,
+ "ry": 34
+ },
+ "Madhyadesh": {
+ "cx": 670,
+ "cy": 817,
+ "rx": 34,
+ "ry": 34
+ },
+ "Andhra": {
+ "cx": 743,
+ "cy": 1090,
+ "rx": 34,
+ "ry": 34
+ },
+ "Maharashtra": {
+ "cx": 438,
+ "cy": 969,
+ "rx": 34,
+ "ry": 34
+ },
+ "Gujarat": {
+ "cx": 220,
+ "cy": 678,
+ "rx": 34,
+ "ry": 34
+ },
+ "Karnataka": {
+ "cx": 550,
+ "cy": 1278,
+ "rx": 34,
+ "ry": 34
+ },
+ "Tamilakam": {
+ "cx": 704,
+ "cy": 1399,
+ "rx": 34,
+ "ry": 34
+ }
+ },
+ "mongol_invasion_regions": {
+ "Mountain Passes": {
+ "cx": 302,
+ "cy": 140,
+ "rx": 83,
+ "ry": 28
+ },
+ "Punjab": {
+ "cx": 478,
+ "cy": 220,
+ "rx": 58,
+ "ry": 19
+ },
+ "Delhi": {
+ "cx": 647,
+ "cy": 375,
+ "rx": 148,
+ "ry": 148
+ }
+ }
+ },
+ "rects": {
+ "available_boxes": {
+ "Mongol Invaders": {
+ "x": 24,
+ "y": 100,
+ "w": 177,
+ "h": 110
+ },
+ "DS Available": {
+ "x": 796,
+ "y": 91,
+ "w": 388,
+ "h": 223
+ },
+ "BK Available": {
+ "x": 21,
+ "y": 908,
+ "w": 238,
+ "h": 224
+ },
+ "VE Available": {
+ "x": 21,
+ "y": 1405,
+ "w": 239,
+ "h": 225
+ }
+ },
+ "tracks": {
+ "Track 24": {
+ "x": 1198,
+ "y": 403,
+ "w": 61,
+ "h": 61
+ },
+ "Track 18": {
+ "x": 1197,
+ "y": 14,
+ "w": 62,
+ "h": 63
+ },
+ "Track 0": {
+ "x": 15,
+ "y": 14,
+ "w": 62,
+ "h": 63
+ },
+ "BK Influence 0": {
+ "x": 18,
+ "y": 1186,
+ "w": 61,
+ "h": 63
+ },
+ "BK Influence 1": {
+ "x": 89,
+ "y": 1186,
+ "w": 61,
+ "h": 63
+ },
+ "BK Influence 2": {
+ "x": 160,
+ "y": 1186,
+ "w": 61,
+ "h": 63
+ },
+ "BK Influence 3": {
+ "x": 231,
+ "y": 1186,
+ "w": 61,
+ "h": 63
+ },
+ "BK Influence 4": {
+ "x": 302,
+ "y": 1186,
+ "w": 61,
+ "h": 63
+ },
+ "VE Influence 0": {
+ "x": 18,
+ "y": 1292,
+ "w": 61,
+ "h": 63
+ },
+ "VE Influence 1": {
+ "x": 89,
+ "y": 1292,
+ "w": 61,
+ "h": 63
+ },
+ "VE Influence 2": {
+ "x": 160,
+ "y": 1292,
+ "w": 61,
+ "h": 63
+ },
+ "VE Influence 3": {
+ "x": 231,
+ "y": 1291,
+ "w": 61,
+ "h": 63
+ },
+ "VE Influence 4": {
+ "x": 302,
+ "y": 1292,
+ "w": 61,
+ "h": 63
+ }
+ },
+ "sequence_of_play": {
+ "Limited Command": {
+ "x": 854,
+ "y": 1305,
+ "w": 90,
+ "h": 54
+ },
+ "Eligible Factions": {
+ "x": 854,
+ "y": 1367,
+ "w": 91,
+ "h": 201
+ },
+ "Pass": {
+ "x": 854,
+ "y": 1578,
+ "w": 90,
+ "h": 56
+ },
+ "Ineligible Factions": {
+ "x": 1166,
+ "y": 1367,
+ "w": 90,
+ "h": 201
+ },
+ "Command and Decree": {
+ "x": 1016,
+ "y": 1371,
+ "w": 77,
+ "h": 77
+ },
+ "Event or Command": {
+ "x": 1016,
+ "y": 1488,
+ "w": 77,
+ "h": 77
+ }
+ }
+ }
+}
+
+/* STATE */
+
+function piece_space(p) {
+ return view.pieces[p]
+}
+
+/* BUILD */
+
+let ui = {
+ map: document.getElementById("map"),
+ favicon: document.getElementById("favicon"),
+ header: document.querySelector("header"),
+ status: document.getElementById("status"),
+ spaces: [],
+ control: [],
+ card_tip: document.getElementById("card_tip"),
+ this_card: document.getElementById("this_card"),
+ shaded_event: document.getElementById("shaded_event"),
+ unshaded_event: document.getElementById("unshaded_event"),
+ deck_outer: document.getElementById("deck_outer"),
+ deck_size: document.getElementById("deck_size"),
+ of_gods_and_kings: document.getElementById("of_gods_and_kings"),
+ dynasty_card: document.getElementById("dynasty_card"),
+ tokens: {
+ token_ds_vp: document.getElementById("token_ds_vp"),
+ token_bk_vp: document.getElementById("token_bk_vp"),
+ token_ve_vp: document.getElementById("token_ve_vp"),
+ token_bk_influence: document.getElementById("token_bk_influence"),
+ token_ve_influence: document.getElementById("token_ve_influence"),
+ token_mongol_cavalry: document.getElementById("token_mongol_cavalry"),
+ cavalry_1: document.getElementById("cavalry_1"),
+ cavalry_2: document.getElementById("cavalry_2"),
+ cavalry_3: document.getElementById("cavalry_3"),
+ cavalry_4: document.getElementById("cavalry_4"),
+ cavalry_5: document.getElementById("cavalry_5"),
+ cavalry_6: document.getElementById("cavalry_6"),
+ cavalry_7: document.getElementById("cavalry_7"),
+ cavalry_8: document.getElementById("cavalry_8"),
+ },
+ pieces: [],
+ resources: [
+ document.getElementById("ds_resources"),
+ document.getElementById("bk_resources"),
+ document.getElementById("ve_resources"),
+ ],
+ cylinder: [
+ document.getElementById("ds_cylinder"),
+ document.getElementById("bk_cylinder"),
+ document.getElementById("ve_cylinder"),
+ ],
+}
+
+function create(t, p, ...c) {
+ let e = document.createElement(t)
+ Object.assign(e, p)
+ e.append(c)
+ return e
+}
+
+function register_action(e, action, id) {
+ e.my_action = action
+ e.my_id = id
+ e.onmousedown = on_click_action
+}
+
+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 toggle_pieces() {
+ if (ui.map.classList.contains("hide_tokens")) {
+ ui.map.classList.remove("hide_tokens")
+ ui.map.classList.remove("hide_pieces")
+ } else if (ui.map.classList.contains("hide_pieces")) {
+ ui.map.classList.add("hide_tokens")
+ } else {
+ ui.map.classList.add("hide_pieces")
+ }
+}
+
+function on_click_action(evt) {
+ if (evt.button === 0)
+ send_action(evt.target.my_action, evt.target.my_id)
+}
+
+function init_ui() {
+ register_action(ui.this_card, "event", undefined)
+ register_action(ui.unshaded_event, "unshaded", undefined)
+ register_action(ui.shaded_event, "shaded", undefined)
+ register_action(ui.resources[DS], "resources", DS)
+ register_action(ui.resources[BK], "resources", BK)
+ register_action(ui.resources[VE], "resources", VE)
+
+ ui.this_card.onmouseenter = on_focus_this_event
+ ui.this_card.onmouseleave = on_blur_event
+ ui.shaded_event.onmouseenter = on_focus_shaded_event
+ ui.shaded_event.onmouseleave = on_focus_this_event
+ ui.unshaded_event.onmouseenter = on_focus_unshaded_event
+ ui.unshaded_event.onmouseleave = on_focus_this_event
+
+ for (let s = 0; s < 20; ++s) {
+ let e = null
+
+ // provinces
+ if (s < S_DELHI) {
+ let { cx, cy } = layout.circles.provinces[space_name[s]]
+ ui.control[s] = e = create("div", { className: "token tributary" })
+ e.style.left = (cx - 24) + "px"
+ e.style.top = (cy - 24) + "px"
+ document.getElementById("tokens").appendChild(e)
+
+ ui.spaces[s] = e = create("div", { className: "space province" })
+ e.style.left = (cx - 45) + "px"
+ e.style.top = (cy - 45) + "px"
+ e.style.width = (90 - 4) + "px"
+ e.style.height = (90 - 4) + "px"
+ register_action(e, "space", s)
+ document.getElementById("spaces").appendChild(e)
+ }
+
+ // delhi & passes
+ if (s >= S_DELHI && s <= S_PUNJAB) {
+ let { cx, cy, rx, ry } = layout.circles.mongol_invasion_regions[space_name[s]]
+ ui.spaces[s] = e = create("div", { className: "space province" })
+ e.style.left = (cx - rx) + "px"
+ e.style.top = (cy - ry) + "px"
+ e.style.width = (rx * 2 - 4) + "px"
+ e.style.height = (ry * 2 - 4) + "px"
+ register_action(e, "space", s)
+ document.getElementById("spaces").appendChild(e)
+ }
+
+ // boxes
+ if (s >= S_MONGOL_INVADERS && s <= S_VE_AVAILABLE) {
+ let { x, y, w, h } = layout.rects.available_boxes[space_name[s]]
+ ui.spaces[s] = e = create("div", { className: "space" })
+ e.style.left = x + "px"
+ e.style.top = y + "px"
+ e.style.width = (w - 4) + "px"
+ e.style.height = (h - 4) + "px"
+ register_action(e, "space", s)
+ document.getElementById("spaces").appendChild(e)
+ }
+ }
+
+ function create_piece(c, action, id, x, y) {
+ let e = create("div", {
+ className: c,
+ my_action: action,
+ my_id: id,
+ my_x_offset: x,
+ my_y_offset: y,
+ onmousedown: on_click_action
+ })
+ document.getElementById("pieces").appendChild(e)
+ return e
+ }
+
+ function create_piece_list(faction, type, c, x, y) {
+ for (let p = first_piece[faction][type]; p <= last_piece[faction][type]; ++p)
+ ui.pieces[p] = create_piece(c, "piece", p, x, y)
+ }
+
+ ui.pieces = []
+
+return
+
+ /*
+ <div class="piece ds governor" style="left:200px;top:380px"></div>
+ <div class="piece bk amir rebel" style="left:230px;top:380px"></div>
+ <div class="piece ve raja rebel" style="left:260px;top:380px"></div>
+ <div class="piece ds cube" style="left:210px;top:430px"></div>
+ <div class="piece mongol cube" style="left:245px;top:430px"></div>
+ <div class="piece ds disk" style="left:200px;top:480px"></div>
+ <div class="piece bk disk" style="left:250px;top:480px"></div>
+ <div class="piece ve disk" style="left:300px;top:480px"></div>
+ */
+
+
+ create_piece_list(DS, DISK, "piece ds disk", -4, 10)
+ create_piece_list(DS, GOVERNOR, "piece ds governor", 0, 4)
+ create_piece_list(DS, CUBE, "piece ds cube", 0, 4)
+
+ create_piece_list(BK, DISK, "piece ve disk", -4, 10)
+ create_piece_list(BK, AMIR, "piece ve amir", 2, 0)
+
+ create_piece_list(VE, DISK, "piece ve disk", -4, 10)
+ create_piece_list(VE, RAJA, "piece ve raja", 2, 0)
+
+ create_piece_list(MONGOLS, CUBE, "piece mongol cube", 2, 0)
+}
+
+/* UPDATE */
+
+function action_menu_item(action) {
+ let menu = document.getElementById(action + "_menu")
+ if (view.actions && action in view.actions) {
+ menu.classList.toggle("hide", false)
+ menu.classList.toggle("disabled", view.actions[action] === 0)
+ return 1
+ } else {
+ menu.classList.toggle("hide", true)
+ return 0
+ }
+}
+
+function action_menu(menu, action_list) {
+ let x = 0
+ for (let action of action_list)
+ x |= action_menu_item(action)
+ menu.classList.toggle("hide", !x)
+}
+
+const LAYOUT_CACHE = {
+ Center: [],
+ Govt: [],
+ FARC: [],
+ AUC: [],
+ Cartels: [],
+ COIN: [],
+ INSURGENTS: [],
+}
+
+function get_layout_xy(s, f = "Center") {
+ if (!LAYOUT_CACHE[f][s]) {
+ let id = (f !== "Center") ? data.spaces[s].id + " " + f : data.spaces[s].id
+ LAYOUT_CACHE[f][s] = LAYOUT[id]
+ }
+ return LAYOUT_CACHE[f][s]
+}
+
+function filter_piece_list(list, space, faction, type) {
+ for (let i = first_piece[faction][type]; i <= last_piece[faction][type]; ++i)
+ if (view.pieces[i] === space)
+ list.push(ui.pieces[i])
+}
+
+function layout_available(faction, type, xorig, yorig) {
+ let list = []
+ filter_piece_list(list, AVAILABLE, faction, type)
+ layout_pieces(list, xorig, yorig + 35, null, AVAILABLE)
+}
+
+function layout_pieces(list, xorig, yorig, bases, shipments, s, faction) {
+ const dx = 17
+ const dy = 11
+ let off_x = 0
+ let off_y = 0
+ let rotate = 0
+
+ if (s >= 0)
+ rotate = (data.spaces[s].type === "mountain") ? 1 : 0
+
+ function layout_piece_rowcol(nrow, ncol, row, col, e, z) {
+ // basic piece size = 29x36
+ let x = xorig - (row * dx - col * dx) - 15 + off_x
+ let y = yorig - (row * dy + col * dy) - 24 + off_y
+ let xo = e.my_x_offset
+ let yo = e.my_y_offset
+ e.style.left = (xo + x) + "px"
+ e.style.top = (yo + y) + "px"
+ e.style.zIndex = y
+ e.my_x = x + 15
+ e.my_y = y + 24
+ e.my_z = z
+ }
+
+ if (list.length > 0) {
+ let nrow = Math.round(Math.sqrt(list.length))
+ let ncol = Math.ceil(list.length / nrow)
+ let z = 50
+ let i = 0
+ if ((s >= 0 && s <= last_city) || s >= first_loc) {
+ off_x = (nrow - ncol) * 6
+ off_y = (nrow - 1) * 8
+ }
+ for (let row = 0; row < nrow; ++row)
+ for (let col = 0; col < ncol; ++col)
+ if (i < list.length)
+ layout_piece_rowcol(nrow, ncol, row, col, list[list.length-(++i)], z--)
+ }
+
+ if (bases)
+ layout_dept_bases(bases, xorig + off_x, yorig + 12 + off_y, s)
+}
+
+function place_piece(p, x, y, z) {
+ p.style.left = x + "px"
+ p.style.top = y + "px"
+ if (z)
+ p.style.zIndex = z
+ p.my_x = x
+ p.my_y = y
+ p.my_z = z
+}
+
+function layout_dept_bases(list, xc, yc, s) {
+ if (list.length === 1) {
+ place_piece(list[0], xc - 20 + 32, yc - 10, 52)
+ }
+ if (list.length === 2) {
+ place_piece(list[0], xc - 20 + 18, yc - 0, 52)
+ place_piece(list[1], xc - 20 + 18 + 32, yc - 21, 51)
+ }
+ if (list.length === 3) {
+ // TODO
+ place_piece(list[0], xc - 20 + 18, yc - 0, 52)
+ place_piece(list[1], xc - 20 + 18 + 32, yc - 21, 51)
+ place_piece(list[2], xc - 20 + 18 + 32, yc - 21, 100)
+ }
+}
+
+function layout_available_bases(list, x0, y0, cols, rows, dx, dy) {
+ let x = x0
+ let y = y0
+ for (let i = 0; i < list.length; ++i) {
+ place_piece(list[list.length-i-1], x - 44 - 6, y + 8)
+ y += dy
+ if (i % rows === rows - 1) {
+ x -= dx
+ y = y0
+ }
+ }
+}
+
+function layout_sop() {
+ let i, x, y, z
+
+ // Eligible
+ x = 1164 - 22
+ y = 480
+ z = 1
+ let order = data.card_order[view.deck[0]]
+ for (let faction of order) {
+ if (view.cylinder[faction] === ELIGIBLE) {
+ place_piece(ui.cylinder[faction], x, y, z)
+ y += 40
+ z += 1
+ }
+ }
+
+ // Ineligible
+ x = 1510 - 22
+ y = 480
+ z = 1
+ for (let faction = 0; faction < 4; ++faction) {
+ if (view.cylinder[faction] === INELIGIBLE) {
+ place_piece(ui.cylinder[faction], x, y, z)
+ y += 40
+ z += 1
+ }
+ }
+
+ // Pass
+ x = 1164 - 22 - 24
+ y = 688 - 28
+ z = 1
+ i = 0
+ for (let faction = 0; faction < 4; ++faction) {
+ if (view.cylinder[faction] === SOP_PASS) {
+ place_piece(ui.cylinder[faction], x, y, z)
+ x += 48
+ z += 1
+ if (++i === 2) { x -= 72; y += 28 }
+ }
+ }
+
+ for (let [i, x, y] of sop_xy) {
+ for (let faction = 0; faction < 4; ++faction)
+ if (view.cylinder[faction] === i)
+ place_piece(ui.cylinder[faction], x, y)
+ }
+}
+
+function layout_score_cell(list, x, y, dx, dy) {
+ let z = 1
+ if (list.length > 1) {
+ if (dy > 0) y -= 12
+ if (dy < 0) y += 12
+ if (dx > 0) x -= 12
+ if (dx < 0) x += 12
+ }
+ for (let p of list) {
+ if (p.classList.contains("token"))
+ place_piece(p, x - 24, y - 24, z)
+ else
+ place_piece(p, x - 22, y - 24, z)
+ x += dx
+ y += dy
+ z += 1
+ }
+}
+
+function layout_score() {
+ let list = []
+ let x, y
+ for (let i = 0; i <= 24; ++i) {
+ list.length = 0
+
+ if (view.vp[DS] === i) list.push(ui.tokens.token_ds_vp)
+ if (view.vp[BK] === i) list.push(ui.tokens.token_bk_vp)
+ if (view.vp[VE] === i) list.push(ui.tokens.token_ve_vp)
+
+ for (let faction = 0; faction < 3; ++faction)
+ if (view.resources[faction] === i)
+ list.push(ui.resources[faction])
+
+ // X: 15 -> 1198 (0-18)
+ // Y: 14 -> 403 (18-24)
+
+ if (i < 18) y = 20
+ else y = 20 + (i - 18) * 65
+
+ if (i < 18) x = 23 + (i * 65.6)
+ else x = 1204
+
+ x = Math.round(x) + 24
+ y = Math.round(y) + 24
+
+ if (i < 17) layout_score_cell(list, x, y, 0, 28)
+ else if (i === 17) layout_score_cell(list, x, y, -18, 25)
+ else layout_score_cell(list, x, y, -41, 0)
+ }
+}
+
+function update_rebels(faction, type, rebel) {
+ let p0 = first_piece[faction][type]
+ let p1 = last_piece[faction][type]
+ for (let i = 0, p = p0; p <= p1; ++i, ++p) {
+ if (underground & (1 << i))
+ ui.pieces[p].classList.add("rebel")
+ else
+ ui.pieces[p].classList.remove("rebel")
+ }
+}
+
+function make_card_class_name(c) {
+ return "card card_" + c
+ // TODO:
+ if (set_has([1,2,3,7,9,10,11,13], view.deck[0]))
+ return "card card_" + c + " u" + data.card_unshaded_lines[c] + " s" + data.card_shaded_lines[c] + " c"
+ else
+ return "card card_" + c + " u" + data.card_unshaded_lines[c] + " s" + data.card_shaded_lines[c]
+}
+
+function update_player_active(name, x) {
+ if (roles[name])
+ roles[name].element.classList.toggle("active", x)
+}
+
+let once = true
+function on_update() {
+ if (once) {
+ init_ui()
+ once = false
+ }
+
+ switch (player) {
+ case "DS": ui.favicon.href = "images/Flag_DS.png"; break
+ case "BK": ui.favicon.href = "images/Flag_BK.png"; break
+ case "VE": ui.favicon.href = "images/Flag_VE.png"; break
+ }
+
+ ui.header.classList.toggle("ds", view.current === DS)
+ ui.header.classList.toggle("bk", view.current === BK)
+ ui.header.classList.toggle("ve", view.current === VE)
+
+ ui.tokens.token_bk_influence.classList.toggle("action", is_action("bk_inf"))
+ ui.tokens.token_ve_influence.classList.toggle("action", is_action("ve_inf"))
+
+ ui.resources[DS].classList.toggle("action", is_action("resources", DS))
+ ui.resources[BK].classList.toggle("action", is_action("resources", BK))
+ ui.resources[VE].classList.toggle("action", is_action("resources", VE))
+
+ update_player_active(NAME_DS, view.current === DS)
+ update_player_active(NAME_VE, view.current === VE)
+ update_player_active(NAME_BK, view.current === BK)
+
+ ui.this_card.className = make_card_class_name(view.deck[0])
+ if (view.deck[1] > 0) {
+ ui.deck_outer.className = "card card_back"
+ ui.deck_size.textContent = `${view.deck[1]}`
+ } else {
+ ui.deck_outer.className = "hide"
+ }
+
+ ui.this_card.classList.toggle("action", !!(view.actions && view.actions.event === 1))
+ ui.shaded_event.classList.toggle("action", !!(view.actions && view.actions.shaded === 1))
+ ui.unshaded_event.classList.toggle("action", !!(view.actions && view.actions.unshaded === 1))
+
+ layout_score()
+
+return
+
+ layout_sop()
+
+ layout_available(GOVT, TROOPS, 114, 248)
+ layout_available(GOVT, POLICE, 114, 448)
+ layout_available(FARC, GUERRILLA, 1396, 234)
+ layout_available(AUC, GUERRILLA, 196, 2370)
+ layout_available(CARTELS, GUERRILLA, 1465, 1970)
+
+ for (let i = view.farc_zones.length; i < ui.farc_zones.length; ++i)
+ ui.farc_zones[i].className = "hide"
+
+ let tix = 0
+
+ let list = []
+ let bases = []
+ let drugs = []
+ for (let s = 0; s < data.spaces.length; ++s) {
+ let id = data.spaces[s].id
+ let xy
+
+ if (s <= last_pop) {
+ switch (view.support[s]) {
+ case -2: ui.support[s].className = "token active_opposition"; break
+ case -1: ui.support[s].className = "token passive_opposition"; break
+ case 0: ui.support[s].className = "hide"; break
+ case 1: ui.support[s].className = "token passive_support"; break
+ case 2: ui.support[s].className = "token active_support"; break
+ }
+ }
+
+ if (s >= first_loc && s <= last_loc) {
+ if (set_has(view.sabotage, s))
+ ui.sabotage[s].className = "token sabotage"
+ else
+ ui.sabotage[s].className = "hide"
+ }
+
+ if (s >= first_dept && s <= last_dept) {
+ ui.farc_zone[s].classList.toggle("hide", !(view.farc_zones & (1<<s)))
+ }
+
+ if (s <= last_dept) {
+ if (view.govt_control & (1<<s))
+ ui.control[s].className = "token govt_control"
+ else if (view.farc_control & (1<<s))
+ ui.control[s].className = "token farc_control"
+ else
+ ui.control[s].className = "hide"
+ }
+
+ tix = layout_terror(tix, s, map_get(view.terror, s, 0) * 1)
+
+ update_rebels(BK, AMIR, view.rebel[BK])
+ update_rebels(VE, RAJA, view.rebel[VE])
+
+ drugs.length = 0
+ for (let i = 0; i < 4; ++i) {
+ let shx = view.shipments[i]
+ if (shx !== 0) {
+ if ((shx & 3) === 0 && view.pieces[(shx >> 2)] === s)
+ layout_shipments_push(drugs, ui.pieces[shx>>2], ui.shipments[i], piece_faction(shx>>2))
+ else if ((shx & 3) !== 0 && (shx >> 2) === s)
+ layout_shipments_push(drugs, null, ui.shipments[i], shx & 3)
+ }
+ }
+
+ if (s <= last_city) {
+ list.length = bases.length = 0
+ filter_piece_list(list, s, FARC, GUERRILLA)
+ filter_piece_list(list, s, AUC, GUERRILLA)
+ filter_piece_list(list, s, CARTELS, GUERRILLA)
+ filter_piece_list(list, s, GOVT, TROOPS)
+ filter_piece_list(list, s, GOVT, POLICE)
+ filter_piece_list(bases, s, GOVT, BASE)
+ filter_piece_list(bases, s, FARC, BASE)
+ filter_piece_list(bases, s, AUC, BASE)
+ filter_piece_list(bases, s, CARTELS, BASE)
+ xy = get_layout_xy(s)
+ layout_pieces(list, xy[0], xy[1], null, null, s, 0)
+ layout_city_bases(bases, xy[0], xy[1] + get_layout_radius(s) - 12, s)
+
+ layout_city_shipments(s, drugs, xy[0], xy[1])
+ } else if (s <= last_dept) {
+ list.length = bases.length = 0
+ filter_piece_list(list, s, FARC, GUERRILLA)
+ filter_piece_list(bases, s, FARC, BASE)
+ xy = get_layout_xy(s, "FARC")
+ layout_pieces(list, xy[0], xy[1], bases, drugs, s, FARC)
+
+ list.length = bases.length = 0
+ filter_piece_list(list, s, AUC, GUERRILLA)
+ filter_piece_list(bases, s, AUC, BASE)
+ xy = get_layout_xy(s, "AUC")
+ layout_pieces(list, xy[0], xy[1], bases, drugs, s, AUC)
+
+ list.length = bases.length = 0
+ filter_piece_list(list, s, CARTELS, GUERRILLA)
+ filter_piece_list(bases, s, CARTELS, BASE)
+ xy = get_layout_xy(s, "Cartels")
+ layout_pieces(list, xy[0], xy[1], bases, drugs, s, CARTELS)
+
+ list.length = bases.length = 0
+ filter_piece_list(list, s, GOVT, TROOPS)
+ filter_piece_list(list, s, GOVT, POLICE)
+ filter_piece_list(bases, s, GOVT, BASE)
+ xy = get_layout_xy(s, "Govt")
+ layout_pieces(list, xy[0], xy[1], bases, null, s, GOVT)
+ } else {
+ list.length = 0
+ filter_piece_list(list, s, FARC, GUERRILLA)
+ filter_piece_list(list, s, AUC, GUERRILLA)
+ filter_piece_list(list, s, CARTELS, GUERRILLA)
+ xy = get_layout_xy(s, "INSURGENTS")
+ layout_pieces(list, xy[0], xy[1], null, s)
+
+ list.length = 0
+ filter_piece_list(list, s, GOVT, TROOPS)
+ filter_piece_list(list, s, GOVT, POLICE)
+ xy = get_layout_xy(s, "COIN")
+ layout_pieces(list, xy[0], xy[1], null, s)
+
+ xy = get_layout_xy(s)
+ layout_loc_shipments(s, drugs, xy[0], xy[1])
+ }
+
+ ui.spaces[s].classList.toggle("action", is_action("space", s))
+ ui.spaces[s].classList.toggle("selected", view.where === s)
+ }
+
+ for (; tix < 40; ++tix)
+ ui.terror[tix].className = "hide"
+
+ for (let i = first_piece[AUC][GUERRILLA]; i <= last_piece[AUC][GUERRILLA]; ++i)
+ ui.pieces[i].classList.toggle("hide", view.pieces[i] === OUT_OF_PLAY)
+
+ list.length = 0
+ for (let i = 0; i < 4; ++i) {
+ let shx = view.shipments[i]
+ let shf = shx & 3
+ if (shx === 0)
+ list.push(ui.shipments[i])
+ if (shf === 0)
+ ui.shipments[i].className = "token shipment"
+ else if (shf === FARC)
+ ui.shipments[i].className = "token shipment farc"
+ else if (shf === AUC)
+ ui.shipments[i].className = "token shipment auc"
+ else if (shf === CARTELS)
+ ui.shipments[i].className = "token shipment cartels"
+ if (view.actions && view.actions.shipment && set_has(view.actions.shipment, i))
+ ui.shipments[i].classList.add("action")
+ if (view.selected_shipment === i)
+ ui.shipments[i].classList.add("selected")
+ }
+ layout_available_bases(list, 1532, 1722, 2, 2, 89, 69)
+
+ list.length = 0
+ filter_piece_list(list, AVAILABLE, GOVT, BASE)
+ layout_available_bases(list, 287 + 177, 371, 3, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, AVAILABLE, FARC, BASE)
+ layout_available_bases(list, 446 + 543, 2295, 9, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, AVAILABLE, AUC, BASE)
+ layout_available_bases(list, 446 + 360, 2386, 6, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, AVAILABLE, CARTELS, BASE)
+ layout_available_bases(list, 1373 + 183, 2117, 3, 5, 63, 63)
+
+ if (view.actions && view.actions.piece)
+ for (let i = 0; i < ui.pieces.length; ++i)
+ ui.pieces[i].classList.toggle("action", set_has(view.actions.piece, i))
+ else
+ for (let i = 0; i < ui.pieces.length; ++i)
+ ui.pieces[i].classList.remove("action")
+ for (let i = 0; i < ui.pieces.length; ++i)
+ ui.pieces[i].classList.toggle("selected", view.who === i)
+
+ action_menu(document.getElementById("negotiate_menu"), [
+ "remove_pieces",
+ "transfer_resources",
+ "transfer_shipment",
+ "ask_resources",
+ "ask_shipment",
+ ])
+
+ // Select Faction
+ action_button("govt", "Government")
+ action_button("farc", "FARC")
+ action_button("auc", "AUC")
+ action_button("cartels", "Cartels")
+
+ confirm_action_button("choose_govt", "Government", "Choose GOVERNMENT to execute this event?")
+ confirm_action_button("choose_farc", "FARC", "Choose FARC to execute this event?")
+ confirm_action_button("choose_auc", "AUC", "Choose AUC to execute this event?")
+ confirm_action_button("choose_cartels", "Cartels", "Choose CARTELS to execute this event?")
+
+ // Select Operation
+ action_button("train", "Train")
+ action_button("patrol", "Patrol")
+ action_button("sweep", "Sweep")
+ action_button("assault", "Assault")
+ action_button("rally", "Rally")
+ action_button("march", "March")
+ action_button("attack", "Attack")
+ action_button("terror", "Terror")
+
+ // Select Special Activity
+ action_button("air_lift", "Air Lift")
+ action_button("air_strike", "Air Strike")
+ action_button("eradicate", "Eradicate")
+ action_button("extort", "Extort")
+ action_button("ambush", "Ambush")
+ action_button("assassinate", "Assassinate")
+ action_button("kidnap", "Kidnap")
+ action_button("cultivate", "Cultivate")
+ action_button("process", "Process")
+ action_button("bribe", "Bribe")
+
+ // Train/Rally sub-actions
+ action_button("move", "Move")
+ action_button("flip", "Flip")
+ action_button("base", "Base")
+ action_button("civic", "Civic Action")
+
+ action_button("support", "Support")
+ action_button("opposition", "Opposition")
+
+ action_button("remove", "Remove")
+ action_button("roll", "Roll")
+ action_button("skip", "Skip")
+ action_button("next", "Next")
+ action_button("pass", "Pass")
+
+ action_button("end_train", "End Train")
+ action_button("end_patrol", "End Patrol")
+ action_button("end_sweep", "End Sweep")
+ action_button("end_assault", "End Assault")
+ action_button("end_rally", "End Rally")
+ action_button("end_march", "End March")
+ action_button("end_attack", "End Attack")
+ action_button("end_terror", "End Terror")
+
+ action_button("end_air_lift", "End Air Lift")
+ action_button("end_extort", "End Extort")
+ action_button("end_assassinate", "End Assassinate")
+ action_button("end_kidnap", "End Kidnap")
+ action_button("end_process", "End Process")
+ action_button("end_bribe", "End Bribe")
+
+ action_button("end_event", "End Event")
+
+ action_button("deny", "Deny")
+ action_button("done", "Done")
+ action_button("undo", "Undo")
+}
+
+/* TOOLTIPS */
+
+function register_card_tip(e, c) {
+ e.onmouseenter = () => on_focus_card_tip(c)
+ e.onmouseleave = on_blur_card_tip
+}
+
+function on_focus_this_event() {
+ let c = view.deck[0]
+ if (c > 0)
+ ui.status.textContent = data.card_title[c]
+}
+
+function on_focus_unshaded_event() {
+ let c = view.deck[0]
+ if (c > 0) {
+ let f = data.card_flavor[c]
+ if (f)
+ ui.status.textContent = data.card_title[c] + " - " + f
+ else
+ ui.status.textContent = data.card_title[c]
+ }
+}
+
+function on_focus_shaded_event() {
+ let c = view.deck[0]
+ if (c > 0) {
+ ui.status.textContent = data.card_title[c] + " - " + data.card_flavor_shaded[c]
+ }
+}
+
+function on_blur_event() {
+ ui.status.textContent = ""
+}
+
+function on_focus_card_tip(c) {
+ ui.card_tip.className = "card card_" + c
+}
+
+function on_blur_card_tip() {
+ ui.card_tip.className = "hide"
+}
+
+function on_focus_space_tip(s) {
+ ui.spaces[s].classList.add("tip")
+}
+
+function on_blur_space_tip(s) {
+ ui.spaces[s].classList.remove("tip")
+}
+
+function on_click_space_tip(s) {
+ scroll_into_view(ui.spaces[s])
+}
+
+/* LOG */
+
+function sub_card(match, p1) {
+ let x = p1 | 0
+ let n = data.card_title[x]
+ return `<span class="tip" onmouseenter="on_focus_card_tip(${x})" onmouseleave="on_blur_card_tip()">${n}</span>`
+}
+
+function sub_space(match, p1) {
+ let x = p1 | 0
+ let n = data.space_name[x]
+ return `<span class="tip" onmouseenter="on_focus_space_tip(${x})" onmouseleave="on_blur_space_tip(${x})" onmousedown="on_click_space_tip(${x})">${n}</span>`
+}
+
+function on_log(text) {
+ let p = document.createElement("div")
+
+ if (text.match(/^>/)) {
+ text = text.substring(1)
+ p.className = "indent"
+ }
+
+ text = text.replace(/&/g, "&amp;")
+ text = text.replace(/</g, "&lt;")
+ text = text.replace(/>/g, "&gt;")
+
+ if (text.match(/^\.h1/)) {
+ text = text.substring(4)
+ p.className = "h1"
+ }
+ else if (text.match(/^\.h2 Gov/)) {
+ text = text.substring(3)
+ p.className = "h2 govt"
+ }
+ else if (text.match(/^\.h2 AUC/)) {
+ text = text.substring(3)
+ p.className = "h2 auc"
+ }
+ else if (text.match(/^\.h2 Cartels/)) {
+ text = text.substring(3)
+ p.className = "h2 cartels"
+ }
+ else if (text.match(/^\.h2 FARC/)) {
+ text = text.substring(3)
+ p.className = "h2 farc"
+ }
+ else if (text.match(/^\.h2 /)) {
+ text = text.substring(3)
+ p.className = "h2"
+ }
+ else if (text.match(/^\.h3/)) {
+ text = text.substring(4)
+ p.className = "h3"
+ }
+ else if (text.match(/^\.h4/)) {
+ text = text.substring(4)
+ p.className = "h4"
+ }
+ else if (text.match(/^\.n/)) {
+ text = text.substring(3)
+ p.className = "italic"
+ }
+ else if (text.match(/^\.i/)) {
+ text = text.substring(3)
+ p.className = "indent italic"
+ }
+
+ text = text.replace(/C(\d+)/g, sub_card)
+ text = text.replace(/S(\d+)/g, sub_space)
+
+ p.innerHTML = text
+ return p
+}