From 2ab8a12ee5aa715e04b35e21e24af647e8108683 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 29 Dec 2021 17:01:28 +0100 Subject: Simplify styles and html for play.html Change font css link. Clean up common play.css file. Mobile layout. --- info/rules.html | 2 +- play.css | 327 +++++++++++++++++++++++++++ play.html | 580 +++++++++++++----------------------------------- play.js | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ui.js | 667 -------------------------------------------------------- 5 files changed, 1115 insertions(+), 1100 deletions(-) create mode 100644 play.css create mode 100644 play.js delete mode 100644 ui.js diff --git a/info/rules.html b/info/rules.html index 5ad204f..7ceabb1 100644 --- a/info/rules.html +++ b/info/rules.html @@ -2,7 +2,7 @@ 300: Earth & Water Rules - + + -
-
-
Chat
-
-
+ -
- -
- - diff --git a/play.js b/play.js new file mode 100644 index 0000000..eed7667 --- /dev/null +++ b/play.js @@ -0,0 +1,639 @@ +"use strict"; + +const GREECE = "Greece"; +const PERSIA = "Persia"; + +const SPACES = [ + "Abydos", + "Athenai", + "Delphi", + "Ephesos", + "Eretria", + "Korinthos", + "Larissa", + "Naxos", + "Pella", + "Sparta", + "Thebai", + "reserve", + "extra", +]; + +const PORTS = { + "Abydos":{"x":866,"y":625,"w":138,"h":138,"layout_x":855,"layout_y":585,"wrap":4}, + "Ephesos":{"x":450,"y":765,"w":138,"h":138,"layout_x":424,"layout_y":743,"wrap":3}, + "Athenai":{"x":515,"y":353,"w":138,"h":138,"layout_x":521,"layout_y":379,"wrap":4}, + "Eretria":{"x":682,"y":481,"w":138,"h":138,"layout_x":683,"layout_y":510,"wrap":4}, + "Naxos":{"x":475,"y":575,"w":138,"h":138,"layout_x":503,"layout_y":581,"wrap":3}, + "Pella":{"x":931,"y":317,"w":138,"h":138,"layout_x":919,"layout_y":345,"wrap":4}, + "Sparta":{"x":259,"y":449,"w":138,"h":138,"layout_x":251,"layout_y":470,"wrap":4}, + "Thebai":{"x":689,"y":282,"w":138,"h":138,"layout_x":701,"layout_y":311,"wrap":4} +}; + +const CITIES = { + "Abydos":{"x":863,"y":654,"w":92,"h":90}, + "Ephesos":{"x":509,"y":766,"w":92,"h":90}, + "Athenai":{"x":537,"y":293,"w":84,"h":81}, + "Delphi":{"x":607,"y":92,"w":84,"h":81}, + "Eretria":{"x":668,"y":436,"w":84,"h":81}, + "Korinthos":{"x":442,"y":137,"w":84,"h":81}, + "Larissa":{"x":799,"y":107,"w":84,"h":81}, + "Naxos":{"x":408,"y":590,"w":84,"h":81}, + "Pella":{"x":960,"y":266,"w":84,"h":81}, + "Sparta":{"x":278,"y":344,"w":84,"h":81}, + "Thebai":{"x":671,"y":221,"w":84,"h":81} +}; + +let ui = { + cards: {}, + backs: {}, + cities: {}, + ports: {}, + greek_fleet: {}, + greek_army: {}, + persian_fleet: {}, + persian_army: {}, + all_fleets: [], + all_armies: [], + selected_armies: null, + selected_fleets: null, +}; + +let was_blank = true; +create_log_entry = function (text) { + let p = document.createElement("div"); + text = text.replace(/&/g, "&"); + text = text.replace(//g, ">"); + text = text.replace(/card (\d+)/g, + 'card $1'); + if (text.match(/Greece plays.*:\n/)) + text = text.replace(/:\n(.*)/, ':\n$1'); + if (text.match(/Persia plays.*:\n/)) + text = text.replace(/:\n(.*)/, ':\n$1'); + if (text.match(/^Start Campaign /)) { + p.className = 'st'; + text = text.substring(6); + } + if (text.match(/Supply Phase$/)) + p.className = 'hr'; + if (text.match(/(Greece|Persia|Nobody) scores/)) + p.className = 'hr'; + if (text.match(/Greek Preparation Phase$/)) + p.className = 'hr'; + if (was_blank && text.match(/^(Greece|Persia) (plays|passes)/)) + p.className = 'hr'; + was_blank = (text.length === 0) + p.innerHTML = text; + return p; +} + +function remove_from_array(array, item) { + let i = array.indexOf(item); + if (i >= 0) + array.splice(i, 1); +} + +function on_focus_card_tip(card_number) { + document.getElementById("tooltip").className = "card show card_" + card_number; +} + +function on_blur_card_tip() { + document.getElementById("tooltip").classList = "card"; +} + +function on_focus_discard(evt) { + if (game.discard) + document.getElementById("tooltip").className = "card show card_" + game.discard; + else + document.getElementById("tooltip").className = "card"; +} + +function on_blur_discard(evt) { + document.getElementById("tooltip").classList = "card"; +} + +function on_focus_bridge(evt) { document.getElementById("status").textContent = "Hellespont"; } +function on_focus_city(evt) { document.getElementById("status").textContent = evt.target.city; } +function on_focus_port(evt) { document.getElementById("status").textContent = evt.target.port + " (port)"; } +function on_blur(evt) { document.getElementById("status").textContent = ""; } + +function on_click_bridge(evt) { + if (game.actions && game.actions.destroy) + send_action('destroy'); + else if (game.actions && game.actions.build) + send_action('build'); +} + +function on_click_army(evt) { + if (game.land_movement && player) { + let here = (player == PERSIA ? ui.persian_army : ui.greek_army)[game.land_movement]; + if (here.includes(evt.target)) { + if (ui.selected_armies && ui.selected_armies.includes(evt.target)) + remove_from_array(ui.selected_armies, evt.target); + else + ui.selected_armies.push(evt.target); + } + update_ui(); + } + if (game.naval_transport && player) { + let here = (player == PERSIA ? ui.persian_army : ui.greek_army)[game.naval_movement]; + if (here.includes(evt.target)) { + if (ui.selected_armies && ui.selected_armies.includes(evt.target)) { + remove_from_array(ui.selected_armies, evt.target); + } else { + if (ui.selected_armies.length < ui.selected_fleets.length && ui.selected_armies.length < 3) + ui.selected_armies.push(here[ui.selected_armies.length]); + } + } + update_ui(); + } +} + +function on_click_fleet(evt) { + if (game.naval_movement && player) { + let here = (player == PERSIA ? ui.persian_fleet : ui.greek_fleet)[game.naval_movement]; + if (here.includes(evt.target)) { + if (ui.selected_fleets && ui.selected_fleets.includes(evt.target)) { + remove_from_array(ui.selected_fleets, evt.target); + while (ui.selected_armies.length > ui.selected_fleets.length) + ui.selected_armies.pop(); + } else { + ui.selected_fleets.push(evt.target); + } + } + update_ui(); + } +} + +function on_click_city(evt) { + if (!game.land_movement) + return send_action('city', evt.target.city); + if (game.actions && game.actions.city && game.actions.city.includes(evt.target.city)) { + let armies = ui.selected_armies.length; + if (armies > 0) + socket.emit('action', 'city', [evt.target.city, armies]); + } +} + +function on_click_port(evt) { + if (!game.naval_movement) + send_action('port', evt.target.port); + if (game.actions && game.actions.port && game.actions.port.includes(evt.target.port)) { + let fleets = ui.selected_fleets.length; + if (fleets > 0) { + let armies = ui.selected_armies.length; + socket.emit('action', 'port', [evt.target.port, fleets, armies]); + } + } +} + +function build_ui() { + for (let c = 1; c <= 16; ++c) { + ui.cards[c] = document.getElementById("card_"+c); + ui.cards[c].card = c; + ui.cards[c].addEventListener("click", on_card); + ui.backs[c] = document.getElementById("back_"+c); + } + + for (let city in CITIES) { + let info = CITIES[city]; + let e = ui.cities[city] = document.getElementById("city_" + city); + e.city = city; + e.addEventListener("mouseenter", on_focus_city); + e.addEventListener("mouseleave", on_blur); + e.addEventListener("click", on_click_city); + e.style.left = Math.round(info.x - info.w/2) + "px"; + e.style.top = Math.round(info.y - info.h/2) + "px"; + e.style.width = info.w + "px"; + e.style.height = info.h + "px"; + } + + for (let port in PORTS) { + let info = PORTS[port]; + let e = ui.ports[port] = document.getElementById("port_" + port); + e.port = port; + e.addEventListener("mouseenter", on_focus_port); + e.addEventListener("mouseleave", on_blur); + e.addEventListener("click", on_click_port); + e.style.left = Math.round(info.x - info.w/2) + "px"; + e.style.top = Math.round(info.y - info.h/2) + "px"; + e.style.width = info.w + "px"; + e.style.height = info.h + "px"; + } + + for (let city in CITIES) { + ui.greek_army[city] = []; + ui.greek_fleet[city] = []; + ui.persian_army[city] = []; + ui.persian_fleet[city] = []; + } + + ui.greek_army.reserve = []; + ui.greek_fleet.reserve = []; + ui.persian_army.reserve = []; + ui.persian_fleet.reserve = []; + + ui.greek_army.extra = []; + ui.greek_fleet.extra = []; + ui.persian_army.extra = []; + ui.persian_fleet.extra = []; + + for (let i = 0; i < 9; ++i) { + let e = document.getElementById("ga"+(i+1)); + e.sort_index = i; + ui.greek_army.extra.push(e); + ui.all_armies.push(e); + e.addEventListener("click", on_click_army); + } + for (let i = 0; i < 5; ++i) { + let e = document.getElementById("gf"+(i+1)); + e.sort_index = i; + ui.greek_fleet.extra.push(e); + ui.all_fleets.push(e); + e.addEventListener("click", on_click_fleet); + } + for (let i = 0; i < 24; ++i) { + let e = document.getElementById("pa"+(i+1)); + e.sort_index = i; + ui.persian_army.extra.push(e); + ui.all_armies.push(e); + e.addEventListener("click", on_click_army); + } + for (let i = 0; i < 6; ++i) { + let e = document.getElementById("pf"+(i+1)); + e.sort_index = i; + ui.persian_fleet.extra.push(e); + ui.all_fleets.push(e); + e.addEventListener("click", on_click_fleet); + } + + document.getElementById("bridge").addEventListener("click", on_click_bridge); + document.getElementById("bridge").addEventListener("mouseenter", on_focus_bridge); + document.getElementById("bridge").addEventListener("mouseleave", on_blur); + + document.getElementById("discard").addEventListener("mouseenter", on_focus_discard); + document.getElementById("discard").addEventListener("mouseleave", on_blur_discard); +} + +function greek_info() { + let text = ""; + if (game.g_cards == 1) + text += "1 card in hand"; + else + text += game.g_cards + " cards in hand"; + if (game.trigger.acropolis_on_fire) + text += "\nAcropolis on Fire!"; + if (game.trigger.carneia_festival) + text += "\nCarneia Festival!"; + return text; +} + +function persian_info() { + if (game.p_cards == 1) + return "1 card in hand"; + return game.p_cards + " cards in hand"; +} + +function show_marker(id, class_name, show = 1, enabled = 0) { + let elt = document.getElementById(id); + if (show) + class_name += " show"; + if (enabled) + class_name += " enabled"; + elt.className = class_name; +} + +function on_update() { + document.getElementById("greek_info").textContent = greek_info(); + document.getElementById("persian_info").textContent = persian_info(); + + if (!game.discard) + document.getElementById("discard").className = "card show card_back"; + else + document.getElementById("discard").className = "card show card_" + game.discard; + + document.getElementById("deck_info").textContent = + "Deck: " + game.deck_size + " \u2014 Discard: " + game.discard_size; + + action_button("battle", "Battle"); + action_button("build", "Build bridge"); + action_button("destroy", "Destroy bridge"); + action_button("draw", "Draw"); + action_button("pass", "Pass"); + action_button("next", "Next"); + action_button("undo", "Undo"); + + if (game.actions && game.actions.destroy) + document.getElementById("bridge").className = "show destroy"; + else if (game.actions && game.actions.build) + document.getElementById("bridge").className = "show build" + else if (game.trigger.hellespont) + document.getElementById("bridge").className = "show"; + else + document.getElementById("bridge").className = ""; + + show_marker("darius", "persian_army", game.trigger.darius); + show_marker("xerxes", "persian_army", game.trigger.xerxes); + show_marker("artemisia", "persian_fleet", game.trigger.artemisia); + show_marker("miltiades", "greek_army", game.trigger.miltiades); + show_marker("themistocles", "greek_army", game.trigger.themistocles); + show_marker("leonidas", "greek_army", game.trigger.leonidas); + show_marker("campaign", "marker campaign_" + game.campaign); + + if (game.vp < 0) + show_marker("vp", "marker vp_g" + (-game.vp)); + else if (game.vp > 0) + show_marker("vp", "marker vp_p" + game.vp); + else + show_marker("vp", "marker vp_0"); + + let hand = game.hand; + let draw = game.draw; + for (let c = 1; c <= 16; ++c) { + ui.cards[c].classList.remove('enabled'); + if (hand && hand.includes(c)) + ui.cards[c].classList.add('show'); + else + ui.cards[c].classList.remove('show'); + if (c <= draw) + ui.backs[c].classList.add('show'); + else + ui.backs[c].classList.remove('show'); + } + + if (game.show_greek_hand) + document.getElementById("hand").classList.add("greek"); + else + document.getElementById("hand").classList.remove("greek"); + + function update_units(index, elements) { + let overflow = []; + let extra = elements.extra; + + // remove if too many + for (let space in game.units) { + let list = elements[space]; + let n = game.units[space][index] | 0; + while (list.length > n) + overflow.push(list.shift()); + } + + // add if not enough + for (let space in game.units) { + let list = elements[space]; + let n = game.units[space][index]; + while (list.length < n) { + if (overflow.length > 0) { + list.unshift(overflow.pop()); + } else { + let e = extra.pop(); + e.classList.add("show"); + list.unshift(e); + } + } + } + + // and hide the overflow + while (overflow.length > 0) { + let e = overflow.pop(); + e.classList.remove("show"); + extra.push(e); + } + } + + update_units(0, ui.greek_army); + update_units(1, ui.persian_army); + update_units(2, ui.greek_fleet); + update_units(3, ui.persian_fleet); + + ui.selected_armies = null; + if (game.land_movement) { + if (player == PERSIA) + ui.selected_armies = ui.persian_army[game.land_movement].slice(); + if (player == GREECE) + ui.selected_armies = ui.greek_army[game.land_movement].slice(); + } + + ui.selected_fleets = null; + if (game.naval_movement) { + if (player == PERSIA) { + ui.selected_fleets = ui.persian_fleet[game.naval_movement].slice(); + ui.selected_armies = []; + } + if (player == GREECE) { + ui.selected_fleets = ui.greek_fleet[game.naval_movement].slice(); + ui.selected_armies = []; + } + } + + for (let city in CITIES) + ui.cities[city].classList.remove('enabled'); + for (let port in PORTS) + ui.ports[port].classList.remove('enabled'); + + if (game.actions && game.actions.city) { + for (let city of game.actions.city) + ui.cities[city].classList.add('enabled'); + } + if (game.actions && game.actions.port) { + for (let port of game.actions.port) + ui.ports[port].classList.add('enabled'); + } + + update_ui(); +} + +function update_ui() { + function layout_fleets(a, b, xorig, yorig, wrap) { + if (a.length + b.length > 0) { + let w = 26; + let h = 20; + let xstep = w + 2; + let ystep = h + 0; + let stagger = 14; + let line, para = []; + let i = 0, k = 0; + para.push(line = []); + for (let e of a) { + if (i == wrap - k) { para.push(line = []); i = 0; k = 1 - k; } + line.push(e); + ++i; + } + if (i != 0 && b.length > 0) { para.push(line = []); i = 0; k = 1 - k; } + for (let e of b) { + if (i == wrap - k) { para.push(line = []); i = 0; k = 1 - k; } + line.push(e); + ++i; + } + let y = yorig - Math.floor(ystep * para.length / 2); + k = 0; + let cw = (para.length > 1 ? wrap : para[0].length); + for (let row = 0; row < para.length; ++row) { + let x = xorig - Math.floor(xstep * cw / 2) + k * stagger; + for (let col = 0; col < para[row].length; ++col) { + para[row][col].style.left = x + "px"; + para[row][col].style.top = y + "px"; + x += xstep; + } + y += ystep; + k = 1 - k; + } + } + } + + function layout_armies(list, xorig, yorig) { + const dx = 12; + const dy = 8; + if (list.length > 0) { + let ncol = Math.round(Math.sqrt(list.length)); + let nrow = Math.ceil(list.length / ncol); + function layout_army(row, col, e, z) { + let x = xorig - (row * dx - col * dx) - 10 + (nrow-ncol) * 6; + let y = yorig - (row * dy + col * dy) - 13 + (nrow-1) * 8; + e.style.left = x + "px"; + e.style.top = y + "px"; + e.style.zIndex = z; + } + let z = 50; + let i = 0; + if (player == GREECE) + for (let row = nrow-1; row >= 0; --row) + for (let col = ncol-1; col >= 0 && i < list.length; --col) + layout_army(row, col, list[i++], z--); + else + for (let row = 0; row < nrow; ++row) + for (let col = 0; col < ncol && i < list.length; ++col) + layout_army(row, col, list[list.length-(++i)], z--); + } + } + + function list_armies(city) { + let ga = ui.greek_army[city]; + let pa = ui.persian_army[city]; + if (game.transport && game.transport.where == city) { + if (game.transport.who == GREECE) + ga = ga.slice(game.transport.count); + if (game.transport.who == PERSIA) + pa = pa.slice(game.transport.count); + } + if (game.naval_movement) { + ga = ga.filter(a => !ui.selected_armies.includes(a)); + pa = pa.filter(a => !ui.selected_armies.includes(a)); + } + return ga.concat(pa); + } + + layout_fleets(ui.greek_fleet.reserve, [], 95, 150, 5); + layout_fleets(ui.persian_fleet.reserve, [], 1240-95, 878-150, 6); + layout_armies(ui.greek_army.reserve, 80, 220); + layout_armies(ui.persian_army.reserve, 1240-80, 878-220) + + for (let port in PORTS) + layout_fleets(ui.greek_fleet[port], ui.persian_fleet[port], + PORTS[port].layout_x, PORTS[port].layout_y, PORTS[port].wrap); + + for (let city in CITIES) + layout_armies(list_armies(city), + CITIES[city].x, CITIES[city].y); + + function layout_transport(a, f) { + a.style.left = (parseInt(f.style.left) + 13 - 11) + "px"; + if (player == GREECE) + a.style.top = (parseInt(f.style.top) + 10 - 13 + 10) + "px"; + else + a.style.top = (parseInt(f.style.top) + 10 - 13 - 10) + "px"; + a.style.zIndex = 2; + } + + if (game.transport) { + let city = game.transport.where; + let alist = (game.transport.who == GREECE ? ui.greek_army : ui.persian_army)[city]; + let flist = (game.transport.who == GREECE ? ui.greek_fleet : ui.persian_fleet)[city]; + for (let i = 0; i < game.transport.count; ++i) + layout_transport(alist[i], flist[i]); + } + if (game.naval_movement) { + for (let i = 0; i < ui.selected_armies.length; ++i) + layout_transport(ui.selected_armies[i], ui.selected_fleets[i]); + } + + for (let e of ui.all_armies) + if (ui.selected_armies && ui.selected_armies.includes(e)) + e.classList.add("selected"); + else + e.classList.remove("selected"); + + for (let e of ui.all_fleets) + if (ui.selected_fleets && ui.selected_fleets.includes(e)) + e.classList.add("selected"); + else + e.classList.remove("selected"); +} + +let current_popup_card = 0; + +function show_popup_menu(evt, list) { + document.querySelectorAll("#popup div").forEach(e => e.classList.remove('enabled')); + for (let item of list) { + let e = document.getElementById("menu_" + item); + e.classList.add('enabled'); + } + let popup = document.getElementById("popup"); + popup.style.display = 'block'; + popup.style.left = (evt.clientX-50) + "px"; + popup.style.top = (evt.clientY-12) + "px"; + ui.cards[current_popup_card].classList.add("selected"); +} + +function hide_popup_menu() { + let popup = document.getElementById("popup"); + popup.style.display = 'none'; + if (current_popup_card) { + ui.cards[current_popup_card].classList.remove("selected"); + current_popup_card = 0; + } +} + +function on_card_event() { + send_action('card_event', current_popup_card); + hide_popup_menu(); +} +function on_card_move() { + send_action('card_move', current_popup_card); + hide_popup_menu(); +} + +function is_card_action(action, card) { + return game.actions && game.actions[action] && game.actions[action].includes(card); +} + +function on_card(evt) { + if (game.actions) { + let card = evt.target.card; + if (is_card_action('discard', card)) { + send_action('discard', card); + } else { + let menu = []; + if (is_card_action('card_event', card)) + menu.push('card_event'); + if (is_card_action('card_move', card)) + menu.push('card_move'); + if (menu.length > 0) { + current_popup_card = card; + show_popup_menu(evt, menu); + } + } + } +} + +function toggle_markers() { + document.getElementById("map").classList.toggle("hide_markers"); +} + +if (param_role == GREECE) + document.getElementById("map").classList.add("greek"); + +build_ui(); +scroll_with_middle_mouse("main", 2); +init_client(["Greece", "Persia"]); diff --git a/ui.js b/ui.js deleted file mode 100644 index d088a26..0000000 --- a/ui.js +++ /dev/null @@ -1,667 +0,0 @@ -"use strict"; - -const GREECE = "Greece"; -const PERSIA = "Persia"; - -const SPACES = [ - "Abydos", - "Athenai", - "Delphi", - "Ephesos", - "Eretria", - "Korinthos", - "Larissa", - "Naxos", - "Pella", - "Sparta", - "Thebai", - "reserve", - "extra", -]; - -const PORTS = { - "Abydos":{"x":866,"y":625,"w":138,"h":138,"layout_x":855,"layout_y":585,"wrap":4}, - "Ephesos":{"x":450,"y":765,"w":138,"h":138,"layout_x":424,"layout_y":743,"wrap":3}, - "Athenai":{"x":515,"y":353,"w":138,"h":138,"layout_x":521,"layout_y":379,"wrap":4}, - "Eretria":{"x":682,"y":481,"w":138,"h":138,"layout_x":683,"layout_y":510,"wrap":4}, - "Naxos":{"x":475,"y":575,"w":138,"h":138,"layout_x":503,"layout_y":581,"wrap":3}, - "Pella":{"x":931,"y":317,"w":138,"h":138,"layout_x":919,"layout_y":345,"wrap":4}, - "Sparta":{"x":259,"y":449,"w":138,"h":138,"layout_x":251,"layout_y":470,"wrap":4}, - "Thebai":{"x":689,"y":282,"w":138,"h":138,"layout_x":701,"layout_y":311,"wrap":4} -}; - -const CITIES = { - "Abydos":{"x":863,"y":654,"w":92,"h":90}, - "Ephesos":{"x":509,"y":766,"w":92,"h":90}, - "Athenai":{"x":537,"y":293,"w":84,"h":81}, - "Delphi":{"x":607,"y":92,"w":84,"h":81}, - "Eretria":{"x":668,"y":436,"w":84,"h":81}, - "Korinthos":{"x":442,"y":137,"w":84,"h":81}, - "Larissa":{"x":799,"y":107,"w":84,"h":81}, - "Naxos":{"x":408,"y":590,"w":84,"h":81}, - "Pella":{"x":960,"y":266,"w":84,"h":81}, - "Sparta":{"x":278,"y":344,"w":84,"h":81}, - "Thebai":{"x":671,"y":221,"w":84,"h":81} -}; - -let ui = { - cards: {}, - backs: {}, - cities: {}, - ports: {}, - greek_fleet: {}, - greek_army: {}, - persian_fleet: {}, - persian_army: {}, - all_fleets: [], - all_armies: [], - selected_armies: null, - selected_fleets: null, -}; - -let was_blank = true; -create_log_entry = function (text) { - let p = document.createElement("div"); - text = text.replace(/&/g, "&"); - text = text.replace(//g, ">"); - text = text.replace(/card (\d+)/g, - 'card $1'); - if (text.match(/Greece plays.*:\n/)) - text = text.replace(/:\n(.*)/, ':\n$1'); - if (text.match(/Persia plays.*:\n/)) - text = text.replace(/:\n(.*)/, ':\n$1'); - if (text.match(/^Start Campaign /)) { - p.className = 'st'; - text = text.substring(6); - } - if (text.match(/Supply Phase$/)) - p.className = 'hr'; - if (text.match(/(Greece|Persia|Nobody) scores/)) - p.className = 'hr'; - if (text.match(/Greek Preparation Phase$/)) - p.className = 'hr'; - if (was_blank && text.match(/^(Greece|Persia) (plays|passes)/)) - p.className = 'hr'; - was_blank = (text.length === 0) - p.innerHTML = text; - return p; -} - -function remove_from_array(array, item) { - let i = array.indexOf(item); - if (i >= 0) - array.splice(i, 1); -} - -function on_focus_card_tip(card_number) { - document.getElementById("tooltip").className = "card show card_" + card_number; -} - -function on_blur_card_tip() { - document.getElementById("tooltip").classList = "card"; -} - -function on_focus_discard(evt) { - if (game.discard) - document.getElementById("tooltip").className = "card show card_" + game.discard; - else - document.getElementById("tooltip").className = "card"; -} - -function on_blur_discard(evt) { - document.getElementById("tooltip").classList = "card"; -} - -function on_focus_bridge(evt) { document.getElementById("status").textContent = "Hellespont"; } -function on_focus_city(evt) { document.getElementById("status").textContent = evt.target.city; } -function on_focus_port(evt) { document.getElementById("status").textContent = evt.target.port + " (port)"; } -function on_blur(evt) { document.getElementById("status").textContent = ""; } - -function on_click_bridge(evt) { - if (game.actions && game.actions.destroy) - send_action('destroy'); - else if (game.actions && game.actions.build) - send_action('build'); -} - -function on_click_army(evt) { - if (game.land_movement && player) { - let here = (player == PERSIA ? ui.persian_army : ui.greek_army)[game.land_movement]; - if (here.includes(evt.target)) { - if (ui.selected_armies && ui.selected_armies.includes(evt.target)) - remove_from_array(ui.selected_armies, evt.target); - else - ui.selected_armies.push(evt.target); - } - update_ui(); - } - if (game.naval_transport && player) { - let here = (player == PERSIA ? ui.persian_army : ui.greek_army)[game.naval_movement]; - if (here.includes(evt.target)) { - if (ui.selected_armies && ui.selected_armies.includes(evt.target)) { - remove_from_array(ui.selected_armies, evt.target); - } else { - if (ui.selected_armies.length < ui.selected_fleets.length && ui.selected_armies.length < 3) - ui.selected_armies.push(here[ui.selected_armies.length]); - } - } - update_ui(); - } -} - -function on_click_fleet(evt) { - if (game.naval_movement && player) { - let here = (player == PERSIA ? ui.persian_fleet : ui.greek_fleet)[game.naval_movement]; - if (here.includes(evt.target)) { - if (ui.selected_fleets && ui.selected_fleets.includes(evt.target)) { - remove_from_array(ui.selected_fleets, evt.target); - while (ui.selected_armies.length > ui.selected_fleets.length) - ui.selected_armies.pop(); - } else { - ui.selected_fleets.push(evt.target); - } - } - update_ui(); - } -} - -function on_click_city(evt) { - if (!game.land_movement) - return send_action('city', evt.target.city); - if (game.actions && game.actions.city && game.actions.city.includes(evt.target.city)) { - let armies = ui.selected_armies.length; - if (armies > 0) - socket.emit('action', 'city', [evt.target.city, armies]); - } -} - -function on_click_port(evt) { - if (!game.naval_movement) - send_action('port', evt.target.port); - if (game.actions && game.actions.port && game.actions.port.includes(evt.target.port)) { - let fleets = ui.selected_fleets.length; - if (fleets > 0) { - let armies = ui.selected_armies.length; - socket.emit('action', 'port', [evt.target.port, fleets, armies]); - } - } -} - -function build_ui() { - for (let c = 1; c <= 16; ++c) { - ui.cards[c] = document.getElementById("card_"+c); - ui.cards[c].card = c; - ui.cards[c].addEventListener("click", on_card); - ui.backs[c] = document.getElementById("back_"+c); - } - - for (let city in CITIES) { - let info = CITIES[city]; - let e = ui.cities[city] = document.getElementById("city_" + city); - e.city = city; - e.addEventListener("mouseenter", on_focus_city); - e.addEventListener("mouseleave", on_blur); - e.addEventListener("click", on_click_city); - e.style.left = Math.round(info.x - info.w/2) + "px"; - e.style.top = Math.round(info.y - info.h/2) + "px"; - e.style.width = info.w + "px"; - e.style.height = info.h + "px"; - } - - for (let port in PORTS) { - let info = PORTS[port]; - let e = ui.ports[port] = document.getElementById("port_" + port); - e.port = port; - e.addEventListener("mouseenter", on_focus_port); - e.addEventListener("mouseleave", on_blur); - e.addEventListener("click", on_click_port); - e.style.left = Math.round(info.x - info.w/2) + "px"; - e.style.top = Math.round(info.y - info.h/2) + "px"; - e.style.width = info.w + "px"; - e.style.height = info.h + "px"; - } - - for (let city in CITIES) { - ui.greek_army[city] = []; - ui.greek_fleet[city] = []; - ui.persian_army[city] = []; - ui.persian_fleet[city] = []; - } - - ui.greek_army.reserve = []; - ui.greek_fleet.reserve = []; - ui.persian_army.reserve = []; - ui.persian_fleet.reserve = []; - - ui.greek_army.extra = []; - ui.greek_fleet.extra = []; - ui.persian_army.extra = []; - ui.persian_fleet.extra = []; - - for (let i = 0; i < 9; ++i) { - let e = document.getElementById("ga"+(i+1)); - e.sort_index = i; - ui.greek_army.extra.push(e); - ui.all_armies.push(e); - e.addEventListener("click", on_click_army); - } - for (let i = 0; i < 5; ++i) { - let e = document.getElementById("gf"+(i+1)); - e.sort_index = i; - ui.greek_fleet.extra.push(e); - ui.all_fleets.push(e); - e.addEventListener("click", on_click_fleet); - } - for (let i = 0; i < 24; ++i) { - let e = document.getElementById("pa"+(i+1)); - e.sort_index = i; - ui.persian_army.extra.push(e); - ui.all_armies.push(e); - e.addEventListener("click", on_click_army); - } - for (let i = 0; i < 6; ++i) { - let e = document.getElementById("pf"+(i+1)); - e.sort_index = i; - ui.persian_fleet.extra.push(e); - ui.all_fleets.push(e); - e.addEventListener("click", on_click_fleet); - } - - document.getElementById("bridge").addEventListener("click", on_click_bridge); - document.getElementById("bridge").addEventListener("mouseenter", on_focus_bridge); - document.getElementById("bridge").addEventListener("mouseleave", on_blur); - - document.getElementById("discard").addEventListener("mouseenter", on_focus_discard); - document.getElementById("discard").addEventListener("mouseleave", on_blur_discard); -} - -function greek_info() { - let text = ""; - if (game.g_cards == 1) - text += "1 card in hand"; - else - text += game.g_cards + " cards in hand"; - if (game.trigger.acropolis_on_fire) - text += "\nAcropolis on Fire!"; - if (game.trigger.carneia_festival) - text += "\nCarneia Festival!"; - return text; -} - -function persian_info() { - if (game.p_cards == 1) - return "1 card in hand"; - return game.p_cards + " cards in hand"; -} - -function show_marker(id, class_name, show = 1, enabled = 0) { - let elt = document.getElementById(id); - if (show) - class_name += " show"; - if (enabled) - class_name += " enabled"; - elt.className = class_name; -} - -function show_undo_button(sel, action, use_label = false) { - let button = document.querySelector(sel); - if (game.actions) { - button.classList.remove("hide"); - if (game.actions && action in game.actions) { - if (game.actions[action]) { - if (use_label) - button.textContent = game.actions[action]; - button.disabled = false; - } else { - button.disabled = true; - } - } else { - button.disabled = true; - } - } else { - button.classList.add("hide"); - } -} - -function on_update() { - document.getElementById("greek_info").textContent = greek_info(); - document.getElementById("persian_info").textContent = persian_info(); - - if (!game.discard) - document.getElementById("discard").className = "card show card_back"; - else - document.getElementById("discard").className = "card show card_" + game.discard; - - document.getElementById("deck_info").textContent = - "Deck: " + game.deck_size + " \u2014 Discard: " + game.discard_size; - - show_action_button("#button_battle", "battle"); - show_action_button("#button_build", "build"); - show_action_button("#button_destroy", "destroy"); - show_action_button("#button_draw", "draw"); - show_action_button("#button_next", "next"); - show_action_button("#button_pass", "pass"); - show_undo_button("#button_undo", "undo"); - - if (game.actions && game.actions.destroy) - document.getElementById("bridge").className = "show destroy"; - else if (game.actions && game.actions.build) - document.getElementById("bridge").className = "show build" - else if (game.trigger.hellespont) - document.getElementById("bridge").className = "show"; - else - document.getElementById("bridge").className = ""; - - show_marker("darius", "persian_army", game.trigger.darius); - show_marker("xerxes", "persian_army", game.trigger.xerxes); - show_marker("artemisia", "persian_fleet", game.trigger.artemisia); - show_marker("miltiades", "greek_army", game.trigger.miltiades); - show_marker("themistocles", "greek_army", game.trigger.themistocles); - show_marker("leonidas", "greek_army", game.trigger.leonidas); - show_marker("campaign", "marker campaign_" + game.campaign); - - if (game.vp < 0) - show_marker("vp", "marker vp_g" + (-game.vp)); - else if (game.vp > 0) - show_marker("vp", "marker vp_p" + game.vp); - else - show_marker("vp", "marker vp_0"); - - let hand = game.hand; - let draw = game.draw; - for (let c = 1; c <= 16; ++c) { - ui.cards[c].classList.remove('enabled'); - if (hand && hand.includes(c)) - ui.cards[c].classList.add('show'); - else - ui.cards[c].classList.remove('show'); - if (c <= draw) - ui.backs[c].classList.add('show'); - else - ui.backs[c].classList.remove('show'); - } - - if (game.show_greek_hand) - document.getElementById("hand").classList.add("greek"); - else - document.getElementById("hand").classList.remove("greek"); - - function update_units(index, elements) { - let overflow = []; - let extra = elements.extra; - - // remove if too many - for (let space in game.units) { - let list = elements[space]; - let n = game.units[space][index] | 0; - while (list.length > n) - overflow.push(list.shift()); - } - - // add if not enough - for (let space in game.units) { - let list = elements[space]; - let n = game.units[space][index]; - while (list.length < n) { - if (overflow.length > 0) { - list.unshift(overflow.pop()); - } else { - let e = extra.pop(); - e.classList.add("show"); - list.unshift(e); - } - } - } - - // and hide the overflow - while (overflow.length > 0) { - let e = overflow.pop(); - e.classList.remove("show"); - extra.push(e); - } - } - - update_units(0, ui.greek_army); - update_units(1, ui.persian_army); - update_units(2, ui.greek_fleet); - update_units(3, ui.persian_fleet); - - ui.selected_armies = null; - if (game.land_movement) { - if (player == PERSIA) - ui.selected_armies = ui.persian_army[game.land_movement].slice(); - if (player == GREECE) - ui.selected_armies = ui.greek_army[game.land_movement].slice(); - } - - ui.selected_fleets = null; - if (game.naval_movement) { - if (player == PERSIA) { - ui.selected_fleets = ui.persian_fleet[game.naval_movement].slice(); - ui.selected_armies = []; - } - if (player == GREECE) { - ui.selected_fleets = ui.greek_fleet[game.naval_movement].slice(); - ui.selected_armies = []; - } - } - - for (let city in CITIES) - ui.cities[city].classList.remove('enabled'); - for (let port in PORTS) - ui.ports[port].classList.remove('enabled'); - - if (game.actions && game.actions.city) { - for (let city of game.actions.city) - ui.cities[city].classList.add('enabled'); - } - if (game.actions && game.actions.port) { - for (let port of game.actions.port) - ui.ports[port].classList.add('enabled'); - } - - update_ui(); -} - -function update_ui() { - function layout_fleets(a, b, xorig, yorig, wrap) { - if (a.length + b.length > 0) { - let w = 26; - let h = 20; - let xstep = w + 2; - let ystep = h + 0; - let stagger = 14; - let line, para = []; - let i = 0, k = 0; - para.push(line = []); - for (let e of a) { - if (i == wrap - k) { para.push(line = []); i = 0; k = 1 - k; } - line.push(e); - ++i; - } - if (i != 0 && b.length > 0) { para.push(line = []); i = 0; k = 1 - k; } - for (let e of b) { - if (i == wrap - k) { para.push(line = []); i = 0; k = 1 - k; } - line.push(e); - ++i; - } - let y = yorig - Math.floor(ystep * para.length / 2); - k = 0; - let cw = (para.length > 1 ? wrap : para[0].length); - for (let row = 0; row < para.length; ++row) { - let x = xorig - Math.floor(xstep * cw / 2) + k * stagger; - for (let col = 0; col < para[row].length; ++col) { - para[row][col].style.left = x + "px"; - para[row][col].style.top = y + "px"; - x += xstep; - } - y += ystep; - k = 1 - k; - } - } - } - - function layout_armies(list, xorig, yorig) { - const dx = 12; - const dy = 8; - if (list.length > 0) { - let ncol = Math.round(Math.sqrt(list.length)); - let nrow = Math.ceil(list.length / ncol); - function layout_army(row, col, e, z) { - let x = xorig - (row * dx - col * dx) - 10 + (nrow-ncol) * 6; - let y = yorig - (row * dy + col * dy) - 13 + (nrow-1) * 8; - e.style.left = x + "px"; - e.style.top = y + "px"; - e.style.zIndex = z; - } - let z = 50; - let i = 0; - if (player == GREECE) - for (let row = nrow-1; row >= 0; --row) - for (let col = ncol-1; col >= 0 && i < list.length; --col) - layout_army(row, col, list[i++], z--); - else - for (let row = 0; row < nrow; ++row) - for (let col = 0; col < ncol && i < list.length; ++col) - layout_army(row, col, list[list.length-(++i)], z--); - } - } - - function list_armies(city) { - let ga = ui.greek_army[city]; - let pa = ui.persian_army[city]; - if (game.transport && game.transport.where == city) { - if (game.transport.who == GREECE) - ga = ga.slice(game.transport.count); - if (game.transport.who == PERSIA) - pa = pa.slice(game.transport.count); - } - if (game.naval_movement) { - ga = ga.filter(a => !ui.selected_armies.includes(a)); - pa = pa.filter(a => !ui.selected_armies.includes(a)); - } - return ga.concat(pa); - } - - layout_fleets(ui.greek_fleet.reserve, [], 95, 150, 5); - layout_fleets(ui.persian_fleet.reserve, [], 1240-95, 878-150, 6); - layout_armies(ui.greek_army.reserve, 80, 220); - layout_armies(ui.persian_army.reserve, 1240-80, 878-220) - - for (let port in PORTS) - layout_fleets(ui.greek_fleet[port], ui.persian_fleet[port], - PORTS[port].layout_x, PORTS[port].layout_y, PORTS[port].wrap); - - for (let city in CITIES) - layout_armies(list_armies(city), - CITIES[city].x, CITIES[city].y); - - function layout_transport(a, f) { - a.style.left = (parseInt(f.style.left) + 13 - 11) + "px"; - if (player == GREECE) - a.style.top = (parseInt(f.style.top) + 10 - 13 + 10) + "px"; - else - a.style.top = (parseInt(f.style.top) + 10 - 13 - 10) + "px"; - a.style.zIndex = 2; - } - - if (game.transport) { - let city = game.transport.where; - let alist = (game.transport.who == GREECE ? ui.greek_army : ui.persian_army)[city]; - let flist = (game.transport.who == GREECE ? ui.greek_fleet : ui.persian_fleet)[city]; - for (let i = 0; i < game.transport.count; ++i) - layout_transport(alist[i], flist[i]); - } - if (game.naval_movement) { - for (let i = 0; i < ui.selected_armies.length; ++i) - layout_transport(ui.selected_armies[i], ui.selected_fleets[i]); - } - - for (let e of ui.all_armies) - if (ui.selected_armies && ui.selected_armies.includes(e)) - e.classList.add("selected"); - else - e.classList.remove("selected"); - - for (let e of ui.all_fleets) - if (ui.selected_fleets && ui.selected_fleets.includes(e)) - e.classList.add("selected"); - else - e.classList.remove("selected"); -} - -function on_destroy() { if (game.actions) { send_action('destroy'); } } -function on_battle() { if (game.actions) { send_action('battle'); } } -function on_build() { if (game.actions) { send_action('build'); } } -function on_draw() { if (game.actions) { send_action('draw'); } } -function on_next() { if (game.actions) { send_action('next'); } } -function on_pass() { if (game.actions) { send_action('pass'); } } -function on_undo() { if (game.actions) { send_action('undo'); } } - -let current_popup_card = 0; - -function show_popup_menu(evt, list) { - document.querySelectorAll("#popup div").forEach(e => e.classList.remove('enabled')); - for (let item of list) { - let e = document.getElementById("menu_" + item); - e.classList.add('enabled'); - } - let popup = document.getElementById("popup"); - popup.style.display = 'block'; - popup.style.left = (evt.clientX-50) + "px"; - popup.style.top = (evt.clientY-12) + "px"; - ui.cards[current_popup_card].classList.add("selected"); -} - -function hide_popup_menu() { - let popup = document.getElementById("popup"); - popup.style.display = 'none'; - if (current_popup_card) { - ui.cards[current_popup_card].classList.remove("selected"); - current_popup_card = 0; - } -} - -function on_card_event() { - send_action('card_event', current_popup_card); - hide_popup_menu(); -} -function on_card_move() { - send_action('card_move', current_popup_card); - hide_popup_menu(); -} - -function is_card_action(action, card) { - return game.actions && game.actions[action] && game.actions[action].includes(card); -} - -function on_card(evt) { - if (game.actions) { - let card = evt.target.card; - if (is_card_action('discard', card)) { - send_action('discard', card); - } else { - let menu = []; - if (is_card_action('card_event', card)) - menu.push('card_event'); - if (is_card_action('card_move', card)) - menu.push('card_move'); - if (menu.length > 0) { - current_popup_card = card; - show_popup_menu(evt, menu); - } - } - } -} - -function toggle_markers() { - document.getElementById("map").classList.toggle("hide_markers"); -} - -if (param_role == GREECE) - document.getElementById("map").classList.add("greek"); - -build_ui(); -scroll_with_middle_mouse("#grid_center", 2); -init_client(["Greece", "Persia"]); -- cgit v1.2.3