diff --git a/package-lock.json b/package-lock.json index 6fe14372c..ba4fcfb58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "googleapis": "^143.0.0", "hammerjs": "^2.0.8", "jimp": "^0.22.12", + "lit": "^3.2.1", "msgpack5": "^6.0.2", "node-addon-api": "^8.1.0", "node-gyp": "^10.2.0", @@ -43,7 +44,7 @@ "@types/d3": "^7.4.3", "@types/jest": "^29.5.12", "@types/mocha": "^10.0.7", - "@types/node": "^22.5.2", + "@types/node": "^22.7.5", "@types/sinon": "^17.0.3", "@types/uuid": "^10.0.0", "@types/ws": "^8.5.11", @@ -69,7 +70,7 @@ "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tsx": "^4.17.0", - "typescript": "^5.5.4", + "typescript": "^5.6.3", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" @@ -3655,6 +3656,21 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", + "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, "node_modules/@npmcli/agent": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", @@ -4428,9 +4444,9 @@ } }, "node_modules/@types/node": { - "version": "22.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz", - "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==", + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -4542,6 +4558,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", @@ -9990,6 +10012,37 @@ "dev": true, "license": "MIT" }, + "node_modules/lit": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz", + "integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.1.0", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-element": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz", + "integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-html": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz", + "integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/load-bmfont": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", @@ -13470,9 +13523,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index a549a3c76..033f7c891 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@types/d3": "^7.4.3", "@types/jest": "^29.5.12", "@types/mocha": "^10.0.7", - "@types/node": "^22.5.2", + "@types/node": "^22.7.5", "@types/sinon": "^17.0.3", "@types/uuid": "^10.0.0", "@types/ws": "^8.5.11", @@ -46,7 +46,7 @@ "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tsx": "^4.17.0", - "typescript": "^5.5.4", + "typescript": "^5.6.3", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" @@ -68,6 +68,7 @@ "googleapis": "^143.0.0", "hammerjs": "^2.0.8", "jimp": "^0.22.12", + "lit": "^3.2.1", "msgpack5": "^6.0.2", "node-addon-api": "^8.1.0", "node-gyp": "^10.2.0", diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index cbd22ab31..3b28f02fc 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -8,19 +8,24 @@ import {EventBus} from "../../core/EventBus"; import {TransformHandler} from "./TransformHandler"; import {Layer} from "./layers/Layer"; import {EventsDisplay} from "./layers/EventsDisplay"; -import {RadialMenu} from "./layers/RadialMenu"; +import {RadialMenu} from "./layers/radial/RadialMenu"; +import {EmojiTable} from "./layers/radial/EmojiTable"; export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer { const transformHandler = new TransformHandler(game, eventBus, canvas) + const emojiTable = document.querySelector('emoji-table') as EmojiTable; + if (!emojiTable || !(emojiTable instanceof EmojiTable)) { + console.error('EmojiTable element not found in the DOM'); + } const layers: Layer[] = [ new TerrainLayer(game), new TerritoryLayer(game, eventBus), new NameLayer(game, game.config().theme(), transformHandler, clientID), new UILayer(eventBus, game, clientID, transformHandler), new EventsDisplay(eventBus, game, clientID), - new RadialMenu(eventBus, game, transformHandler, clientID), + new RadialMenu(eventBus, game, transformHandler, clientID, emojiTable as EmojiTable), ] return new GameRenderer(game, eventBus, canvas, transformHandler, layers) @@ -65,7 +70,7 @@ export class GameRenderer { this.layers.forEach(l => { if (l.shouldTransform()) { - l.render(this.context) + l.renderLayer(this.context) } }) @@ -73,7 +78,7 @@ export class GameRenderer { this.layers.forEach(l => { if (!l.shouldTransform()) { - l.render(this.context) + l.renderLayer(this.context) } }) diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index ce390f1e0..17a2dd078 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -263,7 +263,7 @@ export class EventsDisplay implements Layer { this.events[index] = event; } - render(): void { } + renderLayer(): void { } renderTable(): void { if (this.events.length === 0) { diff --git a/src/client/graphics/layers/Layer.ts b/src/client/graphics/layers/Layer.ts index 11f6e3e7e..2c7d7ac7a 100644 --- a/src/client/graphics/layers/Layer.ts +++ b/src/client/graphics/layers/Layer.ts @@ -3,6 +3,6 @@ import {TransformHandler} from "../TransformHandler" export interface Layer { init() tick() - render(context: CanvasRenderingContext2D) + renderLayer(context: CanvasRenderingContext2D) shouldTransform(): boolean } \ No newline at end of file diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 3303ff322..b71f1874c 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -100,7 +100,7 @@ export class NameLayer implements Layer { } } - public render(mainContex: CanvasRenderingContext2D) { + public renderLayer(mainContex: CanvasRenderingContext2D) { const [upperLeft, bottomRight] = this.transformHandler.screenBoundingRect() for (const render of this.renders) { render.isVisible = this.isVisible(render, upperLeft, bottomRight) diff --git a/src/client/graphics/layers/TerrainLayer.ts b/src/client/graphics/layers/TerrainLayer.ts index 5505cce62..69cffec3d 100644 --- a/src/client/graphics/layers/TerrainLayer.ts +++ b/src/client/graphics/layers/TerrainLayer.ts @@ -41,7 +41,7 @@ export class TerrainLayer implements Layer { }) } - render(context: CanvasRenderingContext2D) { + renderLayer(context: CanvasRenderingContext2D) { context.drawImage( this.canvas, -this.game.width() / 2, diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index b2c797761..4b9dd496d 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -50,7 +50,7 @@ export class TerritoryLayer implements Layer { }) } - render(context: CanvasRenderingContext2D) { + renderLayer(context: CanvasRenderingContext2D) { this.renderTerritory() this.context.putImageData(this.imageData, 0, 0); context.drawImage( diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 34d49234c..7543c6168 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -30,7 +30,7 @@ export class UILayer implements Layer { } - render(context: CanvasRenderingContext2D) { + renderLayer(context: CanvasRenderingContext2D) { if (!this.game.inSpawnPhase()) { return } diff --git a/src/client/graphics/layers/radial/EmojiTable.ts b/src/client/graphics/layers/radial/EmojiTable.ts new file mode 100644 index 000000000..a689be1b3 --- /dev/null +++ b/src/client/graphics/layers/radial/EmojiTable.ts @@ -0,0 +1,107 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, state} from 'lit/decorators.js'; + +const emojiTable: string[][] = [ + ["đ", "đą", "đ¤Š", "đ¯", "đĨē"], + ["đĒĻ", "đ", "đĨ", "đĨ", "đĨ"], + ["đ¤", "đĨ°", "đ", "đ", "đĨ"], + ["đĒ", "đĨŗ", "đ", "đ", "đ¤Ļââī¸"], + ["đ", "đ", "đ", "đĨą", "đ"], + ["â¤ī¸", "đ°", "đ¤", "đĄī¸", "đĨ"], + ["đ", "đī¸", "âĄī¸", "âŦ ī¸", "âī¸"], + ["âī¸", "âī¸", "âŦī¸", "âī¸", "âŦī¸"] +]; + +@customElement('emoji-table') +export class EmojiTable extends LitElement { + static styles = css` + :host { + display: block; + } + .emoji-table { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; + background-color: #1E1E1E; + padding: 15px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); + border-radius: 10px; + display: flex; + flex-direction: column; + align-items: center; + max-width: 95vw; + max-height: 95vh; + overflow-y: auto; + } + .emoji-row { + display: flex; + justify-content: center; + width: 100%; + } + .emoji-button { + font-size: 60px; + width: 80px; + height: 80px; + border: 1px solid #333; + background-color: #2C2C2C; + color: white; + border-radius: 12px; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + justify-content: center; + align-items: center; + margin: 8px; + } + .emoji-button:hover { + background-color: #3A3A3A; + transform: scale(1.1); + } + .emoji-button:active { + background-color: #4A4A4A; + transform: scale(0.95); + } + .hidden { + display: none !important; + } + `; + + @state() + private _hidden = true; + + public onEmojiClicked: (emoji: string) => void = () => { } + + render() { + return html` +