"use strict" // TODO: layout cubes in spaces in two groups (max 4 each space) // TODO: layout cubes on tracks/pools as one space let layout = [] let space_layout_cube = [] let space_layout_disc = [] let ui = { cards: [ null ], cubes: [], discs: [], spaces: [], red_momentum: document.getElementById("red_momentum"), blue_momentum: document.getElementById("blue_momentum"), political_vp: document.getElementById("political_vp"), military_vp: document.getElementById("military_vp"), round_marker: document.getElementById("round_marker"), } const card_names = [ "Initiative", "Jules Ducatel", "The Murder of Vincenzini", "Brassardiers", "Jules Ferry", "Le Figaro", "Général Louis Valentin", "Général Espivent", "Les Amis de l'Ordre", "Socialist Newspaper Ban", "Fortification of Mont-Valérien", "Adolphe Thiers", "Otto von Bismarck", "Général Ernest de Cissey", "Colonel de Lochner", "Jules Favre", "Hostage Decree", "Maréchal Macmahon", "Paule Minck", "Walery Wroblewski", "Banque de France", "Le Réveil", "Execution of Generals", "Les Cantinières", "Eugène Protot", "Paul Cluseret", "Gaston Crémieux", "Luise Michel", "Jaroslav Dombrowski", "Raoul Rigault", "Karl Marx", "Blanquists", "Général Lullier", "Jules Vallès", "Charles Delescluze", "Conciliation", "Georges Clemenceau", "Archbishop Georges Darboy", "Victor Hugo", "Léon Gambetta", "Elihu Washburne", "Freemason Parade", "Paris Cannons", "Aux Barricades!", "Commune's Stronghold", "Fighting in Issy Village", "Battle of Mont-Valérien", "Raid on Château de Vincennes", "Revolution in the Press", "Pius IX", "Socialist International", "Royalists Dissension", "Rise of Republicanism", "Legitimacy", ] const space_names = [ "National Assembly", "Royalists", "Republicans", "Press", "Catholic Church", "Social Movements", "Mont-Valérien", "Fort D'Issy", "Château de Vincennes", "Butte Montmartre", "Butte-Aux-Cailles", "Père Lachaise", "Prussian Occupied Territory", "Versailles HQ", "Red Cube Pool 1", "Red Cube Pool 2", "Red Cube Pool 3", "Red Crisis Track Start", "Red Crisis Track Escalation", "Red Crisis Track Tension", "Red Crisis Track Final Crisis", "Red Bonus Cubes 1", "Red Bonus Cubes 2", "Red Bonus Cubes 3", "Blue Cube Pool", "Blue Crisis Track Start", "Blue Crisis Track Escalation", "Blue Crisis Track Tension", "Blue Crisis Track Final Crisis", "Blue Bonus Cubes 1", "Blue Bonus Cubes 2", "Blue Bonus Cubes 3", "Prussian Collaboration 1", "Prussian Collaboration 2", "Prussian Collaboration 3", ] const space_count = space_names.length // :r !python3 tools/genboxes.py const boxes = { "Royalists": [80,428,126,126], "Republicans": [490,428,126,126], "Catholic Church": [80,786,126,126], "Social Movements": [490,786,126,126], "Fort D'Issy": [844,793,126,126], "Butte-Aux-Cailles": [1038,660,126,126], "Père Lachaise": [1206,591,126,126], "Château de Vincennes": [1342,650,126,127], "Press": [294,727,112,112], "National Assembly": [292,496,112,112], "Butte Montmartre": [1052,460,112,111], "Mont-Valérien": [717,507,112,112], "Versailles HQ": [662,850,101,95], "Prussian Occupied Territory": [1298,353,180,86], "Red Crisis Track Start": [982,61,90,112], "Red Crisis Track Escalation": [1072,61,84,112], "Red Crisis Track Tension": [1156,61,83,112], "Red Crisis Track Final Crisis": [1239,61,83,112], "Blue Crisis Track Start": [432,61,88,112], "Blue Crisis Track Escalation": [348,61,84,112], "Blue Crisis Track Tension": [265,61,83,112], "Blue Crisis Track Final Crisis": [182,61,83,112], "Blue Objective Card": [120,996,272,54], "Red Objective Card": [1114,996,272,54], "Blue Cube Pool": [180,198,198,55], "Red Bonus Cubes 1": [1072,22,84,39], "Red Bonus Cubes 2": [1156,22,83,39], "Red Bonus Cubes 3": [1239,22,83,39], "Blue Bonus Cubes 1": [348,22,84,39], "Blue Bonus Cubes 2": [265,22,83,39], "Blue Bonus Cubes 3": [182,22,83,39], "Red Cube Pool 1": [787,330,115,39], "Red Cube Pool 2": [902,330,136,39], "Red Cube Pool 3": [1038,330,157,40], "Prussian Collaboration 1": [600,330,115,39], "Prussian Collaboration 2": [463,330,136,39], "Prussian Collaboration 3": [306,330,157,39], } function is_action(action) { if (view.actions && view.actions[action]) return true return false } function is_card_action(action, card) { if (view.actions && view.actions[action] && view.actions[action].includes(card)) return true return false } function is_piece_action(i) { if (view.actions && view.actions.piece && view.actions.piece.includes(i)) return true return false } function is_space_action(i) { if (view.actions && view.actions.space && view.actions.space.includes(i)) return true return false } function on_blur(evt) { document.getElementById("status").textContent = "" } function on_focus_space(evt) { document.getElementById("status").textContent = evt.target.my_name } function on_click_red_momentum(evt) { if (evt.button === 0) { if (send_action('red_momentum')) evt.stopPropagation() } } function on_click_blue_momentum(evt) { if (evt.button === 0) { if (send_action('blue_momentum')) evt.stopPropagation() } } function on_click_card(evt) { if (evt.button === 0) { if (send_action('card', evt.target.my_card)) evt.stopPropagation() } } function on_click_space(evt) { if (evt.button === 0) { if (send_action('space', evt.target.my_space)) evt.stopPropagation() } } function on_click_cube(evt) { if (evt.button === 0) { if (send_action('piece', evt.target.my_cube)) evt.stopPropagation() } } function on_click_disc(evt) { if (evt.button === 0) { if (send_action('piece', evt.target.my_disc)) evt.stopPropagation() } } function build_user_interface() { let elt document.getElementById("red_momentum").addEventListener("mousedown", on_click_red_momentum) document.getElementById("blue_momentum").addEventListener("mousedown", on_click_blue_momentum) for (let c = 1; c <= 41 + 12; ++c) { elt = ui.cards[c] = document.createElement("div") elt.className = `card card_${c}` elt.my_card = c elt.addEventListener("click", on_click_card) } for (let i = 0; i < 36; ++i) { elt = ui.cubes[i] = document.createElement("div") if (i < 18) elt.className = "piece cube red" else elt.className = "piece cube blue" elt.my_cube = i elt.addEventListener("mousedown", on_click_cube) document.getElementById("pieces").appendChild(elt) } for (let i = 0; i < 4; ++i) { elt = ui.discs[i] = document.createElement("div") if (i < 2) elt.className = "piece disc red" else elt.className = "piece disc blue" elt.my_disc = i + 36 elt.addEventListener("mousedown", on_click_disc) document.getElementById("pieces").appendChild(elt) } for (let i = 0; i < space_count; ++i) { let name = space_names[i] let [x, y, w, h] = boxes[name] elt = ui.spaces[i] = document.createElement("div") elt.className = "space" elt.my_space = i elt.my_name = name elt.addEventListener("mousedown", on_click_space) elt.addEventListener("mouseenter", on_focus_space) elt.addEventListener("mouseleave", on_blur) let bw = 8 elt.style.top = y + "px" elt.style.left = x + "px" elt.style.width = (w - bw * 2) + "px" elt.style.height = (h - bw * 2) + "px" space_layout_cube[i] = { x: x + Math.round(w/2), y: y + Math.round(h*1/2) } space_layout_disc[i] = { x: x + w, y: y + h } document.getElementById("spaces").appendChild(elt) } } function layout_cubes(list, xorig, yorig) { const dx = 20 const dy = 11 if (list.length > 0) { let ncol = Math.round(Math.sqrt(list.length)) let nrow = Math.ceil(list.length / ncol) function place_cube(row, col, e, z) { let x = xorig - (row * dx - col * dx) - 18 + (nrow-ncol) * 6 let y = yorig - (row * dy + col * dy) - 28 + (nrow-1) * 8 e.style.left = x + "px" e.style.top = y + "px" e.style.zIndex = z } let z = 50 let i = 0 for (let row = 0; row < nrow; ++row) for (let col = 0; col < ncol && i < list.length; ++col) place_cube(row, col, list[list.length-(++i)], z--) } } function layout_disc(s, disc) { if (s > 0) { disc.classList.remove("hide") disc.style.left = (space_layout_disc[s].x - 50 - 12) + "px" disc.style.top = (space_layout_disc[s].y - 20 - 8) + "px" disc.style.zIndex = 51 } else disc.classList.add("hide") } function on_focus_card_tip(card_number) { document.getElementById("tooltip").className = "card card_" + card_number } function on_blur_card_tip() { document.getElementById("tooltip").classList = "card hide" } function sub_card_name(match, p1, offset, string) { let c = p1 | 0 let n = card_names[c] return `${n}` } function on_log(text) { let p = document.createElement("div") if (text.match(/^>/)) { text = text.substring(1) p.className = 'i' } text = text.replace(/&/g, "&") text = text.replace(//g, ">") text = text.replace(/#(\d+)/g, sub_card_name) if (text.match(/^\.h1/)) { text = text.substring(4) p.className = 'h1' } if (text.match(/^\.h2/)) { text = text.substring(4) if (text === 'Commune') p.className = 'h2 commune' else if (text === 'Versailles') p.className = 'h2 versailles' else p.className = 'h2' } p.innerHTML = text return p } function on_update() { if (view.initiative === "Commune") document.getElementById("commune_info").textContent = "\u2756" else document.getElementById("commune_info").textContent = "" if (view.initiative === "Versailles") document.getElementById("versailles_info").textContent = "\u2756" else document.getElementById("versailles_info").textContent = "" ui.round_marker.className = `piece pawn round${view.round}` if (is_action("red_momentum")) ui.red_momentum.className = `piece cylinder red m${view.red_momentum} action` else ui.red_momentum.className = `piece cylinder red m${view.red_momentum}` if (is_action("blue_momentum")) ui.blue_momentum.className = `piece cylinder blue m${view.blue_momentum} action` else ui.blue_momentum.className = `piece cylinder blue m${view.blue_momentum}` ui.military_vp.className = `piece cylinder purple vp${5+view.military_vp}` ui.political_vp.className = `piece cylinder orange vp${5+view.political_vp}` document.getElementById("hand").replaceChildren() document.getElementById("final").replaceChildren() document.getElementById("discard").replaceChildren() document.getElementById("set_aside").replaceChildren() document.getElementById("objective").replaceChildren() if (view.final) document.getElementById("final").appendChild(ui.cards[view.final]) if (view.discard) document.getElementById("discard").appendChild(ui.cards[view.discard]) if (view.hand) for (let c of view.hand) document.getElementById("hand").appendChild(ui.cards[c]) if (view.set_aside) for (let c of view.set_aside) document.getElementById("set_aside").appendChild(ui.cards[c]) if (view.objective) for (let c of view.objective) document.getElementById("objective").appendChild(ui.cards[c]) for (let i = 0; i < space_names.length; ++i) layout[i] = [] for (let i = 0; i < 36; ++i) { if (view.pieces[i] >= 0) { layout[view.pieces[i]].push(ui.cubes[i]) ui.cubes[i].classList.remove("hide") ui.cubes[i].classList.toggle("action", is_piece_action(i)) } else { ui.cubes[i].classList.add("hide") } } for (let i = 0; i < space_count; ++i) { layout_cubes(layout[i], space_layout_cube[i].x, space_layout_cube[i].y) ui.spaces[i].classList.toggle("action", is_space_action(i)) } for (let i = 0; i < 4; ++i) { layout_disc(view.pieces[36+i], ui.discs[i]) ui.discs[i].classList.toggle("action", is_piece_action(36+i)) } for (let i = 1; i < ui.cards.length; ++i) { ui.cards[i].classList.toggle("action", is_card_action('card', i)) ui.cards[i].classList.toggle("selected", i === view.selected_card) } action_button("commune", "Commune") action_button("versailles", "Versailles") action_button("spend", "Spend") action_button("draw", "Draw") action_button("momentum", "Momentum") action_button("event", "Event") action_button("political", "Political") action_button("military", "Military") action_button("public_opinion", "Public Opinion") action_button("paris", "Paris") action_button("ops", "Operations") action_button("place", "Place") action_button("remove", "Remove") action_button("replace", "Replace") action_button("end_remove", "End Remove") action_button("end_ops", "End Operations") action_button("end_event", "End Event") action_button("skip", "Skip") action_button("done", "Done") action_button("undo", "Undo") } build_user_interface() scroll_with_middle_mouse("main")