Fraser's picture
better gen
a46ce65
raw
history blame
7.53 kB
<script lang="ts">
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import type { PicletInstance, BattleMove } from '$lib/db/schema';
import BattleField from '../Battle/BattleField.svelte';
import BattleControls from '../Battle/BattleControls.svelte';
import { BattleService } from '$lib/db/battleService';
import { getEffectivenessText, getEffectivenessColor } from '$lib/types/picletTypes';
export let playerPiclet: PicletInstance;
export let enemyPiclet: PicletInstance;
export let isWildBattle: boolean = true;
export let onBattleEnd: (result: any) => void = () => {};
export let rosterPiclets: PicletInstance[] = []; // All roster piclets passed from parent
// Battle state
let currentMessage = isWildBattle
? `A wild ${enemyPiclet.nickname} appeared!`
: `Trainer wants to battle!`;
let battlePhase: 'intro' | 'main' | 'moveSelect' | 'picletSelect' | 'ended' = 'intro';
let processingTurn = false;
let battleEnded = false;
// HP animation states
let playerHpPercentage = playerPiclet.currentHp / playerPiclet.maxHp;
let enemyHpPercentage = enemyPiclet.currentHp / enemyPiclet.maxHp;
onMount(() => {
// Start intro sequence
setTimeout(() => {
currentMessage = `Go, ${playerPiclet.nickname}!`;
setTimeout(() => {
currentMessage = `What will ${playerPiclet.nickname} do?`;
battlePhase = 'main';
}, 1500);
}, 2000);
});
function handleAction(action: string) {
if (processingTurn || battleEnded) return;
switch (action) {
case 'catch':
if (isWildBattle) {
processingTurn = true;
currentMessage = 'You threw a Piclet Ball!';
setTimeout(() => {
currentMessage = 'The wild piclet broke free!';
processingTurn = false;
}, 2000);
}
break;
case 'run':
if (isWildBattle) {
currentMessage = 'Got away safely!';
battleEnded = true;
setTimeout(() => onBattleEnd(false), 1500);
} else {
currentMessage = "You can't run from a trainer battle!";
}
break;
}
}
function handleMoveSelect(move: BattleMove) {
battlePhase = 'main';
processingTurn = true;
currentMessage = `${playerPiclet.nickname} used ${move.name}!`;
setTimeout(() => {
if (!BattleService.doesMoveHit(move.accuracy)) {
currentMessage = `${playerPiclet.nickname}'s attack missed!`;
setTimeout(() => enemyTurn(), 1500);
return;
}
// Calculate damage with type effectiveness
const { damage, effectiveness } = BattleService.calculateDamage(playerPiclet, enemyPiclet, move);
// Update enemy HP
const newHp = Math.max(0, enemyPiclet.currentHp - damage);
enemyPiclet.currentHp = newHp;
enemyHpPercentage = newHp / enemyPiclet.maxHp;
// Show effectiveness message if applicable
const effectivenessMessage = getEffectivenessText(effectiveness);
if (effectivenessMessage) {
setTimeout(() => {
currentMessage = effectivenessMessage;
}, 1000);
}
setTimeout(() => {
if (enemyHpPercentage <= 0) {
currentMessage = `${enemyPiclet.nickname} fainted!`;
battleEnded = true;
setTimeout(() => onBattleEnd(true), 2000);
} else {
enemyTurn();
}
}, effectivenessMessage ? 2500 : 1500);
}, 1500);
}
function enemyTurn() {
const enemyMove = enemyPiclet.moves[Math.floor(Math.random() * enemyPiclet.moves.length)];
currentMessage = `${enemyPiclet.nickname} used ${enemyMove.name}!`;
setTimeout(() => {
if (!BattleService.doesMoveHit(enemyMove.accuracy)) {
currentMessage = `${enemyPiclet.nickname}'s attack missed!`;
setTimeout(() => {
currentMessage = `What will ${playerPiclet.nickname} do?`;
processingTurn = false;
}, 1500);
return;
}
const { damage, effectiveness } = BattleService.calculateDamage(enemyPiclet, playerPiclet, enemyMove);
// Update player HP
const newHp = Math.max(0, playerPiclet.currentHp - damage);
playerPiclet.currentHp = newHp;
playerHpPercentage = newHp / playerPiclet.maxHp;
// Show effectiveness message if applicable
const effectivenessMessage = getEffectivenessText(effectiveness);
if (effectivenessMessage) {
setTimeout(() => {
currentMessage = effectivenessMessage;
}, 1000);
}
setTimeout(() => {
if (playerHpPercentage <= 0) {
currentMessage = `${playerPiclet.nickname} fainted!`;
battleEnded = true;
setTimeout(() => onBattleEnd(false), 2000);
} else {
currentMessage = `What will ${playerPiclet.nickname} do?`;
processingTurn = false;
}
}, effectivenessMessage ? 2500 : 1500);
}, 1500);
}
function handlePicletSelect(piclet: PicletInstance) {
battlePhase = 'main';
currentMessage = `Come back, ${playerPiclet.nickname}!`;
setTimeout(() => {
playerPiclet = piclet;
playerHpPercentage = piclet.currentHp / piclet.maxHp;
currentMessage = `Go, ${piclet.nickname}!`;
setTimeout(() => {
currentMessage = `What will ${piclet.nickname} do?`;
}, 1500);
}, 1500);
}
function handleBack() {
battlePhase = 'main';
}
</script>
<div class="battle-page" transition:fade={{ duration: 300 }}>
<nav class="battle-nav">
<button class="back-button" on:click={() => onBattleEnd('cancelled')} style="display: none;">
← Back
</button>
<h1>{isWildBattle ? 'Wild Battle' : 'Battle'}</h1>
<div class="nav-spacer"></div>
</nav>
<div class="battle-content">
<BattleField
{playerPiclet}
{enemyPiclet}
{playerHpPercentage}
{enemyHpPercentage}
showIntro={battlePhase === 'intro'}
/>
<BattleControls
{currentMessage}
{battlePhase}
{processingTurn}
{battleEnded}
{isWildBattle}
{playerPiclet}
{enemyPiclet}
{rosterPiclets}
onAction={handleAction}
onMoveSelect={handleMoveSelect}
onPicletSelect={handlePicletSelect}
onBack={handleBack}
/>
</div>
</div>
<style>
.battle-page {
position: fixed;
inset: 0;
z-index: 1000;
height: 100vh;
display: flex;
flex-direction: column;
background: #f8f9fa;
overflow: hidden;
padding-top: env(safe-area-inset-top);
}
@media (max-width: 768px) {
.battle-page {
background: white;
}
.battle-page::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: env(safe-area-inset-top);
background: white;
z-index: 1;
}
}
.battle-nav {
display: none; /* Hide navigation in battle */
}
.back-button {
background: none;
border: none;
color: #007bff;
font-size: 1rem;
cursor: pointer;
padding: 0.5rem;
}
.battle-nav h1 {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.nav-spacer {
width: 60px;
}
.battle-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
background: #f8f9fa;
}
</style>