|
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) |
|
}; |
|
} |
|
} |