"use strict"
// TODO: tooltip lord card when mouseover cylinder
function toggle_pieces() {
document.getElementById("pieces").classList.toggle("hide")
}
function toggle_seats() {
document.getElementById("seats").classList.toggle("hide")
}
// === CONSTANTS (matching those in rules.js) ===
function find_lord(name) { return data.lords.findIndex((x) => x.name === name) }
function find_card(name) { return data.cards.findIndex((x) => x.name === name) }
function find_locale(name) { return data.locales.findIndex(x => x.name === name) }
const LORD_HENRY_VI = find_lord("Henry VI")
const LOC_LONDON = find_locale("London")
const first_york_lord = 0
const last_york_lord = 13
const first_lancaster_lord = 14
const last_lancaster_lord = 27
const first_york_card = 0
const last_york_card = 36
const first_lancaster_card = 37
const last_lancaster_card = 73
const last_aow_card = last_lancaster_card
const first_locale = 0
const last_locale = data.locales.length - 1
const first_vassal = 0
const last_vassal = data.vassals.length - 1
const RETINUE = 0
const VASSAL = 1
const MEN_AT_ARMS = 2
const LONGBOWMEN = 3
const MILITIA = 4
const BURGUNDIANS = 5
const MERCENARIES = 6
const force_type_count = 7
const force_action_name = [ "retinue", "vassal", "men_at_arms", "longbowmen", "militia", "burgundians", "mercenaries" ]
const force_class_name = [ "retinue", "vassal", "shape men_at_arms", "shape longbowmen", "shape militia", "shape burgundians", "shape mercenaries" ]
const routed_force_action_name = [ "routed_retinue", "routed_vassal", "routed_men_at_arms", "routed_longbowmen", "routed_militia", "routed_burgundians", "routed_mercenaries" ]
const COIN = 1
const asset_type_count = 4
const asset_action_name = [ "prov", "coin", "cart", "ship" ]
const asset_type_x34 = [ 1, 1, 1, 0 ]
const NOWHERE = -1
const CALENDAR = 100
const CALENDAR_EXILE = 200
const LONDON_FOR_YORK = 300
const CAPTURE_OF_THE_KING = 400 // Ia. special rule (400 + lord ID that has him captured)
const VASSAL_READY = 29
const VASSAL_CALENDAR = 30
const VASSAL_OUT_OF_PLAY = 31
const TOWN = "town"
const CITY = "city"
const FORTRESS = "fortress"
const SUMMER = 0
const SPRING = 1
const WINTER = 2
const AUTUMN = 3
const SEASONS = [
null,
WINTER,
SPRING,
SUMMER,
AUTUMN,
WINTER,
WINTER,
SPRING,
SUMMER,
AUTUMN,
WINTER,
WINTER,
SPRING,
SUMMER,
AUTUMN,
WINTER,
null
]
// === ACTIONS ===
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_action(evt) {
if (evt.button === 0) {
if (evt.target.my_id === undefined) {
send_action(evt.target.my_action)
if (evt.target.my_action_2)
send_action(evt.target.my_action_2)
} else {
send_action(evt.target.my_action, evt.target.my_id)
if (evt.target.my_action_2)
send_action(evt.target.my_action_2, evt.target.my_id)
}
}
}
function register_action(elt, action, id, action_2) {
elt.my_id = id
elt.my_action = action
elt.my_action_2 = action_2
elt.onmousedown = on_action
}
// === TOOLTIPS ===
function register_tooltip(elt, focus, blur) {
if (typeof focus === "function")
elt.onmouseenter = focus
else
elt.onmouseenter = () => on_focus(focus)
if (blur)
elt.onmouseleave = blur
else
elt.onmouseleave = on_blur
}
function on_focus(text) {
document.getElementById("status").textContent = text
}
function on_blur() {
document.getElementById("status").textContent = ""
update_current_card_display()
}
function get_locale_tip(id) {
return data.locales[id].name
}
function on_focus_cylinder(evt) {
let lord = evt.target.my_id
let info = data.lords[lord]
let loc = get_lord_locale(lord)
let tip = info.short_name
on_focus(tip)
ui.command.replaceChildren(ui.lords2[lord])
}
// === GAME STATE ===
function current_season() {
return SEASONS[view.turn >> 1]
}
function max_plan_length() {
switch (current_season()) {
case SUMMER: return 7
case WINTER: return 4
case SPRING: return 6
case AUTUMN: return 6
}
}
function is_york_lord(lord) {
return lord >= first_york_lord && lord <= last_york_lord
}
function is_lancaster_lord(lord) {
return lord >= first_lancaster_lord && lord <= last_lancaster_lord
}
function get_lord_locale(lord) {
return map_get(view.pieces.locale, lord, -1)
}
function get_lord_moved(lord) {
return map_get(view.pieces.moved, lord, 0)
}
function get_lord_assets(lord, n) {
return map_get_pack4(view.pieces.assets, lord, n, 0)
}
function get_lord_forces(lord, n) {
return map_get_pack4(view.pieces.forces, lord, n, 0)
}
function get_lord_routed(lord, n) {
return map_get_pack4(view.pieces.routed, lord, n, 0)
}
function get_lord_capability(lord, n) {
return map2_get(view.pieces.capabilities, lord, n, -1)
}
function is_lord_in_exile(lord) {
return get_lord_locale(lord) >= CALENDAR_EXILE
}
function count_lord_all_forces(lord) {
return (
get_lord_forces(lord, MERCENARIES) +
get_lord_forces(lord, BURGUNDIANS) +
get_lord_forces(lord, MEN_AT_ARMS) +
get_lord_forces(lord, MILITIA) +
get_lord_forces(lord, LONGBOWMEN)
)
}
function count_favour(type) {
let n = 0
for (let x = first_locale; x <= last_locale; x++) {
if (data.locales[x].type !== type)
continue
if (set_has(view.pieces.favourl, x))
n += 1
if (set_has(view.pieces.favoury, x))
n -= 1
}
return n
}
function get_vassal_lord(vassal) {
return view.pieces.vassals[vassal] & 31
}
function get_vassal_service(vassal) {
return view.pieces.vassals[vassal] >> 5
}
function for_each_vassal_with_lord(lord, f) {
for (let x = first_vassal; x <= last_vassal; x++)
if (get_vassal_lord(x) === lord)
f(x)
}
function is_york_locale(loc) {
return loc >= first_york_locale && loc <= last_york_locale
}
function is_lancaster_locale(loc) {
return loc >= first_lancaster_locale && loc <= last_lancaster_locale
}
function is_lord_on_map(lord) {
let loc = get_lord_locale(lord)
return loc !== NOWHERE && loc < CALENDAR
}
function is_lord_in_game(lord) {
return get_lord_locale(lord) !== NOWHERE
}
function is_lord_on_calendar(lord) {
let loc = get_lord_locale(lord)
return loc >= CALENDAR
}
function is_levy_phase() {
return (view.turn & 1) === 0
}
function is_lord_in_battle(lord) {
if (view.battle && view.battle.array) {
for (let i = 0; i < 6; ++i)
if (view.battle.array[i] === lord)
return true
}
return false
}
function is_lord_command(ix) {
if (view.battle)
return false
return view.command === ix
}
function is_lord_selected(ix) {
if (view.group)
return set_has(view.group, ix)
if (view.who >= 0)
return ix === view.who
return false
}
function lord_has_capability_card(lord, c) {
let name = data.cards[c].capability
let c1 = get_lord_capability(lord, 0)
if (c1 >= 0 && data.cards[c1].capability === name)
return true
let c2 = get_lord_capability(lord, 1)
if (c2 >= 0 && data.cards[c2].capability === name)
return true
return false
}
function lord_has_capability(lord, card_or_list) {
if (Array.isArray(card_or_list)) {
for (let card of card_or_list)
if (lord_has_capability_card(lord, card))
return true
return false
}
return lord_has_capability_card(lord, card_or_list)
}
// === BUILD UI ===
const calendar_boxes = {
"box1": [204,38,100,162],
"box2": [306,38,100,162],
"box3": [408,38,100,162],
"box4": [510,38,100,162],
"box5": [612,38,100,162],
"box6": [734,38,100,162],
"box7": [836,38,100,162],
"box8": [938,38,100,162],
"box9": [1040,38,100,162],
"box10": [1142,38,100,162],
"box11": [734,260,100,162],
"box12": [836,260,100,162],
"box13": [938,260,100,162],
"box14": [1040,260,100,162],
"box15": [1142,260,100,162],
"box16": [938,423,310,52],
}
const track_xy = []
const calendar_xy = []
const locale_xy = []
const ui = {
favicon: document.getElementById("favicon"),
locale: [],
locale_name: [],
depleted: [],
locale_markers_rose: [],
lord_cylinder: [],
mat: [],
mat_card: [],
mat_caps: [],
retinue: [],
routed_retinue: [],
troops: [],
routed_troops: [],
assets: [],
lord_exile: [],
vassal_cal: [], // token on calendar
vassal_map: [], // token on map
vassal_mat: [], // token on mat
valour_area: [],
marker_area: [],
lord_moved1: [],
lord_moved2: [],
lord_fled: [],
lord_feed: [],
cards: [],
cards2: [],
lords2: [],
seat: [],
plan_panel: document.getElementById("plan_panel"),
plan: document.getElementById("plan"),
plan_actions: document.getElementById("plan_actions"),
plan_cards: [],
plan_action_cards: [],
arts_of_war_panel: document.getElementById("arts_of_war_panel"),
arts_of_war: document.getElementById("arts_of_war"),
events_panel: document.getElementById("events_panel"),
events: document.getElementById("events"),
hand_panel: document.getElementById("hand_panel"),
hand: document.getElementById("hand"),
held_york: document.querySelector("#role_York .role_stat"),
held_lancaster: document.querySelector("#role_Lancaster .role_stat"),
command: document.getElementById("turn_info"),
turn: document.getElementById("turn"),
end: document.getElementById("end"),
victory_check: document.getElementById("victory_check"),
fortresses: document.getElementById("fortresses"),
towns: document.getElementById("towns"),
cities: document.getElementById("cities"),
influence: document.getElementById("ip"),
battle: document.getElementById("battle"),
court1_panel: document.getElementById("court1_panel"),
court2_panel: document.getElementById("court2_panel"),
court1_header: document.getElementById("court1_header"),
court2_header: document.getElementById("court2_header"),
court1: document.getElementById("court1"),
court2: document.getElementById("court2"),
battle_panel: document.getElementById("battle_panel"),
battle_header: document.getElementById("battle_header"),
battle_grid: document.getElementById("battle_grid"),
battle_grid_array: [
document.getElementById("grid_a1"),
document.getElementById("grid_a2"),
document.getElementById("grid_a3"),
document.getElementById("grid_d1"),
document.getElementById("grid_d2"),
document.getElementById("grid_d3"),
],
}
let locale_layout_york = []
let locale_layout_lanc = []
let calendar_layout_vassal = []
let calendar_layout_york = []
let calendar_layout_lanc = []
function clean_name(name) {
return name.toLowerCase().replaceAll("&", "and").replaceAll(" ", "_")
}
function build_div(parent, className) {
let e = document.createElement("div")
e.className = className
if (parent)
parent.appendChild(e)
return e
}
function build_lord_mat(lord, ix, side, name) {
let mat = build_div(null, `mat ${side} ${name}`)
let board = build_div(mat, "board")
ui.mat_card[ix] = build_div(board, "card lord " + side + " " + name)
ui.lords2[ix] = build_div(null, "card lord " + side + " " + name)
build_div(board, "mask " + side)
ui.retinue[ix] = build_div(board, "retinue_vassals")
ui.routed_retinue[ix] = build_div(board, "routed_retinue_vassals")
ui.troops[ix] = build_div(board, "troops")
ui.routed_troops[ix] = build_div(board, "routed_troops")
ui.assets[ix] = build_div(board, "assets")
ui.mat_caps[ix] = build_div(mat, "capabilities")
ui.valour_area[ix] = build_div(board, "valour_area")
ui.marker_area[ix] = build_div(board, "marker_area")
ui.lord_moved1[ix] = build_div(ui.marker_area[ix], "marker square moved_fought one hide")
ui.lord_moved2[ix] = build_div(ui.marker_area[ix], "marker square moved_fought two hide")
ui.lord_fled[ix] = build_div(ui.marker_area[ix], "marker square fled hide")
ui.lord_feed[ix] = build_div(ui.marker_area[ix], "marker small feed x2")
ui.mat[ix] = mat
register_action(ui.mat_card[ix], "lord", ix)
}
function build_card(side, c, id) {
let card = ui.cards[c] = document.querySelector(`div[data-card="${id}"]`)
ui.cards2[c] = card.cloneNode(true)
register_action(card, "card", c)
}
function build_plan() {
let elt
for (let i = 0; i < 7; ++i) {
elt = document.createElement("div")
elt.className = "hide"
ui.plan_cards.push(elt)
ui.plan.appendChild(elt)
}
for (let lord = 0; lord < 28; ++lord) {
let side = lord < 14 ? "york" : "lancaster"
elt = document.createElement("div")
elt.className = `card cc ${side} ${data.lords[lord].id}`
register_action(elt, "plan", lord)
ui.plan_action_cards.push(elt)
ui.plan_actions.appendChild(elt)
}
ui.plan_action_pass_york = elt = document.createElement("div")
elt.className = `card cc york pass`
register_action(elt, "plan", -1)
ui.plan_actions.appendChild(elt)
ui.plan_action_pass_lancaster = elt = document.createElement("div")
elt.className = `card cc lancaster pass`
register_action(elt, "plan", -1)
ui.plan_actions.appendChild(elt)
}
function build_map() {
for (let i = 0; i <= 16; ++i) {
calendar_layout_york[i] = []
calendar_layout_lanc[i] = []
calendar_layout_vassal[i] = []
}
for (let i = 0; i < data.locales.length; ++i) {
locale_layout_york[i] = []
locale_layout_lanc[i] = []
}
data.locales.forEach((locale, ix) => {
let region = locale.region ? clean_name(locale.region) : ""
let { x, y, w, h } = locale.box
let ax, ay, aw, ah
let xc = Math.round(x + w / 2)
let yc = Math.round(y + h / 2)
let e
locale_xy[ix] = [ xc, yc ]
if (locale.type === "exile_box") {
locale_xy[ix] = [ xc, y + 45 ]
ax = x + 6
ay = y + 6
aw = w - 13
ah = h - 21
} else {
ax = x - 6
ay = y - 6
aw = w + 12
ah = h + 8
}
// Main Area
e = ui.locale[ix] = document.createElement("div")
if (locale.type === "exile_box")
e.className = "locale " + locale.type + " " + locale.name.toLowerCase()
else
e.className = "locale " + locale.type + " " + region.toLowerCase()
e.style.left = ax + "px"
e.style.top = ay + "px"
e.style.width = aw + "px"
e.style.height = ah + "px"
register_action(e, "locale", ix, "laden_march")
register_tooltip(e, get_locale_tip(ix))
document.getElementById("locales").appendChild(e)
// London for York
if (ix === LOC_LONDON) {
e = ui.london_for_york = document.createElement("div")
e.className = "hide"
e.style.top = yc - 20 - 13 + "px"
//e.style.top = y + h - 43 - 13 + "px"
e.style.left = xc - 20 - 13 + "px"
e.style.pointerEvents = "none"
document.getElementById("pieces").appendChild(e)
}
// Favour
if (locale.type === "exile_box") {
e = ui.locale_markers_rose[ix] = document.createElement("div")
e.className = "hide"
e.style.top = (y + h - 50) + "px"
e.style.left = (xc - 27) + "px"
e.style.pointerEvents = "none"
document.getElementById("pieces").appendChild(e)
} else {
e = ui.locale_markers_rose[ix] = document.createElement("div")
e.className = "hide"
e.style.top = yc - 20 + "px"
//e.style.top = y + h - 43 + "px"
e.style.left = xc - 20 + "px"
e.style.pointerEvents = "none"
document.getElementById("pieces").appendChild(e)
}
// Depleted/Exhausted
e = ui.depleted[ix] = document.createElement("div")
e.className = "hide marker small depexh " + locale.name
e.style.top = yc - 20 - 13 + "px"
//e.style.top = y + h - 43 - 13 + "px"
e.style.left = xc - 20 + 13 + "px"
e.style.pointerEvents = "none"
document.getElementById("pieces").appendChild(e)
})
let layout_seat_york = []
let layout_seat_lanc = []
for (let loc = first_locale; loc <= last_locale; ++loc) {
layout_seat_york[loc] = []
layout_seat_lanc[loc] = []
}
data.lords.forEach((lord, ix) => {
let e = ui.lord_cylinder[ix] = document.createElement("div")
let side = lord.side.toLowerCase()
e.className = "cylinder " + side + " " + lord.id + " hide"
register_action(e, "lord", ix)
register_tooltip(e, on_focus_cylinder)
document.getElementById("pieces").appendChild(e)
let exile = ui.lord_exile[ix] = document.createElement("div")
exile.className = "marker small exile hide"
exile.style.zIndex=1
document.getElementById("pieces").appendChild(exile)
build_lord_mat(lord, ix, side, lord.id)
let loc = data.lords[ix].seat
e = ui.seat[ix] = document.createElement("div")
e.my_id = ix
register_tooltip(e, on_focus_cylinder)
document.getElementById("seats").appendChild(e)
if (is_york_lord(ix)) {
e.className = "hide seat york " + lord.id
layout_seat_york[loc].push(e)
} else {
e.className = "hide seat lancaster " + lord.id
layout_seat_lanc[loc].push(e)
}
})
function layout_seat_markers(loc, dx, dy, list) {
let [ x, y ] = locale_xy[loc]
y -= (list.length - 1) * 8
x += dx
y += dy
for (let e of list) {
e.style.top = y - 37 + "px"
e.style.left = x - 37 + "px"
y += 16
}
}
for (let loc = first_locale; loc <= last_locale; ++loc) {
if (layout_seat_lanc[loc].length + layout_seat_york[loc].length === 1) {
layout_seat_markers(loc, 0, -22, layout_seat_lanc[loc])
layout_seat_markers(loc, 0, -22, layout_seat_york[loc])
} else if (data.locales[loc].name === "Calais" || data.locales[loc].name === "Carlisle") {
layout_seat_markers(loc, -44, 0, layout_seat_york[loc].concat(layout_seat_lanc[loc]))
} else {
layout_seat_markers(loc, -44, 0, layout_seat_lanc[loc])
layout_seat_markers(loc, 44, 0, layout_seat_york[loc])
}
}
ui.captured_king = document.createElement("div")
ui.captured_king.className = "cylinder lancaster " + data.lords[LORD_HENRY_VI].id
ui.captured_king.style.position = "static"
data.vassals.forEach((vassal, ix) => {
let e
if (vassal.box) {
let { x, y, w, h } = vassal.box
e = ui.vassal_map[ix] = document.createElement("div")
let xc = Math.round(x + w / 2)
let yc = Math.round(y + h / 2)
e.className = "hide unit vassal vassal_" + vassal.name.toLowerCase()
e.style.position = "absolute"
e.style.top = yc - 27 + "px"
e.style.left = xc - 27 + "px"
register_action(e, "vassal", ix)
register_tooltip(e, data.vassals[ix].name)
document.getElementById("pieces").appendChild(e)
}
e = ui.vassal_cal[ix] = document.createElement("div")
e.className = "hide unit vassal vassal_" + vassal.name.toLowerCase()
e.style.position = "absolute"
register_action(e, "vassal", ix)
register_tooltip(e, data.vassals[ix].name)
document.getElementById("pieces").appendChild(e)
e = ui.vassal_mat[ix] = document.createElement("div")
e.className = "unit vassal vassal_" + vassal.name.toLowerCase()
register_action(e, "vassal", ix)
register_tooltip(e, data.vassals[ix].name)
})
for (let i = 1; i <= 16; ++i)
calendar_xy[i] = calendar_boxes["box" + i]
for (let i = 0; i <= 45; ++i) {
let name = "track" + i
let x = 0, y = 0
if (i <= 25) {
x = 24 + i * 47.25 + 22
y = 1577 + 22
} else {
x = 1205 + 22
y = 1577 - (i-25) * 47.25 + 22
}
track_xy[i] = [ x, y ]
}
build_plan()
for (let i = 0; i < 6; ++i)
register_action(ui.battle_grid_array[i], "array", i)
for (let c = first_york_card; c <= last_york_card; ++c)
build_card("york", c, "Y" + (1 + c - first_york_card))
for (let c = first_lancaster_card; c <= last_lancaster_card; ++c)
build_card("lancaster", c, "L" + (1 + c - first_lancaster_card))
ui.card_aow_lancaster_back = build_div(null, "card aow lancaster back")
ui.card_aow_york_back = build_div(null, "card aow york back")
ui.card_cc_lancaster_back = build_div(null, "card cc lancaster back")
ui.card_cc_york_back = build_div(null, "card cc york back")
ui.card_cc = []
for (let i = 0; i < 14; ++i)
ui.card_cc[i] = build_div(null, "card cc york " + data.lords[i].id)
for (let i = 14; i < 28; ++i)
ui.card_cc[i] = build_div(null, "card cc lancaster " + data.lords[i].id)
}
// === UPDATE UI ===
let used_cache = {}
let unused_cache = {}
function get_cached_element(className, action, id) {
let key = className
if (action !== undefined)
key += "/" + action + "/" + id
if (!(key in unused_cache)) {
unused_cache[key] = []
used_cache[key] = []
}
if (unused_cache[key].length > 0) {
let elt = unused_cache[key].pop()
used_cache[key].push(elt)
return elt
}
let elt = document.createElement("div")
elt.className = className
used_cache[key].push(elt)
if (action !== undefined)
register_action(elt, action, id)
return elt
}
function restart_cache() {
for (let k in used_cache) {
let u = used_cache[k]
let uu = unused_cache[k]
while (u.length > 0)
uu.push(u.pop())
}
}
function update_current_card_display() {
// TODO: clone card elements instead of using classes
if (typeof view.what === "number" && view.what >= 0) {
ui.command.replaceChildren(ui.cards2[view.what])
} else if ((view.turn & 1) === 0) {
if (player === "Lancaster")
ui.command.replaceChildren(ui.card_aow_lancaster_back)
else
ui.command.replaceChildren(ui.card_aow_york_back)
} else if (view.command < 0) {
if (player === "Lancaster")
ui.command.replaceChildren(ui.card_cc_lancaster_back)
else
ui.command.replaceChildren(ui.card_cc_york_back)
} else {
ui.command.replaceChildren(ui.card_cc[view.command])
}
}
function layout_locale_cylinders(loc, list, dx) {
let [xc, yc] = locale_xy[loc]
let dy = (list.length - 1) * -15
let x = xc + dx
let y = yc + dy
let z = 5
for (let ix of list) {
let e = ui.lord_cylinder[ix]
e.style.top = (y - 23) + "px"
e.style.left = (x - 23) + "px"
e.style.zIndex = z++
y += 30
}
}
function layout_exile_box_cylinders(loc, list, dy) {
let [xc, yc] = locale_xy[loc]
let dx = (list.length - 1) * -23
let x = xc + dx
let y = yc + dy
let z = 5
for (let ix of list) {
let e = ui.lord_cylinder[ix]
e.style.top = (y - 23) + "px"
e.style.left = (x - 23) + "px"
e.style.zIndex = z++
x += 46
}
}
function layout_calendar() {
for (let loc = 1; loc <= 16; ++loc) {
let [cx, cy] = calendar_xy[loc]
let list
list = calendar_layout_lanc[loc]
for (let i = 0; i < list.length; ++i) {
let e = ui.lord_cylinder[list[i]]
let x = cx, y = cy, z = 30 + i
x += 5
y += i * 30 - 2
e.style.top = y + "px"
e.style.left = x + "px"
e.style.zIndex = z
// cylinder is 44x48, exile marker is 39
if (is_lord_in_exile(list[i])) {
e = ui.lord_exile[list[i]]
e.style.top = y + 7 + "px"
e.style.left = x + 3 - 20 + "px"
e.style.zIndex = z + 1
}
}
list = calendar_layout_york[loc]
for (let i = 0; i < list.length; ++i) {
// ui.lord_exile[ix].classList.toggle("hide", !is_lord_in_exile(ix))
let e = ui.lord_cylinder[list[i]]
let x = cx, y = cy, z = 30 + i
x += 50
y += i * 30 - 2
e.style.top = y + "px"
e.style.left = x + "px"
e.style.zIndex = z
// cylinder is 44, exile marker is 39
if (is_lord_in_exile(list[i])) {
e = ui.lord_exile[list[i]]
e.style.top = y + 7 + "px"
e.style.left = x + 3 + 20 + "px"
e.style.zIndex = z - 1
}
}
list = calendar_layout_vassal[loc]
for (let i = 0; i < list.length; ++i) {
let e = list[i]
let x = cx, y = cy, z = 20 - i
let len_lanc = calendar_layout_lanc[loc].length
let len_york = calendar_layout_york[loc].length
if (len_lanc <= len_york) {
y += len_lanc * 30 + 48-30 + 3 + i * 32
x += 7
} else {
y += len_york * 30 + 48-30 + 3 + i * 32
x += 42
}
e.style.top = y + "px"
e.style.left = x + "px"
e.style.zIndex = z
}
}
}
function add_vassal(parent, vassal, lord, routed) {
let elt = ui.vassal_mat[vassal]
elt.classList.toggle("selected", view.vassal === vassal || set_has(view.vassal, vassal))
elt.classList.toggle("action", is_action("vassal", vassal))
parent.appendChild(elt)
}
function add_force(parent, type, lord, routed, first, z) {
let elt
if (routed) {
if (first && is_action(routed_force_action_name[type], lord))
elt = get_cached_element("action unit " + force_class_name[type], routed_force_action_name[type], lord)
else
elt = get_cached_element("unit " + force_class_name[type], routed_force_action_name[type], lord)
} else {
if (first && is_action(force_action_name[type], lord))
elt = get_cached_element("action unit " + force_class_name[type], force_action_name[type], lord)
else
elt = get_cached_element("unit " + force_class_name[type], force_action_name[type], lord)
}
elt.style.zIndex = z
parent.appendChild(elt)
}
function add_asset(parent, type, n, lord, first, z) {
let elt
if (first && is_action(asset_action_name[type], lord))
elt = get_cached_element("action asset " + asset_action_name[type] + " x" + n, asset_action_name[type], lord)
else
elt = get_cached_element("asset " + asset_action_name[type] + " x" + n)
elt.style.zIndex = z
parent.appendChild(elt)
}
function update_forces(parent, a, b, forces, lord_ix, routed) {
parent.replaceChildren()
let z = 5
for (let i = a; i <= b; ++i) {
if (i === VASSAL) {
for_each_vassal_with_lord(lord_ix, v => {
if (view.battle) {
if (set_has(view.battle.routed_vassals, v) === routed)
add_vassal(parent, v, lord_ix, routed)
} else {
if (routed === false)
add_vassal(parent, v, lord_ix, routed)
}
})
} else {
let n = map_get_pack4(forces, lord_ix, i, 0)
for (let k = 0; k < n; ++k) {
add_force(parent, i, lord_ix, routed, k === n-1, z++)
}
if (i > 1) {
z = 5
parent.appendChild(get_cached_element("break"))
}
}
}
}
function update_assets(parent, assets, lord_ix) {
parent.replaceChildren()
let z = 5
for (let i = 0; i < asset_type_count; ++i) {
let n = map_get_pack4(assets, lord_ix, i, 0)
if (asset_type_x34[i]) {
while (n >= 4) {
n -= 4
add_asset(parent, i, 4, lord_ix, n === 0, z++)
}
while (n >= 3) {
n -= 3
add_asset(parent, i, 3, lord_ix, n === 0, z++)
}
}
while (n >= 2) {
n -= 2
add_asset(parent, i, 2, lord_ix, n === 0, z++)
}
while (n >= 1) {
n -= 1
add_asset(parent, i, 1, lord_ix, n === 0, z++)
}
if (i < 2) {
z = 5
parent.appendChild(get_cached_element("break"))
}
}
}
function add_valour(parent, lord) {
let elt
elt = get_cached_element("marker valour small")
parent.appendChild(elt)
}
function update_valour(lord, parent, battle) {
parent.replaceChildren()
if (!battle) return
let n = map_get(battle.valour, lord, 0)
for (let i = 0; i < n; i++)
add_valour(parent, lord)
}
function update_lord_mat(ix) {
ui.mat[ix].classList.remove("ravine")
ui.mat[ix].classList.remove("engaged")
if (view.battle) {
if (view.battle.ravine === ix)
ui.mat[ix].classList.add("ravine")
if (view.engaged) {
for (let p of view.engaged) {
if (ix === view.battle.array[p])
ui.mat[ix].classList.add("engaged")
}
}
}
if (view.reveal & (1 << ix)) {
ui.mat[ix].classList.remove("hidden")
update_assets(ui.assets[ix], view.pieces.assets, ix)
update_forces(ui.retinue[ix], 0, 1, view.pieces.forces, ix, false)
update_forces(ui.routed_retinue[ix], 0, 1, view.pieces.routed, ix, true)
update_forces(ui.troops[ix], 2, 6, view.pieces.forces, ix, false)
update_forces(ui.routed_troops[ix], 2, 6, view.pieces.routed, ix, true)
ui.lord_feed[ix].classList.toggle("hide", count_lord_all_forces(ix) <= 6)
if (get_lord_locale(LORD_HENRY_VI) === CAPTURE_OF_THE_KING + ix)
ui.marker_area[ix].appendChild(ui.captured_king)
} else {
ui.mat[ix].classList.add("hidden")
ui.assets[ix].replaceChildren()
ui.retinue[ix].replaceChildren()
ui.routed_retinue[ix].replaceChildren()
ui.troops[ix].replaceChildren()
ui.routed_troops[ix].replaceChildren()
ui.lord_moved1[ix].classList.add("hide")
ui.lord_moved2[ix].classList.add("hide")
ui.lord_feed[ix].classList.add("hide")
if (get_lord_locale(LORD_HENRY_VI) === CAPTURE_OF_THE_KING + ix)
ui.marker_area[ix].appendChild(ui.captured_king)
}
let m = get_lord_moved(ix)
ui.lord_moved1[ix].classList.toggle("hide", is_levy_phase() || (m !== 1 && m !== 2))
ui.lord_moved2[ix].classList.toggle("hide", is_levy_phase() || (m !== 2))
ui.lord_fled[ix].classList.toggle("hide", view.battle === undefined || !set_has(view.battle.fled, ix))
update_valour(ix, ui.valour_area[ix], view.battle)
}
function update_lord(ix) {
let locale = get_lord_locale(ix)
if (locale < 0 || locale > CALENDAR_EXILE + 16) {
ui.lord_cylinder[ix].classList.add("hide")
ui.mat[ix].classList.remove("action")
return
}
if (locale < CALENDAR) {
if (is_york_lord(ix))
locale_layout_york[locale].push(ix)
else
locale_layout_lanc[locale].push(ix)
ui.lord_cylinder[ix].classList.remove("hide")
update_lord_mat(ix)
ui.lord_exile[ix].classList.add("hide")
} else if (locale <= CALENDAR_EXILE + 16) {
let t = locale > CALENDAR_EXILE ? locale - CALENDAR_EXILE : locale - CALENDAR
if (is_york_lord(ix))
calendar_layout_york[t].push(ix)
else
calendar_layout_lanc[t].push(ix)
ui.lord_cylinder[ix].classList.remove("hide")
ui.lord_exile[ix].classList.toggle("hide", !is_lord_in_exile(ix))
}
ui.mat_card[ix].classList.toggle("action", is_action("lord", ix))
ui.lord_cylinder[ix].classList.toggle("action", is_action("lord", ix))
ui.lord_cylinder[ix].classList.toggle("selected", is_lord_selected(ix))
ui.mat[ix].classList.toggle("selected", is_lord_selected(ix))
ui.lord_cylinder[ix].classList.toggle("command", is_lord_command(ix))
ui.mat[ix].classList.toggle("command", is_lord_command(ix))
}
function update_locale(loc) {
if (data.locales[loc].type === "exile_box") {
layout_exile_box_cylinders(loc, locale_layout_lanc[loc], 0)
layout_exile_box_cylinders(loc, locale_layout_york[loc], 30)
} else {
layout_locale_cylinders(loc, locale_layout_lanc[loc], -30)
layout_locale_cylinders(loc, locale_layout_york[loc], 30)
}
ui.locale[loc].classList.toggle("action", is_action("locale", loc) || is_action("laden_march", loc))
ui.locale[loc].classList.toggle("selected", view.where === loc || set_has(view.where, loc))
ui.locale[loc].classList.toggle("supply_path", !!(view.supply && view.supply[0] === loc))
ui.locale[loc].classList.toggle("supply_source", !!(view.supply && view.supply[1] === loc))
if (ui.locale_name[loc]) {
ui.locale_name[loc].classList.toggle("action", is_action("locale", loc) || is_action("laden_march", loc))
}
if (set_has(view.pieces.exhausted, loc))
ui.depleted[loc].className = "marker small exhausted"
else if (set_has(view.pieces.depleted, loc))
ui.depleted[loc].className = "marker small depleted"
else
ui.depleted[loc].className = "hide"
if (data.locales[loc].type === "exile_box") {
if (set_has(view.pieces.favourl, loc))
ui.locale_markers_rose[loc].className = "marker circle exile_rose lancaster"
else if (set_has(view.pieces.favoury, loc))
ui.locale_markers_rose[loc].className = "marker circle exile_rose york"
else
ui.locale_markers_rose[loc].className = "hide"
} else {
if (set_has(view.pieces.favourl, loc))
ui.locale_markers_rose[loc].className = "marker small rose lancaster"
else if (set_has(view.pieces.favoury, loc))
ui.locale_markers_rose[loc].className = "marker small rose york"
else
ui.locale_markers_rose[loc].className = "hide"
}
if (loc === LOC_LONDON) {
if (set_has(view.pieces.favoury, LONDON_FOR_YORK))
ui.london_for_york.className = "marker small rose york"
else
ui.london_for_york.className = "hide"
}
}
function update_plan() {
if (view.plan) {
let is_planning = view.actions && view.actions.plan
ui.plan_panel.classList.remove("hide")
for (let i = 0; i < 7; ++i) {
if (i < view.plan.length) {
let lord = view.plan[i]
if (lord < 0) {
if (player === "York")
ui.plan_cards[i].className = "card cc york pass"
else
ui.plan_cards[i].className = "card cc lancaster pass"
} else {
if (lord < 14)
ui.plan_cards[i].className = "card cc york " + data.lords[lord].id
else
ui.plan_cards[i].className = "card cc lancaster " + data.lords[lord].id
}
} else if (is_planning && i < max_plan_length()) {
if (player === "York")
ui.plan_cards[i].className = "card cc york back"
else
ui.plan_cards[i].className = "card cc lancaster back"
} else {
ui.plan_cards[i].className = "hide"
}
}
if (is_planning) {
ui.plan_actions.classList.remove("hide")
for (let lord = 0; lord < 28; ++lord) {
if (is_lord_in_game(lord)) {
ui.plan_action_cards[lord].classList.remove("hide")
if (is_action("plan", lord)) {
ui.plan_action_cards[lord].classList.add("action")
ui.plan_action_cards[lord].classList.remove("disabled")
} else {
ui.plan_action_cards[lord].classList.remove("action")
ui.plan_action_cards[lord].classList.add("disabled")
}
} else {
ui.plan_action_cards[lord].classList.add("hide")
}
}
if (is_action("plan", -1)) {
ui.plan_action_pass_york.classList.add("action")
ui.plan_action_pass_york.classList.remove("disabled")
ui.plan_action_pass_lancaster.classList.add("action")
ui.plan_action_pass_lancaster.classList.remove("disabled")
} else {
ui.plan_action_pass_york.classList.remove("action")
ui.plan_action_pass_york.classList.add("disabled")
ui.plan_action_pass_lancaster.classList.remove("action")
ui.plan_action_pass_lancaster.classList.add("disabled")
}
} else {
ui.plan_actions.classList.add("hide")
}
} else {
ui.plan_panel.classList.add("hide")
}
}
function update_cards() {
for (let c = 0; c <= last_aow_card; ++c) {
let elt = ui.cards[c]
elt.classList.toggle("selected", c === view.what)
elt.classList.toggle("action", is_action("card", c))
}
if (view.arts_of_war) {
ui.arts_of_war_panel.classList.remove("hide")
ui.arts_of_war.replaceChildren()
for (let c of view.arts_of_war)
ui.arts_of_war.appendChild(ui.cards[c])
} else {
ui.arts_of_war_panel.classList.add("hide")
}
if (view.events.length > 0) {
ui.events_panel.classList.remove("hide")
ui.events.replaceChildren()
for (let c of view.events)
ui.events.appendChild(ui.cards[c])
} else {
ui.events_panel.classList.add("hide")
}
if (view.hand && view.hand.length > 0) {
ui.hand_panel.classList.remove("hide")
ui.hand.replaceChildren()
if (view.hand) {
for (let c of view.hand)
ui.hand.appendChild(ui.cards[c])
}
} else {
ui.hand_panel.classList.add("hide")
}
for (let ix = 0; ix < data.lords.length; ++ix) {
ui.mat_caps[ix].replaceChildren()
if (view.reveal & (1 << ix)) {
let c = get_lord_capability(ix, 0)
if (c >= 0)
ui.mat_caps[ix].appendChild(ui.cards[c])
c = get_lord_capability(ix, 1)
if (c >= 0)
ui.mat_caps[ix].appendChild(ui.cards[c])
}
}
}
function update_battle() {
let array = view.battle.array
for (let i = 0; i < array.length; ++i) {
let lord = array[i]
ui.battle_grid_array[i].replaceChildren()
if (lord >= 0)
ui.battle_grid_array[i].appendChild(ui.mat[lord])
ui.battle_grid_array[i].classList.toggle("action", is_action("array", i))
}
}
function update_court() {
let ycourt_panel = (player === "Lancaster") ? ui.court2_panel : ui.court1_panel
let lcourt_panel = (player === "Lancaster") ? ui.court1_panel : ui.court2_panel
let ycourt_hdr = (player === "Lancaster") ? ui.court2_header : ui.court1_header
let lcourt_hdr = (player === "Lancaster") ? ui.court1_header : ui.court2_header
let ycourt = (player === "Lancaster") ? ui.court2 : ui.court1
let lcourt = (player === "Lancaster") ? ui.court1 : ui.court2
ycourt_panel.className = "panel court_panel york"
lcourt_panel.className = "panel court_panel lancaster"
ycourt_hdr.textContent = "York Lords"
lcourt_hdr.textContent = "Lancaster Lords"
ycourt.replaceChildren()
lcourt.replaceChildren()
for (let lord = first_york_lord; lord <= last_york_lord; ++lord)
if (!is_lord_in_battle(lord) && is_lord_on_map(lord))
ycourt.appendChild(ui.mat[lord])
for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; ++lord)
if (!is_lord_in_battle(lord) && is_lord_on_map(lord))
lcourt.appendChild(ui.mat[lord])
}
function update_vassals() {
for (let v = first_vassal; v <= last_vassal; v++) {
let loc = get_vassal_lord(v)
let srv = get_vassal_service(v)
if (loc === VASSAL_OUT_OF_PLAY) {
// not present
ui.vassal_cal[v].classList.add("hide")
if (ui.vassal_map[v]) {
ui.vassal_map[v].classList.add("hide")
}
} else if (loc === VASSAL_READY) {
// on map
ui.vassal_cal[v].classList.add("hide")
if (ui.vassal_map[v]) {
ui.vassal_map[v].classList.remove("hide")
ui.vassal_map[v].classList.toggle("action", is_action("vassal", v))
ui.vassal_map[v].classList.toggle("selected", v === view.vassal || set_has(view.vassal, v))
}
} else {
// on calendar (+ maybe on lord mat)
if (data.vassals[v].service > 0) {
ui.vassal_cal[v].classList.remove("hide")
ui.vassal_cal[v].classList.toggle("action", is_action("vassal", v))
ui.vassal_cal[v].classList.toggle("selected", v === view.vassal || set_has(view.vassal, v))
calendar_layout_vassal[srv].push(ui.vassal_cal[v])
} else {
// special vassal not on calendar
ui.vassal_cal[v].classList.add("hide")
}
if (ui.vassal_map[v]) {
ui.vassal_map[v].classList.add("hide")
}
}
}
}
var track_offset = new Array(46).fill(0)
function show_track_marker(elt, pos) {
let n = track_offset[pos]++
let x = track_xy[pos][0] - 25
let y = track_xy[pos][1] - 25
if (pos <= 10) {
y -= 51 * n
} else if (pos < 24) {
y -= 24 * n
} else if (pos > 26) {
x -= 51 * n
} else {
x -= 24 * n
y -= 24 * n
}
elt.style.left = x + "px"
elt.style.top = y + "px"
}
function list_has_friendly_lords(list) {
for (let lord of list) {
if (player === "York" && is_york_lord(lord))
return true
if (player === "Lancaster" && is_lancaster_lord(lord))
return true
}
return false
}
function on_update() {
restart_cache()
switch (player) {
case "York":
ui.favicon.href = "favicons/favicon_york.png"
break
case "Lancaster":
ui.favicon.href = "favicons/favicon_henry_vi.png"
break
default:
ui.favicon.href = "favicons/favicon_warwick.png"
break
}
for (let i = 0; i <= 16; ++i) {
calendar_layout_york[i].length = 0
calendar_layout_lanc[i].length = 0
calendar_layout_vassal[i].length = 0
}
for (let i = 0; i < data.locales.length; ++i) {
locale_layout_york[i].length = 0
locale_layout_lanc[i].length = 0
}
track_offset.fill(0)
for (let ix = 0; ix < data.lords.length; ++ix) {
ui.seat[ix].classList.toggle("hide", !is_lord_in_game(ix))
if (get_lord_locale(ix) < 0) {
ui.lord_cylinder[ix].classList.add("hide")
} else {
ui.lord_cylinder[ix].classList.remove("hide")
update_lord(ix)
}
}
update_vassals()
layout_calendar()
for (let loc = 0; loc < data.locales.length; ++loc)
update_locale(loc)
if (view.battle) {
let { x, y, w, h } = data.locales[view.battle.where].box
ui.battle.className = "marker circle battle"
ui.battle.style.left = ((x+w/2)|0) - 27 + "px"
ui.battle.style.top = y + h - 27 + "px"
} else {
ui.battle.className = "hide"
}
update_current_card_display()
if (view.turn & 1) {
ui.turn.className = `marker circle turn campaign`
} else {
ui.turn.className = `marker circle turn levy`
}
ui.turn.style.left = (calendar_xy[view.turn >> 1][0] + 91 - 52) + "px"
ui.turn.style.top = (calendar_xy[view.turn >> 1][1] + 94) + "px"
if (view.end < 16) {
ui.end.style.display = null
ui.end.style.left = (calendar_xy[view.end][0] + 91 - 52) + "px"
ui.end.style.top = (calendar_xy[view.end][1] + 94) + "px"
} else {
ui.end.style.display = "none"
}
ui.held_york.textContent = `${view.held_y} Held`
ui.held_lancaster.textContent = `${view.held_l} Held`
if (view.victory_check <= 45) {
ui.victory_check.style.display = null
show_track_marker(ui.victory_check, view.victory_check)
} else {
ui.victory_check.style.display = "none"
}
let towns = count_favour(TOWN)
ui.towns.classList.toggle("york", towns < 0)
ui.towns.classList.toggle("lancaster", towns >= 0)
show_track_marker(ui.towns, Math.abs(towns))
let cities = count_favour(CITY)
ui.cities.classList.toggle("york", cities < 0)
ui.cities.classList.toggle("lancaster", cities >= 0)
show_track_marker(ui.cities, Math.abs(cities))
let fortresses = count_favour(FORTRESS)
ui.fortresses.classList.toggle("york", fortresses < 0)
ui.fortresses.classList.toggle("lancaster", fortresses >= 0)
show_track_marker(ui.fortresses, Math.abs(fortresses))
ui.influence.classList.toggle("york", view.influence < 0)
ui.influence.classList.toggle("lancaster", view.influence >= 0)
show_track_marker(ui.influence, Math.abs(view.influence))
update_plan()
update_cards()
if (view.battle) {
ui.battle_panel.classList.remove("hide")
ui.battle_header.textContent = "Battle at " + data.locales[view.battle.where].name
if (view.battle.attacker === player) {
ui.battle_grid.className = "attacker"
} else {
ui.battle_grid.className = "defender"
}
update_battle()
} else {
ui.battle_panel.classList.add("hide")
}
update_court()
// QUESTIONS
action_button("favour", "Favour")
action_button("influence", "Influence")
action_button("stronghold", "Stronghold")
action_button("port", "Port")
// CAPABILITIES / EVENTS
action_button("add_men_at_arms", "Add Men at Arms")
action_button("add_militia", "Add Militia")
action_button("add_militia2", "Add 2 Militia")
action_button("agitators", "Agitators")
action_button("commission_of_array", "Commission of Array")
action_button("exile", "Exile")
action_button("exile_pact", "Exile Pact")
action_button("final_charge", "Final Charge")
action_button("heralds", "Heralds")
action_button("levy_beloved_warwick", "Beloved Warwick")
action_button("levy_irishmen", "Irishmen")
action_button("loyalty_and_trust", "Loyalty and Trust")
action_button("merchants", "Merchants")
action_button("regroup", "Regroup")
action_button("richard_iii", "Richard III")
action_button("soldiers_of_fortune", "Soldiers of Fortune")
action_button("vanguard", "Vanguard")
// MARCH
action_button("march", "March")
action_button("approach", "Approach")
action_button("intercept", "Intercept")
action_button("battle", "Battle")
// ARTS OF WAR
action_button("play", "Play")
action_button("hold", "Hold")
action_button("discard", "Discard")
// LEVY
action_button("levy_troops", "Troops")
action_button("take_ship", "Ship")
action_button("take_prov", "Provender")
action_button("take_cart", "Cart")
action_button("take_all", "Take All")
action_button("capability", "Capability")
// CAMPAIGN
action_button("supply", "Supply")
action_button("sail", "Sail")
action_button("forage", "Forage")
action_button("tax", "Tax")
// LEVY & CAMPAIGN
action_button("parley", "Parley")
// PAY/FEED/PILLAGE
action_button("pay_all", "Pay All")
action_button("pay", "Pay")
action_button("pillage", "Pillage")
action_button("disband", "Disband")
// INFLUENCE CHECK
action_button_with_argument("check", 2, "Check +2")
action_button_with_argument("check", 1, "Check +1")
action_button_with_argument("check", 0, "Check")
action_button("end_array", "End Array")
action_button("end_battle_round", "End Round")
action_button("end_command", "End Command")
action_button("end_discard", "End Discard")
action_button("end_feed", "End Feed")
action_button("end_muster", "End Muster")
action_button("end_pay", "End Pay")
action_button("end_plan", "End Plan")
action_button("end_spoils", "End Spoils")
action_button("roll", "Roll")
action_button("pass", "Pass")
action_button("done", "Done")
action_button("undo", "Undo")
}
// === LOG ===
function on_focus_card_tip(c) {
ui.command.replaceChildren(ui.cards2[c])
}
function on_blur_card_tip() {
update_current_card_display()
}
function sub_card_capability(match, p1) {
let x = p1 | 0
return `${data.cards[x].capability}`
}
function sub_card_event(match, p1) {
let x = p1 | 0
return `${data.cards[x].event}`
}
function on_focus_locale_tip(loc) {
ui.locale[loc].classList.add("tip")
if (ui.locale_name[loc])
ui.locale_name[loc].classList.add("tip")
}
function on_blur_locale_tip(loc) {
ui.locale[loc].classList.remove("tip")
if (ui.locale_name[loc])
ui.locale_name[loc].classList.remove("tip")
}
function on_click_locale_tip(loc) {
ui.locale[loc].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
}
function on_click_lord_tip(lord) {
ui.mat[lord].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
}
function sub_locale_name(match, p1) {
let x = p1 | 0
let n = data.locales[x].name
return `${n}`
}
function sub_lord_name(match, p1) {
let x = p1 | 0
let n = data.lords[x].short_name
return `${n}`
}
function sub_vassal_name(match, x) {
let n = data.vassals[x].name
return `${n}`
}
const ICONS_SVG = {
B0: '',
B1: '',
B2: '',
B3: '',
B4: '',
B5: '',
B6: '',
W0: '',
W1: '',
W2: '',
W3: '',
W4: '',
W5: '',
W6: '',
}
const ICONS_HTM = {
B0: '0',
B1: '1',
B2: '2',
B3: '3',
B4: '4',
B5: '5',
B6: '6',
W0: '0',
W1: '1',
W2: '2',
W3: '3',
W4: '4',
W5: '5',
W6: '6',
}
const ICONS_TXT = {
B0: "\u25cf",
B1: "\u2776",
B2: "\u2777",
B3: "\u2778",
B4: "\u2779",
B5: "\u277A",
B6: "\u277B",
W0: "\u25cb",
W1: "\u2460",
W2: "\u2461",
W3: "\u2462",
W4: "\u2463",
W5: "\u2464",
W6: "\u2465",
}
function sub_icon(match) {
return ICONS_TXT[match]
}
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"
}
text = text.replace(/&/g, "&")
text = text.replace(//g, ">")
text = text.replace(/C(\d+)/g, sub_card_capability)
text = text.replace(/E(\d+)/g, sub_card_event)
text = text.replace(/L(\d+)/g, sub_lord_name)
text = text.replace(/S(\d+)/g, sub_locale_name)
text = text.replace(/V(\d+)/g, sub_vassal_name)
text = text.replace(/\b[BW]\d\b/g, sub_icon)
if (text.match(/^\.h1/)) {
text = text.substring(4)
p.className = "h1"
} else if (text.match(/^\.h2y/)) {
text = text.substring(5)
p.className = "h2 york"
} else if (text.match(/^\.h2l/)) {
text = text.substring(5)
p.className = "h2 lancaster"
} else if (text.match(/^\.h2/)) {
text = text.substring(4)
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"
}
p.innerHTML = text
return p
}
build_map()
scroll_with_middle_mouse("main")
// === COMMON LIBRARY ===
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
}
function map_get_pack4(map, lord, k) {
return pack4_get(map_get(map, lord, 0), k)
}
function map2_get(map, x, y, v) {
return map_get(map, (x << 1) + y, v)
}
function set_has(set, item) {
if (!Array.isArray(set))
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 pack1_get(word, n) {
return (word >>> n) & 1
}
function pack2_get(word, n) {
n = n << 1
return (word >>> n) & 3
}
function pack4_get(word, n) {
n = n << 2
return (word >>> n) & 15
}