faint
Browse files
src/lib/components/Battle/BattleEffects.svelte
CHANGED
@@ -4,11 +4,12 @@
|
|
4 |
|
5 |
export let effects: Array<{type: string, emoji: string, duration: number}> = [];
|
6 |
export let flash: boolean = false;
|
|
|
7 |
|
8 |
-
// GBA-style flicker animation parameters
|
9 |
const flickerCount = 19;
|
10 |
const frameDelay = 2;
|
11 |
-
const flickerDuration =
|
12 |
|
13 |
// Flicker state management
|
14 |
let isFlickering = false;
|
@@ -16,6 +17,11 @@
|
|
16 |
let flickerFrame = 0;
|
17 |
let flickerInterval: number;
|
18 |
|
|
|
|
|
|
|
|
|
|
|
19 |
// Particle system configuration
|
20 |
const PARTICLES_PER_EFFECT = 6; // Number of emoji particles per effect
|
21 |
const SPAWN_RADIUS = 80; // Increased radius around piclet where particles spawn
|
@@ -62,6 +68,11 @@
|
|
62 |
startFlickerAnimation();
|
63 |
}
|
64 |
|
|
|
|
|
|
|
|
|
|
|
65 |
function startFlickerAnimation() {
|
66 |
isFlickering = true;
|
67 |
flickerFrame = 0;
|
@@ -87,19 +98,60 @@
|
|
87 |
}, frameDuration);
|
88 |
}
|
89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
onMount(() => {
|
91 |
return () => {
|
92 |
if (flickerInterval) {
|
93 |
clearInterval(flickerInterval);
|
94 |
}
|
|
|
|
|
|
|
95 |
};
|
96 |
});
|
97 |
</script>
|
98 |
|
99 |
<!-- Effects wrapper with relative positioning for particles -->
|
100 |
<div class="effects-wrapper">
|
101 |
-
<!-- GBA-style flicker effect -->
|
102 |
-
<div
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
<slot />
|
104 |
</div>
|
105 |
|
|
|
4 |
|
5 |
export let effects: Array<{type: string, emoji: string, duration: number}> = [];
|
6 |
export let flash: boolean = false;
|
7 |
+
export let faint: boolean = false;
|
8 |
|
9 |
+
// GBA-style flicker animation parameters (matching original Snaplings timing)
|
10 |
const flickerCount = 19;
|
11 |
const frameDelay = 2;
|
12 |
+
const flickerDuration = 1000; // milliseconds - matches Snaplings original
|
13 |
|
14 |
// Flicker state management
|
15 |
let isFlickering = false;
|
|
|
17 |
let flickerFrame = 0;
|
18 |
let flickerInterval: number;
|
19 |
|
20 |
+
// Faint animation state management
|
21 |
+
let isFainting = false;
|
22 |
+
let faintProgress = 0;
|
23 |
+
let faintAnimationId: number;
|
24 |
+
|
25 |
// Particle system configuration
|
26 |
const PARTICLES_PER_EFFECT = 6; // Number of emoji particles per effect
|
27 |
const SPAWN_RADIUS = 80; // Increased radius around piclet where particles spawn
|
|
|
68 |
startFlickerAnimation();
|
69 |
}
|
70 |
|
71 |
+
// Watch for faint changes to trigger faint animation
|
72 |
+
$: if (faint && !isFainting) {
|
73 |
+
startFaintAnimation();
|
74 |
+
}
|
75 |
+
|
76 |
function startFlickerAnimation() {
|
77 |
isFlickering = true;
|
78 |
flickerFrame = 0;
|
|
|
98 |
}, frameDuration);
|
99 |
}
|
100 |
|
101 |
+
function startFaintAnimation() {
|
102 |
+
isFainting = true;
|
103 |
+
faintProgress = 0;
|
104 |
+
|
105 |
+
const faintDuration = 1200; // milliseconds - matches Snaplings original
|
106 |
+
const startTime = performance.now();
|
107 |
+
|
108 |
+
function updateFaintAnimation(currentTime: number) {
|
109 |
+
const elapsed = currentTime - startTime;
|
110 |
+
const progress = Math.min(elapsed / faintDuration, 1);
|
111 |
+
|
112 |
+
// Use easeIn curve for acceleration as it falls away
|
113 |
+
faintProgress = progress * progress;
|
114 |
+
|
115 |
+
if (progress < 1) {
|
116 |
+
faintAnimationId = requestAnimationFrame(updateFaintAnimation);
|
117 |
+
} else {
|
118 |
+
// Animation completed
|
119 |
+
isFainting = false;
|
120 |
+
faintProgress = 1; // Keep final state
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
faintAnimationId = requestAnimationFrame(updateFaintAnimation);
|
125 |
+
}
|
126 |
+
|
127 |
onMount(() => {
|
128 |
return () => {
|
129 |
if (flickerInterval) {
|
130 |
clearInterval(flickerInterval);
|
131 |
}
|
132 |
+
if (faintAnimationId) {
|
133 |
+
cancelAnimationFrame(faintAnimationId);
|
134 |
+
}
|
135 |
};
|
136 |
});
|
137 |
</script>
|
138 |
|
139 |
<!-- Effects wrapper with relative positioning for particles -->
|
140 |
<div class="effects-wrapper">
|
141 |
+
<!-- GBA-style flicker effect with faint animation -->
|
142 |
+
<div
|
143 |
+
class="effects-container"
|
144 |
+
class:is-fainting={faint}
|
145 |
+
style="
|
146 |
+
opacity: {(flash && isFlickering) ? (flickerVisible ? 1 : 0) : (faint && faintProgress >= 1 ? 0 : 1)};
|
147 |
+
{faint ? `
|
148 |
+
transform:
|
149 |
+
scale(1, ${Math.max(0, 1 - faintProgress)})
|
150 |
+
matrix(1, 0, ${-faintProgress * 0.5}, 1, 0, 0);
|
151 |
+
transform-origin: bottom center;
|
152 |
+
` : ''}
|
153 |
+
"
|
154 |
+
>
|
155 |
<slot />
|
156 |
</div>
|
157 |
|
src/lib/components/Battle/BattleField.svelte
CHANGED
@@ -18,6 +18,8 @@
|
|
18 |
export let enemyEffects: Array<{type: string, emoji: string, duration: number}> = [];
|
19 |
export let playerFlash: boolean = false;
|
20 |
export let enemyFlash: boolean = false;
|
|
|
|
|
21 |
|
22 |
// Animation states
|
23 |
let playerVisible = false;
|
@@ -74,7 +76,7 @@
|
|
74 |
{#if enemyVisible}
|
75 |
<div class="enemy-piclet-wrapper" class:animate-in={showIntro}>
|
76 |
<!-- Enemy Battle Effects wrap the image for flicker animation -->
|
77 |
-
<BattleEffects effects={enemyEffects} flash={enemyFlash}>
|
78 |
<img
|
79 |
class="piclet-image enemy-image"
|
80 |
src={enemyPiclet.imageData || enemyPiclet.imageUrl}
|
@@ -118,7 +120,7 @@
|
|
118 |
{#if playerVisible}
|
119 |
<div class="player-piclet-wrapper" class:animate-in={showIntro}>
|
120 |
<!-- Player Battle Effects wrap the image for flicker animation -->
|
121 |
-
<BattleEffects effects={playerEffects} flash={playerFlash}>
|
122 |
<img
|
123 |
class="piclet-image player-image"
|
124 |
src={playerPiclet.imageData || playerPiclet.imageUrl}
|
|
|
18 |
export let enemyEffects: Array<{type: string, emoji: string, duration: number}> = [];
|
19 |
export let playerFlash: boolean = false;
|
20 |
export let enemyFlash: boolean = false;
|
21 |
+
export let playerFaint: boolean = false;
|
22 |
+
export let enemyFaint: boolean = false;
|
23 |
|
24 |
// Animation states
|
25 |
let playerVisible = false;
|
|
|
76 |
{#if enemyVisible}
|
77 |
<div class="enemy-piclet-wrapper" class:animate-in={showIntro}>
|
78 |
<!-- Enemy Battle Effects wrap the image for flicker animation -->
|
79 |
+
<BattleEffects effects={enemyEffects} flash={enemyFlash} faint={enemyFaint}>
|
80 |
<img
|
81 |
class="piclet-image enemy-image"
|
82 |
src={enemyPiclet.imageData || enemyPiclet.imageUrl}
|
|
|
120 |
{#if playerVisible}
|
121 |
<div class="player-piclet-wrapper" class:animate-in={showIntro}>
|
122 |
<!-- Player Battle Effects wrap the image for flicker animation -->
|
123 |
+
<BattleEffects effects={playerEffects} flash={playerFlash} faint={playerFaint}>
|
124 |
<img
|
125 |
class="piclet-image player-image"
|
126 |
src={playerPiclet.imageData || playerPiclet.imageUrl}
|
src/lib/components/Pages/Battle.svelte
CHANGED
@@ -38,6 +38,8 @@
|
|
38 |
let enemyEffects: Array<{type: string, emoji: string, duration: number}> = [];
|
39 |
let playerFlash = false;
|
40 |
let enemyFlash = false;
|
|
|
|
|
41 |
|
42 |
onMount(() => {
|
43 |
// Initialize battle engine with converted piclet definitions
|
@@ -253,15 +255,34 @@
|
|
253 |
if (message.includes('missed')) {
|
254 |
triggerEffect('both', 'miss', '💫', 800);
|
255 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
}
|
257 |
|
258 |
function triggerDamageFlash(target: 'player' | 'enemy') {
|
259 |
if (target === 'player') {
|
260 |
playerFlash = true;
|
261 |
-
setTimeout(() => playerFlash = false,
|
262 |
} else {
|
263 |
enemyFlash = true;
|
264 |
-
setTimeout(() => enemyFlash = false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
}
|
266 |
}
|
267 |
|
@@ -348,6 +369,8 @@
|
|
348 |
{enemyEffects}
|
349 |
{playerFlash}
|
350 |
{enemyFlash}
|
|
|
|
|
351 |
/>
|
352 |
|
353 |
<BattleControls
|
|
|
38 |
let enemyEffects: Array<{type: string, emoji: string, duration: number}> = [];
|
39 |
let playerFlash = false;
|
40 |
let enemyFlash = false;
|
41 |
+
let playerFaint = false;
|
42 |
+
let enemyFaint = false;
|
43 |
|
44 |
onMount(() => {
|
45 |
// Initialize battle engine with converted piclet definitions
|
|
|
255 |
if (message.includes('missed')) {
|
256 |
triggerEffect('both', 'miss', '💫', 800);
|
257 |
}
|
258 |
+
|
259 |
+
// Faint effects
|
260 |
+
if (message.includes('fainted')) {
|
261 |
+
if (message.includes(playerName)) {
|
262 |
+
triggerFaintAnimation('player');
|
263 |
+
} else if (message.includes(enemyName)) {
|
264 |
+
triggerFaintAnimation('enemy');
|
265 |
+
}
|
266 |
+
}
|
267 |
}
|
268 |
|
269 |
function triggerDamageFlash(target: 'player' | 'enemy') {
|
270 |
if (target === 'player') {
|
271 |
playerFlash = true;
|
272 |
+
setTimeout(() => playerFlash = false, 1000); // Match original Snaplings flicker duration
|
273 |
} else {
|
274 |
enemyFlash = true;
|
275 |
+
setTimeout(() => enemyFlash = false, 1000); // Match original Snaplings flicker duration
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
function triggerFaintAnimation(target: 'player' | 'enemy') {
|
280 |
+
if (target === 'player') {
|
281 |
+
playerFaint = true;
|
282 |
+
// Don't reset - faint animation should persist until battle ends
|
283 |
+
} else {
|
284 |
+
enemyFaint = true;
|
285 |
+
// Don't reset - faint animation should persist until battle ends
|
286 |
}
|
287 |
}
|
288 |
|
|
|
369 |
{enemyEffects}
|
370 |
{playerFlash}
|
371 |
{enemyFlash}
|
372 |
+
{playerFaint}
|
373 |
+
{enemyFaint}
|
374 |
/>
|
375 |
|
376 |
<BattleControls
|