Fraser commited on
Commit
6530b95
·
1 Parent(s): 82b6f62

better stats

Browse files
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
- gap: 4px;
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
- const playerDefinition = picletInstanceToBattleDefinition(playerPiclet);
 
59
  const enemyDefinition = picletInstanceToBattleDefinition(enemyPiclet);
60
 
61
- battleEngine = new BattleEngine(playerDefinition, enemyDefinition, playerPiclet.level, enemyPiclet.level);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- currentMessage = `Come back, ${currentPlayerPiclet.nickname}!`;
337
 
338
- setTimeout(() => {
339
- // Convert the selected piclet to battle definition and switch
340
- const newPicletDefinition = picletInstanceToBattleDefinition(piclet);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
- try {
343
- // TODO: Implement switching in battle engine
344
- // For now, just update the UI
345
- currentPlayerPiclet = piclet;
346
- playerHpPercentage = piclet.currentHp / piclet.maxHp;
347
- currentMessage = `Go, ${piclet.nickname}!`;
348
 
349
- setTimeout(() => {
350
- currentMessage = `What will ${piclet.nickname} do?`;
351
- }, 1500);
352
- } catch (error) {
353
- console.error('Switch error:', error);
354
- currentMessage = 'Unable to switch Piclets!';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  }
356
- }, 1500);
 
 
 
 
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
- // Calculate current stats based on level
50
- const calculateStat = (base: number, level: number) => Math.floor((base * level) / 50 + 5);
51
- const calculateHp = (base: number, level: number) => Math.floor((base * level) / 50 + level + 10);
 
 
 
 
 
 
 
 
 
 
 
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