From 757293a903855ea1123976f395d6c87f48a13a83 Mon Sep 17 00:00:00 2001
From: bye <bye@byecorps.com>
Date: Thu, 29 Aug 2024 13:10:07 +0100
Subject: [PATCH] update (havent dont that in a whike

---
 src/index.html                 | 12 +++--
 src/js/extras.js               | 16 +++++++
 src/js/main.js                 | 87 ++++++++++++++++++++++------------
 src/js/objects.js              | 38 +++++++++++++++
 src/js/objects/player.js       | 21 ++++----
 src/js/rooms/debug_sprites.js  | 12 +++++
 src/js/rooms/debug_stars.js    | 11 +++++
 src/js/rooms/game.js           | 85 ++++++++++++++++++++++++++++-----
 src/js/rooms/mainMenu.js       | 28 +++++++++--
 utils/size.js                  |  7 +++
 utils/sprite_editor/index.html |  7 ++-
 11 files changed, 259 insertions(+), 65 deletions(-)
 create mode 100644 src/js/rooms/debug_sprites.js
 create mode 100644 src/js/rooms/debug_stars.js

diff --git a/src/index.html b/src/index.html
index e70b4a8..672636a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,9 +3,7 @@
         margin:0;
         background: #1a1a1a;
     }
-    canvas {
-        width: 256px;
-        height: 240px;
+    #c {
         position:fixed;top:50%;left:50%;translate:-50% -50%;
         image-rendering: pixelated;
     }
@@ -14,14 +12,18 @@
 <script src="./main.js"></script>
 <script>
     fl = Math.floor;
+    a = c.getContext`2d`;
     u = _=>{
         w = innerWidth;
         h = innerHeight;
         wS = fl(w/256);
         hS = fl(h/256);
         s=Math.min(wS,hS);
-        c.style.width = 256*s;
-        c.style.height= 240*s;
+        c.width = 256*s;
+        c.height= 240*s;
+        canvas.pixelRatio = s;
+        canvas.setScale(canvas.scale);
+        a.imageSmoothingEnabled = false;
     }
     u();addEventListener('resize', u, !0);
 </script>
