better stats
Browse files- src/lib/components/Battle/PicletInfo.svelte +37 -21
- src/lib/components/Pages/Battle.svelte +124 -20
- src/lib/db/piclets.ts +14 -3
    	
        src/lib/components/Battle/PicletInfo.svelte
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 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;
         | 
| @@ -11,6 +12,11 @@ | |
| 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);
         | 
| 16 |  | 
| @@ -23,17 +29,16 @@ | |
| 23 | 
             
                setTimeout(() => hpFlash = false, 300);
         | 
| 24 | 
             
                previousHp = displayHp;
         | 
| 25 | 
             
              }
         | 
| 26 | 
            -
              
         | 
| 27 | 
            -
              // Get type emoji (simplified - should map from actual types)
         | 
| 28 | 
            -
              const typeEmoji = '🔥'; // Default fire type
         | 
| 29 | 
             
            </script>
         | 
| 30 |  | 
| 31 | 
             
            <div class="piclet-info-wrapper {isPlayer ? 'player-info-wrapper' : 'enemy-info-wrapper'}">
         | 
| 32 | 
            -
              <div class="piclet-info">
         | 
|  | |
|  | |
|  | |
| 33 | 
             
                <!-- Name Row -->
         | 
| 34 | 
             
                <div class="name-row">
         | 
| 35 | 
             
                  <span class="piclet-name">{piclet.nickname}</span>
         | 
| 36 | 
            -
                  <span class="type-emoji">{typeEmoji}</span>
         | 
| 37 | 
             
                  <span class="level-badge">Lv.{piclet.level}</span>
         | 
| 38 | 
             
                </div>
         | 
| 39 |  | 
| @@ -90,25 +95,40 @@ | |
| 90 | 
             
                border-radius: 8px;
         | 
| 91 | 
             
                padding: 12px;
         | 
| 92 | 
             
                min-width: 160px;
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 93 | 
             
              }
         | 
| 94 |  | 
| 95 | 
             
              /* Name Row */
         | 
| 96 | 
             
              .name-row {
         | 
| 97 | 
             
                display: flex;
         | 
| 98 | 
             
                align-items: center;
         | 
| 99 | 
            -
                 | 
| 100 | 
             
                margin-bottom: 8px;
         | 
|  | |
|  | |
| 101 | 
             
              }
         | 
| 102 |  | 
| 103 | 
             
              .piclet-name {
         | 
| 104 | 
             
                font-weight: 600;
         | 
| 105 | 
             
                font-size: 14px;
         | 
| 106 | 
             
                color: #1a1a1a;
         | 
| 107 | 
            -
                flex: 1;
         | 
| 108 | 
            -
              }
         | 
| 109 | 
            -
              
         | 
| 110 | 
            -
              .type-emoji {
         | 
| 111 | 
            -
                font-size: 12px;
         | 
| 112 | 
             
              }
         | 
| 113 |  | 
| 114 | 
             
              .level-badge {
         | 
| @@ -128,6 +148,8 @@ | |
| 128 | 
             
                border-radius: 4px;
         | 
| 129 | 
             
                overflow: hidden;
         | 
| 130 | 
             
                margin-bottom: 4px;
         | 
|  | |
|  | |
| 131 | 
             
              }
         | 
| 132 |  | 
| 133 | 
             
              .hp-fill {
         | 
| @@ -140,6 +162,8 @@ | |
| 140 | 
             
                font-size: 11px;
         | 
| 141 | 
             
                color: #666;
         | 
| 142 | 
             
                margin-bottom: 4px;
         | 
|  | |
|  | |
| 143 | 
             
              }
         | 
| 144 |  | 
| 145 | 
             
              .hp-values {
         | 
| @@ -167,6 +191,8 @@ | |
| 167 | 
             
                border-radius: 2px;
         | 
| 168 | 
             
                overflow: hidden;
         | 
| 169 | 
             
                margin-bottom: 2px;
         | 
|  | |
|  | |
| 170 | 
             
              }
         | 
