diff options
-rw-r--r-- | about.html | 8 | ||||
-rw-r--r-- | map.jpg | bin | 325614 -> 324322 bytes | |||
-rw-r--r-- | play.css | 127 | ||||
-rw-r--r-- | play.html | 63 | ||||
-rw-r--r-- | play.js | 137 | ||||
-rw-r--r-- | rules.js | 2 |
6 files changed, 166 insertions, 171 deletions
@@ -14,13 +14,13 @@ consequences. As the bashaw of Tripoli, the other player will continue the lucrative piracy of the fearsome corsairs while countering the American threat on land and sea. -<br clear=left> - -<p> -Designer: Kevin Bertram <p> +Designer: Kevin Bertram. +<br> Copyright © 2020 <a href="https://www.fortcircle.com/games/">Fort Circle Games</a>. +<br> +Programming © 2021 Tor Andersson. <ul> <li><a href="/shores-of-tripoli/info/rules.html">Rules of the Game</a> Binary files differ@@ -23,18 +23,28 @@ body.United_States header.your_turn { background-color: skyblue; } flex-wrap: wrap; min-height: 300px; justify-content: left; - max-width: 2476px; - margin: 15px auto; -} - -.hand .card { - margin: 10px; + max-width: 1620px; + margin: 0 auto; + padding: 15px; + gap: 15px; } .hand_separator { + margin: 0 auto; + max-width: 1650px; border-bottom: 2px dotted gainsboro; } +@media (max-width: 800px) { + .hand, .hand_separator { + min-width: 1650px; + } +} + +@media (max-height: 700px) { + .card_info { display: none } +} + body.Observer #hand_cards { display: none; } body.Observer .hand_separator { display: none; } @@ -53,7 +63,7 @@ body.Observer .hand_separator { display: none; } height: 350px; border-radius: 12px; display: none; - box-shadow: 1px 1px 5px rgba(0,0,0,0.5); + box-shadow: 1px 1px 5px #0008; } .card.show { @@ -92,9 +102,10 @@ body.Observer .hand_separator { display: none; } padding: 4px; } -#tooltip.card { +#tooltip { + pointer-events: none; position: fixed; - z-index: 100; + z-index: 600; right: 240px; top: 60px; } @@ -102,19 +113,32 @@ body.Observer .hand_separator { display: none; } display: block; } +@media (max-width: 800px) { + #tooltip { + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + } +} + /* MAP */ #mapwrap { - box-shadow: 0px 0px 15px rgba(0,0,0,0.8); - width: 2476px; - height: 801px; + box-shadow: 0px 0px 15px #0008; + width: 1650px; + height: 532px; + margin-bottom: 5px; } #map { display: block; - width: 2476px; - height: 801px; + width: 1650px; + height: 532px; background-color: black; + background-image: url(map.jpg); + background-size: 1650px 532px; } svg { @@ -136,7 +160,7 @@ svg circle, .piece { pointer-events: none; background-size: cover; background-repeat: no-repeat; - filter: drop-shadow(1px 1px 4px rgba(0,0,0,0.5)); + filter: drop-shadow(0px 1px 3px #0008); } .piece.highlight { @@ -146,20 +170,20 @@ svg circle, .piece { } .piece.damaged { - filter: brightness(60%) drop-shadow(1px 1px 4px rgba(0,0,0,0.5)); + filter: brightness(60%) drop-shadow(0px 1px 3px #0008); } -.piece.us_frigate { width: 42px; height: 29px; background-image: url("icons/us_frigate.svg"); } -.piece.se_frigate { width: 42px; height: 29px; background-image: url("icons/se_frigate.svg"); } -.piece.tr_frigate { width: 42px; height: 29px; background-image: url("icons/tr_frigate.svg"); } -.piece.us_gunboat { width: 33px; height: 27px; background-image: url("icons/us_gunboat.svg"); } -.piece.tr_corsair { width: 33px; height: 27px; background-image: url("icons/tr_corsair.svg"); } -.piece.al_corsair { width: 33px; height: 27px; background-image: url("icons/al_corsair.svg"); } -.piece.us_marine { width: 21px; height: 21px; background-image: url("icons/us_marine.svg"); } -.piece.ar_infantry { width: 21px; height: 21px; background-image: url("icons/ar_infantry.svg"); } -.piece.tr_infantry { width: 21px; height: 21px; background-image: url("icons/tr_infantry.svg"); } -.piece.gold { width: 40px; height: 40px; background-image: url("icons/gold.png"); } -.piece.marker { width: 50px; height: 50px; border-radius: 50%; background-color: #444; border: 2px solid black; } +.piece.us_frigate { width: 29px; height: 20px; background-image: url("icons/us_frigate.svg"); } +.piece.se_frigate { width: 29px; height: 20px; background-image: url("icons/se_frigate.svg"); } +.piece.tr_frigate { width: 29px; height: 20px; background-image: url("icons/tr_frigate.svg"); } +.piece.us_gunboat { width: 22px; height: 18px; background-image: url("icons/us_gunboat.svg"); } +.piece.tr_corsair { width: 22px; height: 18px; background-image: url("icons/tr_corsair.svg"); } +.piece.al_corsair { width: 22px; height: 18px; background-image: url("icons/al_corsair.svg"); } +.piece.us_marine { width: 16px; height: 16px; background-image: url("icons/us_marine.svg"); } +.piece.ar_infantry { width: 16px; height: 16px; background-image: url("icons/ar_infantry.svg"); } +.piece.tr_infantry { width: 16px; height: 16px; background-image: url("icons/tr_infantry.svg"); } +.piece.gold { width: 28px; height: 28px; background-image: url("icons/gold.png"); } +.piece.marker { width: 34px; height: 34px; border-radius: 50%; background-color: #444; border: 2px solid black; } .harbor { fill-opacity: 0; @@ -196,47 +220,18 @@ svg circle, .piece { /* CARD ACTION POPUP MENU */ -#popup { - position: fixed; - user-select: none; - background-color: gainsboro; - left: 10px; - top: 100px; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.3); - z-index: 200; - min-width: 20ex; - white-space: nowrap; - display: none; -} -#popup div { padding: 3pt 8pt; color: gray; display: none; } -#popup div.enabled { color: black; display: block; } -#popup div.enabled:hover { background-color: teal; color: white; } -#popup div.always { display: block; } -body.Tripolitania #popup div.tr_always { display: block; } -body.United_States #popup div.us_always { display: block; } +#us_popup, #tr_popup { box-shadow: 0px 8px 16px 0px #0004; } -/* MOBILE PHONE LAYOUT */ +#us_popup { background-color: hsl(207, 50%, 90%) } +#us_popup li.title { background-color: hsl(207, 50%, 66%) } +#us_popup li.action:hover { background-color: hsl(207, 50%, 26%) } -@media (max-width: 640px) { - .hand .card { - width: 125px; - height: 175px; - border-radius: 6px; - } - .hand { - min-height: 125px; - } - .hand_separator { - display: none; - } - #core_cards { - background-color: darkslategray; - } - #tooltip.card { - top: 10px; - right: 10px; - } -} +#tr_popup { background-color: hsl(3, 50%, 90%) } +#tr_popup li.title { background-color: hsl(3, 50%, 71%) } +#tr_popup li.action:hover { background-color: hsl(3, 50%, 31%) } + +#us_popup li[data-action="card_take"].disabled { display: none } +#tr_popup li[data-action="card_take"].disabled { display: none } /* CARD IMAGES */ @@ -2,14 +2,14 @@ <!-- vim:set nowrap: --> <html> <head> -<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1"> +<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, interactive-widget=resizes-content"> <meta charset="UTF-8"> <title>SHORES OF TRIPOLI</title> <link rel="icon" href="Flag_of_Tripoli_18th_century.svg"> <link rel="stylesheet" href="/fonts/fonts.css"> -<link rel="stylesheet" href="/common/play.css"> +<link rel="stylesheet" href="/common/client.css"> <link rel="stylesheet" href="play.css"> -<script defer src="/common/play.js"></script> +<script defer src="/common/client.js"></script> <script defer src="data.js"></script> <script defer src="play.js"></script> </head> @@ -17,30 +17,38 @@ <div id="tooltip" class="card"></div> -<div id="popup" onmouseleave="hide_popup_menu()"> -<div id="menu_card_event" class="always" onclick="on_card_event()">Play Event</div> -<div id="menu_card_move_frigates" class="us_always" onclick="on_card_move_frigates()">Discard to move two frigates</div> -<div id="menu_card_build_gunboat" class="us_always" onclick="on_card_build_gunboat()">Discard to build a gunboat</div> -<div id="menu_card_pirate_raid" class="tr_always" onclick="on_card_pirate_raid()">Discard to pirate raid</div> -<div id="menu_card_build_corsair" class="tr_always" onclick="on_card_build_corsair()">Discard to build a corsair</div> -<div id="menu_card_take" onclick="on_card_take()">Place in hand</div> -</div> +<menu id="us_popup"> + <li class="title">TITLE + <li class="separator"> + <li data-action="card_event"> 🎴 Event + <li data-action="card_move_frigates"> ⛵ Move two frigates + <li data-action="card_build_gunboat"> 🔨 Build a gunboat + <li data-action="card_take"> ✋ Place in hand +</menu> + +<menu id="tr_popup"> + <li class="title">TITLE + <li class="separator"> + <li data-action="card_event"> 🎴 Event + <li data-action="card_pirate_raid"> 🚣 Pirate raid + <li data-action="card_build_corsair"> 🔨 Build a corsair + <li data-action="card_take"> ✋ Place in hand +</menu> <header> <div id="toolbar"> - <div class="menu"> - <div class="menu_title"><img src="/images/cog.svg"></div> - <div class="menu_popup"> - <a class="menu_item" href="info/rules.html" target="_blank">Rules</a> - <a class="menu_item" href="info/history.html" target="_blank">History</a> - <div class="menu_separator"></div> - <a class="menu_item" href="info/tr_cards.html" target="_blank">Tripolitan Cards</a> - <a class="menu_item" href="info/us_cards.html" target="_blank">United States Cards</a> - <div class="resign menu_separator"></div> - <div class="resign menu_item" onclick="confirm_resign()">Resign</div> - </div> - </div> - <div class="icon_button" onclick="toggle_zoom()"><img src="/images/magnifying-glass.svg"></div> + <details> + <summary><img src="/images/cog.svg"></summary> + <menu> + <li><a class="menu_item" href="info/rules.html" target="_blank">Rules</a> + <li><a class="menu_item" href="info/history.html" target="_blank">History</a> + <li class="separator"> + <li><a class="menu_item" href="info/tr_cards.html" target="_blank">Tripolitan Cards</a> + <li><a class="menu_item" href="info/us_cards.html" target="_blank">United States Cards</a> + <li class="resign separator"> + <li class="resign" onclick="confirm_resign()">Resign + </menu> + </details> </div> <div id="prompt"></div> <div id="actions"></div> @@ -69,12 +77,11 @@ <div id="log"></div> </aside> -<main> +<main data-min-zoom="0.75" data-max-zoom="1.5" onclick="hide_popup_menu()"> -<div id="mapwrap" class="fit"> +<div id="mapwrap"> <div id="map"> -<svg id="svgmap" width="2476px" height="801px" viewBox="0 0 2476 801"> -<image href="map.jpg" x="0" y="0" width="2476" height="801" /> +<svg id="svgmap" width="1650px" height="532px" viewBox="0 0 2475 798"> <path class="patrol_zone" d="M184.82 276.533a136 136 0 00-136 136 136 136 0 0073.566 120.69c1.096.19 2.461.65 4.215 1.378 3.6 1.5 4.499 1.5 10.399.1 3.6-.9 10.3-2.1 15-2.7 4.7-.7 12.3-2.601 16.9-4.301 7.3-2.8 9.3-4.1 15.5-10.2 3.9-3.9 10-8.7 13.6-10.7 4.5-2.5 8.9-6.3 14-12 3.316-3.722 7.856-8.463 11.225-11.737a92.27 92.27 0 01-19.04-55.945 92.27 92.27 0 0192.075-92.26 136 136 0 00-111.44-58.325z" id="tangier_patrol_zone"/> <path class="patrol_zone" d="M615.705 21.148c-2.433.155-2.407.483-1.205 1.352 3.9 2.8 4.2 3.4 4.1 7.7-.2 4.9-1.2 7.1-2.8 6.2-.6-.4-2-.7-3.2-.8-1.7-.1-2.1-.7-1.9-2.9.2-2.4 0-2.7-1.3-1.6-1.7 1.4-.9 7.5 1.3 9.7 1.6 1.7 1.6 1.7-.2 5.1-.8 1.7-1.2 4.2-.9 6.3.5 2.9.3 3.5-2 4.6-2.8 1.3-5.4 5.7-4.1 7 .4.4 1 .1 1.2-.6.3-.6 1.1-1.2 1.9-1.2 1.9 0 1.9 6.4-.1 8-1.9 1.6-1.9 3 .1 4.5 1.4.9 1.4 1.6.5 3.3-1.4 2.7-.6 4.3 1.6 3.5 1.4-.5 1.5-.1.9 2.8-.5 1.9-.8 3.8-.8 4.4 0 1.8 2 12.9 2.7 15.1.5 1.8.1 2.3-2.6 3.3-1.7.6-4.2 1.1-5.5 1.1-1.2 0-3.5 1.3-5.1 3-3.2 3.4-4.8 3.8-5.7 1.3-.6-1.4-.8-1.4-2.2.5-.8 1.2-2.4 2.2-3.4 2.2-1.6 0-1.8.5-1.4 2.9.3 1.6.1 3.8-.5 5-1 1.8-1.5 1.9-3.2 1-2.4-1.2-2.5-1.1-4.7 3.3-1.6 3.1-2 3.3-6.3 3.1l-4.7-.1-.7 8.1c-.4 4.5-1.1 8.8-1.7 9.4-.6.7-1.9 4.5-2.9 8.5-1 3.9-2.5 7.8-3.3 8.7-1.1 1.3-1.2 3.2-.6 9.1.6 5.6.4 8.2-.6 10.4-.8 1.6-1.4 3.7-1.4 4.8 0 1-1.1 2.4-2.5 3-2.7 1.2-2.7 1-2.9 13.7-.1 9.3-1.4 11.3-4.1 6.2-.8-1.7-2.2-3.1-3.1-3.1-.8 0-1.9-1-2.4-2.3-1.2-2.8-4.5-6.7-5.8-6.7-.5 0-1.5.7-2.2 1.6-.8.8-2.5 1.4-4.1 1.2-1.6-.1-3.6.5-4.8 1.6-1.1 1-2.5 1.5-3.1 1.1-.6-.4-1.7-.2-2.4.4-.7.6-2.1.9-3 .5-1-.4-1.8 0-2.2 1-.8 2-4 2.1-4.8.1-.3-.8-1.6-1.5-3-1.5s-2.8-.5-3.2-1.1c-.4-.7-1.6-.1-3.4 1.6-2.1 2-3.3 2.5-5.4 1.9-2.3-.5-2.7-1-2.1-3.1.4-1.6.2-2.2-.5-1.8-.5.3-1 1.6-1 2.9 0 2.4-3.4 4.6-6.9 4.6-1 0-2.4.7-3.1 1.5-.7.8-2.1 1.5-3.2 1.5s-2.6.7-3.4 1.5c-1.522 1.439-2.921 1.203-5.975-1.378.038.626.07 1.252.094 1.878 0 50.96-41.31 92.27-92.269 92.27-50.96 0-92.27-41.31-92.27-92.27a92.27 92.27 0 015.942-32.45c-4.284-.742-6.922-1.72-6.922-2.75 0-.5-1.2-.7-2.8-.3-3.5.7-4.1 0-8.7-9.4-1.9-3.9-4-7.1-4.6-7.1-.6 0-.9.3-.7.7 2.1 4.1 3.6 8.5 4.2 12 .5 3.5.2 4.6-1.7 6.9-2.4 2.8-11.9 6-13.9 4.7-.6-.3-2.7-.6-4.6-.5-1.823.076-3.414-.312-4.951-1.246C260.979 286.86 348.139 359.979 450 360c115.98 0 210-94.02 210-210a210 210 0 00-44.295-128.852z" id="gibraltar_patrol_zone"/> <path class="patrol_zone" d="M879 113.5a136 136 0 00-136 136 136 136 0 009.45 49.746c.019.017.033.036.05.054 2.1 2 3.6 2.5 7.6 2.5 6.3.1 10.3.8 15 2.8 3.2 1.4 4 1.4 6.5.1 1.6-.8 4.199-2.7 5.699-4.2 2.228-2.228 3.107-2.635 6.176-2.204A92.27 92.27 0 01883.5 225.73a92.27 92.27 0 0183.928 54.151c.461-.22.86-.38 1.073-.38 1 0 1.799-1.6 5.499-9.7 1.2-2.7 2.6-5.1 3.2-5.4 2.6-1.6 19.8-2.4 22-1 1.3.8 2.3.8 3.6-.1 1.1-.6 3.8-.9 6.4-.6 2.986.365 4.169.353 5.17-.606a136 136 0 00.63-12.594 136 136 0 00-136-136z" id="algiers_patrol_zone"/> @@ -1,5 +1,7 @@ "use strict"; +const SCALE = 2/3 + const SEASON_X = [ 893, 978, 1064, 1149 ]; const YEAR_X = { 1801: 175, 1802: 294, 1803: 413, 1804: 532, 1805: 652, 1806: 771 }; const YEAR_Y = 728; @@ -335,26 +337,26 @@ function on_update() { function update_year_marker(year) { let e = document.getElementById("year"); - e.style.left = Math.round(YEAR_X[year] - 27) + "px"; - e.style.top = Math.round(YEAR_Y - 27) + "px"; + e.style.left = Math.round((YEAR_X[year] - 27) * SCALE) + "px"; + e.style.top = Math.round((YEAR_Y - 27) * SCALE) + "px"; } function update_season_marker(season) { let e = document.getElementById("season"); - e.style.left = Math.round(SEASON_X[season] - 27) + "px"; - e.style.top = Math.round(YEAR_Y - 27) + "px"; + e.style.left = Math.round((SEASON_X[season] - 27) * SCALE) + "px"; + e.style.top = Math.round((YEAR_Y - 27) * SCALE) + "px"; } function set_piece_xy(p, x, y) { let e = ui.pieces[p]; - e.style.left = Math.round(x - e.offsetWidth/2) + "px"; - e.style.top = Math.round(y - e.offsetHeight/2) + "px"; + e.style.left = Math.round(x * SCALE - e.offsetWidth/2) + "px"; + e.style.top = Math.round(y * SCALE - e.offsetHeight/2) + "px"; } function set_gold_xy(i, x, y) { let e = ui.gold[i]; - e.style.left = Math.round(x - e.offsetWidth/2) + "px"; - e.style.top = Math.round(y - e.offsetHeight/2) + "px"; + e.style.left = Math.round(x * SCALE - e.offsetWidth/2) + "px"; + e.style.top = Math.round(y * SCALE - e.offsetHeight/2) + "px"; } function layout_space(location, s, x0, y0, size) { @@ -485,81 +487,74 @@ function update_spaces() { /* CARD ACTION MENU */ -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 is_action(action, card) { + return view.actions && view.actions[action] && view.actions[action].includes(card) +} + +function show_popup_menu(evt, menu_id, target_id, title) { + let menu = document.getElementById(menu_id) + + let show = true + for (let item of menu.querySelectorAll("li")) { + let action = item.dataset.action + if (action) { + if (is_action(action, target_id)) { + show = true + item.classList.add("action") + item.classList.remove("disabled") + item.onclick = function () { + send_action(action, target_id) + hide_popup_menu() + evt.stopPropagation() + } + } else { + item.classList.remove("action") + item.classList.add("disabled") + item.onclick = null + } + } } -} - -function on_card_event() { - if (send_action('card_event', current_popup_card)) - hide_popup_menu(); -} -function on_card_take() { - if (send_action('card_take', current_popup_card)) - hide_popup_menu(); -} + if (show) { + menu.onmouseleave = hide_popup_menu + menu.style.display = "block" + if (title) { + let item = menu.querySelector("li.title") + if (item) { + item.onclick = hide_popup_menu + item.textContent = title + } + } -function on_card_move_frigates() { - if (send_action('card_move_frigates', current_popup_card)) - hide_popup_menu(); -} + let w = menu.clientWidth + let h = menu.clientHeight + let x = Math.max(5, Math.min(evt.clientX - w / 2, window.innerWidth - w - 5)) + let y = Math.max(5, Math.min(evt.clientY - 12, window.innerHeight - h - 40)) + menu.style.left = x + "px" + menu.style.top = y + "px" -function on_card_pirate_raid() { - if (send_action('card_pirate_raid', current_popup_card)) - hide_popup_menu(); -} - -function on_card_build_gunboat() { - if (send_action('card_build_gunboat', current_popup_card)) - hide_popup_menu(); -} + return true + } -function on_card_build_corsair() { - if (send_action('card_build_corsair', current_popup_card)) - hide_popup_menu(); + return false } -function is_card_action(action, card) { - return view.actions && view.actions[action] && view.actions[action].includes(card); +function hide_popup_menu() { + document.getElementById("us_popup").style.display = "none" + document.getElementById("tr_popup").style.display = "none" } function on_click_card(evt) { if (view.actions) { - let card = evt.target.card; - if (is_card_action('discard', card)) { - send_action('discard', card); + let card = evt.target.card + if (is_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_take', card)) menu.push('card_take'); - if (is_card_action('card_move_frigates', card)) menu.push('card_move_frigates'); - if (is_card_action('card_pirate_raid', card)) menu.push('card_pirate_raid'); - if (is_card_action('card_build_gunboat', card)) menu.push('card_build_gunboat'); - if (is_card_action('card_build_corsair', card)) menu.push('card_build_corsair'); - if (menu.length > 0) { - current_popup_card = card; - show_popup_menu(evt, menu); - } + if (card >= 1 && card <= 27) + show_popup_menu(evt, "us_popup", card, US_CARD_NAMES[card - 1]) + else + show_popup_menu(evt, "tr_popup", card, TR_CARD_NAMES[card - 28]) + evt.stopPropagation() } } } @@ -2979,8 +2979,6 @@ exports.resign = function (state, current) { return game; } -exports.is_checkpoint = (a, b) => a.season !== b.season; - exports.view = function(state, current) { game = state; |