Fraser's picture
closer to battle running
4c208f2
raw
history blame
9.4 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 { BattleEngine } from '$lib/battle-engine/BattleEngine';
import type { BattleState, MoveAction } from '$lib/battle-engine/types';
import { picletInstanceToBattleDefinition, battlePicletToInstance } from '$lib/utils/battleConversion';
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
// Initialize battle engine
let battleEngine: BattleEngine;
let battleState: BattleState;
let currentPlayerPiclet = playerPiclet;
let currentEnemyPiclet = enemyPiclet;
// 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(() => {
// Initialize battle engine with converted piclet definitions
const playerDefinition = picletInstanceToBattleDefinition(playerPiclet);
const enemyDefinition = picletInstanceToBattleDefinition(enemyPiclet);
battleEngine = new BattleEngine(playerDefinition, enemyDefinition, playerPiclet.level, enemyPiclet.level);
battleState = battleEngine.getState();
// 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) {
if (!battleEngine) return;
battlePhase = 'main';
processingTurn = true;
// Find the corresponding move in the battle engine
const battleMove = battleState.playerPiclet.moves.find(m => m.move.name === move.name);
if (!battleMove) return;
const moveAction: MoveAction = {
type: 'move',
moveIndex: battleState.playerPiclet.moves.indexOf(battleMove)
};
try {
// Choose random enemy move (could be improved with AI)
const availableEnemyMoves = battleState.opponentPiclet.moves.filter(m => m.currentPP > 0);
if (availableEnemyMoves.length === 0) {
currentMessage = `${currentEnemyPiclet.nickname} has no moves left!`;
processingTurn = false;
return;
}
const randomEnemyMove = availableEnemyMoves[Math.floor(Math.random() * availableEnemyMoves.length)];
const enemyMoveIndex = battleState.opponentPiclet.moves.indexOf(randomEnemyMove);
const enemyAction: MoveAction = {
type: 'move',
moveIndex: enemyMoveIndex
};
// Execute the turn - battle engine handles priority automatically
const result = battleEngine.executeTurn(moveAction, enemyAction);
battleState = battleEngine.getState();
// Show battle messages with timing
if (result.log && result.log.length > 0) {
let messageIndex = 0;
function showNextBattleMessage() {
if (messageIndex < result.log.length) {
currentMessage = result.log[messageIndex];
messageIndex++;
setTimeout(showNextBattleMessage, 1500);
} else {
// After all messages, check battle end or continue
finalizeTurn();
}
}
showNextBattleMessage();
} else {
finalizeTurn();
}
function finalizeTurn() {
// Update UI state from battle engine
updateUIFromBattleState();
// Check for battle end
if (battleState.winner) {
battleEnded = true;
const winMessage = battleState.winner === 'player'
? `${currentEnemyPiclet.nickname} fainted! You won!`
: `${currentPlayerPiclet.nickname} fainted! You lost!`;
currentMessage = winMessage;
setTimeout(() => {
onBattleEnd(battleState.winner === 'player');
}, 2000);
} else {
setTimeout(() => {
currentMessage = `What will ${currentPlayerPiclet.nickname} do?`;
processingTurn = false;
}, 1000);
}
}
} catch (error) {
console.error('Battle engine error:', error);
currentMessage = 'Something went wrong in battle!';
processingTurn = false;
}
}
function updateUIFromBattleState() {
if (!battleState) return;
// Update player piclet state
currentPlayerPiclet = battlePicletToInstance(battleState.playerPiclet, currentPlayerPiclet);
playerHpPercentage = battleState.playerPiclet.currentHp / battleState.playerPiclet.maxHp;
// Update enemy piclet state
currentEnemyPiclet = battlePicletToInstance(battleState.opponentPiclet, currentEnemyPiclet);
enemyHpPercentage = battleState.opponentPiclet.currentHp / battleState.opponentPiclet.maxHp;
}
function handlePicletSelect(piclet: PicletInstance) {
if (!battleEngine) return;
battlePhase = 'main';
currentMessage = `Come back, ${currentPlayerPiclet.nickname}!`;
setTimeout(() => {
// Convert the selected piclet to battle definition and switch
const newPicletDefinition = picletInstanceToBattleDefinition(piclet);
try {
// TODO: Implement switching in battle engine
// For now, just update the UI
currentPlayerPiclet = piclet;
playerHpPercentage = piclet.currentHp / piclet.maxHp;
currentMessage = `Go, ${piclet.nickname}!`;
setTimeout(() => {
currentMessage = `What will ${piclet.nickname} do?`;
}, 1500);
} catch (error) {
console.error('Switch error:', error);
currentMessage = 'Unable to switch Piclets!';
}
}, 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={currentPlayerPiclet}
enemyPiclet={currentEnemyPiclet}
{playerHpPercentage}
{enemyHpPercentage}
showIntro={battlePhase === 'intro'}
{battleState}
/>
<BattleControls
{currentMessage}
{battlePhase}
{processingTurn}
{battleEnded}
{isWildBattle}
playerPiclet={currentPlayerPiclet}
enemyPiclet={currentEnemyPiclet}
{rosterPiclets}
{battleState}
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>