piclets / src /lib /components /Battle /PicletInfo.svelte
Fraser's picture
show act type
ea83c78
raw
history blame
5.79 kB
<script lang="ts">
import type { PicletInstance } from '$lib/db/schema';
import { getXpProgress, getXpTowardsNextLevel } from '$lib/services/levelingService';
import { TYPE_DATA } from '$lib/types/picletTypes';
export let piclet: PicletInstance;
export let hpPercentage: number;
export let xpPercentage: number = 0; // Legacy prop - will be overridden for players
export let isPlayer: boolean;
// Calculate real XP percentage using levelingService
$: realXpPercentage = isPlayer ? getXpProgress(piclet.xp, piclet.level, piclet.tier) : 0;
$: xpTowardsNext = isPlayer ? getXpTowardsNextLevel(piclet.xp, piclet.level, piclet.tier) : { current: 0, needed: 0, percentage: 0 };
// Type-based styling
$: typeData = TYPE_DATA[piclet.primaryType];
$: typeColor = typeData.color;
$: typeLogoPath = `/classes/${piclet.primaryType}.png`;
$: hpColor = hpPercentage > 0.5 ? '#34c759' : hpPercentage > 0.25 ? '#ffcc00' : '#ff3b30';
$: displayHp = Math.ceil(piclet.currentHp);
// Track HP changes for animations
let previousHp = displayHp;
let hpFlash = false;
$: if (displayHp !== previousHp) {
hpFlash = true;
setTimeout(() => hpFlash = false, 300);
previousHp = displayHp;
}
</script>
<div class="piclet-info-wrapper {isPlayer ? 'player-info-wrapper' : 'enemy-info-wrapper'}">
<div class="piclet-info" style="--type-logo: url('{typeLogoPath}')">
<!-- Type Logo Background -->
<div class="type-logo-background"></div>
<!-- Name Row -->
<div class="name-row">
<span class="piclet-name">{piclet.nickname}</span>
<span class="level-badge">Lv.{piclet.level}</span>
</div>
<!-- HP Section -->
<div class="stat-section">
<div class="stat-label">HP</div>
<div class="hp-bar">
<div
class="hp-fill"
style="width: {hpPercentage * 100}%; background-color: {hpColor}"
></div>
</div>
{#if isPlayer}
<div class="hp-text">
<span class="hp-values" class:hp-flash={hpFlash}>{displayHp}/{piclet.maxHp}</span>
</div>
{/if}
</div>
<!-- XP Section (Player only) -->
{#if isPlayer}
<div class="stat-section">
<div class="stat-label">XP</div>
<div class="xp-bar">
<div
class="xp-fill"
style="width: {xpTowardsNext.percentage}%"
></div>
</div>
</div>
{/if}
</div>
<!-- Triangle Pointer -->
<div class="triangle-pointer {isPlayer ? 'player-pointer' : 'enemy-pointer'}"></div>
</div>
<style>
.piclet-info-wrapper {
position: absolute;
display: flex;
align-items: center;
}
.player-info-wrapper {
right: 16px;
bottom: 20px;
flex-direction: row-reverse;
}
.enemy-info-wrapper {
left: 16px;
top: 20px;
flex-direction: row;
}
.piclet-info {
background: rgba(255, 255, 255, 0.9);
border-radius: 8px;
padding: 12px;
min-width: 160px;
position: relative;
overflow: hidden;
}
/* Type Logo Background */
.type-logo-background {
position: absolute;
bottom: -4px;
right: 0px;
width: 35px;
height: 35px;
background-image: var(--type-logo);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
opacity: 0.12;
pointer-events: none;
z-index: 1;
}
/* Name Row */
.name-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
position: relative;
z-index: 2;
}
.piclet-name {
font-weight: 600;
font-size: 14px;
color: #1a1a1a;
}
.level-badge {
background: rgba(142, 142, 147, 0.3);
padding: 2px 6px;
border-radius: 12px;
font-size: 11px;
font-weight: 700;
color: #333;
}
/* Stat Sections */
.stat-section {
margin-bottom: 6px;
position: relative;
z-index: 2;
}
.stat-section:last-child {
margin-bottom: 0;
}
.stat-label {
font-size: 10px;
font-weight: 600;
color: #666;
margin-bottom: 2px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* HP Bar */
.hp-bar {
width: 120px;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 2px;
}
.hp-fill {
height: 100%;
transition: width 0.5s ease, background-color 0.3s ease;
}
/* HP Text */
.hp-text {
font-size: 11px;
color: #666;
}
.hp-values {
font-weight: 600;
transition: all 0.2s ease;
}
.hp-values.hp-flash {
color: #ff4444;
text-shadow: 0 0 8px rgba(255, 68, 68, 0.6);
animation: hpFlash 0.3s ease-in-out;
}
@keyframes hpFlash {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* XP Bar */
.xp-bar {
width: 120px;
height: 6px;
background: #e0e0e0;
border-radius: 3px;
overflow: hidden;
}
.xp-fill {
height: 100%;
background: #2196f3;
transition: width 1.2s ease-out;
}
/* Triangle Pointer */
.triangle-pointer {
width: 0;
height: 0;
border-style: solid;
}
.player-pointer {
border-width: 8px 16px 8px 0;
border-color: transparent rgba(255, 255, 255, 0.9) transparent transparent;
margin-right: -1px;
}
.enemy-pointer {
border-width: 8px 0 8px 16px;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.9);
margin-left: -1px;
}
@media (max-width: 768px) {
.piclet-info {
min-width: 140px;
padding: 8px;
}
.piclet-name {
font-size: 12px;
}
.hp-bar {
width: 100px;
}
.xp-bar {
width: 100px;
}
}
</style>