feat: add invisible walls

dunkirk.sh 72a98fab c09b4d3e

verified
Changed files
+77 -43
public
sprites
src
public/sprites/bean.png

This is a binary file and will not be displayed.

+31 -31
src/confetti.ts
···
// Extend the KAPLAYCtx type to include our addConfetti method
declare module "kaplay" {
interface KAPLAYCtx {
-
addConfetti?: (pos: { x: number, y: number }) => GameObj[];
+
addConfetti?: (pos: { x: number; y: number }) => GameObj[];
}
}
// Function to create a confetti effect at a position
-
export function addConfetti(k: KAPLAYCtx, pos: { x: number, y: number }) {
+
export function addConfetti(k: KAPLAYCtx, pos: { x: number; y: number }) {
// Number of confetti particles
const PARTICLE_COUNT = 50;
-
+
// Confetti colors
const COLORS = [
-
[255, 0, 0], // Red
-
[0, 255, 0], // Green
-
[0, 0, 255], // Blue
-
[255, 255, 0], // Yellow
-
[255, 0, 255], // Magenta
-
[0, 255, 255], // Cyan
-
[255, 165, 0], // Orange
-
[128, 0, 128], // Purple
-
];
-
+
[255, 0, 0], // Red
+
[0, 255, 0], // Green
+
[0, 0, 255], // Blue
+
[255, 255, 0], // Yellow
+
[255, 0, 255], // Magenta
+
[0, 255, 255], // Cyan
+
[255, 165, 0], // Orange
+
[128, 0, 128], // Purple
+
] as const;
+
// Create particles
const particles: GameObj[] = [];
-
+
for (let i = 0; i < PARTICLE_COUNT; i++) {
// Random color
const color = COLORS[Math.floor(Math.random() * COLORS.length)];
-
+
// Random size
const size = Math.random() * 8 + 2; // 2-10 pixels
-
+
// Random shape (circle or rect)
const isCircle = Math.random() > 0.5;
-
+
// Random velocity
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 400 + 100; // 100-500 pixels per second
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed - 200; // Initial upward boost
-
+
// Random rotation speed
const rotSpeed = Math.random() * 10 - 5; // -5 to 5 radians per second
-
+
// Create particle
const particle = k.add([
isCircle ? k.circle(size / 2) : k.rect(size, size / 2),
k.pos(pos.x, pos.y),
-
k.color(...color),
+
k.color(color[0], color[1], color[2]),
k.anchor("center"),
k.rotate(Math.random() * 360), // Random initial rotation
k.opacity(1),
···
fadeStart: 0.7, // When to start fading (0.7 = 70% of lifespan)
},
]);
-
+
// Update function for the particle
particle.onUpdate(() => {
// Update position based on velocity
particle.pos.x += particle.vx * k.dt();
particle.pos.y += particle.vy * k.dt();
-
+
// Apply gravity
particle.vy += particle.gravity * k.dt();
-
+
// Apply rotation
particle.angle += particle.rotSpeed * k.dt() * 60;
-
+
// Update lifespan
particle.lifespan -= k.dt();
-
+
// Fade out
if (particle.lifespan < particle.fadeStart) {
particle.opacity = Math.max(0, particle.lifespan / particle.fadeStart);
}
-
+
// Destroy when lifespan is over
if (particle.lifespan <= 0) {
particle.destroy();
}
});
-
+
particles.push(particle);
}
-
+
return particles;
}
···
export function confettiPlugin(k: KAPLAYCtx) {
return {
// Add the confetti function to the game context
-
addConfetti(pos: { x: number, y: number }) {
+
addConfetti(pos: { x: number; y: number }) {
return addConfetti(k, pos);
-
}
+
},
};
}
-
export default confettiPlugin;
+
export default confettiPlugin;
+46 -12
src/main.ts
···
import kaplay from "kaplay";
import player from "./player";
-
import { makeEnemy, type EnemyComp } from "./enemy";
-
import confettiPlugin, { addConfetti } from "./confetti";
+
import { makeEnemy } from "./enemy";
+
import confettiPlugin from "./confetti";
const k = kaplay({ plugins: [crew] });
k.loadRoot("./"); // A good idea for Itch.io publishing later
···
k.color(127, 200, 255),
]);
+
// Create walls around the edge of the map
+
// Left wall
+
const leftWall = k.add([
+
k.rect(20, k.height()),
+
k.pos(-20, 0),
+
k.outline(4),
+
k.area(),
+
k.body({ isStatic: true }),
+
k.color(127, 200, 255),
+
k.opacity(0.5),
+
]);
+
+
// Right wall
+
const rightWall = k.add([
+
k.rect(20, k.height()),
+
k.pos(k.width(), 0),
+
k.outline(4),
+
k.area(),
+
k.body({ isStatic: true }),
+
k.color(127, 200, 255),
+
k.opacity(0.5),
+
]);
+
+
// Top wall
+
const topWall = k.add([
+
k.rect(k.width(), 20),
+
k.pos(0, -20),
+
k.outline(4),
+
k.area(),
+
k.body({ isStatic: true }),
+
k.color(127, 200, 255),
+
k.opacity(0.5),
+
]);
+
// Create player object with components
const playerObj = k.add([
k.pos(120, 500),
···
gameTime += 1; // Increment game time by 1 second
// Every 30 seconds, increase difficulty
-
if (score != 0 && score % (50 * difficultyLevel) === 0) {
+
if (score != 0 && score % (50 + 5 * difficultyLevel) === 0) {
difficultyLevel += 1;
// Increase max enemies (cap at 15)
···
switch (side) {
case 0: // top
-
x = Math.random() * k.width();
-
y = -50;
+
x = Math.random() * (k.width() - 40) + 20; // Avoid spawning behind side walls
+
y = 10; // Just inside the top wall
break;
case 1: // right
-
x = k.width() + 50;
-
y = Math.random() * k.height();
+
x = k.width() - 10; // Just inside the right wall
+
y = Math.random() * (k.height() - 48 - 20) + 20; // Avoid spawning behind top wall or inside ground
break;
case 2: // bottom
-
x = Math.random() * k.width();
-
y = k.height() + 50;
+
x = Math.random() * (k.width() - 40) + 20; // Avoid spawning behind side walls
+
y = k.height() - 58; // Just above the ground (ground is at height-48 with height 48)
break;
case 3: // left
-
x = -50;
-
y = Math.random() * k.height();
+
x = 10; // Just inside the left wall
+
y = Math.random() * (k.height() - 48 - 20) + 20; // Avoid spawning behind top wall or inside ground
break;
}
···
enemies = enemies.filter((e) => e !== newEnemy);
// Increase score when enemy is destroyed
-
score += 10 + Math.pow(difficultyLevel, 0.75);
+
score += Math.round(10 + Math.pow(difficultyLevel, 0.75));
// Update score display
scoreText.text = `Score: ${score}`;