diff --git a/.gitignore b/.gitignore
index 890b7b6b..b58afaf9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,9 @@ Thumbs.db
*.log
.env
+jsconfig.json
+node_modules
+build
+
+large
+old
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..3ea0eee0
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,33 @@
+{
+ "name": "ft_transcendence",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "typescript": "^4.8.4"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ }
+ },
+ "dependencies": {
+ "typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..425985bb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "devDependencies": {
+ "typescript": "^4.8.4"
+ }
+}
diff --git a/pong.html b/pong.html
deleted file mode 100644
index b7fc54c0..00000000
--- a/pong.html
+++ /dev/null
@@ -1,298 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pong.html b/src/pong.html
new file mode 100644
index 00000000..14a78226
--- /dev/null
+++ b/src/pong.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pong.ts b/src/pong.ts
new file mode 100644
index 00000000..5f0bfd70
--- /dev/null
+++ b/src/pong.ts
@@ -0,0 +1,359 @@
+
+// import {component, score, line} from "./class.js";
+// @ts-check
+
+let gridDisplay = false;
+
+let pong: gameArea;
+
+let wall_top: Rectangle;
+let wall_bottom: Rectangle;
+let player1: Player;
+let player2: Player;
+let ball: Ball;
+let score1: TextElem;
+let score2: TextElem;
+
+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
+ let w = pong.canvas.width;
+ let h = pong.canvas.height;
+ let w_mid = w/2;
+ let h_mid = h/2;
+ const pw = w/50;
+ const ph = pw*5;
+ const ball_size = pw; // const ball_size = w/50;
+ const score_size = w/16;
+ const wall_size = w/100;
+ const playerSpeed = w/75;
+ const ballSpeed = w/75;
+
+// Component
+ wall_top = new Rectangle({x: 0, y: 0}, "grey", w, wall_size);
+ wall_bottom = new Rectangle({x: 0, y: h-wall_size}, "grey", w, wall_size);
+
+ player1 = new Player({x: 0+w/50, y: h_mid-ph/2}, "white", pw, ph);
+ player2 = new Player({x: w-w/50-pw, y: h_mid-ph/2}, "white", pw, ph);
+ player1.speed = playerSpeed;
+ player2.speed = playerSpeed;
+
+ ball = new Ball({x: w_mid-ball_size/2, y: h_mid-ball_size/2}, "white", ball_size);
+ ball.speed = ballSpeed;
+ ball.dir.x = -0.8;
+ ball.dir.y = +0.2;
+
+ score1 = new TextElem({x: w_mid-w/8, y: w/12}, "white", score_size+"px");
+ score1.text = "0";
+ score2 = new TextElem({x: w_mid+w/8-score_size/2, y: w/12}, "white", score_size+"px");
+ score2.text = "0";
+
+// Grid
+ const grid_size = w/500;
+ w_grid_mid = new Rectangle({x: 0, y: h_mid}, "darkgreen", w, grid_size);
+ w_grid_u1 = new Rectangle({x: 0, y: h/4}, "darkgreen", w, grid_size);
+ w_grid_d1 = new Rectangle({x: 0, y: h-h/4}, "darkgreen", w, grid_size);
+ h_grid_mid = new Rectangle({x: w_mid, y: 0}, "darkgreen", grid_size, h);
+ h_grid_u1 = new Rectangle({x: w/4, y: 0}, "darkgreen", grid_size, h);
+ h_grid_d1 = new Rectangle({x: w-w/4, y: 0}, "darkgreen", grid_size, h);
+
+// dashed line TODO
+ // midLine = new component(grid_size, h, "white", w_mid, 0);
+
+ 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() {
+ // @ts-ignore
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+ stop() {
+ clearInterval(this.interval);
+ }
+}
+
+// Bad drawLine, TODO make a drawLine with fillRect()
+// function drawLine(start, end, color, pattern)
+// {
+// let ctx = pong.ctx;
+// ctx.beginPath();
+// ctx.setLineDash(pattern);
+// ctx.moveTo(start[0], start[1]);
+// ctx.lineTo(end[0], end[1]);
+// ctx.strokeStyle = color;
+// ctx.stroke();
+// }
+
+function gameLoop()
+{
+ handleInput();
+
+ ball.move();
+ if (ball.collision(wall_top) || ball.collision(wall_bottom))
+ ball.bounce();
+ else if (ball.collision(player1))
+ ball.bounce(player1);
+ else if (ball.collision(player2))
+ ball.bounce(player2);
+
+ draw();
+}
+
+function draw()
+{
+ pong.clear();
+
+ if (gridDisplay)
+ drawGrid();
+
+ // drawLine([w_mid, 0], [w_mid, h], "white", [10, 10]); // bad
+
+ wall_top.update();
+ wall_bottom.update();
+ player1.update();
+ player2.update();
+ ball.update();
+ score1.update();
+ score2.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)
+ {
+ 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.move();
+ if (player1.collision(wall_top) || player1.collision(wall_bottom))
+ {
+ player1.dir.y = player1.dir.y * -1;
+ player1.move();
+ }
+
+ 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.move();
+ if (player2.collision(wall_top) || player2.collision(wall_bottom))
+ {
+ player2.dir.y = player2.dir.y * -1;
+ player2.move();
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// class.js
+
+type Vector = {
+ x: number;
+ y: number;
+}
+
+
+interface Component {
+ pos: Vector;
+ color: string;
+ // ctx: CanvasRenderingContext2D;
+ update(): void;
+}
+
+
+class Rectangle implements Component {
+ pos: Vector;
+ color: string;
+ width: number;
+ height: number;
+ constructor(pos: Vector, color: string, width: number, height: number) {
+ this.pos = 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);
+ }
+ 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 Player extends Rectangle implements Moving {
+ dir: Vector = {x: 0.0, y: 0.0};
+ speed: number = 1;
+ constructor(pos: Vector, color: string, width: number, height: number) {
+ super(pos, color, width, height);
+ }
+ move() {
+ this.pos.x += this.dir.x * this.speed;
+ this.pos.y += this.dir.y * this.speed;
+ }
+}
+
+
+class Ball extends Rectangle implements Moving {
+ dir: Vector = {x: 0.0, y: 0.0};
+ speed: number = 1;
+ constructor(pos: Vector, color: string, size: number) {
+ super(pos, color, size, size);
+ }
+ move() {
+ this.pos.x += this.dir.x * this.speed;
+ this.pos.y += this.dir.y * this.speed;
+ }
+ bounce(collider?: Rectangle) {
+ if (collider instanceof Player)
+ this._bouncePlayer(collider);
+ else // Could be more generic, but it's OK, because in Pong collider can only be Player or Wall.
+ this._bounceWall();
+
+ }
+ 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
+ ball.dir.x = ball.dir.x * -1;
+ }
+}
+
+// conflict with Text
+class TextElem implements Component {
+ pos: Vector;
+ color: string;
+ size: string;
+ font: string = "Consolas";
+ text: string = "";
+ constructor(pos: Vector, color: string, size: string, font?: string) {
+ this.pos = pos;
+ this.color = color;
+ this.size = size;
+ if (font)
+ this.font = font;
+ }
+ update() {
+ let ctx = pong.ctx;
+ ctx.font = this.size + " " + this.font;
+ ctx.fillStyle = this.color;
+ ctx.fillText(this.text, this.pos.x, this.pos.y);
+ }
+}
+
+// class line {
+// width: number;
+// height: number;
+// color: string;
+// x: number;
+// y: number;
+// constructor(width: number, height: number, color: string, x: number, y: number) {
+// this.width = width;
+// this.height = height;
+// this.color = color;
+// this.x = x;
+// this.y = y;
+// }
+// update() {
+// let ctx = pong.ctx;
+// ctx.fillStyle = this.color;
+// ctx.fillRect(this.x, this.y, this.width, this.height);
+// }
+// }
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..fddada1e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,105 @@
+{
+ "include": ["./src"],
+ "compilerOptions": {
+ "outDir": "./build",
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* 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. */
+ // "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. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}