summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/common/client.css68
-rw-r--r--public/common/client.js83
-rw-r--r--public/common/replay.js16
-rw-r--r--server.js13
4 files changed, 124 insertions, 56 deletions
diff --git a/public/common/client.css b/public/common/client.css
index 86f968b..c039afe 100644
--- a/public/common/client.css
+++ b/public/common/client.css
@@ -125,7 +125,7 @@ aside {
grid-row: 2;
display: grid;
overflow: clip;
- grid-template-rows: auto minmax(0, 1fr);
+ grid-template-rows: auto auto minmax(0, 1fr);
width: 212px;
background-color: white;
border-left: 1px solid black;
@@ -137,6 +137,8 @@ aside {
}
#turn_info {
+ grid-column: 1;
+ grid-row: 2;
border-bottom: 1px solid black;
padding: 4px 8px;
white-space: pre-line;
@@ -145,9 +147,13 @@ aside {
line-height: 18px;
}
+#turn_info:empty {
+ display: none;
+}
+
#log {
grid-column: 1;
- grid-row: 2;
+ grid-row: 3;
overflow-y: scroll;
}
@@ -398,21 +404,51 @@ menu li:hover img {
/* ROLES */
-.role_info {
+.role {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ padding-top: 3px;
border-bottom: 1px solid black;
+ font-size: 16px;
+ line-height: 1.5;
+}
+
+.role.hide {
+ display: none;
}
.role_name {
- border-bottom: 1px solid black;
- padding-top: 3px;
- padding-bottom: 3px;
+ grid-row: 1;
+ grid-column: 1;
padding-left: 4px;
+ white-space: nowrap;
+ overflow: clip;
+ text-overflow: ellipsis;
+}
+
+.role_name::before {
+ content: "\25cb ";
+ opacity: 0.6;
+}
+
+.role.present .role_name::before {
+ content: "\25cf ";
+}
+
+.role_stat {
+ grid-row: 1;
+ grid-column: 2;
+ text-align: right;
padding-right: 4px;
}
.role_user {
- font-style: italic;
+ grid-row: 2;
+ grid-column: 1 / 3;
text-align: right;
+ padding-bottom: 3px;
+ padding-right: 5px;
+ font-style: italic;
}
.role_user a {
@@ -424,13 +460,15 @@ menu li:hover img {
text-decoration: underline;
}
-.role_name::before {
- content: "\25cb ";
- opacity: 0.6;
+.role_info {
+ grid-row: 3;
+ grid-column: 1 / 3;
+ border-top: 1px solid black;
+ background-color: silver;
}
-.role.present .role_name::before {
- content: "\25cf ";
+.role_info:empty {
+ display: none;
}
/* MAP */
@@ -580,9 +618,9 @@ menu li:hover img {
}
@media (max-height: 600px) {
- .role_name:not(:hover) div.role_user {
- display: none;
- }
+ .role:not(:hover) .role_name { padding-bottom: 3px; }
+ .role:not(:hover) .role_stat { padding-bottom: 3px; }
+ .role:not(:hover) .role_user { display: none; }
}
@media (max-width: 800px) {
diff --git a/public/common/client.js b/public/common/client.js
index ae0f0b8..79592c9 100644
--- a/public/common/client.js
+++ b/public/common/client.js
@@ -4,7 +4,7 @@
/* PUBLIC GLOBALS */
-var roles = Array.from(document.querySelectorAll(".role")).map(x=>({id:x.id,role:x.id.replace(/^role_/,"").replace(/_/g," ")}))
+var roles = null
var player = "Observer"
var view = null
@@ -163,7 +163,7 @@ function init_chat() {
<div id="chat_text"></div>
<form id="chat_form" action=""><input id="chat_input" autocomplete="off"></form>
`
- document.querySelector("body").appendChild(chat_window)
+ document.body.appendChild(chat_window)
let chat_button = document.getElementById("chat_button")
chat_button.classList.remove("hide")
@@ -191,7 +191,7 @@ function init_chat() {
}
})
- document.querySelector("body").addEventListener("keydown", e => {
+ document.body.addEventListener("keydown", e => {
if (e.key === "Escape") {
if (chat.is_visible) {
e.preventDefault()
@@ -298,7 +298,7 @@ function init_notepad() {
<textarea id="notepad_input" maxlength="16000" oninput="dirty_notepad()"></textarea>
<div id="notepad_footer"><button id="notepad_save" onclick="save_notepad()" disabled>Save</button></div>
`
- document.querySelector("body").appendChild(notepad_window)
+ document.body.appendChild(notepad_window)
notepad = {
is_visible: false,
@@ -379,19 +379,46 @@ function on_game_over() {
}
}
-/* CONNECT TO GAME SERVER */
+/* PLAYER ROLE LIST */
+
+function init_role_element(role_id, role_name) {
+ let e_role = document.createElement("div")
+ e_role.id = role_id
+ e_role.className = "role"
+ e_role.innerHTML =
+ `<div class="role_name"><span>${role_name}</span></div>` +
+ `<div class="role_stat"></div>` +
+ `<div class="role_user"></div>` +
+ `<div class="role_info"></div>`
+ document.getElementById("roles").appendChild(e_role)
+ return e_role
+}
-// XXX
function init_player_names(players) {
- for (let i = 0; i < roles.length; ++i) {
- let p = players.find(p => p.role === roles[i].role)
- if (p)
- document.getElementById(roles[i].id).querySelector(".role_user").innerHTML = `<a href="/user/${p.name}" target="_blank">${p.name}</a>`
+ roles = {}
+ for (let pp of players) {
+ let class_name = pp.role.replace(/\W/g, "_")
+ let id = "role_" + class_name
+ let e = document.getElementById(id)
+ if (!e)
+ e = init_role_element(id, pp.role)
+ let obj = roles[pp.role] = {
+ class_name: class_name,
+ id: id,
+ element: e,
+ name: e.querySelector(".role_name"),
+ stat: e.querySelector(".role_stat"),
+ user: e.querySelector(".role_user"),
+ }
+ if (pp.name)
+ obj.user.innerHTML = `<a href="/user/${pp.name}" target="_blank">${pp.name}</a>`
else
- document.getElementById(roles[i].id).querySelector(".role_user").textContent = "NONE"
+ obj.user.textContent = 'NONE'
}
}
+/* CONNECT TO GAME SERVER */
+
function send_message(cmd, arg) {
let data = JSON.stringify([ cmd, arg ])
console.log("SEND %s %s", cmd, arg)
@@ -476,7 +503,7 @@ function connect_play() {
case "players":
player = arg[0]
- document.querySelector("body").classList.add(player.replace(/ /g, "_"))
+ document.body.classList.add(player.replace(/\W/g, "_"))
if (player !== "Observer") {
init_chat()
init_notepad()
@@ -487,13 +514,8 @@ function connect_play() {
break
case "presence":
- {
- let list = Array.isArray(arg) ? arg : Object.keys(arg)
- for (let i = 0; i < roles.length; ++i) {
- let elt = document.getElementById(roles[i].id)
- elt.classList.toggle("present", list.includes(roles[i].role))
- }
- }
+ for (let role in roles)
+ roles[role].element.classList.toggle("present", arg.includes(role))
break
case "state":
@@ -509,6 +531,7 @@ function connect_play() {
game_log.push(line)
on_update_header()
+ on_update_roles()
if (typeof on_update === "function")
on_update()
on_update_log(view.log_start, game_log.length)
@@ -521,12 +544,10 @@ function connect_play() {
if (snap_count === 0)
replay_panel.remove()
else
- document.querySelector("body").appendChild(replay_panel)
- console.log("SNAPSIZE", snap_count)
+ document.body.appendChild(replay_panel)
break
case "snap":
- console.log("SNAP", arg[0])
snap_active[arg[0]] = arg[1]
snap_cache[arg[0]] = arg[2]
show_snap(arg[0])
@@ -569,6 +590,12 @@ function on_update_header() {
old_active = view.active
}
+function on_update_roles() {
+ if (view.active !== undefined)
+ for (let role in roles)
+ roles[role].element.classList.toggle("active", view.active === role)
+}
+
/* LOG */
function on_update_log(change_start, end) {
@@ -591,7 +618,7 @@ function on_update_log(change_start, end) {
entry.style.textDecoration = "none"
div.appendChild(entry)
} else if (typeof on_log === "function") {
- div.appendChild(on_log(text))
+ div.appendChild(on_log(text, i))
} else {
let entry = document.createElement("div")
entry.textContent = text
@@ -860,6 +887,7 @@ function show_snap(snap_id) {
view = snap_cache[snap_id]
view.prompt = "Replay " + snap_id + " / " + snap_count + " \u2013 " + snap_active[snap_id]
on_update_header()
+ on_update_roles()
on_update()
on_update_log(view.log, view.log)
}
@@ -889,6 +917,7 @@ function on_snap_stop() {
view = snap_view
snap_view = null
on_update_header()
+ on_update_roles()
on_update()
on_update_log(game_log.length, game_log.length)
}
@@ -902,16 +931,16 @@ window.addEventListener("keydown", (evt) => {
if (document.activeElement === document.getElementById("notepad_input"))
return
if (evt.key === "Shift")
- document.querySelector("body").classList.add("shift")
+ document.body.classList.add("shift")
})
window.addEventListener("keyup", (evt) => {
if (evt.key === "Shift")
- document.querySelector("body").classList.remove("shift")
+ document.body.classList.remove("shift")
})
window.addEventListener("blur", function (evt) {
- document.querySelector("body").classList.remove("shift")
+ document.body.classList.remove("shift")
})
/* TOGGLE ZOOM MAP TO FIT */
@@ -976,8 +1005,6 @@ var update_zoom = function () {}
update_map_size()
- console.log("INIT MAP", map, map_w, map_h, window.devicePixelRatio)
-
var transform0 = { x: 0, y: 0, scale: 1 }
var transform1 = { x: 0, y: 0, scale: 1 }
var old_scale = 1
diff --git a/public/common/replay.js b/public/common/replay.js
index c1033f5..c8c04ea 100644
--- a/public/common/replay.js
+++ b/public/common/replay.js
@@ -137,11 +137,9 @@ function update_replay_view() {
player = "Observer"
}
- let body = document.querySelector("body")
- body.classList.remove("Observer")
- for (let i = 0; i < roles.length; ++i)
- body.classList.remove(roles[i].role.replace(/ /g, "_"))
- body.classList.add(player.replace(/ /g, "_"))
+ document.body.classList.toggle("Observer", player === "Observer")
+ for (let r in roles)
+ document.body.classList.toggle(roles[r].class_name, player === r)
view = rules.view(replay_state, player)
if (params.mode !== "debug")
@@ -153,7 +151,7 @@ function update_replay_view() {
view.game_over = 1
if (replay.length > 0) {
- if (document.querySelector("body").classList.contains("shift")) {
+ if (document.body.classList.contains("shift")) {
view.prompt = `[${replay_this}/${replay.length}] ${replay_state.active} / ${replay_state.state}`
if (replay_this < replay.length)
view.prompt += ` / ${replay[replay_this][1]} ${replay[replay_this][2]}`
@@ -308,7 +306,7 @@ async function load_replay() {
set_hash(replay.length)
on_hash_change()
window.addEventListener("hashchange", on_hash_change)
- document.querySelector("body").appendChild(replay_panel)
+ document.body.appendChild(replay_panel)
} else {
console.log("REPLAY NOT AVAILABLE")
replay_state = body.state
@@ -319,8 +317,8 @@ async function load_replay() {
let viewpoint_panel = document.createElement("div")
viewpoint_panel.id = "viewpoint_panel"
create_viewpoint_button(viewpoint_panel, "Active", "Active")
- for (let r of roles)
- create_viewpoint_button(viewpoint_panel, r.role, r.role)
+ for (let r in roles)
+ create_viewpoint_button(viewpoint_panel, r, r)
create_viewpoint_button(viewpoint_panel, "Observer", "Observer")
document.getElementById("actions").appendChild(viewpoint_panel)
diff --git a/server.js b/server.js
index b4620e6..e852ca9 100644
--- a/server.js
+++ b/server.js
@@ -2737,8 +2737,8 @@ wss.on('connection', (socket, req) => {
SLOG(socket, "OPEN " + socket.seen)
try {
- let title_id = SQL_SELECT_GAME_TITLE.get(socket.game_id)
- if (title_id !== socket.title_id)
+ let game = SQL_SELECT_GAME.get(socket.game_id)
+ if (game.title_id !== socket.title_id)
return socket.close(1000, "Invalid game ID.")
let players = socket.players = SQL_SELECT_PLAYERS_JOIN.all(socket.game_id)
@@ -2756,8 +2756,13 @@ wss.on('connection', (socket, req) => {
}
}
- if (socket.seen === 0)
- send_message(socket, 'players', [socket.role, players.map(p => ({ name: p.name, role: p.role }))])
+ if (socket.seen === 0) {
+ let roles = get_game_roles(game.title_id, game.scenario, parse_game_options(game.options))
+ send_message(socket, 'players', [
+ socket.role,
+ roles.map(r => ({ role: r, name: players.find(p => p.role === r)?.name }))
+ ])
+ }
if (game_clients[socket.game_id]) {
game_clients[socket.game_id].push(socket)