From cf25270c90efead31384ac7a99498b959ec2d703 Mon Sep 17 00:00:00 2001
From: Bye <bye@byecorps.com>
Date: Sun, 20 Aug 2023 21:28:29 +0100
Subject: [PATCH] Start with physics.

---
 .idea/2023.iml                  |   1 +
 assets/ball.ase                 | Bin 0 -> 849 bytes
 assets/catapult.ase             | Bin 0 -> 1245 bytes
 src/img/ball.webp               | Bin 0 -> 74 bytes
 src/img/catapult.webp           | Bin 0 -> 184 bytes
 src/index.html                  |   2 +-
 src/js/canvas.js                |   7 ++
 src/js/game.js                  | 166 +++++++++++++++++++++++++-------
 src/js/{ => inputs}/keyboard.js |   0
 src/js/inputs/mouse.js          |  11 +++
 src/js/objects.js               |   8 +-
 src/js/text.js                  |  12 +--
 12 files changed, 164 insertions(+), 43 deletions(-)
 create mode 100644 assets/ball.ase
 create mode 100644 assets/catapult.ase
 create mode 100644 src/img/ball.webp
 create mode 100644 src/img/catapult.webp
 rename src/js/{ => inputs}/keyboard.js (100%)
 create mode 100644 src/js/inputs/mouse.js

diff --git a/.idea/2023.iml b/.idea/2023.iml
index 24643cc..d3f1fda 100644
--- a/.idea/2023.iml
+++ b/.idea/2023.iml
@@ -5,6 +5,7 @@
       <excludeFolder url="file://$MODULE_DIR$/.tmp" />
       <excludeFolder url="file://$MODULE_DIR$/temp" />
       <excludeFolder url="file://$MODULE_DIR$/tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/dist" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
diff --git a/assets/ball.ase b/assets/ball.ase
new file mode 100644
index 0000000000000000000000000000000000000000..066d42155ef8a13fb79c430e3f8ec8e63b2bfa4b
GIT binary patch
literal 849
zcmWG!W?*=*l#zjhfrCMTfsuiMA%y`M*fKDJ1Q`Sv1dxTP#=gkJ!0_uME7(?61_lN(
z1_lOp1+bM!c62Z@Fi0xcLPcz#G@SYmX8mVisI08~&%m%~(W3th3<iNs{}~t@CmsIJ
zz>s+PF-ZLXXOMWJ<9`MQ&qbO485jy5PXddd{?EW*k;?!QUnKw%{|r*I=rhP1i%gJs
z;35z?>otfx{TXDkXC#Q-)deD7Usm|f!0`Vw$h1mFkjUXe5cwah>;G+#Ig1QHB98+>
z<o`+okowcR{xdLa5&*GpJ0^le5;uX!U9bN$Fw9Z_`Q^4F$o-!a|Nm!Tc)STjCJKN|
zo8$=c%c4S%$YHR!1_Q`6M@NuIU=xU3v<pNkFo4W-O#BTBheaTfNtZz+B&;@V0?EF9
z4YKk7e~`FMK-7N*hS1Q^{|pSJrKSHF7!>5?|1&U1OH2Ph$iTqB0!k&&sHm*0T(oGB
zL12^Pq{E4qA20g<*&xx;b5UmD<4KGDpSH+l@LVNO_}OvM=S3Eo41tRnX1!)O{kh3A
zlA)`M0TgDRCsjH!fP!if!~ffh78x*rf~V5p|LI+u1PnkylDO&ruGg~^DsMX;{+#&#
z@uvTY0*fX&eqL1g|L~^&8VtJ}9sdV5{a>`}zXHQ1$Hd=>i~di#{J*lYa?_?wAV2;8
zZxav|8X8(!TB;x~FD)%C5AuKlGdR6K2vFK#XYfg^Of6C{bY@^+U{!!hFhOVraNekx
ulbq1NkeT`N|A`rloDDy?K7G=VSjIM6W|LIZY2hMyX2$=F3>VW}9|8ax#TtA7

literal 0
HcmV?d00001

