mirror of
https://github.com/danbulant/slightlyComplicatedTicTacToe
synced 2026-07-06 19:50:38 +00:00
overlay + test game
This commit is contained in:
parent
2cac7aea5f
commit
7240f0a539
9 changed files with 171 additions and 18 deletions
4
client/src/hooks.js
Normal file
4
client/src/hooks.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// @ts-ignore
|
||||||
|
export async function handle({ event, resolve }) {
|
||||||
|
return resolve(event, { ssr: false });
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,8 @@ class ConnectedClient extends EventTarget {
|
||||||
readyState: number = 0;
|
readyState: number = 0;
|
||||||
pings: number[] = [];
|
pings: number[] = [];
|
||||||
score: number = 0;
|
score: number = 0;
|
||||||
|
lives: number = 3;
|
||||||
|
lastScoreChange: number = 0;
|
||||||
|
|
||||||
constructor(public ws: WebsocketConnection, public name: string) {
|
constructor(public ws: WebsocketConnection, public name: string) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -84,8 +86,17 @@ class ConnectedClient extends EventTarget {
|
||||||
this.dispatchEvent(new FastEvent("message", msg.d));
|
this.dispatchEvent(new FastEvent("message", msg.d));
|
||||||
messages.update(t => { t.push({ author: this.name, content: msg.d });return t})
|
messages.update(t => { t.push({ author: this.name, content: msg.d });return t})
|
||||||
break;
|
break;
|
||||||
|
case "lives":
|
||||||
|
this.lives = msg.d;
|
||||||
|
players.update(t => t);
|
||||||
|
break;
|
||||||
|
case "score":
|
||||||
|
this.score = msg.d;
|
||||||
|
this.lastScoreChange = Date.now();
|
||||||
|
players.update(t => t);
|
||||||
|
break;
|
||||||
case "start":
|
case "start":
|
||||||
gameData.set({ score: 0 });
|
gameData.set({ score: 0, lives: 3, lastScoreChange: 0 });
|
||||||
// break not on purpose
|
// break not on purpose
|
||||||
default:
|
default:
|
||||||
console.log("MSG", msg);
|
console.log("MSG", msg);
|
||||||
|
|
@ -236,6 +247,9 @@ export class WebsocketConnection extends EventTarget {
|
||||||
this.fast.delete(msg.client);
|
this.fast.delete(msg.client);
|
||||||
players.set(this.fast);
|
players.set(this.fast);
|
||||||
messages.update(t => { t.push({ author: " SYS ", content: `${msg.client} left`});return t})
|
messages.update(t => { t.push({ author: " SYS ", content: `${msg.client} left`});return t})
|
||||||
|
if(this.fast.size == 0) {
|
||||||
|
gameData.set(null);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "host": {
|
case "host": {
|
||||||
|
|
@ -280,6 +294,17 @@ export class WebsocketConnection extends EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setScore(score: number) {
|
||||||
|
if (!this.roomName) return console.log("Not in a room");
|
||||||
|
this.broadcast({ t: "score", d: score });
|
||||||
|
gameData.update(t => { t!.score = score; t!.lastScoreChange = Date.now(); return t});
|
||||||
|
}
|
||||||
|
setLives(lives: number) {
|
||||||
|
if (!this.roomName) return console.log("Not in a room");
|
||||||
|
this.broadcast({ t: "lives", d: lives });
|
||||||
|
gameData.update(t => { t!.lives = lives; return t});
|
||||||
|
}
|
||||||
|
|
||||||
createGame(name: string) {
|
createGame(name: string) {
|
||||||
this.ws.send(JSON.stringify({ t: "create", name: name }));
|
this.ws.send(JSON.stringify({ t: "create", name: name }));
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +315,7 @@ export class WebsocketConnection extends EventTarget {
|
||||||
for(const [, client] of this.fast) {
|
for(const [, client] of this.fast) {
|
||||||
client.score = 0;
|
client.score = 0;
|
||||||
}
|
}
|
||||||
|
gameData.set({ score: 0, lives: 3, lastScoreChange: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
join(name: string) {
|
join(name: string) {
|
||||||
|
|
@ -313,4 +339,4 @@ export const lastError: Writable<string> = writable("");
|
||||||
export const room: Writable<{ name: string, host: string } | null> = writable(null);
|
export const room: Writable<{ name: string, host: string } | null> = writable(null);
|
||||||
export const players: Writable<Map<string, ConnectedClient>> = writable(new Map);
|
export const players: Writable<Map<string, ConnectedClient>> = writable(new Map);
|
||||||
export const messages: Writable<{ author: string, content: string }[]> = writable([]);
|
export const messages: Writable<{ author: string, content: string }[]> = writable([]);
|
||||||
export const gameData: Writable<{ score: number }|null> = writable(null);
|
export const gameData: Writable<{ score: number, lives: number, lastScoreChange: number }|null> = writable(null);
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
transform: rotate3d(1, 0, 0, 0deg);
|
transform: rotate3d(1, 0, 0, 0deg);
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
animation: dice 1.8s 0.7s ease-in-out;
|
animation: dice 1.2s 0.5s ease-in-out;
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
}
|
}
|
||||||
@keyframes dice {
|
@keyframes dice {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,95 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { connection, gameData, players } from "$lib/Websocket";
|
||||||
|
import { flip } from "svelte/animate";
|
||||||
|
|
||||||
<slot />
|
let scoreboard: { name: string, score: number, lives: number, lastScoreChange: number }[] = [];
|
||||||
|
$: {
|
||||||
|
scoreboard = [...$players.values(), { name: $connection!.name, score: $gameData!.score, lives: $gameData!.lives, lastScoreChange: $gameData!.lastScoreChange }].sort((a, b) => (b.score - a.score) || (b.lives - a.lives) || (a.lastScoreChange - b.lastScoreChange) || a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="left">
|
||||||
|
<div class="hearts">
|
||||||
|
{#each [...Array($gameData?.lives).keys()] as i}
|
||||||
|
<img src="/assets/heart.png" alt="">
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{#each scoreboard as player (player.name)}
|
||||||
|
<li animate:flip class:dead-player={!player.lives}>
|
||||||
|
{player.name}
|
||||||
|
<span class="score">{player.score}</span>
|
||||||
|
{#if !player.lives}
|
||||||
|
<div class="dead">DEAD</div>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
<div class="right">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.hearts img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
.hearts {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
background-color: #85e65c;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
max-width: calc(100vw - 310px);
|
||||||
|
border-left: 10px solid #bd5ce6;
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 300px;
|
||||||
|
height: calc(100% - 10px);
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
li.dead-player {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
.dead {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
.score {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #bd5ce6;
|
||||||
|
color: #85e65c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -4,22 +4,19 @@ import { GameScene } from "./scene";
|
||||||
var ratio = window.devicePixelRatio || 1;
|
var ratio = window.devicePixelRatio || 1;
|
||||||
export function resize() {
|
export function resize() {
|
||||||
if(!game || !htmlcanvas) return;
|
if(!game || !htmlcanvas) return;
|
||||||
try {
|
// try {
|
||||||
game.scale.resize(htmlcanvas.parentElement!.clientWidth * ratio, htmlcanvas.parentElement!.clientHeight * ratio);
|
// game.scale.resize(htmlcanvas.parentElement!.clientWidth * ratio, htmlcanvas.parentElement!.clientHeight * ratio);
|
||||||
} catch(e) {
|
// } catch(e) {
|
||||||
// @ts-ignore
|
// // @ts-ignore
|
||||||
console.error(e, new ErrorEvent(e.type, { colno: e.colno, error: e, lineno: e.lineno, message: e.message, filename: e.filename }));
|
// console.error(e, new ErrorEvent(e.type, { colno: e.colno, error: e, lineno: e.lineno, message: e.message, filename: e.filename }));
|
||||||
window.dispatchEvent(new ErrorEvent("error", e as any));
|
// window.dispatchEvent(new ErrorEvent("error", e as any));
|
||||||
}
|
// }
|
||||||
// console.log("size", htmlcanvas.parentElement!.clientWidth * ratio, htmlcanvas.parentElement!.clientHeight * ratio);
|
// console.log("size", htmlcanvas.parentElement!.clientWidth * ratio, htmlcanvas.parentElement!.clientHeight * ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {HTMLCanvasElement} */
|
|
||||||
var htmlcanvas: HTMLCanvasElement;
|
var htmlcanvas: HTMLCanvasElement;
|
||||||
/** @type {Game} */
|
|
||||||
var game: Game;
|
var game: Game;
|
||||||
/** @type {GameScene} */
|
var gs: GameScene | null = null;
|
||||||
var gs: GameScene;
|
|
||||||
export function setCanvas(canvas: HTMLCanvasElement) {
|
export function setCanvas(canvas: HTMLCanvasElement) {
|
||||||
htmlcanvas = canvas;
|
htmlcanvas = canvas;
|
||||||
var ctx = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
var ctx = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
||||||
|
|
@ -43,7 +40,12 @@ export function setCanvas(canvas: HTMLCanvasElement) {
|
||||||
title: "Multidie",
|
title: "Multidie",
|
||||||
version: "0",
|
version: "0",
|
||||||
scene: [gs],
|
scene: [gs],
|
||||||
backgroundColor: "#01021B",
|
backgroundColor: "#85e65c",
|
||||||
banner: false
|
banner: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stop() {
|
||||||
|
game.destroy(false);
|
||||||
|
gs = null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { connection, gameData } from "$lib/Websocket";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { setCanvas, resize, stop } from "./init";
|
||||||
|
|
||||||
|
var canvas: HTMLCanvasElement;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
console.log("Started");
|
||||||
|
setCanvas(canvas);
|
||||||
|
return () => {
|
||||||
|
console.log("Stopped");
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function increaseScore() {
|
||||||
|
$connection!.setScore($gameData!.score + 1);
|
||||||
|
}
|
||||||
|
function decreaseLives() {
|
||||||
|
$connection!.setLives($gameData!.lives - 1);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:resize={resize} />
|
||||||
|
|
||||||
|
|
||||||
|
<canvas bind:this={canvas} on:click={increaseScore} on:contextmenu={decreaseLives} />
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
let i = setTimeout(() => {
|
let i = setTimeout(() => {
|
||||||
visible = false;
|
visible = false;
|
||||||
}, 3000);
|
}, 2000);
|
||||||
return () => clearTimeout(i);
|
return () => clearTimeout(i);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import Button from '../components/button.svelte';
|
import Button from '../components/button.svelte';
|
||||||
|
|
||||||
function startGame() {
|
function startGame() {
|
||||||
|
$connection!.startGame();
|
||||||
}
|
}
|
||||||
let content = "";
|
let content = "";
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
|
|
|
||||||
BIN
client/static/assets/heart.png
Normal file
BIN
client/static/assets/heart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
Loading…
Reference in a new issue