Skip to content

Commit 1fd8d46

Browse files
committed
feat: even moar fishies
1 parent 700c29f commit 1fd8d46

File tree

16 files changed

+880
-122
lines changed

16 files changed

+880
-122
lines changed

showcase/fishies/knowledge.md

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,46 +12,42 @@ A fish tank simulation with swimming fish, predator behavior, and interactive el
1212

1313
## Animation Guidelines
1414
- Use CSS transitions for smooth movement
15-
- Use 3D transforms for depth:
16-
- Container needs perspective: 2000px
17-
- Container needs transform-style: preserve-3d
18-
- Fish: ±200px depth, fast cycle
19-
- Shark: ±300px depth, medium cycle
20-
- Whale shark: ±400px depth, slow cycle
21-
- Scale with depth for more dramatic effect:
22-
- Fish: ±10% scale
23-
- Shark: ±15% scale
24-
- Whale shark: ±20% scale
25-
- Use rotateY instead of scaleX for direction changes
26-
- Remove transitions for immediate reactions to events
27-
- Add depth to environment:
28-
- Use transparent gradient layers in background
29-
- Animate container with subtle 3D movement
30-
- Create parallax effect with container animation
31-
- Death animation sequence:
32-
1. Fish straightens out when shark attacks
33-
2. When shark moves away, fish flips upside down
34-
3. After 500ms, transforms to skull (☠️) and starts fading
35-
4. Completes fade out over next 500ms
36-
- Scale emojis for different creature sizes
15+
- Use 3D transforms for depth
16+
- Scale with depth for dramatic effect
17+
- Use rotateY for direction changes
18+
- Remove transitions for immediate reactions
3719

3820
## Combat Mechanics
39-
- Shark must be moving towards fish to attack (within 30px range)
40-
- Fish speed increases based on distance to shark using quadratic easing
41-
- Whale shark protects fish by pushing shark away when nearby
21+
- Shark must be moving towards fish to attack
22+
- Fish speed increases near predators
23+
- Whale shark protects fish from sharks
4224

4325
## Game Balance
44-
- Base fish speed: 0.5 units (slow peaceful swimming)
45-
- Shark speed: 3 units (fast predator)
46-
- Whale shark speed: 4 units (fast enough to catch shark)
47-
- Fish speed multiplier: gradually increases up to 2x within 200px of shark
48-
- Protection distances:
49-
- Shark catching fish: 15px (must be within 15 degrees of directly facing fish)
50-
- Whale shark slaps shark away 250px when within 100px range
26+
- Base fish speed: 0.5 units
27+
- Shark speed: 3 units
28+
- Whale shark speed: 4 units
29+
- Protection radius: 100px for whale shark
30+
31+
## Interactive Elements
32+
- Click for bubbles
33+
- Shift+click for food
34+
- Ctrl/Cmd+click for sparkles
35+
- Ripple effects under fast predators
36+
37+
## Fish Behavior
38+
- School with nearby fish
39+
- Avoid predators
40+
- Seek food
41+
- Hide near decorations when threatened
42+
43+
## Ecosystem Interactions
44+
- Predator competition
45+
- Safe zones near decorations
46+
- Territorial behaviors
47+
- Water current effects
5148

5249
## Verifying changes
5350
After every change, run:
5451
```bash
5552
npm run lint && npm run typecheck
5653
```
57-
This will check for lint issues and type errors.

showcase/fishies/src/App.css

Lines changed: 153 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,49 @@
55
background: linear-gradient(180deg, #0f4c81 0%, #1e90ff 50%, #0d3b66 100%);
66
perspective: 2000px;
77
transform-style: preserve-3d;
8+
0% { transform: translate3d(0, 0, 0) scale(1); }
9+
50% { transform: translate3d(20px, -30px, 100px) scale(1.3) rotate(10deg); }
10+
100% { transform: translate3d(0, 0, 0) scale(1); }
811
}
912

