Fraser commited on
Commit
b1c7f35
·
1 Parent(s): ea83c78
src/lib/components/Battle/BattleControls.svelte CHANGED
@@ -59,10 +59,10 @@
59
  padding: 1rem;
60
  background: #f8f9fa;
61
  border-bottom: 1px solid #e0e0e0;
62
- text-align: center;
63
  display: flex;
64
  align-items: center;
65
- justify-content: center;
66
  }
67
 
68
  .message-bar.special {
 
59
  padding: 1rem;
60
  background: #f8f9fa;
61
  border-bottom: 1px solid #e0e0e0;
62
+ text-align: left;
63
  display: flex;
64
  align-items: center;
65
+ justify-content: flex-start;
66
  }
67
 
68
  .message-bar.special {
src/lib/components/Battle/BattleEffects.svelte CHANGED
@@ -21,6 +21,7 @@
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
@@ -69,7 +70,7 @@
69
  }
70
 
71
  // Watch for faint changes to trigger faint animation
72
- $: if (faint && !isFainting) {
73
  startFaintAnimation();
74
  }
75
 
@@ -118,6 +119,7 @@
118
  // Animation completed
119
  isFainting = false;
120
  faintProgress = 1; // Keep final state
 
121
  }
122
  }
123
 
@@ -143,8 +145,8 @@
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);
@@ -185,6 +187,7 @@
185
  position: relative;
186
  display: inline-block;
187
  transition: opacity 0.05s ease;
 
188
  }
189
 
190
  .effect-particle {
 
21
  let isFainting = false;
22
  let faintProgress = 0;
23
  let faintAnimationId: number;
24
+ let hasFainted = false;
25
 
26
  // Particle system configuration
27
  const PARTICLES_PER_EFFECT = 6; // Number of emoji particles per effect
 
70
  }
71
 
72
  // Watch for faint changes to trigger faint animation
73
+ $: if (faint && !isFainting && !hasFainted) {
74
  startFaintAnimation();
75
  }
76
 
 
119
  // Animation completed
120
  isFainting = false;
121
  faintProgress = 1; // Keep final state
122
+ hasFainted = true; // Mark as permanently fainted
123
  }
124
  }
125
 
 
145
  class="effects-container"
146
  class:is-fainting={faint}
147
  style="
