diff --git a/frontend/src/pages/pong/pong.ts b/frontend/src/pages/pong/pong.ts
index ea7dd2e..6380161 100644
--- a/frontend/src/pages/pong/pong.ts
+++ b/frontend/src/pages/pong/pong.ts
@@ -1,12 +1,10 @@
-import { addRoute, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing";
+import { addRoute, navigateTo, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing";
import authHtml from './pong.html?raw';
import io from 'socket.io-client';
import type { CSocket, GameMove, GameUpdate } from "./socket";
import { showError, showInfo } from "@app/toast";
-
-// TODO: local game (2player -> server -> 2player : current setup)
-// TODO: tournament via remote (dedicated queu? idk)
-//
+import { getUser } from "@app/auth";
+import { isNullish } from "@app/utils";
// get the name of the machine used to connect
declare module 'ft_state' {
@@ -15,8 +13,15 @@ declare module 'ft_state' {
}
}
+ // GameRdyDown = "Ready Up?"
+ // GameRdyUp = "Ready down?"
+enum QueueState {
+ InQueu = "In Queue",
+ InGame = "In Game",
+ Iddle = "Queue Up",
+};
+
document.addEventListener("ft:pageChange", (newUrl) => {
- // we are still on a pong page => keep the socket around !
if (newUrl.detail.startsWith('/app/pong') || newUrl.detail.startsWith('/pong')) return;
if (window.__state.pongSock !== undefined) window.__state.pongSock.close();
window.__state.pongSock = undefined;
@@ -30,73 +35,83 @@ export function getSocket(): CSocket {
function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn {
setTitle('Pong Game Page');
+ // MAYBE: "queue up" btn : adds timer to page for duration of queue
+ // TODO: "local play" btn : emit "local new game" evt to server; play game on single computer (maybe need to change keys-handling logic)
+
return {
-
html: authHtml, postInsert: async (app) => {
- const checkbox = document.getElementById("modeToggle") as HTMLInputElement;
- const label = document.getElementById("toggleLabel") as HTMLSpanElement;
- const track = document.getElementById("toggleTrack") as HTMLDivElement;
- const knob = document.getElementById("toggleKnob") as HTMLSpanElement;
-
- checkbox.addEventListener("change", () => { // web vs local
- if (checkbox.checked) {
- label.textContent = "Web";
- track.classList.replace("bg-gray-300", "bg-blue-600");
- knob.classList.add("translate-x-7");
- } else {
- label.textContent = "Local";
- track.classList.replace("bg-blue-600", "bg-gray-300");
- knob.classList.remove("translate-x-7");
- }
- });
-
-
+ const DEFAULT_COLOR = "white";
+ const SELF_COLOR = "red";
+ const user = getUser();
+ let currentGame: GameUpdate | null = null;
const batLeft = document.querySelector
("#batleft");
const batRight = document.querySelector("#batright");
const ball = document.querySelector("#ball");
const score = document.querySelector("#score-board");
- if (!batLeft || !batRight || !ball || !score)
- return showError('fatal error');
+ const playerL = document.querySelector('#player-left');
+ const playerR = document.querySelector('#player-right');
+ const queueBtn = document.querySelector("#QueueBtn");
let socket = getSocket();
- let currentGame: GameUpdate | null = null;
+ if (isNullish(user)) { // if no user (no loggin / other) : GTFO
+ navigateTo("/app");
+ return ;
+ }
+ if (!batLeft || !batRight || !ball || !score || !queueBtn || !playerL || !playerR) // sanity check
+ return showError('fatal error');
+
+ // ---
// keys handler
+ // ---
const keys: Record = {};
- document.addEventListener("keydown", (e) => {
- keys[e.key.toLowerCase()] = true;
- });
- document.addEventListener("keyup", (e) => {
- keys[e.key.toLowerCase()] = false;
- });
+ document.addEventListener("keydown", (e) => {keys[e.key.toLowerCase()] = true;});
+ document.addEventListener("keyup", (e) => {keys[e.key.toLowerCase()] = false;});
setInterval(() => { // key sender
+ if (queueBtn.innerText !== QueueState.InGame)//we're in game ? continue | gtfo
+ return ;
if (currentGame === null) return;
+
let packet: GameMove = {
move: null,
moveRight: null,
}
- if ((keys['w'] !== keys['s'])) {
- packet.move = keys['w'] ? 'up' : 'down';
- }
- if (currentGame.local && (keys['o'] !== keys['l'])) {
- packet.moveRight = keys['o'] ? 'up' : 'down';
- }
+ if (queueBtn.innerText !== QueueState.InGame)//we're in game ? continue | gtfo
+ return ;
+ if (currentGame === null) return;
+
+ if ((keys['w'] !== keys['s']))
+ packet.move = keys['w'] ? 'up' : 'down';
+ if (currentGame.local && (keys['o'] !== keys['l']))
+ packet.moveRight = keys['o'] ? 'up' : 'down';
socket.emit('gameMove', packet);
}, 1000 / 60);
+ // ---
+ // keys end
+ // ---
+
+ // ---
+ // position logic (client)
+ // ---
+ const DEFAULT_POSITIONS : GameUpdate = {
+ gameId:"",
+ ball:{size:16, x:800/2, y:450/2},
+ left:{id:"", paddle:{x:40, y:185, width:12, height:80}, score:0},
+ right:{id:"", paddle:{x:748, y:185, width:12, height:80}, score:0},
+ local:false
+ };
const render = (state: GameUpdate) => {
currentGame = state;
- //batLeft.style.transform = `translateY(${state.left.paddle.y}px) translateX(${state.left.paddle.x}px)`;
batLeft.style.top = `${state.left.paddle.y}px`;
batLeft.style.left = `${state.left.paddle.x}px`;
batLeft.style.width = `${state.left.paddle.width}px`;
batLeft.style.height = `${state.left.paddle.height}px`;
- //batRight.style.transform = `translateY(${state.right.paddle.y}px) translateX(-${state.left.paddle.x}px)`;
batRight.style.top = `${state.right.paddle.y}px`;
batRight.style.left = `${state.right.paddle.x}px`;
batRight.style.width = `${state.right.paddle.width}px`;
@@ -106,19 +121,71 @@ function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn
ball.style.height = `${state.ball.size * 2}px`;
ball.style.width = `${state.ball.size * 2}px`;
-
score.innerText = `${state.left.score} | ${state.right.score}`
}
-
- socket.on('gameEnd', () => currentGame = null);
-
socket.on('gameUpdate', (state: GameUpdate) => render(state));
- socket.on('newGame', (state: GameUpdate) => render(state));
+ // ---
+ // position logic (client) end
+ // ---
- socket.on('updateInformation', (e) => showInfo(`UpdateInformation: t=${e.totalUser};q=${e.inQueue}`));
- socket.on('queueEvent', (e) => showInfo(`QueueEvent: ${e}`));
- //socket.emit('enqueue');
- socket.emit('localGame');
+ // ---
+ // queue evt
+ // ---
+ function set_pretty(batU : HTMLDivElement, txtU : HTMLDivElement, txtO : HTMLDivElement, colorYou : string) {
+ batU.style.backgroundColor = colorYou;
+ txtU.style.color = colorYou;
+ txtU.innerText = "you";
+ txtO.innerHTML = "The Mechant";
+ }
+ queueBtn.addEventListener("click", ()=>{
+ if (queueBtn.innerText !== QueueState.Iddle) {
+ if (queueBtn.innerText === QueueState.InQueu) {
+ socket.emit("dequeue");
+ queueBtn.innerText = QueueState.Iddle;
+ }
+ return ;
+ }
+ queueBtn.innerText = QueueState.InQueu;
+ socket.emit('enqueue');
+ });
+ socket.on('newGame', (state) => {
+ render(state);
+ queueBtn.innerText = QueueState.InGame;
+ queueBtn.style.color = 'red';
+ batLeft.style.backgroundColor = DEFAULT_COLOR;
+ batRight.style.backgroundColor = DEFAULT_COLOR;
+ if (state.left.id === user.id) {
+ set_pretty(batLeft, playerL, playerR, SELF_COLOR);
+ } else if (state.right.id === user.id) {
+ set_pretty(batRight, playerR, playerL, SELF_COLOR);
+ } else
+ showError("couldn't find your id in game");
+ }); // TODO: notif user of new game w "ready up" btn
+
+ socket.on("gameEnd", () => {
+ queueBtn.innerHTML = QueueState.Iddle;
+ queueBtn.style.color = 'white';
+ render(DEFAULT_POSITIONS);
+ batLeft.style.backgroundColor = DEFAULT_COLOR;
+ batRight.style.backgroundColor = DEFAULT_COLOR;
+ playerR.style.color = "";
+ playerL.style.color = "";
+ playerR.innerText = "";
+ playerL.innerText = "";
+ currentGame = null;
+ })
+ // ---
+ // queue evt end
+ // ---
+ render(DEFAULT_POSITIONS);
+ batLeft.style.backgroundColor = DEFAULT_COLOR;
+ batRight.style.backgroundColor = DEFAULT_COLOR;
+
+ socket.on('updateInformation', (e) => showInfo(`UpdateInformation: t=${e.totalUser};q=${e.inQueue}`)); // queue info TODO: delete for final product
+ socket.on('queueEvent', (e) => showInfo(`QueueEvent: ${e}`)); // queue evt can be left in product
+ showInfo("butter");
+ showInfo("butter-toast");
+ // socket.emit('localGame');
}
}
};
diff --git a/frontend/src/pages/pong/socket.ts b/frontend/src/pages/pong/socket.ts
index bf6b971..064e7f4 100644
--- a/frontend/src/pages/pong/socket.ts
+++ b/frontend/src/pages/pong/socket.ts
@@ -29,6 +29,7 @@ export type GameMove = {
moveRight: 'up' | 'down' | null,
}
+// TODO: add new evt such as "local play", "ready-up" see: ./pong.ts
export interface ClientToServer {
enqueue: () => void;
dequeue: () => void;
diff --git a/src/pong/src/game.ts b/src/pong/src/game.ts
index abb8e6b..6acfdb3 100644
--- a/src/pong/src/game.ts
+++ b/src/pong/src/game.ts
@@ -26,9 +26,9 @@ export class Paddle {
}
class Ball {
- public static readonly DEFAULT_SPEED = 1;
+ public static readonly DEFAULT_SPEED = 3;
public static readonly DEFAULT_SIZE = 16;
- public static readonly DEFAULT_MAX_SPEED = 30;
+ public static readonly DEFAULT_MAX_SPEED = 15;
public static readonly DEFAULT_MIN_SPEED = Ball.DEFAULT_SPEED;
public static readonly DEFAULT_ACCEL_FACTOR = 1.2;
@@ -49,6 +49,7 @@ class Ball {
public collided(
side: 'left' | 'right' | 'top' | 'bottom',
walls: { [k in typeof side]: number },
+ snap: boolean = true,
) {
// this.speed *= this.accel_factor;
this.speed = Math.max(
@@ -65,9 +66,11 @@ class Ball {
this.angle = -this.angle + Math.PI;
c = 'x';
}
- this[c] =
- walls[side] +
- this.size * (side === 'right' || side === 'bottom' ? -1 : 1);
+ if (snap){
+ this[c] =
+ walls[side] +
+ this.size * (side === 'right' || side === 'bottom' ? -1 : 1);
+ }
while (this.angle >= Math.PI) {
this.angle -= 2 * Math.PI;
@@ -139,8 +142,8 @@ export class Pong {
left: this.leftPaddle.x + this.leftPaddle.width,
right: 0,
top: 0,
- bottom: 0,
- });
+ bottom: 0}, false
+ );
return;
}
if (this.paddleCollision(this.rightPaddle, 'right')) {
@@ -148,8 +151,7 @@ export class Pong {
right: this.rightPaddle.x,
left: 0,
top: 0,
- bottom: 0,
- });
+ bottom: 0}, false);
return;
}
const wallCollision = this.boxCollision();
@@ -184,6 +186,10 @@ export class Pong {
}
private paddleCollision(paddle: Paddle, side: 'left' | 'right'): boolean {
+ if (Math.abs(this.ball.angle) > Math.PI / 2 && side !== 'left' ||
+ Math.abs(this.ball.angle) < Math.PI / 2 && side !== 'right')
+ return (false)
+
// now we check only if the ball is near enought in the y axis to permform the collision
if (!(
// check if ball is bellow the top of the paddle
@@ -194,7 +200,6 @@ export class Pong {
// so we know that the y is close enougth to be a bit, so we check the X. are we closer than the ball size ? if yes -> hit
if (
// check if the paddle.x is at most ball.size away from the center of the ball => we have a hit houston
- // call he pentagon, 9 11
Math.abs(
paddle.x + paddle.width * (side === 'left' ? 1 : 0)
- this.ball.x)