the game
1import type { KAPLAYCtx, GameObj } from "kaplay";
2
3// Extend the KAPLAYCtx type to include our addConfetti method
4declare module "kaplay" {
5 interface KAPLAYCtx {
6 addConfetti?: (pos: { x: number; y: number }) => GameObj[];
7 }
8}
9
10// Function to create a confetti effect at a position
11export function addConfetti(k: KAPLAYCtx, pos: { x: number; y: number }) {
12 // Number of confetti particles
13 const PARTICLE_COUNT = 50;
14
15 // Confetti colors
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 ] as const;
26
27 // Create particles
28 const particles: GameObj[] = [];
29
30 for (let i = 0; i < PARTICLE_COUNT; i++) {
31 // Random color
32 const color = COLORS[Math.floor(Math.random() * COLORS.length)];
33
34 // Random size
35 const size = Math.random() * 8 + 2; // 2-10 pixels
36
37 // Random shape (circle or rect)
38 const isCircle = Math.random() > 0.5;
39
40 // Random velocity
41 const angle = Math.random() * Math.PI * 2;
42 const speed = Math.random() * 400 + 100; // 100-500 pixels per second
43 const vx = Math.cos(angle) * speed;
44 const vy = Math.sin(angle) * speed - 200; // Initial upward boost
45
46 // Random rotation speed
47 const rotSpeed = Math.random() * 10 - 5; // -5 to 5 radians per second
48
49 // Create particle
50 const particle = k.add([
51 isCircle ? k.circle(size / 2) : k.rect(size, size / 2),
52 k.pos(pos.x, pos.y),
53 k.color(color[0], color[1], color[2]),
54 k.anchor("center"),
55 k.rotate(Math.random() * 360), // Random initial rotation
56 k.opacity(1),
57 k.z(100), // Above most game elements
58 {
59 // Custom properties for movement
60 vx,
61 vy,
62 rotSpeed,
63 gravity: 980, // Gravity effect
64 lifespan: Math.random() * 1 + 1, // 1-2 seconds
65 fadeStart: 0.7, // When to start fading (0.7 = 70% of lifespan)
66 },
67 ]);
68
69 // Update function for the particle
70 particle.onUpdate(() => {
71 // Update position based on velocity
72 particle.pos.x += particle.vx * k.dt();
73 particle.pos.y += particle.vy * k.dt();
74
75 // Apply gravity
76 particle.vy += particle.gravity * k.dt();
77
78 // Apply rotation
79 particle.angle += particle.rotSpeed * k.dt() * 60;
80
81 // Update lifespan
82 particle.lifespan -= k.dt();
83
84 // Fade out
85 if (particle.lifespan < particle.fadeStart) {
86 particle.opacity = Math.max(0, particle.lifespan / particle.fadeStart);
87 }
88
89 // Destroy when lifespan is over
90 if (particle.lifespan <= 0) {
91 particle.destroy();
92 }
93 });
94
95 particles.push(particle);
96 }
97
98 return particles;
99}
100
101// Component to add confetti method to the game context
102export function confettiPlugin(k: KAPLAYCtx) {
103 return {
104 // Add the confetti function to the game context
105 addConfetti(pos: { x: number; y: number }) {
106 return addConfetti(k, pos);
107 },
108 };
109}
110
111export default confettiPlugin;