"use strict"
function scroll_into_view_if_mobile(e) {
// if ("ontouchstart" in window)
if (window.innerWidth <= 800)
e.scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
}
function set_has(set, item) {
let a = 0
let b = set.length - 1
while (a <= b) {
let m = (a + b) >> 1
let x = set[m]
if (item < x)
b = m - 1
else if (item > x)
a = m + 1
else
return true
}
return false
}
const FRANKS = "Franks"
const SARACENS = "Saracens"
const ENEMY = { Saracens: "Franks", Franks: "Saracens" }
const NOWHERE = 0
const DEAD = 1
const F_POOL = 2
const S_POOL = 3
const SEA = 4
const ENGLAND = 5
const FRANCE = 6
const GERMANIA = 7
const SHIELD_NAMES = {}
SHIELD_NAMES[town_index["Antioch"]] = "Bohemond, Templars, Turcopoles"
SHIELD_NAMES[town_index["Latakia"]] = "Bohemond"
SHIELD_NAMES[town_index["Sa\xf4ne"]] = "Josselin"
SHIELD_NAMES[town_index["Margat"]] = "Hospitallers"
SHIELD_NAMES[town_index["Krak"]] = "Hospitallers"
SHIELD_NAMES[town_index["Tartus"]] = "Templars"
SHIELD_NAMES[town_index["Tripoli"]] = "Bohemond, Raymond"
SHIELD_NAMES[town_index["Beirut"]] = "Turcopoles, King Guy"
SHIELD_NAMES[town_index["Sidon"]] = "Reynald (Sidon)"
SHIELD_NAMES[town_index["Beaufort"]] = "Reynald (Sidon)"
SHIELD_NAMES[town_index["Tyre"]] = "Conrad, King Guy"
SHIELD_NAMES[town_index["Acre"]] = "Turcopoles, Hospitallers, King Guy"
SHIELD_NAMES[town_index["Tiberias"]] = "Turcopoles, Raymond"
SHIELD_NAMES[town_index["Baisan"]] = "Hospitallers"
SHIELD_NAMES[town_index["Caesarea"]] = "Walter"
SHIELD_NAMES[town_index["Nablus"]] = "Balian"
SHIELD_NAMES[town_index["Amman"]] = "Templars"
SHIELD_NAMES[town_index["Jaffa"]] = "King Guy"
SHIELD_NAMES[town_index["Jerusalem"]] = "King Guy, Hospitallers, Templars"
SHIELD_NAMES[town_index["Ascalon"]] = "Balian, King Guy"
SHIELD_NAMES[town_index["Hebron"]] = "King Guy"
SHIELD_NAMES[town_index["Gaza"]] = "Templars"
SHIELD_NAMES[town_index["Kerak"]] = "Reynald (Kerak)"
SHIELD_NAMES[town_index["Egypt"]] = "Saladin, Qara-Qush, Yuzpah"
SHIELD_NAMES[town_index["Aleppo"]] = "Saladin, Sanjar, Zangi"
SHIELD_NAMES[town_index["Ashtera"]] = "Yazkuj"
SHIELD_NAMES[town_index["Artah"]] = "Sulaiman"
SHIELD_NAMES[town_index["Damascus"]] = "Saladin, Keukburi, Al Mashtub"
SHIELD_NAMES[town_index["Homs"]] = "Tuman, Shirkuh"
SHIELD_NAMES[town_index["Zerdana"]] = "Jurdik"
SHIELD_NAMES[town_index["Baalbek"]] = "Bahram"
SHIELD_NAMES[town_index["Hama"]] = "Taqi al Din"
SHIELD_NAMES[town_index["Banyas"]] = "Qaimaz"
const KINGDOM = {
"Syria": "Syria",
"Jerusalem": "Kingdom of Jerusalem",
"Antioch": "Principality of Antioch",
"Tripoli": "County of Tripoli",
}
const VICTORY_TOWNS = [
town_index["Aleppo"],
town_index["Damascus"],
town_index["Egypt"],
town_index["Antioch"],
town_index["Tripoli"],
town_index["Acre"],
town_index["Jerusalem"]
]
let label_layout = window.localStorage['crusader-rex/label-layout'] || 'spread'
function set_spread_layout() {
label_layout = 'spread'
window.localStorage['crusader-rex/label-layout'] = label_layout
update_map()
}
function set_stack_layout() {
label_layout = 'stack'
window.localStorage['crusader-rex/label-layout'] = label_layout
update_map()
}
function toggle_blocks() {
document.getElementById("map").classList.toggle("hide_blocks")
}
let ui = {
cards: [],
card_backs: [],
towns: [],
blocks: [],
battle_menu: [],
battle_block: [],
present: new Set(),
}
function remember_position(e) {
if (e.classList.contains("show")) {
let rect = e.getBoundingClientRect()
e.my_parent = true
e.my_x = rect.x
e.my_y = rect.y
} else {
e.my_parent = false
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: 250, easing: "ease" }
)
}
} else {
e.animate(
[
{ opacity: 0 },
{ opacity: 1 }
],
{ duration: 250, easing: "ease" }
)
}
}
}
function on_focus_space_tip(x) {
ui.towns[x].classList.add("tip")
}
function on_blur_space_tip(x) {
ui.towns[x].classList.remove("tip")
}
function on_click_space_tip(x) {
ui.towns[x].scrollIntoView({ block:"center", inline:"center", behavior:"smooth" })
}
function sub_space_name(match, p1, offset, string) {
let x = p1 | 0
let n = TOWNS[x].name
return `${n}`
}
function on_log(text) {
let p = document.createElement("div")
if (text.match(/^>/)) {
text = text.substring(1)
p.className = "i"
}
text = text.replace(/&/g, "&")
text = text.replace(//g, ">")
text = text.replace(/\u2192 /g, "\u2192\xa0")
text = text.replace(/^([A-Z]):/, ' $1 ')
text = text.replace(/#(\d+)/g, sub_space_name)
if (text.match(/^\.h1 /))
p.className = 'h1', text = text.substring(4)
if (text.match(/^\.h2 F/))
p.className = 'h2 F', text = text.substring(4)
if (text.match(/^\.h2 S/))
p.className = 'h2 S', text = text.substring(4)
if (text.match(/^\.h3 /))
p.className = 'h3', text = text.substring(4)
if (text.match(/^\.h4 /))
p.className = 'h4', text = text.substring(4)
p.innerHTML = text
return p
}
function on_focus_town(evt) {
let where = evt.target.town
let text = TOWNS[where].name
if (where in SHIELD_NAMES)
text += " \u2014 " + SHIELD_NAMES[where]
let kingdom = KINGDOM[TOWNS[where].region]
if (kingdom)
text += " \u2014 " + kingdom
if (VICTORY_TOWNS.includes(where))
text += " \u2014 1 VP"
document.getElementById("status").textContent = text
}
function on_blur_town(evt) {
document.getElementById("status").textContent = ""
}
function on_click_town(evt) {
let where = evt.target.town
send_action('town', where) || send_action('townb', where)
}
const STEP_TEXT = [ 0, "I", "II", "III", "IIII" ]
const HEIR_TEXT = [ 0, '\u00b9', '\u00b2', '\u00b3', '\u2074', '\u2075' ]
function block_name(who) { return BLOCKS[who].name; }
function block_home(who) { return BLOCKS[who].home; }
function block_owner(who) { return BLOCKS[who].owner; }
function on_focus_map_block(evt) {
let info = BLOCKS[evt.target.block]
let where = view.location[evt.target.block]
if ((info.owner === player || info.owner === "Assassins") && where !== S_POOL && where !== F_POOL) {
let text = info.name + " "
if (info.move)
text += info.move + "-"
text += STEP_TEXT[info.steps] + "-" + info.initiative + info.fire_power
document.getElementById("status").textContent = text
} else {
document.getElementById("status").textContent = info.owner
}
}
function on_blur_map_block(evt) {
document.getElementById("status").textContent = ""
}
function on_click_map_block(evt) {
let b = evt.target.block
if (!view.battle)
send_action('block', b)
}
function on_focus_battle_block(evt) {
let b = evt.target.block
let msg
if (!evt.target.classList.contains("known")) {
if (block_owner(b) === FRANKS)
msg = "Franks"
else if (block_owner(b) === SARACENS)
msg = "Saracens"
} else {
msg = block_name(b)
}
if (view.actions && view.actions.fire && view.actions.fire.includes(b))
msg = "Fire with " + msg
else if (view.actions && view.actions.storm && view.actions.storm.includes(b))
msg = "Storm with " + msg
else if (view.actions && view.actions.sally && view.actions.sally.includes(b))
msg = "Sally with " + msg
else if (view.actions && view.actions.withdraw && view.actions.withdraw.includes(b))
msg = "Withdraw with " + msg
else if (view.actions && view.actions.hit && view.actions.hit.includes(b))
msg = "Take hit on " + msg
document.getElementById("status").textContent = msg
}
function on_blur_battle_block(evt) {
document.getElementById("status").textContent = ""
}
function on_click_battle_block(evt) {
let b = evt.target.block
send_action('block', b)
}
function on_focus_fire(evt) {
document.getElementById("status").textContent =
"Fire with " + block_name(evt.target.block)
}
function on_focus_retreat(evt) {
if (view.battle.storming.includes(evt.target.block))
document.getElementById("status").textContent =
"Withdraw with " + block_name(evt.target.block)
else
document.getElementById("status").textContent =
"Retreat with " + block_name(evt.target.block)
}
function on_focus_harry(evt) {
document.getElementById("status").textContent =
"Harry with " + block_name(evt.target.block)
}
function on_focus_charge(evt) {
document.getElementById("status").textContent =
"Charge with " + block_name(evt.target.block)
}
function on_focus_withdraw(evt) {
document.getElementById("status").textContent =
"Withdraw with " + block_name(evt.target.block)
}
function on_focus_storm(evt) {
document.getElementById("status").textContent =
"Storm with " + block_name(evt.target.block)
}
function on_focus_sally(evt) {
document.getElementById("status").textContent =
"Sally with " + block_name(evt.target.block)
}
function on_focus_hit(evt) {
document.getElementById("status").textContent =
"Take hit on " + block_name(evt.target.block)
}
function on_blur_battle_button(evt) {
document.getElementById("status").textContent = ""
}
function on_click_hit(evt) { send_action('hit', evt.target.block); }
function on_click_fire(evt) { send_action('fire', evt.target.block); }
function on_click_retreat(evt) { send_action('retreat', evt.target.block); }
function on_click_charge(evt) { send_action('charge', evt.target.block); }
function on_click_harry(evt) { send_action('harry', evt.target.block); }
function on_click_withdraw(evt) { send_action('withdraw', evt.target.block); }
function on_click_storm(evt) { send_action('storm', evt.target.block); }
function on_click_sally(evt) { send_action('sally', evt.target.block); }
function on_click_card(evt) {
let c = evt.target.id.split("+")[1] | 0
send_action('play', c)
}
function build_battle_button(menu, b, c, click, enter, img_src) {
let img = new Image()
img.draggable = false
img.classList.add("action")
img.classList.add(c)
img.setAttribute("src", img_src)
img.addEventListener("click", click)
img.addEventListener("mouseenter", enter)
img.addEventListener("mouseleave", on_blur_battle_button)
img.block = b
menu.appendChild(img)
}
function battle_block_class_name(block) {
return `block block_${block.image} ${block.owner}`
}
function build_battle_block(b, block) {
let element = document.createElement("div")
element.className = battle_block_class_name(block)
element.addEventListener("mouseenter", on_focus_battle_block)
element.addEventListener("mouseleave", on_blur_battle_block)
element.addEventListener("click", on_click_battle_block)
element.block = b
ui.battle_block[b] = element
let menu_list = document.createElement("div")
menu_list.className = "battle_menu_list"
build_battle_button(menu_list, b, "hit",
on_click_hit, on_focus_hit,
"/images/cross-mark.svg")
build_battle_button(menu_list, b, "charge",
on_click_charge, on_focus_charge,
"/images/mounted-knight.svg")
build_battle_button(menu_list, b, "fire",
on_click_fire, on_focus_fire,
"/images/pointy-sword.svg")
build_battle_button(menu_list, b, "harry",
on_click_harry, on_focus_harry,
"/images/arrow-flights.svg")
build_battle_button(menu_list, b, "retreat",
on_click_retreat, on_focus_retreat,
"/images/flying-flag.svg")
build_battle_button(menu_list, b, "withdraw",
on_click_withdraw, on_focus_withdraw,
"/images/stone-tower.svg")
build_battle_button(menu_list, b, "storm",
on_click_storm, on_focus_storm,
"/images/siege-tower.svg")
build_battle_button(menu_list, b, "sally",
on_click_sally, on_focus_sally,
"/images/doorway.svg")
let menu = document.createElement("div")
menu.className = "battle_menu"
menu.appendChild(element)
menu.appendChild(menu_list)
menu.block = b
ui.battle_menu[b] = menu
}
function build_map_block(b, block) {
let element = document.createElement("div")
element.classList.add("block")
element.classList.add("known")
element.classList.add(BLOCKS[b].owner)
element.classList.add("block_" + block.image)
element.addEventListener("mouseenter", on_focus_map_block)
element.addEventListener("mouseleave", on_blur_map_block)
element.addEventListener("click", on_click_map_block)
element.block = b
return element
}
function build_town(t, town) {
let element = document.createElement("div")
element.town = t
element.classList.add("town")
element.addEventListener("mouseenter", on_focus_town)
element.addEventListener("mouseleave", on_blur_town)
element.addEventListener("click", on_click_town)
ui.towns_element.appendChild(element)
return element
}
function build_map() {
let element
ui.blocks_element = document.getElementById("blocks")
ui.offmap_element = document.getElementById("offmap")
ui.towns_element = document.getElementById("towns")
for (let c = 1; c <= 27; ++c) {
ui.cards[c] = document.getElementById("card+"+c)
ui.cards[c].addEventListener("click", on_click_card)
}
for (let c = 1; c <= 6; ++c)
ui.card_backs[c] = document.getElementById("back+"+c)
for (let t = SEA; t < TOWNS.length; ++t) {
let town = TOWNS[t]
let name = town.name
if (t === SEA) {
element = document.getElementById("svgmap").getElementById("sea")
element.town = SEA
element.addEventListener("mouseenter", on_focus_town)
element.addEventListener("mouseleave", on_blur_town)
element.addEventListener("click", on_click_town)
ui.towns[t] = element
} else {
element = ui.towns[t] = build_town(t, town)
let xo = Math.round(element.offsetWidth/2)
let yo = Math.round(element.offsetHeight/2)
element.style.left = (town.layout.x - xo) + "px"
element.style.top = (town.layout.y - yo) + "px"
}
}
for (let b = 0; b < BLOCKS.length; ++b) {
let block = BLOCKS[b]
ui.blocks[b] = build_map_block(b, block)
build_battle_block(b, block)
}
}
function update_steps(b, steps, element) {
element.classList.remove("r0")
element.classList.remove("r1")
element.classList.remove("r2")
element.classList.remove("r3")
element.classList.add("r"+(BLOCKS[b].steps - steps))
}
function layout_blocks(location, secret, known) {
if (label_layout === 'stack')
document.getElementById("map").classList.add("stack_layout")
else
document.getElementById("map").classList.remove("stack_layout")
if (label_layout === 'spread' ||
(location === S_POOL || location === F_POOL || location === DEAD ||
location === ENGLAND || location === FRANCE || location === GERMANIA))
layout_blocks_spread(location, secret, known)
else
layout_blocks_stacked(location, secret, known)
}
function layout_blocks_spread(town, north, south) {
let wrap = TOWNS[town].layout.wrap
let rows = []
if ((north.length > wrap || south.length > wrap) || (north.length + south.length <= 3)) {
north = north.concat(south)
south = []
}
function wrap_row(input) {
while (input.length > wrap) {
rows.push(input.slice(0, wrap))
input = input.slice(wrap)
}
if (input.length > 0)
rows.push(input)
}
wrap_row(north)
wrap_row(south)
if (TOWNS[town].layout.minor > 0.5)
rows.reverse()
for (let r = 0; r < rows.length; ++r) {
let cols = rows[r]
for (let c = 0; c < cols.length; ++c)
position_block(town, r, rows.length, c, cols.length, cols[c])
}
}
function position_block(town, row, n_rows, col, n_cols, element) {
let space = TOWNS[town]
let block_size = 60+6
let padding = 4
if (town === ENGLAND || town === FRANCE || town === GERMANIA)
padding = 21
let offset = block_size + padding
let row_size = (n_rows-1) * offset
let col_size = (n_cols-1) * offset
let x = space.layout.x
let y = space.layout.y
if (space.layout.axis === 'X') {
x -= col_size * space.layout.major
y -= row_size * space.layout.minor
x += col * offset
y += row * offset
} else {
y -= col_size * space.layout.major
x -= row_size * space.layout.minor
y += col * offset
x += row * offset
}
element.style.left = ((x - block_size/2)|0)+"px"
element.style.top = ((y - block_size/2)|0)+"px"
}
function layout_blocks_stacked(location, secret, known) {
let s = secret.length
let k = known.length
let both = secret.length > 0 && known.length > 0
let i = 0
while (secret.length > 0)
position_block_stacked(location, i++, (s-1)/2, both ? 1 : 0, secret.shift())
i = 0
while (known.length > 0)
position_block_stacked(location, i++, (k-1)/2, 0, known.shift())
}
function position_block_stacked(location, i, c, k, element) {
let space = TOWNS[location]
let block_size = 60+6
let x = space.layout.x + (i - c) * 16 + k * 12
let y = space.layout.y + (i - c) * 16 - k * 12
element.style.left = ((x - block_size/2)|0)+"px"
element.style.top = ((y - block_size/2)|0)+"px"
}
function show_block(element) {
if (element.parentElement !== ui.blocks_element)
ui.blocks_element.appendChild(element)
}
function hide_block(element) {
if (element.parentElement !== ui.offmap_element)
ui.offmap_element.appendChild(element)
}
function is_known_block(info, who, town) {
if (view.game_over && player === 'Observer')
return true
if (town === DEAD)
return true
if ((town === S_POOL || town === F_POOL) && who !== view.who)
return false
if (info.owner === player || info.owner === "Assassins" || who === view.assassinate)
return true
return false
}
function update_map() {
let layout = {}
document.getElementById("frank_vp").textContent = view.f_vp + " VP"
document.getElementById("saracen_vp").textContent = view.s_vp + " VP"
document.getElementById("timeline").className = "year_" + view.year
if (view.turn < 1)
document.getElementById("turn_info").textContent =
"Year " + view.year
else if (view.turn < 6)
document.getElementById("turn_info").textContent =
"Turn " + view.turn + " of Year " + view.year
else
document.getElementById("turn_info").textContent =
"Winter Turn of Year " + view.year
for (let t = 0; t < TOWNS.length; ++t)
layout[t] = { north: [], south: [] }
for (let b = 0; b < BLOCKS.length; ++b) {
let info = BLOCKS[b]
let element = ui.blocks[b]
let town = view.location[b]
let moved = (set_has(view.moved, b) && b !== view.who) ? " moved" : ""
if (town === DEAD) {
moved = " moved"
}
if (town === NOWHERE) {
town = DEAD
moved = " removed"
}
if (is_known_block(info, b, town)) {
let image = " block_" + info.image
let steps = " r" + (info.steps - view.steps[b])
let known = " known"
element.classList = info.owner + known + " block" + image + steps + moved
} else {
let besieging = ""
if (view.sieges[town] === info.owner) {
if (view.winter_campaign === town)
besieging = " winter_campaign"
else
besieging = " besieging"
}
let jihad = ""
if (view.jihad === town && info.owner === view.p1)
jihad = " jihad"
element.classList = info.owner + " block" + moved + besieging + jihad
}
if (town !== DEAD) {
if (info.owner === FRANKS)
layout[town].north.push(element)
else
layout[town].south.push(element)
}
show_block(element)
}
for (let b = 0; b < BLOCKS.length; ++b) {
let info = BLOCKS[b]
let element = ui.blocks[b]
let town = view.location[b]
if (town === DEAD) {
if (info.owner === FRANKS)
layout[F_POOL].north.unshift(element)
else
layout[S_POOL].south.unshift(element)
}
}
for (let b = 0; b < BLOCKS.length; ++b) {
let info = BLOCKS[b]
let element = ui.blocks[b]
let town = view.location[b]
if (town === NOWHERE) {
if (info.owner === FRANKS)
layout[F_POOL].north.unshift(element)
else
layout[S_POOL].south.unshift(element)
}
}
for (let t = 0; t < TOWNS.length; ++t)
layout_blocks(t, layout[t].north, layout[t].south)
for (let t = SEA; t < TOWNS.length; ++t) {
if (ui.towns[t]) {
ui.towns[t].classList.remove('highlight')
ui.towns[t].classList.remove('muster')
ui.towns[t].classList.remove('bad')
}
}
if (view.actions && view.actions.town) {
for (let t of view.actions.town) {
ui.towns[t].classList.add('highlight')
}
}
if (view.actions && view.actions.townb) {
for (let t of view.actions.townb) {
ui.towns[t].classList.add('highlight')
ui.towns[t].classList.add('bad')
}
}
if (view.muster)
ui.towns[view.muster].classList.add('muster')
if (!view.battle) {
if (view.actions && view.actions.block)
for (let b of view.actions.block)
ui.blocks[b].classList.add('highlight')
}
if (view.who >= 0 && !view.battle)
ui.blocks[view.who].classList.add('selected')
for (let b of view.castle)
ui.blocks[b].classList.add('castle')
}
function update_card_display(element, card, prior_card) {
if (!card && !prior_card) {
element.className = "show card card_back"
} else if (prior_card) {
element.className = "show card prior " + CARDS[prior_card].image
} else {
element.className = "show card " + CARDS[card].image
}
}
function update_cards() {
update_card_display(document.getElementById("frank_card"), view.f_card, view.prior_f_card)
update_card_display(document.getElementById("saracen_card"), view.s_card, view.prior_s_card)
for (let c = 1; c <= 27; ++c) {
let element = ui.cards[c]
if (view.hand.includes(c)) {
element.classList.add("show")
if (view.actions && view.actions.play) {
if (view.actions.play.includes(c)) {
element.classList.add("enabled")
element.classList.remove("disabled")
} else {
element.classList.remove("enabled")
element.classList.add("disabled")
}
} else {
element.classList.remove("enabled")
element.classList.remove("disabled")
}
} else {
element.classList.remove("show")
}
}
let n = view.hand.length
for (let c = 1; c <= 6; ++c)
if (c <= n && player === 'Observer')
ui.card_backs[c].classList.add("show")
else
ui.card_backs[c].classList.remove("show")
}
function compare_blocks(a, b) {
let aa = BLOCKS[a].initiative + BLOCKS[a].fire_power
let bb = BLOCKS[b].initiative + BLOCKS[b].fire_power
if (aa === bb)
return (a < b) ? -1 : (a > b) ? 1 : 0
return (aa < bb) ? -1 : (aa > bb) ? 1 : 0
}
function insert_battle_block(root, node, block) {
for (let i = 0; i < root.children.length; ++i) {
let prev = root.children[i]
if (compare_blocks(prev.block, block) > 0) {
root.insertBefore(node, prev)
return
}
}
root.appendChild(node)
}
function update_battle() {
function fill_cell(name, list, show) {
let cell = document.getElementById(name)
ui.present.clear()
for (let block of list) {
ui.present.add(block)
if (!cell.contains(ui.battle_menu[block]))
insert_battle_block(cell, ui.battle_menu[block], block)
ui.battle_menu[block].className = "battle_menu"
if (view.actions && view.actions.fire && view.actions.fire.includes(block))
ui.battle_menu[block].classList.add('fire')
if (view.actions && view.actions.retreat && view.actions.retreat.includes(block))
ui.battle_menu[block].classList.add('retreat')
if (view.actions && view.actions.harry && view.actions.harry.includes(block))
ui.battle_menu[block].classList.add('harry')
if (view.actions && view.actions.charge && view.actions.charge.includes(block))
ui.battle_menu[block].classList.add('charge')
if (view.actions && view.actions.withdraw && view.actions.withdraw.includes(block))
ui.battle_menu[block].classList.add('withdraw')
if (view.actions && view.actions.storm && view.actions.storm.includes(block))
ui.battle_menu[block].classList.add('storm')
if (view.actions && view.actions.sally && view.actions.sally.includes(block))
ui.battle_menu[block].classList.add('sally')
if (view.actions && view.actions.charge && view.actions.charge.includes(block))
ui.battle_menu[block].classList.add('charge')
if (view.actions && view.actions.treachery && view.actions.treachery.includes(block))
ui.battle_menu[block].classList.add('treachery')
if (view.actions && view.actions.hit && view.actions.hit.includes(block))
ui.battle_menu[block].classList.add('hit')
let class_name = battle_block_class_name(BLOCKS[block])
if (view.actions && view.actions.block && view.actions.block.includes(block))
class_name += " highlight"
if (set_has(view.moved, block))
class_name += " moved"
if (block === view.who)
class_name += " selected"
if (block === view.battle.halfhit)
class_name += " halfhit"
if (view.jihad === view.battle.town && block_owner(block) === view.p1)
class_name += " jihad"
if (view.battle.sallying.includes(block))
show = true
if (view.battle.storming.includes(block))
show = true
if (show || block_owner(block) === player) {
class_name += " known"
ui.battle_block[block].className = class_name
update_steps(block, view.steps[block], ui.battle_block[block], false)
} else {
ui.battle_block[block].className = class_name
}
}
for (let b = 0; b < BLOCKS.length; ++b) {
if (!ui.present.has(b)) {
if (cell.contains(ui.battle_menu[b]))
cell.removeChild(ui.battle_menu[b])
}
}
}
if (player === FRANKS) {
fill_cell("ER", view.battle.SR, false)
fill_cell("EC", view.battle.SC, view.battle.show_castle)
fill_cell("EF", view.battle.SF, view.battle.show_field)
fill_cell("FF", view.battle.FF, view.battle.show_field)
fill_cell("FC", view.battle.FC, view.battle.show_castle)
fill_cell("FR", view.battle.FR, false)
document.getElementById("FC").className = "c" + view.battle.FCS
document.getElementById("EC").className = "c" + view.battle.SCS
} else {
fill_cell("ER", view.battle.FR, false)
fill_cell("EC", view.battle.FC, view.battle.show_castle)
fill_cell("EF", view.battle.FF, view.battle.show_field)
fill_cell("FF", view.battle.SF, view.battle.show_field)
fill_cell("FC", view.battle.SC, view.battle.show_castle)
fill_cell("FR", view.battle.SR, false)
document.getElementById("EC").className = "c" + view.battle.FCS
document.getElementById("FC").className = "c" + view.battle.SCS
}
}
let flash_timer = 0
function start_flash() {
let element = document.getElementById("battle_message")
let tick = true
if (flash_timer)
return
flash_timer = setInterval(function () {
if (!view.flash_next) {
element.textContent = view.battle ? view.battle.flash : ""
clearInterval(flash_timer)
flash_timer = 0
} else {
element.textContent = tick ? view.battle.flash : view.flash_next
tick = !tick
}
}, 1000)
}
function on_update() {
action_button("eliminate", "Eliminate")
action_button("winter_campaign", "Winter campaign")
action_button("sea_move", "Sea move")
action_button("end_sea_move", "End sea move")
action_button("group_move", "Group move")
action_button("end_group_move", "End group move")
action_button("muster", "Muster")
action_button("end_muster", "End muster")
action_button("end_retreat", "End retreat")
action_button("end_regroup", "End regroup")
action_button("end_move_phase", "End move phase")
action_button("assign", "Assign hits")
action_button("pass", "Pass")
action_button("next", "Next")
action_button("undo", "Undo")
document.getElementById("frank_vp").textContent = view.f_vp
document.getElementById("saracen_vp").textContent = view.s_vp
for (let c = 1; c <= 27; ++c)
remember_position(ui.cards[c])
update_cards()
update_map()
if (view.battle) {
document.getElementById("battle_header").textContent = view.battle.title
document.getElementById("battle_message").textContent = view.battle.flash
if (view.flash_next)
start_flash()
if (!document.getElementById("battle").classList.contains("show")) {
document.getElementById("battle").classList.add("show")
scroll_into_view_if_mobile(document.getElementById("battle"))
}
update_battle()
} else {
document.getElementById("battle").classList.remove("show")
}
for (let c = 1; c <= 27; ++c)
animate_position(ui.cards[c])
}
build_map()
drag_element_with_mouse("#battle", "#battle_header")
scroll_with_middle_mouse("main", 3)