diff --git a/.gitignore b/.gitignore index b58afaf9..ad799a41 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,9 @@ Thumbs.db *.log .env -jsconfig.json node_modules -build +*.js +www/**/*.html large old diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..347bf03f --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Node", + "target": "ES2020", + "strictNullChecks": true, + "strictFunctionTypes": true + }, + "exclude": [ + "node_modules", + "**/node_modules/*" + ] +} \ No newline at end of file diff --git a/make.sh b/make.sh new file mode 100644 index 00000000..f4ad7416 --- /dev/null +++ b/make.sh @@ -0,0 +1,4 @@ +#!/bin/bash +npx tsc +mkdir -p www +cp ./src/client/*.js ./src/client/*.html ./www/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3ea0eee0..f8a273e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,16 @@ "packages": { "": { "devDependencies": { + "@types/node": "^18.11.5", "typescript": "^4.8.4" } }, + "node_modules/@types/node": { + "version": "18.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz", + "integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ==", + "dev": true + }, "node_modules/typescript": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", @@ -23,6 +30,12 @@ } }, "dependencies": { + "@types/node": { + "version": "18.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz", + "integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ==", + "dev": true + }, "typescript": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", diff --git a/package.json b/package.json index 425985bb..2d65b5c7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,7 @@ { + "type": "module", "devDependencies": { + "@types/node": "^18.11.5", "typescript": "^4.8.4" } } diff --git a/src/client/class.ts b/src/client/class.ts new file mode 100644 index 00000000..929f293c --- /dev/null +++ b/src/client/class.ts @@ -0,0 +1,267 @@ + +// type Vector = { +// x: number; +// y: number; +// } + +class Vector { + x: number; + y: number; + constructor(x: number = 0, y: number = 0) { + this.x = x; + this.y = y; + } + assign(x: number, y: number) { + this.x = x; + this.y = y; + } +} + +class VectorInteger extends Vector { + // PLACEHOLDER + // VectorInteger with set/get dont work (No draw on the screen). Why ? +} + +/* +class VectorInteger { + // private _x: number = 0; + // private _y: number = 0; + // constructor(x: number = 0, y: number = 0) { + // this._x = x; + // this._y = y; + // } + // get x(): number { + // return this._x; + // } + // set x(v: number) { + // // this._x = Math.floor(v); + // this._x = v; + // } + // get y(): number { + // return this._y; + // } + // set y(v: number) { + // // this._y = Math.floor(v); + // this._y = v; + // } +} +*/ + +interface Component { + pos: VectorInteger; + color: string; + ctx: CanvasRenderingContext2D; // TODO: reference in place of global 'pong.ctx' call + update(): void; + clear(): void; +} + + +class Rectangle implements Component { + ctx: CanvasRenderingContext2D; + pos: VectorInteger; + color: string; + width: number; + height: number; + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number) { + this.ctx = ctx; + this.pos = Object.assign({}, pos); + this.color = color; + this.width = width; + this.height = height; + } + update() { + this.ctx.fillStyle = this.color; + this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height); + } + clear(pos?: VectorInteger) { + if (pos) + this.ctx.clearRect(pos.x, pos.y, this.width, this.height); + else + this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height); + } + collision(collider: Rectangle): boolean { // Collision WIP. To redo + var myleft = this.pos.x; + var myright = this.pos.x + (this.width); + var mytop = this.pos.y; + var mybottom = this.pos.y + (this.height); + var otherleft = collider.pos.x; + var otherright = collider.pos.x + (collider.width); + var othertop = collider.pos.y; + var otherbottom = collider.pos.y + (collider.height); + if ((mybottom < othertop) + || (mytop > otherbottom) + || (myright < otherleft) + || (myleft > otherright)) { + return false; + } + else + return true; + } +} + + +interface Moving { + dir: Vector; + speed: number; + move(): void; +} + + +class MovingRectangle extends Rectangle implements Moving { + dir: Vector = new Vector(0,0); + speed: number; + readonly baseSpeed: number; + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) { + super(ctx, pos, color, width, height); + this.baseSpeed = baseSpeed; + this.speed = baseSpeed; + } + move() { // Math.floor WIP until VectorInteger debug + this.pos.x += Math.floor(this.dir.x * this.speed); + this.pos.y += Math.floor(this.dir.y * this.speed); + } + moveAndCollide(colliderArr: Rectangle[]) { + let oldPos = Object.assign({}, this.pos); + this.move(); + if (colliderArr.some(this.collision, this)) + { + this.pos.x = oldPos.x; + this.pos.y = oldPos.y; + } + else + { + this.clear(oldPos); + this.update(); + } + } +} + + +class Player extends MovingRectangle { + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) { + super(ctx, pos, color, width, height, baseSpeed); + } +} + + +class Ball extends MovingRectangle { + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, baseSpeed: number) { + super(ctx, pos, color, size, size, baseSpeed); + } + bounce(collider?: Rectangle) { + /* Could be more generic, but testing only player is enough, + because in Pong collider can only be Player or Wall. */ + if (collider instanceof Player) { + this._bouncePlayer(collider); + } + else { + this._bounceWall(); + } + } + moveAndBounce(colliderArr: Rectangle[]) { + let oldPos = Object.assign({}, this.pos); + this.move(); + let i = colliderArr.findIndex(this.collision, this); + if (i != -1) + { + this.bounce(colliderArr[i]); + this.move(); + } + this.clear(oldPos); + this.update(); + } + private _bounceWall() { // Should be enough for Wall + this.dir.y = this.dir.y * -1; + } + private _bouncePlayer(collider: Player) { // WIP + // Bounce for Player need to be more complexe than this + this.speed += this.baseSpeed/20; + this.dir.x = this.dir.x * -1; + } +} + +// conflict with Text +class TextElem implements Component { + ctx: CanvasRenderingContext2D; + pos: VectorInteger; + color: string; + size: number; + font: string; + text: string = ""; + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, font: string = "Bit5x3") { + this.ctx = ctx; + this.pos = Object.assign({}, pos); + this.color = color; + this.size = size; + this.font = font; + } + update() { + this.ctx.font = this.size + "px" + " " + this.font; + this.ctx.fillStyle = this.color; + this.ctx.fillText(this.text, this.pos.x, this.pos.y); + } + clear() { + // clear no very accurate for Text + // https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics + let textMetric = this.ctx.measureText(this.text); + // console.log("textMetric.width = "+textMetric.width); + // console.log("size = "+this.size); + // console.log("x = "+this.pos.x); + // console.log("y = "+this.pos.y); + this.ctx.clearRect(this.pos.x - 1, this.pos.y-this.size + 1, textMetric.width, this.size); + // +1 and -1 because float imprecision (and Math.floor() with VectorInteger dont work for the moment) + // (or maybe its textMetric imprecision ?) + } +} + +class TextNumericValue extends TextElem { + private _value: number = 0; + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, font?: string) { + super(ctx, pos, color, size, font); + } + get value() { + return this._value; + } + set value(v: number) { + this._value = v; + this.text = v.toString(); + } +} + + +class Line extends Rectangle { + gapeCount: number = 0; + segmentCount: number; + segmentWidth: number; + segmentHeight: number; + constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, gapeCount?: number) { + super(ctx, pos, color, width, height); + if (gapeCount) + this.gapeCount = gapeCount; + this.segmentCount = this.gapeCount * 2 + 1; + + this.segmentWidth = this.width; + this.segmentHeight = this.height / this.segmentCount; + + // for Horizontal Line + // this.segmentWidth = this.width / this.segmentCount; + // this.segmentHeight = this.height; + } + update() { + this.ctx.fillStyle = this.color; + let pos: VectorInteger = new VectorInteger; + let i = 0; + while (i < this.segmentCount) + { + // for Horizontal Line + // pos.y = this.pos.y; + // pos.x = this.pos.x + this.segmentWidth * i; + pos.x = this.pos.x; + pos.y = this.pos.y + this.segmentHeight * i; + this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight); + i += 2; + } + } +} + +export {Vector, VectorInteger, Rectangle, MovingRectangle, Player, Ball, TextElem, TextNumericValue, Line} diff --git a/src/pong.html b/src/client/pong.html similarity index 81% rename from src/pong.html rename to src/client/pong.html index 4f641946..22e61091 100644 --- a/src/pong.html +++ b/src/client/pong.html @@ -17,7 +17,7 @@ } #canvas-container { text-align: center; - border: dashed red 5px; + /* border: dashed red 5px; */ /* max-height: 80vh; */ /* overflow: hidden; */ } @@ -29,13 +29,12 @@ } - +
- - - + + diff --git a/src/client/pong.ts b/src/client/pong.ts new file mode 100644 index 00000000..b35208b5 --- /dev/null +++ b/src/client/pong.ts @@ -0,0 +1,277 @@ + +/* Keys + Player 1: W/S + Player 2: Up/Down + Grid On-Off: G +*/ + +import {Vector, VectorInteger, Rectangle, MovingRectangle, Player, Ball, TextElem, TextNumericValue, Line} from "./class.js"; +// @ts-check + +let gridDisplay = false; +let ballInPlay = true; + +let pong: gameArea; + +let wall_top: Rectangle; +let wall_bottom: Rectangle; +let player1: Player; +let player2: Player; +let ball: Ball; +let score1: TextNumericValue; +let score2: TextNumericValue; +let midLine: Line; + +let w_grid_mid: Rectangle; +let w_grid_u1: Rectangle; +let w_grid_d1: Rectangle; +let h_grid_mid: Rectangle; +let h_grid_u1: Rectangle; +let h_grid_d1: Rectangle; + +function startGame() +{ + pong = new gameArea(); + + // Const + const w = pong.canvas.width; + const h = pong.canvas.height; + const w_mid = Math.floor(w/2); + const h_mid = Math.floor(h/2); + const pw = Math.floor(w/50); + const ph = pw*5; + const ballSize = pw; + const scoreSize = Math.floor(w/16); + const midLineSize = Math.floor(w/150); + const wallSize = Math.floor(w/100); + const gridSize = Math.floor(w/500); + const playerSpeed = Math.floor(w/75); + const ballSpeed = Math.floor(w/75); + + let pos = new VectorInteger; + // Component + pos.assign(0, 0); + wall_top = new Rectangle(pong.ctx, pos, "grey", w, wallSize); + pos.assign(0, h-wallSize); + wall_bottom = new Rectangle(pong.ctx, pos, "grey", w, wallSize); + + pos.assign(0+pw, h_mid-ph/2); + player1 = new Player(pong.ctx, pos, "white", pw, ph, playerSpeed); + pos.assign(w-pw-pw, h_mid-ph/2); + player2 = new Player(pong.ctx, pos, "white", pw, ph, playerSpeed); + + pos.assign(w_mid-ballSize/2, h_mid-ballSize/2); + ball = new Ball(pong.ctx, pos, "white", ballSize, ballSpeed); + ball.dir.assign(-0.8, +0.2); + + pos.assign(w_mid-scoreSize*1.6, scoreSize*1.5); + score1 = new TextNumericValue(pong.ctx, pos, "white", scoreSize); + pos.assign(w_mid+scoreSize*1.1, scoreSize*1.5); + score2 = new TextNumericValue(pong.ctx, pos, "white", scoreSize); + score1.value = 0; + score2.value = 0; + + pos.assign(w_mid-midLineSize/2, 0+wallSize); + midLine = new Line(pong.ctx, pos, "white", midLineSize, h-wallSize*2, 15); + + // Grid + pos.assign(0, h_mid); + w_grid_mid = new Rectangle(pong.ctx, pos, "darkgreen", w, gridSize); + pos.assign(0, h/4); + w_grid_u1 = new Rectangle(pong.ctx, pos, "darkgreen", w, gridSize); + pos.assign(0, h-h/4); + w_grid_d1 = new Rectangle(pong.ctx, pos, "darkgreen", w, gridSize); + pos.assign(w_mid, 0); + h_grid_mid = new Rectangle(pong.ctx, pos, "darkgreen", gridSize, h); + pos.assign(w/4, 0); + h_grid_u1 = new Rectangle(pong.ctx, pos, "darkgreen", gridSize, h); + pos.assign(w-w/4, 0); + h_grid_d1 = new Rectangle(pong.ctx, pos, "darkgreen", gridSize, h); + + // Start + score1.update(); // first Text draw init the custom font (graphic leftover ortherwise) (a better solution ?) + drawInit(); + pong.start(); +} + +class gameArea { + keys: string[]; + interval: number = 0; + canvas: HTMLCanvasElement; + ctx: CanvasRenderingContext2D; + constructor() { + this.keys = []; + // this.canvas = {}; + this.canvas = document.createElement("canvas"); + // this.ctx = {}; + this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D; + /* ratio 5/3 (1.66) */ + const ratio = 1.66666; + this.canvas.width = 1500; + this.canvas.height = this.canvas.width / ratio; + let container = document.getElementById("canvas-container"); + if (container) + container.insertBefore(this.canvas, container.childNodes[0]); + } + start() { + this.interval = window.setInterval(gameLoop, 20); + window.addEventListener('keydown', function (e) { pong.addKey(e.key); }); + window.addEventListener('keyup', function (e) { pong.deleteKey(e.key); }); + } + addKey(key: string) { + key = key.toLowerCase(); + var i = pong.keys.indexOf(key); + if (i == -1) + pong.keys.push(key); + } + deleteKey(key: string) { + key = key.toLowerCase(); + var i = pong.keys.indexOf(key); + if (i != -1) + pong.keys.splice(i, 1); + } + clear() { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + stop() { + clearInterval(this.interval); + } +} + +function gameLoop() +{ + /* + // I try to clear only what need to be update. + // Will revert to clear() all if not satisfactory. + pong.clear(); + */ + handleInput(); + + if (ballInPlay) + { + ball.moveAndBounce([wall_top, wall_bottom, player1, player2]); + if (ball.pos.x > pong.canvas.width) { + ballInPlay = false; + score1.clear(); + ++score1.value; + setTimeout(newRound, 1500); + } + else if (ball.pos.x < 0 - ball.width) { + ballInPlay = false; + score2.clear(); + ++score2.value; + setTimeout(newRound, 1500); + } + } + + draw(); +} + +function newRound() +{ + // https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches + if (score1.value >= 11 + || score2.value >= 11) + { + if (Math.abs(score1.value - score2.value) >= 2) + { + if (score1.value > score2.value) { + alert("Player 1 WIN"); + } + else { + alert("Player 2 WIN"); + } + return; + } + } + ball.pos.x = pong.canvas.width/2; + ball.pos.y = (pong.canvas.height * 0.1) + Math.floor(random() * (pong.canvas.height * 0.8)); + ball.speed = ball.baseSpeed; + ballInPlay = true; +} + +function random(min: number = 0, max: number = 1) { + return Math.random() * (max - min) + min; +} + +function draw() +{ + if (gridDisplay) { + drawGrid(); + } + midLine.update(); + score1.update(); + score2.update(); +} + +function drawStatic() +{ + wall_top.update(); + wall_bottom.update(); + midLine.update(); +} + +function drawInit() +{ + pong.clear(); + drawStatic(); + player1.update(); + player2.update(); +} + +function drawGrid() +{ + w_grid_mid.update(); + w_grid_u1.update(); + w_grid_d1.update(); + + h_grid_mid.update(); + h_grid_u1.update(); + h_grid_d1.update(); +} + +function handleInput() +{ + var keys = pong.keys; + if (keys.length == 0) + return; + + if (keys.indexOf("g") != -1) + { + if (gridDisplay) + { + pong.clear(); + drawStatic(); + } + gridDisplay = !gridDisplay; + pong.deleteKey("g"); + } + playerMove(keys); +} + +function playerMove(keys: string[]) +{ + player1.dir.y = 0; + if (keys.indexOf("w") != -1) { + player1.dir.y += -1; + } + if (keys.indexOf("s") != -1) { + player1.dir.y += 1; + } + player1.moveAndCollide([wall_top, wall_bottom]); + + player2.dir.y = 0; + if (keys.indexOf("ArrowUp".toLowerCase()) != -1) { + player2.dir.y += -1; + } + if (keys.indexOf("ArrowDown".toLowerCase()) != -1) { + player2.dir.y += 1; + } + player2.moveAndCollide([wall_top, wall_bottom]); +} + + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +startGame(); diff --git a/src/pong.ts b/src/pong.ts deleted file mode 100644 index c7d74175..00000000 --- a/src/pong.ts +++ /dev/null @@ -1,542 +0,0 @@ - -/* Keys - Player 1: W/S - Player 2: Up/Down - Grid On-Off: G -*/ - -// import {component, score, line} from "./class.js"; -// @ts-check - -let gridDisplay = false; -let ballInPlay = true; - -let pong: gameArea; - -let wall_top: Rectangle; -let wall_bottom: Rectangle; -let player1: Player; -let player2: Player; -let ball: Ball; -let score1: TextNumericValue; -let score2: TextNumericValue; -let midLine: Line; - -let w_grid_mid: Rectangle; -let w_grid_u1: Rectangle; -let w_grid_d1: Rectangle; -let h_grid_mid: Rectangle; -let h_grid_u1: Rectangle; -let h_grid_d1: Rectangle; - -function startGame() -{ - pong = new gameArea(); - - // Const - const w = pong.canvas.width; - const h = pong.canvas.height; - const w_mid = Math.floor(w/2); - const h_mid = Math.floor(h/2); - const pw = Math.floor(w/50); - const ph = pw*5; - const ballSize = pw; - const scoreSize = Math.floor(w/16); - const midLineSize = Math.floor(w/150); - const wallSize = Math.floor(w/100); - const gridSize = Math.floor(w/500); - const playerSpeed = Math.floor(w/75); - const ballSpeed = Math.floor(w/75); - - let pos = new VectorInteger; - // Component - pos.assign(0, 0); - wall_top = new Rectangle(pos, "grey", w, wallSize); - pos.assign(0, h-wallSize); - wall_bottom = new Rectangle(pos, "grey", w, wallSize); - - pos.assign(0+pw, h_mid-ph/2); - player1 = new Player(pos, "white", pw, ph, playerSpeed); - pos.assign(w-pw-pw, h_mid-ph/2); - player2 = new Player(pos, "white", pw, ph, playerSpeed); - - pos.assign(w_mid-ballSize/2, h_mid-ballSize/2); - ball = new Ball(pos, "white", ballSize, ballSpeed); - ball.dir.assign(-0.8, +0.2); - - pos.assign(w_mid-scoreSize*1.6, scoreSize*1.5); - score1 = new TextNumericValue(pos, "white", scoreSize); - pos.assign(w_mid+scoreSize*1.1, scoreSize*1.5); - score2 = new TextNumericValue(pos, "white", scoreSize); - score1.value = 0; - score2.value = 0; - - pos.assign(w_mid-midLineSize/2, 0+wallSize); - midLine = new Line(pos, "white", midLineSize, h-wallSize*2, 15); - - // Grid - pos.assign(0, h_mid); - w_grid_mid = new Rectangle(pos, "darkgreen", w, gridSize); - pos.assign(0, h/4); - w_grid_u1 = new Rectangle(pos, "darkgreen", w, gridSize); - pos.assign(0, h-h/4); - w_grid_d1 = new Rectangle(pos, "darkgreen", w, gridSize); - pos.assign(w_mid, 0); - h_grid_mid = new Rectangle(pos, "darkgreen", gridSize, h); - pos.assign(w/4, 0); - h_grid_u1 = new Rectangle(pos, "darkgreen", gridSize, h); - pos.assign(w-w/4, 0); - h_grid_d1 = new Rectangle(pos, "darkgreen", gridSize, h); - - // Start - score1.update(); // first Text draw init the custom font (graphic leftover ortherwise) (a better solution ?) - drawInit(); - pong.start(); -} - -class gameArea { - keys: string[]; - interval: number = 0; - canvas: HTMLCanvasElement; - ctx: CanvasRenderingContext2D; - constructor() { - this.keys = []; - // this.canvas = {}; - this.canvas = document.createElement("canvas"); - // this.ctx = {}; - this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D; - /* ratio 5/3 (1.66) */ - const ratio = 1.66666; - this.canvas.width = 1500; - this.canvas.height = this.canvas.width / ratio; - let container = document.getElementById("canvas-container"); - if (container) - container.insertBefore(this.canvas, container.childNodes[0]); - } - start() { - this.interval = setInterval(gameLoop, 20); - window.addEventListener('keydown', function (e) { pong.addKey(e.key); }); - window.addEventListener('keyup', function (e) { pong.deleteKey(e.key); }); - } - addKey(key: string) { - key = key.toLowerCase(); - var i = pong.keys.indexOf(key); - if (i == -1) - pong.keys.push(key); - } - deleteKey(key: string) { - key = key.toLowerCase(); - var i = pong.keys.indexOf(key); - if (i != -1) - pong.keys.splice(i, 1); - } - clear() { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - stop() { - clearInterval(this.interval); - } -} - -function gameLoop() -{ - /* - // I try to clear only what need to be update. - // Will revert to clear() all if not satisfactory. - pong.clear(); - */ - handleInput(); - - if (ballInPlay) - { - ball.moveAndBounce([wall_top, wall_bottom, player1, player2]); - if (ball.pos.x > pong.canvas.width) { - ballInPlay = false; - score1.clear(); - ++score1.value; - setTimeout(newRound, 1500); - } - else if (ball.pos.x < 0 - ball.width) { - ballInPlay = false; - score2.clear(); - ++score2.value; - setTimeout(newRound, 1500); - } - } - - draw(); -} - -function newRound() -{ - // https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches - if (score1.value >= 11 - || score2.value >= 11) - { - if (Math.abs(score1.value - score2.value) >= 2) - { - if (score1.value > score2.value) { - alert("Player 1 WIN"); - } - else { - alert("Player 2 WIN"); - } - return; - } - } - ball.pos.x = pong.canvas.width/2; - ball.pos.y = pong.canvas.height/4 + Math.floor(random() * pong.canvas.height/2); - ball.speed = ball.baseSpeed; - ballInPlay = true; -} - -function random(min: number = 0, max: number = 1) { - return Math.random() * (max - min) + min; -} - -function draw() -{ - if (gridDisplay) { - drawGrid(); - } - midLine.update(); - score1.update(); - score2.update(); -} - -function drawStatic() -{ - wall_top.update(); - wall_bottom.update(); - midLine.update(); -} - -function drawInit() -{ - pong.clear(); - drawStatic(); - player1.update(); - player2.update(); -} - -function drawGrid() -{ - w_grid_mid.update(); - w_grid_u1.update(); - w_grid_d1.update(); - - h_grid_mid.update(); - h_grid_u1.update(); - h_grid_d1.update(); -} - -function handleInput() -{ - var keys = pong.keys; - if (keys.length == 0) - return; - - if (keys.indexOf("g") != -1) - { - if (gridDisplay) - { - pong.clear(); - drawStatic(); - } - gridDisplay = !gridDisplay; - pong.deleteKey("g"); - } - playerMove(keys); -} - -function playerMove(keys: string[]) -{ - player1.dir.y = 0; - if (keys.indexOf("w") != -1) { - player1.dir.y += -1; - } - if (keys.indexOf("s") != -1) { - player1.dir.y += 1; - } - player1.moveAndCollide([wall_top, wall_bottom]); - - player2.dir.y = 0; - if (keys.indexOf("ArrowUp".toLowerCase()) != -1) { - player2.dir.y += -1; - } - if (keys.indexOf("ArrowDown".toLowerCase()) != -1) { - player2.dir.y += 1; - } - player2.moveAndCollide([wall_top, wall_bottom]); -} - - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// class.js - -// type Vector = { -// x: number; -// y: number; -// } - -class Vector { - x: number; - y: number; - constructor(x: number = 0, y: number = 0) { - this.x = x; - this.y = y; - } - assign(x: number, y: number) { - this.x = x; - this.y = y; - } -} - -class VectorInteger extends Vector { - // PLACEHOLDER - // VectorInteger with set/get dont work (No draw on the screen). Why ? -} - -/* -class VectorInteger { - // private _x: number = 0; - // private _y: number = 0; - // constructor(x: number = 0, y: number = 0) { - // this._x = x; - // this._y = y; - // } - // get x(): number { - // return this._x; - // } - // set x(v: number) { - // // this._x = Math.floor(v); - // this._x = v; - // } - // get y(): number { - // return this._y; - // } - // set y(v: number) { - // // this._y = Math.floor(v); - // this._y = v; - // } -} -*/ - -interface Component { - pos: VectorInteger; - color: string; - // ctx: CanvasRenderingContext2D; // TODO: reference in place of global 'pong.ctx' call - update(): void; - clear(): void; -} - - -class Rectangle implements Component { - pos: VectorInteger; - color: string; - width: number; - height: number; - constructor(pos: VectorInteger, color: string, width: number, height: number) { - this.pos = Object.assign({}, pos); - this.color = color; - this.width = width; - this.height = height; - } - update() { - let ctx = pong.ctx; - ctx.fillStyle = this.color; - ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height); - } - clear(pos?: VectorInteger) { - let ctx = pong.ctx; - if (pos) - ctx.clearRect(pos.x, pos.y, this.width, this.height); - else - ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height); - } - collision(collider: Rectangle): boolean { // Collision WIP. To redo - var myleft = this.pos.x; - var myright = this.pos.x + (this.width); - var mytop = this.pos.y; - var mybottom = this.pos.y + (this.height); - var otherleft = collider.pos.x; - var otherright = collider.pos.x + (collider.width); - var othertop = collider.pos.y; - var otherbottom = collider.pos.y + (collider.height); - if ((mybottom < othertop) - || (mytop > otherbottom) - || (myright < otherleft) - || (myleft > otherright)) { - return false; - } - else - return true; - } -} - - -interface Moving { - dir: Vector; - speed: number; - move(): void; -} - - -class MovingRectangle extends Rectangle implements Moving { - dir: Vector = new Vector(0,0); - speed: number; - readonly baseSpeed: number; - constructor(pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) { - super(pos, color, width, height); - this.baseSpeed = baseSpeed; - this.speed = baseSpeed; - } - move() { // Math.floor WIP until VectorInteger debug - this.pos.x += Math.floor(this.dir.x * this.speed); - this.pos.y += Math.floor(this.dir.y * this.speed); - } - moveAndCollide(colliderArr: Rectangle[]) { - let oldPos = Object.assign({}, this.pos); - this.move(); - if (colliderArr.some(this.collision, this)) - { - this.pos.x = oldPos.x; - this.pos.y = oldPos.y; - } - else - { - this.clear(oldPos); - this.update(); - } - } -} - - -class Player extends MovingRectangle { - constructor(pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) { - super(pos, color, width, height, baseSpeed); - } -} - - -class Ball extends MovingRectangle { - constructor(pos: VectorInteger, color: string, size: number, baseSpeed: number) { - super(pos, color, size, size, baseSpeed); - } - bounce(collider?: Rectangle) { - /* Could be more generic, but testing only player is enough, - because in Pong collider can only be Player or Wall. */ - if (collider instanceof Player) { - this._bouncePlayer(collider); - } - else { - this._bounceWall(); - } - } - moveAndBounce(colliderArr: Rectangle[]) { - let oldPos = Object.assign({}, this.pos); - this.move(); - let i = colliderArr.findIndex(this.collision, this); - if (i != -1) - { - this.bounce(colliderArr[i]); - this.move(); - } - this.clear(oldPos); - this.update(); - } - private _bounceWall() { // Should be enough for Wall - ball.dir.y = ball.dir.y * -1; - } - private _bouncePlayer(collider: Player) { // WIP - // Bounce for Player need to be more complexe than this - this.speed += this.baseSpeed/20; - ball.dir.x = ball.dir.x * -1; - } -} - -// conflict with Text -class TextElem implements Component { - pos: VectorInteger; - color: string; - size: number; - font: string; - text: string = ""; - constructor(pos: VectorInteger, color: string, size: number, font: string = "Bit5x3") { - this.pos = Object.assign({}, pos); - this.color = color; - this.size = size; - this.font = font; - } - update() { - let ctx = pong.ctx; - ctx.font = this.size + "px" + " " + this.font; - ctx.fillStyle = this.color; - ctx.fillText(this.text, this.pos.x, this.pos.y); - } - clear() { - // clear no very accurate for Text - // https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics - let ctx = pong.ctx; - let textMetric = ctx.measureText(this.text); - // console.log("textMetric.width = "+textMetric.width); - // console.log("size = "+this.size); - // console.log("x = "+this.pos.x); - // console.log("y = "+this.pos.y); - ctx.clearRect(this.pos.x - 1, this.pos.y-this.size + 1, textMetric.width, this.size); - // +1 and -1 because float imprecision (and Math.floor() with VectorInteger dont work for the moment) - // (or maybe its textMetric imprecision ?) - } -} - -class TextNumericValue extends TextElem { - private _value: number = 0; - constructor(pos: VectorInteger, color: string, size: number, font?: string) { - super(pos, color, size, font); - } - get value() { - return this._value; - } - set value(v: number) { - this._value = v; - this.text = v.toString(); - } -} - - -class Line extends Rectangle { - gapeCount: number = 0; - segmentCount: number; - segmentWidth: number; - segmentHeight: number; - constructor(pos: VectorInteger, color: string, width: number, height: number, gapeCount?: number) { - super(pos, color, width, height); - if (gapeCount) - this.gapeCount = gapeCount; - this.segmentCount = this.gapeCount * 2 + 1; - - this.segmentWidth = this.width; - this.segmentHeight = this.height / this.segmentCount; - - // for Horizontal Line - // this.segmentWidth = this.width / this.segmentCount; - // this.segmentHeight = this.height; - } - update() { - let ctx = pong.ctx; - ctx.fillStyle = this.color; - let pos: VectorInteger = new VectorInteger; - let i = 0; - while (i < this.segmentCount) - { - // for Horizontal Line - // pos.y = this.pos.y; - // pos.x = this.pos.x + this.segmentWidth * i; - pos.x = this.pos.x; - pos.y = this.pos.y + this.segmentHeight * i; - ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight); - i += 2; - } - } -} diff --git a/src/server/server.ts b/src/server/server.ts new file mode 100644 index 00000000..6a8523fd --- /dev/null +++ b/src/server/server.ts @@ -0,0 +1,40 @@ +// @ts-check + +// var http = require("http"); +// var url = require("url"); +// var fs = require("fs"); +// var path = require("path"); +import http from "http"; +import url from "url"; +import fs from "fs"; +import path from "path"; + + +const hostname = "localhost"; +const port = 8080; +const root = "/mnt/c/Users/Lucky/Desktop/code/ft_transcendence/www/"; + +const server = http.createServer((req, res) => { + // var q = new URL(req.url, `http://${req.getHeaders().host}`) + // @ts-ignore + var q = url.parse(req.url, true); + var filename = root + q.pathname; + fs.readFile(filename, (err, data) => { + if (err) { + res.writeHead(404, {"Content-Type": "text/html"}); + return res.end("404 Not Found"); + } + if (path.extname(filename) === ".html") { + res.writeHead(200, {"Content-Type": "text/html"}); + } + if (path.extname(filename) === ".js") { + res.writeHead(200, {"Content-Type": "application/javascript"}); + } + res.write(data); + return res.end(); + }); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index fddada1e..338cf3cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,9 @@ { - "include": ["./src"], + "include": ["src"], +// "exclude": ["node_modules"], "compilerOptions": { - "outDir": "./build", + // "outDir": "./build", + // "types": ["node"], /* Specify type package names to be included without being referenced in a source file. */ /* Visit https://aka.ms/tsconfig to read more about this file */ /* Projects */ @@ -27,14 +29,14 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + // "module": "commonjs", /* Specify what module code is generated. */ + "module": "ES6", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "resolveJsonModule": true, /* Enable importing .json files. */ diff --git a/src/Bit5x3.woff b/www/Bit5x3.woff similarity index 100% rename from src/Bit5x3.woff rename to www/Bit5x3.woff diff --git a/src/Bit5x3.woff2 b/www/Bit5x3.woff2 similarity index 100% rename from src/Bit5x3.woff2 rename to www/Bit5x3.woff2