piclets / src /lib /components /Battle /BattleField.svelte
Fraser's picture
battle ui
190d7a5
raw
history blame
6.24 kB
<script lang="ts">
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import type { PicletInstance } from '$lib/db/schema';
import PicletInfo from './PicletInfo.svelte';
export let playerPiclet: PicletInstance;
export let enemyPiclet: PicletInstance;
export let playerHpPercentage: number;
export let enemyHpPercentage: number;
export let showIntro: boolean = false;
// Animation states
let playerVisible = false;
let enemyVisible = false;
let trainerVisible = true;
// Calculate player XP percentage (0-100% of current level)
const playerXpPercentage = (playerPiclet.xp / 100) * 100; // Simplified, should use actual XP curve
onMount(() => {
if (showIntro) {
// Intro animation sequence
setTimeout(() => {
trainerVisible = false;
enemyVisible = true;
}, 500);
setTimeout(() => {
playerVisible = true;
}, 1500);
} else {
// Skip intro
playerVisible = true;
enemyVisible = true;
trainerVisible = false;
}
});
</script>
<div class="battle-field">
<!-- Trainer intro image -->
{#if showIntro && trainerVisible}
<div class="trainer-intro" transition:fade={{ duration: 300 }}>
<img src="/assets/default_trainer.png" alt="Trainer" />
</div>
{/if}
<div class="battle-content">
<!-- Enemy Row -->
<div class="enemy-row">
<div class="enemy-stack">
<PicletInfo
piclet={enemyPiclet}
hpPercentage={enemyHpPercentage}
xpPercentage={0}
isPlayer={false}
/>
{#if enemyVisible}
<div class="enemy-piclet-wrapper" transition:fade={{ duration: 300 }}>
<img
class="piclet-image enemy-image"
src={enemyPiclet.imageData || enemyPiclet.imageUrl}
alt={enemyPiclet.nickname}
on:error={(e) => e.currentTarget.src = 'https://via.placeholder.com/100x100?text=Piclet'}
/>
<img
class="platform enemy-platform"
src="/assets/grass.PNG"
alt="Platform"
on:error={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling.style.display = 'block';
}}
/>
<div class="platform-fallback enemy-platform-fallback" style="display: none;"></div>
</div>
{/if}
</div>
</div>
<div class="spacer"></div>
<!-- Player Row -->
<div class="player-row">
<div class="player-stack">
{#if playerVisible}
<div class="player-piclet-wrapper" transition:fade={{ duration: 300 }}>
<img
class="piclet-image player-image"
src={playerPiclet.imageData || playerPiclet.imageUrl}
alt={playerPiclet.nickname}
on:error={(e) => e.currentTarget.src = 'https://via.placeholder.com/120x120?text=Piclet'}
/>
<img
class="platform player-platform"
src="/assets/grass.PNG"
alt="Platform"
on:error={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling.style.display = 'block';
}}
/>
<div class="platform-fallback player-platform-fallback" style="display: none;"></div>
</div>
{/if}
<PicletInfo
piclet={playerPiclet}
hpPercentage={playerHpPercentage}
xpPercentage={playerXpPercentage}
isPlayer={true}
/>
</div>
</div>
</div>
</div>
<style>
.battle-field {
height: 280px;
position: relative;
overflow: hidden;
background: repeating-linear-gradient(
to bottom,
rgba(76, 175, 80, 0.2) 0px,
rgba(76, 175, 80, 0.2) 5px,
rgba(76, 175, 80, 0.1) 5px,
rgba(76, 175, 80, 0.1) 10px
);
}
.trainer-intro {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
.trainer-intro img {
width: 200px;
height: auto;
}
.battle-content {
display: flex;
flex-direction: column;
height: 100%;
}
/* Enemy Row */
.enemy-row {
flex: 1;
position: relative;
}
.enemy-stack {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
.enemy-piclet-wrapper {
position: absolute;
right: 40px;
top: 0;
}
.enemy-image {
width: 100px;
height: 100px;
object-fit: contain;
display: block;
}
.enemy-platform {
width: 140px;
height: 140px;
position: absolute;
bottom: -60px;
left: -20px;
z-index: 0;
object-fit: cover;
}
/* Player Row */
.player-row {
height: 140px;
position: relative;
}
.player-stack {
position: relative;
width: 100%;
height: 100%;
}
.player-piclet-wrapper {
position: absolute;
left: 40px;
bottom: 0;
}
.player-image {
width: 120px;
height: 120px;
object-fit: contain;
display: block;
}
.player-platform {
width: 160px;
height: 160px;
position: absolute;
bottom: -80px;
left: -20px;
z-index: 0;
object-fit: cover;
}
/* Platform fallbacks */
.platform-fallback {
position: absolute;
background: rgba(76, 175, 80, 0.3);
border-radius: 50%;
}
.enemy-platform-fallback {
width: 140px;
height: 140px;
bottom: -60px;
left: -20px;
}
.player-platform-fallback {
width: 160px;
height: 160px;
bottom: -80px;
left: -20px;
}
/* Piclet images */
.piclet-image {
image-rendering: auto;
filter: drop-shadow(-2px 0 4px rgba(0, 0, 0, 0.1));
position: relative;
z-index: 1;
}
.spacer {
flex: 1;
}
@media (max-width: 768px) {
.enemy-image {
width: 100px;
height: 100px;
}
.player-image {
width: 120px;
height: 120px;
}
.enemy-piclet-wrapper {
right: 40px;
}
.player-piclet-wrapper {
left: 40px;
}
}
</style>