1013
/* The fish-tank container holds all moving parts */
1114
.fish-tank {
1215
width: 100%;
1316
height: 100%;
1417
position: relative;
15-
animation: tank-depth 20s infinite ease-in-out;
18+
animation: tank-depth 20s infinite ease-in-out, tank-current 10s ease-in-out infinite;
19+
cursor: pointer;
20+
}
21+
22+
/* Caustics overlay for dynamic lighting */
23+
.caustics-overlay {
24+
position: absolute;
25+
top: 0;
26+
left: 0;
27+
width: 100%;
28+
height: 100%;
29+
pointer-events: none;
30+
z-index: 0;
31+
background: radial-gradient(
32+
circle at 50% 50%,
33+
rgba(255, 255, 255, 0.05),
34+
transparent 60%
35+
);
36+
opacity: 0.7;
37+
animation: caustics 8s ease-in-out infinite alternate;
38+
}
39+
40+
/* Ambient light overlay */
41+
.ambient-light {
42+
position: absolute;
43+
top: 0;
44+
left: 0;
45+
width: 100%;
46+
height: 100%;
47+
pointer-events: none;
48+
z-index: 0;
49+
background: linear-gradient(45deg, rgba(255,255,255,0.05), transparent);
50+
animation: ambient-light-move 15s ease-in-out infinite;
1651
}
1752

1853
/* Subtle light-ray overlay for extra depth */
@@ -33,44 +68,149 @@
3368
}
3469

