Skip to content

Commit 4c05c72

Browse files
authored
Merge pull request #83 from codersforcauses/issue-47-Add_explosions_to_committee_page
Issue 47 add explosions to committee page
2 parents 8c53a22 + e65e214 commit 4c05c72

20 files changed

Lines changed: 1004 additions & 33 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,5 +293,6 @@ dist
293293

294294
# misc
295295
.DS_Store
296+
.cursor/
296297

297298
opt/

client/package-lock.json

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
2.68 KB
Loading

client/public/sfx/xplsion_0.mp3

83.4 KB
Binary file not shown.

client/public/sfx/xplsion_1.mp3

166 KB
Binary file not shown.

client/public/sfx/xplsion_2.mp3

128 KB
Binary file not shown.

client/public/sfx/xplsion_3.mp3

122 KB
Binary file not shown.

client/scripts/open-when-ready.js

Whitespace-only changes.

client/src/components/main/Navbar.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import Image from "next/image";
55
import Link from "next/link";
66
import { useState } from "react";
77

8+
import { useExplosionContext } from "@/contexts/ExplosionContext";
9+
810
export default function Navbar() {
911
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
12+
const { triggerExplosionAt } = useExplosionContext();
1013

1114
const navItems = [
1215
{ href: "/", label: "Home" },
@@ -16,11 +19,19 @@ export default function Navbar() {
1619
{ href: "/artwork", label: "Art Showcase" },
1720
];
1821

22+
const handleNavClick = (e: React.MouseEvent) => {
23+
triggerExplosionAt(e.clientX, e.clientY);
24+
};
25+
1926
return (
2027
<>
2128
<header className="sticky top-0 z-50 flex h-24 w-full items-center rounded-md border-b border-border/20 bg-background px-20 font-jersey10">
2229
<div className="flex flex-1 items-center">
23-
<Link href="/" className="flex items-center gap-3 text-2xl lg:mr-5">
30+
<Link
31+
href="/"
32+
className="flex items-center gap-3 text-2xl lg:mr-5"
33+
onClick={handleNavClick}
34+
>
2435
<Image
2536
src="/game_dev_club_logo.svg"
2637
alt="logo"
@@ -44,6 +55,7 @@ export default function Navbar() {
4455
<Link
4556
key={item.href}
4657
href={item.href}
58+
onClick={handleNavClick}
4759
className="whitespace-nowrap text-foreground/90 transition-colors duration-150 hover:text-primary"
4860
>
4961
{item.label}
@@ -67,7 +79,10 @@ export default function Navbar() {
6779
<Link
6880
key={item.href}
6981
href={item.href}
70-
onClick={() => setIsDropdownOpen(false)}
82+
onClick={(e) => {
83+
handleNavClick(e);
84+
setIsDropdownOpen(false);
85+
}}
7186
className="block whitespace-nowrap px-4 py-3 text-lg transition-colors duration-150 hover:bg-accent"
7287
>
7388
{item.label}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import React, { useMemo } from "react";
2+
3+
interface CraterProps {
4+
size?: number;
5+
intensity?: number;
6+
}
7+
8+
/**
9+
* Generates an irregular crater polygon
10+
*/
11+
function generateCraterShape(baseRadius: number, points: number = 10): string {
12+
const coords: string[] = [];
13+
14+
for (let i = 0; i < points; i++) {
15+
const angle = (i / points) * Math.PI * 2;
16+
const radius = baseRadius * (0.75 + Math.random() * 0.3);
17+
const x = 50 + Math.cos(angle) * radius;
18+
const y = 50 + Math.sin(angle) * radius;
19+
coords.push(`${x.toFixed(1)},${y.toFixed(1)}`);
20+
}
21+
22+
return coords.join(" ");
23+
}
24+
25+
/**
26+
* Generates a crack as a polygon shape (actual gap in ground)
27+
* Returns points for a wedge-shaped fissure
28+
*/
29+
function generateCrackFissure(
30+
angle: number,
31+
startRadius: number,
32+
length: number,
33+
): string {
34+
// Crack is a tapered wedge shape - wide at crater, sharp point at end
35+
const widthAtStart = 7 + Math.random() * 5; // Bit skinnier (7-12)
36+
const widthAtEnd = 0.5 + Math.random() * 1; // Sharp point at tip (0.5-1.5)
37+
38+
// Calculate perpendicular angle for width
39+
const perpAngle = angle + Math.PI / 2;
40+
41+
// Points along the crack with some jaggedness
42+
const segments = 3;
43+
const points: Array<{ x: number; y: number }> = [];
44+
const pointsBack: Array<{ x: number; y: number }> = [];
45+
46+
let currentAngle = angle;
47+
48+
for (let i = 0; i <= segments; i++) {
49+
const t = i / segments;
50+
// Start from inside crater and extend outward
51+
const radius = startRadius + length * t;
52+
const width = widthAtStart + (widthAtEnd - widthAtStart) * t;
53+
54+
// Add jaggedness to angle
55+
if (i > 0 && i < segments) {
56+
currentAngle += (Math.random() - 0.5) * 0.3;
57+
}
58+
59+
const centerX = 50 + Math.cos(currentAngle) * radius;
60+
const centerY = 50 + Math.sin(currentAngle) * radius;
61+
62+
// Offset perpendicular for width
63+
const offsetX = (Math.cos(perpAngle) * width) / 2;
64+
const offsetY = (Math.sin(perpAngle) * width) / 2;
65+
66+
// Add random jaggedness to edges - less at the tip for sharp point
67+
const jagAmount = 4 * (1 - t * 0.8); // More jagged at start, smooth at tip
68+
const jag1 = (Math.random() - 0.5) * jagAmount;
69+
const jag2 = (Math.random() - 0.5) * jagAmount;
70+
71+
points.push({
72+
x: centerX + offsetX + jag1,
73+
y: centerY + offsetY + jag1,
74+
});
75+
pointsBack.unshift({
76+
x: centerX - offsetX + jag2,
77+
y: centerY - offsetY + jag2,
78+
});
79+
}
80+
81+
// Combine into polygon
82+
const allPoints = [...points, ...pointsBack];
83+
return allPoints.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
84+
}
85+
86+
/**
87+
* Crater with fissure-style cracks (actual gaps, not lines)
88+
*/
89+
export const Crater = React.memo(function Crater({
90+
size = 100,
91+
intensity = 1,
92+
}: CraterProps) {
93+
const uniqueId = useMemo(() => Math.random().toString(36).substr(2, 9), []);
94+
95+
// Crater shapes
96+
const outerCrater = useMemo(() => generateCraterShape(22, 12), []);
97+
const innerCrater = useMemo(() => generateCraterShape(14, 10), []);
98+
const deepCrater = useMemo(() => generateCraterShape(8, 8), []);
99+
100+
// Generate 2-4 crack fissures (reduced for performance)
101+
const fissures = useMemo(() => {
102+
const count = 2 + Math.floor(Math.random() * 3);
103+
const result: Array<{ points: string }> = [];
104+
105+
for (let i = 0; i < count; i++) {
106+
const baseAngle = (i / count) * Math.PI * 2;
107+
const angle = baseAngle + (Math.random() - 0.5) * 0.8;
108+
const length = 20 + Math.random() * 18;
109+
110+
result.push({
111+
points: generateCrackFissure(angle, 14, length),
112+
});
113+
}
114+
115+
return result;
116+
}, []);
117+
118+
// Colors
119+
const voidColor = `rgba(0, 0, 0, ${intensity})`;
120+
const deepColor = `rgba(8, 5, 2, ${0.95 * intensity})`;
121+
const craterColor = `rgba(20, 15, 8, ${0.9 * intensity})`;
122+
const rimColor = `rgba(45, 35, 25, ${0.7 * intensity})`;
123+
124+
return (
125+
<svg
126+
width={size}
127+
height={size}
128+
viewBox="0 0 100 100"
129+
style={{ overflow: "visible", contain: "layout style" }}
130+
>
131+
<defs>
132+
<radialGradient
133+
id={`crater-grad-${uniqueId}`}
134+
cx="40%"
135+
cy="40%"
136+
r="60%"
137+
>
138+
<stop offset="0%" stopColor={voidColor} />
139+
<stop offset="50%" stopColor={deepColor} />
140+
<stop offset="80%" stopColor={craterColor} />
141+
<stop offset="100%" stopColor={rimColor} />
142+
</radialGradient>
143+
</defs>
144+
145+
{/* Crack fissures - simplified to single polygon each */}
146+
{fissures.map((fissure, i) => (
147+
<polygon key={i} points={fissure.points} fill={voidColor} />
148+
))}
149+
150+
{/* Outer crater rim */}
151+
<polygon points={outerCrater} fill={rimColor} />
152+
153+
{/* Main crater */}
154+
<polygon points={outerCrater} fill={`url(#crater-grad-${uniqueId})`} />
155+
156+
{/* Inner crater layer */}
157+
<polygon points={innerCrater} fill={deepColor} />
158+
159+
{/* Deepest void */}
160+
<polygon points={deepCrater} fill={voidColor} />
161+
162+
{/* Crater rim edge */}
163+
<polygon
164+
points={outerCrater}
165+
fill="none"
166+
stroke="rgba(60, 50, 40, 0.4)"
167+
strokeWidth="1.5"
168+
strokeLinejoin="round"
169+
/>
170+
</svg>
171+
);
172+
});

0 commit comments

Comments
 (0)