File size: 5,275 Bytes
a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 a46ce65 7428b13 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
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)
};
}
} |