piclets / src /lib /db /battleService.ts
Fraser's picture
better gen
a46ce65
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<PicletInstance> {
const caughtPiclet: Omit<PicletInstance, 'id'> = {
...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)
};
}
}