more more
Browse files
src/lib/components/Battle/BattleControls.svelte
CHANGED
|
@@ -34,7 +34,7 @@
|
|
| 34 |
<div class="action-area">
|
| 35 |
{#if waitingForContinue}
|
| 36 |
<!-- Tap to continue overlay -->
|
| 37 |
-
<div class="tap-continue-overlay" role="button" tabindex="0" onclick={onContinueTap}>
|
| 38 |
<div class="tap-indicator">
|
| 39 |
<span>Tap to continue</span>
|
| 40 |
</div>
|
|
|
|
| 34 |
<div class="action-area">
|
| 35 |
{#if waitingForContinue}
|
| 36 |
<!-- Tap to continue overlay -->
|
| 37 |
+
<div class="tap-continue-overlay" role="button" tabindex="0" onclick={onContinueTap} onkeydown={(e) => e.key === 'Enter' || e.key === ' ' ? onContinueTap() : null}>
|
| 38 |
<div class="tap-indicator">
|
| 39 |
<span>Tap to continue</span>
|
| 40 |
</div>
|
src/lib/components/PicletGenerator/PicletGenerator.svelte
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
import PicletResult from './PicletResult.svelte';
|
| 8 |
import { removeBackground } from '$lib/utils/professionalImageProcessing';
|
| 9 |
import { extractPicletMetadata } from '$lib/services/picletMetadata';
|
| 10 |
-
import { savePicletInstance,
|
| 11 |
import { PicletType, TYPE_DATA } from '$lib/types/picletTypes';
|
| 12 |
import { EncounterService } from '$lib/db/encounterService';
|
| 13 |
|
|
@@ -892,7 +892,7 @@ Write your response within \`\`\`json\`\`\``;
|
|
| 892 |
console.log('- stats:', cleanStats);
|
| 893 |
|
| 894 |
// Convert to PicletInstance format and save
|
| 895 |
-
const picletInstance = await
|
| 896 |
const picletId = await savePicletInstance(picletInstance);
|
| 897 |
console.log('Piclet auto-saved as uncaught with ID:', picletId);
|
| 898 |
|
|
|
|
| 7 |
import PicletResult from './PicletResult.svelte';
|
| 8 |
import { removeBackground } from '$lib/utils/professionalImageProcessing';
|
| 9 |
import { extractPicletMetadata } from '$lib/services/picletMetadata';
|
| 10 |
+
import { savePicletInstance, generatedDataToPicletInstance } from '$lib/db/piclets';
|
| 11 |
import { PicletType, TYPE_DATA } from '$lib/types/picletTypes';
|
| 12 |
import { EncounterService } from '$lib/db/encounterService';
|
| 13 |
|
|
|
|
| 892 |
console.log('- stats:', cleanStats);
|
| 893 |
|
| 894 |
// Convert to PicletInstance format and save
|
| 895 |
+
const picletInstance = await generatedDataToPicletInstance(picletData);
|
| 896 |
const picletId = await savePicletInstance(picletInstance);
|
| 897 |
console.log('Piclet auto-saved as uncaught with ID:', picletId);
|
| 898 |
|
src/lib/components/PicletGenerator/PicletResult.svelte
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
import type { PicletWorkflowState } from '$lib/types';
|
| 3 |
import type { PicletInstance } from '$lib/db/schema';
|
| 4 |
import { TYPE_DATA, PicletType } from '$lib/types/picletTypes';
|
| 5 |
-
import {
|
| 6 |
import PicletCard from '../Piclets/PicletCard.svelte';
|
| 7 |
import PicletDetail from '../Piclets/PicletDetail.svelte';
|
| 8 |
|
|
@@ -34,7 +34,7 @@
|
|
| 34 |
};
|
| 35 |
|
| 36 |
// Create piclet instance asynchronously
|
| 37 |
-
|
| 38 |
picletInstance = { ...instance, id: 0 }; // Add temporary id for display
|
| 39 |
}).catch(error => {
|
| 40 |
console.error('Failed to create piclet instance:', error);
|
|
@@ -76,7 +76,7 @@
|
|
| 76 |
{#if picletInstance}
|
| 77 |
<div class="piclet-preview">
|
| 78 |
<PicletCard
|
| 79 |
-
|
| 80 |
size={140}
|
| 81 |
onClick={handleCardClick}
|
| 82 |
/>
|
|
|
|
| 2 |
import type { PicletWorkflowState } from '$lib/types';
|
| 3 |
import type { PicletInstance } from '$lib/db/schema';
|
| 4 |
import { TYPE_DATA, PicletType } from '$lib/types/picletTypes';
|
| 5 |
+
import { generatedDataToPicletInstance } from '$lib/db/piclets';
|
| 6 |
import PicletCard from '../Piclets/PicletCard.svelte';
|
| 7 |
import PicletDetail from '../Piclets/PicletDetail.svelte';
|
| 8 |
|
|
|
|
| 34 |
};
|
| 35 |
|
| 36 |
// Create piclet instance asynchronously
|
| 37 |
+
generatedDataToPicletInstance(picletData).then(instance => {
|
| 38 |
picletInstance = { ...instance, id: 0 }; // Add temporary id for display
|
| 39 |
}).catch(error => {
|
| 40 |
console.error('Failed to create piclet instance:', error);
|
|
|
|
| 76 |
{#if picletInstance}
|
| 77 |
<div class="piclet-preview">
|
| 78 |
<PicletCard
|
| 79 |
+
piclet={picletInstance}
|
| 80 |
size={140}
|
| 81 |
onClick={handleCardClick}
|
| 82 |
/>
|
src/lib/components/Piclets/NewlyCaughtPicletDetail.svelte
CHANGED
|
@@ -62,12 +62,12 @@
|
|
| 62 |
}
|
| 63 |
</script>
|
| 64 |
|
| 65 |
-
<div class="detail-overlay" role="dialog" aria-modal="true" tabindex="-1" onclick={onClose}>
|
| 66 |
<div class="detail-container newly-caught" role="document" onclick={handleContainerClick}>
|
| 67 |
|
| 68 |
<!-- Celebration overlay -->
|
| 69 |
{#if showCelebration}
|
| 70 |
-
<div class="celebration-overlay" role="button" tabindex="0" onclick={dismissCelebration}>
|
| 71 |
<div class="celebration-content">
|
| 72 |
<div class="celebration-sparkles">✨</div>
|
| 73 |
<h1 class="celebration-title">Welcome to your team!</h1>
|
|
|
|
| 62 |
}
|
| 63 |
</script>
|
| 64 |
|
| 65 |
+
<div class="detail-overlay" role="dialog" aria-modal="true" tabindex="-1" onclick={onClose} onkeydown={(e) => e.key === 'Escape' ? onClose() : null}>
|
| 66 |
<div class="detail-container newly-caught" role="document" onclick={handleContainerClick}>
|
| 67 |
|
| 68 |
<!-- Celebration overlay -->
|
| 69 |
{#if showCelebration}
|
| 70 |
+
<div class="celebration-overlay" role="button" tabindex="0" onclick={dismissCelebration} onkeydown={(e) => e.key === 'Enter' || e.key === ' ' ? dismissCelebration() : null}>
|
| 71 |
<div class="celebration-content">
|
| 72 |
<div class="celebration-sparkles">✨</div>
|
| 73 |
<h1 class="celebration-title">Welcome to your team!</h1>
|
src/lib/db/piclets.ts
CHANGED
|
@@ -4,6 +4,139 @@ import { PicletType, AttackType, getTypeFromConcept } from '../types/picletTypes
|
|
| 4 |
import type { PicletStats } from '../types';
|
| 5 |
import { generateUnlockLevels } from '../services/unlockLevels';
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
// Save a new PicletInstance
|
| 9 |
export async function savePicletInstance(piclet: Omit<PicletInstance, 'id'>): Promise<number> {
|
|
|
|
| 4 |
import type { PicletStats } from '../types';
|
| 5 |
import { generateUnlockLevels } from '../services/unlockLevels';
|
| 6 |
|
| 7 |
+
// Interface for generated piclet data (from PicletResult component)
|
| 8 |
+
interface GeneratedPicletData {
|
| 9 |
+
name: string;
|
| 10 |
+
imageUrl: string;
|
| 11 |
+
imageData?: string;
|
| 12 |
+
imageCaption: string;
|
| 13 |
+
concept: string;
|
| 14 |
+
imagePrompt: string;
|
| 15 |
+
stats: PicletStats;
|
| 16 |
+
createdAt: Date;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
// Convert generated piclet data to a PicletInstance
|
| 20 |
+
export async function generatedDataToPicletInstance(data: GeneratedPicletData, level: number = 5): Promise<Omit<PicletInstance, 'id'>> {
|
| 21 |
+
if (!data.stats) {
|
| 22 |
+
throw new Error('Generated data must have stats to create PicletInstance');
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
// All generated data must now have the battle-ready format
|
| 26 |
+
const stats = data.stats as PicletStats;
|
| 27 |
+
|
| 28 |
+
if (!stats.baseStats || !stats.specialAbility || !stats.movepool) {
|
| 29 |
+
throw new Error('Generated stats must be in battle-ready format with baseStats, specialAbility, and movepool');
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
// Calculate base stats from battle-ready format
|
| 33 |
+
const baseHp = Math.floor(stats.baseStats.hp * 2 + 50);
|
| 34 |
+
const baseAttack = Math.floor(stats.baseStats.attack * 1.5 + 30);
|
| 35 |
+
const baseDefense = Math.floor(stats.baseStats.defense * 1.5 + 30);
|
| 36 |
+
const baseSpeed = Math.floor(stats.baseStats.speed * 1.5 + 30);
|
| 37 |
+
|
| 38 |
+
// Determine primary type from battle stats
|
| 39 |
+
const normalizedType = stats.primaryType.toLowerCase();
|
| 40 |
+
const validType = Object.values(PicletType).find(type => type === normalizedType);
|
| 41 |
+
const primaryType = validType || getTypeFromConcept(data.concept, data.imageCaption);
|
| 42 |
+
|
| 43 |
+
if (!validType) {
|
| 44 |
+
console.warn(`Invalid primaryType "${stats.primaryType}" from stats, falling back to concept detection`);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// Create moves from battle-ready format (without unlock levels initially)
|
| 48 |
+
const baseMoves: BattleMove[] = stats.movepool.map(move => ({
|
| 49 |
+
name: move.name,
|
| 50 |
+
type: move.type as unknown as AttackType,
|
| 51 |
+
power: move.power,
|
| 52 |
+
accuracy: move.accuracy,
|
| 53 |
+
pp: move.pp,
|
| 54 |
+
currentPp: move.pp,
|
| 55 |
+
description: move.description,
|
| 56 |
+
unlockLevel: 1 // Temporary, will be set below
|
| 57 |
+
}));
|
| 58 |
+
|
| 59 |
+
// Generate unlock levels for moves and special ability
|
| 60 |
+
const { movesWithUnlocks, abilityUnlockLevel } = generateUnlockLevels(baseMoves, stats.specialAbility);
|
| 61 |
+
const moves = movesWithUnlocks;
|
| 62 |
+
|
| 63 |
+
// Field stats are variations of regular stats
|
| 64 |
+
const baseFieldAttack = Math.floor(baseAttack * 0.8);
|
| 65 |
+
const baseFieldDefense = Math.floor(baseDefense * 0.8);
|
| 66 |
+
|
| 67 |
+
// Use Pokemon-accurate stat calculations (matching levelingService)
|
| 68 |
+
const calculateStat = (base: number, level: number) => {
|
| 69 |
+
if (level === 1) {
|
| 70 |
+
return Math.max(1, Math.floor(base / 10) + 5);
|
| 71 |
+
}
|
| 72 |
+
return Math.floor((2 * base * level) / 100) + 5;
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
const calculateHp = (base: number, level: number) => {
|
| 76 |
+
if (level === 1) {
|
| 77 |
+
return Math.max(1, Math.floor(base / 10) + 11);
|
| 78 |
+
}
|
| 79 |
+
return Math.floor((2 * base * level) / 100) + level + 10;
|
| 80 |
+
};
|
| 81 |
+
|
| 82 |
+
const maxHp = calculateHp(baseHp, level);
|
| 83 |
+
|
| 84 |
+
const bst = baseHp + baseAttack + baseDefense + baseFieldAttack + baseFieldDefense + baseSpeed;
|
| 85 |
+
|
| 86 |
+
return {
|
| 87 |
+
// Type Info
|
| 88 |
+
typeId: data.name.toLowerCase().replace(/\s+/g, '-'),
|
| 89 |
+
nickname: data.name,
|
| 90 |
+
primaryType: primaryType,
|
| 91 |
+
secondaryType: undefined,
|
| 92 |
+
|
| 93 |
+
// Current Stats
|
| 94 |
+
currentHp: maxHp,
|
| 95 |
+
maxHp,
|
| 96 |
+
level,
|
| 97 |
+
xp: 0,
|
| 98 |
+
attack: calculateStat(baseAttack, level),
|
| 99 |
+
defense: calculateStat(baseDefense, level),
|
| 100 |
+
fieldAttack: calculateStat(baseFieldAttack, level),
|
| 101 |
+
fieldDefense: calculateStat(baseFieldDefense, level),
|
| 102 |
+
speed: calculateStat(baseSpeed, level),
|
| 103 |
+
|
| 104 |
+
// Base Stats
|
| 105 |
+
baseHp,
|
| 106 |
+
baseAttack,
|
| 107 |
+
baseDefense,
|
| 108 |
+
baseFieldAttack,
|
| 109 |
+
baseFieldDefense,
|
| 110 |
+
baseSpeed,
|
| 111 |
+
|
| 112 |
+
// Battle
|
| 113 |
+
moves,
|
| 114 |
+
nature: stats.nature,
|
| 115 |
+
specialAbility: stats.specialAbility,
|
| 116 |
+
specialAbilityUnlockLevel: abilityUnlockLevel,
|
| 117 |
+
|
| 118 |
+
// Roster
|
| 119 |
+
isInRoster: false,
|
| 120 |
+
rosterPosition: undefined,
|
| 121 |
+
|
| 122 |
+
// Metadata
|
| 123 |
+
caught: false, // Scanned Piclets start as uncaught
|
| 124 |
+
caughtAt: undefined, // Will be set when caught
|
| 125 |
+
bst,
|
| 126 |
+
tier: stats.tier, // Use tier from stats
|
| 127 |
+
role: 'balanced', // Could be enhanced based on stat distribution
|
| 128 |
+
variance: 0,
|
| 129 |
+
|
| 130 |
+
// Original generation data
|
| 131 |
+
imageUrl: data.imageUrl,
|
| 132 |
+
imageData: data.imageData,
|
| 133 |
+
imageCaption: data.imageCaption,
|
| 134 |
+
concept: data.concept,
|
| 135 |
+
description: stats.description,
|
| 136 |
+
imagePrompt: data.imagePrompt
|
| 137 |
+
};
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
|
| 141 |
// Save a new PicletInstance
|
| 142 |
export async function savePicletInstance(piclet: Omit<PicletInstance, 'id'>): Promise<number> {
|