"use strict"
/* global view, action_button, send_action */
// TODO: pool for hero points to animate ?
// TODO: pool for faction markers to animate ?
const ui = {
header: document.querySelector("header"),
hand_panel: document.getElementById("hand_panel"),
map_container: document.getElementById("pieces"),
tooltip: document.getElementById("tooltip"),
fascist_cards: document.getElementById("fascist_cards"),
trash: document.getElementById("trash"),
hand: document.getElementById("hand"),
player_areas: document.getElementById("player_areas"),
panels: [
document.getElementById("player_area_a"),
document.getElementById("player_area_c"),
document.getElementById("player_area_m"),
],
tableaus: [
document.getElementById("tableau_a"),
document.getElementById("tableau_c"),
document.getElementById("tableau_m"),
],
tokens: [
document.getElementById("tokens_a"),
document.getElementById("tokens_c"),
document.getElementById("tokens_m"),
],
bag_of_glory: document.getElementById("bag_of_glory"),
// spaces
tracks_x: [],
tracks: [],
fronts: [],
status_fronts: [],
con_fronts: [],
str_fronts: [],
// tokens
standees: [],
bonuses: [],
medallions: [],
blank_markers: [],
// cards
cards: [],
}
/* :r! node tools/parse-layout.js */
const boxes = {
"Northern": [149,146,149,149],
"Aragon": [563,275,149,149],
"Southern": [341,641,149,149],
"Madrid": [195,398,161,161],
"Liberty": [955,65,662,59],
"Collectivization": [954,231,663,60],
"SovietSupport": [954,564,663,59],
"ForeignAid": [954,730,663,59],
"Government": [1015,398,602,58],
"Medallion1": [608,834,69,70],
"Medallion2": [690,834,70,70],
"Medallion3": [772,834,70,70],
"Medallion4": [855,834,70,70],
"Medallion5": [937,834,70,70],
"CurrentYearDeck": [38,976,187,261],
"CurrentEvent1": [301,976,187,261],
"CurrentEvent2": [526,976,187,261],
"CurrentEvent3": [751,976,187,261],
"CurrentEvent4": [976,976,187,261],
"Glory1": [1320,966,59,59],
"Glory2": [1418,966,119,59],
"Glory3": [1399,1075,58,58],
"Glory4": [1278,1153,300,59],
"Bonus1": [728,726,74,73],
"Bonus2": [818,726,74,73],
}
function toggle_pieces() { // eslint-disable-line no-unused-vars
ui.map_container.classList.toggle("hide")
}
let animation_register = []
function register_animation(e) {
animation_register.push(e)
}
function remember_position(e) {
if (e.parentElement) {
let prect = e.parentElement.getBoundingClientRect()
let rect = e.getBoundingClientRect()
e.my_visible = 1
e.my_parent = e.parentElement
e.my_px = prect.x
e.my_py = prect.y
e.my_x = rect.x
e.my_y = rect.y
} else {
e.my_visible = 0
e.my_parent = null
e.my_x = 0
e.my_y = 0
e.my_z = 0
}
}
function animate_position(e) {
if (e.parentElement && e.my_visible) {
let prect = e.parentElement.getBoundingClientRect()
let rect = e.getBoundingClientRect()
let dx, dy
if (e.parentElement === e.my_parent) {
// animate element within animated element...
dx = (e.my_x - e.my_px) - (rect.x - prect.x)
dy = (e.my_y - e.my_py) - (rect.y - prect.y)
} else {
dx = e.my_x - rect.x
dy = e.my_y - rect.y
}
// fade in
if (!e.my_parent) {
e.animate(
[
{ opacity: 0 },
{ opacity: 1 }
],
{ duration: 500, easing: "ease" }
)
}
if (dx !== 0 || dy !== 0) {
let dist = Math.sqrt((dx * dx) + (dy * dy))
let time = Math.max(500, Math.min(1000, dist / 2))
e.animate(
[
{ transform: `translate(${dx}px, ${dy}px)`, },
{ transform: "translate(0, 0)", },
],
{ duration: time, easing: "ease" }
)
}
}
}
let action_register = []
function register_action(e, action, id) {
e.my_action = action
e.my_id = id
e.onmousedown = on_click_action
action_register.push(e)
}
function on_click_action(evt) {
if (evt.button === 0)
if (send_action(evt.target.my_action, evt.target.my_id))
evt.stopPropagation()
}
function is_action(action, arg) {
if (arg === undefined)
return !!(view.actions && view.actions[action] === 1)
return !!(view.actions && view.actions[action] && view.actions[action].includes(arg))
}
let on_init_once = false
function build_track(t, action_name, track_name, a, b) {
let [x, y, w, h] = boxes[track_name]
let e
ui.tracks[t] = []
ui.tracks_x[t] = []
for (let s = a; s <= b; ++s) {
const bm = t * 11 + s
ui.tracks_x[t][s] = Math.round(x) + 4 + "px"
e = ui.tracks[t][s] = document.createElement("div")
e.className = "track"
e.style.top = y + "px"
e.style.left = Math.round(x) + "px"
register_action(e, action_name, s)
ui.map_container.appendChild(e)
e = (ui.blank_markers[bm] = document.createElement("div"))
e.className = "red token blank hide"
e.style.top = Math.round(y+4) + "px"
e.style.left = Math.round(x+3) + "px"
register_action(e, "blank_marker", bm)
ui.map_container.appendChild(e)
x += 60.5
}
e = (ui.standees[t] = document.createElement("div"))
e.className = "white token standee standee_" + t
e.style.top = y - 10 + "px"
e.style.left = boxes[track_name][0] + 4 + "px"
register_action(e, "standee", t)
register_animation(e)
ui.map_container.appendChild(ui.standees[t])
}
function build_bonus(b, box_name, cname) {
var [x, y, w, h] = boxes[box_name]
var e = ui.bonuses[b] = document.createElement("div")
e.className = "red token round " + cname
e.style.top = Math.round(y + w/2 - 32) + "px"
e.style.left = Math.round(x + w/2 - 32) + "px"
register_action(e, "bonus", b)
ui.map_container.appendChild(e)
}
function build_front(i, box_name) {
var [x, y, w, h] = boxes[box_name]
var e
e = ui.status_fronts[i] = document.createElement("div")
e.className = "hide"
e.style.position = "absolute"
e.style.top = Math.round(y + h / 2 - 140) + "px"
e.style.left = Math.round(x + w / 2 - 103) + "px"
ui.map_container.appendChild(e)
e = ui.con_fronts[i] = document.createElement("div")
e.className = "front_container"
e.style.top = y + 5 + "px"
e.style.left = x + "px"
e.style.width = w - 25 + "px"
e.style.height = 55 + "px"
ui.map_container.appendChild(e)
e = ui.str_fronts[i] = document.createElement("div")
e.className = "front_container"
e.style.top = y + h - 55 - 5+ "px"
e.style.left = x + "px"
e.style.width = w - 25 + "px"
e.style.height = 55 + "px"
ui.map_container.appendChild(e)
e = ui.fronts[i] = document.createElement("div")
e.className = "front"
e.style.top = Math.round(y-3) + "px"
e.style.left = Math.round(x-3) + "px"
e.style.width = Math.round(w - 6) + "px"
e.style.height = Math.round(h - 6) + "px"
register_action(e, "front", i)
ui.map_container.appendChild(e)
}
function on_init() {
var i, e
if (on_init_once)
return
on_init_once = true
ui.roles_list = document.getElementById("roles"),
ui.roles = [
document.getElementById("role_Anarchist"),
document.getElementById("role_Communist"),
document.getElementById("role_Moderate"),
]
build_track(0, "tr0", "Liberty", 0, 10)
build_track(1, "tr1", "Collectivization", 0, 10)
build_track(2, "tr2", "Government", 1, 10)
build_track(3, "tr3", "SovietSupport", 0, 10)
build_track(4, "tr4", "ForeignAid", 0, 10)
build_bonus(0, "Bonus1", "bonus_morale")
build_bonus(1, "Bonus2", "bonus_teamwork")
build_front(0, "Aragon")
build_front(1, "Madrid")
build_front(2, "Northern")
build_front(3, "Southern")
ui.medallion_container = []
for (i = 0; i < 5; ++i) {
e = ui.medallion_container[i] = document.createElement("div")
e.className = "medallion_container"
e.style.top = boxes["Medallion"+(i+1)][1] + "px"
e.style.left = boxes["Medallion"+(i+1)][0] + "px"
ui.map_container.appendChild(e)
}
ui.raw_glory_container = []
for (i = 0; i < 4; ++i) {
e = ui.raw_glory_container[i] = document.createElement("div")
e.className = "glory_container"
e.style.left = boxes["Glory" + (i+1)][0] + "px"
e.style.top = boxes["Glory" + (i+1)][1] + "px"
ui.map_container.appendChild(e)
}
ui.glory_container = [
ui.raw_glory_container[0],
ui.raw_glory_container[1],
ui.raw_glory_container[1],
ui.raw_glory_container[2],
ui.raw_glory_container[3],
ui.raw_glory_container[3],
ui.raw_glory_container[3],
ui.raw_glory_container[3],
ui.raw_glory_container[3],
]
e = ui.fascist_deck = document.createElement("div")
e.className = "fascist_deck"
e.style.top = boxes["CurrentYearDeck"][1] - 10 + "px"
e.style.left = boxes["CurrentYearDeck"][0] - 10 + "px"
ui.map_container.appendChild(e)
ui.current_events = []
for (i = 0; i < 4; ++i) {
e = (ui.current_events[i] = document.createElement("div"))
e.className = "current_events"
e.style.left = boxes["CurrentEvent" + (i+1)][0] - 10 + "px"
e.style.top = boxes["CurrentEvent" + (i+1)][1] - 10 + "px"
ui.map_container.appendChild(e)
}
for (i = 0; i < 9; ++i) {
e = (ui.medallions[i] = document.createElement("div"))
e.className = "pink token medallion medallion_" + i
register_action(e, "medallion", i)
register_animation(e)
}
for (i = 1; i <= 120; ++i) {
e = (ui.cards[i] = document.createElement("div"))
e.className = "card card_" + i
register_action(e, "card", i)
register_animation(e)
}
e = ui.initiative = document.createElement("div")
e.className = "red token round initiative"
register_animation(e)
}
function place_cards(e, cards) {
if (cards) {
for (var c of cards) {
e.appendChild(ui.cards[c])
ui.cards[c].classList.toggle("selected", view.selected_cards.includes(c))
}
}
}
const faction_class = {
"Anarchist": "anarchist",
"Communist": "communist",
"Moderate": "moderate",
"0": "anarchist",
"1": "communist",
"2": "moderate",
"f": "fascist",
}
function update_front(status_card, str_container, con_container, front, front_id) {
var i, n, e, cn
str_container.replaceChildren()
con_container.replaceChildren()
if (front.status === "Victory") {
status_card.className = "card card_111"
return
}
if (front.status === "Defeat") {
status_card.className = "card card_112"
return
}
status_card.className = "hide"
if (front.value < 0) {
n = -front.value
cn = "brown token front_minus"
} else {
n = front.value
cn = "pink token front_plus"
}
for (i = 0; i < n; ++i) {
e = document.createElement("div")
e.className = cn
str_container.appendChild(e)
}
var current_card = view.current_events[view.current_events.length-1]
if (current_card && data.cards[current_card].test.front === front_id) {
e = document.createElement("div")
e.className = "red token player fascist"
con_container.appendChild(e)
}
for (i = 0; i < 3; ++i) {
if (front.contributions.includes(i)) {
e = document.createElement("div")
e.className = "red token player " + faction_class[i]
con_container.appendChild(e)
}
}
}
function update_medallions(container, list) {
for (var i of list)
container.appendChild(ui.medallions[i])
}
function update_hero_points(container, n) {
for (var i = 0; i < n; ++i) {
var e = document.createElement("div")
e.className = "token pink hero_point"
container.appendChild(e)
}
}
function update_stat_line(x) {
var i, line
line = ""
for (i = 0; i < view.hero_points[x]; ++i)
line += "\u272b"
if (view.initiative === x)
line += (view.year & 1) ? " \u2bc6" : " \u2bc5"
return line
}
function on_update() { // eslint-disable-line no-unused-vars
var i, x, e
on_init()
animation_register.forEach(remember_position)
ui.player_areas.replaceChildren()
ui.roles_list.replaceChildren()
for (i = 0; i < 3; ++i) {
ui.player_areas.appendChild(ui.panels[view.player_order[i]])
ui.roles_list.appendChild(ui.roles[view.player_order[i]])
}
ui.header.classList.toggle("fascist", !!view.fascist)
ui.initiative.classList.toggle("ccw", (view.year & 1) === 0)
roles.Anarchist.stat.textContent = update_stat_line(0)
roles.Communist.stat.textContent = update_stat_line(1)
roles.Moderate.stat.textContent = update_stat_line(2)
// TODO: played card (side panel?)
// TODO: player draw/discard/trash
// TODO: bag of glory
ui.fascist_deck.replaceChildren()
if (view.year === 1)
place_cards(ui.fascist_deck, [ 120, 119, 118 ])
else if (view.year === 2)
place_cards(ui.fascist_deck, [ 120, 119 ])
else
place_cards(ui.fascist_deck, [ 120])
ui.fascist_cards.replaceChildren()
place_cards(ui.fascist_cards, view.fascist_cards)
ui.trash.replaceChildren()
place_cards(ui.trash, view.trash)
ui.hand_panel.classList = "panel " + faction_class[player]
ui.hand.replaceChildren()
place_cards(ui.hand, view.hand)
ui.tableaus[0].replaceChildren()
ui.tableaus[1].replaceChildren()
ui.tableaus[2].replaceChildren()
ui.tableaus[0].appendChild(ui.cards[117])
ui.tableaus[1].appendChild(ui.cards[116])
ui.tableaus[2].appendChild(ui.cards[115])
place_cards(ui.tableaus[0], view.tableaus[0])
place_cards(ui.tableaus[1], view.tableaus[1])
place_cards(ui.tableaus[2], view.tableaus[2])
for (i = 0; i <= 1; ++i) {
ui.bonuses[i].classList.toggle("red", !!view.bonuses[i])
ui.bonuses[i].classList.toggle("gray", !view.bonuses[i])
ui.bonuses[i].classList.toggle("off", !view.bonuses[i])
}
for (i = 0; i < 4; ++i) {
ui.current_events[i].replaceChildren()
if (i < view.current_events.length)
ui.current_events[i].appendChild(ui.cards[view.current_events[i]])
}
for (i = 0; i < 5; ++i)
ui.standees[i].style.left = ui.tracks_x[i][view.tracks[i]]
for (i = 0; i < 55; ++i) {
if (ui.blank_markers[i])
ui.blank_markers[i].classList.toggle("hide",
!view.triggered_track_effects.includes(i))
}
for (i = 0; i < 5; ++i) {
ui.medallion_container[i].replaceChildren()
if (i < view.medallions[3].length) {
x = view.medallions[3][i]
if (x !== null)
ui.medallion_container[i].appendChild(ui.medallions[x])
}
}
ui.tokens[0].replaceChildren()
ui.tokens[1].replaceChildren()
ui.tokens[2].replaceChildren()
ui.tokens[view.initiative].appendChild(ui.initiative)
update_medallions(ui.tokens[0], view.medallions[0])
update_medallions(ui.tokens[1], view.medallions[1])
update_medallions(ui.tokens[2], view.medallions[2])
update_hero_points(ui.tokens[0], view.hero_points[0])
update_hero_points(ui.tokens[1], view.hero_points[1])
update_hero_points(ui.tokens[2], view.hero_points[2])
update_front(ui.status_fronts[0], ui.str_fronts[0], ui.con_fronts[0], view.fronts[0], "a")
update_front(ui.status_fronts[1], ui.str_fronts[1], ui.con_fronts[1], view.fronts[1], "m")
update_front(ui.status_fronts[2], ui.str_fronts[2], ui.con_fronts[2], view.fronts[2], "n")
update_front(ui.status_fronts[3], ui.str_fronts[3], ui.con_fronts[3], view.fronts[3], "s")
for (e of ui.raw_glory_container)
e.replaceChildren()
for (i = 0; i < view.glory.length; ++i) {
x = view.glory[i]
e = document.createElement("div")
e.className = "red token player " + faction_class[x]
ui.glory_container[i].appendChild(e)
}
ui.bag_of_glory.replaceChildren()
if (Array.isArray(view.bag_of_glory)) {
for (x of view.bag_of_glory) {
e = document.createElement("div")
e.className = "red token player " + faction_class[x]
ui.bag_of_glory.appendChild(e)
}
}
action_button("Anarchist", "Anarchist")
action_button("Communist", "Communist")
action_button("Moderate", "Moderate")
action_button("add_glory", "Add to Bag of Glory")
action_button("add_to_front", "+1 to a Front")
action_button("draw_card", "Draw a Card")
action_button("draw_cards", "Draw Cards")
action_button("draw_glory", "Draw from Bag of Glory")
action_button("gain_hp", "Gain Hero Points")
action_button("lose_hp", "Lose Hero Points")
action_button("play_to_tableau", "Action Points")
action_button("play_for_event", "Event")
action_button("remove_blank_marker", "Remove Blank marker")
action_button("use_momentum", "Momentum")
action_button("use_ap", "Action Points")
action_button("use_morale_bonus", "Morale Bonus")
action_button("skip", "Skip")
action_button("yes", "Yes")
action_button("no", "No")
action_button("confirm", "Confirm")
action_button("done", "Done")
action_button("spend_hp", "Use Hero Points")
action_button("end_turn", "End Turn")
action_button("undo", "Undo")
for (e of action_register)
e.classList.toggle("action", is_action(e.my_action, e.my_id))
animation_register.forEach(animate_position)
}
function on_focus_card_tip(x) {
ui.tooltip.className = "card card_" + x
}
function on_focus_medallion_tip(x) {
ui.tooltip.className = "pink token medallion medallion_" + x
}
function on_blur_tip(x) {
ui.tooltip.className = "hide"
}
function sub_card(_match, p1) {
return `${data.cards[p1].title}`
}
function sub_medallion(_match, p1) {
return `${data.medallions[p1].name}`
}
function sub_token(match) {
switch (match) {
case "T0": return `
`
case "T1": return `
`
case "T2": return `
`
case "T3": return `
`
case "HP": return `
`
}
return match
}
function on_log(text) { // eslint-disable-line no-unused-vars
let p = document.createElement("div")
if (text.match(/^>>/)) {
text = text.substring(2)
p.className = "ii"
}
if (text.match(/^>/)) {
text = text.substring(1)
p.className = "i"
}
text = text.replace(/<-/g, "\u2190")
text = text.replace(/->/g, "\u2192")
text = text.replace(/&/g, "&")
text = text.replace(//g, ">")
text = text.replace(/-( ?[\d(])/g, "\u2212$1")
text = text.replace(/\bC(\d+)\b/g, sub_card)
text = text.replace(/\bM(\d+)\b/g, sub_medallion)
text = text.replace(/\bT[acmf]\b/g, sub_token)
text = text.replace(/\bHP\b/g, "Hero points")
// text = text.replace(/\bHP\b/g, "\u272b")
// text = text.replace(/\bHP\b/g, sub_token)
if (text.startsWith("#")) {
p.className = "h " + faction_class[text[1]]
text = text.substring(3)
}
p.innerHTML = text
return p
}