diff --git a/assets/catapult.ase b/assets/catapult.ase
new file mode 100644
index 0000000000000000000000000000000000000000..a8c382e32a94596922eefc37340c7bbbc4dfcec2
GIT binary patch
literal 1245
zcmcc1!ocugDI<d+g9L*D10w?iLka^juw`Hb2{H&U2p|hnjUCIv!0_uM7uZ%V1_lN(
z1_lOp1+bM!c62Z@Fi0xcLPcz#G@SYmX8mVisI08~&%m%~(W3th3<iNs{}~t@CmsIJ
zz>s+PF-ZLXXOMWJ<9`MQ&qbO485jy5PXddd{?EW*k;?!QUnKw%{|r*I=rhP1i%gJs
z;35z?>otfx{TXDkXC#Q-)deD7Usm|f!0`Vw$h1mFkjUXe5cwah>;G+#Ig1QHB98+>
z<o`+okowcR{xdLa5&*GpJ0^le5;uX!U9bN$Fw9Z_`Q^4F$o-!a|Nm!Tc)STjCJKN|
zo8$=c%c4S%$YHR!1_Q`6M@NuIU=xU3v<pNkFo4W-O#BTBheaTfNtZz+B&;@V0?EF9
z4YKk7e~`FMK-7N*hS1Q^{|pSJrKSHF7!>5?|1&U1OH2Ph$iTqB0!k&&sHm*0T(oGB
zL12^Pq{E4qA20g<*&xx;b5UmD<4KGDpSH+l@LVNO_}OvM=S3Eo41tRnX1!)O{kh3A
zlA)`M0TgDRCsjH!fP!if!~ffh78x*rf~V5p|LI+u1PnkylDO&ruGg~^DsMX;{+#&#
z@uvTY0*fX&eqL1g|L~^&8VtJ}9sdV5{a>`}zXHQ1$Hd=>i~di#{J*lYa?_?wAV2;8
zZxav|8X8(!TB;x~FD)%C3-W*hGdR6K2vFK#We85r&o96w=~k4On<|f0Cp&{rVr6QP
zf)RdcV^F}cLX-7>CWy@p42BG13>9<UPWR?&FyL{iHqC#0ZRz(<Y;rymm0A{h_TI8$
z<ldas_1^K9q3XVkJ(-h#&U${%V#jq&^-YIup171){C2l;`82^+rCPJgxikJ9dl`SP
zeOL2Q`GW^v{l9ec)U$;jAIq$6KHC4ehtITbO=9tmL;a<y`|FNag}>E3^5Vh6<5g_z
z`u6KCR;`uYc<xc9ceU(?`p0K8zOPGvJ2~I=<og2?r&TXZFXI;uo#n>Bz`&}&#Gt{z
z&w%g%Cj%!##hl~>hXybAw*Lla8dw!hCkMCt%0<fBRmS`%P<&a{v9VvIUXV+kp|K=s
zO+MHZW(IB)Q`F$5C`E%zaZq<S;WwdTsWQ{$!?`cY5*{n>?v%?rEN*w>j!S=mvf7*p
ucNvr9^^S-vUb=h2VfN{}*@7z$zBo8---Msdy7o%l{L?;hGwhz)`2_$Icd69?

literal 0
HcmV?d00001

diff --git a/src/img/ball.webp b/src/img/ball.webp
new file mode 100644
index 0000000000000000000000000000000000000000..49d38764d87b47fd9a1391fb90d4ba8b70622c54
GIT binary patch
literal 74
zcmWIYbaQiJU|<M$bqWXzu<$WuU|`T^Kfov;?x5f>Q)|xxg<NK_a{r?oEE>VB;?L|E
f{^v6vWw_w5US(02^18B0KBYzf6<eOmb1?t_;Q1EV

literal 0
HcmV?d00001

diff --git a/src/img/catapult.webp b/src/img/catapult.webp
new file mode 100644
index 0000000000000000000000000000000000000000..12ba75241a220c23e3bc79221a45d51fd70bff06
GIT binary patch
literal 184
zcmWIYbaUIlz`zjh>J$(bVBxcbfq_Ba-~g+D`ho?y(q`8-cc*B$Ey<lNEj?$!?y58O
z4F4C-=9O`oB;w{im(e4sA+%s+cccEb{i~YeIR*cQ-`JLW_{}$AVWmsHJq!3YxtuZ&
z?Jl|gPcyppmp^}Q-9pYwuHEx9n@YHM?KIJT`hWk$ZINF#zTWX_joPf#PR>0$Pn<dP
sgg0iN$ESm>-R(1GWX!&AvwNOb&hh8YQ_{6B7Tz>eo0V&LD^flU0PvJmzyJUM

literal 0
HcmV?d00001

diff --git a/src/index.html b/src/index.html
index 6cb5043..12f2612 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,4 +1,4 @@
-<!-- maybe slightly copied from herebefrog's -->
+<!-- maybe slightly copied from herebefrog -->
 
 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
 <style>
diff --git a/src/js/canvas.js b/src/js/canvas.js
index 2cc5c38..2b3eb0d 100644
--- a/src/js/canvas.js
+++ b/src/js/canvas.js
@@ -18,6 +18,13 @@ class Canvas {
         this.cY = 0;
     }
 
+    setDimensions (w, h) {
+        this.canvas.width = w;
+        this.canvas.height = h;
+        this.width = this.canvas.width;
+        this.height = this.canvas.height;
+    }
+
     fill(c="black") {
         this.ctx.fillStyle = c;
         this.ctx.fillRect(0, 0, this.width, this.height);
diff --git a/src/js/game.js b/src/js/game.js
index e93272e..9031c01 100644
--- a/src/js/game.js
+++ b/src/js/game.js
@@ -3,14 +3,20 @@ import { WIDTH, HEIGHT, GAME_TITLE } from "./config.js";
 import { Canvas } from "./canvas.js";
 import { TextRenderer } from "./text.js";
 import { Room, Object } from "./objects.js";
-import { isKeyUp, whichKeyDown } from "./keyboard.js";
 import { getParameter } from "./utils.js";
+import { getMousePos } from "./inputs/mouse.js";
+import { isKeyUp, whichKeyDown } from "./inputs/keyboard.js";
+
+
+let imgPrefix = "../img/";
 
 let assets = {
     images: {
-        splash: "../img/splash1.webp",
-        font: "../img/hampsterfont.webp",
-        selector: "../img/selector.webp",
+        splash: "splash1.webp",
+        font: "hampsterfont.webp",
+        selector: "selector.webp",
+        catapult: "catapult.webp",
+        debug_ball: "ball.webp",
     },
 }
 
@@ -33,11 +39,11 @@ let pressedLastFrame = [];
 canvas.fill("#222034");
 
 let splash = new Image();
-splash.src = assets.images.splash;
+splash.src = imgPrefix + assets.images.splash;
 splash.onload = () => {
     canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);
     let font = new Image();
-    font.src = assets.images.font;
+    font.src = imgPrefix + assets.images.font;
     font.onload = () => {
         console.log("font loaded")
         text = new TextRenderer(canvas, font);
@@ -48,14 +54,13 @@ splash.onload = () => {
     }
 }
 
-// Entity class is here becuase otherwise every entity would need the canvas passed into it
+// Entity class is here because otherwise every entity would need the canvas passed into it
 class Entity extends Object {
     constructor(x=0, y=0, spritesheet=null, sprite=null) {
         super();
         this.x = x;
         this.y = y;
         this.sprite = sprite;
-        this.spritesheet = spritesheet;
     }
 
     draw() {
@@ -70,7 +75,7 @@ let currentRoom = rooms[roomIndex];
 let searchForRoom = (name) => {
     // returns the room's index in the rooms array
     for (let i = 0; i < rooms.length; i++) {
-        if (rooms[i].name == name) return i;
+        if (rooms[i].name === name) return i;
     } throw new Error("Room not found:"+name+". Are you sure it's pushed?");
 }
 
@@ -95,15 +100,10 @@ debugRoom.roomList = [];
 debugRoom.options = {
     "main": [
         {"label": "Change Room", "action": _ => {debugRoom.submenu = "changeRoom"; debugRoom.index = 0;}},
-        {"label": "Change Target FPS", "action": _ => {debugRoom.submenu = "changeTargetFPS"; debugRoom.index = 0;}},
         {"label": "Unlock refresh rate", "action": _ => {runAtMonitorRefreshRate = !runAtMonitorRefreshRate;}},
         {"label": "Exit", "action": _ => {changeRoom(searchForRoom("menu"))}},
     ],
     "changeRoom": [],
-    "changeTargetFPS": [
-        {"label": "60", "action": _ => {targetFrames = 60;}},
-        {"label": "30", "action": _ => {targetFrames = 30;}},
-    ],
 };
 debugRoom.init = () => {
     if (!debug) changeRoom(searchForRoom("menu"));
@@ -130,7 +130,6 @@ debugRoom.keyDown = (key) => {
 }
 
 debugRoom.draw = () => {
-    canvas.fill("black");
 
     canvas.drawRect(Math.sin(currentFrame /(canvas.width / 2)) * canvas.width - 32, canvas.height-64, 32, 32, "#222034");
 }
@@ -151,14 +150,10 @@ menuRoom.options = [
 ];
 if (debug) menuRoom.options.push({"label": "Debug Room", "action": _ => {changeRoom(searchForRoom("debug"))}});
 menuRoom.index = 0;
-
-menuRoom.draw = () => {
-    canvas.fill("black");
-}
 menuRoom.drawGUI = () => {
     text.render(GAME_TITLE, 8, 7*4);
     for (let i = 0; i < menuRoom.options.length; i++) {
-        if (i == menuRoom.index) {
+        if (i === menuRoom.index) {
             text.render(">", 8, 7*(i+5));
         }
         text.render(menuRoom.options[i].label, 16, 7*(i+5));
@@ -184,18 +179,22 @@ const testing_graphing = new Room("testing_graphing");
 testing_graphing.yScale = 10;
 testing_graphing.a = 0;
 testing_graphing.b = 0;
-testing_graphing.c = (canvas.height/4)*testing_graphing.yScale;
+testing_graphing.c = 0;
+
+testing_graphing.init = () => {
+    // canvas.setDimensions(window.innerWidth, window.innerHeight);
+}
 
 testing_graphing.keyDown = (key) => {
     if (pressedLastFrame.includes(key)) return;
 
     const keyActions = {
-        ArrowUp: () => testing_graphing.a += 0.25,
-        ArrowDown: () => testing_graphing.a -= 0.25,
-        ArrowLeft: () => testing_graphing.b -= 0.25,
-        ArrowRight: () => testing_graphing.b += 0.25,
-        KeyA: () => testing_graphing.c -= 0.25,
-        KeyD: () => testing_graphing.c += 0.25,
+        ArrowUp: () => testing_graphing.a++,
+        ArrowDown: () => testing_graphing.a--,
+        ArrowLeft: () => testing_graphing.b --,
+        ArrowRight: () => testing_graphing.b ++,
+        KeyA: () => testing_graphing.c++,
+        KeyD: () => testing_graphing.c--,
     };
 
     const action = keyActions[key];
@@ -205,7 +204,6 @@ testing_graphing.keyDown = (key) => {
 testing_graphing.draw = () => {
     // draws a quadratic graph
     // y = ax^2 + bx + c
-    canvas.fill("black");
     canvas.drawLine(0, canvas.height/2, canvas.width, canvas.height/2, "white");
     canvas.drawLine(canvas.width/2, 0, canvas.width/2, canvas.height, "white");
     for (let i = 0; i < canvas.width; i++) {
@@ -219,15 +217,105 @@ testing_graphing.drawGUI = () => {
     text.render(`y = ${testing_graphing.a}x^2 + ${testing_graphing.b}x + ${testing_graphing.c}`, 0, 0);
 }
 
+const testing_physics = new Room("testing_physics");
+testing_physics.init = () => {
+    canvas.setDimensions(1000, 500)
+}
+testing_physics.gravity = 0.00005; // m/s^2
+testing_physics.rho = 1;
+
+let testing_ball = new Entity(canvas.width/2, 0);
+testing_physics.objects.push(testing_ball);
+
+testing_ball.velocity = { x: 10, y: 0 };
+testing_ball.mass = 70; // kg
+testing_ball.radius = 4; // pixels
+testing_ball.restitution = 0.2; // 0-1, 1 being perfectly elastic
+testing_ball.Cd = 0.47; // dimensionless
+
+testing_ball.step = () => {
+    // Calculate forces on the x and y axis
+    var Fx = -0.5 * testing_ball.Cd * testing_ball.A * testing_physics.rho * testing_ball.velocity.x * testing_ball.velocity.x * testing_ball.velocity.x / Math.abs(testing_ball.velocity.x);
+    var Fy = -0.5 * testing_ball.Cd * testing_ball.A * testing_physics.rho * testing_ball.velocity.y * testing_ball.velocity.y * testing_ball.velocity.y / Math.abs(testing_ball.velocity.y);
+
+    Fx = (isNaN(Fx) ? 0 : Fx);
+    Fy = (isNaN(Fy) ? 0 : Fy);
+
+    // Acceleration
+    var ax = (Fx / testing_ball.mass);
+    var ay = testing_physics.gravity + (Fy / testing_ball.mass);
+
+    testing_ball.velocity.x += ax * targetFrames;
+    testing_ball.velocity.y += ay * targetFrames;
+
+    // Position
+    testing_ball.x += testing_ball.velocity.x * targetFrames;
+    testing_ball.y += testing_ball.velocity.y * targetFrames;
+
+    if (testing_ball.y > canvas.height - testing_ball.radius) {
+        testing_ball.velocity.y *= -testing_ball.restitution;
+        testing_ball.y = canvas.height - testing_ball.radius;
+    }
+
+    if (testing_ball.x > canvas.width - testing_ball.radius) {
+        testing_ball.velocity.x *= -testing_ball.restitution;
+        testing_ball.x = canvas.width - testing_ball.radius;
+    }
+
+    if (testing_ball.x < testing_ball.radius) {
+        testing_ball.velocity.x *= -testing_ball.restitution;
+        testing_ball.x = testing_ball.radius;
+    }
+
+    debugStatuses.push(`acceleration: ${ax}, ${ay}`);
+    debugStatuses.push(`force: ${Fx}, ${Fy}`);
+}
+
+testing_ball.draw = () => {
+    canvas.drawImage(assets.images.debug_ball, testing_ball.x - 4, testing_ball.y - 4);
+}
+
+testing_physics.keyDown = (key) => {
+    if (pressedLastFrame.includes(key)) return;
+
+    const keyActions = {
+        KeyL: _=> {
+            testing_ball.velocity = {x: 20, y: 100}
+        },
+        KeyS: _=> {
+            testing_ball.velocity = {x: 0, y: 0}
+        }
+    };
+
+    const action = keyActions[key];
+    if (action) action();
+}
+
+testing_physics.drawGUI = () => {
+    debugStatuses.push(`position: ${testing_ball.x}, ${testing_ball.y}`);
+    debugStatuses.push(`velocity: ${testing_ball.velocity.x}, ${testing_ball.velocity.y}`);
+
+}
+
+testing_physics.onclick = (pos={x:0,y:0}) => {
+    testing_ball.x = pos.x;
+    testing_ball.y = pos.y;
+}
+
 rooms.push(loadingRoom);
 rooms.push(menuRoom);
 rooms.push(debugRoom);
 
 // REMOVE THESE
 rooms.push(testing_graphing);
+rooms.push(testing_physics);
 
 currentRoom = rooms[roomIndex];
 
+canvas.canvas.addEventListener('mousedown', function(evt) {
+    const mousePos = getMousePos(canvas.canvas, evt);
+    currentRoom.onclick(mousePos);
+}, false);
 
 
 let main = () => { // main game loop
@@ -243,7 +331,8 @@ let main = () => { // main game loop
 
     currentRoom.step();
 
-    currentRoom.draw();
+    // canvas.fill(currentRoom.background);
+
     currentRoom.drawGUI();
 
     let currentKeys = whichKeyDown();
@@ -267,12 +356,23 @@ let main = () => { // main game loop
     }
 
     for (let i = 0; i < debugStatuses.length; i++) {
-        // console.debug(debugStatuses[i]);
-        if (typeof(debugStatuses[i]) == "string") text.render(debugStatuses[i], 0, canvas.height-text.charHeight*(debugStatuses.length-i));
-        if (typeof(debugStatuses[i]) == "object") {text.render(debugStatuses[i].msg, 0, canvas.height-text.charHeight*(debugStatuses.length-i)); debugStatuses[i].ttl--;}
+        switch (typeof (debugStatuses[i])) {
+            case "string":
+                text.render(debugStatuses[i], 0, canvas.height - text.charHeight * (debugStatuses.length - i));
+                break;
+            case "object":
+                console.debug("OBJECT!!")
+                text.render(debugStatuses[i].msg, 0, canvas.height-text.charHeight*(debugStatuses.length-i));
+                debugStatuses[i].ttl--;
+                break;
+        }
+        if (typeof(debugStatuses[i]) == "object") {}
 
     }
 
+    currentRoom.draw();
+
+
     lastFrameTime = now;
     }
 
@@ -282,7 +382,7 @@ let init = () => {
     for (let image in assets.images) {
         currentRoom.updateStatus("Loading image " + image);
         let img = new Image();
-        img.src = assets.images[image];
+        img.src = imgPrefix + assets.images[image];
         img.onload = () => {
             assets.images[image] = img;
         }
diff --git a/src/js/keyboard.js b/src/js/inputs/keyboard.js
similarity index 100%
rename from src/js/keyboard.js
rename to src/js/inputs/keyboard.js
diff --git a/src/js/inputs/mouse.js b/src/js/inputs/mouse.js
new file mode 100644
index 0000000..6b7c433
--- /dev/null
+++ b/src/js/inputs/mouse.js
@@ -0,0 +1,11 @@
+export const getMousePos = (canvas, evt) => {
+    const rect = canvas.getBoundingClientRect();
+    let x = evt.clientX - rect.left;
+    let y = evt.clientY - rect.top;
+    x /= rect.width;
+    y /= rect.height;
+    x *= canvas.width;
+    y *= canvas.height;
+    return {x,y};
+  }
+
diff --git a/src/js/objects.js b/src/js/objects.js
index f194109..0db9387 100644
--- a/src/js/objects.js
+++ b/src/js/objects.js
@@ -9,13 +9,14 @@ class Room extends Object {
         super();
         this.objects = [];
         this.name = name; // needs to be unique, otherwise the searching code will just use the first one it finds.
+        this.background = "#000000";
     }
 
     init(){}
 
     draw() {
-        for (let i = 0; i < this.objects.length; i++) {
-            this.objects[i].draw();
+        for (const item of this.objects) {
+            item.draw();
         }
     }
 
@@ -24,6 +25,9 @@ class Room extends Object {
     }
 
     keyDown(key) {
+        for (const item of this.objects) {
+            item.keyDown(key);
+        }
     }
 
     keyUp(key) {
diff --git a/src/js/text.js b/src/js/text.js
index 9fd464c..c881cc8 100644
--- a/src/js/text.js
+++ b/src/js/text.js
@@ -16,20 +16,18 @@ class TextRenderer {
         let { canvas, fontWidth, fontHeight } = this;
 
         let index = this.fontChars.indexOf(letter.toLowerCase());
-        if (index == -1) {
+        if (index === -1) {
             if (!substituteOK) return;
-            canvas.drawText(letter, x, y, "#ffffff", 7, "monospace");
+            canvas.drawText(letter, x, y, "#ffffff", 5, "monospace");
         }
         let sx = index * fontWidth;
         let sy = 0;
         // draw image to context
         let yOffset = 0;
         // if the letter is ",", offset it by -1
-        if (letter == ",") {
-            yOffset = -1;
-        }
+        if (letter === ",") yOffset = 1;
         canvas.sliceImage(this.fontimg, x+canvas.cX, y + yOffset + canvas.cY, fontWidth, fontHeight, sx, sy, fontWidth, fontHeight); 
-            // canvas.cX and canvas.cY are the camera offsets. we dont want to have text flying off the screen.
+            // canvas.cX and canvas.cY are the camera offsets. we don't want to have text flying off the screen.
             // you can counteract this by specifying x-cX and x-cY when calling this.
     }
 
@@ -37,7 +35,7 @@ class TextRenderer {
         let heightOffset = 0;
         let xOffset = 0;
         for (let i = 0; i < text.length; i++) {
-            if (text[i] == "\n") {
+            if (text[i] === "\n") {
                 heightOffset++;
                 xOffset = 0;
                 continue;
-- 
GitLab