148
+ opacity: {(flash && isFlickering) ? (flickerVisible ? 1 : 0) : (hasFainted || (faint && faintProgress >= 1) ? 0 : 1)};
149
+ {faint || hasFainted ? `
150
  transform:
151
  scale(1, ${Math.max(0, 1 - faintProgress)})
152
  matrix(1, 0, ${-faintProgress * 0.5}, 1, 0, 0);
 
187
  position: relative;
188
  display: inline-block;
189
  transition: opacity 0.05s ease;
190
+ z-index: 2; /* Ensure effects appear above platform (z-index: 0) */
191
  }
192
 
193
  .effect-particle {
src/lib/components/Battle/BattleField.svelte CHANGED
@@ -20,6 +20,8 @@
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;
@@ -74,7 +76,7 @@
74
  />
75
 
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
@@ -118,7 +120,7 @@
118
  <div class="player-row">
119
  <div class="player-stack" class:intro-animations={showIntro}>
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
@@ -335,20 +337,30 @@
335
  /* Animations */
336
  .enemy-piclet-wrapper {
337
  animation-fill-mode: both;
 
338
  }
339
 
340
  .enemy-piclet-wrapper.animate-in {
341
  animation: enemySlideIn 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
342
  }
343
 
 
 
 
 
344
  .player-piclet-wrapper {
345
  animation-fill-mode: both;
 
346
  }
347
 
348
  .player-piclet-wrapper.animate-in {
349
  animation: playerSlideIn 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
350
  }
351
 
 
 
 
 
352
  @keyframes enemySlideIn {
353
  0% {
354
  transform: translateX(150px) translateY(-50px) scale(1.5);
@@ -377,6 +389,30 @@
377
  }
378
  }
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  /* Info box animations */
381
  .enemy-stack.intro-animations :global(.piclet-info-wrapper) {
382
  animation: fadeSlideDown 0.5s ease-out 0.3s both;
 
20
  export let enemyFlash: boolean = false;
21
  export let playerFaint: boolean = false;
22
  export let enemyFaint: boolean = false;
23
+ export let playerLunge: boolean = false;
24
+ export let enemyLunge: boolean = false;
25
 
26
  // Animation states
27
  let playerVisible = false;
 
76
  />
77
 
78
  {#if enemyVisible}
79
+ <div class="enemy-piclet-wrapper" class:animate-in={showIntro} class:lunge={enemyLunge}>
80
  <!-- Enemy Battle Effects wrap the image for flicker animation -->
81
  <BattleEffects effects={enemyEffects} flash={enemyFlash} faint={enemyFaint}>
82
  <img
 
120
  <div class="player-row">
121
  <div class="player-stack" class:intro-animations={showIntro}>
122
  {#if playerVisible}
123
+ <div class="player-piclet-wrapper" class:animate-in={showIntro} class:lunge={playerLunge}>
124
  <!-- Player Battle Effects wrap the image for flicker animation -->
125
  <BattleEffects effects={playerEffects} flash={playerFlash} faint={playerFaint}>
126
  <img
 
337
  /* Animations */
338
  .enemy-piclet-wrapper {
339
  animation-fill-mode: both;
340
+ transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
341
  }
342
 
343
  .enemy-piclet-wrapper.animate-in {
344
  animation: enemySlideIn 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
345
  }
346
 
347
+ .enemy-piclet-wrapper.lunge {
348
+ animation: enemyLunge 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
349
+ }
350
+
351
  .player-piclet-wrapper {
352
  animation-fill-mode: both;
353
+ transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
354
  }
355
 
356
  .player-piclet-wrapper.animate-in {
357
  animation: playerSlideIn 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
358
  }
359
 
360
+ .player-piclet-wrapper.lunge {
361
+ animation: playerLunge 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
362
+ }
363
+
364
  @keyframes enemySlideIn {
365
  0% {
366
  transform: translateX(150px) translateY(-50px) scale(1.5);
 
389
  }
390
  }
391
 
392
+ @keyframes enemyLunge {
393
+ 0% {
394
+ transform: translateX(0) translateY(0) scale(1);
395
+ }
396
+ 40% {
397
+ transform: translateX(-60px) translateY(10px) scale(1.1);
398
+ }
399
+ 100% {
400
+ transform: translateX(0) translateY(0) scale(1);
401
+ }
402
+ }
403
+
404
+ @keyframes playerLunge {
405
+ 0% {
406
+ transform: translateX(0) translateY(0) scale(1);
407
+ }
408
+ 40% {
409
+ transform: translateX(60px) translateY(-10px) scale(1.1);
410
+ }
411
+ 100% {
412
+ transform: translateX(0) translateY(0) scale(1);
413
+ }
414
+ }
415
+
416
  /* Info box animations */
417
  .enemy-stack.intro-animations :global(.piclet-info-wrapper) {
418
  animation: fadeSlideDown 0.5s ease-out 0.3s both;
src/lib/components/Battle/PicletInfo.svelte CHANGED
@@ -18,17 +18,6 @@
18
  $: typeLogoPath = `/classes/${piclet.primaryType}.png`;
19
 
20
  $: hpColor = hpPercentage > 0.5 ? '#34c759' : hpPercentage > 0.25 ? '#ffcc00' : '#ff3b30';
21
- $: displayHp = Math.ceil(piclet.currentHp);
22
-
23
- // Track HP changes for animations
24
- let previousHp = displayHp;
25
- let hpFlash = false;
26
-
27
- $: if (displayHp !== previousHp) {
28
- hpFlash = true;
29
- setTimeout(() => hpFlash = false, 300);
30
- previousHp = displayHp;
31
- }
32
  </script>
33
 
34
  <div class="piclet-info-wrapper {isPlayer ? 'player-info-wrapper' : 'enemy-info-wrapper'}">
@@ -43,7 +32,7 @@
43
  </div>
44
 
45
  <!-- HP Section -->
46
- <div class="stat-section">
47
  <div class="stat-label">HP</div>
48
  <div class="hp-bar">
49
  <div
@@ -51,16 +40,11 @@
51
  style="width: {hpPercentage * 100}%; background-color: {hpColor}"
52
  ></div>
53
  </div>
54
- {#if isPlayer}
55
- <div class="hp-text">
56
- <span class="hp-values" class:hp-flash={hpFlash}>{displayHp}/{piclet.maxHp}</span>
57
- </div>
58
- {/if}
59
  </div>
60
 
61
  <!-- XP Section (Player only) -->
62
  {#if isPlayer}
63
- <div class="stat-section">
64
  <div class="stat-label">XP</div>
65
  <div class="xp-bar">
66
  <div
@@ -145,14 +129,17 @@
145
  color: #333;
146
  }
147
 
148
- /* Stat Sections */
149
- .stat-section {
150
- margin-bottom: 6px;
 
 
 
151
  position: relative;
152
  z-index: 2;
153
  }
154
 
155
- .stat-section:last-child {
156
  margin-bottom: 0;
157
  }
158
 
@@ -160,9 +147,10 @@
160
  font-size: 10px;
161
  font-weight: 600;
162
  color: #666;
163
- margin-bottom: 2px;
164
  text-transform: uppercase;
165
  letter-spacing: 0.5px;
 
 
166
  }
167
 
168
  /* HP Bar */
@@ -172,7 +160,7 @@
172
  background: #e0e0e0;
173
  border-radius: 4px;
174
  overflow: hidden;
175
- margin-bottom: 2px;
176
  }
177
 
178
  .hp-fill {
@@ -180,29 +168,6 @@
180
  transition: width 0.5s ease, background-color 0.3s ease;
181
  }
182
 
183
- /* HP Text */
184
- .hp-text {
185
- font-size: 11px;
186
- color: #666;
187
- }
188
-
189
- .hp-values {
190
- font-weight: 600;
191
- transition: all 0.2s ease;
192
- }
193
-
194
- .hp-values.hp-flash {
195
- color: #ff4444;
196
- text-shadow: 0 0 8px rgba(255, 68, 68, 0.6);
197
- animation: hpFlash 0.3s ease-in-out;
198
- }
199
-
200
- @keyframes hpFlash {
201
- 0% { transform: scale(1); }
202
- 50% { transform: scale(1.1); }
203
- 100% { transform: scale(1); }
204
- }
205
-
206
  /* XP Bar */
207
  .xp-bar {
208
  width: 120px;
@@ -210,6 +175,7 @@
210
  background: #e0e0e0;
211
  border-radius: 3px;
212
  overflow: hidden;
 
213
  }
214
 
215
  .xp-fill {
@@ -248,11 +214,7 @@
248
  font-size: 12px;
249
  }
250
 
251
- .hp-bar {
252
- width: 100px;
253
- }
254
-
255
- .xp-bar {
256
  width: 100px;
257
  }
258
  }
 
18
  $: typeLogoPath = `/classes/${piclet.primaryType}.png`;
19
 
20
  $: hpColor = hpPercentage > 0.5 ? '#34c759' : hpPercentage > 0.25 ? '#ffcc00' : '#ff3b30';
 
 
 
 
 
 
 
 
 
 
 
21
  </script>
22
 
23
  <div class="piclet-info-wrapper {isPlayer ? 'player-info-wrapper' : 'enemy-info-wrapper'}">
 
32
  </div>
33
 
34
  <!-- HP Section -->
35
+ <div class="stat-row">
36
  <div class="stat-label">HP</div>
37
  <div class="hp-bar">
38
  <div
 
40
  style="width: {hpPercentage * 100}%; background-color: {hpColor}"
41
  ></div>
42
  </div>
 
 
 
 
 
43
  </div>
44
 
45
  <!-- XP Section (Player only) -->
46
  {#if isPlayer}
47
+ <div class="stat-row">
48
  <div class="stat-label">XP</div>
49
  <div class="xp-bar">
50
  <div
 
129
  color: #333;
130
  }
131
 
132
+ /* Stat Rows */
133
+ .stat-row {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 8px;
137
+ margin-bottom: 4px;
138
  position: relative;
139
  z-index: 2;
140
  }
141
 
142
+ .stat-row:last-child {
143
  margin-bottom: 0;
144
  }
145
 
 
147
  font-size: 10px;
148
  font-weight: 600;
149
  color: #666;
 
150
  text-transform: uppercase;
151
  letter-spacing: 0.5px;
152
+ width: 18px;
153
+ flex-shrink: 0;
154
  }
155
 
156
  /* HP Bar */
 
160
  background: #e0e0e0;
161
  border-radius: 4px;
162
  overflow: hidden;
163
+ flex: 1;
164
  }
165
 
166
  .hp-fill {
 
168
  transition: width 0.5s ease, background-color 0.3s ease;
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  /* XP Bar */
172
  .xp-bar {
173
  width: 120px;
 
175
  background: #e0e0e0;
176
  border-radius: 3px;
177
  overflow: hidden;
178
+ flex: 1;
179
  }
180
 
181
  .xp-fill {
 
214
  font-size: 12px;
215
  }
216
 
217
+ .hp-bar, .xp-bar {
 
 
 
 
218
  width: 100px;
219
  }
220
  }
src/lib/components/Pages/Battle.svelte CHANGED
@@ -42,6 +42,8 @@
42
  let enemyFlash = false;
43
  let playerFaint = false;
44
  let enemyFaint = false;
 
 
45
 
46
  // Battle results state
47
  let battleResultsVisible = false;
@@ -222,6 +224,15 @@
222
  const playerInternalName = battleState?.playerPiclet?.definition?.name || '';
223
  const enemyInternalName = battleState?.opponentPiclet?.definition?.name || '';
224
 
 
 
 
 
 
 
 
 
 
225
  // Damage effects
226
  if (message.includes('took') && message.includes('damage')) {
227
  if (message.includes(playerInternalName)) {
@@ -322,6 +333,16 @@
322
  }
323
  }
324
 
 
 
 
 
 
 
 
 
 
 
325
  function triggerEffect(target: 'player' | 'enemy' | 'both', type: string, emoji: string, duration: number) {
326
  const effect = { type, emoji, duration };
327
 
@@ -544,6 +565,8 @@
544
  {enemyFlash}
545
  {playerFaint}
546
  {enemyFaint}
 
 
547
  />
548
 
549
  <BattleControls
 
42
  let enemyFlash = false;
43
  let playerFaint = false;
44
  let enemyFaint = false;
45
+ let playerLunge = false;
46
+ let enemyLunge = false;
47
 
48
  // Battle results state
49
  let battleResultsVisible = false;
 
224
  const playerInternalName = battleState?.playerPiclet?.definition?.name || '';
225
  const enemyInternalName = battleState?.opponentPiclet?.definition?.name || '';
226
 
227
+ // Attack lunge effects - trigger when a Piclet uses a move
228
+ if (message.includes(' used ')) {
229
+ if (message.includes(playerInternalName)) {
230
+ triggerLungeAnimation('player');
231
+ } else if (message.includes(enemyInternalName)) {
232
+ triggerLungeAnimation('enemy');
233
+ }
234
+ }
235
+
236
  // Damage effects
237
  if (message.includes('took') && message.includes('damage')) {
238
  if (message.includes(playerInternalName)) {
 
333
  }
334
  }
335
 
336
+ function triggerLungeAnimation(target: 'player' | 'enemy') {
337
+ if (target === 'player') {
338
+ playerLunge = true;
339
+ setTimeout(() => playerLunge = false, 600); // Reset after animation
340
+ } else {
341
+ enemyLunge = true;
342
+ setTimeout(() => enemyLunge = false, 600); // Reset after animation
343
+ }
344
+ }
345
+
346
  function triggerEffect(target: 'player' | 'enemy' | 'both', type: string, emoji: string, duration: number) {
347
  const effect = { type, emoji, duration };
348
 
 
565
  {enemyFlash}
566
  {playerFaint}
567
  {enemyFaint}
568
+ {playerLunge}
569
+ {enemyLunge}
570
  />
571
 
572
  <BattleControls