summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/common/columbia.css (renamed from public/common/battle_abc.css)18
-rw-r--r--public/common/play.css (renamed from public/common/grid.css)9
-rw-r--r--public/common/play.js (renamed from public/common/client.js)245
-rw-r--r--public/images/sherlock-holmes-mirror.svg1
4 files changed, 168 insertions, 105 deletions
diff --git a/public/common/battle_abc.css b/public/common/columbia.css
index 3a211a1..1a646b6 100644
--- a/public/common/battle_abc.css
+++ b/public/common/columbia.css
@@ -1,4 +1,4 @@
-/* ABC BATTLE DISPLAY FOR COLUMBIA BLOCK GAMES */
+/* BATTLE DIALOG FOR COLUMBIA BLOCK GAMES */
#battle {
position: absolute;
@@ -11,18 +11,22 @@
box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
visibility: hidden;
}
+
#battle.show {
visibility: visible;
}
+
#battle th {
padding: 0;
border: 1px solid black;
}
+
#battle td {
padding: 0;
border: 1px solid black;
vertical-align: top;
}
+
#battle_header {
font-weight: bold;
height: 2em;
@@ -31,12 +35,14 @@
user-select: none;
cursor: move;
}
+
#battle_message {
font-weight: normal;
height: 2em;
line-height: 2em;
padding: 0 8px;
}
+
#FA, #FB, #FC, #FD, #FF, #FR, #EA, #EB, #EC, #ED, #EF, #ER {
display: flex;
flex-wrap: wrap;
@@ -64,11 +70,6 @@
display: none;
}
-.battle_menu .action:hover { background-color: red; }
-.battle_menu .action.retreat:hover { background-color: #eee; }
-.battle_menu .action.withdraw:hover { background-color: #eee; }
-.battle_menu .action.pass:hover { background-color: gray; }
-
.battle_menu_list {
margin-top: 5px;
min-height: 26px;
@@ -82,6 +83,11 @@
display: none;
}
+.battle_menu .action:hover { background-color: red; }
+.battle_menu .action.retreat:hover { background-color: #eee; }
+.battle_menu .action.withdraw:hover { background-color: #eee; }
+.battle_menu .action.pass:hover { background-color: gray; }
+
.battle_menu.fire .action.fire { display: inline; }
.battle_menu.retreat .action.retreat { display: inline; }
.battle_menu.pass .action.pass { display: inline; }
diff --git a/public/common/grid.css b/public/common/play.css
index 228c0aa..64e9202 100644
--- a/public/common/grid.css
+++ b/public/common/play.css
@@ -25,7 +25,7 @@ body:not(.shift) .debug {
display: none;
}
-body.Observer .resign, body.replay .resign {
+body.Observer .resign {
display: none;
}
@@ -184,7 +184,7 @@ header .replay {
display: flex;
}
-header button.replay_button {
+header .replay button {
margin: 0;
}
@@ -311,12 +311,11 @@ header button.replay_button {
border: 1px solid black;
background-color: white;
box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
- display: none;
- grid-template-rows: auto minmax(0, 1fr) auto;
+ visibility: hidden;
}
#chat_window.show {
- display: grid;
+ visibility: visible;
}
#chat_header {
diff --git a/public/common/client.js b/public/common/play.js
index d9a1f48..ede3a5b 100644
--- a/public/common/client.js
+++ b/public/common/play.js
@@ -117,13 +117,10 @@ window.addEventListener("focus", stop_blinker);
function init_chat() {
// only fetch new messages when we reconnect!
if (chat !== null) {
- console.log("RECONNECT CHAT");
- socket.emit("getchat", chat.log);
+ send_message("getchat", chat.log);
return;
}
- console.log("CONNECT CHAT");
-
let chat_window = document.createElement("div");
chat_window.id = "chat_window";
chat_window.innerHTML = `
@@ -156,7 +153,7 @@ function init_chat() {
let input = document.getElementById("chat_input");
e.preventDefault();
if (input.value) {
- socket.emit("chat", input.value);
+ send_message("chat", input.value);
input.value = "";
} else {
hide_chat();
@@ -179,7 +176,7 @@ function init_chat() {
}
});
- socket.emit("getchat", 0);
+ send_message("getchat", 0);
}
function save_chat() {
@@ -251,6 +248,38 @@ function toggle_chat() {
show_chat();
}
+/* REMATCH BUTTON */
+
+function remove_resign_menu() {
+ document.querySelectorAll(".resign").forEach(x => x.remove());
+}
+
+function goto_rematch() {
+ window.location = "/rematch/" + params.game_id + "/" + params.role;
+}
+
+function goto_replay() {
+ window.location = "/" + params.title_id + "/replay:" + params.game_id;
+}
+
+function on_game_over() {
+ function icon_button(id, img, title, fn) {
+ if (!document.getElementById(id)) {
+ let button = document.createElement("div");
+ button.id = id;
+ button.title = title;
+ button.className = "icon_button";
+ button.innerHTML = '<img src="/images/' + img + '.svg">';
+ button.addEventListener("click", fn);
+ document.querySelector("header").appendChild(button);
+ }
+ }
+ icon_button("replay_button", "sherlock-holmes-mirror", "Watch replay", goto_replay);
+ if (player !== "Observer")
+ icon_button("rematch_button", "cycle", "Propose a rematch!", goto_rematch);
+ remove_resign_menu();
+}
+
/* CONNECT TO GAME SERVER */
function init_player_names(players) {
@@ -261,76 +290,102 @@ function init_player_names(players) {
}
}
-function init_play_client() {
- const ROLE_SEL = [
- ".role.one",
- ".role.two",
- ".role.three",
- ".role.four",
- ".role.five",
- ".role.six",
- ".role.seven",
- ];
+function send_message(cmd, arg) {
+ let data = JSON.stringify([cmd, arg]);
+ console.log("SEND %s %s", cmd, arg);
+ socket.send(data);
+}
- console.log("JOINING", params.title_id + "/" + params.game_id + "/" + params.role);
+let reconnect_count = 0;
+let reconnect_max = 10;
- socket = io({
- transports: ["websocket"],
- query: { title: params.title_id, game: params.game_id, role: params.role },
- });
+function connect_play() {
+ if (reconnect_count >= reconnect_max) {
+ document.title = "DISCONNECTED";
+ document.getElementById("prompt").textContent = "Disconnected.";
+ return;
+ }
- socket.on("connect", () => {
- console.log("CONNECTED");
- document.querySelector("header").classList.remove("disconnected");
- });
+ let protocol = (window.location.protocol === "http:") ? "ws" : "wss";
+ let seen = document.getElementById("log").children.length;
+ let url = `${protocol}://${window.location.host}/play-socket?title=${params.title_id}&game=${params.game_id}&role=${params.role}&seen=${seen}`;
- socket.on("disconnect", () => {
- console.log("DISCONNECTED");
- document.getElementById("prompt").textContent = "Disconnected from server!";
- document.querySelector("header").classList.add("disconnected");
- });
+ console.log("CONNECTING", url);
+ document.getElementById("prompt").textContent = "Connecting... ";
+
+ socket = new WebSocket(url);
- socket.on("roles", (me, players) => {
- console.log("PLAYERS", me, JSON.stringify(players));
- player = me;
- document.querySelector("body").classList.add(player.replace(/ /g, "_"));
- if (player !== "Observer")
- init_chat();
- init_player_names(players);
+ window.addEventListener('beforeunload', function () {
+ socket.close(1000);
});
- socket.on("presence", (presence) => {
- console.log("PRESENCE", JSON.stringify(presence));
- for (let i = 0; i < roles.length; ++i) {
- let elt = document.getElementById(roles[i].id);
- if (roles[i].role in presence)
- elt.classList.add("present");
- else
- elt.classList.remove("present");
+ socket.onopen = function (evt) {
+ console.log("OPEN");
+ document.querySelector("header").classList.remove("disconnected");
+ reconnect_count = 0;
+ }
+
+ socket.onclose = function (evt) {
+ console.log("CLOSE %d", evt.code);
+ if (evt.code === 1000 && evt.reason !== "") {
+ document.getElementById("prompt").textContent = "Disconnected: " + evt.reason;
+ document.title = "DISCONNECTED";
}
- });
+ if (evt.code !== 1000) {
+ document.querySelector("header").classList.add("disconnected");
+ document.getElementById("prompt").textContent = `Reconnecting soon... (${reconnect_count+1}/${reconnect_max})`;
+ let wait = 1000 * (Math.random() + 0.5) * Math.pow(2, reconnect_count++);
+ console.log("WAITING %.1f TO RECONNECT", wait/1000);
+ setTimeout(connect_play, wait);
+ }
+ }
- socket.on("state", (new_view) => {
- console.log("STATE");
- view = new_view;
- on_update_header();
- on_update();
- on_update_log();
- });
+ socket.onmessage = function (evt) {
+ let [ cmd, arg ] = JSON.parse(evt.data);
+ console.log("MESSAGE %s", cmd);
+ switch (cmd) {
+ case 'error':
+ document.getElementById("prompt").textContent = arg;
+ break;
- socket.on("save", (msg) => {
- console.log("SAVE");
- window.localStorage[params.title_id + "/save"] = msg;
- });
+ case 'chat':
+ update_chat(arg[0], arg[1], arg[2], arg[3]);
+ break;
- socket.on("error", (msg) => {
- console.log("ERROR", msg);
- document.getElementById("prompt").textContent = msg;
- });
+ case 'players':
+ player = arg[0];
+ document.querySelector("body").classList.add(player.replace(/ /g, "_"));
+ if (player !== "Observer")
+ init_chat();
+ else
+ remove_resign_menu();
+ init_player_names(arg[1]);
+ break;
- socket.on("chat", function (item) {
- update_chat(item[0], item[1], item[2], item[3]);
- });
+ case 'presence':
+ for (let i = 0; i < roles.length; ++i) {
+ let elt = document.getElementById(roles[i].id);
+ if (roles[i].role in arg)
+ elt.classList.add("present");
+ else
+ elt.classList.remove("present");
+ }
+ break;
+
+ case 'state':
+ view = arg;
+ on_update_header();
+ on_update();
+ on_update_log();
+ if (view.game_over)
+ on_game_over();
+ break;
+
+ case 'save':
+ window.localStorage[params.title_id + "/save"] = msg;
+ break;
+ }
+ }
}
/* HEADER */
@@ -474,17 +529,16 @@ function send_action(verb, noun) {
return;
// Reset action list here so we don't send more than one action per server prompt!
if (noun !== undefined) {
- if (view.actions && view.actions[verb] && view.actions[verb].includes(noun)) {
+ let realnoun = Array.isArray(noun) ? noun[0] : noun;
+ if (view.actions && view.actions[verb] && view.actions[verb].includes(realnoun)) {
view.actions = null;
- console.log("ACTION", verb, JSON.stringify(noun));
- socket.emit("action", verb, noun);
+ send_message("action", [verb, noun]);
return true;
}
} else {
if (view.actions && view.actions[verb]) {
view.actions = null;
- console.log("ACTION", verb);
- socket.emit("action", verb);
+ send_message("action", [verb]);
return true;
}
}
@@ -493,21 +547,21 @@ function send_action(verb, noun) {
function confirm_resign() {
if (window.confirm("Are you sure that you want to resign?"))
- socket.emit("resign");
+ send_message("resign");
}
/* DEBUGGING */
function send_save() {
- socket.emit("save");
+ send_message("save");
}
function send_restore() {
- socket.emit("restore", window.localStorage[params.title_id + "/save"]);
+ send_message("restore", window.localStorage[params.title_id + "/save"]);
}
function send_restart(scenario) {
- socket.emit("restart", scenario);
+ send_message("restart", scenario);
}
/* REPLAY */
@@ -546,9 +600,10 @@ async function require(path) {
let replay = null;
-async function init_replay_client() {
+async function init_replay() {
+ remove_resign_menu();
+
document.getElementById("prompt").textContent = "Loading replay...";
- document.querySelector("body").classList.add("replay");
console.log("LOADING RULES");
let rules = await require("rules.js");
@@ -660,7 +715,7 @@ async function init_replay_client() {
if (viewpoint === "Active") {
player = s.active;
- if (player === "All" || player === "Both" || !player)
+ if (player === "All" || player === "Both" || player === "None" || !player)
player = "Observer";
}
@@ -679,10 +734,13 @@ async function init_replay_client() {
view.game_over = 1;
if (replay.length > 0) {
- if (document.querySelector("body").classList.contains("shift"))
- view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state} / ${replay[p].action} ${replay[p].arguments}`;
- else
+ if (document.querySelector("body").classList.contains("shift")) {
+ view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state}`;
+ if (p < replay.length)
+ view.prompt += ` / ${replay[p].action} ${replay[p].arguments}`;
+ } else {
view.prompt = "[" + p + "/" + replay.length + "] " + view.prompt;
+ }
}
if (log_length < view.log.length)
view.log_start = log_length;
@@ -696,11 +754,10 @@ async function init_replay_client() {
on_update_log();
}
- function replay_button(div, label, fn) {
+ function text_button(div, txt, fn) {
let button = document.createElement("button");
button.addEventListener("click", fn);
- button.className = "replay_button";
- button.textContent = label;
+ button.textContent = txt;
div.appendChild(button);
return button;
}
@@ -715,20 +772,20 @@ async function init_replay_client() {
let div = document.createElement("div");
div.className = "replay";
- replay_button(div, "Active", () => set_viewpoint("Active"));
+ text_button(div, "Active", () => set_viewpoint("Active"));
for (let r of roles)
- replay_button(div, r.role, () => set_viewpoint(r.role));
- replay_button(div, "Observer", () => set_viewpoint("Observer"));
+ text_button(div, r.role, () => set_viewpoint(r.role));
+ text_button(div, "Observer", () => set_viewpoint("Observer"));
document.querySelector("header").appendChild(div);
div = document.createElement("div");
div.className = "replay";
- replay_button(div, "<<<", () => goto_replay(1));
- replay_button(div, "<<", () => goto_replay(prev()));
- replay_button(div, "<\xa0", () => goto_replay(p-1));
- replay_button(div, "\xa0>", () => goto_replay(p+1));
- replay_button(div, ">>", () => goto_replay(next()));
- replay_button(div, "Run", play_pause_replay).style.width = "65px";
+ text_button(div, "<<<", () => goto_replay(1));
+ text_button(div, "<<", () => goto_replay(prev()));
+ text_button(div, "<\xa0", () => goto_replay(p-1));
+ text_button(div, "\xa0>", () => goto_replay(p+1));
+ text_button(div, ">>", () => goto_replay(next()));
+ text_button(div, "Run", play_pause_replay).style.width = "65px";
document.querySelector("header").appendChild(div);
if (window.location.hash === "")
@@ -744,7 +801,7 @@ async function init_replay_client() {
}
}
-if (params.mode === "play")
- init_play_client();
if (params.mode === "replay")
- init_replay_client();
+ init_replay();
+if (params.mode === "play")
+ connect_play();
diff --git a/public/images/sherlock-holmes-mirror.svg b/public/images/sherlock-holmes-mirror.svg
new file mode 100644
index 0000000..f2f0f19
--- /dev/null
+++ b/public/images/sherlock-holmes-mirror.svg
@@ -0,0 +1 @@
+<svg style="height:512px;width:512px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M326.582 36.882s24.67 5.747 35.756 15.557c38.586 34.149 51.256 67.652 53.746 105.53l43.97 47.023c-96.221-17.906-207.672-21.92-340.581-20.912 21.002-14.144 41.37-25.753 59.322-36.814 14.473-35.496 33.701-76.597 65.934-95.373 14.145-8.24 29.144-14.024 43.962-16.83 10.736-20.307 35.4-11.819 37.89 1.82zM407.023 214.8c-20.328 40.62-56.635 79.575-89.761 103.012-18.256 12.63-36.742 21.653-51.035 24.144-11.946 2.242-23.46-1.416-28.825-10.672l-.002-.004v-.002c-8.103-14.299-14.714-28.724-20.359-43.197-15.776 1.737-33.408 2.541-38.04-1.178-7.31-5.871 8.751-56.088 16.056-69.443C192 211.24 192 204.665 192 199.88c39.436-2.194 150.638 6.423 215.023 14.92zm-342.142 6.07c13.335.302 24.897 9.857 33.428 22.668 9.413 14.137 16.138 33.34 18.798 55.055 2.66 21.714.771 41.971-4.95 57.963-4.29 11.99-11.133 22.318-20.655 27.515l10.77 108.676-17.913 1.775-10.615-107.13c-12.015-1.592-22.443-10.62-30.299-22.418-9.413-14.137-16.136-33.339-18.797-55.053-2.66-21.714-.77-41.974 4.952-57.965 5.721-15.991 15.983-29.026 31.087-30.877a29.036 29.036 0 0 1 4.194-.209zm-2.004 18.076c-5.494.673-11.846 6.541-16.33 19.075-4.485 12.533-6.397 30.421-4.033 49.71 2.363 19.29 8.538 36.186 15.916 47.266 7.377 11.08 14.955 15.242 20.449 14.568 5.494-.673 11.844-6.54 16.328-19.074 4.485-12.533 6.396-30.42 4.033-49.709-2.363-19.289-8.536-36.187-15.914-47.267-5.011-6.164-12.69-15.168-20.449-14.569zm349.951 3.584c6.243 8.543 13.975 17.27 23.111 25.744 12.11 11.231 26.664 21.827 43.198 30.211-37.101 7.524-77.514 23.385-115.21 42.594-42.326 21.57-80.776 47.18-106.775 71.145l-2.314-24.686c11.388-12.634 23.526-23.83 35.869-34.252 12.152-5.001 24.557-12.203 36.797-20.672 36.371-25.164 70.878-60.916 85.324-90.084zM68.246 253.806c-.036 21.503-3.015 45.534-9.771 64.632-6.729-19.745-7.02-55.246 9.771-64.632zm137.598 54.43a353.014 353.014 0 0 0 7.74 16.574c-7.33 5.423-11.536 10.592-13.904 15.505-3.119 6.47-3.49 13.077-2.176 21.295 2.24 14.002 10.213 31.472 14.32 52.23 9.049 12.67 1.565 56.042-18.265 60.938-44.677 11.03-69.71-35.7-59.614-46.716 9.817-10.713 37.598-19.736 57.92-19.952-4.048-14.733-9.79-28.985-12.136-43.656-1.666-10.412-1.325-21.456 3.736-31.955 4.255-8.827 11.576-16.856 22.379-24.264zm272.875 8.81c4.751 7.296 9.937 15.484 15.281 24.413V494H238.848c2.921-14.12 5.609-28.7 8.763-42.629.65-5.674 5.269-9.093 9.059-13.314 21.957-24.459 66.328-55.92 115.432-80.942 35.16-17.917 72.94-32.625 106.617-40.07z"/></svg> \ No newline at end of file