Fraser commited on
Commit
9774b79
·
1 Parent(s): 366ca7a

better battle

Browse files
src/lib/components/Battle/BattleField.svelte CHANGED
@@ -22,25 +22,43 @@
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;
28
  let enemyVisible = false;
29
  let trainerVisible = true;
30
 
 
 
 
 
31
  // Calculate player XP percentage (0-100% of current level)
32
  const playerXpPercentage = (playerPiclet.xp / 100) * 100; // Simplified, should use actual XP curve
33
 
34
  onMount(() => {
35
  if (showIntro) {
36
- // Intro animation sequence
37
  setTimeout(() => {
38
  trainerVisible = false;
39
- enemyVisible = true;
 
 
 
 
40
  }, 500);
41
 
42
  setTimeout(() => {
43
- playerVisible = true;
 
 
 
 
44
  }, 1000);
45
  } else {
46
  // Skip intro
@@ -49,9 +67,41 @@
49
  trainerVisible = false;
50
  }
51
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  </script>
53
 
54
  <div class="battle-field">
 
 
 
 
 
55
  <!-- Trainer intro image -->
56
  {#if showIntro && trainerVisible}
57
  <div class="trainer-intro" transition:fade={{ duration: 300 }}>
@@ -59,6 +109,20 @@
59
  </div>
60
  {/if}
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  <div class="battle-content">
63
  <!-- Field Effects Display -->
64
  {#if battleState?.fieldEffects}
@@ -181,6 +245,16 @@
181
  );
182
  }
183
 
 
 
 
 
 
 
 
 
 
 
184
  .trainer-intro {
185
  position: absolute;
186
  top: 50%;
@@ -195,6 +269,41 @@
195
  animation: trainerPulse 0.5s ease-in-out;
196
  }
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  @keyframes trainerPulse {
199
  0% {
200
  transform: scale(1);
 
22
  export let enemyFaint: boolean = false;
23
  export let playerLunge: boolean = false;
24
  export let enemyLunge: boolean = false;
25
+ export let isWildBattle: boolean = true;
26
+ export let showWhiteFlash: boolean = false;
27
+ export let playerTrainerVisible: boolean = false;
28
+ export let enemyTrainerVisible: boolean = false;
29
+ export let playerTrainerSlideOut: boolean = false;
30
+ export let enemyTrainerSlideOut: boolean = false;
31
 
32
  // Animation states
33
  let playerVisible = false;
34
  let enemyVisible = false;
35
  let trainerVisible = true;
36
 
37
+ // Trainer animation states
38
+ let playerTrainerSliding = false;
39
+ let enemyTrainerSliding = false;
40
+
41
  // Calculate player XP percentage (0-100% of current level)
42
  const playerXpPercentage = (playerPiclet.xp / 100) * 100; // Simplified, should use actual XP curve
43
 
44
  onMount(() => {
45
  if (showIntro) {
46
+ // Intro animation sequence - show trainers first
47
  setTimeout(() => {
48
  trainerVisible = false;
49
+ if (!isWildBattle) {
50
+ enemyTrainerVisible = true;
51
+ } else {
52
+ enemyVisible = true;
53
+ }
54
  }, 500);
55
 
56
  setTimeout(() => {
57
+ if (!isWildBattle) {
58
+ playerTrainerVisible = true;
59
+ } else {
60
+ playerTrainerVisible = true; // Show player trainer even in wild battles
61
+ }
62
  }, 1000);
63
  } else {
64
  // Skip intro
 
67
  trainerVisible = false;
68
  }
69
  });
70
+
71
+ // Watch for trainer slide-out triggers
72
+ $: if (playerTrainerSlideOut && !playerTrainerSliding) {
73
+ playerTrainerSliding = true;
74
+ setTimeout(() => {
75
+ playerTrainerVisible = false;
76
+ // Trigger white flash then show player monster
77
+ showWhiteFlash = true;
78
+ setTimeout(() => {
79
+ showWhiteFlash = false;
80
+ playerVisible = true;
81
+ }, 300);
82
+ }, 600); // Time for slide-out animation
83
+ }
84
+
85
+ $: if (enemyTrainerSlideOut && !enemyTrainerSliding) {
86
+ enemyTrainerSliding = true;
87
+ setTimeout(() => {
88
+ enemyTrainerVisible = false;
89
+ // Trigger white flash then show enemy monster
90
+ showWhiteFlash = true;
91
+ setTimeout(() => {
92
+ showWhiteFlash = false;
93
+ enemyVisible = true;
94
+ }, 300);
95
+ }, 600); // Time for slide-out animation
96
+ }
97
  </script>
98
 
99
  <div class="battle-field">
100
+ <!-- White flash overlay -->
101
+ {#if showWhiteFlash}
102
+ <div class="white-flash" transition:fade={{ duration: 300 }}></div>
103
+ {/if}
104
+
105
  <!-- Trainer intro image -->
106
  {#if showIntro && trainerVisible}
107
  <div class="trainer-intro" transition:fade={{ duration: 300 }}>
 
109
  </div>
110
  {/if}
111
 
112
+ <!-- Player Trainer -->
113
+ {#if playerTrainerVisible}
114
+ <div class="player-trainer" class:slide-out-left={playerTrainerSlideOut} transition:fade={{ duration: 300 }}>
115
+ <img src="/assets/default_trainer.png" alt="Player Trainer" />
116
+ </div>
117
+ {/if}
118
+
119
+ <!-- Enemy Trainer (only for trainer battles) -->
120
+ {#if !isWildBattle && enemyTrainerVisible}
121
+ <div class="enemy-trainer" class:slide-out-right={enemyTrainerSlideOut} transition:fade={{ duration: 300 }}>
122
+ <img src="/assets/default_trainer.png" alt="Enemy Trainer" />
123
+ </div>
124
+ {/if}
125
+
126
  <div class="battle-content">
127
  <!-- Field Effects Display -->
128
  {#if battleState?.fieldEffects}
 
245
  );
246
  }
247
 
248
+ .white-flash {
249
+ position: fixed;
250
+ top: 0;
251
+ left: 0;
252
+ right: 0;
253
+ bottom: 0;
254
+ background: white;
255
+ z-index: 50;
256
+ }
257
+
258
  .trainer-intro {
259
  position: absolute;
260
  top: 50%;
 
269
  animation: trainerPulse 0.5s ease-in-out;
270
  }
271
 
272
+ .player-trainer {
273
+ position: absolute;
274
+ bottom: 20px;
275
+ left: 20px;
276
+ z-index: 10;
277
+ transition: transform 0.6s ease-in-out;
278
+ }
279
+
280
+ .player-trainer img {
281
+ width: 120px;
282
+ height: auto;
283
+ transform: scaleX(-1); /* Face right */
284
+ }
285
+
286
+ .player-trainer.slide-out-left {
287
+ transform: translateX(-200px);
288
+ }
289
+
290
+ .enemy-trainer {
291
+ position: absolute;
292
+ top: 20px;
293
+ right: 20px;
294
+ z-index: 10;
295
+ transition: transform 0.6s ease-in-out;
296
+ }
297
+
298
+ .enemy-trainer img {
299
+ width: 120px;
300
+ height: auto;
301
+ }
302
+
303
+ .enemy-trainer.slide-out-right {
304
+ transform: translateX(200px);
305
+ }
306
+
307
  @keyframes trainerPulse {
308
  0% {
309
  transform: scale(1);
src/lib/components/Pages/Battle.svelte CHANGED
@@ -31,6 +31,13 @@
31
  let processingTurn = false;
32
  let battleEnded = false;
33
 
 
 
 
 
 
 
 
34
  // HP animation states
35
  let playerHpPercentage = playerPiclet.currentHp / playerPiclet.maxHp;
36
  let enemyHpPercentage = enemyPiclet.currentHp / enemyPiclet.maxHp;
@@ -81,11 +88,27 @@
81
 
82
  // Start intro sequence
83
  setTimeout(() => {
84
- currentMessage = `Go, ${playerPiclet.nickname}!`;
85
- setTimeout(() => {
86
- currentMessage = `What will ${playerPiclet.nickname} do?`;
87
- battlePhase = 'main';
88
- }, 1500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }, 2000);
90
  });
91
 
@@ -185,13 +208,13 @@
185
  // Update UI state from battle engine
186
  updateUIFromBattleState();
187
 
188
- // Check for battle end
189
  if (battleState.winner) {
190
  battleEnded = true;
191
- const winMessage = battleState.winner === 'player'
192
- ? `${currentEnemyPiclet.nickname} fainted! You won!`
193
- : `${currentPlayerPiclet.nickname} fainted! You lost!`;
194
- currentMessage = winMessage;
195
 
196
  // Trigger faint animation for the defeated Piclet
197
  if (battleState.winner === 'player') {
@@ -200,10 +223,18 @@
200
  playerFaint = true;
201
  }
202
 
203
- // Process battle results with XP and level ups
204
- setTimeout(async () => {
205
- await handleBattleResults(battleState.winner === 'player');
206
- }, 2000);
 
 
 
 
 
 
 
 
207
  } else {
208
  setTimeout(() => {
209
  currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
@@ -303,14 +334,8 @@
303
  triggerEffect('both', 'miss', '💫', 800);
304
  }
305
 
306
- // Faint effects
307
- if (message.includes('fainted')) {
308
- if (message.includes(playerInternalName)) {
309
- triggerFaintAnimation('player');
310
- } else if (message.includes(enemyInternalName)) {
311
- triggerFaintAnimation('enemy');
312
- }
313
- }
314
  }
315
 
316
  function triggerDamageFlash(target: 'player' | 'enemy') {
@@ -387,10 +412,16 @@
387
  return;
388
  }
389
 
390
- // Update display immediately before processing the turn
391
- currentPlayerPiclet = piclet;
392
- playerHpPercentage = piclet.currentHp / piclet.maxHp;
393
  currentMessage = `Go, ${piclet.nickname}!`;
 
 
 
 
 
 
 
 
394
 
395
  const switchAction: SwitchAction = {
396
  type: 'switch',
@@ -467,13 +498,13 @@
467
  // Update UI state from battle engine
468
  updateUIFromBattleState();
469
 
470
- // Check for battle end
471
  if (battleState.winner) {
472
  battleEnded = true;
473
- const winMessage = battleState.winner === 'player'
474
- ? `${currentEnemyPiclet.nickname} fainted! You won!`
475
- : `${currentPlayerPiclet.nickname} fainted! You lost!`;
476
- currentMessage = winMessage;
477
 
478
  // Trigger faint animation for the defeated Piclet
479
  if (battleState.winner === 'player') {
@@ -482,10 +513,18 @@
482
  playerFaint = true;
483
  }
484
 
485
- // Process battle results with XP and level ups
486
- setTimeout(async () => {
487
- await handleBattleResults(battleState.winner === 'player');
488
- }, 2000);
 
 
 
 
 
 
 
 
489
  } else {
490
  setTimeout(() => {
491
  currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
@@ -586,6 +625,12 @@
586
  {enemyFaint}
587
  {playerLunge}
588
  {enemyLunge}
 
 
 
 
 
 
589
  />
590
 
591
  <BattleControls
 
31
  let processingTurn = false;
32
  let battleEnded = false;
33
 
34
+ // Trainer animation states
35
+ let showWhiteFlash = false;
36
+ let playerTrainerVisible = false;
37
+ let enemyTrainerVisible = false;
38
+ let playerTrainerSlideOut = false;
39
+ let enemyTrainerSlideOut = false;
40
+
41
  // HP animation states
42
  let playerHpPercentage = playerPiclet.currentHp / playerPiclet.maxHp;
43
  let enemyHpPercentage = enemyPiclet.currentHp / enemyPiclet.maxHp;
 
88
 
89
  // Start intro sequence
90
  setTimeout(() => {
91
+ if (!isWildBattle) {
92
+ // Enemy trainer sends out first
93
+ currentMessage = `Go, ${enemyPiclet.nickname}!`;
94
+ enemyTrainerSlideOut = true;
95
+ setTimeout(() => {
96
+ currentMessage = `Go, ${playerPiclet.nickname}!`;
97
+ playerTrainerSlideOut = true;
98
+ setTimeout(() => {
99
+ currentMessage = `What will ${playerPiclet.nickname} do?`;
100
+ battlePhase = 'main';
101
+ }, 2000); // Wait for trainer slide + flash + monster appear
102
+ }, 2000); // Wait for enemy trainer sequence
103
+ } else {
104
+ // Wild battle - player sends out
105
+ currentMessage = `Go, ${playerPiclet.nickname}!`;
106
+ playerTrainerSlideOut = true;
107
+ setTimeout(() => {
108
+ currentMessage = `What will ${playerPiclet.nickname} do?`;
109
+ battlePhase = 'main';
110
+ }, 2000); // Wait for trainer slide + flash + monster appear
111
+ }
112
  }, 2000);
113
  });
114
 
 
208
  // Update UI state from battle engine
209
  updateUIFromBattleState();
210
 
211
+ // Check for battle end - but handle faint message sequence properly
212
  if (battleState.winner) {
213
  battleEnded = true;
214
+ const defeatedPiclet = battleState.winner === 'player' ? currentEnemyPiclet : currentPlayerPiclet;
215
+
216
+ // First show the faint message and trigger animation
217
+ currentMessage = `${defeatedPiclet.nickname} fainted!`;
218
 
219
  // Trigger faint animation for the defeated Piclet
220
  if (battleState.winner === 'player') {
 
223
  playerFaint = true;
224
  }
225
 
226
+ // Wait for faint animation, then show win/loss message
227
+ setTimeout(() => {
228
+ const winMessage = battleState.winner === 'player'
229
+ ? 'You won!'
230
+ : 'You lost!';
231
+ currentMessage = winMessage;
232
+
233
+ // Process battle results with XP and level ups
234
+ setTimeout(async () => {
235
+ await handleBattleResults(battleState.winner === 'player');
236
+ }, 1500);
237
+ }, 2000); // Wait for faint animation
238
  } else {
239
  setTimeout(() => {
240
  currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
 
334
  triggerEffect('both', 'miss', '💫', 800);
335
  }
336
 
337
+ // Faint effects - only trigger when we see the faint message from battle log
338
+ // Don't trigger here since we handle it in finalizeTurn for proper sequencing
 
 
 
 
 
 
339
  }
340
 
341
  function triggerDamageFlash(target: 'player' | 'enemy') {
 
412
  return;
413
  }
414
 
415
+ // Show the switch message and trigger white flash animation
 
 
416
  currentMessage = `Go, ${piclet.nickname}!`;
417
+ showWhiteFlash = true;
418
+
419
+ // After flash, update display
420
+ setTimeout(() => {
421
+ currentPlayerPiclet = piclet;
422
+ playerHpPercentage = piclet.currentHp / piclet.maxHp;
423
+ showWhiteFlash = false;
424
+ }, 300);
425
 
426
  const switchAction: SwitchAction = {
427
  type: 'switch',
 
498
  // Update UI state from battle engine
499
  updateUIFromBattleState();
500
 
501
+ // Check for battle end - but handle faint message sequence properly
502
  if (battleState.winner) {
503
  battleEnded = true;
504
+ const defeatedPiclet = battleState.winner === 'player' ? currentEnemyPiclet : currentPlayerPiclet;
505
+
506
+ // First show the faint message and trigger animation
507
+ currentMessage = `${defeatedPiclet.nickname} fainted!`;
508
 
509
  // Trigger faint animation for the defeated Piclet
510
  if (battleState.winner === 'player') {
 
513
  playerFaint = true;
514
  }
515
 
516
+ // Wait for faint animation, then show win/loss message
517
+ setTimeout(() => {
518
+ const winMessage = battleState.winner === 'player'
519
+ ? 'You won!'
520
+ : 'You lost!';
521
+ currentMessage = winMessage;
522
+
523
+ // Process battle results with XP and level ups
524
+ setTimeout(async () => {
525
+ await handleBattleResults(battleState.winner === 'player');
526
+ }, 1500);
527
+ }, 2000); // Wait for faint animation
528
  } else {
529
  setTimeout(() => {
530
  currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
 
625
  {enemyFaint}
626
  {playerLunge}
627
  {enemyLunge}
628
+ {isWildBattle}
629
+ {showWhiteFlash}
630
+ {playerTrainerVisible}
631
+ {enemyTrainerVisible}
632
+ {playerTrainerSlideOut}
633
+ {enemyTrainerSlideOut}
634
  />
635
 
636
  <BattleControls
src/lib/components/PicletGenerator/PicletGenerator.svelte CHANGED
@@ -215,11 +215,11 @@ Format your response exactly as follows:
215
  "conversation_id": "",
216
  };
217
 
218
- // Create default settings based on qwen.html with max thinking tokens
219
  const defaultSettings = {
220
  "model": "qwen3-235b-a22b",
221
  "sys_prompt": "You are a creative monster designer specializing in transforming everyday objects into imaginative Pokémon-style creatures. Follow the exact format provided and create detailed, engaging descriptions that bring these monsters to life.",
222
- "thinking_budget": 38
223
  };
224
 
225
  // Create thinking button state
@@ -312,11 +312,11 @@ Create a concise visual description (1-3 sentences, max 100 words). Focus only o
312
  "conversation_id": "",
313
  };
314
 
315
- // Create default settings based on qwen.html
316
  const defaultSettings = {
317
  "model": "qwen3-235b-a22b",
318
  "sys_prompt": "You are an expert at creating concise visual descriptions for image generation. Extract ONLY visual appearance details and describe them in 1-2 sentences (max 50 words). Focus on colors, shape, eyes, limbs, and distinctive features. Omit all non-visual information like abilities, personality, or backstory.",
319
- "thinking_budget": 38
320
  };
321
 
322
  // Create thinking button state
@@ -676,11 +676,11 @@ Write your response within \`\`\`json\`\`\``;
676
  "conversation_id": "",
677
  };
678
 
679
- // Create default settings based on qwen.html
680
  const defaultSettings = {
681
  "model": "qwen3-235b-a22b",
682
  "sys_prompt": "You are a game designer specializing in monster stats and abilities. You must ONLY output valid JSON that matches the provided schema exactly. Do not include any text before or after the JSON. Do not include null values in your JSON response. Your entire response should be wrapped in a ```json``` code block.",
683
- "thinking_budget": 38
684
  };
685
 
686
  // Create thinking button state
 
215
  "conversation_id": "",
216
  };
217
 
218
+ // Create default settings based on qwen.html with minimal thinking tokens
219
  const defaultSettings = {
220
  "model": "qwen3-235b-a22b",
221
  "sys_prompt": "You are a creative monster designer specializing in transforming everyday objects into imaginative Pokémon-style creatures. Follow the exact format provided and create detailed, engaging descriptions that bring these monsters to life.",
222
+ "thinking_budget": 3
223
  };
224
 
225
  // Create thinking button state
 
312
  "conversation_id": "",
313
  };
314
 
315
+ // Create default settings based on qwen.html with minimal thinking tokens
316
  const defaultSettings = {
317
  "model": "qwen3-235b-a22b",
318
  "sys_prompt": "You are an expert at creating concise visual descriptions for image generation. Extract ONLY visual appearance details and describe them in 1-2 sentences (max 50 words). Focus on colors, shape, eyes, limbs, and distinctive features. Omit all non-visual information like abilities, personality, or backstory.",
319
+ "thinking_budget": 3
320
  };
321
 
322
  // Create thinking button state
 
676
  "conversation_id": "",
677
  };
678
 
679
+ // Create default settings based on qwen.html with minimal thinking tokens
680
  const defaultSettings = {
681
  "model": "qwen3-235b-a22b",
682
  "sys_prompt": "You are a game designer specializing in monster stats and abilities. You must ONLY output valid JSON that matches the provided schema exactly. Do not include any text before or after the JSON. Do not include null values in your JSON response. Your entire response should be wrapped in a ```json``` code block.",
683
+ "thinking_budget": 3
684
  };
685
 
686
  // Create thinking button state