Fraser commited on
Commit
82b6f62
·
1 Parent(s): d871370
src/lib/components/Battle/PicletInfo.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
  import type { PicletInstance } from '$lib/db/schema';
3
- import { getXpProgress, getXpForNextLevel } from '$lib/services/levelingService';
4
 
5
  export let piclet: PicletInstance;
6
  export let hpPercentage: number;
@@ -9,7 +9,7 @@
9
 
10
  // Calculate real XP percentage using levelingService
11
  $: realXpPercentage = isPlayer ? getXpProgress(piclet.xp, piclet.level, piclet.tier) : 0;
12
- $: nextLevelXp = isPlayer ? getXpForNextLevel(piclet.level, piclet.tier) : 0;
13
 
14
  $: hpColor = hpPercentage > 0.5 ? '#4caf50' : hpPercentage > 0.2 ? '#ffc107' : '#f44336';
15
  $: displayHp = Math.ceil(piclet.currentHp);
@@ -55,20 +55,10 @@
55
  <div class="xp-bar">
56
  <div
57
  class="xp-fill"
58
- style="width: {realXpPercentage}%"
59
  ></div>
60
  </div>
61
 
62
- <!-- XP Progress Text (Player only) -->
63
- {#if piclet.level < 100}
64
- <div class="xp-text">
65
- <span class="xp-progress">{Math.floor(realXpPercentage)}% to next level</span>
66
- </div>
67
- {:else}
68
- <div class="xp-text">
69
- <span class="xp-progress">MAX LEVEL</span>
70
- </div>
71
- {/if}
72
  {/if}
73
  </div>
74
 
@@ -182,7 +172,7 @@
182
  .xp-fill {
183
  height: 100%;
184
  background: #2196f3;
185
- transition: width 0.8s ease;
186
  }
187
 
188
  /* XP Text */
@@ -193,6 +183,7 @@
193
 
194
  .xp-progress {
195
  font-weight: 500;
 
196
  }
197
 
198
  /* Triangle Pointer */
 
1
  <script lang="ts">
2
  import type { PicletInstance } from '$lib/db/schema';
3
+ import { getXpProgress, getXpTowardsNextLevel } from '$lib/services/levelingService';
4
 
5
  export let piclet: PicletInstance;
6
  export let hpPercentage: number;
 
9
 
10
  // Calculate real XP percentage using levelingService
11
  $: realXpPercentage = isPlayer ? getXpProgress(piclet.xp, piclet.level, piclet.tier) : 0;
12
+ $: xpTowardsNext = isPlayer ? getXpTowardsNextLevel(piclet.xp, piclet.level, piclet.tier) : { current: 0, needed: 0, percentage: 0 };
13
 
14
  $: hpColor = hpPercentage > 0.5 ? '#4caf50' : hpPercentage > 0.2 ? '#ffc107' : '#f44336';
15
  $: displayHp = Math.ceil(piclet.currentHp);
 
55
  <div class="xp-bar">
56
  <div
57
  class="xp-fill"
58
+ style="width: {xpTowardsNext.percentage}%"
59
  ></div>
60
  </div>
61
 
 
 
 
 
 
 
 
 
 
 
62
  {/if}
63
  </div>
64
 
 
172
  .xp-fill {
173
  height: 100%;
174
  background: #2196f3;
175
+ transition: width 1.2s ease-out;
176
  }
177
 
178
  /* XP Text */
 
183
 
184
  .xp-progress {
185
  font-weight: 500;
186
+ transition: all 0.3s ease;
187
  }
188
 
189
  /* Triangle Pointer */
src/lib/components/Pages/Battle.svelte CHANGED
@@ -365,52 +365,53 @@
365
  // Calculate XP gained from defeating the enemy
366
  const xpGained = calculateBattleXp(currentEnemyPiclet, 1);
367
 
368
- // Apply XP to player's Piclet
369
- const updatedPlayerPiclet = {
370
- ...currentPlayerPiclet,
371
- xp: currentPlayerPiclet.xp + xpGained
372
- };
373
-
374
- // Process any level ups
375
- const { newInstance, levelUpInfo } = processAllLevelUps(updatedPlayerPiclet);
376
-
377
- // Save updated Piclet to database
378
- if (newInstance.id) {
379
- await db.picletInstances.update(newInstance.id, newInstance);
380
- }
381
-
382
- // Update local state
383
- currentPlayerPiclet = newInstance;
384
-
385
- // Prepare battle results for display
386
- battleResults = {
387
- victory: true,
388
- xpGained,
389
- levelUps: levelUpInfo,
390
- newLevel: newInstance.level
391
- };
392
-
393
- // Show battle results screen
394
- if (xpGained > 0 || levelUpInfo.length > 0) {
395
- battleResultsVisible = true;
396
 
397
- // Auto-dismiss after showing results
398
- setTimeout(() => {
399
- battleResultsVisible = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  onBattleEnd(true);
401
- }, levelUpInfo.length > 0 ? 4000 : 2500);
402
  } else {
403
  onBattleEnd(true);
404
  }
405
  } else {
406
  // Player lost - no XP gained
407
- battleResults = {
408
- victory: false,
409
- xpGained: 0,
410
- levelUps: [],
411
- newLevel: currentPlayerPiclet.level
412
- };
413
-
414
  onBattleEnd(false);
415
  }
