window.player = { uid: NaN };
window.info_elem = document.getElementById("miscinfo");
window.identform = document.getElementById("identform");
window.statusline = document.getElementsByClassName("statusline")[0];
window.bcont_elem = document.getElementById("board-container");
window.board_elem = document.getElementById("board");
window.cursor_frame = document.getElementById("cursor-frame");
window.queued_pos = undefined;
window.room = {
name: undefined,
bconf: { w: NaN, h: NaN, tile_w: NaN, tile_h: NaN, mine_ratio: undefined },
board: {},
cbounds: {},
socket: undefined,
last_packet: undefined,
identity: JSON.parse(localStorage.getItem("identity")),
cursors: new Map(),
};
if (room.identity == null) {
statusline.style.display = "none";
identform.style.display = "initial";
} else {
join();
}
function join() {
if (room.identity == null) {
room.identity = {};
room.identity.name = document.getElementById("name-in").value;
room.identity.clr = document.getElementById("clr-in").value;
localStorage.setItem("identity", JSON.stringify(room.identity));
}
identform.style.display = "none";
room.socket = connect();
statusline.style.display = "flex";
}
function clear_ident() {
localStorage.removeItem("identity");
document.location.reload();
}
function connect() {
let wsproto = (window.location.protocol == "https:")? "wss:": "ws:";
let s = new WebSocket(`${wsproto}//${location.hostname}:${location.port}${location.pathname}/ws`);
s.onopen = function() {
s.send(`register ${room.identity.name} ${room.identity.clr}`);
}
s.onmessage = function(e) {
room.last_packet = e;
let d = e.data;
if (typeof d == "object") {
d.arrayBuffer().then(acceptBoard);
info_elem.onclick = undefined;
info_elem.innerHTML = `${room.name} (${room.bconf.w}x${room.bconf.h}) >> Running, ${room.bconf.mine_ratio} tiles are mines`;
} else if (typeof e.data == "string") {
let fields = d.split(" ");
switch (fields[0]) {
case "pos": {
let posdata = JSON.parse(fields[1]);
posdata.forEach(pdat => {
let oid = Number(pdat[0]);
let x = pdat[1][0];
let y = pdat[1][1];
let curs = room.cursors.get(oid);
if (oid != player.uid && curs != undefined) {
movCursor(curs, x, y);
} else {
console.log("livepos sys incoherent");
}
});
} break;
case "players": {
let pdata = JSON.parse(fields[1]);
console.log(pdata);
pdata.forEach(p => {
let oid = Number(p[0]);
let name = p[1];
let clr = p[2];
console.log(oid, name, clr);
if (!room.cursors.has(oid)) {
createCursor(oid, name, clr);
}
});
} break;
case "regack": {
room.name = fields[1];
name = fields[2];
player.uid = Number(fields[3]);
let dims = fields[4].split("x");
room.bconf.w = Number(dims[0]);
room.bconf.h = Number(dims[1]);
room.bconf.mine_ratio = fields[5];
createCursor(player.uid, name, room.identity.clr);
} break;
case "win": {
info_elem.innerHTML = "You win! Click here to play again.";
info_elem.onclick = e => { s.send("reset") };
} break;
case "lose": {
let badone = fields[1];
info_elem.innerHTML = `You lost, ${badone} was blown up. Click here to retry.`;
info_elem.onclick = e => { s.send("reset") };
} break;
case "logoff": {
let oid = Number(fields[1]);
room.cursors.get(oid).elem.remove();
room.cursors.get(oid).selwin.remove();
room.cursors.delete(oid);
} break;
}
}
}
s.onerror = function(e) { info_elem.innerHTML += `
Connection error: ${e}`; }
s.onclose = function(e) { info_elem.innerHTML = "Connection closed"; }
return s;
}
function acceptBoard(data) {
let dataarr = new Uint8Array(data);
let vals = fflate.inflateSync(dataarr);
room.board = vals.reduce((s,c) => {
let v = String.fromCodePoint(c);
if (v == ' ') {
s = s + " ";
} else {
s = s + v;
}
return s;
}, "");
let last = room.board[0];
let last_idx = 0;
let split_board = [];
for (let i = 1; i < room.board.length+1; i++) {
let cur = room.board[i];
let gamechars = /^[CFO# 1-8]+$/;
if ((cur != last && gamechars.test(cur)) || cur == undefined) {
let txt = room.board.substr(last_idx, i-last_idx);
switch(txt[0]) {
case 'O':
txt = `${txt}`;
break;
case 'C':
txt = `${txt}`;
break;
case 'F':
txt = `${txt}`;
break;
case '1': txt = `${txt}`; break;
case '2': txt = `${txt}`; break;
case '3': txt = `${txt}`; break;
case '4': txt = `${txt}`; break;
case '5': txt = `${txt}`; break;
case '6': txt = `${txt}`; break;
default: txt = `${txt}`; break;
}
split_board.push(txt);
last_idx = i;
}
last = room.board[i];
}
board_elem.innerHTML = split_board.join("");
room.cbounds = getBoardBounds();
}
function createCursor(id, name, clr) {
// shit doesn't line up
let cursor = document.createElement("div");
cursor.style.position = "absolute";
let nametag = document.createElement("p");
nametag.innerHTML = name;
nametag.classList.add('cursor-name');
let selection_window = document.createElement("div");
selection_window.style.backgroundColor = clr + "a0";
selection_window.style.position = "absolute";
selection_window.classList.add('cursor');
cursor.appendChild(nametag);
cursor.classList.add('cursor');
cursor.style.color = clr;
document.getElementById('cursor-frame').append(cursor);
document.getElementById('cursor-frame').append(selection_window);
let c = { name: name, elem: cursor, selwin: selection_window };
if (id == window.player.uid) {
document.addEventListener('mousemove', e => {
let bcoords = pageToBoardPx(e.pageX, e.pageY);
movCursor(c, bcoords[0], bcoords[1]);
window.queued_pos = bcoords;
},
false);
}
room.cursors.set(id, {name: name, elem: cursor, selwin: selection_window});
return cursor;
}
function pageToBoardPx(x,y) {
return [Math.floor(x - room.cbounds.ox), Math.floor(y - room.cbounds.oy)];
}
function movCursor(c, bx, by) {
c.elem.style.left = (room.cbounds.ox + bx) + 'px';
c.elem.style.top = (room.cbounds.oy + by) + 'px';
movSelWin(c.selwin, bx, by);
}
function movSelWin(win, bx, by) {
let tpos = tilepos(bx,by);
console.log(tpos);
if (tpos.x > (room.bconf.w - 1) || tpos.x < 0 || tpos.y > (room.bconf.h - 1) || tpos.y < 0) {
win.style.display = "none";
} else {
win.style.display = "";
}
win.style.left = (tpos.x * room.bconf.tile_w) + 'px';
win.style.top = (tpos.y * room.bconf.tile_h) + 'px';
win.style.width = room.bconf.tile_w + 'px';
win.style.height = room.bconf.tile_h + 'px';
}
function getBoardBounds() {
let a = bcont_elem.getBoundingClientRect();
let b = board_elem.getBoundingClientRect();
room.bconf.tile_w = b.width / room.bconf.w;
room.bconf.tile_h = 48;
return {
ox: b.x + window.scrollX,
oy: a.y + window.scrollY,
w: b.width,
h: a.height
};
}
window.onresize = () => {
room.cbounds = getBoardBounds();
}
bcont_elem.onclick = function(e) {
let bcoords = pageToBoardPx(e.pageX, e.pageY);
let tpos = tilepos(bcoords[0], bcoords[1]);
let cmd = `reveal ${tpos.x} ${tpos.y}`;
room.socket.send(cmd);
}
bcont_elem.oncontextmenu = function(e) {
let bcoords = pageToBoardPx(e.pageX, e.pageY);
let tpos = tilepos(bcoords[0], bcoords[1]);
let cmd = `flag ${tpos.x} ${tpos.y}`;
room.socket.send(cmd);
return false;
}
// these are board-px coords
function tilepos(bx,by) {
let b = room.cbounds; // we can assume it is already computed by earlier aux calls
let tilex = Math.floor(room.bconf.w * bx/b.w);
let tiley = Math.floor(room.bconf.h * by/b.h);
return { x: tilex, y: tiley };
}
(function sendPos() {
let qp = window.queued_pos;
if (qp) {
room.socket.send(`pos ${qp[0]} ${qp[1]}`);
window.queued_pos = undefined;
}
setTimeout(function() {
sendPos();
}, 16);
})();