piclets / src /lib /components /Piclets /AbilityDisplay.svelte
Fraser's picture
MORE
782d90b
raw
history blame
9.66 kB
<script lang="ts">
import type { BattleEffect, AbilityTrigger } from '$lib/types';
interface Props {
ability: {
name: string;
description: string;
effects?: BattleEffect[];
triggers?: AbilityTrigger[];
};
expanded?: boolean;
}
let { ability, expanded = false }: Props = $props();
function getEffectIcon(effectType: string): string {
switch (effectType) {
case 'damage': return 'βš”οΈ';
case 'modifyStats': return 'πŸ“Š';
case 'applyStatus': return 'πŸ’«';
case 'heal': return 'πŸ’š';
case 'manipulatePP': return '⚑';
case 'fieldEffect': return '🌍';
case 'counter': return 'πŸ”„';
case 'priority': return '⚑';
case 'removeStatus': return '✨';
case 'mechanicOverride': return 'βš™οΈ';
default: return '❓';
}
}
function getEffectColor(effectType: string): string {
switch (effectType) {
case 'damage': return '#ff6b6b';
case 'modifyStats': return '#4dabf7';
case 'applyStatus': return '#9775fa';
case 'heal': return '#51cf66';
case 'manipulatePP': return '#ffd43b';
case 'fieldEffect': return '#495057';
case 'counter': return '#fd7e14';
case 'priority': return '#20c997';
case 'removeStatus': return '#74c0fc';
case 'mechanicOverride': return '#868e96';
default: return '#adb5bd';
}
}
function getTriggerIcon(event: string): string {
switch (event) {
case 'onDamageTaken': return 'πŸ›‘οΈ';
case 'onDamageDealt': return 'βš”οΈ';
case 'onContactDamage': return 'πŸ‘Š';
case 'onCriticalHit': return 'πŸ’₯';
case 'endOfTurn': return 'πŸ”„';
case 'onLowHP': return '❀️';
case 'onStatusInflicted': return 'πŸ’«';
case 'onHPDrained': return '🩸';
case 'onKO': return 'πŸ’€';
case 'onSwitchIn': return '➑️';
case 'onSwitchOut': return '⬅️';
case 'beforeMoveUse': return '⏰';
case 'afterMoveUse': return 'βœ…';
case 'onFullHP': return 'πŸ’š';
case 'onOpponentContactMove': return '🀜';
case 'onStatChange': return 'πŸ“ˆ';
case 'onTypeChange': return 'πŸ”„';
default: return '⚑';
}
}
function formatEffectDescription(effect: BattleEffect): string {
let desc = effect.type.charAt(0).toUpperCase() + effect.type.slice(1);
if (effect.target !== 'self') {
desc += ` (${effect.target})`;
}
if (effect.condition) {
desc += ` when ${effect.condition}`;
}
if (effect.amount) {
desc += ` - ${effect.amount}`;
}
if (effect.stats) {
const statChanges = Object.entries(effect.stats).map(([stat, change]) =>
`${stat}: ${change}`
).join(', ');
desc += ` (${statChanges})`;
}
if (effect.status) {
desc += ` - ${effect.status}`;
}
return desc;
}
</script>
<div class="ability-display">
<div class="ability-header">
<div class="ability-name-section">
<span class="ability-icon">✨</span>
<div class="ability-info">
<h3 class="ability-name">{ability.name}</h3>
<p class="ability-description">{ability.description}</p>
</div>
</div>
{#if (ability.effects?.length || 0) + (ability.triggers?.length || 0) > 0}
<div class="ability-counts">
{#if ability.effects?.length}
<span class="count-badge effects">
{ability.effects.length} effect{ability.effects.length !== 1 ? 's' : ''}
</span>
{/if}
{#if ability.triggers?.length}
<span class="count-badge triggers">
{ability.triggers.length} trigger{ability.triggers.length !== 1 ? 's' : ''}
</span>
{/if}
</div>
{/if}
</div>
{#if expanded && (ability.effects?.length || ability.triggers?.length)}
<div class="ability-details">
{#if ability.effects?.length}
<div class="effects-section">
<h4 class="section-title">
<span class="section-icon">🎯</span>
Passive Effects
</h4>
<div class="effects-list">
{#each ability.effects as effect}
<div class="effect-item">
<span
class="effect-icon"
style="color: {getEffectColor(effect.type)}"
>
{getEffectIcon(effect.type)}
</span>
<div class="effect-details">
<span class="effect-type">{effect.type}</span>
<span class="effect-description">{formatEffectDescription(effect)}</span>
</div>
</div>
{/each}
</div>
</div>
{/if}
{#if ability.triggers?.length}
<div class="triggers-section">
<h4 class="section-title">
<span class="section-icon">⚑</span>
Triggered Effects
</h4>
<div class="triggers-list">
{#each ability.triggers as trigger}
<div class="trigger-item">
<div class="trigger-header">
<span class="trigger-icon">{getTriggerIcon(trigger.event)}</span>
<div class="trigger-info">
<span class="trigger-event">{trigger.event}</span>
{#if trigger.condition}
<span class="trigger-condition">when {trigger.condition}</span>
{/if}
</div>
</div>
{#if trigger.effects?.length}
<div class="trigger-effects">
{#each trigger.effects as effect}
<div class="trigger-effect">
<span
class="effect-icon small"
style="color: {getEffectColor(effect.type)}"
>
{getEffectIcon(effect.type)}
</span>
<span class="effect-summary">{formatEffectDescription(effect)}</span>
</div>
{/each}
</div>
{/if}
</div>
{/each}
</div>
</div>
{/if}
</div>
{/if}
</div>
<style>
.ability-display {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 1px solid #dee2e6;
border-radius: 12px;
padding: 16px;
margin: 8px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.ability-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
}
.ability-name-section {
display: flex;
align-items: flex-start;
gap: 12px;
flex: 1;
}
.ability-icon {
font-size: 24px;
margin-top: 2px;
}
.ability-info {
flex: 1;
}
.ability-name {
font-size: 18px;
font-weight: 600;
color: #495057;
margin: 0 0 4px 0;
}
.ability-description {
font-size: 14px;
color: #6c757d;
margin: 0;
line-height: 1.4;
}
.ability-counts {
display: flex;
flex-direction: column;
gap: 4px;
align-items: flex-end;
}
.count-badge {
font-size: 11px;
padding: 3px 8px;
border-radius: 12px;
font-weight: 500;
white-space: nowrap;
}
.count-badge.effects {
background: #e3f2fd;
color: #1976d2;
}
.count-badge.triggers {
background: #fff3e0;
color: #f57c00;
}
.ability-details {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #e9ecef;
}
.section-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: #495057;
margin: 0 0 12px 0;
}
.section-icon {
font-size: 16px;
}
.effects-section,
.triggers-section {
margin-bottom: 16px;
}
.effects-list,
.triggers-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.effect-item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.7);
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.08);
}
.effect-icon {
font-size: 16px;
margin-top: 1px;
}
.effect-icon.small {
font-size: 14px;
}
.effect-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.effect-type {
font-size: 12px;
font-weight: 600;
color: #495057;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.effect-description {
font-size: 13px;
color: #6c757d;
line-height: 1.3;
}
.trigger-item {
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 8px;
padding: 12px;
}
.trigger-header {
display: flex;
align-items: flex-start;
gap: 10px;
margin-bottom: 8px;
}
.trigger-icon {
font-size: 16px;
margin-top: 1px;
}
.trigger-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.trigger-event {
font-size: 13px;
font-weight: 600;
color: #495057;
}
.trigger-condition {
font-size: 12px;
color: #868e96;
font-style: italic;
}
.trigger-effects {
display: flex;
flex-direction: column;
gap: 6px;
padding-left: 26px;
}
.trigger-effect {
display: flex;
align-items: center;
gap: 8px;
}
.effect-summary {
font-size: 12px;
color: #6c757d;
line-height: 1.3;
}
</style>