the game

feat: add invisible walls

dunkirk.sh 72a98fab c09b4d3e

verified
+77 -43
public/sprites/bean.png

This is a binary file and will not be displayed.

+31 -31
src/confetti.ts
··· 3 3 // Extend the KAPLAYCtx type to include our addConfetti method 4 4 declare module "kaplay" { 5 5 interface KAPLAYCtx { 6 - addConfetti?: (pos: { x: number, y: number }) => GameObj[]; 6 + addConfetti?: (pos: { x: number; y: number }) => GameObj[]; 7 7 } 8 8 } 9 9 10 10 // Function to create a confetti effect at a position 11 - export function addConfetti(k: KAPLAYCtx, pos: { x: number, y: number }) { 11 + export function addConfetti(k: KAPLAYCtx, pos: { x: number; y: number }) { 12 12 // Number of confetti particles 13 13 const PARTICLE_COUNT = 50; 14 - 14 + 15 15 // Confetti colors 16 16 const COLORS = [ 17 - [255, 0, 0], // Red 18 - [0, 255, 0], // Green 19 - [0, 0, 255], // Blue 20 - [255, 255, 0], // Yellow 21 - [255, 0, 255], // Magenta 22 - [0, 255, 255], // Cyan 23 - [255, 165, 0], // Orange 24 - [128, 0, 128], // Purple 25 - ]; 26 - 17 + [255, 0, 0], // Red 18 + [0, 255, 0], // Green 19 + [0, 0, 255], // Blue 20 + [255, 255, 0], // Yellow 21 + [255, 0, 255], // Magenta 22 + [0, 255, 255], // Cyan 23 + [255, 165, 0], // Orange 24 + [128, 0, 128], // Purple 25 + ] as const; 26 + 27 27 // Create particles 28 28 const particles: GameObj[] = []; 29 - 29 + 30 30 for (let i = 0; i < PARTICLE_COUNT; i++) { 31 31 // Random color 32 32 const color = COLORS[Math.floor(Math.random() * COLORS.length)]; 33 - 33 + 34 34 // Random size 35 35 const size = Math.random() * 8 + 2; // 2-10 pixels 36 - 36 + 37 37 // Random shape (circle or rect) 38 38 const isCircle = Math.random() > 0.5; 39 - 39 + 40 40 // Random velocity 41 41 const angle = Math.random() * Math.PI * 2; 42 42 const speed = Math.random() * 400 + 100; // 100-500 pixels per second 43 43 const vx = Math.cos(angle) * speed; 44 44 const vy = Math.sin(angle) * speed - 200; // Initial upward boost 45 - 45 + 46 46 // Random rotation speed 47 47 const rotSpeed = Math.random() * 10 - 5; // -5 to 5 radians per second 48 - 48 + 49 49 // Create particle 50 50 const particle = k.add([ 51 51 isCircle ? k.circle(size / 2) : k.rect(size, size / 2), 52 52 k.pos(pos.x, pos.y), 53 - k.color(...color), 53 + k.color(color[0], color[1], color[2]), 54 54 k.anchor("center"), 55 55 k.rotate(Math.random() * 360), // Random initial rotation 56 56 k.opacity(1), ··· 65 65 fadeStart: 0.7, // When to start fading (0.7 = 70% of lifespan) 66 66 }, 67 67 ]); 68 - 68 + 69 69 // Update function for the particle 70 70 particle.onUpdate(() => { 71 71 // Update position based on velocity 72 72 particle.pos.x += particle.vx * k.dt(); 73 73 particle.pos.y += particle.vy * k.dt(); 74 - 74 + 75 75 // Apply gravity 76 76 particle.vy += particle.gravity * k.dt(); 77 - 77 + 78 78 // Apply rotation 79 79 particle.angle += particle.rotSpeed * k.dt() * 60; 80 - 80 + 81 81 // Update lifespan 82 82 particle.lifespan -= k.dt(); 83 - 83 + 84 84 // Fade out 85 85 if (particle.lifespan < particle.fadeStart) { 86 86 particle.opacity = Math.max(0, particle.lifespan / particle.fadeStart); 87 87 } 88 - 88 + 89 89 // Destroy when lifespan is over 90 90 if (particle.lifespan <= 0) { 91 91 particle.destroy(); 92 92 } 93 93 }); 94 - 94 + 95 95 particles.push(particle); 96 96 } 97 - 97 + 98 98 return particles; 99 99 } 100 100 ··· 102 102 export function confettiPlugin(k: KAPLAYCtx) { 103 103 return { 104 104 // Add the confetti function to the game context 105 - addConfetti(pos: { x: number, y: number }) { 105 + addConfetti(pos: { x: number; y: number }) { 106 106 return addConfetti(k, pos); 107 - } 107 + }, 108 108 }; 109 109 } 110 110 111 - export default confettiPlugin; 111 + export default confettiPlugin;
+46 -12
src/main.ts
··· 2 2 import kaplay from "kaplay"; 3 3 4 4 import player from "./player"; 5 - import { makeEnemy, type EnemyComp } from "./enemy"; 6 - import confettiPlugin, { addConfetti } from "./confetti"; 5 + import { makeEnemy } from "./enemy"; 6 + import confettiPlugin from "./confetti"; 7 7 8 8 const k = kaplay({ plugins: [crew] }); 9 9 k.loadRoot("./"); // A good idea for Itch.io publishing later ··· 27 27 k.color(127, 200, 255), 28 28 ]); 29 29 30 + // Create walls around the edge of the map 31 + // Left wall 32 + const leftWall = k.add([ 33 + k.rect(20, k.height()), 34 + k.pos(-20, 0), 35 + k.outline(4), 36 + k.area(), 37 + k.body({ isStatic: true }), 38 + k.color(127, 200, 255), 39 + k.opacity(0.5), 40 + ]); 41 + 42 + // Right wall 43 + const rightWall = k.add([ 44 + k.rect(20, k.height()), 45 + k.pos(k.width(), 0), 46 + k.outline(4), 47 + k.area(), 48 + k.body({ isStatic: true }), 49 + k.color(127, 200, 255), 50 + k.opacity(0.5), 51 + ]); 52 + 53 + // Top wall 54 + const topWall = k.add([ 55 + k.rect(k.width(), 20), 56 + k.pos(0, -20), 57 + k.outline(4), 58 + k.area(), 59 + k.body({ isStatic: true }), 60 + k.color(127, 200, 255), 61 + k.opacity(0.5), 62 + ]); 63 + 30 64 // Create player object with components 31 65 const playerObj = k.add([ 32 66 k.pos(120, 500), ··· 54 88 gameTime += 1; // Increment game time by 1 second 55 89 56 90 // Every 30 seconds, increase difficulty 57 - if (score != 0 && score % (50 * difficultyLevel) === 0) { 91 + if (score != 0 && score % (50 + 5 * difficultyLevel) === 0) { 58 92 difficultyLevel += 1; 59 93 60 94 // Increase max enemies (cap at 15) ··· 120 154 121 155 switch (side) { 122 156 case 0: // top 123 - x = Math.random() * k.width(); 124 - y = -50; 157 + x = Math.random() * (k.width() - 40) + 20; // Avoid spawning behind side walls 158 + y = 10; // Just inside the top wall 125 159 break; 126 160 case 1: // right 127 - x = k.width() + 50; 128 - y = Math.random() * k.height(); 161 + x = k.width() - 10; // Just inside the right wall 162 + y = Math.random() * (k.height() - 48 - 20) + 20; // Avoid spawning behind top wall or inside ground 129 163 break; 130 164 case 2: // bottom 131 - x = Math.random() * k.width(); 132 - y = k.height() + 50; 165 + x = Math.random() * (k.width() - 40) + 20; // Avoid spawning behind side walls 166 + y = k.height() - 58; // Just above the ground (ground is at height-48 with height 48) 133 167 break; 134 168 case 3: // left 135 - x = -50; 136 - y = Math.random() * k.height(); 169 + x = 10; // Just inside the left wall 170 + y = Math.random() * (k.height() - 48 - 20) + 20; // Avoid spawning behind top wall or inside ground 137 171 break; 138 172 } 139 173 ··· 146 180 enemies = enemies.filter((e) => e !== newEnemy); 147 181 148 182 // Increase score when enemy is destroyed 149 - score += 10 + Math.pow(difficultyLevel, 0.75); 183 + score += Math.round(10 + Math.pow(difficultyLevel, 0.75)); 150 184 151 185 // Update score display 152 186 scoreText.text = `Score: ${score}`;