416
  }
@@ -464,11 +465,6 @@
464
  <div class="battle-results-card">
465
  <h2>{battleResults.victory ? 'Victory!' : 'Defeat!'}</h2>
466
 
467
- {#if battleResults.victory && battleResults.xpGained > 0}
468
- <div class="xp-gain">
469
- <p><strong>{currentPlayerPiclet.nickname}</strong> gained <strong>{battleResults.xpGained} XP</strong>!</p>
470
- </div>
471
- {/if}
472
 
473
  {#if battleResults.levelUps.length > 0}
474
  {#each battleResults.levelUps as levelUp}
@@ -592,19 +588,6 @@
592
  color: #1a1a1a;
593
  }
594
 
595
- .xp-gain {
596
- background: #e3f2fd;
597
- border-radius: 8px;
598
- padding: 1rem;
599
- margin: 1rem 0;
600
- border: 2px solid #2196f3;
601
- }
602
-
603
- .xp-gain p {
604
- margin: 0;
605
- color: #1565c0;
606
- font-size: 1.1rem;
607
- }
608
 
609
  .level-up {
610
  background: linear-gradient(135deg, #fff3e0 0%, #ffcc02 100%);
 
365
  // Calculate XP gained from defeating the enemy
366
  const xpGained = calculateBattleXp(currentEnemyPiclet, 1);
367
 
368
+ if (xpGained > 0) {
369
+ // Animate XP gain by updating UI first
370
+ const updatedPlayerPiclet = {
371
+ ...currentPlayerPiclet,
372
+ xp: currentPlayerPiclet.xp + xpGained
373
+ };
374
+ currentPlayerPiclet = updatedPlayerPiclet;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
+ // Wait a moment for XP bar animation
377
+ await new Promise(resolve => setTimeout(resolve, 1500));
378
+
379
+ // Process any level ups
380
+ const { newInstance, levelUpInfo } = processAllLevelUps(updatedPlayerPiclet);
381
+
382
+ // Save updated Piclet to database
383
+ if (newInstance.id) {
384
+ await db.picletInstances.update(newInstance.id, newInstance);
385
+ }
386
+
387
+ // Update local state with final leveled instance
388
+ currentPlayerPiclet = newInstance;
389
+
390
+ // Show level up results if any occurred
391
+ if (levelUpInfo.length > 0) {
392
+ battleResults = {
393
+ victory: true,
394
+ xpGained,
395
+ levelUps: levelUpInfo,
396
+ newLevel: newInstance.level
397
+ };
398
+
399
+ battleResultsVisible = true;
400
+
401
+ // Auto-dismiss after showing level up
402
+ setTimeout(() => {
403
+ battleResultsVisible = false;
404
+ onBattleEnd(true);
405
+ }, 4000);
406
+ } else {
407
+ // No level up, just end battle
408
  onBattleEnd(true);
409
+ }
410
  } else {
411
  onBattleEnd(true);
412
  }
413
  } else {
414
  // Player lost - no XP gained
 
 
 
 
 
 
 
415
  onBattleEnd(false);
416
  }
417
  }
 
465
  <div class="battle-results-card">
466
  <h2>{battleResults.victory ? 'Victory!' : 'Defeat!'}</h2>
467
 
 
 
 
 
 
468
 
469
  {#if battleResults.levelUps.length > 0}
470
  {#each battleResults.levelUps as levelUp}
 
588
  color: #1a1a1a;
589
  }
590
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
 
592
  .level-up {
593
  background: linear-gradient(135deg, #fff3e0 0%, #ffcc02 100%);
src/lib/components/Piclets/PicletDetail.svelte CHANGED
@@ -8,7 +8,7 @@
8
  import AbilityDisplay from './AbilityDisplay.svelte';
9
  import MoveDisplay from './MoveDisplay.svelte';
10
  import { picletInstanceToBattleDefinition } from '$lib/utils/battleConversion';
11
- import { recalculatePicletStats, getXpProgress, getXpForNextLevel } from '$lib/services/levelingService';
12
 
13
  interface Props {
14
  instance: PicletInstance;
@@ -28,7 +28,7 @@
28
 
29
  // XP and level calculations
30
  const xpProgress = $derived(getXpProgress(updatedInstance.xp, updatedInstance.level, updatedInstance.tier));
31
- const nextLevelXp = $derived(getXpForNextLevel(updatedInstance.level, updatedInstance.tier));
32
 
33
  // Type-based styling
34
  const typeData = $derived(TYPE_DATA[instance.primaryType]);
@@ -136,7 +136,7 @@
136
  <div class="level-info">
137
  <span class="level-label">Level {updatedInstance.level}</span>
138
  {#if updatedInstance.level < 100}
139
- <span class="xp-label">{Math.floor(xpProgress)}% to next level</span>
140
  {:else}
141
  <span class="xp-label">MAX LEVEL</span>
142
  {/if}
@@ -144,7 +144,7 @@
144
 
145
  {#if updatedInstance.level < 100}
146
  <div class="xp-progress-bar">
147
- <div class="xp-progress-fill" style="width: {xpProgress}%"></div>
148
  </div>
149
  {/if}
150
 
 
8
  import AbilityDisplay from './AbilityDisplay.svelte';
9
  import MoveDisplay from './MoveDisplay.svelte';
10
  import { picletInstanceToBattleDefinition } from '$lib/utils/battleConversion';
11
+ import { recalculatePicletStats, getXpProgress, getXpTowardsNextLevel } from '$lib/services/levelingService';
12
 
13
  interface Props {
14
  instance: PicletInstance;
 
28
 
29
  // XP and level calculations
30
  const xpProgress = $derived(getXpProgress(updatedInstance.xp, updatedInstance.level, updatedInstance.tier));
31
+ const xpTowardsNext = $derived(getXpTowardsNextLevel(updatedInstance.xp, updatedInstance.level, updatedInstance.tier));
32
 
33
  // Type-based styling
34
  const typeData = $derived(TYPE_DATA[instance.primaryType]);
 
136
  <div class="level-info">
137
  <span class="level-label">Level {updatedInstance.level}</span>
138
  {#if updatedInstance.level < 100}
139
+ <span class="xp-label">{xpTowardsNext.current}/{xpTowardsNext.needed} to next level</span>
140
  {:else}
141
  <span class="xp-label">MAX LEVEL</span>
142
  {/if}
 
144
 
145
  {#if updatedInstance.level < 100}
146
  <div class="xp-progress-bar">
147
+ <div class="xp-progress-fill" style="width: {xpTowardsNext.percentage}%"></div>
148
  </div>
149
  {/if}
150
 
src/lib/services/levelingService.ts CHANGED
@@ -172,6 +172,31 @@ export function getXpProgress(currentXp: number, currentLevel: number, tier: str
172
  return Math.min(100, Math.max(0, (xpIntoLevel / xpNeededForLevel) * 100));
173
  }
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  /**
176
  * Recalculate all stats for a Piclet based on current level and nature
177
  */
 
172
  return Math.min(100, Math.max(0, (xpIntoLevel / xpNeededForLevel) * 100));
173
  }
174
 
175
+ /**
176
+ * Get current XP towards next level in X/Y format
177
+ */
178
+ export function getXpTowardsNextLevel(currentXp: number, currentLevel: number, tier: string = 'medium'): {
179
+ current: number;
180
+ needed: number;
181
+ percentage: number;
182
+ } {
183
+ if (currentLevel >= 100) {
184
+ return { current: 0, needed: 0, percentage: 100 };
185
+ }
186
+
187
+ const currentLevelXp = getXpForLevel(currentLevel, tier);
188
+ const nextLevelXp = getXpForLevel(currentLevel + 1, tier);
189
+ const xpIntoLevel = Math.max(0, currentXp - currentLevelXp);
190
+ const xpNeededForLevel = nextLevelXp - currentLevelXp;
191
+ const percentage = Math.min(100, Math.max(0, (xpIntoLevel / xpNeededForLevel) * 100));
192
+
193
+ return {
194
+ current: xpIntoLevel,
195
+ needed: xpNeededForLevel,
196
+ percentage
197
+ };
198
+ }
199
+
200
  /**
201
  * Recalculate all stats for a Piclet based on current level and nature
202
  */