diff options
author | Tor Andersson <tor@ccxvii.net> | 2022-10-02 17:33:01 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2022-11-30 13:26:51 +0100 |
commit | 11016ba45398d98a02e063de65e1e1fa9d73a58c (patch) | |
tree | 284ff3715ac5346067f504d9297b964f4c56b688 /play.js | |
parent | 04bd6f688a047ccaa804c4e32f64405873b2f49b (diff) | |
download | crusader-rex-11016ba45398d98a02e063de65e1e1fa9d73a58c.tar.gz |
Optimize game state representation using numbers instead of string ids.
Diffstat (limited to 'play.js')
-rw-r--r-- | play.js | 902 |
1 files changed, 492 insertions, 410 deletions
@@ -1,219 +1,302 @@ -"use strict"; +"use strict" + +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 FRANKS = "Franks"; -const SARACENS = "Saracens"; -const ASSASSINS = "Assassins"; const ENEMY = { Saracens: "Franks", Franks: "Saracens" } -const DEAD = "Dead"; -const F_POOL = "FP"; -const S_POOL = "SP"; -const ENGLAND = "England"; -const FRANCE = "France"; -const GERMANIA = "Germania"; + +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 = [ - "Aleppo", "Damascus", "Egypt", - "Antioch", "Tripoli", "Acre", "Jerusalem" -]; + 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'; +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(); + 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(); + label_layout = 'stack' + window.localStorage['crusader-rex/label-layout'] = label_layout + update_map() } function toggle_blocks() { - document.getElementById("map").classList.toggle("hide_blocks"); + document.getElementById("map").classList.toggle("hide_blocks") } let ui = { - cards: {}, - card_backs: {}, - towns: {}, - blocks: {}, - battle_menu: {}, - battle_block: {}, + cards: [], + card_backs: [], + towns: [], + blocks: [], + battle_menu: [], + battle_block: [], present: new Set(), } +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 `<span class="tip" onmouseenter="on_focus_space_tip(${x})" onmouseleave="on_blur_space_tip(${x})" onclick="on_click_space_tip(${x})">${n}</span>` +} + function on_log(text) { - let p = document.createElement("div"); - text = text.replace(/&/g, "&"); - text = text.replace(/</g, "<"); - text = text.replace(/>/g, ">"); + 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(/>/g, ">") - text = text.replace(/\u2192 /g, "\u2192\xa0"); + text = text.replace(/\u2192 /g, "\u2192\xa0") - text = text.replace(/^([A-Z]):/, '<span class="$1"> $1 </span>'); + text = text.replace(/^([A-Z]):/, '<span class="$1"> $1 </span>') - if (text.match(/^~ .* ~$/)) - p.className = 'br', text = text.substring(2, text.length-2); - else if (text.match(/^Start Franks/)) - p.className = 'F'; - else if (text.match(/^Start Saracens/)) - p.className = 'S'; - else if (text.match(/^Start /)) - p.className = 'st', text = text.replace(/\.$/, ""); - else if (text.match(/^(Battle in)/)) - p.className = 'bs'; + text = text.replace(/#(\d+)/g, sub_space_name) - if (text.match(/^Start /)) - text = text.substring(6); + 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; + p.innerHTML = text + return p } function on_focus_town(evt) { - let where = evt.target.town; - let text = where; - if (where in SHIELDS) - text += " \u2014 " + SHIELDS[where].join(", "); - let kingdom = KINGDOM[TOWNS[where].region]; + 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; + text += " \u2014 " + kingdom if (VICTORY_TOWNS.includes(where)) - text += " \u2014 1 VP"; - document.getElementById("status").textContent = text; + text += " \u2014 1 VP" + document.getElementById("status").textContent = text } function on_blur_town(evt) { - document.getElementById("status").textContent = ""; + document.getElementById("status").textContent = "" } function on_click_town(evt) { - let where = evt.target.town; - send_action('town', where); + let where = evt.target.town + send_action('town', where) } -const STEP_TEXT = [ 0, "I", "II", "III", "IIII" ]; -const HEIR_TEXT = [ 0, '\u00b9', '\u00b2', '\u00b3', '\u2074', '\u2075' ]; +const STEP_TEXT = [ 0, "I", "II", "III", "IIII" ] +const HEIR_TEXT = [ 0, '\u00b9', '\u00b2', '\u00b3', '\u2074', '\u2075' ] -function block_name(who) { return who; } +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 + " "; + 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.combat; - document.getElementById("status").textContent = text; + 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; + document.getElementById("status").textContent = info.owner } } function on_blur_map_block(evt) { - document.getElementById("status").textContent = ""; + document.getElementById("status").textContent = "" } function on_click_map_block(evt) { - let b = evt.target.block; + let b = evt.target.block if (!view.battle) - send_action('block', b); + send_action('block', b) } function on_focus_battle_block(evt) { - let b = evt.target.block; - let msg; + let b = evt.target.block + let msg if (!evt.target.classList.contains("known")) { if (block_owner(b) === FRANKS) - msg = "Franks"; + msg = "Franks" else if (block_owner(b) === SARACENS) - msg = "Saracens"; + msg = "Saracens" } else { - msg = block_name(b); + msg = block_name(b) } if (view.actions && view.actions.fire && view.actions.fire.includes(b)) - msg = "Fire with " + msg; + msg = "Fire with " + msg else if (view.actions && view.actions.storm && view.actions.storm.includes(b)) - msg = "Storm with " + msg; + msg = "Storm with " + msg else if (view.actions && view.actions.sally && view.actions.sally.includes(b)) - msg = "Sally with " + msg; + msg = "Sally with " + msg else if (view.actions && view.actions.withdraw && view.actions.withdraw.includes(b)) - msg = "Withdraw with " + msg; + msg = "Withdraw with " + msg else if (view.actions && view.actions.hit && view.actions.hit.includes(b)) - msg = "Take hit on " + msg; + msg = "Take hit on " + msg - document.getElementById("status").textContent = msg; + document.getElementById("status").textContent = msg } function on_blur_battle_block(evt) { - document.getElementById("status").textContent = ""; + document.getElementById("status").textContent = "" } function on_click_battle_block(evt) { - let b = evt.target.block; - send_action('block', b); + 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); + "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); + "Withdraw with " + block_name(evt.target.block) else document.getElementById("status").textContent = - "Retreat with " + block_name(evt.target.block); + "Retreat with " + block_name(evt.target.block) } function on_focus_harry(evt) { document.getElementById("status").textContent = - "Harry with " + block_name(evt.target.block); + "Harry with " + block_name(evt.target.block) } function on_focus_charge(evt) { document.getElementById("status").textContent = - "Charge with " + block_name(evt.target.block); + "Charge with " + block_name(evt.target.block) } function on_focus_withdraw(evt) { document.getElementById("status").textContent = - "Withdraw with " + block_name(evt.target.block); + "Withdraw with " + block_name(evt.target.block) } function on_focus_storm(evt) { document.getElementById("status").textContent = - "Storm with " + block_name(evt.target.block); + "Storm with " + block_name(evt.target.block) } function on_focus_sally(evt) { document.getElementById("status").textContent = - "Sally with " + block_name(evt.target.block); + "Sally with " + block_name(evt.target.block) } function on_focus_hit(evt) { document.getElementById("status").textContent = - "Take hit on " + block_name(evt.target.block); + "Take hit on " + block_name(evt.target.block) } function on_blur_battle_button(evt) { - document.getElementById("status").textContent = ""; + document.getElementById("status").textContent = "" } function on_click_hit(evt) { send_action('hit', evt.target.block); } @@ -226,573 +309,572 @@ 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); + 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); + 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}`; + 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 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"; + 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"); + "/images/cross-mark.svg") build_battle_button(menu_list, b, "charge", on_click_charge, on_focus_charge, - "/images/mounted-knight.svg"); + "/images/mounted-knight.svg") build_battle_button(menu_list, b, "fire", on_click_fire, on_focus_fire, - "/images/pointy-sword.svg"); + "/images/pointy-sword.svg") build_battle_button(menu_list, b, "harry", on_click_harry, on_focus_harry, - "/images/arrow-flights.svg"); + "/images/arrow-flights.svg") build_battle_button(menu_list, b, "retreat", on_click_retreat, on_focus_retreat, - "/images/flying-flag.svg"); + "/images/flying-flag.svg") build_battle_button(menu_list, b, "withdraw", on_click_withdraw, on_focus_withdraw, - "/images/stone-tower.svg"); + "/images/stone-tower.svg") build_battle_button(menu_list, b, "storm", on_click_storm, on_focus_storm, - "/images/siege-tower.svg"); + "/images/siege-tower.svg") build_battle_button(menu_list, b, "sally", on_click_sally, on_focus_sally, - "/images/doorway.svg"); + "/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; + 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; + 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; + 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; + let element - ui.blocks_element = document.getElementById("blocks"); - ui.offmap_element = document.getElementById("offmap"); - ui.towns_element = document.getElementById("towns"); + 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); + 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 name in TOWNS) { - let town = TOWNS[name]; - if (name === F_POOL || name === S_POOL || name === DEAD) - continue; - if (name === "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[name] = element; + 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[name] = build_town(name, town); - let xo = Math.round(element.offsetWidth/2); - let yo = Math.round(element.offsetHeight/2); - element.style.left = (town.x - xo) + "px"; - element.style.top = (town.y - yo) + "px"; + 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 in BLOCKS) { - let block = BLOCKS[b]; - ui.blocks[b] = build_map_block(b, block); - build_battle_block(b, block); + 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)); + 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"); + document.getElementById("map").classList.add("stack_layout") else - document.getElementById("map").classList.remove("stack_layout"); + 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); + layout_blocks_spread(location, secret, known) else - layout_blocks_stacked(location, secret, known); + layout_blocks_stacked(location, secret, known) } function layout_blocks_spread(town, north, south) { - let wrap = TOWNS[town].wrap; - let rows = []; + 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 = []; + north = north.concat(south) + south = [] } function wrap_row(input) { while (input.length > wrap) { - rows.push(input.slice(0, wrap)); - input = input.slice(wrap); + rows.push(input.slice(0, wrap)) + input = input.slice(wrap) } if (input.length > 0) - rows.push(input); + rows.push(input) } - wrap_row(north); - wrap_row(south); + wrap_row(north) + wrap_row(south) - if (TOWNS[town].layout_minor > 0.5) - rows.reverse(); + if (TOWNS[town].layout.minor > 0.5) + rows.reverse() for (let r = 0; r < rows.length; ++r) { - let cols = rows[r]; + let cols = rows[r] for (let c = 0; c < cols.length; ++c) - position_block(town, r, rows.length, c, cols.length, cols[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; + 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.x; - let y = space.y; - - if (space.layout_axis === 'X') { - x -= col_size * space.layout_major; - y -= row_size * space.layout_minor; - x += col * offset; - y += row * offset; + 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; + 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"; + 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; + 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; + 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()); + 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.x + (i - c) * 16 + k * 12; - let y = space.y + (i - c) * 16 - k * 12; - element.style.left = ((x - block_size/2)|0)+"px"; - element.style.top = ((y - block_size/2)|0)+"px"; + let space = TOWNS[location] + let block_size = 60+6 + let x = space.x + (i - c) * 16 + k * 12 + let y = space.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); + ui.blocks_element.appendChild(element) } function hide_block(element) { if (element.parentElement !== ui.offmap_element) - ui.offmap_element.appendChild(element); + ui.offmap_element.appendChild(element) } function is_known_block(info, who, town) { if (view.game_over && player === 'Observer') - return true; + return true if (town === DEAD) - return true; + 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; + return false + if (info.owner === player || info.owner === "Assassins" || who === view.assassinate) + return true + return false } function update_map() { - let layout = {}; + 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; + 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; + "Year " + view.year else if (view.turn < 6) document.getElementById("turn_info").textContent = - "Turn " + view.turn + " of Year " + view.year; + "Turn " + view.turn + " of Year " + view.year else document.getElementById("turn_info").textContent = - "Winter Turn of Year " + view.year; + "Winter Turn of Year " + view.year - for (let town in TOWNS) - layout[town] = { north: [], south: [] }; + for (let t = 0; t < TOWNS.length; ++t) + layout[t] = { north: [], south: [] } - for (let b in view.location) { - let info = BLOCKS[b]; - let element = ui.blocks[b]; - let town = view.location[b]; - let moved = view.moved[b] ? " moved" : ""; + 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) ? " moved" : "" if (town === DEAD) { - moved = " moved"; + moved = " moved" } - if (town === null) { - town = DEAD; - moved = " removed"; + 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; + 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 = ""; + let besieging = "" if (view.sieges[town] === info.owner) { if (view.winter_campaign === town) - besieging = " winter_campaign"; + besieging = " winter_campaign" else - besieging = " besieging"; + besieging = " besieging" } - let jihad = ""; + let jihad = "" if (view.jihad === town && info.owner === view.p1) - jihad = " jihad"; - element.classList = info.owner + " block" + moved + besieging + jihad; + jihad = " jihad" + element.classList = info.owner + " block" + moved + besieging + jihad } if (town !== DEAD) { if (info.owner === FRANKS) - layout[town].north.push(element); + layout[town].north.push(element) else - layout[town].south.push(element); + layout[town].south.push(element) } - show_block(element); + show_block(element) } - for (let b in view.location) { - let info = BLOCKS[b]; - let element = ui.blocks[b]; - let town = view.location[b]; + 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); + layout[F_POOL].north.unshift(element) else - layout[S_POOL].south.unshift(element); + layout[S_POOL].south.unshift(element) } } - for (let b in view.location) { - let info = BLOCKS[b]; - let element = ui.blocks[b]; - let town = view.location[b]; - if (town === null) { + 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); + layout[F_POOL].north.unshift(element) else - layout[S_POOL].south.unshift(element); + layout[S_POOL].south.unshift(element) } } - for (let town in TOWNS) - layout_blocks(town, layout[town].north, layout[town].south); + for (let t = 0; t < TOWNS.length; ++t) + layout_blocks(t, layout[t].north, layout[t].south) - for (let where in TOWNS) { - if (ui.towns[where]) { - ui.towns[where].classList.remove('highlight'); - ui.towns[where].classList.remove('muster'); + for (let t = SEA; t < TOWNS.length; ++t) { + if (ui.towns[t]) { + ui.towns[t].classList.remove('highlight') + ui.towns[t].classList.remove('muster') } } if (view.actions && view.actions.town) - for (let where of view.actions.town) - ui.towns[where].classList.add('highlight'); + for (let t of view.actions.town) + ui.towns[t].classList.add('highlight') if (view.muster) - ui.towns[view.muster].classList.add('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'); + ui.blocks[b].classList.add('highlight') } - if (view.who && !view.battle) - ui.blocks[view.who].classList.add('selected'); + 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'); + ui.blocks[b].classList.add('castle') } function update_card_display(element, card, prior_card) { if (!card && !prior_card) { - element.className = "show card card_back"; + element.className = "show card card_back" } else if (prior_card) { - element.className = "show card prior " + CARDS[prior_card].image; + element.className = "show card prior " + CARDS[prior_card].image } else { - element.className = "show card " + CARDS[card].image; + 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); + 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]; + let element = ui.cards[c] if (view.hand.includes(c)) { - element.classList.add("show"); + element.classList.add("show") if (view.actions && view.actions.play) { if (view.actions.play.includes(c)) { - element.classList.add("enabled"); - element.classList.remove("disabled"); + element.classList.add("enabled") + element.classList.remove("disabled") } else { - element.classList.remove("enabled"); - element.classList.add("disabled"); + element.classList.remove("enabled") + element.classList.add("disabled") } } else { - element.classList.remove("enabled"); - element.classList.remove("disabled"); + element.classList.remove("enabled") + element.classList.remove("disabled") } } else { - element.classList.remove("show"); + element.classList.remove("show") } } - let n = view.hand.length; + let n = view.hand.length for (let c = 1; c <= 6; ++c) if (c <= n && player === 'Observer') - ui.card_backs[c].classList.add("show"); + ui.card_backs[c].classList.add("show") else - ui.card_backs[c].classList.remove("show"); + ui.card_backs[c].classList.remove("show") } function compare_blocks(a, b) { - let aa = BLOCKS[a].combat; - let bb = BLOCKS[b].combat; + 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; + 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]; + let prev = root.children[i] if (compare_blocks(prev.block, block) > 0) { - root.insertBefore(node, prev); - return; + root.insertBefore(node, prev) + return } } - root.appendChild(node); + root.appendChild(node) } function update_battle() { function fill_cell(name, list, show) { - let cell = document.getElementById(name); + let cell = document.getElementById(name) - ui.present.clear(); + ui.present.clear() for (let block of list) { - ui.present.add(block); + ui.present.add(block) if (!cell.contains(ui.battle_menu[block])) - insert_battle_block(cell, ui.battle_menu[block], block); + insert_battle_block(cell, ui.battle_menu[block], block) - ui.battle_menu[block].className = "battle_menu"; + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + ui.battle_menu[block].classList.add('hit') - let class_name = battle_block_class_name(BLOCKS[block]); + let class_name = battle_block_class_name(BLOCKS[block]) if (view.actions && view.actions.block && view.actions.block.includes(block)) - class_name += " highlight"; - if (view.moved[block]) - class_name += " moved"; + class_name += " highlight" + if (set_has(view.moved, block)) + class_name += " moved" if (block === view.who) - class_name += " selected"; + class_name += " selected" if (block === view.battle.halfhit) - class_name += " halfhit"; + class_name += " halfhit" if (view.jihad === view.battle.town && block_owner(block) === view.p1) - class_name += " jihad"; + class_name += " jihad" if (view.battle.sallying.includes(block)) - show = true; + show = true if (view.battle.storming.includes(block)) - show = true; + 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); + 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; + ui.battle_block[block].className = class_name } } - for (let b in BLOCKS) { + 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]); + 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; + 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; + 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; +let flash_timer = 0 function start_flash() { - let element = document.getElementById("battle_message"); - let tick = true; + let element = document.getElementById("battle_message") + let tick = true if (flash_timer) - return; + return flash_timer = setInterval(function () { if (!view.flash_next) { - element.textContent = view.battle ? view.battle.flash : ""; - clearInterval(flash_timer); - flash_timer = 0; + 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; + element.textContent = tick ? view.battle.flash : view.flash_next + tick = !tick } - }, 1000); + }, 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("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; - - update_cards(); - update_map(); + 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("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 + + update_cards() + update_map() if (view.battle) { - document.getElementById("battle_header").textContent = view.battle.title; - document.getElementById("battle_message").textContent = view.battle.flash; + document.getElementById("battle_header").textContent = view.battle.title + document.getElementById("battle_message").textContent = view.battle.flash if (view.flash_next) - start_flash(); - document.getElementById("battle").classList.add("show"); - update_battle(); + start_flash() + document.getElementById("battle").classList.add("show") + update_battle() } else { - document.getElementById("battle").classList.remove("show"); + document.getElementById("battle").classList.remove("show") } } -build_map(); +build_map() -drag_element_with_mouse("#battle", "#battle_header"); -scroll_with_middle_mouse("main", 3); +drag_element_with_mouse("#battle", "#battle_header") +scroll_with_middle_mouse("main", 3) |