| import type { PicletInstance, BattleState, BattlePhase, BattleMove } from './schema'; |
| import { db } from './index'; |
| import { getEffectivenessMultiplier, AttackType } from '../types/picletTypes'; |
|
|
| export class BattleService { |
| |
| static createBattleState( |
| playerPiclet: PicletInstance, |
| enemyPiclet: PicletInstance, |
| isWildBattle: boolean = true |
| ): BattleState { |
| return { |
| phase: 'intro' as BattlePhase, |
| currentTurn: 0, |
| playerPiclet, |
| enemyPiclet, |
| isWildBattle, |
| processingTurn: false, |
| battleEnded: false |
| }; |
| } |
|
|
| |
| 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; |
| |
| |
| const effectiveness = getEffectivenessMultiplier( |
| move.type, |
| defender.primaryType, |
| defender.secondaryType |
| ); |
| |
| |
| const stab = (move.type === attacker.primaryType as unknown as AttackType || move.type === attacker.secondaryType as unknown as AttackType) ? 1.5 : 1; |
| |
| |
| let damage = Math.floor((baseDamage * (attackStat / defenseStat) * 0.5) + 10); |
| |
| |
| damage = Math.floor(damage * effectiveness * stab); |
| |
| |
| const randomFactor = 0.85 + Math.random() * 0.15; |
| damage = Math.floor(damage * randomFactor); |
| |
| |
| if (effectiveness > 0 && damage < 1) { |
| damage = 1; |
| } |
| |
| return { damage, effectiveness }; |
| } |
|
|
| |
| static doesMoveHit(accuracy: number): boolean { |
| return Math.random() * 100 < accuracy; |
| } |
|
|
| |
| static calculateCaptureRate( |
| targetPiclet: PicletInstance, |
| targetMaxHp: number |
| ): number { |
| const hpFactor = (targetMaxHp - targetPiclet.currentHp) / targetMaxHp; |
| const levelFactor = Math.max(0.5, 1 - (targetPiclet.level / 100)); |
| |
| |
| const baseRate = 0.3; |
| const captureRate = baseRate + (hpFactor * 0.4) + (levelFactor * 0.3); |
| |
| return Math.min(0.95, captureRate); |
| } |
|
|
| |
| static attemptCapture( |
| targetPiclet: PicletInstance |
| ): { success: boolean; shakes: number } { |
| const captureRate = this.calculateCaptureRate(targetPiclet, targetPiclet.maxHp); |
| const roll = Math.random(); |
| |
| |
| 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 |
| }; |
| } |
|
|
| |
| static async createCaughtPiclet( |
| wildPiclet: PicletInstance |
| ): Promise<PicletInstance> { |
| const caughtPiclet: Omit<PicletInstance, 'id'> = { |
| ...wildPiclet, |
| isInRoster: false, |
| rosterPosition: undefined, |
| caughtAt: new Date() |
| }; |
| |
| const id = await db.picletInstances.add(caughtPiclet); |
| return { ...caughtPiclet, id }; |
| } |
|
|
| |
| static calculateExpGain( |
| defeatedPiclet: PicletInstance, |
| isWild: boolean |
| ): number { |
| const baseExp = 50 + (defeatedPiclet.level * 10); |
| const wildModifier = isWild ? 1 : 1.5; |
| |
| return Math.floor(baseExp * wildModifier); |
| } |
|
|
| |
| 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 |
| }; |
| } |
|
|
| |
| static getExpForLevel(level: number): number { |
| |
| return Math.floor(Math.pow(level, 2.5) * 10); |
| } |
|
|
| |
| static applyLevelUpStats(piclet: PicletInstance): PicletInstance { |
| |
| 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) |
| }; |
| } |
| } |