diff --git a/class/Gamepad.js b/class/Gamepad.js index 3fbb36c..913fee3 100644 --- a/class/Gamepad.js +++ b/class/Gamepad.js @@ -2,32 +2,6 @@ import EventEmitter from "./EventEmitter.js"; export default class extends EventEmitter { downKeys = new Set(); - static mask(key) { - switch (key.toLowerCase()) { - case 'a': - case 'arrowleft': - return 'left'; - case 'd': - case 'arrowright': - return 'right'; - case 'w': - case 'arrowup': - return 'up'; - case 's': - case 'arrowdown': - return 'down'; - case 'z': - return 'z'; - default: - return null; - } - } - - constructor(parent) { - super(); - this.parent = parent; - } - init() { window.addEventListener('blur', this.blur.bind(this)); window.addEventListener('keydown', this.down.bind(this)); @@ -47,7 +21,6 @@ export default class extends EventEmitter { this.downKeys.add(key); this.emit('down', key); - this.parent.updateRecords(key); } up(event) { @@ -59,7 +32,6 @@ export default class extends EventEmitter { this.downKeys.delete(key); this.emit('up', key); - this.parent.updateRecords(key); } toggle(key) { @@ -71,7 +43,29 @@ export default class extends EventEmitter { } close() { + window.removeEventListener('blur', this.blur.bind(this)); window.removeEventListener('keydown', this.down.bind(this)); window.removeEventListener('keyup', this.up.bind(this)); } + + static mask(key) { + switch (key.toLowerCase()) { + case 'a': + case 'arrowleft': + return 'left'; + case 'd': + case 'arrowright': + return 'right'; + case 'w': + case 'arrowup': + return 'up'; + case 's': + case 'arrowdown': + return 'down'; + case 'z': + return 'z'; + default: + return null; + } + } } \ No newline at end of file diff --git a/class/Player.js b/class/Player.js index cd76c95..6a21004 100644 --- a/class/Player.js +++ b/class/Player.js @@ -15,7 +15,7 @@ const Bike = { export default class Player { dead = false; explosion = null; - gamepad = new Gamepad(this); + gamepad = new Gamepad(); ghost = false; gravity = new Vector(0, .3); itemsCollected = new Set(); @@ -31,6 +31,8 @@ export default class Player { this.records = records; } else { this.gamepad.init(); + this.gamepad.on('down', this.updateRecords.bind(this)); + this.gamepad.on('up', this.updateRecords.bind(this)); } this.createCosmetics(); @@ -43,10 +45,10 @@ export default class Player { } createCosmetics() { - this.cosmetics = this._user != void 0 ? this._user.cosmetics : { head: "hat" } + this.cosmetics = this._user != void 0 ? this._user.cosmetics : { head: 'hat' } } - createVehicle(vehicle = "BMX") { + createVehicle(vehicle = 'BMX') { this.vehicle = new Bike[vehicle](this); } @@ -93,19 +95,19 @@ export default class Player { if (this.ghost) { if (this.records[0].has(this.scene.currentTime)) { - this.gamepad.toggle("left"); + this.gamepad.toggle('left'); } if (this.records[1].has(this.scene.currentTime)) { - this.gamepad.toggle("right"); + this.gamepad.toggle('right'); } if (this.records[2].has(this.scene.currentTime)) { - this.gamepad.toggle("up"); + this.gamepad.toggle('up'); } if (this.records[3].has(this.scene.currentTime)) { - this.gamepad.toggle("down"); + this.gamepad.toggle('down'); } if (this.records[4].has(this.scene.currentTime)) { @@ -128,29 +130,29 @@ export default class Player { } this.scene.cameraFocus = this.vehicle.head; - if (typeof keys === "string") { + if (typeof keys == 'string') { keys = new Set([ keys ]); } - if (keys.has("left") && !this.records[0].delete(this.scene.currentTime)) { + if (keys.has('left') && !this.records[0].delete(this.scene.currentTime)) { this.records[0].add(this.scene.currentTime); } - if (keys.has("right") && !this.records[1].delete(this.scene.currentTime)) { + if (keys.has('right') && !this.records[1].delete(this.scene.currentTime)) { this.records[1].add(this.scene.currentTime); } - if (keys.has("up") && !this.records[2].delete(this.scene.currentTime)) { + if (keys.has('up') && !this.records[2].delete(this.scene.currentTime)) { this.records[2].add(this.scene.currentTime); } - if (keys.has("down") && !this.records[3].delete(this.scene.currentTime)) { + if (keys.has('down') && !this.records[3].delete(this.scene.currentTime)) { this.records[3].add(this.scene.currentTime); } - if (keys.has("z") && this.gamepad.downKeys.has("z") && !this.dead) { + if (keys.has('z') && this.gamepad.downKeys.has('z') && !this.dead) { if (!this.records[4].delete(this.scene.currentTime)) { this.records[4].add(this.scene.currentTime); this.vehicle.swap(); @@ -178,20 +180,20 @@ export default class Player { async trackComplete() { if (this.targetsCollected === this.scene.targets && this.scene.currentTime > 0 && !this.scene.editor) { alert(await fetch("/tracks/ghosts/save", { - method: "post", + method: 'post', body: new URLSearchParams({ - id: window.location.pathname.split("/")[2], + id: window.location.pathname.split('/')[2], vehicle: this.vehicle.name, time: this.scene.currentTime, code: `${game.scene.firstPlayer.records.map(record => [...record].join(" ")).join(",")},${this.scene.currentTime},${this.vehicle.name}` }) - }).then(r => r.text()).then(function (response) { + }).then(r => r.text()).then(function(response) { if (response !== "Ghost saved!") { if (confirm(response)) { return fetch("/tracks/ghosts/save", { - method: "post", + method: 'post', body: new URLSearchParams({ - id: window.location.pathname.split("/")[2], + id: window.location.pathname.split('/')[2], vehicle: this.vehicle.name, time: this.scene.currentTime, code: `${game.scene.firstPlayer.records.map(record => [...record].join(" ")).join(",")},${this.scene.currentTime},${this.vehicle.name}`, @@ -252,18 +254,18 @@ export default class Player { } reset() { - this.hat = null; - this.slow = false; this.dead = false; this.explosion = null; - this.pendingConsumables = 0; - this.itemsCollected = new Set(); this.gravity = new Vector(0, .3); + this.hat = null; + this.itemsCollected = new Set(); + this.pendingConsumables = 0; + this.slow = false; this.snapshots.reset(); if (this.ghost) { this.gamepad.downKeys.clear(); } else { - this.records = Array.from({ length: 5 }, () => new Set()); + this.records.forEach(set => set.clear()); this.updateRecords(this.gamepad.downKeys); } diff --git a/class/handler/Tool.js b/class/handler/Tool.js index aef9c68..01f8729 100644 --- a/class/handler/Tool.js +++ b/class/handler/Tool.js @@ -52,11 +52,14 @@ export default class { this.currentTool.scenery = style; } - let settings = this.scene.parent.container.querySelector('bhr-game-toolbar')?.querySelector(".left .tool-settings"); + let settings = this.scene.parent.container.querySelector('bhr-game-toolbar')?.querySelector('.left .tool-settings'); settings !== null && settings.style.setProperty('display', ['brush', 'circle', 'eraser'].includes(this.selected) ? 'block' : 'none'); settings = settings.querySelector('div[data-id=eraser]'); settings !== null && settings.style.setProperty('display', this.selected == 'eraser' ? 'block' : 'none'); + let tool = this.scene.parent.container.querySelector(`.toolbar-item${style ? '.scenery' : ''}.${name} > input[type=radio]`); + tool !== null && (tool.checked = true); + this.scene.parent.canvas.style.setProperty('cursor', name == 'camera' ? 'move' : 'none'); } diff --git a/class/scenes/Main.js b/class/scenes/Main.js index aaaa53a..92bc153 100644 --- a/class/scenes/Main.js +++ b/class/scenes/Main.js @@ -140,22 +140,21 @@ export default class { } watchGhost(data, { id, vehicle = 'BMX' } = {}) { - let records = data.split(/\s?\u002C\s?/g).map(item => new Set(item.split(/\s+/g).filter(item => item).map(item => isNaN(+item) ? item : +item))); - let v = Array.from(records[records.length - 1])[0]; + const records = data.split(/\s*,\s*/g).map(item => item.split(/\s+/g).reduce((newArr, arr) => isNaN(arr) ? arr : newArr.add(parseInt(arr)), new Set())); + const v = records.at(-1); if (['BMX', 'MTB'].includes(v.toUpperCase())) { vehicle = v; } this.reset(); - let player = id && this.players.find(player => player.id === +id); + let player = id && this.players.find(player => player.id == id); if (!player) { - this.players.push(new Player(this, { + player = new Player(this, { records, vehicle - })); - - player = this.players[this.players.length - 1]; - player.id = +id; + }); + player.id = id; + this.players.push(player); } this.cameraFocus = player.vehicle.head; @@ -239,10 +238,9 @@ export default class { } } - ctx.beginPath(), - ctx.lineWidth = 10, - ctx.strokeStyle = this.parent.settings.theme == 'dark' ? "#1b1b1b" : "#fff", - ctx.fillStyle = this.parent.settings.theme == 'dark' ? "#fbfbfb" : "#000"; + ctx.textBaseline = 'middle', + ctx.fillStyle = '#'.padEnd(7, this.parent.settings.theme == 'dark' ? 'fb' : '0'), + ctx.strokeStyle = '#'.padEnd(7, this.parent.settings.theme == 'dark' ? '1b' : 'f'); let i = this.timeText; if (this.paused && !this.parent.settings.ap) { @@ -276,31 +274,41 @@ export default class { padding: 5 }); ctx.clip(), - ctx.filter = "blur(10px)", + ctx.filter = 'blur(10px)', ctx.drawImage(ctx.canvas, rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, rect.width, rect.height); ctx.filter = 'none', ctx.fillStyle = 'rgba(128,128,128,0.4)', ctx.fill(), ctx.restore(); - ctx.textBaseline = 'middle', ctx.fillText(i, 55, 12); ctx.save(), ctx.beginPath(), ctx.lineWidth = goalStrokeWidth, - ctx.fillStyle = "#ff0", - ctx.strokeStyle = this.parent.settings.theme == 'dark' ? "#fbfbfb" : "#000"; + ctx.fillStyle = '#ff0', + ctx.strokeStyle = this.parent.settings.theme == 'dark' ? '#fbfbfb' : '#000'; ctx.arc(45, 12, goalRadius / 1.5, 0, 2 * Math.PI), ctx.fill(), ctx.stroke(), ctx.restore(); if (this.players.length > 1) { for (var p = 1, player = this.players[p]; p < this.players.length; player = this.players[++p]) { - ctx.textAlign = "right", - ctx.fillStyle = this.parent.settings.theme == 'dark' ? "#999" : "#aaa", - ctx.strokeText(i = (player.name || "Ghost") + (player.targetsCollected === this.targets ? " finished!" : ": " + player.targetsCollected + " / " + this.targets), this.parent.canvas.width - 7, 16 * p + (p * 4)); - ctx.fillText(i, this.parent.canvas.width - 7, 16 * p + (p * 4)), - ctx.textAlign = "left", - ctx.fillStyle = this.parent.settings.theme == 'dark' ? "#fbfbfb" : "#000"; + i = (player.name || 'Ghost') + (player.targetsCollected === this.targets ? " finished!" : ": " + player.targetsCollected + " / " + this.targets); + const text = ctx.measureText(i) + ctx.save(); + let rect = roundedRect.call(ctx, ctx.canvas.width - text.width - 40, 8 * (p - 1) + (p * 12) - (text.fontBoundingBoxAscent + text.fontBoundingBoxDescent) / 4, text.width, (text.fontBoundingBoxAscent + text.fontBoundingBoxDescent) / 2, 40, { + padding: 5 + }); + ctx.clip(), + ctx.filter = 'blur(10px)', + ctx.drawImage(ctx.canvas, rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, rect.width, rect.height); + ctx.filter = 'none', + ctx.fillStyle = 'rgba(128,128,128,0.4)', + ctx.fill(), + ctx.restore(); + ctx.save(), + ctx.textAlign = 'right'; + ctx.fillText(i, ctx.canvas.width - 40, 8 * (p - 1) + (p * 12)); + ctx.restore(); } } @@ -308,8 +316,8 @@ export default class { let b = (this.parent.canvas.width - 250) / 2; c = (this.parent.canvas.height - 150) / 2; ctx.lineWidth = 1; - ctx.strokeStyle = this.parent.settings.theme === "dark" ? "#1b1b1b" : "#fff"; - ctx.fillStyle = "rgba(0, 0, 0, 0.4)"; + ctx.strokeStyle = this.parent.settings.theme == 'dark' ? '#1b1b1b' : '#fff'; + ctx.fillStyle = 'rgba(0,0,0,0.4)'; ctx.fillRect(0, 0, this.parent.canvas.width, c); ctx.fillRect(0, c + 150, this.parent.canvas.width, c); ctx.fillRect(0, c, b, 150); @@ -520,6 +528,7 @@ function roundedRect(x, y, width, height, radius = 0, options = {}) { } radius = Math.min(width / 2, height / 2, radius); + this.beginPath(); this.moveTo(x + radius, y); this.lineTo(x + width - radius, y); this.arcTo(x + width, y, x + width, y + radius, radius); diff --git a/index.html b/index.html index 2945bfe..b25e0b3 100644 --- a/index.html +++ b/index.html @@ -8,17 +8,6 @@ - @@ -184,8 +173,8 @@ - + \ No newline at end of file