init
This commit is contained in:
226
static/game.js
Normal file
226
static/game.js
Normal file
@@ -0,0 +1,226 @@
|
||||
// CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
|
||||
// if (w < 2 * r) r = w / 2;
|
||||
// if (h < 2 * r) r = h / 2;
|
||||
// this.beginPath();
|
||||
// this.moveTo(x+r, y);
|
||||
// this.arcTo(x+w, y, x+w, y+h, r);
|
||||
// this.arcTo(x+w, y+h, x, y+h, r);
|
||||
// this.arcTo(x, y+h, x, y, r);
|
||||
// this.arcTo(x, y, x+w, y, r);
|
||||
// this.closePath();
|
||||
// return this;
|
||||
// }
|
||||
|
||||
function startGame() {
|
||||
const canvas = document.querySelector("canvas");
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.font = "24px Montserrat"
|
||||
ctx.imageSmoothingEnabled = true
|
||||
|
||||
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
if (x < 0 || y < 0 || x > rect.width || y > rect.height) return;
|
||||
|
||||
// Adjust for potential canvas scaling (e.g. high DPI)
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
socket.emit("cursor", x * scaleX, y * scaleY);
|
||||
})
|
||||
|
||||
canvas.addEventListener('click', e => click(true, e, canvas))
|
||||
canvas.addEventListener('contextmenu', e => click(false, e, canvas))
|
||||
|
||||
gameLoop(ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} left
|
||||
* @param {PointerEvent} e
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
*/
|
||||
function click(left, e, canvas) {
|
||||
e.preventDefault()
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
if (x < 0 || y < 0 || x > rect.width || y > rect.height) return;
|
||||
|
||||
// Adjust for potential canvas scaling (e.g. high DPI)
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
socket.emit("click", left, Math.floor((x * scaleX - offsetX()) / CELL_SIZE), Math.floor((y * scaleY - offsetY()) / CELL_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
function gameLoop(ctx) {
|
||||
ctx.save()
|
||||
ctx.clearRect(0, 0, 800, 600)
|
||||
|
||||
|
||||
for (let x = 0; x < data.field.length; x++)
|
||||
for (let y = 0; y < data.field[x].length; y++)
|
||||
drawCell(ctx, x, y)
|
||||
|
||||
const x = 400
|
||||
const y = 600 - (600 - (CELL_SIZE * MAX_HEIGTH)) * 0.5
|
||||
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.fillStyle = getColor('fg')
|
||||
|
||||
const w = data.field.length
|
||||
const h = w && data.field[0].length
|
||||
ctx.fillText(`${data.text} 🚩 ${data.flags} 💣 ${data.bombs} Поле ${w}x${h}`.trim(), x, y)
|
||||
|
||||
for (const sid of Object.keys(data.cursors))
|
||||
drawCursor(ctx, sid)
|
||||
|
||||
|
||||
ctx.restore();
|
||||
window.requestAnimationFrame(() => gameLoop(ctx))
|
||||
}
|
||||
|
||||
const HALF_CELL_SIZE = 16
|
||||
const CELL_SIZE = HALF_CELL_SIZE * 2
|
||||
const MAX_WIDTH = 25
|
||||
const MAX_HEIGTH = 15
|
||||
|
||||
function offsetX() {
|
||||
return (MAX_WIDTH - data.field.length) * HALF_CELL_SIZE
|
||||
}
|
||||
function offsetY() {
|
||||
return (MAX_HEIGTH - (data.field.length && data.field[0].length)) * HALF_CELL_SIZE
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {number} cx
|
||||
* @param {number} cy
|
||||
*/
|
||||
function drawCell(ctx, cx, cy) {
|
||||
let cell = data.field[cx][cy]
|
||||
switch (cell) {
|
||||
case "":
|
||||
case "🚩":
|
||||
case "❓":
|
||||
ctx.fillStyle = getColor("bgCellClosed")
|
||||
break;
|
||||
case " ":
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
case "8":
|
||||
ctx.fillStyle = getColor("bgCellOpen")
|
||||
break
|
||||
default:
|
||||
ctx.fillStyle = getColor("bgCellBomb")
|
||||
break
|
||||
}
|
||||
ctx.strokeStyle = getColor("bg")
|
||||
ctx.lineWidth = 4
|
||||
|
||||
const x = cx * CELL_SIZE + offsetX();
|
||||
const y = cy * CELL_SIZE + offsetY();
|
||||
ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE)
|
||||
ctx.strokeRect(x, y, CELL_SIZE, CELL_SIZE)
|
||||
switch (cell) {
|
||||
case "":
|
||||
case " ":
|
||||
return
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
case "8":
|
||||
ctx.fillStyle = getColor('fgCell' + cell)
|
||||
break
|
||||
default:
|
||||
ctx.fillStyle = "white"
|
||||
break
|
||||
}
|
||||
|
||||
ctx.textAlign = "center"
|
||||
ctx.textBaseline = "middle"
|
||||
ctx.fillText(cell, x + HALF_CELL_SIZE, y + HALF_CELL_SIZE)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {string} sid
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} w
|
||||
* @param {number} h
|
||||
*/
|
||||
function drawCursor(ctx, sid, w = 15, h = 20) {
|
||||
const { x, y, nickname } = data.cursors[sid];
|
||||
|
||||
const a = sid.charCodeAt(0) % 256;
|
||||
const b = sid.charCodeAt(1) % 256;
|
||||
const c = sid.charCodeAt(2) % 3;
|
||||
|
||||
const aStr = a < 16 ? "0" + a.toString(16) : a.toString(16);
|
||||
const bStr = b < 16 ? "0" + b.toString(16) : b.toString(16);
|
||||
|
||||
let color = "#";
|
||||
switch (c) {
|
||||
case 0:
|
||||
color += "ff" + aStr + bStr;
|
||||
break;
|
||||
case 1:
|
||||
color += aStr + "ff" + bStr;
|
||||
break;
|
||||
default:
|
||||
color += aStr + bStr + "ff";
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y + h * 0.6);
|
||||
ctx.lineTo(x + w * 0.6, y + h * 0.5);
|
||||
ctx.lineTo(x + w, y + h * 0.9);
|
||||
ctx.lineTo(x + w * 0.9, y + h);
|
||||
ctx.lineTo(x + w * 0.5, y + h * 0.6);
|
||||
ctx.lineTo(x + w * 0.6, y + h);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = "black"
|
||||
ctx.lineWidth = 1
|
||||
ctx.stroke();
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillStyle = "#00000077";
|
||||
const { width: tw, ideographicBaseline: th } = ctx.measureText(nickname)
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(x + w * 2, y, tw + w * 2, Math.abs(th), [w])
|
||||
ctx.fill()
|
||||
ctx.fillStyle = "#ffffff"
|
||||
ctx.fillText(nickname, x + w * 3, y)
|
||||
}
|
||||
Reference in New Issue
Block a user