| 171 |  | 
| 172 | 
             
              .xp-fill {
         | 
| @@ -175,16 +201,6 @@ | |
| 175 | 
             
                transition: width 1.2s ease-out;
         | 
| 176 | 
             
              }
         | 
| 177 |  | 
| 178 | 
            -
              /* XP Text */
         | 
| 179 | 
            -
              .xp-text {
         | 
| 180 | 
            -
                font-size: 10px;
         | 
| 181 | 
            -
                color: #666;
         | 
| 182 | 
            -
              }
         | 
| 183 | 
            -
              
         | 
| 184 | 
            -
              .xp-progress {
         | 
| 185 | 
            -
                font-weight: 500;
         | 
| 186 | 
            -
                transition: all 0.3s ease;
         | 
| 187 | 
            -
              }
         | 
| 188 |  | 
| 189 | 
             
              /* Triangle Pointer */
         | 
| 190 | 
             
              .triangle-pointer {
         | 
|  | |
| 1 | 
             
            <script lang="ts">
         | 
| 2 | 
             
              import type { PicletInstance } from '$lib/db/schema';
         | 
| 3 | 
             
              import { getXpProgress, getXpTowardsNextLevel } from '$lib/services/levelingService';
         | 
| 4 | 
            +
              import { TYPE_DATA } from '$lib/types/picletTypes';
         | 
| 5 |  | 
| 6 | 
             
              export let piclet: PicletInstance;
         | 
| 7 | 
             
              export let hpPercentage: number;
         | 
|  | |
| 12 | 
             
              $: realXpPercentage = isPlayer ? getXpProgress(piclet.xp, piclet.level, piclet.tier) : 0;
         | 
| 13 | 
             
              $: xpTowardsNext = isPlayer ? getXpTowardsNextLevel(piclet.xp, piclet.level, piclet.tier) : { current: 0, needed: 0, percentage: 0 };
         | 
| 14 |  | 
| 15 | 
            +
              // Type-based styling
         | 
| 16 | 
            +
              $: typeData = TYPE_DATA[piclet.primaryType];
         | 
| 17 | 
            +
              $: typeColor = typeData.color;
         | 
| 18 | 
            +
              $: typeLogoPath = `/classes/${piclet.primaryType}.png`;
         | 
| 19 | 
            +
              
         | 
| 20 | 
             
              $: hpColor = hpPercentage > 0.5 ? '#4caf50' : hpPercentage > 0.2 ? '#ffc107' : '#f44336';
         | 
| 21 | 
             
              $: displayHp = Math.ceil(piclet.currentHp);
         | 
| 22 |  | 
|  | |
| 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'}">
         | 
| 35 | 
            +
              <div class="piclet-info" style="--type-logo: url('{typeLogoPath}')">
         | 
| 36 | 
            +
                <!-- Type Logo Background -->
         | 
| 37 | 
            +
                <div class="type-logo-background"></div>
         | 
| 38 | 
            +
                
         | 
| 39 | 
             
                <!-- Name Row -->
         | 
| 40 | 
             
                <div class="name-row">
         | 
| 41 | 
             
                  <span class="piclet-name">{piclet.nickname}</span>
         | 
|  | |
| 42 | 
             
                  <span class="level-badge">Lv.{piclet.level}</span>
         | 
| 43 | 
             
                </div>
         | 
| 44 |  | 
|  | |
| 95 | 
             
                border-radius: 8px;
         | 
| 96 | 
             
                padding: 12px;
         | 
| 97 | 
             
                min-width: 160px;
         | 
| 98 | 
            +
                position: relative;
         | 
| 99 | 
            +
                overflow: hidden;
         | 
| 100 | 
            +
              }
         | 
| 101 | 
            +
              
         | 
| 102 | 
            +
              /* Type Logo Background */
         | 
