"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"), fascist_cards: document.getElementById("fascist_cards"), trash: document.getElementById("trash"), hand: document.getElementById("hand"), player_areas: document.getElementById("player_areas"), panels: { a: document.getElementById("player_area_a"), c: document.getElementById("player_area_c"), m: document.getElementById("player_area_m"), }, tableaus: { a: document.getElementById("tableau_a"), c: document.getElementById("tableau_c"), m: document.getElementById("tableau_m"), }, tokens: { a: document.getElementById("tokens_a"), c: document.getElementById("tokens_c"), m: 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, action_id, 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", action_id) 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 = { a: document.getElementById("role_Anarchist"), c: document.getElementById("role_Communist"), m: 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, "a", "Aragon") build_front(1, "m", "Madrid") build_front(2, "n", "Northern") build_front(3, "s", "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", "a": "anarchist", "c": "communist", "m": "moderate", } 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 of ["a", "c", "m"]) { 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("a") roles.Communist.stat.textContent = update_stat_line("c") roles.Moderate.stat.textContent = update_stat_line("m") // 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.a.replaceChildren() ui.tableaus.c.replaceChildren() ui.tableaus.m.replaceChildren() ui.tableaus.a.appendChild(ui.cards[117]) ui.tableaus.c.appendChild(ui.cards[116]) ui.tableaus.m.appendChild(ui.cards[115]) place_cards(ui.tableaus.a, view.tableaus.a) place_cards(ui.tableaus.c, view.tableaus.c) place_cards(ui.tableaus.m, view.tableaus.m) 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.pool.length) { x = view.medallions.pool[i] if (x !== null) ui.medallion_container[i].appendChild(ui.medallions[x]) } } ui.tokens.a.replaceChildren() ui.tokens.c.replaceChildren() ui.tokens.m.replaceChildren() ui.tokens[view.initiative].appendChild(ui.initiative) update_medallions(ui.tokens.a, view.medallions.a) update_medallions(ui.tokens.c, view.medallions.c) update_medallions(ui.tokens.m, view.medallions.m) update_hero_points(ui.tokens.a, view.hero_points.a) update_hero_points(ui.tokens.c, view.hero_points.c) update_hero_points(ui.tokens.m, view.hero_points.m) update_front(ui.status_fronts[0], ui.str_fronts[0], ui.con_fronts[0], view.fronts.a, "a") update_front(ui.status_fronts[1], ui.str_fronts[1], ui.con_fronts[1], view.fronts.m, "m") update_front(ui.status_fronts[2], ui.str_fronts[2], ui.con_fronts[2], view.fronts.n, "n") update_front(ui.status_fronts[3], ui.str_fronts[3], ui.con_fronts[3], view.fronts.s, "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", "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_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, "&") text = text.replace(//g, ">") text = text.replace(/-( ?[\d(])/g, "\u2212$1") // text = text.replace(/C(\d+)/g, sub_card) if (text.startsWith("#")) { p.className = "h " + text[1] text = text.substring(3) } p.innerHTML = text return p }