3570
@keyframes tank-depth {
36-
0%,
37-
100% {
38-
transform: translate3d(0, 0, 0);
71+
0%, 100% {
72+
transform: translate3d(0, 0, 0) rotate3d(1, 1, 0, 0deg);
3973
}
4074
50% {
41-
transform: translate3d(50px, 30px, -200px);
75+
transform: translate3d(50px, 30px, -200px) rotate3d(1, 1, 0, 3deg);
4276
}
4377
}
4478

79+
@keyframes tank-current {
80+
0%, 100% { transform: translate3d(0, 0, 0); }
81+
50% { transform: translate3d(10px, -5px, 0); }
82+
}
83+
4584
@keyframes jellyfish-float {
4685
0% {
47-
transform: translate(0, 0) rotate(0deg);
86+
transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
87+
}
88+
50% {
89+
transform: translate3d(10px, -20px, 100px) rotate(5deg) scale(1.1);
90+
}
91+
100% {
92+
transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
93+
}
94+
}
95+
96+
@keyframes jellyfish-playful {
97+
0% {
98+
transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
4899
}
49100
50% {
50-
transform: translate(10px, -20px) rotate(5deg);
101+
transform: translate3d(10px, -20px, 100px) rotate(5deg) scale(1.1);
51102
}
52103
100% {
53-
transform: translate(0, 0) rotate(0deg);
104+
transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
54105
}
55106
}
56107

57108
@keyframes bubble-rise {
58109
0% {
59-
transform: translateY(0) scale(1);
110+
transform: translate3d(0, 0, 0) scale(1);
60111
opacity: 1;
61112
}
62113
100% {
63-
transform: translateY(-100px) scale(0.5);
114+
transform: translate3d(var(--drift-x, 20px), -100px, 50px) scale(0.5);
64115
opacity: 0;
65116
}
66117
}
67118

68119
@keyframes sway {
69-
0%,
70-
100% {
120+
0%, 100% {
121+
transform: rotate(0deg) translate3d(0, 0, 20px);
122+
}
123+
50% {
124+
transform: rotate(5deg) translate3d(0, 0, 40px);
125+
}
126+
}
127+
128+
@keyframes starfish-rotate {
129+
0% {
71130
transform: rotate(0deg);
72131
}
132+
100% {
133+
transform: rotate(15deg);
134+
}
135+
}
136+
137+
@keyframes crab-walk {
138+
0% {
139+
transform: translate3d(0, 0, 10px) rotate(-2deg);
140+
}
141+
25% {
142+
transform: translate3d(5px, 0, 10px) rotate(0deg);
143+
}
144+
75% {
145+
transform: translate3d(-5px, 0, 10px) rotate(0deg);
146+
}
147+
100% {
148+
transform: translate3d(0, 0, 10px) rotate(-2deg);
149+
}
150+
}
151+
152+
@keyframes caustics {
153+
0% {
154+
transform: translate3d(0, 0, 0);
155+
opacity: 0.7;
156+
}
73157
50% {
74-
transform: rotate(5deg);
158+
transform: translate3d(10px, -10px, 0);
159+
opacity: 0.8;
160+
}
161+
100% {
162+
transform: translate3d(-10px, 10px, 0);
163+
opacity: 0.7;
164+
}
165+
}
166+
167+
@keyframes ambient-light-move {
168+
0% { transform: translate(0, 0); }
169+
50% { transform: translate(20px, 20px); }
170+
100% { transform: translate(0, 0); }
171+
}
172+
173+
@keyframes food-pulse {
174+
0%, 100% { transform: scale(1); opacity: 1; }
175+
50% { transform: scale(1.2); opacity: 0.8; }
176+
}
177+
178+
@keyframes water-ripple {
179+
0% { transform: scale(0); opacity: 0.5; }
180+
100% { transform: scale(4); opacity: 0; }
181+
}
182+
183+
@keyframes plankton-drift {
184+
0% { transform: translate(0, 0); opacity: 0; }
185+
50% { opacity: 1; }
186+
100% { transform: translate(50px, -50px); opacity: 0; }
187+
}
188+
189+
@keyframes turtle-swim {
190+
0% { transform: translate3d(0, 0, 0) rotate(0deg); }
191+
50% { transform: translate3d(20px, -10px, 50px) rotate(5deg); }
192+
100% { transform: translate3d(0, 0, 0) rotate(0deg); }
193+
}
194+
195+
@keyframes dolphin-swim {
196+
0% {
197+
transform: translate3d(0, 0, 0) rotate(0deg);
198+
}
199+
50% {
200+
transform: translate3d(20px, -10px, 50px) rotate(5deg);
201+
}
202+
100% {
203+
transform: translate3d(0, 0, 0) rotate(0deg);
204+
}
205+
}
206+
207+
@keyframes sparkle-fade {
208+
0% {
209+
transform: scale(0.5);
210+
opacity: 1;
211+
}
212+
100% {
213+
transform: scale(1.5);
214+
opacity: 0;
75215
}
76216
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { CSSProperties } from "react";
2+
3+
interface CrabProps {
4+
x: number;
5+
y: number;
6+
size?: number;
7+
}
8+
9+
export const Crab: React.FC<CrabProps> = ({ x, y, size = 40 }) => {
10+
const style: CSSProperties = {
11+
position: "absolute",
12+
left: `${x}px`,
13+
top: `${y}px`,
14+
fontSize: `${size}px`,
15+
zIndex: 0,
16+
animation: "crab-walk 4s linear infinite",
17+
};
18+
19+
return <div style={style}>🦀</div>;
20+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { CSSProperties } from "react";
2+
3+
interface DolphinProps {
4+
x: number;
5+
y: number;
6+
size?: number;
7+
}
8+
9+
export const Dolphin: React.FC<DolphinProps> = ({ x, y, size = 40 }) => {
10+
const style: CSSProperties = {
11+
position: "absolute",
12+
left: `${x}px`,
13+
top: `${y}px`,
14+
fontSize: `${size}px`,
15+
zIndex: 1,
16+
transform: `
17+
translate3d(0, 0, ${Math.sin(Date.now() / 2500) * 150}px)
18+
rotateY(${Math.random() < 0.5 ? 0 : 180}deg)
19+
`,
20+
animation: "dolphin-swim 6s ease-in-out infinite",
21+
};
22+
23+
return <div style={style}>🐬</div>;
24+
};

showcase/fishies/src/components/Fish.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface FishProps {
99
isDying?: boolean;
1010
dyingStartTime?: number;
1111
isBeingEaten?: boolean;
12+
isHiding?: boolean;
1213
}
1314

1415
export const Fish: React.FC<FishProps> = ({
@@ -19,6 +20,7 @@ export const Fish: React.FC<FishProps> = ({
1920
isDying,
2021
dyingStartTime,
2122
isBeingEaten,
23+
isHiding,
2224
}) => {
2325
const style: CSSProperties = {
2426
position: "absolute",
@@ -41,7 +43,7 @@ export const Fish: React.FC<FishProps> = ({
4143
transition: isDying ? "opacity 1s ease-out" : "none",
4244
fontSize: "24px",
4345
color,
44-
opacity: isDying ? (Date.now() - dyingStartTime! > 500 ? 0 : 1) : 1,
46+
opacity: isDying ? (Date.now() - dyingStartTime! > 500 ? 0 : 1) : isHiding ? 0.6 : 1,
4547
};
4648

4749
return (

0 commit comments

Comments
 (0)