| 103 | 
            +
              .type-logo-background {
         | 
| 104 | 
            +
                position: absolute;
         | 
| 105 | 
            +
                bottom: 1px;
         | 
| 106 | 
            +
                left: 1px;
         | 
| 107 | 
            +
                width: 35px;
         | 
| 108 | 
            +
                height: 35px;
         | 
| 109 | 
            +
                background-image: var(--type-logo);
         | 
| 110 | 
            +
                background-size: contain;
         | 
| 111 | 
            +
                background-repeat: no-repeat;
         | 
| 112 | 
            +
                background-position: center;
         | 
| 113 | 
            +
                opacity: 0.12;
         | 
| 114 | 
            +
                pointer-events: none;
         | 
| 115 | 
            +
                z-index: 1;
         | 
| 116 | 
             
              }
         | 
| 117 |  | 
| 118 | 
             
              /* Name Row */
         | 
| 119 | 
             
              .name-row {
         | 
| 120 | 
             
                display: flex;
         | 
| 121 | 
             
                align-items: center;
         | 
| 122 | 
            +
                justify-content: space-between;
         | 
| 123 | 
             
                margin-bottom: 8px;
         | 
| 124 | 
            +
                position: relative;
         | 
| 125 | 
            +
                z-index: 2;
         | 
| 126 | 
             
              }
         | 
| 127 |  | 
| 128 | 
             
              .piclet-name {
         | 
| 129 | 
             
                font-weight: 600;
         | 
| 130 | 
             
                font-size: 14px;
         | 
| 131 | 
             
                color: #1a1a1a;
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 132 | 
             
              }
         | 
| 133 |  | 
| 134 | 
             
              .level-badge {
         | 
|  | |
| 148 | 
             
                border-radius: 4px;
         | 
| 149 | 
             
                overflow: hidden;
         | 
| 150 | 
             
                margin-bottom: 4px;
         | 
| 151 | 
            +
                position: relative;
         | 
| 152 | 
            +
                z-index: 2;
         | 
| 153 | 
             
              }
         | 
| 154 |  | 
| 155 | 
             
              .hp-fill {
         | 
|  | |
| 162 | 
             
                font-size: 11px;
         | 
| 163 | 
             
                color: #666;
         | 
| 164 | 
             
                margin-bottom: 4px;
         | 
| 165 | 
            +
                position: relative;
         | 
| 166 | 
            +
                z-index: 2;
         | 
| 167 | 
             
              }
         | 
| 168 |  | 
| 169 | 
             
              .hp-values {
         | 
|  | |
| 191 | 
             
                border-radius: 2px;
         | 
| 192 | 
             
                overflow: hidden;
         | 
| 193 | 
             
                margin-bottom: 2px;
         | 
| 194 | 
            +
                position: relative;
         | 
| 195 | 
            +
                z-index: 2;
         | 
| 196 | 
             
              }
         | 
| 197 |  | 
| 198 | 
             
              .xp-fill {
         | 
|  | |
| 201 | 
             
                transition: width 1.2s ease-out;
         | 
| 202 | 
             
              }
         | 
| 203 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 204 |  | 
| 205 | 
             
              /* Triangle Pointer */
         | 
