summaryrefslogtreecommitdiff
path: root/play.js
diff options
context:
space:
mode:
Diffstat (limited to 'play.js')
-rw-r--r--play.js880
1 files changed, 834 insertions, 46 deletions
diff --git a/play.js b/play.js
index 76a8079..188af9b 100644
--- a/play.js
+++ b/play.js
@@ -1,77 +1,865 @@
+"use strict"
+
+// vim: set nowrap:
+/* globals data, view, action_button, action_button_with_argument, confirm_action_button, send_action, params
+*/
+
+function toggle_pieces() {
+ document.getElementById("pieces").classList.toggle("hide")
+}
+
+/* DATA */
+
+const P_FRANCE = 0
+const P_BAVARIA = 1
+const P_PRUSSIA = 2
+const P_SAXONY = 3
+const P_PRAGMATIC = 4
+const P_AUSTRIA = 5
+
const cities = data.cities
const last_city = cities.name.length - 1
+const ELIMINATED = data.cities.name.length
+const REMOVED = ELIMINATED + 1
+const ELIMINATED_TRAIN_X = 1065
+const ELIMINATED_TRAIN_Y = 210
+const ELIMINATED_DISC_X = 1365
+const ELIMINATED_DISC_Y = 210
+const ELIMINATED_GENERAL_X = 1040
+const ELIMINATED_GENERAL_Y = 160
+const ELIMINATED_GENERAL_DX = 50
+
+const power_class = [ "france", "bavaria", "prussia", "saxony", "pragmatic", "austria" ]
+const power_name = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic", "Austria" ]
+
+const all_powers = [ 0, 1, 2, 3, 4, 5 ]
+
+let suit_class = [ "spades", "clubs", "hearts", "diamonds", "reserve" ]
+let suit_letter = [ "S", "C", "H", "D", "R" ]
+
+function to_deck(c) {
+ return c >> 7
+}
+
+function to_suit(c) {
+ return (c >> 4) & 7
+}
+
+function to_value(c) {
+ return c & 15
+}
+
+/* PANEL ORDER */
+
+const panel_order = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA, P_AUSTRIA+1 ]
+const panel_start = {
+ "Observer": P_FRANCE,
+ "Louis XV": P_FRANCE,
+ "Frederick": P_PRUSSIA,
+ "Maria Theresa": P_AUSTRIA,
+}
+
+function remember_position(e) {
+ if (e.parentElement) {
+ let rect = e.getBoundingClientRect()
+ e.my_parent = e.parentElement
+ e.my_x = rect.x
+ e.my_y = rect.y
+ } else {
+ e.my_parent = null
+ e.my_x = 0
+ e.my_y = 0
+ }
+}
+
+function animate_position(e) {
+ if (e.parentElement) {
+ if (e.my_parent) {
+ let rect = e.getBoundingClientRect()
+ let dx = e.my_x - rect.x
+ let dy = e.my_y - rect.y
+ if (dx !== 0 || dy !== 0) {
+ e.animate(
+ [
+ { transform: `translate(${dx}px, ${dy}px)`, },
+ { transform: "translate(0, 0)", },
+ ],
+ { duration: 333, easing: "ease" }
+ )
+ }
+ }
+ }
+}
+
+function sort_power_panel(animate) {
+ let start = panel_start[params.role] | 0
+
+ if (animate)
+ for (let i = 0; i < 7; ++i)
+ remember_position(ui.power_panel[i])
+
+ ui.power_panel_list.replaceChildren()
+ for (let i = 0; i < 7; ++i) {
+ let p = panel_order[(i + start) % 7]
+ ui.power_panel_list.appendChild(ui.power_panel[p])
+ }
+
+ if (view && view.actions)
+ ui.power_panel_list.prepend(ui.power_panel[view.power])
+
+ if (animate)
+ for (let i = 0; i < 7; ++i)
+ animate_position(ui.power_panel[i])
+}
+
+/* BUILD UI */
+
const ui = {
- map_pieces: document.getElementById("pieces"),
+ prompt: document.getElementById("prompt"),
+ status: document.getElementById("status"),
+ header: document.querySelector("header"),
+ spaces_element: document.getElementById("spaces"),
+ pieces_element: document.getElementById("pieces"),
+ markers_element: document.getElementById("markers"),
+ political_display: document.getElementById("political_display"),
+ power_panel_list: document.getElementById("power_panel_list"),
+ power_header: [
+ document.getElementById("hand_france_header"),
+ document.getElementById("hand_bavaria_header"),
+ document.getElementById("hand_prussia_header"),
+ document.getElementById("hand_saxony_header"),
+ document.getElementById("hand_pragmatic_header"),
+ document.getElementById("hand_austria_header"),
+ document.getElementById("political_header"),
+ ],
+ power_panel: [
+ document.getElementById("hand_france_panel"),
+ document.getElementById("hand_bavaria_panel"),
+ document.getElementById("hand_prussia_panel"),
+ document.getElementById("hand_saxony_panel"),
+ document.getElementById("hand_pragmatic_panel"),
+ document.getElementById("hand_austria_panel"),
+ document.getElementById("political_panel"),
+ ],
+ hand: [
+ document.getElementById("hand_france"),
+ document.getElementById("hand_bavaria"),
+ document.getElementById("hand_prussia"),
+ document.getElementById("hand_saxony"),
+ document.getElementById("hand_pragmatic"),
+ document.getElementById("hand_austria"),
+ ],
cities: [],
- roads: [],
+ action_register: [],
+}
+
+function register_action(target, action, id) {
+ target.my_id = id
+ target.my_action = action
+ target.onmousedown = (evt) => on_click_action(evt, target)
+ ui.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 process_actions() {
+ for (let target of ui.action_register)
+ target.classList.toggle("action", is_action(target.my_action, target.my_id))
}
-function make_road(type, x, y, dx, dy) {
+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 create_element(action, id, style) {
let e = document.createElement("div")
- e.className = type
- e.style.left = x + "px"
- e.style.top = y + "px"
- let a = Math.atan2(dy, dx)
- let s = (Math.hypot(dx, dy) - 15) / 20
- e.style.transform =
- "rotate(" + a + "rad)" +
- "scale(" + s + ", 1)"
- // TODO: rotate to align
- map_pieces.appendChild(e)
+ e.className = style
+ register_action(e, action, id)
+ return e
+}
+
+function create_piece(action, id, style) {
+ let e = document.createElement("div")
+ e.className = style
+ register_action(e, action, id)
+ e.onmouseenter = on_focus_piece
+ e.onmouseleave = on_blur_piece
+ return e
+}
+
+function create_marker(style) {
+ let e = document.createElement("div")
+ e.className = style
+ return e
+}
+
+function make_tc_deck(n) {
+ for (let suit = 0; suit <= 3; ++suit) {
+ for (let value = 2; value <= 10; ++value) {
+ let c = (n << 7) | (suit << 4) | value
+ ui.tc[c] = create_element("card", c, "card tc deck_" + (n+1) + " " + suit_class[suit] + " " + suit_letter[suit] + value)
+ }
+ }
+ for (let value = 2; value <= 3; ++value) {
+ let c = (n << 7) | (4 << 4) | value
+ ui.tc[c] = create_element("card", c, "card tc deck_" + (n+1) + " reserve R")
+ }
+}
+
+function make_tc_deck_back(n) {
+ let list = []
+ for (let i = 0; i < 38; ++i) {
+ let e = document.createElement("div")
+ e.className = "card tc reverse " + n
+ list.push(e)
+ }
+ return list
+}
+
+function make_political_card(fc) {
+ let e = document.createElement("div")
+ if (fc === 0)
+ e.className = "card political reverse"
+ else
+ e.className = "card political c" + fc
+ return e
+}
+
+function has_removed_all_pieces(pow) {
+ for (let p of all_power_generals[pow])
+ if (view.pos[p] !== REMOVED)
+ return false
+ for (let p of all_power_trains[pow])
+ if (view.pos[p] !== REMOVED)
+ return false
+ return true
}
function on_init() {
- if (false) {
- for (let a = 0; a <= last_city; ++a) {
- for (let b of cities.major_roads[a]) {
- if (a < b) {
- let dx = cities.x[a] - cities.x[b]
- let dy = cities.y[a] - cities.y[b]
- let x = (cities.x[a] + cities.x[b]) / 2
- let y = (cities.y[a] + cities.y[b]) / 2
- make_road("major_road", x - 10, y - 3, dx, dy)
- }
- }
- for (let b of cities.roads[a]) {
- if (a < b) {
- let dx = cities.x[a] - cities.x[b]
- let dy = cities.y[a] - cities.y[b]
- let x = (cities.x[a] + cities.x[b]) / 2
- let y = (cities.y[a] + cities.y[b]) / 2
- make_road("road", x - 10, y - 1, dx, dy)
- }
+ ui.pieces = [
+ create_piece("piece", 0, "piece cylinder france_1"),
+ create_piece("piece", 1, "piece cylinder france_2"),
+ create_piece("piece", 2, "piece cylinder france_3"),
+ create_piece("piece", 3, "piece cylinder france_4"),
+ create_piece("piece", 4, "piece cylinder france_5"),
+ create_piece("piece", 5, "piece cylinder bavaria_1"),
+
+ create_piece("piece", 6, "piece cylinder prussia_1"),
+ create_piece("piece", 7, "piece cylinder prussia_2"),
+ create_piece("piece", 8, "piece cylinder prussia_3"),
+ create_piece("piece", 9, "piece cylinder prussia_4"),
+ create_piece("piece", 10, "piece cylinder saxony_1"),
+ create_piece("piece", 11, "piece cylinder pragmatic_1"),
+ create_piece("piece", 12, "piece cylinder pragmatic_2"),
+ create_piece("piece", 13, "piece cylinder pragmatic_3"),
+
+ create_piece("piece", 14, "piece cylinder austria_1"),
+ create_piece("piece", 15, "piece cylinder austria_2"),
+ create_piece("piece", 16, "piece cylinder austria_3"),
+ create_piece("piece", 17, "piece cylinder austria_4"),
+ create_piece("piece", 18, "piece cylinder austria_5"),
+ create_piece("piece", 19, "piece cylinder austria_6"),
+
+ create_piece("piece", 20, "piece cube france"),
+ create_piece("piece", 21, "piece cube france"),
+ create_piece("piece", 22, "piece cube bavaria"),
+
+ create_piece("piece", 23, "piece cube prussia"),
+ create_piece("piece", 24, "piece cube prussia"),
+ create_piece("piece", 25, "piece cube saxony"),
+ create_piece("piece", 26, "piece cube pragmatic"),
+
+ create_piece("piece", 27, "piece cube austria"),
+ create_piece("piece", 28, "piece cube austria"),
+ create_piece("piece", 29, "piece cube austria"),
+
+ create_piece("piece", 30, "piece disc austria"),
+ create_piece("piece", 31, "piece disc austria"),
+ ]
+
+ ui.troops = []
+ for (let i = 0; i < 20; ++i)
+ ui.troops[i] = create_marker("hide")
+ for (let e of ui.troops)
+ ui.pieces_element.appendChild(e)
+
+ /*
+ ui.conquest = []
+ ui.retro = []
+ for (let s of all_objectives) {
+ for (let pow = 0; pow < 7; ++pow) {
+ if (set_has(objective1[pow], s) || set_has(objective2[pow], s)) {
+ ui.conquest[s] = create_conquest("marker conquest " + power_class[pow], s)
+ ui.retro[s] = create_conquest("marker retroactive " + power_class[pow], s)
}
}
}
+ */
+
+ ui.tc = []
+ make_tc_deck(0)
+ make_tc_deck(1)
+ make_tc_deck(2)
+ make_tc_deck(3)
+
+ ui.tc_back = [
+ make_tc_deck_back("deck_1"),
+ make_tc_deck_back("deck_2"),
+ make_tc_deck_back("deck_3"),
+ make_tc_deck_back("deck_4"),
+ ]
+
+ ui.combat = document.createElement("div")
+ ui.combat.id = "combat"
+ ui.combat.style.zIndex = 2000
+
+ ui.tcbreak = document.createElement("div")
+ ui.tcbreak.className = "draw-break"
+
+ ui.political = []
+ for (let fc = 0; fc <= 18; ++fc)
+ ui.political[fc] = make_political_card(fc)
for (let a = 0; a <= last_city; ++a) {
let e = ui.cities[a] = document.createElement("div")
let x = cities.x[a]
let y = cities.y[a]
- if (data.type.major_fortress.includes(a)) {
- e.className = "major fortress " + cities.country[a]
- x -= 20
- y -= 20
+ if (set_has(data.type.major_fortress, a)) {
+ e.className = "space major_fortress"
+ x -= 16 + 6 + 4
+ y -= 16 + 6 + 4
+ }
+ else if (set_has(data.type.minor_fortress, a)) {
+ e.className = "space minor_fortress"
+ x -= 14 + 5 + 4
+ y -= 14 + 5 + 4
}
- else if (data.type.minor_fortress.includes(a)) {
- e.className = "minor fortress " + cities.country[a]
- x -= 14
- y -= 14
+ else if (set_has(data.type.box, a)) {
+ e.className = "space box"
+ if (data.cities.name[a] === "England") {
+ e.className = "space box england"
+ x -= 24 + 4
+ y -= 28 + 4
+ } else {
+ x -= 22 + 4
+ y -= 22 + 4
+ }
}
else {
- e.className = "city " + cities.country[a]
- x -= 12
- y -= 12
+ e.className = "space city"
+ x -= 9 + 5 + 4
+ y -= 9 + 5 + 4
}
- e.classList.add("hide")
+ if (set_has(data.country.Austria, a)) e.classList.add("country_austria")
+ if (set_has(data.country.France, a)) e.classList.add("country_france")
+ if (set_has(data.country.Prussia, a)) e.classList.add("country_prussia")
+ if (set_has(data.country.Netherlands, a)) e.classList.add("country_netherlands")
+ if (set_has(data.country.Silesia, a)) e.classList.add("country_silesia")
+ if (set_has(data.country.Saxony, a)) e.classList.add("country_saxony")
+ if (set_has(data.country.Bavaria, a)) e.classList.add("country_bavaria")
+ if (set_has(data.country.Poland, a)) e.classList.add("country_poland")
+ if (set_has(data.country.Empire, a)) e.classList.add("country_empire")
+
+ register_action(e, "space", a)
+
+ e.onmouseenter = on_focus_city
+ e.onmouseleave = on_blur_city
+
+ //e.classList.add("hide")
e.style.left = x + "px"
e.style.top = y + "px"
- e.title = cities.name[a]
- map_pieces.appendChild(e)
+
+ ui.spaces_element.appendChild(e)
}
+
+ sort_power_panel(false)
+
+ update_favicon()
}
on_init()
+
+/* TOOLTIPS */
+
+function on_click_city_tip(loc) {
+ ui.cities[loc].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
+}
+
+function on_focus_city_tip(s) {
+ ui.cities[s].classList.add("tip")
+}
+
+function on_blur_city_tip(s) {
+ ui.cities[s].classList.remove("tip")
+}
+
+function on_click_piece_tip(loc) {
+ ui.pieces[loc].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
+}
+
+function on_focus_piece_tip(s) {
+ ui.pieces[s].classList.add("tip")
+}
+
+function on_blur_piece_tip(s) {
+ ui.pieces[s].classList.remove("tip")
+}
+
+function on_focus_city(evt) {
+ ui.status.textContent = data.cities.name[evt.target.my_id]
+}
+
+function on_blur_city() {
+ ui.status.textContent = ""
+}
+
+function on_focus_piece(evt) {
+ ui.status.textContent = piece_tooltip_name[evt.target.my_id]
+}
+
+function on_blur_piece() {
+ ui.status.textContent = ""
+}
+
+/* UPDATE UI */
+
+function layout_general_offset(g, s) {
+ // if not selected: number of unselected generals below us
+ // if not selected: (number of unselected generals + 1) + number of selected generals below us
+ if (!set_has(view.selected, g)) {
+ let n = 0
+ for (let i = g+1; i < 20; ++i)
+ if (view.pos[i] === s && !set_has(view.selected, i))
+ ++n
+ return n
+ } else {
+ let n = 0
+ for (let i = 0; i < 20; ++i)
+ if (view.pos[i] === s && !set_has(view.selected, i))
+ ++n
+ if (n > 0)
+ ++n
+ for (let i = g+1; i < 20; ++i)
+ if (view.pos[i] === s && set_has(view.selected, i))
+ ++n
+ return n
+ }
+}
+
+function layout_general_offset_elim(g) {
+ let n = 0
+ let p = get_cylinder_power(g)
+ for (let i of all_power_generals[p])
+ if (i > g && view.pos[i] === ELIMINATED)
+ ++n
+ return n
+}
+
+function layout_train_offset(g, s) {
+ let n = 0
+ for (let i = g+1; i < 35; ++i)
+ if (view.pos[i] === s)
+ ++n
+ return n
+}
+
+function get_cylinder_power(id) {
+ for (let p of all_powers)
+ if (set_has(all_power_generals[p], id))
+ return p
+ return -1
+}
+
+function layout_general(id, s) {
+ let e = ui.pieces[id]
+ let x, y, n
+
+ if (s === REMOVED) {
+ if (e.parentElement === ui.pieces_element)
+ e.remove()
+ return
+ }
+ if (e.parentElement !== ui.pieces_element)
+ ui.pieces_element.appendChild(e)
+
+ if (s === ELIMINATED) {
+ n = layout_general_offset_elim(id)
+ x = ELIMINATED_GENERAL_X + ELIMINATED_GENERAL_DX * get_cylinder_power(id)
+ y = ELIMINATED_GENERAL_Y
+ } else {
+ n = layout_general_offset(id, s)
+ x = data.cities.x[s]
+ y = data.cities.y[s]
+ }
+
+ let selected = set_has(view.selected, id)
+
+ e.style.left = (x - 21) + "px"
+ e.style.top = (y - 29 - 15 * n) + "px"
+ e.style.zIndex = y + n
+ e.classList.toggle("selected", selected)
+ e.classList.toggle("oos", (view.oos & (1 <<id)) !== 0)
+
+ e = ui.troops[id]
+ // e.style.left = (x + 21 + 1) + "px"
+ // e.style.top = (y - 7 - 14 * n) + "px"
+ e.style.left = (x - 7) + "px"
+ // e.style.top = (y + 7 - 15 * n) + "px"
+ e.style.top = (y + 2 - 15 * n) + "px"
+ e.style.zIndex = y + n + 1
+ e.className = power_class[GENERAL_POWER[id]] + " piece number n" + view.troops[id]
+}
+
+function layout_train(id, s) {
+ let e = ui.pieces[id]
+ let n = layout_train_offset(id, s)
+ let x, y
+
+ if (s === REMOVED) {
+ if (e.parentElement === ui.pieces_element)
+ e.remove()
+ return
+ }
+ if (e.parentElement !== ui.pieces_element)
+ ui.pieces_element.appendChild(e)
+
+ if (s === ELIMINATED) {
+ x = ELIMINATED_TRAIN_X
+ y = ELIMINATED_TRAIN_Y
+ } else {
+ x = data.cities.x[s]
+ y = data.cities.y[s]
+ }
+
+ e.style.left = (x - 14 + n * 33) + "px"
+ e.style.top = (y - 21 - n * 0) + "px"
+ e.classList.toggle("selected", set_has(view.selected, id))
+}
+
+function layout_hussar(id, s) {
+ let e = ui.pieces[id]
+ let n = 0
+ let x, y
+
+ if (e.parentElement !== ui.pieces_element)
+ ui.pieces_element.appendChild(e)
+
+ if (s === ELIMINATED) {
+ x = ELIMINATED_DISC_X
+ y = ELIMINATED_DISC_Y
+ n = id - 30
+ } else {
+ x = data.cities.x[s]
+ y = data.cities.y[s]
+ }
+
+ e.style.left = (x - 14) + "px"
+ e.style.top = (y - 16 - n * 12) + "px"
+ e.classList.toggle("selected", set_has(view.selected, id))
+}
+
+function layout_combat_marker() {
+ let x = (data.cities.x[view.attacker] + data.cities.x[view.defender]) >> 1
+ let y = (data.cities.y[view.attacker] + data.cities.y[view.defender]) >> 1
+ ui.combat.style.left = x - 20 + "px"
+ ui.combat.style.top = y - 20 + "px"
+}
+
+function create_conquest(style, s) {
+ let x = data.cities.x[s]
+ let y = data.cities.y[s]
+ let e = document.createElement("div")
+ e.dataset.id = s
+ e.style.left = (x - 16) + "px"
+ e.style.top = (y - 16) + "px"
+ e.className = style
+ return e
+}
+
+function update_favicon() {
+ let favicon = document.querySelector('link[rel="icon"]')
+ switch (params.role) {
+ case "Frederick": favicon.href = "favicon/favicon_frederick.png"; break
+ case "Elisabeth": favicon.href = "favicon/favicon_elisabeth.png"; break
+ case "Maria Theresa": favicon.href = "favicon/favicon_maria_theresa.png"; break
+ case "Pompadour": favicon.href = "favicon/favicon_pompadour.png"; break
+ }
+}
+
+function cmp_tc(a, b) {
+ let ax = (to_suit(a) << 7) + (to_value(a) << 3) + to_deck(a)
+ let bx = (to_suit(b) << 7) + (to_value(b) << 3) + to_deck(b)
+ return ax - bx
+}
+
+const colorize_S = '<span class="suit spades">\u2660</span>'
+const colorize_C = '<span class="suit clubs">\u2663</span>'
+const colorize_H = '<span class="suit hearts">\u2665</span>'
+const colorize_D = '<span class="suit diamonds">\u2666</span>'
+const colorize_R = '$1<span class="suit reserve">R</span>'
+
+const suit_text = [
+ '<span class="suit spades">\u2660</span>',
+ '<span class="suit clubs">\u2663</span>',
+ '<span class="suit hearts">\u2665</span>',
+ '<span class="suit diamonds">\u2666</span>',
+ '<span class="suit reserve">R</span>'
+]
+
+function colorize(text) {
+ text = text.replaceAll("\u2660", colorize_S)
+ text = text.replaceAll("\u2663", colorize_C)
+ text = text.replaceAll("\u2665", colorize_H)
+ text = text.replaceAll("\u2666", colorize_D)
+ text = text.replace(/(\d+)R/g, colorize_R)
+ return text
+}
+
+function on_update() {
+// let text = colorize(view.prompt)
+// if (text !== view.prompt)
+// ui.prompt.innerHTML = text
+
+ ui.header.classList.toggle("france", view.power === P_FRANCE)
+ ui.header.classList.toggle("bavaria", view.power === P_BAVARIA)
+ ui.header.classList.toggle("prussia", view.power === P_PRUSSIA)
+ ui.header.classList.toggle("saxony", view.power === P_SAXONY)
+ ui.header.classList.toggle("pragmatic", view.power === P_PRAGMATIC)
+ ui.header.classList.toggle("austira", view.power === P_AUSTRIA)
+
+ sort_power_panel(true)
+
+/*
+ for (let p = 0; p < 20; ++p)
+ layout_general(p, view.pos[p])
+ for (let p = 20; p < 30; ++p)
+ layout_train(p, view.pos[p])
+ for (let p = 30; p < 32; ++p)
+ layout_hussar(p, view.pos[p])
+*/
+
+ let back = [ 0, 0, 0, 0 ]
+
+ for (let pow of all_powers) {
+ /*
+ let banner = `${power_name[pow]} \u2014 ${view.pt[pow]} troops`
+ let m_obj = count_total_objectives(pow)
+ if (m_obj > 0) {
+ let n_obj = count_captured_objectives(pow)
+ if (pow === P_AUSTRIA && view.oo)
+ m_obj += "*"
+ banner += ` \u2014 ${n_obj} of ${m_obj} objectives`
+ }
+ ui.power_header[pow].textContent = banner
+ */
+
+ ui.hand[pow].replaceChildren()
+ view.hand[pow].sort(cmp_tc)
+ for (let c of view.hand[pow]) {
+ if ((c & 15) === 0)
+ ui.hand[pow].appendChild(ui.tc_back[c>>7][back[c>>7]++])
+ else
+ ui.hand[pow].appendChild(ui.tc[c])
+ }
+ }
+
+ if (view.draw) {
+ view.draw.sort(cmp_tc)
+ if (view.hand[view.power].length > 0)
+ ui.hand[view.power].appendChild(ui.tcbreak)
+ for (let c of view.draw)
+ ui.hand[view.power].appendChild(ui.tc[c])
+ }
+
+ /*
+ ui.political_display.replaceChildren()
+ if (view.oo > 0)
+ ui.political_display.appendChild(ui.tc[view.oo])
+ ui.political_display.appendChild(ui.fate[0])
+ if (typeof view.fate === "object")
+ for (let c of view.fate)
+ ui.political_display.appendChild(ui.fate[c])
+ */
+
+ ui.markers_element.replaceChildren()
+ /*
+ for (let s of view.conquest)
+ ui.markers_element.appendChild(ui.conquest[s])
+ for (let s of view.retro)
+ ui.markers_element.appendChild(ui.retro[s])
+ */
+
+ if (view.attacker !== undefined && view.defender !== undefined) {
+ ui.markers_element.appendChild(ui.combat)
+ layout_combat_marker()
+ }
+
+ for (let v = 16; v >= 0; --v)
+ action_button_with_argument("value", v, v)
+
+ for (let p = 0; p < 24; ++p)
+ action_button_with_argument("detach", p, "Detach " + piece_button_name[p])
+
+ action_button("take", "Take")
+ action_button("give", "Give")
+ action_button("recruit", "Recruit")
+ action_button("transfer", "Transfer")
+
+ action_button("stop", "Stop")
+ action_button("pass", "Pass")
+ action_button("next", "Next")
+ action_button("done", "Done")
+
+ action_button("end_cards", "End card draw")
+ action_button("end_setup", "End setup")
+ action_button("end_recruit", "End recruit")
+ action_button("end_movement", "End movement")
+ action_button("end_combat", "End combat")
+ action_button("end_supply", "End supply")
+ action_button("end_turn", "End turn")
+
+ confirm_action_button("confirm_end_movement", "End movement",
+ "You still have UNMOVED pieces.?")
+
+ action_button("undo", "Undo")
+
+ process_actions()
+}
+
+/* LOG */
+
+const piece_log_name = [
+]
+
+const piece_power = [
+]
+
+const piece_button_name = [
+]
+
+const piece_tooltip_name = [
+]
+
+function sub_piece(match, p1) {
+ let x = p1 | 0
+ let n = piece_log_name[x]
+ let p = power_class[piece_power[x]]
+ return `<span class="piece_tip ${p}" onclick="on_click_piece_tip(${x})" onmouseenter="on_focus_piece_tip(${x})" onmouseleave="on_blur_piece_tip(${x})">${n}</span>`
+}
+
+function sub_space(match, p1) {
+ let x = p1 | 0
+ let n = data.cities.name[x]
+ return `<span class="city_tip" onclick="on_click_city_tip(${x})" onmouseenter="on_focus_city_tip(${x})" onmouseleave="on_blur_city_tip(${x})">${n}</span>`
+}
+
+function sub_tc(match, p1) {
+ return value + suit_text[suit]
+}
+
+function on_log(text) {
+ let p = document.createElement("div")
+
+ if (text.match(/^>>/)) {
+ text = text.substring(2)
+ p.className = "ii"
+ }
+
+ if (text.match(/^>/)) {
+ text = text.substring(1)
+ p.className = "i"
+ }
+
+ if (text.match(/^!/)) {
+ text = "Combat"
+ p.className = "combat"
+ }
+
+ text = text.replace(/&/g, "&amp;")
+ text = text.replace(/</g, "&lt;")
+ text = text.replace(/>/g, "&gt;")
+
+ text = text.replace(/S(\d+)/g, sub_space)
+ text = text.replace(/P(\d+)/g, sub_piece)
+ text = colorize(text)
+
+ if (text.match(/^\$(\d+)/)) {
+ let fx = parseInt(text.substring(1))
+ if (fx < 48 + 6)
+ text = `<div class="q">${fate_flavor_text[fx]}</div><div></div><div>${fate_effect_text[fx]}</div><div></div>`
+ else
+ text = `<div class="q">${fate_flavor_text[fx]}</div><div></div>`
+ }
+ else if (text.match(/^# /)) {
+ p.className = "h fate"
+ text = text.substring(2)
+ }
+ else if (text.match(/^\.s1/))
+ text = the_war_in_the_west_text
+ else if (text.match(/^\.s2/))
+ text = the_austrian_theater_text
+ else if (text.match(/^=\d/)) {
+ p.className = "h " + power_class[text[1]]
+ text = power_name[text[1]]
+ }
+
+ p.innerHTML = text
+ return p
+}
+
+/* COMMON LIBRARY */
+
+function array_insert(array, index, item) {
+ for (let i = array.length; i > index; --i)
+ array[i] = array[i - 1]
+ array[index] = item
+}
+
+function set_has(set, item) {
+ if (set === item) return true
+ if (set === undefined) return false
+ if (set === null) return false
+ 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 set_add(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
+ }
+ array_insert(set, a, item)
+}
+
+function set_add_all(set, other) {
+ for (let item of other)
+ set_add(set, item)
+}