import type { PicletInstance, BattleState, BattlePhase, BattleMove } from './schema'; import { db } from './index'; import { getEffectivenessMultiplier, AttackType } from '../types/picletTypes'; export class BattleService { // Initialize a new battle static createBattleState( playerPiclet: PicletInstance, enemyPiclet: PicletInstance, isWildBattle: boolean = true ): BattleState { return { phase: 'intro' as BattlePhase, currentTurn: 0, playerPiclet, enemyPiclet, isWildBattle, processingTurn: false, battleEnded: false }; } // Calculate damage with type effectiveness static calculateDamage( attacker: PicletInstance, defender: PicletInstance, move: BattleMove ): { damage: number; effectiveness: number } { const baseDamage = move.power || 50; const attackStat = move.power > 0 ? attacker.attack : attacker.fieldAttack; const defenseStat = move.power > 0 ? defender.defense : defender.fieldDefense; // Type effectiveness const effectiveness = getEffectivenessMultiplier( move.type, defender.primaryType, defender.secondaryType ); // STAB (Same Type Attack Bonus) - 1.5x if move type matches attacker's type const stab = (move.type === attacker.primaryType as unknown as AttackType || move.type === attacker.secondaryType as unknown as AttackType) ? 1.5 : 1; // Simple damage formula let damage = Math.floor((baseDamage * (attackStat / defenseStat) * 0.5) + 10); // Apply type effectiveness and STAB damage = Math.floor(damage * effectiveness * stab); // Add some randomness (85-100% of calculated damage) const randomFactor = 0.85 + Math.random() * 0.15; damage = Math.floor(damage * randomFactor); // Minimum 1 damage for non-immune moves if (effectiveness > 0 && damage < 1) { damage = 1; } return { damage, effectiveness }; } // Check if move hits (based on accuracy) static doesMoveHit(accuracy: number): boolean { return Math.random() * 100 < accuracy; } // Calculate capture rate for wild piclets static calculateCaptureRate( targetPiclet: PicletInstance, targetMaxHp: number ): number { const hpFactor = (targetMaxHp - targetPiclet.currentHp) / targetMaxHp; const levelFactor = Math.max(0.5, 1 - (targetPiclet.level / 100)); // Base capture rate increases with damage and lower level const baseRate = 0.3; // 30% base rate const captureRate = baseRate + (hpFactor * 0.4) + (levelFactor * 0.3); return Math.min(0.95, captureRate); // Cap at 95% } // Attempt to catch a wild piclet static attemptCapture( targetPiclet: PicletInstance ): { success: boolean; shakes: number } { const captureRate = this.calculateCaptureRate(targetPiclet, targetPiclet.maxHp); const roll = Math.random(); // Calculate shakes (0-3) let shakes = 0; if (roll < captureRate * 0.9) shakes = 1; if (roll < captureRate * 0.7) shakes = 2; if (roll < captureRate * 0.5) shakes = 3; return { success: roll < captureRate, shakes }; } // Create a caught piclet instance static async createCaughtPiclet( wildPiclet: PicletInstance ): Promise { const caughtPiclet: Omit = { ...wildPiclet, isInRoster: false, // Goes to storage initially rosterPosition: undefined, caughtAt: new Date() }; const id = await db.picletInstances.add(caughtPiclet); return { ...caughtPiclet, id }; } // Calculate experience gain static calculateExpGain( defeatedPiclet: PicletInstance, isWild: boolean ): number { const baseExp = 50 + (defeatedPiclet.level * 10); const wildModifier = isWild ? 1 : 1.5; // Trainer piclets give more exp return Math.floor(baseExp * wildModifier); } // Check if piclet should level up static checkLevelUp( piclet: PicletInstance, expGained: number ): { leveledUp: boolean; newLevel: number } { const newExp = piclet.xp + expGained; const expForNextLevel = this.getExpForLevel(piclet.level + 1); if (newExp >= expForNextLevel) { return { leveledUp: true, newLevel: piclet.level + 1 }; } return { leveledUp: false, newLevel: piclet.level }; } // Get experience required for a level static getExpForLevel(level: number): number { // Simple exponential growth formula return Math.floor(Math.pow(level, 2.5) * 10); } // Apply stat boosts for level up static applyLevelUpStats(piclet: PicletInstance): PicletInstance { // Simple stat growth (5-10% increase per level) const growthFactor = 1.07; return { ...piclet, level: piclet.level + 1, maxHp: Math.floor(piclet.maxHp * growthFactor), currentHp: Math.floor(piclet.currentHp * growthFactor), attack: Math.floor(piclet.attack * growthFactor), defense: Math.floor(piclet.defense * growthFactor), fieldAttack: Math.floor(piclet.fieldAttack * growthFactor), fieldDefense: Math.floor(piclet.fieldDefense * growthFactor), speed: Math.floor(piclet.speed * growthFactor) }; } }