summaryrefslogtreecommitdiff
path: root/play.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-10-02 17:33:01 +0200
committerTor Andersson <tor@ccxvii.net>2022-11-30 13:26:51 +0100
commit11016ba45398d98a02e063de65e1e1fa9d73a58c (patch)
tree284ff3715ac5346067f504d9297b964f4c56b688 /play.js
parent04bd6f688a047ccaa804c4e32f64405873b2f49b (diff)
downloadcrusader-rex-11016ba45398d98a02e063de65e1e1fa9d73a58c.tar.gz
Optimize game state representation using numbers instead of string ids.
Diffstat (limited to 'play.js')
-rw-r--r--play.js902
1 files changed, 492 insertions, 410 deletions
diff --git a/play.js b/play.js
index a6b18f6..b675d31 100644
--- a/play.js
+++ b/play.js
@@ -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, "&amp;");
- text = text.replace(/</g, "&lt;");
- text = text.replace(/>/g, "&gt;");
+ let p = document.createElement("div")
+
+ if (text.match(/^>/)) {
+ text = text.substring(1)
+ p.className = "i"
+ }
+
+ text = text.replace(/&/g, "&amp;")
+ text = text.replace(/</g, "&lt;")
+ text = text.replace(/>/g, "&gt;")
- 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)