summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--about.html1
-rw-r--r--cover.1x.jpgbin27012 -> 27980 bytes
-rw-r--r--cover.2x.jpgbin57726 -> 86219 bytes
-rw-r--r--play.css21
-rw-r--r--play.html86
-rw-r--r--play.js120
-rw-r--r--rules.js70
7 files changed, 137 insertions, 161 deletions
diff --git a/about.html b/about.html
index 71b5eee..3a39766 100644
--- a/about.html
+++ b/about.html
@@ -1,7 +1,6 @@
<p>
Unlicensed game prototype!
-<br clear=left>
<ul>
<li><a href="/washingtons-war/info/rulebook.html">Rulebook</a>
diff --git a/cover.1x.jpg b/cover.1x.jpg
index 7889c4d..dfdc372 100644
--- a/cover.1x.jpg
+++ b/cover.1x.jpg
Binary files differ
diff --git a/cover.2x.jpg b/cover.2x.jpg
index 09dc03e..a96ff62 100644
--- a/cover.2x.jpg
+++ b/cover.2x.jpg
Binary files differ
diff --git a/play.css b/play.css
index 4b2604f..fec181d 100644
--- a/play.css
+++ b/play.css
@@ -56,20 +56,9 @@ aside {
/* CARD ACTION POPUP MENU */
-#popup {
- position: absolute;
- user-select: none;
- background-color: #ddd;
- left: 10px;
- top: 100px;
- box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.3);
- z-index: 100;
- min-width: 20ex;
- white-space: nowrap;
-}
-#popup div { padding: 3pt 8pt; display: none; }
-#popup div.enabled { padding: 3pt 8pt; display: block; }
-#popup div:hover { background-color: teal; color: white; }
+#popup { max-width: 250px; }
+#popup li.title { text-align: center }
+#popup li.disabled { display: none }
/* MAP WITH MARKERS, CUs, LEADERS, AND SPACES */
@@ -172,7 +161,6 @@ aside {
line-height: 50px;
padding-left: 6px;
font-size: 30px;
- user-select: none;
color: white;
text-shadow: 0px 0px 3px black;
font-weight: bold;
@@ -269,7 +257,8 @@ aside {
.hand .card.enabled:hover, .hand .card.selected {
Xtransform: scale(1.5) translate(0,-30px);
- transform: scale(1.1);
+ Xtransform: scale(1.1);
+ transform: translate(0,-10px);
z-index: 10;
}
diff --git a/play.html b/play.html
index f162900..9236937 100644
--- a/play.html
+++ b/play.html
@@ -1,14 +1,15 @@
<!DOCTYPE html>
-<html>
+<html lang="en">
<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, viewport-fit=cover">
+<meta name="theme-color" content="#444">
<meta charset="utf-8">
<title>WASHINGTON'S WAR</title>
<link rel="icon" href="favicon.png">
<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="cards.js"></script>
<script defer src="play.js"></script>
@@ -16,60 +17,33 @@
</head>
<body>
-<div id="popup" onmouseleave="hide_popup_menu()">
- <div id="menu_card_play_event" onclick="on_card_play_event()">
- Play Event
- </div>
- <div id="menu_card_discard_event" onclick="on_card_discard_event()">
- Discard for PC action
- </div>
- <div id="menu_card_campaign" onclick="on_card_campaign()">
- Play Campaign
- </div>
- <div id="menu_card_ops_general" onclick="on_card_ops_general()">
- Activate a General
- </div>
- <div id="menu_card_ops_pc" onclick="on_card_ops_pc()">
- Place PC markers
- </div>
- <div id="menu_card_ops_reinforcements" onclick="on_card_ops_reinforcements()">
- Bring on Reinforcements
- </div>
- <div id="menu_card_ops_queue" onclick="on_card_ops_queue()">
- Place into Operations Queue
- </div>
- <div id="menu_card_battle_play" onclick="on_card_battle_play()">
- Play for +2 DRM
- </div>
- <div id="menu_card_battle_discard" onclick="on_card_battle_discard()">
- Discard for +1 DRM
- </div>
- <div id="menu_exchange_for_discard" onclick="on_exchange_for_discard()">
- Exchange for Discarded Event
- </div>
-</div>
+<menu id="popup">
+ <li class="title">CARD
+ <li class="separator">
+ <li data-action="card_play_event"> Play Event
+ <li data-action="card_discard_event"> Discard for PC action
+ <li data-action="card_campaign"> Play Campaign
+ <li data-action="card_ops_general"> Activate a General
+ <li data-action="card_ops_pc"> Place PC markers
+ <li data-action="card_ops_reinforcements"> Bring on Reinforcements
+ <li data-action="card_ops_queue"> Place into Operations Queue
+ <li data-action="card_battle_play"> Play for +2 DRM
+ <li data-action="card_battle_discard"> Discard for +1 DRM
+ <li data-action="exchange_for_discard"> Exchange for Discarded Event
+</menu>
<header>
- <div class="menu">
- <div class="menu_title"><img src="/images/cog.svg"></div>
- <div class="menu_popup">
- <div class="menu_item" onclick="window.open('info/playbook.html', '_blank')">Playbook</div>
- <div class="menu_item" onclick="window.open('info/rulebook.html', '_blank')">Rulebook</div>
- <div class="menu_item" onclick="window.open('cards.html', '_blank')">Cards</div>
- <div class="menu_separator"></div>
- <div class="menu_item" onclick="send_save()">&#x1F41E; Save</div>
- <div class="menu_item" onclick="send_restore()">&#x1F41E; Restore</div>
- <div class="menu_separator"></div>
- <div class="menu_item" onclick="send_restart()">&#x26a0; Restart</div>
- </div>
+ <div id="toolbar">
+ <details>
+ <summary><img src="/images/cog.svg"></summary>
+ <menu>
+ <li><a href="info/playbook.html" target="blank">Playbook</a>
+ <li><a href="info/rulebook.html" target="blank">Rulebook</a>
+ <li><a href="cards.html" target="blank">Cards</a>
+ </menu>
+ </details>
+ <button onclick="toggle_markers()"><img src="/images/earth-america.svg"></button>
</div>
-
- <div class="icon_button" onclick="toggle_markers()"><img src="/images/earth-america.svg"></div>
- <div class="icon_button" onclick="toggle_zoom()"><img src="/images/magnifying-glass.svg"></div>
- <div class="icon_button" onclick="toggle_log()"><img src="/images/scroll-quill.svg"></div>
-
- <div id="prompt"></div>
- <div id="actions"></div>
</header>
<aside>
@@ -95,7 +69,7 @@
<div id="log"></div>
</aside>
-<main>
+<main onclick="hide_popup_menu()">
<div id="mapwrap">
<div id="map">
diff --git a/play.js b/play.js
index e5b2cc5..3a0c7ec 100644
--- a/play.js
+++ b/play.js
@@ -419,86 +419,67 @@ function on_update() {
}
}
-let 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, 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');
+function show_popup_menu(evt, menu_id, target_id, title) {
+ let menu = document.getElementById(menu_id)
+
+ let show = false
+ 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
+ }
+ }
}
- 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;
+ 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
+ }
+ }
+
+ 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"
+
+ return true
}
-}
-function on_card_play_event() {
- send_action('card_play_event', current_popup_card);
- hide_popup_menu();
-}
-function on_card_discard_event() {
- send_action('card_discard_event', current_popup_card);
- hide_popup_menu();
+ return false
}
-function on_card_campaign() {
- send_action('card_campaign', current_popup_card);
- hide_popup_menu();
-}
-function on_card_ops_general() {
- send_action('card_ops_general', current_popup_card);
- hide_popup_menu();
-}
-function on_card_ops_pc() {
- send_action('card_ops_pc', current_popup_card);
- hide_popup_menu();
-}
-function on_card_ops_reinforcements() {
- send_action('card_ops_reinforcements', current_popup_card);
- hide_popup_menu();
-}
-function on_card_ops_queue() {
- send_action('card_ops_queue', current_popup_card);
- hide_popup_menu();
-}
-function on_card_ops_queue() {
- send_action('card_ops_queue', current_popup_card);
- hide_popup_menu();
-}
-function on_card_battle_play() {
- send_action('card_battle_play', current_popup_card);
- hide_popup_menu();
-}
-function on_card_battle_discard() {
- send_action('card_battle_discard', current_popup_card);
- hide_popup_menu();
-}
-function on_exchange_for_discard() {
- send_action('exchange_for_discard', current_popup_card);
- hide_popup_menu();
+
+function hide_popup_menu() {
+ document.getElementById("popup").style.display = "none"
}
function on_card(evt) {
if (view.actions) {
let c = evt.target.id.split("+")[1] | 0;
- let menu = [];
- for (let action in view.actions)
- if (Array.isArray(view.actions[action]) && view.actions[action].includes(c))
- menu.push(action);
- if (menu.length > 0) {
- current_popup_card = c;
- show_popup_menu(evt, menu);
- }
+ show_popup_menu(evt, "popup", c, CARDS[c].title)
+ evt.stopPropagation()
}
}
@@ -533,4 +514,3 @@ function toggle_markers() {
document.querySelector("#map").classList.toggle("hide_markers");
}
-scroll_with_middle_mouse("main", 2);
diff --git a/rules.js b/rules.js
index 6e61b80..70891a4 100644
--- a/rules.js
+++ b/rules.js
@@ -71,7 +71,7 @@ let game;
let view;
function random(n) {
- return ((game.seed = game.seed * 69621 % 0x7fffffff) / 0x7fffffff) * n | 0;
+ return (game.seed = game.seed * 200105 % 34359738337) % n
}
function logbr() {
@@ -87,25 +87,63 @@ function logp(s) {
game.log.push(game.active[0] + " " + s);
}
+function object_copy(original) {
+ if (Array.isArray(original)) {
+ let n = original.length
+ let copy = new Array(n)
+ for (let i = 0; i < n; ++i) {
+ let v = original[i]
+ if (typeof v === "object" && v !== null)
+ copy[i] = object_copy(v)
+ else
+ copy[i] = v
+ }
+ return copy
+ } else {
+ let copy = {}
+ for (let i in original) {
+ let v = original[i]
+ if (typeof v === "object" && v !== null)
+ copy[i] = object_copy(v)
+ else
+ copy[i] = v
+ }
+ return copy
+ }
+}
+
function clear_undo() {
- game.undo = [];
+ if (game.undo) {
+ game.undo.length = 0
+ }
}
function push_undo() {
- game.undo.push(JSON.stringify(game, (k,v) => {
- if (k === 'undo') return 0;
- if (k === 'log') return v.length;
- return v;
- }));
+ if (game.undo) {
+ let copy = {}
+ for (let k in game) {
+ let v = game[k]
+ if (k === "undo")
+ continue
+ else if (k === "log")
+ v = v.length
+ else if (typeof v === "object" && v !== null)
+ v = object_copy(v)
+ copy[k] = v
+ }
+ game.undo.push(copy)
+ }
}
function pop_undo() {
- let save_undo = game.undo;
- let save_log = game.log;
- game = JSON.parse(save_undo.pop());
- game.undo = save_undo;
- save_log.length = game.log;
- game.log = save_log;
+ if (game.undo) {
+ let save_log = game.log
+ let save_undo = game.undo
+ game = save_undo.pop()
+ save_log.length = game.log
+ game.log = save_log
+ game.undo = save_undo
+ }
}
function remove_from_array(array, item) {
@@ -3444,11 +3482,7 @@ states.game_over = {
/* CLIENT/SERVER COMMS */
-exports.ready = function (scenario, options, players) {
- return players.length === 2;
-}
-
-exports.setup = function (seed, scenario, players) {
+exports.setup = function (seed, scenario, options) {
setup_game(seed);
return game;
}