/** * Conversion utilities for transforming database types to battle engine types */ import type { PicletInstance, BattleMove } from '$lib/db/schema'; import type { PicletDefinition, Move, BaseStats, SpecialAbility } from '$lib/battle-engine/types'; import type { PicletStats, BattleEffect, AbilityTrigger } from '$lib/types'; import { PicletType, AttackType } from '$lib/types/picletTypes'; import { recalculatePicletStats } from '$lib/services/levelingService'; import { getUnlockedMoves, isSpecialAbilityUnlocked } from '$lib/services/unlockLevels'; /** * Convert PicletInstance to PicletDefinition for battle engine use * Uses levelingService to ensure stats are properly calculated for current level */ export function picletInstanceToBattleDefinition(instance: PicletInstance): PicletDefinition { // First ensure stats are up-to-date for current level and nature const updatedInstance = recalculatePicletStats(instance); // Use the calculated stats directly (no need for complex reverse formulas) const baseStats: BaseStats = { hp: updatedInstance.maxHp, // Pokemon-calculated HP attack: updatedInstance.attack, // Includes level scaling and nature modifiers defense: updatedInstance.defense, // Includes level scaling and nature modifiers speed: updatedInstance.speed // Includes level scaling and nature modifiers }; // Only include unlocked moves const unlockedMoves = getUnlockedMoves(instance.moves, updatedInstance.level); const movepool: Move[] = unlockedMoves.map(move => convertBattleMoveToMove(move, instance.primaryType)); // Ensure at least one move is available (first move should always be unlocked at level 1) if (movepool.length === 0) { console.warn(`Piclet ${instance.nickname} has no unlocked moves at level ${updatedInstance.level}!`); // Emergency fallback - unlock first move if (instance.moves.length > 0) { const firstMove = convertBattleMoveToMove(instance.moves[0], instance.primaryType); movepool.push(firstMove); } } // All Piclets must now have special abilities if (!instance.specialAbility) { throw new Error('Piclet must have a special ability.'); } // Only include special ability if unlocked let specialAbility: SpecialAbility | undefined; if (isSpecialAbilityUnlocked(instance.specialAbilityUnlockLevel, updatedInstance.level)) { specialAbility = instance.specialAbility; } else { // Create a placeholder ability for locked special abilities specialAbility = { name: "Locked Ability", description: `Unlocks at level ${instance.specialAbilityUnlockLevel}`, trigger: "onBattleStart" as any, effects: [] }; } // Determine tier based on BST (Base Stat Total) const bst = baseStats.hp + baseStats.attack + baseStats.defense + baseStats.speed; let tier: 'low' | 'medium' | 'high' | 'legendary'; if (bst <= 300) tier = 'low'; else if (bst <= 400) tier = 'medium'; else if (bst <= 500) tier = 'high'; else tier = 'legendary'; return { name: instance.nickname || instance.typeId, // Keep original name - we'll add prefixes in battle engine description: instance.concept, tier, primaryType: instance.primaryType, secondaryType: instance.secondaryType, baseStats, nature: instance.nature, specialAbility, movepool }; } /** * Convert BattleMove to full Move for battle engine */ function convertBattleMoveToMove(battleMove: BattleMove, primaryType: PicletType): Move { // Create basic effects based on move properties const effects: any[] = []; if (battleMove.power > 0) { effects.push({ type: 'damage', target: 'opponent', amount: battleMove.power >= 80 ? 'strong' : battleMove.power >= 60 ? 'normal' : 'weak' }); } // Add status or stat effects for non-damaging moves if (battleMove.power === 0) { effects.push({ type: 'modifyStats', target: 'self', stats: { attack: 'increase' } }); } return { name: battleMove.name, type: battleMove.type, power: battleMove.power, accuracy: battleMove.accuracy, pp: battleMove.pp, priority: 0, flags: battleMove.power > 0 && battleMove.name.toLowerCase().includes('tackle') ? ['contact'] : [], effects }; } /** * Convert battle engine BattlePiclet back to PicletInstance for state updates */ export function battlePicletToInstance(battlePiclet: any, originalInstance: PicletInstance): PicletInstance { return { ...originalInstance, currentHp: battlePiclet.currentHp, level: battlePiclet.level, attack: battlePiclet.attack, defense: battlePiclet.defense, speed: battlePiclet.speed, // Update moves with current PP moves: battlePiclet.moves.map((moveData: any, index: number) => ({ ...originalInstance.moves[index], currentPp: moveData.currentPP })) }; } /** * Convert PicletStats (from generation) to PicletDefinition for battle engine */ export function picletStatsToBattleDefinition(stats: PicletStats, name: string, concept: string): PicletDefinition { return { name: stats.name || name, description: stats.description || concept, tier: stats.tier, primaryType: stats.primaryType, secondaryType: stats.secondaryType || undefined, baseStats: stats.baseStats, nature: stats.nature, specialAbility: stats.specialAbility, movepool: stats.movepool }; } /** * Strip internal battle prefixes from piclet names for display purposes * Removes "player-" and "enemy-" prefixes that are used internally for animation targeting */ export function stripBattlePrefix(name: string): string { if (name.startsWith('player-')) { return name.substring('player-'.length); } if (name.startsWith('enemy-')) { return name.substring('enemy-'.length); } return name; }