diff --git a/LICENSE.md b/LICENSE.md index 0c17b703..474d0e2d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,24 +1,35 @@ Copyright (c) 2025, KAPLAY Team and contributers -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. In addition, the following restrictions apply: -1. The Software and any modifications made to it may not be used for the purpose of training or improving machine learning algorithms, -including but not limited to artificial intelligence, natural language processing, or data mining. This condition applies to any derivatives, -modifications, or updates based on the Software code. Any usage of the Software in an AI-training dataset is considered a breach of this License. +1. The Software and any modifications made to it may not be used for the purpose + of training or improving machine learning algorithms, including but not + limited to artificial intelligence, natural language processing, or data + mining. This condition applies to any derivatives, modifications, or updates + based on the Software code. Any usage of the Software in an AI-training + dataset is considered a breach of this License. -2. The Software may not be included in any dataset used for training or improving machine learning algorithms, -including but not limited to artificial intelligence, natural language processing, or data mining. +2. The Software may not be included in any dataset used for training or + improving machine learning algorithms, including but not limited to + artificial intelligence, natural language processing, or data mining. -3. Any person or organization found to be in violation of these restrictions will be subject to legal action and may be held liable -for any damages resulting from such use. +3. Any person or organization found to be in violation of these restrictions + will be subject to legal action and may be held liable for any damages + resulting from such use. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index eb6a47cd..9844cb73 100644 --- a/README.md +++ b/README.md @@ -184,12 +184,15 @@ Collections of games made with KAPLAY (and Kaboom), selected by KAPLAY: - [Newgrounds.com](https://www.newgrounds.com/playlist/379920/kaplay-games) ## Credits + - Thanks to [tga](https://space55.xyz) for all his work on the original Kaboom.js - Thanks to [mulfok](https://twitter.com/MulfoK) for the awesome [mulfok32](https://lospec.com/palette-list/mulfok32) color palette, used in KAPLAY sprites and art -- Thanks to [Pixabay](https://pixabay.com/users/pixabay-1/) for the great [burp](https://pixabay.com/sound-effects/burp-104984/) sound, used in `burp()` function +- Thanks to [Pixabay](https://pixabay.com/users/pixabay-1/) for the great + [burp](https://pixabay.com/sound-effects/burp-104984/) sound, used in `burp()` + function - Thansk to [Kenney](https://kenney.nl/) for all used assets for examples - [Impact Sound Pack](https://kenney.nl/assets/impact-sounds) - [1-Bit Platformer Pack](https://kenney.nl/assets/1-bit-platformer-pack) diff --git a/examples/animation.js b/examples/animation.js index 49f47ac1..6a22a74c 100644 --- a/examples/animation.js +++ b/examples/animation.js @@ -26,10 +26,14 @@ rotatingBean.animate("angle", [0, 360], { onClick(() => { rotatingBean.animation.seek(0); -}) +}); -rotatingBean.onAnimateChannelFinished(name => { debug.log(`${name} finished`) }) -rotatingBean.onAnimateFinished(() => { debug.log(`All finished`) }) +rotatingBean.onAnimateChannelFinished(name => { + debug.log(`${name} finished`); +}); +rotatingBean.onAnimateFinished(() => { + debug.log(`All finished`); +}); // Now we'll move this bean from left to right const movingBean = add([ @@ -48,7 +52,7 @@ movingBean.animate("pos", [vec2(50, 150), vec2(150, 150)], { onClick(() => { movingBean.animation.paused = !movingBean.animation.paused; -}) +}); // Same animation as before, but relative to the spawn position const secondMovingBean = add([ @@ -149,7 +153,7 @@ timedSquaringBean.animate( }, ); -// We'll move this bean in a curve +// We'll move this bean in a curve // Using spline interpolation to move according to a smoothened path const curvingBean = add([ sprite("bean"), diff --git a/examples/binding.js b/examples/binding.js index 6a49d9b4..8a346289 100644 --- a/examples/binding.js +++ b/examples/binding.js @@ -2,68 +2,68 @@ // You can set the input bindings for your game! kaplay({ - buttons: { - // Buttons for jumping - "jump": { - // When using a gamepad the button for jumping will be south - gamepad: ["south"], - // When using a keyboard the button will be "up" or "w" - keyboard: ["up", "w"], - // When using a mouse the button will be "left" - mouse: "left", - }, - // Buttons for inspecting - "inspect": { - gamepad: "east", - keyboard: "f", - mouse: "right", - }, - }, + buttons: { + // Buttons for jumping + "jump": { + // When using a gamepad the button for jumping will be south + gamepad: ["south"], + // When using a keyboard the button will be "up" or "w" + keyboard: ["up", "w"], + // When using a mouse the button will be "left" + mouse: "left", + }, + // Buttons for inspecting + "inspect": { + gamepad: "east", + keyboard: "f", + mouse: "right", + }, + }, }); -loadBean() +loadBean(); // Set the gravity acceleration (pixels per second) setGravity(1600); // Add player game object const player = add([ - sprite("bean"), - pos(center()), - area(), - // body() component gives the ability to respond to gravity - body(), + sprite("bean"), + pos(center()), + area(), + // body() component gives the ability to respond to gravity + body(), ]); // Add a platform to hold the player add([ - rect(width(), 48), - outline(4), - area(), - pos(0, height() - 48), - // Give objects a body() component if you don't want other solid objects pass through - body({ isStatic: true }), + rect(width(), 48), + outline(4), + area(), + pos(0, height() - 48), + // Give objects a body() component if you don't want other solid objects pass through + body({ isStatic: true }), ]); // Adds an object with a text add([ - text("Press jump button", { width: width() / 2 }), - pos(12, 12), + text("Press jump button", { width: width() / 2 }), + pos(12, 12), ]); // This runs when the button for "jump" is pressed (will be on any input device) onButtonPress("jump", () => { - // You can get the type of device that the last input was inputted in! - debug.log(getLastInputDeviceType()); + // You can get the type of device that the last input was inputted in! + debug.log(getLastInputDeviceType()); - // Now we'll check if the player is on the ground to make it jump - if (player.isGrounded()) { - // .jump() is provided by body() - player.jump(); - } + // Now we'll check if the player is on the ground to make it jump + if (player.isGrounded()) { + // .jump() is provided by body() + player.jump(); + } }); // When the button for inspecting is pressed we will log in the debug console for our game the text "inspecting" onButtonDown("inspect", () => { - debug.log("inspecting"); + debug.log("inspecting"); }); diff --git a/examples/button.js b/examples/button.js index cb0a71f3..0884a833 100644 --- a/examples/button.js +++ b/examples/button.js @@ -10,7 +10,11 @@ kaplay({ onUpdate(() => setCursor("default")); // Function that adds a button to the game with a given text, position and function -function addButton(txt = "start game", p = vec2(200, 100), f = () => debug.log("hello")) { +function addButton( + txt = "start game", + p = vec2(200, 100), + f = () => debug.log("hello"), +) { // add a parent background object const btn = add([ rect(240, 80, { radius: 8 }), diff --git a/examples/camera.js b/examples/camera.js index 96195645..e7a0d237 100644 --- a/examples/camera.js +++ b/examples/camera.js @@ -103,4 +103,4 @@ onClick(() => { // Use toWorld() to transform a screen-space coordinate (like mousePos()) to // the world-space coordinate, which has the camera transform applied addKaboom(toWorld(mousePos())); -}); \ No newline at end of file +}); diff --git a/examples/collision.js b/examples/collision.js index cd83c473..895386b2 100644 --- a/examples/collision.js +++ b/examples/collision.js @@ -121,4 +121,4 @@ player.onUpdate(() => { // Can also be toggled by pressing F1 debug.inspect = true; -// Check out https://kaplayjs.com/doc/AreaComp/ for everything area() provides \ No newline at end of file +// Check out https://kaplayjs.com/doc/AreaComp/ for everything area() provides diff --git a/examples/component.js b/examples/component.js index b1d11a3a..13dd67eb 100644 --- a/examples/component.js +++ b/examples/component.js @@ -8,113 +8,113 @@ loadBean(); // Components are just function that returns a js object that follows a certain format // This object contains certain properties which then become available in your object to use function funky() { - // Can use local closed variables to store component state - let isFunky = false; - - return { - // ------------------ - // Special properties that controls the behavior of the component (all optional) - - // These properties (id and require specially id) are handled by kaplay, id is the name of the component - // If you want to get all objects with this component you can do get("funky") - // Be careful to tag objects with what might be the id of a component - - id: "funky", // The name of the component - require: ["scale", "color"], // If this component depend on any other components - // If the you put components in require and attach this component to an object that doesn't have these components - // The game will throw an error - - // Runs when the host object is added to the game - add() { - // E.g. Register some events from other components, do some bookkeeping, etc. - }, - - // Runs every frame as long as the host object exists - update() { - if (!isFunky) return; - - // "this" in all component methods refers to the the game object this component is attached to - // Here we're updating some properties provided by other components - this.color = rgb(rand(0, 255), rand(0, 255), rand(0, 255)); - this.scale = vec2(rand(1, 2)); - }, - - // Runs every frame (after update) as long as the host object exists - draw() { - // E.g. Custom drawXXX() operations. - }, - - // Runs when the host object is destroyed - destroy() { - // E.g. Clean up event handlers, etc. - }, - - // When you press F1 you can get a list of inspect properties a component might provide for an object - // Here you can provide custom ones - inspect() { - return "funky: " + isFunky; - }, - - // ------------------ - // All other properties and methods are directly assigned to the host object - - // This means that the object is getting funky, not that you're getting the property funky lol! - getFunky() { - isFunky = true; - }, - }; + // Can use local closed variables to store component state + let isFunky = false; + + return { + // ------------------ + // Special properties that controls the behavior of the component (all optional) + + // These properties (id and require specially id) are handled by kaplay, id is the name of the component + // If you want to get all objects with this component you can do get("funky") + // Be careful to tag objects with what might be the id of a component + + id: "funky", // The name of the component + require: ["scale", "color"], // If this component depend on any other components + // If the you put components in require and attach this component to an object that doesn't have these components + // The game will throw an error + + // Runs when the host object is added to the game + add() { + // E.g. Register some events from other components, do some bookkeeping, etc. + }, + + // Runs every frame as long as the host object exists + update() { + if (!isFunky) return; + + // "this" in all component methods refers to the the game object this component is attached to + // Here we're updating some properties provided by other components + this.color = rgb(rand(0, 255), rand(0, 255), rand(0, 255)); + this.scale = vec2(rand(1, 2)); + }, + + // Runs every frame (after update) as long as the host object exists + draw() { + // E.g. Custom drawXXX() operations. + }, + + // Runs when the host object is destroyed + destroy() { + // E.g. Clean up event handlers, etc. + }, + + // When you press F1 you can get a list of inspect properties a component might provide for an object + // Here you can provide custom ones + inspect() { + return "funky: " + isFunky; + }, + + // ------------------ + // All other properties and methods are directly assigned to the host object + + // This means that the object is getting funky, not that you're getting the property funky lol! + getFunky() { + isFunky = true; + }, + }; } // Adds an object with the funky component const bean = add([ - sprite("bean"), - pos(center()), - anchor("center"), - scale(1), - color(), - area(), - // Use our component here - funky(), - // Tags are empty components, it's equivalent to a { id: "friend" } - "friend", - // Plain objects here are components too and work the same way, except unnamed - { - coolness: 100, - friends: [], - }, + sprite("bean"), + pos(center()), + anchor("center"), + scale(1), + color(), + area(), + // Use our component here + funky(), + // Tags are empty components, it's equivalent to a { id: "friend" } + "friend", + // Plain objects here are components too and work the same way, except unnamed + { + coolness: 100, + friends: [], + }, ]); onKeyPress("space", () => { - // .coolness is from our plain object 'unnamed component' - if (bean.coolness >= 100) { - // We can use .getFunky() provided by the funky() component now - if (!bean.has("funky")) { - bean.use(funky()); + // .coolness is from our plain object 'unnamed component' + if (bean.coolness >= 100) { + // We can use .getFunky() provided by the funky() component now + if (!bean.has("funky")) { + bean.use(funky()); + } + bean.getFunky(); + debug.log(`Funky`); } - bean.getFunky(); - debug.log(`Funky`); - } }); onKeyPress("r", () => { - // .use() is on every game object, it adds a component at runtime - bean.use(rotate(rand(0, 360))); + // .use() is on every game object, it adds a component at runtime + bean.use(rotate(rand(0, 360))); }); onKeyPress("escape", () => { - // .unuse() removes a component from the game object - // The tag is the one that appears on the id - bean.unuse("funky"); - debug.log(`Not funky`); + // .unuse() removes a component from the game object + // The tag is the one that appears on the id + bean.unuse("funky"); + debug.log(`Not funky`); }); // Adds a text object add([text("Press space to get funky", { width: width() }), pos(12, 12)]); bean.onUse((id) => { - debug.log(`Component ${id} added`); + debug.log(`Component ${id} added`); }); bean.onUnuse((id) => { - debug.log(`Component ${id} removed`); + debug.log(`Component ${id} removed`); }); diff --git a/examples/doublejump.js b/examples/doublejump.js index dd34b8d9..2815242a 100644 --- a/examples/doublejump.js +++ b/examples/doublejump.js @@ -2,7 +2,7 @@ // How to use the doubleJump component in this little game kaplay({ - background: [141, 183, 255], + background: [141, 183, 255], }); // Loads sprites @@ -21,200 +21,200 @@ const NUM_PLATFORMS = 5; // a spinning component for fun, for more info check the 'component' example function spin(speed = 1200) { - let spinning = false; - return { - require: ["rotate"], - update() { - if (!spinning) { - return; - } - this.angle -= speed * dt(); - if (this.angle <= -360) { - spinning = false; - this.angle = 0; - } - }, - spin() { - spinning = true; - }, - }; + let spinning = false; + return { + require: ["rotate"], + update() { + if (!spinning) { + return; + } + this.angle -= speed * dt(); + if (this.angle <= -360) { + spinning = false; + this.angle = 0; + } + }, + spin() { + spinning = true; + }, + }; } // Setsup the game scene scene("game", () => { - // This score textObject holds a value property in a plain object - const score = add([ - text("0", { size: 24 }), - pos(24, 24), - { value: 0 }, - ]); - - const bean = add([ - sprite("bean"), - area(), - anchor("center"), - pos(0, 0), - body({ jumpForce: JUMP_FORCE }), - // Adds the double jump component - doubleJump(), - rotate(0), - spin(), - ]); - - // Adds a num of platforms that go from left to right - for (let i = 1; i < NUM_PLATFORMS; i++) { - add([ - sprite("grass"), - area(), - pos(rand(0, width()), i * height() / NUM_PLATFORMS), - body({ isStatic: true }), - anchor("center"), - "platform", - { - speed: rand(120, 320), - dir: choose([-1, 1]), - }, - ]); - } - - // go to the first platform - bean.pos = get("platform")[0].pos.sub(0, 64); - - // Generates coins on those platforms - function genCoin(avoid) { - const plats = get("platform"); - let idx = randi(0, plats.length); - // avoid the spawning on the same platforms - if (avoid != null) { - idx = choose([...plats.keys()].filter((i) => i !== avoid)); - } - const plat = plats[idx]; - add([ - pos(), - anchor("center"), - sprite("coin"), - area(), - follow(plat, vec2(0, -60)), - "coin", - { idx: idx }, - ]); - } - - genCoin(0); - - for (let i = 0; i < width() / 64; i++) { - add([ - pos(i * 64, height()), - sprite("spike"), - area(), - anchor("bot"), - scale(), - "danger", - ]); - } - - bean.onCollide("danger", () => { - go("lose"); - }); - - bean.onCollide("coin", (c) => { - destroy(c); - play("coin"); - score.value += 1; - score.text = score.value.toString(); - genCoin(c.idx); - }); - - // The double jupm component provides us this function that runs when we double jump - bean.onDoubleJump(() => { - // So we can call the spin() method provided by the spin() component to spin - bean.spin(); - }); - - onUpdate("platform", (p) => { - p.move(p.dir * p.speed, 0); - if (p.pos.x < 0 || p.pos.x > width()) { - p.dir = -p.dir; - } - }); - - onKeyPress("space", () => { - bean.doubleJump(); - }); - - // Will move the bean left and right - function move(x) { - bean.move(x, 0); - if (bean.pos.x < 0) { - bean.pos.x = width(); - } - else if (bean.pos.x > width()) { - bean.pos.x = 0; - } - } - - // both keys will trigger - onKeyDown("left", () => { - move(-PLAYER_SPEED); - }); - - onKeyDown("right", () => { - move(PLAYER_SPEED); - }); - - // The south button will call the doubleJump, for more info on gamepads check the 'gamepad' example - onGamepadButtonPress("south", () => bean.doubleJump()); - - onGamepadStick("left", (v) => { - move(v.x * PLAYER_SPEED); - }); - - let timeLeft = 30; - - const timer = add([ - anchor("topright"), - pos(width() - 24, 24), - text(timeLeft.toString()), - ]); - - onUpdate(() => { - timeLeft -= dt(); - if (timeLeft <= 0) { - go("win", score.value); - } - timer.text = timeLeft.toFixed(2); - }); + // This score textObject holds a value property in a plain object + const score = add([ + text("0", { size: 24 }), + pos(24, 24), + { value: 0 }, + ]); + + const bean = add([ + sprite("bean"), + area(), + anchor("center"), + pos(0, 0), + body({ jumpForce: JUMP_FORCE }), + // Adds the double jump component + doubleJump(), + rotate(0), + spin(), + ]); + + // Adds a num of platforms that go from left to right + for (let i = 1; i < NUM_PLATFORMS; i++) { + add([ + sprite("grass"), + area(), + pos(rand(0, width()), i * height() / NUM_PLATFORMS), + body({ isStatic: true }), + anchor("center"), + "platform", + { + speed: rand(120, 320), + dir: choose([-1, 1]), + }, + ]); + } + + // go to the first platform + bean.pos = get("platform")[0].pos.sub(0, 64); + + // Generates coins on those platforms + function genCoin(avoid) { + const plats = get("platform"); + let idx = randi(0, plats.length); + // avoid the spawning on the same platforms + if (avoid != null) { + idx = choose([...plats.keys()].filter((i) => i !== avoid)); + } + const plat = plats[idx]; + add([ + pos(), + anchor("center"), + sprite("coin"), + area(), + follow(plat, vec2(0, -60)), + "coin", + { idx: idx }, + ]); + } + + genCoin(0); + + for (let i = 0; i < width() / 64; i++) { + add([ + pos(i * 64, height()), + sprite("spike"), + area(), + anchor("bot"), + scale(), + "danger", + ]); + } + + bean.onCollide("danger", () => { + go("lose"); + }); + + bean.onCollide("coin", (c) => { + destroy(c); + play("coin"); + score.value += 1; + score.text = score.value.toString(); + genCoin(c.idx); + }); + + // The double jupm component provides us this function that runs when we double jump + bean.onDoubleJump(() => { + // So we can call the spin() method provided by the spin() component to spin + bean.spin(); + }); + + onUpdate("platform", (p) => { + p.move(p.dir * p.speed, 0); + if (p.pos.x < 0 || p.pos.x > width()) { + p.dir = -p.dir; + } + }); + + onKeyPress("space", () => { + bean.doubleJump(); + }); + + // Will move the bean left and right + function move(x) { + bean.move(x, 0); + if (bean.pos.x < 0) { + bean.pos.x = width(); + } + else if (bean.pos.x > width()) { + bean.pos.x = 0; + } + } + + // both keys will trigger + onKeyDown("left", () => { + move(-PLAYER_SPEED); + }); + + onKeyDown("right", () => { + move(PLAYER_SPEED); + }); + + // The south button will call the doubleJump, for more info on gamepads check the 'gamepad' example + onGamepadButtonPress("south", () => bean.doubleJump()); + + onGamepadStick("left", (v) => { + move(v.x * PLAYER_SPEED); + }); + + let timeLeft = 30; + + const timer = add([ + anchor("topright"), + pos(width() - 24, 24), + text(timeLeft.toString()), + ]); + + onUpdate(() => { + timeLeft -= dt(); + if (timeLeft <= 0) { + go("win", score.value); + } + timer.text = timeLeft.toFixed(2); + }); }); // Sets up the scene where we win scene("win", (score) => { - add([ - sprite("bean"), - pos(width() / 2, height() / 2 - 80), - scale(2), - anchor("center"), - ]); - - // display score - add([ - text(score), - pos(width() / 2, height() / 2 + 80), - scale(2), - anchor("center"), - ]); - - // go back to game with space is pressed - onKeyPress("space", () => go("game")); - onGamepadButtonPress("south", () => go("game")); + add([ + sprite("bean"), + pos(width() / 2, height() / 2 - 80), + scale(2), + anchor("center"), + ]); + + // display score + add([ + text(score), + pos(width() / 2, height() / 2 + 80), + scale(2), + anchor("center"), + ]); + + // go back to game with space is pressed + onKeyPress("space", () => go("game")); + onGamepadButtonPress("south", () => go("game")); }); // Sets up the scene where we lose :( scene("lose", () => { - add([ - text("You Lose"), - ]); - onKeyPress("space", () => go("game")); - onGamepadButtonPress("south", () => go("game")); + add([ + text("You Lose"), + ]); + onKeyPress("space", () => go("game")); + onGamepadButtonPress("south", () => go("game")); }); // Starts the game by entering the game scene diff --git a/examples/eatlove.js b/examples/eatlove.js index 424fb0f0..4c0a5513 100644 --- a/examples/eatlove.js +++ b/examples/eatlove.js @@ -4,14 +4,14 @@ kaplay(); // A lttle game about eating fruit! const fruits = [ - "apple", - "pineapple", - "grape", - "watermelon", + "apple", + "pineapple", + "grape", + "watermelon", ]; for (const fruit of fruits) { - loadSprite(fruit, `/sprites/${fruit}.png`); + loadSprite(fruit, `/sprites/${fruit}.png`); } loadSprite("bean", "/sprites/bean.png"); @@ -20,130 +20,130 @@ loadSound("hit", "/examples/sounds/hit.mp3"); loadSound("wooosh", "/examples/sounds/wooosh.mp3"); scene("start", () => { - // Plays the wooosh sound - play("wooosh"); - - add([ - text("Eat All"), - pos(center().sub(0, 100)), - scale(2), - anchor("center"), - ]); - - add([ - sprite("heart"), - pos(center().add(0, 100)), - scale(2), - anchor("center"), - ]); - - wait(1.5, () => go("game")); + // Plays the wooosh sound + play("wooosh"); + + add([ + text("Eat All"), + pos(center().sub(0, 100)), + scale(2), + anchor("center"), + ]); + + add([ + sprite("heart"), + pos(center().add(0, 100)), + scale(2), + anchor("center"), + ]); + + wait(1.5, () => go("game")); }); // main game scene content scene("game", () => { - const SPEED_MIN = 120; - const SPEED_MAX = 640; - - // add the player game object - const player = add([ - sprite("bean"), - pos(40, 20), - area({ scale: 0.5 }), - anchor("center"), - ]); - - // make the layer move by mouse - player.onUpdate(() => { - player.pos = mousePos(); - }); - - // game over if player eats a fruit - player.onCollide("fruit", () => { - go("lose", score); - play("hit"); - }); - - // move the food every frame, destroy it if far outside of screen - onUpdate("food", (food) => { - food.move(-food.speed, 0); - if (food.pos.x < -120) { - destroy(food); - } - }); - - onUpdate("heart", (heart) => { - if (heart.pos.x <= 0) { - go("lose", score); - play("hit"); - addKaboom(heart.pos); - } - }); - - // score counter - let score = 0; - - const scoreLabel = add([ - text(score.toString(), { - size: 32, - }), - pos(12, 12), - ]); - - // increment score if player eats a heart - player.onCollide("heart", (heart) => { - addKaboom(player.pos); - score += 1; - destroy(heart); - scoreLabel.text = score.toString(); - burp(); - shake(12); - }); - - // do this every 0.3 seconds - loop(0.3, () => { - // spawn from right side of the screen - const x = width() + 24; - // spawn from a random y position - const y = rand(0, height()); - // get a random speed - const speed = rand(SPEED_MIN, SPEED_MAX); - // 50% percent chance is heart - const isHeart = chance(0.5); - const spriteName = isHeart ? "heart" : choose(fruits); - - add([ - sprite(spriteName), - pos(x, y), - area({ scale: 0.5 }), - anchor("center"), - "food", - isHeart ? "heart" : "fruit", - { speed: speed }, - ]); - }); + const SPEED_MIN = 120; + const SPEED_MAX = 640; + + // add the player game object + const player = add([ + sprite("bean"), + pos(40, 20), + area({ scale: 0.5 }), + anchor("center"), + ]); + + // make the layer move by mouse + player.onUpdate(() => { + player.pos = mousePos(); + }); + + // game over if player eats a fruit + player.onCollide("fruit", () => { + go("lose", score); + play("hit"); + }); + + // move the food every frame, destroy it if far outside of screen + onUpdate("food", (food) => { + food.move(-food.speed, 0); + if (food.pos.x < -120) { + destroy(food); + } + }); + + onUpdate("heart", (heart) => { + if (heart.pos.x <= 0) { + go("lose", score); + play("hit"); + addKaboom(heart.pos); + } + }); + + // score counter + let score = 0; + + const scoreLabel = add([ + text(score.toString(), { + size: 32, + }), + pos(12, 12), + ]); + + // increment score if player eats a heart + player.onCollide("heart", (heart) => { + addKaboom(player.pos); + score += 1; + destroy(heart); + scoreLabel.text = score.toString(); + burp(); + shake(12); + }); + + // do this every 0.3 seconds + loop(0.3, () => { + // spawn from right side of the screen + const x = width() + 24; + // spawn from a random y position + const y = rand(0, height()); + // get a random speed + const speed = rand(SPEED_MIN, SPEED_MAX); + // 50% percent chance is heart + const isHeart = chance(0.5); + const spriteName = isHeart ? "heart" : choose(fruits); + + add([ + sprite(spriteName), + pos(x, y), + area({ scale: 0.5 }), + anchor("center"), + "food", + isHeart ? "heart" : "fruit", + { speed: speed }, + ]); + }); }); // game over scene scene("lose", (score) => { - add([ - sprite("bean"), - pos(width() / 2, height() / 2 - 108), - scale(3), - anchor("center"), - ]); - - // display score - add([ - text(score), - pos(width() / 2, height() / 2 + 108), - scale(3), - anchor("center"), - ]); - - // go back to game with space is pressed - onKeyPress("space", () => go("start")); - onClick(() => go("start")); + add([ + sprite("bean"), + pos(width() / 2, height() / 2 - 108), + scale(3), + anchor("center"), + ]); + + // display score + add([ + text(score), + pos(width() / 2, height() / 2 + 108), + scale(3), + anchor("center"), + ]); + + // go back to game with space is pressed + onKeyPress("space", () => go("start")); + onClick(() => go("start")); }); // start with the "game" scene diff --git a/examples/fastLoop.js b/examples/fastLoop.js index b61a13d5..515b452c 100644 --- a/examples/fastLoop.js +++ b/examples/fastLoop.js @@ -7,15 +7,20 @@ loadBean(); const interval = 0.001; const delay = 1; -loop(interval, () => { - const bean = add([ - sprite("bean"), - pos(rand(vec2(0), vec2(width(), height()))), - ]); - wait(delay, () => { - destroy(bean); - }); -}, -1, true); +loop( + interval, + () => { + const bean = add([ + sprite("bean"), + pos(rand(vec2(0), vec2(width(), height()))), + ]); + wait(delay, () => { + destroy(bean); + }); + }, + -1, + true, +); const counter = add([ pos(10, 10), @@ -31,14 +36,17 @@ add([ update() { this.width = counter.width; this.height = counter.height; - } - } -]) + }, + }, +]); var beanCount = 0; onAdd(() => beanCount++); onDestroy(() => beanCount--); loop(0.1, () => { - const error = 100 * Math.abs((delay / interval) - beanCount) / (delay / interval); - counter.text = `${beanCount.toFixed().padStart(5)} beans\nerror: ${error.toFixed(2).padStart(5)}%`; -}); \ No newline at end of file + const error = 100 * Math.abs((delay / interval) - beanCount) + / (delay / interval); + counter.text = `${beanCount.toFixed().padStart(5)} beans\nerror: ${ + error.toFixed(2).padStart(5) + }%`; +}); diff --git a/examples/prettyDebug.js b/examples/prettyDebug.js index 163a42b7..a8f31ac6 100644 --- a/examples/prettyDebug.js +++ b/examples/prettyDebug.js @@ -7,8 +7,8 @@ const pretty = { nested: "objects", }, arrays: ["show", "like", "you", "would", "write", "them"], - "own toString is used": vec2(10, 10) -} + "own toString is used": vec2(10, 10), +}; pretty.recursive = pretty; @@ -17,4 +17,3 @@ debug.log("Text in [brackets] doesn't cause issues"); debug.log(pretty); debug.error("This is an error message"); - diff --git a/examples/tiled.js b/examples/tiled.js index aad33e8e..2abe179b 100644 --- a/examples/tiled.js +++ b/examples/tiled.js @@ -16,7 +16,7 @@ add([ add([ circle(4), - pos(150, 150) -]) + pos(150, 150), +]); // debug.inspect = true diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 318c8ae1..2350c385 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -14,4 +14,4 @@ "moduleDetection": "force", "noImplicitAny": false } -} \ No newline at end of file +} diff --git a/src/app/index.ts b/src/app/index.ts index 43bfaa2d..ca3665b3 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -1,3 +1,3 @@ -export * from './app'; -export * from './frame'; -export * from './inputBindings'; +export * from "./app"; +export * from "./frame"; +export * from "./inputBindings"; diff --git a/src/assets/shader.ts b/src/assets/shader.ts index 786b48ec..718d28bf 100644 --- a/src/assets/shader.ts +++ b/src/assets/shader.ts @@ -200,7 +200,10 @@ export function loadShader( vert?: string, frag?: string, ) { - return _k.assets.shaders.addLoaded(name, makeShader(_k.gfx.ggl, vert, frag)); + return _k.assets.shaders.addLoaded( + name, + makeShader(_k.gfx.ggl, vert, frag), + ); } export function loadShaderURL( diff --git a/src/components/draw/sprite.ts b/src/components/draw/sprite.ts index d12dd3d3..e3265beb 100644 --- a/src/components/draw/sprite.ts +++ b/src/components/draw/sprite.ts @@ -251,13 +251,13 @@ export function sprite( if (!spriteData || !curAnim || curAnimDir === null) { return this.frame; } - + const anim = spriteData.anims[curAnim.name]; - + if (typeof anim === "number") { return anim; } - + return this.frame - Math.min(anim.from, anim.to); }, diff --git a/src/components/level/index.ts b/src/components/level/index.ts index bea8c349..b49f382b 100644 --- a/src/components/level/index.ts +++ b/src/components/level/index.ts @@ -1,5 +1,5 @@ -export * from './agent'; -export * from './pathfinder'; -export * from './patrol'; -export * from './sentry'; -export * from './tile'; +export * from "./agent"; +export * from "./pathfinder"; +export * from "./patrol"; +export * from "./sentry"; +export * from "./tile"; diff --git a/src/components/misc/textInput.ts b/src/components/misc/textInput.ts index dfa20b53..81ecee1c 100644 --- a/src/components/misc/textInput.ts +++ b/src/components/misc/textInput.ts @@ -39,7 +39,8 @@ export function textInput( charEv = _k.k.onCharInput((character) => { if ( this.hasFocus - && (!maxInputLength || this.typedText.length < maxInputLength) + && (!maxInputLength + || this.typedText.length < maxInputLength) ) { if (_k.k.isKeyDown("shift")) { this.typedText += character.toUpperCase(); diff --git a/src/components/misc/timer.ts b/src/components/misc/timer.ts index b3dcdf99..0c3ee043 100644 --- a/src/components/misc/timer.ts +++ b/src/components/misc/timer.ts @@ -20,7 +20,7 @@ export interface TimerComp extends Comp { * The maximum number of loops per frame allowed, * to keep loops with sub-frame intervals from freezing the game. */ - maxLoopsPerFrame: number + maxLoopsPerFrame: number; /** * Run the callback after n seconds. */ @@ -33,7 +33,12 @@ export interface TimerComp extends Comp { * * @since v3000.0 */ - loop(time: number, action: () => void, maxLoops?: number, waitFirst?: boolean): TimerController; + loop( + time: number, + action: () => void, + maxLoops?: number, + waitFirst?: boolean, + ): TimerController; /** * Tweeeeen! Note that this doesn't specifically mean tweening on this object's property, this just registers the timer on this object, so the tween will cancel with the object gets destroyed, or paused when obj.paused is true. * @@ -52,9 +57,15 @@ export function timer(maxLoopsPerFrame: number = 1000): TimerComp { return { id: "timer", maxLoopsPerFrame, - loop(this: GameObj, time: number, action: () => void, count: number = -1, waitFirst: boolean = false): TimerController { + loop( + this: GameObj, + time: number, + action: () => void, + count: number = -1, + waitFirst: boolean = false, + ): TimerController { let t: number = waitFirst ? 0 : time; - let onEndEvents = new KEvent; + let onEndEvents = new KEvent(); const ev = this.onUpdate(() => { t += _k.app.state.dt; for (let i = 0; t >= time && i < this.maxLoopsPerFrame; i++) { @@ -69,21 +80,28 @@ export function timer(maxLoopsPerFrame: number = 1000): TimerComp { action(); t -= time; } - }) + }); return { get paused() { - return ev.paused + return ev.paused; }, set paused(p) { - ev.paused = p + ev.paused = p; }, cancel: ev.cancel, onEnd: onEndEvents.add, - then(f) { onEndEvents.add(f); return this; } - } + then(f) { + onEndEvents.add(f); + return this; + }, + }; }, - wait(this: GameObj, time: number, action?: () => void): TimerController { - return this.loop(time, action ?? (() => { }), 1, true); + wait( + this: GameObj, + time: number, + action?: () => void, + ): TimerController { + return this.loop(time, action ?? (() => {}), 1, true); }, tween( this: GameObj, diff --git a/src/components/physics/index.ts b/src/components/physics/index.ts index 621201d3..a2cb6041 100644 --- a/src/components/physics/index.ts +++ b/src/components/physics/index.ts @@ -1,4 +1,4 @@ -export * from './area'; -export * from './body'; -export * from './doubleJump'; -export * from './effectors'; +export * from "./area"; +export * from "./body"; +export * from "./doubleJump"; +export * from "./effectors"; diff --git a/src/components/transform/index.ts b/src/components/transform/index.ts index e65e3339..e42d0222 100644 --- a/src/components/transform/index.ts +++ b/src/components/transform/index.ts @@ -1,10 +1,10 @@ -export * from './anchor'; -export * from './fixed'; -export * from './follow'; -export * from './layer'; -export * from './move'; -export * from './offscreen'; -export * from './pos'; -export * from './rotate'; -export * from './scale'; -export * from './z'; +export * from "./anchor"; +export * from "./fixed"; +export * from "./follow"; +export * from "./layer"; +export * from "./move"; +export * from "./offscreen"; +export * from "./pos"; +export * from "./rotate"; +export * from "./scale"; +export * from "./z"; diff --git a/src/game/gravity.ts b/src/game/gravity.ts index 54fd0c4a..00fee26a 100644 --- a/src/game/gravity.ts +++ b/src/game/gravity.ts @@ -4,7 +4,9 @@ import { type Vec2, vec2 } from "../math"; export function setGravity(g: number) { // If g > 0 use either the current direction or use (0, 1) // Else null - _k.game.gravity = g ? (_k.game.gravity || vec2(0, 1)).unit().scale(g) : null; + _k.game.gravity = g + ? (_k.game.gravity || vec2(0, 1)).unit().scale(g) + : null; } export function getGravity() { @@ -15,7 +17,9 @@ export function getGravity() { export function setGravityDirection(d: Vec2) { // If gravity > 0 keep magnitude, otherwise use 1 - _k.game.gravity = d.unit().scale(_k.game.gravity ? _k.game.gravity.len() : 1); + _k.game.gravity = d.unit().scale( + _k.game.gravity ? _k.game.gravity.len() : 1, + ); } export function getGravityDirection() { diff --git a/src/game/initEvents.ts b/src/game/initEvents.ts index b3fb1325..a356bb8e 100644 --- a/src/game/initEvents.ts +++ b/src/game/initEvents.ts @@ -1,7 +1,6 @@ import { burp } from "../audio"; import { FrameBuffer, updateViewport } from "../gfx"; -import {_k -} from "../kaplay"; +import { _k } from "../kaplay"; import { clamp } from "../math/math"; import { toFixed } from "../utils"; @@ -21,7 +20,9 @@ export function initEvents() { _k.app.onResize(() => { if (_k.app.isFullscreen()) return; const fixedSize = _k.globalOpt.width && _k.globalOpt.height; - if (fixedSize && !_k.globalOpt.stretch && !_k.globalOpt.letterbox) return; + if (fixedSize && !_k.globalOpt.stretch && !_k.globalOpt.letterbox) { + return; + } _k.canvas.width = _k.canvas.offsetWidth * _k.pixelDensity; _k.canvas.height = _k.canvas.offsetHeight * _k.pixelDensity; @@ -35,8 +36,10 @@ export function initEvents() { _k.gfx.ggl.gl.drawingBufferWidth, _k.gfx.ggl.gl.drawingBufferHeight, ); - _k.gfx.width = _k.gfx.ggl.gl.drawingBufferWidth / _k.pixelDensity / _k.gscale; - _k.gfx.height = _k.gfx.ggl.gl.drawingBufferHeight / _k.pixelDensity / _k.gscale; + _k.gfx.width = _k.gfx.ggl.gl.drawingBufferWidth / _k.pixelDensity + / _k.gscale; + _k.gfx.height = _k.gfx.ggl.gl.drawingBufferHeight / _k.pixelDensity + / _k.gscale; } }); diff --git a/src/game/make.ts b/src/game/make.ts index fef1531e..684ed430 100644 --- a/src/game/make.ts +++ b/src/game/make.ts @@ -254,15 +254,15 @@ export function make(comps: CompList = []): GameObj { comp[k]?.(); onCurCompCleanup = null; } - : comp[k]; - gc.push(this.on(k, func).cancel); + : comp[ k]; + gc.push(this.on(k, func).cancel); } else { if (this[k] === undefined) { // assign comp fields to game obj Object.defineProperty(this, k, { - get: () => comp[k], - set: (val) => comp[k] = val, + get: () => comp[ k], + set: (val) => comp[ k] = val, configurable: true, enumerable: true, }); @@ -274,9 +274,9 @@ export function make(comps: CompList = []): GameObj { )?.id; throw new Error( `Duplicate component property: "${k}" while adding component "${comp.id}"` - + (originalCompId - ? ` (originally added by "${originalCompId}")` - : ""), + + (originalCompId + ? ` (originally added by "${originalCompId}")` + : ""), ); } } diff --git a/src/gfx/formatText.ts b/src/gfx/formatText.ts index e8d178c5..7ae0ec88 100644 --- a/src/gfx/formatText.ts +++ b/src/gfx/formatText.ts @@ -86,7 +86,7 @@ export function compileStyledText(txt: string): { if (x !== undefined) { throw new Error( "Styled text error: mismatched tags. " - + `Expected [/${x}], got [/${gn}]`, + + `Expected [/${x}], got [/${gn}]`, ); } else { @@ -146,14 +146,14 @@ export function formatText(opt: DrawTextOpt): FormattedText { outline: Outline | null; filter: TexFilter; } = font instanceof FontData - ? { - outline: font.outline, - filter: font.filter, - } - : { - outline: null, - filter: DEF_FONT_FILTER, - }; + ? { + outline: font.outline, + filter: font.filter, + } + : { + outline: null, + filter: DEF_FONT_FILTER, + }; // TODO: customizable font tex filter const atlas: FontAtlas = fontAtlases[fontName] ?? { diff --git a/src/gfx/texPacker.ts b/src/gfx/texPacker.ts index e7e018bd..1c94ae94 100644 --- a/src/gfx/texPacker.ts +++ b/src/gfx/texPacker.ts @@ -1,6 +1,6 @@ -import type { ImageSource } from "../types"; import { type GfxCtx, Texture } from "../gfx"; import { Quad, Vec2 } from "../math/math"; +import type { ImageSource } from "../types"; export default class TexPacker { private lastTextureId: number = 0; @@ -107,4 +107,4 @@ export default class TexPacker { tex.free(); } } -} \ No newline at end of file +} diff --git a/src/kaplay.ts b/src/kaplay.ts index 07ea0f76..10e4563f 100644 --- a/src/kaplay.ts +++ b/src/kaplay.ts @@ -350,7 +350,8 @@ const kaplay = < tagsAsComponents: true, }, ): TPlugins extends [undefined] ? KAPLAYCtx - : KAPLAYCtx & MergePlugins => { + : KAPLAYCtx & MergePlugins => +{ if (_k.k) { console.warn( "KAPLAY already initialized, you are calling kaplay() multiple times, it may lead bugs!", @@ -900,7 +901,7 @@ const kaplay = < // TODO: this should only run once app.run( - () => { }, + () => {}, () => { frameStart(); @@ -963,7 +964,7 @@ const kaplay = < // clear canvas gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT - | gl.STENCIL_BUFFER_BIT, + | gl.STENCIL_BUFFER_BIT, ); // unbind everything @@ -1407,7 +1408,7 @@ const kaplay = < // export everything to window if global is set if (gopt.global !== false) { for (const key in ctx) { - (window[key]) = ctx[key as keyof KAPLAYCtx]; + ( window[ key]) = ctx[key as keyof KAPLAYCtx]; } } diff --git a/src/types.ts b/src/types.ts index 3fbf1876..d81ee2a6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5460,7 +5460,7 @@ export interface KAPLAYOpt< tagsAsComponents?: boolean; /** * Padding used when adding sprites to texture atlas. - * + * * @default 0 * @experimental This feature is in experimental phase, it will be fully released in v3001.1.0 */ diff --git a/tests/playtests/tsconfig.json b/tests/playtests/tsconfig.json index a70a602c..3ce20bbb 100644 --- a/tests/playtests/tsconfig.json +++ b/tests/playtests/tsconfig.json @@ -14,4 +14,4 @@ "moduleDetection": "force", "noImplicitAny": false } -} \ No newline at end of file +} diff --git a/tests/playtests/weirdTextTags.js b/tests/playtests/weirdTextTags.js index 36ddfe34..956ba41c 100644 --- a/tests/playtests/weirdTextTags.js +++ b/tests/playtests/weirdTextTags.js @@ -6,7 +6,7 @@ const txtEl = add([ pink: { color: MAGENTA, }, - } + }, }), pos(100, 100), ]); diff --git a/tsconfig.dts.json b/tsconfig.dts.json index ae6558e7..8ae3cf93 100644 --- a/tsconfig.dts.json +++ b/tsconfig.dts.json @@ -15,4 +15,4 @@ "include": [ "src/**/*" ] -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 692955f7..2b3427a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,4 +20,4 @@ "scripts/**/*", "tests/auto/color.spec.ts" ] -} \ No newline at end of file +}