\ No newline at end of file
diff --git a/src/js/extras.js b/src/js/extras.js
index dc884e9..77f3731 100644
--- a/src/js/extras.js
+++ b/src/js/extras.js
@@ -6,6 +6,22 @@ export function drawLineThroughPoint(x, y) {
 
 export const clone = structuredClone;
 
+export const round = Math.round;
+export const roundToRatio = x => {
+    return round(x * canvas.pixelRatio) / canvas.pixelRatio
+}
+
+export const abs = Math.abs;
+
+export function mulberry32(a) {
+    return function() {
+        let t = a += 0x6D2B79F5;
+        t = Math.imul(t ^ t >>> 15, t | 1);
+        t ^= t + Math.imul(t ^ t >>> 7, t | 61);
+        return ((t ^ t >>> 14) >>> 0) / 4294967296;
+    }
+}
+
 export const clonePlayer = player => {
     return player;
 }
diff --git a/src/js/main.js b/src/js/main.js
index 65c282b..7b676ee 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,13 +1,14 @@
 
 import Canvas from "../../hampsterengine/src/canvas.js";
 import Engine from "../../hampsterengine/src/engine.js";
-import {FontRenderer} from "./objects";
+import Keyboard from "../../hampsterengine/src/keyboard";
+import {FontRenderer, Stars} from "./objects";
 
 import SoundBox from "./sb-player-small";
 // import {mus_DEMOSONG} from "./songs/DEMOSONGDONOTCOMMIT";
 
 // dependencies used for debugging. comment out code that uses this and they won't be included
-// import Stats from "stats.js";
+import Stats from "stats.js";
 
 // Images
 import font from "../img/font.webp";
@@ -15,39 +16,59 @@ import font from "../img/font.webp";
 // Rooms
 import {rm_mainMenu} from "./rooms/mainMenu";
 import {rm_game} from "./rooms/game";
-import {rm_DEBUG_button} from "./rooms/debug_button";
-import {rm_DEBUG_mouse} from "./rooms/debug_mouse";
-import {rm_DEBUG_music} from "./rooms/debug_music";
+// import {rm_DEBUG_button} from "./rooms/debug_button";
+// import {rm_DEBUG_mouse} from "./rooms/debug_mouse";
+// import {rm_DEBUG_music} from "./rooms/debug_music";
 import {rm_DEBUG_INCURSION} from "./rooms/debug_incursion";
 import {rm_DEBUG_text} from "./rooms/debug_text";
-import Mouse from "../../hampsterengine/src/mouse";
+import {rm_DEBUG_stars} from "./rooms/debug_stars";
+import {rm_DEBUG_sprites} from "./rooms/debug_sprites";
 
 // Music
 // There is none
 
 // Parse query parameters
 const query = new URLSearchParams(window.location.search);
+window.query = query;
 
 // Init the engine and canvas
-const canvas = new Canvas('c');
+const canvas = new Canvas(c);
 const engine = new Engine(canvas);
 const assets = engine.assetStore;
+const keyboard = new Keyboard();
 assets.addImage('font', font);
 
-const fontRenderer = new FontRenderer(assets.get('font'));
+const fontRenderer = new FontRenderer(assets.get`font`);
 window.fR = fontRenderer
 // const mouse = new Mouse(engine);
 // window.mouse = mouse;
 
-const scale = (query.get('debug') ? 4 : 1);
-canvas.width = 256 * scale;
-canvas.height = 240 * scale;
-canvas.pixelRatio = scale;
-canvas.ctx.setTransform(canvas.pixelRatio, 0, 0, canvas.pixelRatio, 0, 0);
+engine.debug = query.get`debug`;
+window.debug = engine.debug;
+
+canvas.width = 256;
+canvas.height = 240;
+
+canvas.setScale();
 canvas.ctx.imageSmoothingEnabled = false;
 
 engine.running = false; // Game uses this as a pause state actually
 
+// // Prerender the stars
+// const tempCanvasStars = document.createElement("canvas");
+// tempCanvasStars.width = 2560;
+// tempCanvasStars.height = 2400;
+// const stars = new Stars(tempCanvasStars);
+// window.starsTest = stars;
+// stars.draw(0, 0, 2560, 2400);
+// const createStarsObjectURL = blob => {
+//     assets.addImage('stars', URL.createObjectURL(blob));
+// }
+// tempCanvasStars.toBlob(createStarsObjectURL);
+
+assets.addMiniSprite('grass', 'IIIIIIQIQQQJZQZZRZRRZRZRSSRSRZZR');
+assets.renderMiniSprite('grass', ['#2a6', '#964', '#853']);
+
 engine.registerRoom(rm_mainMenu, 'mainMenu');
 engine.registerRoom(rm_game, 'game');
 
@@ -56,11 +77,13 @@ engine.registerRoom(rm_game, 'game');
 // engine.registerRoom(rm_DEBUG_music, 'debug_music');
 engine.registerRoom(rm_DEBUG_INCURSION, 'debug_incursion');
 engine.registerRoom(rm_DEBUG_text, 'debug_text');
+engine.registerRoom(rm_DEBUG_stars, 'debug_stars');
+engine.registerRoom(rm_DEBUG_sprites, 'debug_sprites');
 
 // Init stats.js
-// const stats = new Stats();
-// stats.showPanel(0);
-// document.body.appendChild( stats.dom );
+const stats = new Stats();
+stats.showPanel(0);
+document.body.appendChild( stats.dom );
 
 let physicsFrame=0;
 window.physicsFrame = physicsFrame;
@@ -71,7 +94,7 @@ function main() {
     // Draw things. no user interaction or physics here.
 
     try {
-        // stats.begin();
+        stats.begin();
         engine.frames++;
         canvas.fill(engine.room.bgColor ?? 'black');
 
@@ -80,12 +103,13 @@ function main() {
 
         // engine.drawCursor();
 
-        // stats.end();
+        stats.end();
 
-        if (query.get('debug')) {
-            canvas.drawText(`physics ticks: ${physicsFrame} (~${(physicsFrame/60).toFixed(1)}sec)`, 0, 0,{textBaseline:'top'})
-            canvas.drawText(`frames: ${engine.frames} (~${(engine.frames/(physicsFrame/60)).toFixed(1)}FPS <--flawed)`, 0, 8,{textBaseline:'top'})
+        if (debug) {
+            canvas.drawText(`physics ticks: ${engine.physicsFrames} (~${(engine.physicsFrames/60).toFixed(1)}sec)`, 0, 0,{textBaseline:'top'})
+            canvas.drawText(`frames: ${engine.frames}`, 0, 8,{textBaseline:'top'})
             canvas.drawText(`run time: ${((performance.now()-readyTime)/1000).toFixed(1)}sec`, 0, 16, {textBaseline:'top'})
+            canvas.drawText(`keys: ${JSON.stringify(keyboard.keys)}`, 0, 24, {textBaseline:'top'})
         }
 
         // Ask to run at the next frame
@@ -97,10 +121,10 @@ function main() {
         canvas.ctx.setTransform(canvas.pixelRatio, 0, 0, canvas.pixelRatio, 0, 0);
         canvas.fill('#a05555d0');
 
-        const logo = assets.get('splash')
+        const logo = assets.get`splash`
         canvas.drawImage(logo, 10, 10, logo.width, logo.height)
 
-        canvas.setFillColor('black');
+        canvas.setFillColor`black`;
         canvas.drawText(e, 5, canvas.height-5, {
             maxWidth: canvas.width-10,
             textBaseline: 'bottom'
@@ -112,19 +136,23 @@ function main() {
 
 function physicsTick() {
     // Runs 60 times a second regardless of frame rate.
-    physicsFrame++;
+    engine.physicsFrames++;
+    engine.lastPhysicsFrame = performance.now();
 
     engine.room.step();
+
+    keyboard.keysThisFrame = [];
+    keyboard.keysUpThisFrame = [];
 }
 
 console.debug(engine.rooms);
 
-if (query.get('room')) {
-    console.log('Requesting room', query.get('room'));
+if (query.get`room`) {
+    console.log('Requesting room', query.get`room`);
     engine.loadDelay = 0;
-    engine.room = engine.getRoomIndex(query.get('room'));
+    engine.room = engine.getRoomIndex(query.get`room`);
 } else {
-    engine.room = engine.getRoomIndex('mainMenu');
+    engine.room = engine.getRoomIndex`mainMenu`;
 }
 
 // Ensure assets are loaded.
@@ -134,8 +162,9 @@ function load() {
         setTimeout(load, 1000/60);
     } else {
         readyTime = performance.now();
+        engine.lastPhysicsFrame = performance.now();
         main();
-        setInterval(physicsTick, 1000/60); // Update physics 60 times a second
+        setInterval(physicsTick, (query.get`slowdown` ? 500 : 1000/60)); // Update physics 60 times a second
     }
 }
 
diff --git a/src/js/objects.js b/src/js/objects.js
index 7876278..e4054d1 100644
--- a/src/js/objects.js
+++ b/src/js/objects.js
@@ -1,6 +1,8 @@
 import {Entity} from "../../hampsterengine/src/things";
 import ButtonBorder from "../img/button.webp";
 import {rm_game} from "./rooms/game";
+import {mulberry32, round, roundToRatio} from "./extras";
+import Canvas from "../../hampsterengine/src/canvas";
 
 // Private
 
@@ -127,6 +129,42 @@ export class Logo extends Entity {
 
 }
 
+export class Stars {
+    constructor(canvas='c', seed=1522363656) {
+        // Set seed to (Math.random()*2**32)>>>0 for a random seed every time
+        this.seed = seed;
+        this.canvas = new Canvas(canvas);
+
+        this.stars = [];
+    }
+
+    init(w, h) {
+        // Split the draw area into 2*2 squares
+        const gridW = Math.floor(w / 2);
+        const gridH = Math.floor(h / 2);
+
+        for (let ww = 0; ww <= gridW; ww++) {
+            for (let hh = 0; hh <= gridH; hh++) {
+                const offsetX = (ww*2) + (Math.ceil(mulberry32(this.seed - ww * hh)()*10) - 10);
+                const offsetY = (hh*2) + (Math.ceil(mulberry32(this.seed + hh)()*10) - 10);
+                if (mulberry32(this.seed * ww + hh)() > 0.99) this.stars.push({x: offsetX, y: offsetY});
+            }
+        }
+    }
+
+    draw(x, y, w, h) {
+        if (this.stars.length === 0) this.init(w, h);
+
+        x = roundToRatio(x);
+        y = roundToRatio(y);
+
+        canvas.setFillColor('rgba(255,255,255,0.3)');
+        for (const star of this.stars) {
+            if (star.x + x < canvas.width) this.canvas.fillRect(x + star.x, y + star.y, 2, 2);
+        }
+    }
+}
+
 export class FontRenderer {
     constructor(font) {
         this.font = font;
diff --git a/src/js/objects/player.js b/src/js/objects/player.js
index 15c4469..3aefe7e 100644
--- a/src/js/objects/player.js
+++ b/src/js/objects/player.js
@@ -1,23 +1,20 @@
 import {Entity} from "../../../hampsterengine/src/things";
+import {round, roundToRatio} from "../extras";
 
 export default class Player extends Entity {
     constructor(props) {
         super(props);
+
+        this.jumping = false;
     }
 
     draw() {
         canvas.setFillColor('red');
-        canvas.fillRect(this.x,this.y,10,16);
-    }
-
-    checkCollision(other) {
-        // Checks if colliding with another entity. Returns true if colliding.
-
-        return (
-            this.left < other.right &&
-            this.right > other.left &&
-            this.top < other.bottom &&
-            this.bottom > other.top
-        );
+        const timeSinceLastFrame = (performance.now() - engine.lastPhysicsFrame) / 1000 / (query.get('slowdown') ? 60 : 1);
+        const interpolationX = this.vx * timeSinceLastFrame;
+        const interpolationY = this.vy * timeSinceLastFrame;
+        // const interpolationX = 0;
+        // const interpolationY = 0;
+        canvas.fillRect(roundToRatio(this.x+interpolationX),roundToRatio(this.y+interpolationY),this.width,this.height);
     }
 }
diff --git a/src/js/rooms/debug_sprites.js b/src/js/rooms/debug_sprites.js
new file mode 100644
index 0000000..a59d14e
--- /dev/null
+++ b/src/js/rooms/debug_sprites.js
@@ -0,0 +1,12 @@
+
+import {Room} from "../../../hampsterengine/src/things";
+
+export const rm_DEBUG_sprites = new Room();
+rm_DEBUG_sprites.start = _=> {
+    canvas.setScale(3);
+    canvas.ctx.imageSmoothingEnabled = false;
+}
+
+rm_DEBUG_sprites.draw = _=> {
+    canvas.tileImage(engine.assetStore.get('grass').sprite, 0, 0, canvas.width, 8, 8, 8);
+}
diff --git a/src/js/rooms/debug_stars.js b/src/js/rooms/debug_stars.js
new file mode 100644
index 0000000..938a7b6
--- /dev/null
+++ b/src/js/rooms/debug_stars.js
@@ -0,0 +1,11 @@
+import {Room} from "../../../hampsterengine/src/things";
+import {Stars} from "../objects";
+
+export const rm_DEBUG_stars = new Room();
+rm_DEBUG_stars.bgColor = 'black';
+
+rm_DEBUG_stars.draw = _ => {
+    stars.draw(0, 0, canvas.width, canvas.height);
+}
+
+const stars = new Stars();
diff --git a/src/js/rooms/game.js b/src/js/rooms/game.js
index 85c4a12..a1be296 100644
--- a/src/js/rooms/game.js
+++ b/src/js/rooms/game.js
@@ -1,11 +1,11 @@
 
 import {Entity, Room} from "../../../hampsterengine/src/things";
 import Player from "../objects/player";
-import {clone, clonePlayer} from "../extras";
+import {clone, clonePlayer, abs} from "../extras";
 
 export const rm_game = new Room();
 const GRAVITY_X = 0; // I don't think we're going to use X gravity but i'm going to keep in the source in case i do
-const GRAVITY_Y = 150; // Per second
+const GRAVITY_Y = 300; // Per second
 const entities = rm_game.entities;
 
 
@@ -20,36 +20,97 @@ rm_game.stop = _=>{
 rm_game.step = _=>{
     const elapsed = 1 / 60;
 
-    // Clone the player so we can put them back after the first check
-    const old_player = {...player};
-    const entitiesWithoutThePlayer = [...entities].slice(entities.indexOf(player));
+    let friction = 0.9;
+    const boost = keyboard.keys.includes("Shift") ? 40 : 0;
 
-    // Accelerate the player towards GRAVITY_Y
-    player.y += GRAVITY_Y * elapsed;
+    for (const key of keyboard.keys) {
+        switch (key) {
+            case "ArrowLeft":
+                player.vx = -80-boost;
+                break;
+            case "ArrowRight":
+                player.vx = 80+boost;
+                break;
+            case " ":
+                if (!player.jumping) {
+                    player.jumping = true;
+                    player.vy -= 150;
+                }
+        }
+    }
+
+    const entitiesWithoutThePlayer = [...entities].toSpliced(entities.indexOf(player), 1);
+    // console.debug(entitiesWithoutThePlayer);
+
+    player.vx = Math.min(500, player.vx + (player.ax * elapsed + GRAVITY_X*elapsed));
+    player.vy = Math.min(500, player.vy + (player.ay * elapsed + GRAVITY_Y*elapsed));
+    player.x += player.vx * elapsed;
+    player.y += player.vy * elapsed;
+
+    // Make acceleration decay
+    player.ax *= 0.1;
+    player.ay *= 0.1;
 
-    if (player.checkCollision(floor)) {
-        player.y = floor.top - player.height;
+    if (abs(player.ax) < 0.01) player.ax = 0;
+    if (abs(player.ay) < 0.01) player.ay = 0;
+
+    rm_game.x = player.x;
+    rm_game.y = player.y;
+
+    for (const entity of entitiesWithoutThePlayer) {
+        if (player.checkCollision(entity)) {
+            friction = 0.8;
+            let side = player.resolveCollision(entity);
+            if (side === 2) player.jumping = 0;
+            if (side === 1 || side === 3) friction = 0.5;
+        }
     }
+
+    // player.vy *= friction;
+    player.vx *= friction;
+
+    if (abs(player.vy) < 1) player.vy = 0;
+    if (abs(player.vx) < 1) player.vx = 0;
 }
 
 rm_game.drawGui = _ => {
     // Draw the player's position
     canvas.setFillColor('black');
-    canvas.drawText(`(${player.x},${player.y})`, 10, canvas.height-10, {
+    canvas.drawText(`Position: (${player.x},${player.y})`, 10, canvas.height-10, {
         maxWidth: canvas.width-20
     });
+    canvas.drawText(`Velocity: (${player.vx},${player.vy})`, 10, canvas.height-18, {
+        maxWidth: canvas.width-20
+    });
+    canvas.drawText(`Acceleration: (${player.ax},${player.ay})`, 10, canvas.height-26, {
+        maxWidth: canvas.width-20
+    });
+
+    // Draw the player's position
+    canvas.strokeRect(rm_game.x, rm_game.y, 10, 16);
 }
 
 let player = new Player();
+
 player.x = 40;
 player.y = 40;
+player.width = 10;
+player.height = 16;
 window.player = player;
 entities.push(player);
 
 let floor = new Entity();
-floor.x = 0;
+floor.x = 64;
 floor.y = 150;
-floor.width = 256;
+floor.width = 128;
 floor.height = 50;
 floor.draw = _=> { canvas.setStrokeColor('black'); canvas.strokeRect(floor.x, floor.y, floor.width, floor.height) }
 rm_game.entities.push(floor);
+
+let ceiling = new Entity();
+ceiling.x = 64;
+ceiling.y = 75;
+ceiling.width = 128;
+ceiling.height = 50;
+ceiling.draw = _=> { canvas.setStrokeColor('black'); canvas.strokeRect(ceiling.x, ceiling.y, ceiling.width, ceiling.height) }
+rm_game.entities.push(ceiling);
diff --git a/src/js/rooms/mainMenu.js b/src/js/rooms/mainMenu.js
index 507c62d..23ade9f 100644
--- a/src/js/rooms/mainMenu.js
+++ b/src/js/rooms/mainMenu.js
@@ -1,5 +1,5 @@
 import {Room} from "../../../hampsterengine/src/things";
-import {Logo, MainMenuButton} from "../objects";
+import {Logo, MainMenuButton, Stars} from "../objects";
 
 export const rm_mainMenu = new Room();
 rm_mainMenu.bgColor = '#050911';
@@ -10,10 +10,26 @@ rm_mainMenu.bgColor = '#050911';
 // logo.align = 2
 // rm_mainMenu.entities.push(logo);
 
-rm_mainMenu.flasher = !0;
+const stars = new Stars();
 
 rm_mainMenu.step = _=>{
-    if (physicsFrame % 30) rm_mainMenu.flasher = !rm_mainMenu.flasher;
+    for (const key of keyboard.keysThisFrame) {
+        switch (key.toLowerCase()) {
+            case "enter":
+                engine.room = engine.getRoomIndex("game");
+                break;
+            case "o":
+                alert("there are none!");
+                break;
+        }
+    }
+}
+
+rm_mainMenu.draw = _=> {
+    // canvas.tempFilter(function () {
+    //     canvas.drawImage(engine.assetStore.get('stars'), 0, 0, 2560, 2400);
+    // }, "invert(1) opacity(0.3)");
+    stars.draw(-(engine.frames/500), 0, 2560, 240)
 }
 
 rm_mainMenu.drawGui = _ => {
@@ -22,7 +38,9 @@ rm_mainMenu.drawGui = _ => {
     fR.invert = 1;
     fR.draw("Thirteen minutes to space", 30, 8*4);
 
-    if(rm_mainMenu.flasher) fR.draw("Press [ENTER] to start.", 30, canvas.height-(8*8));
+    fR.draw("Press [ENTER] to start.", 30, canvas.height-(8*8));
+    fR.draw("Press [O] for options.", 30, canvas.height-(8*6));
     fR.draw("created by bye", 30, canvas.height-(8*4));
     fR.invert = 0;
-}
\ No newline at end of file
+}
+
diff --git a/utils/size.js b/utils/size.js
index 115f797..b7fe8a6 100644
--- a/utils/size.js
+++ b/utils/size.js
@@ -4,4 +4,11 @@ var stats = fs.statSync("build/game.zip");
 var fileSizeInBytes = stats.size;
 var fileSizeInKilobytes = fileSizeInBytes / 1024;
 
+var statsUncompressed = fs.statSync("build/index.html");
+var fileSizeInBytesUncompressed = statsUncompressed.size;
+var fileSizeInKilobytesUncompressed = fileSizeInBytesUncompressed / 1024;
+
+var compressionRatio = fileSizeInBytesUncompressed / fileSizeInBytes;
+
 console.log(`Current game size: ${Math.round(fileSizeInKilobytes*10)/10}/13.0 KB (${Math.round((fileSizeInKilobytes/13)*100)}%)`);
+console.log(`Current game size (uncompressed): ${fileSizeInKilobytesUncompressed.toFixed(1)}/${(13.0 * compressionRatio).toFixed(1)} KB (${Math.round((fileSizeInKilobytesUncompressed/(13 * compressionRatio))*100)}%)`)
diff --git a/utils/sprite_editor/index.html b/utils/sprite_editor/index.html
index 613f182..02bb5ee 100644
--- a/utils/sprite_editor/index.html
+++ b/utils/sprite_editor/index.html
@@ -2,15 +2,18 @@
 <html lang="en">
 <head>
     <meta charset="UTF-8">
-    <title>Sprite editor</title>
+    <title>Sprite checker</title>
 </head>
 <body>
+
+    <p>using code generated with https://xem.github.io/miniPixelArt/</p>
+
     <canvas id=a>
     <script>
         ctx=a.getContext`2d` // ctx
         pallette="000f000f000fff00fff0f" // color palette (you can remove the colors you didn't use to save bytes)
         pixels=[];
-        "@@@@HHA@@A@@HIIAACCHQCSHARBHHIIA".replace(/./g,
+        "IIIIIIQIQQQJZQZZRZRRZRZRSSRSRZZR".replace(/./g,
             character=>{
                 characterCode=character.charCodeAt(),
                     pixels.push(characterCode&7), // Gets the last three bits
-- 
GitLab