| 206 | 
             
              .triangle-pointer {
         | 
    	
        src/lib/components/Pages/Battle.svelte
    CHANGED
    
    | @@ -5,7 +5,7 @@ | |
| 5 | 
             
              import BattleField from '../Battle/BattleField.svelte';
         | 
| 6 | 
             
              import BattleControls from '../Battle/BattleControls.svelte';
         | 
| 7 | 
             
              import { BattleEngine } from '$lib/battle-engine/BattleEngine';
         | 
| 8 | 
            -
              import type { BattleState, MoveAction } from '$lib/battle-engine/types';
         | 
| 9 | 
             
              import { picletInstanceToBattleDefinition, battlePicletToInstance, stripBattlePrefix } from '$lib/utils/battleConversion';
         | 
| 10 | 
             
              import { calculateBattleXp, processAllLevelUps } from '$lib/services/levelingService';
         | 
| 11 | 
             
              import { db } from '$lib/db/index';
         | 
| @@ -55,10 +55,26 @@ | |
| 55 |  | 
| 56 | 
             
              onMount(() => {
         | 
| 57 | 
             
                // Initialize battle engine with converted piclet definitions
         | 
| 58 | 
            -
                 | 
|  | |
| 59 | 
             
                const enemyDefinition = picletInstanceToBattleDefinition(enemyPiclet);
         | 
| 60 |  | 
| 61 | 
            -
                 | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 62 | 
             
                battleState = battleEngine.getState();
         | 
| 63 |  | 
| 64 | 
             
                // Start intro sequence
         | 
| @@ -175,6 +191,13 @@ | |
| 175 | 
             
                        : `${currentPlayerPiclet.nickname} fainted! You lost!`;
         | 
| 176 | 
             
                      currentMessage = winMessage;
         | 
| 177 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 178 | 
             
                      // Process battle results with XP and level ups
         | 
| 179 | 
             
                      setTimeout(async () => {
         | 
| 180 | 
             
                        await handleBattleResults(battleState.winner === 'player');
         | 
| @@ -333,27 +356,108 @@ | |
| 333 | 
             
                if (!battleEngine) return;
         | 
| 334 |  | 
| 335 | 
             
                battlePhase = 'main';
         | 
| 336 | 
            -
                 | 
| 337 |  | 
| 338 | 
            -
                 | 
| 339 | 
            -
             | 
| 340 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 341 |  | 
| 342 | 
            -
                   | 
| 343 | 
            -
                    //  | 
| 344 | 
            -
                     | 
| 345 | 
            -
                    currentPlayerPiclet = piclet;
         | 
| 346 | 
            -
                    playerHpPercentage = piclet.currentHp / piclet.maxHp;
         | 
| 347 | 
            -
                    currentMessage = `Go, ${piclet.nickname}!`;
         | 
| 348 |  | 
| 349 | 
            -
                     | 
| 350 | 
            -
             | 
| 351 | 
            -
             | 
| 352 | 
            -
             | 
| 353 | 
            -
             | 
| 354 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 355 | 
             
                  }
         | 
| 356 | 
            -
                } | 
|  | |
|  | |
|  | |
|  | |
| 357 | 
             
              }
         | 
| 358 |  | 
| 359 | 
             
              function handleBack() {
         | 
|  | |
| 5 | 
             
              import BattleField from '../Battle/BattleField.svelte';
         | 
| 6 | 
             
              import BattleControls from '../Battle/BattleControls.svelte';
         | 
| 7 | 
             
              import { BattleEngine } from '$lib/battle-engine/BattleEngine';
         | 
| 8 | 
            +
              import type { BattleState, MoveAction, SwitchAction } from '$lib/battle-engine/types';
         | 
| 9 | 
             
              import { picletInstanceToBattleDefinition, battlePicletToInstance, stripBattlePrefix } from '$lib/utils/battleConversion';
         | 
| 10 | 
             
              import { calculateBattleXp, processAllLevelUps } from '$lib/services/levelingService';
         | 
| 11 | 
             
              import { db } from '$lib/db/index';
         | 
|  | |
| 55 |  | 
| 56 | 
             
              onMount(() => {
         | 
| 57 | 
             
                // Initialize battle engine with converted piclet definitions
         | 
| 58 | 
            +
                // Convert full roster for switching support
         | 
| 59 | 
            +
                const playerRosterDefinitions = rosterPiclets.map(p => picletInstanceToBattleDefinition(p));
         | 
| 60 | 
             
                const enemyDefinition = picletInstanceToBattleDefinition(enemyPiclet);
         | 
| 61 |  | 
| 62 | 
            +
                // Find the starting player piclet index in the roster
         | 
| 63 | 
            +
                const startingPlayerIndex = rosterPiclets.findIndex(p => p.id === playerPiclet.id);
         | 
| 64 | 
            +
                
         | 
| 65 | 
            +
                // Initialize with full rosters (player roster vs single enemy)
         | 
| 66 | 
            +
                battleEngine = new BattleEngine(playerRosterDefinitions, enemyDefinition, playerPiclet.level, enemyPiclet.level);
         | 
| 67 | 
            +
                
         | 
| 68 | 
            +
                // If starting piclet is not the first in roster, switch to it
         | 
| 69 | 
            +
                if (startingPlayerIndex > 0) {
         | 
| 70 | 
            +
                  const initialSwitchAction: SwitchAction = {
         | 
| 71 | 
            +
                    type: 'switch',
         | 
| 72 | 
            +
                    piclet: 'player',
         | 
| 73 | 
            +
                    newPicletIndex: startingPlayerIndex
         | 
| 74 | 
            +
                  };
         | 
| 75 | 
            +
                  battleEngine.executeAction(initialSwitchAction, 'player');
         | 
| 76 | 
            +
                }
         | 
| 77 | 
            +
                
         | 
| 78 | 
             
                battleState = battleEngine.getState();
         | 
| 79 |  | 
| 80 | 
             
                // Start intro sequence
         | 
|  | |
| 191 | 
             
                        : `${currentPlayerPiclet.nickname} fainted! You lost!`;
         | 
| 192 | 
             
                      currentMessage = winMessage;
         | 
| 193 |  | 
| 194 | 
            +
                      // Trigger faint animation for the defeated Piclet
         | 
| 195 | 
            +
                      if (battleState.winner === 'player') {
         | 
| 196 | 
            +
                        enemyFaint = true;
         | 
| 197 | 
            +
                      } else {
         | 
| 198 | 
            +
                        playerFaint = true;
         | 
| 199 | 
            +
                      }
         | 
| 200 | 
            +
                      
         | 
| 201 | 
             
                      // Process battle results with XP and level ups
         | 
| 202 | 
             
                      setTimeout(async () => {
         | 
| 203 | 
             
                        await handleBattleResults(battleState.winner === 'player');
         | 
|  | |
| 356 | 
             
                if (!battleEngine) return;
         | 
| 357 |  | 
| 358 | 
             
                battlePhase = 'main';
         | 
| 359 | 
            +
                processingTurn = true;
         | 
| 360 |  | 
| 361 | 
            +
                // Find the index of the selected piclet in the roster
         | 
| 362 | 
            +
                const picletIndex = rosterPiclets.findIndex(p => p.id === piclet.id);
         | 
| 363 | 
            +
                if (picletIndex === -1) {
         | 
| 364 | 
            +
                  console.error('Selected piclet not found in roster');
         | 
| 365 | 
            +
                  processingTurn = false;
         | 
| 366 | 
            +
                  return;
         | 
| 367 | 
            +
                }
         | 
| 368 | 
            +
                
         | 
| 369 | 
            +
                const switchAction: SwitchAction = {
         | 
| 370 | 
            +
                  type: 'switch',
         | 
| 371 | 
            +
                  piclet: 'player',
         | 
| 372 | 
            +
                  newPicletIndex: picletIndex
         | 
| 373 | 
            +
                };
         | 
| 374 | 
            +
                
         | 
| 375 | 
            +
                try {
         | 
| 376 | 
            +
                  // Choose random enemy move (AI continues to act)
         | 
| 377 | 
            +
                  const availableEnemyMoves = battleState.opponentPiclet.moves.filter(m => m.currentPP > 0);
         | 
| 378 | 
            +
                  if (availableEnemyMoves.length === 0) {
         | 
| 379 | 
            +
                    currentMessage = `${currentEnemyPiclet.nickname} has no moves left!`;
         | 
| 380 | 
            +
                    processingTurn = false;
         | 
| 381 | 
            +
                    return;
         | 
| 382 | 
            +
                  }
         | 
| 383 | 
            +
                  
         | 
| 384 | 
            +
                  const randomEnemyMove = availableEnemyMoves[Math.floor(Math.random() * availableEnemyMoves.length)];
         | 
| 385 | 
            +
                  const enemyMoveIndex = battleState.opponentPiclet.moves.indexOf(randomEnemyMove);
         | 
| 386 | 
            +
                  const enemyAction: MoveAction = {
         | 
| 387 | 
            +
                    type: 'move', 
         | 
| 388 | 
            +
                    moveIndex: enemyMoveIndex
         | 
| 389 | 
            +
                  };
         | 
| 390 | 
            +
                  
         | 
| 391 | 
            +
                  // Get log entries before action to track new messages
         | 
| 392 | 
            +
                  const logBefore = battleEngine.getLog();
         | 
| 393 | 
            +
                  
         | 
| 394 | 
            +
                  // Execute the turn - switching vs enemy move
         | 
| 395 | 
            +
                  battleEngine.executeActions(switchAction, enemyAction);
         | 
| 396 | 
            +
                  battleState = battleEngine.getState();
         | 
| 397 | 
            +
                  
         | 
| 398 | 
            +
                  // Get only the new log entries from this turn
         | 
| 399 | 
            +
                  const logAfter = battleEngine.getLog();
         | 
| 400 | 
            +
                  const newLogEntries = logAfter.slice(logBefore.length);
         | 
| 401 | 
            +
                  const result = { log: newLogEntries };
         | 
| 402 | 
            +
                  
         | 
| 403 | 
            +
                  // Show battle messages with timing and visual effects
         | 
| 404 | 
            +
                  if (result.log && result.log.length > 0) {
         | 
| 405 | 
            +
                    let messageIndex = 0;
         | 
| 406 | 
            +
                    function showNextBattleMessage() {
         | 
| 407 | 
            +
                      if (messageIndex < result.log.length) {
         | 
| 408 | 
            +
                        const message = result.log[messageIndex];
         | 
| 409 | 
            +
                        currentMessage = message;
         | 
| 410 | 
            +
                        
         | 
| 411 | 
            +
                        // Trigger visual effects based on message content
         | 
| 412 | 
            +
                        triggerVisualEffectsFromMessage(message);
         | 
| 413 | 
            +
                        
         | 
| 414 | 
            +
                        messageIndex++;
         | 
| 415 | 
            +
                        setTimeout(showNextBattleMessage, 1500);
         | 
| 416 | 
            +
                      } else {
         | 
| 417 | 
            +
                        // After all messages, check battle end or continue
         | 
| 418 | 
            +
                        finalizeSwitchTurn();
         | 
| 419 | 
            +
                      }
         | 
| 420 | 
            +
                    }
         | 
| 421 | 
            +
                    showNextBattleMessage();
         | 
| 422 | 
            +
                  } else {
         | 
| 423 | 
            +
                    finalizeSwitchTurn();
         | 
| 424 | 
            +
                  }
         | 
| 425 |  | 
| 426 | 
            +
                  function finalizeSwitchTurn() {
         | 
| 427 | 
            +
                    // Update UI state from battle engine
         | 
| 428 | 
            +
                    updateUIFromBattleState();
         | 
|  | |
|  | |
|  | |
| 429 |  | 
| 430 | 
            +
                    // Check for battle end
         | 
| 431 | 
            +
                    if (battleState.winner) {
         | 
| 432 | 
            +
                      battleEnded = true;
         | 
| 433 | 
            +
                      const winMessage = battleState.winner === 'player' 
         | 
| 434 | 
            +
                        ? `${currentEnemyPiclet.nickname} fainted! You won!`
         | 
| 435 | 
            +
                        : `${currentPlayerPiclet.nickname} fainted! You lost!`;
         | 
| 436 | 
            +
                      currentMessage = winMessage;
         | 
| 437 | 
            +
                      
         | 
| 438 | 
            +
                      // Trigger faint animation for the defeated Piclet
         | 
| 439 | 
            +
                      if (battleState.winner === 'player') {
         | 
| 440 | 
            +
                        enemyFaint = true;
         | 
| 441 | 
            +
                      } else {
         | 
| 442 | 
            +
                        playerFaint = true;
         | 
| 443 | 
            +
                      }
         | 
| 444 | 
            +
                      
         | 
| 445 | 
            +
                      // Process battle results with XP and level ups
         | 
| 446 | 
            +
                      setTimeout(async () => {
         | 
| 447 | 
            +
                        await handleBattleResults(battleState.winner === 'player');
         | 
| 448 | 
            +
                      }, 2000);
         | 
| 449 | 
            +
                    } else {
         | 
| 450 | 
            +
                      setTimeout(() => {
         | 
| 451 | 
            +
                        currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
         | 
| 452 | 
            +
                        processingTurn = false;
         | 
| 453 | 
            +
                      }, 1000);
         | 
| 454 | 
            +
                    }
         | 
| 455 | 
             
                  }
         | 
| 456 | 
            +
                } catch (error) {
         | 
| 457 | 
            +
                  console.error('Switch error:', error);
         | 
| 458 | 
            +
                  currentMessage = 'Unable to switch Piclets!';
         | 
| 459 | 
            +
                  processingTurn = false;
         | 
| 460 | 
            +
                }
         | 
| 461 | 
             
              }
         | 
| 462 |  | 
| 463 | 
             
              function handleBack() {
         | 
    	
        src/lib/db/piclets.ts
    CHANGED
    
    | @@ -46,9 +46,20 @@ export async function monsterToPicletInstance(monster: Monster, level: number = | |
| 46 | 
             
              const baseFieldAttack = Math.floor(baseAttack * 0.8);
         | 
| 47 | 
             
              const baseFieldDefense = Math.floor(baseDefense * 0.8);
         | 
| 48 |  | 
| 49 | 
            -
              //  | 
| 50 | 
            -
              const calculateStat = (base: number, level: number) =>  | 
| 51 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 52 |  | 
| 53 | 
             
              const maxHp = calculateHp(baseHp, level);
         | 
| 54 |  | 
|  | |
| 46 | 
             
              const baseFieldAttack = Math.floor(baseAttack * 0.8);
         | 
| 47 | 
             
              const baseFieldDefense = Math.floor(baseDefense * 0.8);
         | 
| 48 |  | 
| 49 | 
            +
              // Use Pokemon-accurate stat calculations (matching levelingService)
         | 
| 50 | 
            +
              const calculateStat = (base: number, level: number) => {
         | 
| 51 | 
            +
                if (level === 1) {
         | 
| 52 | 
            +
                  return Math.max(1, Math.floor(base / 10) + 5);
         | 
| 53 | 
            +
                }
         | 
| 54 | 
            +
                return Math.floor((2 * base * level) / 100) + 5;
         | 
| 55 | 
            +
              };
         | 
| 56 | 
            +
              
         | 
| 57 | 
            +
              const calculateHp = (base: number, level: number) => {
         | 
| 58 | 
            +
                if (level === 1) {
         | 
| 59 | 
            +
                  return Math.max(1, Math.floor(base / 10) + 11);
         | 
| 60 | 
            +
                }
         | 
| 61 | 
            +
                return Math.floor((2 * base * level) / 100) + level + 10;
         | 
| 62 | 
            +
              };
         | 
| 63 |  | 
| 64 | 
             
              const maxHp = calculateHp(baseHp, level);
         | 
| 65 |  | 
