2025-11-26 06:53:44 +00:00
|
|
|
<template>
|
|
|
|
|
<div class="h-full flex flex-col p-4 gap-4 bg-[#1b1026] overflow-y-auto custom-scrollbar">
|
2025-11-26 09:53:03 +00:00
|
|
|
<!-- Pet Avatar -->
|
|
|
|
|
<PixelFrame class="flex-shrink-0" title="寵物資訊">
|
2025-11-26 06:53:44 +00:00
|
|
|
<!-- Helper Buttons Overlay -->
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="absolute top-1 right-1 z-30 flex gap-1">
|
2025-11-26 06:53:44 +00:00
|
|
|
<button
|
|
|
|
|
@click="$emit('openAchievements')"
|
|
|
|
|
class="p-1 bg-[#2b193f] border border-[#f6b26b] hover:bg-[#3d2459] active:translate-y-0.5 group"
|
2025-11-26 09:53:03 +00:00
|
|
|
title="成就"
|
2025-11-26 06:53:44 +00:00
|
|
|
>
|
|
|
|
|
<Trophy :size="14" class="text-[#f6b26b] group-hover:text-white" />
|
|
|
|
|
</button>
|
2025-11-26 15:31:46 +00:00
|
|
|
|
|
|
|
|
<!-- Evolution Help Button with Hover Tooltip -->
|
|
|
|
|
<div class="relative group/evo">
|
|
|
|
|
<button
|
|
|
|
|
class="p-1 bg-[#2b193f] border border-[#2ce8f4] hover:bg-[#3d2459] active:translate-y-0.5"
|
|
|
|
|
title="進化條件"
|
|
|
|
|
>
|
|
|
|
|
<HelpCircle :size="14" class="text-[#2ce8f4] group-hover/evo:text-white" />
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<!-- Evolution Info Tooltip (Hover) -->
|
|
|
|
|
<div class="absolute right-0 top-full mt-1 w-48 px-3 py-2 bg-[#0f0816] border-2 border-[#2ce8f4] text-[10px] text-[#e0d8f0] font-mono opacity-0 invisible group-hover/evo:opacity-100 group-hover/evo:visible transition-all duration-200 z-50 shadow-[0_0_20px_rgba(44,232,244,0.3)]">
|
|
|
|
|
<div class="flex items-center justify-between mb-2 pb-1 border-b border-[#2ce8f4]">
|
|
|
|
|
<span class="text-[#2ce8f4] font-bold">進化條件</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Egg → Baby -->
|
|
|
|
|
<div v-if="stats.class === 'egg'" class="space-y-1.5">
|
|
|
|
|
<p class="text-[#99e550] font-bold flex items-center gap-1">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M4 1H6V2H4V1ZM3 2H7V3H3V2ZM2 3H8V8H2V3ZM3 8H7V9H3V8Z" fill="#99e550"/><path d="M3 4H4V5H3V4ZM6 4H7V5H6V4ZM4 6H6V7H4V6Z" fill="#0f0816"/></svg>
|
|
|
|
|
→ 幼年期
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#e0d8f0]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M2 1H8V2H2V1ZM1 2H9V8H1V2ZM2 8H8V9H2V8Z" fill="#f6b26b"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM3 5H7V6H3V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
年齡達到: 5分鐘
|
|
|
|
|
</p>
|
|
|
|
|
<p class="text-[#8f80a0] text-[9px]">蛋孵化後自動進化</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Baby → Child -->
|
|
|
|
|
<div v-else-if="stats.class === 'baby'" class="space-y-1.5">
|
|
|
|
|
<p class="text-[#99e550] font-bold flex items-center gap-1">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 1H7V2H3V1ZM2 2H3V3H2V2ZM7 2H8V3H7V2ZM1 3H2V6H1V3ZM8 3H9V6H8V3ZM2 6H3V7H2V6ZM7 6H8V7H7V6ZM3 7H7V9H3V7Z" fill="#99e550"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM4 5H6V6H4V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
→ 成長期
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#e0d8f0]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M2 1H8V2H2V1ZM1 2H9V8H1V2ZM2 8H8V9H2V8Z" fill="#f6b26b"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM3 5H7V6H3V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
年齡達到: 6小時5分
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#f6b26b]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 2H7V3H3V2ZM2 3H3V6H2V3ZM7 3H8V6H7V3ZM3 6H4V7H3V6ZM6 6H7V7H6V6ZM4 6H6V8H4V6Z" fill="#f6b26b"/></svg>
|
|
|
|
|
力量 ≥ 8
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#2ce8f4]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 1H7V2H3V1ZM2 2H8V8H2V2ZM3 8H7V9H3V8Z" fill="#2ce8f4"/><path d="M3 3H7V4H3V3ZM4 4H6V5H4V4ZM5 5H6V6H5V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
智力 ≥ 8
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Child → Adult -->
|
|
|
|
|
<div v-else-if="stats.class === 'child'" class="space-y-1.5">
|
|
|
|
|
<p class="text-[#99e550] font-bold flex items-center gap-1">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 1H7V2H3V1ZM2 2H8V3H2V2ZM1 3H2V7H1V3ZM8 3H9V7H8V3ZM2 7H3V8H2V7ZM7 7H8V8H7V7ZM3 8H7V9H3V8Z" fill="#99e550"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM3 5H7V6H3V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
→ 成熟期
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#e0d8f0]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M2 1H8V2H2V1ZM1 2H9V8H1V2ZM2 8H8V9H2V8Z" fill="#f6b26b"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM3 5H7V6H3V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
年齡達到: 3天6小時5分
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#f6b26b]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 2H7V3H3V2ZM2 3H3V6H2V3ZM7 3H8V6H7V3ZM3 6H4V7H3V6ZM6 6H7V7H6V6ZM4 6H6V8H4V6Z" fill="#f6b26b"/></svg>
|
|
|
|
|
力量 ≥ 50
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#2ce8f4]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M3 1H7V2H3V1ZM2 2H8V8H2V2ZM3 8H7V9H3V8Z" fill="#2ce8f4"/><path d="M3 3H7V4H3V3ZM4 4H6V5H4V4ZM5 5H6V6H5V5Z" fill="#0f0816"/></svg>
|
|
|
|
|
智力 ≥ 50
|
|
|
|
|
</p>
|
|
|
|
|
<p class="flex items-center gap-1 text-[#99e550]">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M4 1H6V2H4V1ZM3 2H7V3H3V2ZM2 3H8V6H2V3ZM3 6H7V8H3V6ZM4 8H6V9H4V8Z" fill="#99e550"/><path d="M4 3H5V4H4V3ZM5 4H6V5H5V4Z" fill="#0f0816"/></svg>
|
|
|
|
|
敏捷 ≥ 50
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Adult → Evolution Branches -->
|
|
|
|
|
<div v-else-if="stats.class === 'adult'" class="space-y-1.5">
|
|
|
|
|
<p class="text-[#d95763] font-bold flex items-center gap-1">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" class="flex-shrink-0"><path d="M4 1H6V2H4V1ZM3 2H4V3H3V2ZM6 2H7V3H6V2ZM2 3H3V4H2V3ZM7 3H8V4H7V3ZM1 4H9V5H1V4ZM2 5H8V8H2V5ZM3 8H7V9H3V8Z" fill="#d95763"/><path d="M3 3H4V4H3V3ZM6 3H7V4H6V3ZM4 5H5V6H4V5Z" fill="#ffe762"/></svg>
|
|
|
|
|
已達最終階段
|
|
|
|
|
</p>
|
|
|
|
|
<p class="mt-1 text-[9px] text-[#8f80a0]">可能的進化分支:</p>
|
|
|
|
|
<p class="text-[9px] text-[#e0d8f0]">戰士猛虎 (STR優勢)</p>
|
|
|
|
|
<p class="text-[9px] text-[#e0d8f0]">敏捷靈貓 (DEX優勢)</p>
|
|
|
|
|
<p class="text-[9px] text-[#e0d8f0]">智者賢貓 (INT優勢)</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Tooltip Arrow -->
|
|
|
|
|
<div class="absolute top-0 right-2 w-0 h-0 border-l-4 border-l-transparent border-r-4 border-r-transparent border-b-4 border-b-[#2ce8f4] -translate-y-full"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-11-26 09:53:03 +00:00
|
|
|
<button
|
|
|
|
|
@click="$emit('deletePet')"
|
|
|
|
|
class="p-1 bg-[#2b193f] border border-[#d95763] hover:bg-[#3d2459] active:translate-y-0.5 group"
|
|
|
|
|
title="刪除寵物"
|
|
|
|
|
>
|
|
|
|
|
<Trash2 :size="14" class="text-[#d95763] group-hover:text-white" />
|
|
|
|
|
</button>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col items-center p-1 relative">
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="w-20 h-20 bg-[#1b1026] border-4 border-[#4a3b5e] mb-2 relative overflow-visible group shadow-inner flex items-center justify-center">
|
2025-11-26 06:53:44 +00:00
|
|
|
<!-- Background for portrait -->
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="absolute inset-0 bg-[#2b193f] opacity-50 overflow-hidden" />
|
2025-11-26 06:53:44 +00:00
|
|
|
|
|
|
|
|
<!-- The Animated Pixel Avatar -->
|
2025-11-26 15:31:46 +00:00
|
|
|
<div class="w-full h-full relative z-10">
|
2025-11-26 06:53:44 +00:00
|
|
|
<PixelAvatar
|
2025-11-26 15:31:46 +00:00
|
|
|
:stage="stats.class || stats.stage"
|
|
|
|
|
:species="stats.species"
|
|
|
|
|
:stats="{
|
|
|
|
|
str: stats.str,
|
|
|
|
|
int: stats.int,
|
|
|
|
|
dex: stats.dex,
|
|
|
|
|
happiness: stats.happiness,
|
|
|
|
|
generation: stats.generation,
|
|
|
|
|
age: stats.ageSeconds
|
|
|
|
|
}"
|
2025-11-26 06:53:44 +00:00
|
|
|
skinColor="#ffdbac"
|
|
|
|
|
outfitColor="#9fd75b"
|
2025-11-26 15:31:46 +00:00
|
|
|
:is-dead="stats.isDead"
|
2025-11-26 06:53:44 +00:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Scanline on portrait -->
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="absolute inset-0 bg-[linear-gradient(rgba(0,0,0,0)_50%,rgba(0,0,0,0.2)_50%)] bg-[length:100%_4px] pointer-events-none z-20 overflow-hidden" />
|
|
|
|
|
|
|
|
|
|
<!-- Mood Bubble (Shows on Hover) -->
|
|
|
|
|
<div class="absolute -top-6 -right-6 z-30 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none">
|
|
|
|
|
<div class="relative bg-[#e0d8f0] text-[#1b1026] p-2 border-2 border-[#1b1026] shadow-[4px_4px_0_rgba(0,0,0,0.2)]">
|
|
|
|
|
<!-- Pixel Tail -->
|
|
|
|
|
<div class="absolute bottom-[-6px] left-2 w-0 h-0 border-l-[6px] border-l-transparent border-t-[6px] border-t-[#1b1026] border-r-[6px] border-r-transparent"></div>
|
|
|
|
|
<div class="absolute bottom-[-3px] left-2 w-0 h-0 border-l-[4px] border-l-transparent border-t-[4px] border-t-[#e0d8f0] border-r-[4px] border-r-transparent z-10"></div>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col items-center gap-1 min-w-[40px]">
|
|
|
|
|
<!-- Pixel Mood Icon -->
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg" class="image-pixelated">
|
|
|
|
|
<!-- Happy -->
|
|
|
|
|
<g v-if="mood.type === 'happy'">
|
|
|
|
|
<path d="M2 3H3V5H2V3ZM7 3H8V5H7V3Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M2 6H3V7H2V6ZM7 6H8V7H7V6ZM3 7H7V8H3V7Z" fill="#1b1026"/>
|
|
|
|
|
</g>
|
|
|
|
|
<!-- Calm -->
|
|
|
|
|
<g v-else-if="mood.type === 'calm'">
|
|
|
|
|
<path d="M2 4H3V5H2V4ZM7 4H8V5H7V4Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M3 7H7V8H3V7Z" fill="#1b1026"/>
|
|
|
|
|
</g>
|
|
|
|
|
<!-- Bored -->
|
|
|
|
|
<g v-else-if="mood.type === 'bored'">
|
|
|
|
|
<path d="M2 4H4V5H2V4ZM6 4H8V5H6V4Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M3 7H7V8H3V7Z" fill="#1b1026"/>
|
|
|
|
|
</g>
|
|
|
|
|
<!-- Sad -->
|
2025-11-26 15:31:46 +00:00
|
|
|
<g v-else-if="mood.type === 'sad'">
|
2025-11-26 09:53:03 +00:00
|
|
|
<path d="M2 4H3V6H2V4ZM7 4H8V6H7V4Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M3 7H4V8H3V7ZM6 7H7V8H6V7ZM4 6H6V7H4V6Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M2 6H3V7H2V6ZM7 6H8V7H7V6Z" fill="#2ce8f4" fill-opacity="0.5"/>
|
|
|
|
|
</g>
|
2025-11-26 15:31:46 +00:00
|
|
|
<!-- Dead -->
|
|
|
|
|
<g v-else-if="mood.type === 'dead'">
|
|
|
|
|
<path d="M2 3H3V4H4V5H3V4H2V3ZM4 3H5V4H4V3Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M7 3H8V4H9V5H8V4H7V3ZM9 3H10V4H9V3Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M2 5H3V6H2V5ZM4 5H5V6H4V5Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M7 5H8V6H7V5ZM9 5H10V6H9V5Z" fill="#1b1026"/>
|
|
|
|
|
<path d="M3 7H8V8H3V7Z" fill="#1b1026"/>
|
|
|
|
|
</g>
|
2025-11-26 09:53:03 +00:00
|
|
|
</svg>
|
|
|
|
|
<span class="text-[10px] font-bold font-mono leading-none">{{ mood.text }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<h2 class="text-xl text-[#f6b26b] tracking-[0.2em] font-bold border-b-2 border-[#f6b26b] mb-1 leading-none pb-1 font-mono">{{ stats.name }}</h2>
|
|
|
|
|
<span class="text-xs text-[#8f80a0] uppercase tracking-wide font-mono mb-2">{{ translateStage(stats.class || stats.stage) }}</span>
|
|
|
|
|
|
|
|
|
|
<!-- Status Indicators -->
|
|
|
|
|
<div class="flex gap-2 items-center justify-center w-full mt-1 flex-wrap">
|
|
|
|
|
<!-- Sleeping Status -->
|
|
|
|
|
<div v-if="stats.isSleeping" class="px-2 py-0.5 bg-[#2b193f] border border-[#2ce8f4] text-[10px] text-[#2ce8f4] font-mono animate-pulse flex items-center gap-1" title="睡覺中">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M2 2H7V3H2V2ZM2 3H3V4H2V3ZM3 4H4V5H3V4ZM4 5H5V6H4V5ZM5 6H6V7H5V6ZM6 7H7V8H6V7ZM2 7H7V8H2V7Z" fill="#2ce8f4"/>
|
|
|
|
|
</svg>
|
|
|
|
|
睡眠
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Poop Status -->
|
|
|
|
|
<div v-if="stats.poopCount > 0" class="px-2 py-0.5 bg-[#2b193f] border border-[#d95763] text-[10px] text-[#d95763] font-mono animate-pulse flex items-center gap-1" title="需要清理便便">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M4 2H6V3H4V2ZM3 3H7V4H3V3ZM2 4H8V7H2V4ZM3 7H7V8H3V7Z" fill="#d95763"/>
|
|
|
|
|
<path d="M4 3H5V4H4V3ZM6 4H7V5H6V4Z" fill="#ff8f9c" fill-opacity="0.5"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{{ stats.poopCount }}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Sick Status -->
|
|
|
|
|
<div v-if="stats.isSick" class="px-2 py-0.5 bg-[#2b193f] border border-[#99e550] text-[10px] text-[#99e550] font-mono animate-pulse flex items-center gap-1" title="生病了">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M3 2H7V3H3V2ZM2 3H8V8H2V3ZM3 8H7V9H3V8Z" fill="#99e550"/>
|
|
|
|
|
<path d="M3 4H4V5H3V4ZM6 4H7V5H6V4ZM4 6H6V7H4V6Z" fill="#1b1026"/>
|
|
|
|
|
</svg>
|
|
|
|
|
生病
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Dying Status -->
|
|
|
|
|
<div v-if="stats.dyingSeconds > 0" class="px-2 py-0.5 bg-[#2b193f] border border-[#d95763] text-[10px] text-[#d95763] font-mono animate-pulse font-bold flex items-center gap-1" title="瀕死狀態!快急救!">
|
|
|
|
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M2 2H8V4H2V2ZM2 4H3V6H2V4ZM7 4H8V6H7V4ZM3 6H7V7H3V6ZM4 7H6V9H4V7Z" fill="#d95763"/>
|
|
|
|
|
<path d="M3 3H4V4H3V3ZM6 3H7V4H6V3Z" fill="#1b1026"/>
|
|
|
|
|
</svg>
|
|
|
|
|
瀕死
|
|
|
|
|
</div>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</PixelFrame>
|
|
|
|
|
|
|
|
|
|
<!-- Vitals - Updated to Health, Hunger, Happiness -->
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="flex flex-col gap-2 px-1 mb-2">
|
|
|
|
|
<!-- Health (Heart) -->
|
2025-11-26 15:31:46 +00:00
|
|
|
<RetroResourceBar :current="stats.hp" :max="stats.maxHp" type="hp" label="健康" :segments="10">
|
2025-11-26 09:53:03 +00:00
|
|
|
<template #icon>
|
|
|
|
|
<svg width="100%" height="100%" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M2 2H4V3H2V2ZM6 2H8V3H6V2ZM1 3H2V5H1V3ZM8 3H9V5H8V3ZM2 5H3V6H2V5ZM7 5H8V6H7V5ZM3 6H4V7H3V6ZM6 6H7V7H6V6ZM4 7H6V8H4V7Z" fill="#d95763"/>
|
|
|
|
|
<path d="M2 3H4V4H2V3ZM6 3H8V4H6V3ZM2 4H8V5H2V4ZM3 5H7V6H3V5ZM4 6H6V7H4V6Z" fill="#ff8f9c" fill-opacity="0.5"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</template>
|
|
|
|
|
</RetroResourceBar>
|
|
|
|
|
|
|
|
|
|
<!-- Hunger (Star) -->
|
|
|
|
|
<RetroResourceBar v-if="stats.hunger !== undefined" :current="stats.hunger" :max="stats.maxHunger || 100" type="energy" label="飢餓" :segments="10">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<svg width="100%" height="100%" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M4 1H6V3H4V1ZM1 4H3V6H1V4ZM7 4H9V6H7V4ZM4 7H6V9H4V7Z" fill="#f6b26b"/>
|
|
|
|
|
<path d="M4 3H6V7H4V3ZM3 4H7V6H3V4Z" fill="#ffe762"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</template>
|
|
|
|
|
</RetroResourceBar>
|
|
|
|
|
|
|
|
|
|
<!-- Happiness (Potion) -->
|
|
|
|
|
<RetroResourceBar v-if="stats.happiness !== undefined" :current="stats.happiness" :max="stats.maxHappiness || 100" type="mana" label="快樂" :segments="10">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<svg width="100%" height="100%" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M4 1H6V3H4V1ZM3 3H7V4H3V3ZM2 4H8V8H2V4ZM3 8H7V9H3V8Z" fill="#99e550"/>
|
|
|
|
|
<path d="M4 2H5V3H4V2ZM3 5H5V6H3V5ZM6 6H7V7H6V6Z" fill="#e0d8f0" fill-opacity="0.8"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</template>
|
|
|
|
|
</RetroResourceBar>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Pet Details Grid -->
|
|
|
|
|
<PixelFrame class="flex-shrink-0 mt-1" variant="inset">
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="flex flex-col gap-2 p-1">
|
|
|
|
|
<!-- Basic Info -->
|
|
|
|
|
<div class="grid grid-cols-2 gap-x-4 gap-y-2 text-[10px] uppercase text-[#8f80a0]">
|
|
|
|
|
<div class="flex flex-col border-r border-[#4a3b5e] pr-2">
|
|
|
|
|
<span class="text-[#4a3b5e] font-mono">等級</span>
|
|
|
|
|
<span class="text-[#e0d8f0] font-mono tracking-wide">Lv {{ stats.lvl }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col pl-2">
|
|
|
|
|
<span class="text-[#4a3b5e] font-mono">世代</span>
|
|
|
|
|
<span class="text-[#e0d8f0] font-mono tracking-wide">{{ stats.generation }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col border-r border-[#4a3b5e] border-t border-t-[#4a3b5e] pr-2 pt-2">
|
|
|
|
|
<span class="text-[#4a3b5e] font-mono">年齡</span>
|
|
|
|
|
<span class="text-[#e0d8f0] font-mono tracking-wide">{{ stats.age }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Height (Text Label) -->
|
|
|
|
|
<div class="flex flex-col border-t border-[#4a3b5e] pt-2 pl-2">
|
|
|
|
|
<span class="text-[#4a3b5e] font-mono">身高</span>
|
|
|
|
|
<span class="text-[#e0d8f0] font-mono tracking-wide">{{ stats.height }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Weight (Text Label) -->
|
|
|
|
|
<div class="flex flex-col border-t border-[#4a3b5e] pt-2 col-span-2">
|
|
|
|
|
<span class="text-[#4a3b5e] font-mono">體重</span>
|
|
|
|
|
<span class="text-[#e0d8f0] font-mono tracking-wide">{{ stats.weight }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Core Stats -->
|
|
|
|
|
<div class="grid grid-cols-2 gap-x-4 gap-y-2 pt-2 border-t border-[#4a3b5e]">
|
2025-11-26 15:31:46 +00:00
|
|
|
<div class="flex justify-between items-center px-1">
|
|
|
|
|
<span class="text-[10px] text-[#d95763] font-mono">生命</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono">{{ Math.floor(stats.maxHp || 100) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex justify-between items-center px-1">
|
|
|
|
|
<span class="text-[10px] text-[#d95763] font-mono">運氣</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono">{{ Math.floor(stats.luck || 0) }}</span>
|
|
|
|
|
</div>
|
2025-11-26 09:53:03 +00:00
|
|
|
<div class="flex justify-between items-center px-1">
|
|
|
|
|
<span class="text-[10px] text-[#f6b26b] font-mono">力量</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono">{{ Math.floor(stats.str || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex justify-between items-center px-1">
|
|
|
|
|
<span class="text-[10px] text-[#2ce8f4] font-mono">智力</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono">{{ Math.floor(stats.int || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex justify-between items-center px-1">
|
|
|
|
|
<span class="text-[10px] text-[#99e550] font-mono">敏捷</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono">{{ Math.floor(stats.dex || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Combat Stats -->
|
|
|
|
|
<div class="grid grid-cols-3 gap-x-2 gap-y-2 pt-2 border-t border-[#4a3b5e]">
|
|
|
|
|
<div class="flex flex-col items-center">
|
|
|
|
|
<span class="text-[8px] text-[#d95763] font-mono mb-0.5">攻擊</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono font-bold">{{ Math.floor(stats.atk || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col items-center">
|
|
|
|
|
<span class="text-[8px] text-[#f6b26b] font-mono mb-0.5">防禦</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono font-bold">{{ Math.floor(stats.def || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col items-center">
|
|
|
|
|
<span class="text-[8px] text-[#2ce8f4] font-mono mb-0.5">速度</span>
|
|
|
|
|
<span class="text-[10px] text-[#e0d8f0] font-mono font-bold">{{ Math.floor(stats.spd || 0) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
</PixelFrame>
|
|
|
|
|
|
2025-11-26 09:53:03 +00:00
|
|
|
<!-- Fate Display (Enhanced) -->
|
|
|
|
|
<div v-if="fateInfo" class="flex flex-col gap-2 mt-2 mb-6 px-1 flex-shrink-0">
|
|
|
|
|
<PixelFrame class="relative overflow-visible" variant="inset" title="命運">
|
|
|
|
|
<!-- Tier Badge -->
|
|
|
|
|
<div class="absolute -top-2 -right-2 z-10">
|
|
|
|
|
<div
|
|
|
|
|
class="px-2 py-0.5 text-[8px] font-bold font-mono border-2 shadow-lg"
|
|
|
|
|
:style="{
|
|
|
|
|
backgroundColor: fateInfo.color,
|
|
|
|
|
borderColor: fateInfo.color,
|
|
|
|
|
color: '#000',
|
|
|
|
|
boxShadow: `0 0 10px ${fateInfo.color}, 0 0 20px ${fateInfo.color}40`
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
{{ fateInfo.tierName }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="p-2 flex gap-2">
|
|
|
|
|
<!-- Pixel Icon with Glow -->
|
|
|
|
|
<div
|
|
|
|
|
class="w-12 h-12 flex-shrink-0 bg-[#0f0816] border-2 flex items-center justify-center relative overflow-hidden"
|
|
|
|
|
:style="{
|
|
|
|
|
borderColor: fateInfo.color,
|
|
|
|
|
boxShadow: `inset 0 0 10px ${fateInfo.color}40, 0 0 15px ${fateInfo.color}60`
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<!-- Pixel art background pattern -->
|
|
|
|
|
<div class="absolute inset-0 opacity-20" style="background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.1) 2px, rgba(255,255,255,0.1) 4px)"></div>
|
|
|
|
|
|
|
|
|
|
<!-- Icon based on tier -->
|
|
|
|
|
<div class="relative text-2xl animate-pulse" :style="{ filter: `drop-shadow(0 0 4px ${fateInfo.color})` }">
|
|
|
|
|
<Sparkles v-if="fateInfo.tier === 'SSR'" :size="32" :color="fateInfo.color" />
|
|
|
|
|
<Star v-else-if="fateInfo.tier === 'SR'" :size="28" :color="fateInfo.color" />
|
|
|
|
|
<Gem v-else-if="fateInfo.tier === 'R'" :size="24" :color="fateInfo.color" />
|
|
|
|
|
<Circle v-else-if="fateInfo.tier === 'N'" :size="20" :color="fateInfo.color" />
|
|
|
|
|
<Leaf v-else :size="16" :color="fateInfo.color" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Scanlines -->
|
|
|
|
|
<div class="absolute inset-0 bg-[linear-gradient(rgba(0,0,0,0)_50%,rgba(0,0,0,0.3)_50%)] bg-[length:100%_4px] pointer-events-none"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Fate Info -->
|
|
|
|
|
<div class="flex-1 min-w-0">
|
|
|
|
|
<!-- Name with glow effect -->
|
|
|
|
|
<div
|
|
|
|
|
class="text-sm font-bold font-mono mb-1 truncate"
|
|
|
|
|
:style="{
|
|
|
|
|
color: fateInfo.color,
|
|
|
|
|
textShadow: `0 0 8px ${fateInfo.color}, 0 0 12px ${fateInfo.color}80`
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
{{ stats.fate }}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Description -->
|
|
|
|
|
<p class="text-[9px] text-[#8f80a0] leading-tight mb-2 line-clamp-2 font-mono">
|
|
|
|
|
{{ fateInfo.description }}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<!-- Buffs -->
|
|
|
|
|
<div v-if="fateInfo.buffsList && fateInfo.buffsList.length > 0" class="flex flex-wrap gap-1">
|
|
|
|
|
<div
|
|
|
|
|
v-for="(buff, index) in fateInfo.buffsList"
|
|
|
|
|
:key="index"
|
|
|
|
|
class="text-[8px] px-1 py-0.5 border bg-[#0f0816] font-mono"
|
|
|
|
|
:style="{ borderColor: fateInfo.color + '60', color: fateInfo.color }"
|
|
|
|
|
:title="buff"
|
|
|
|
|
>
|
|
|
|
|
{{ buff }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</PixelFrame>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
|
2025-11-26 09:53:03 +00:00
|
|
|
<!-- Gold & Inventory -->
|
|
|
|
|
<div class="mt-auto px-1 pb-4 flex-shrink-0 flex gap-2">
|
|
|
|
|
<!-- Gold Display -->
|
|
|
|
|
<div class="flex-1">
|
|
|
|
|
<RetroCounter :value="stats.gold || 0" color="#ffe762">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<svg width="100%" height="100%" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M3 2H7V3H3V2ZM2 3H8V7H2V3ZM3 7H7V8H3V7Z" fill="#ffe762"/>
|
|
|
|
|
<path d="M4 3H5V6H4V3ZM6 3H7V6H6V3Z" fill="#b48b38"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</template>
|
|
|
|
|
</RetroCounter>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Inventory Button (Styled like Action Buttons) -->
|
|
|
|
|
<button
|
|
|
|
|
@click="$emit('openInventory')"
|
|
|
|
|
class="w-10 bg-[#1b1026] border border-[#4a3b5e] hover:bg-[#2b193f] active:translate-y-0.5 group flex flex-col items-center justify-center gap-1 py-1 transition-all relative overflow-hidden"
|
|
|
|
|
title="背包"
|
|
|
|
|
>
|
|
|
|
|
<!-- Pixel Icon -->
|
|
|
|
|
<div class="w-4 h-4 relative z-10">
|
|
|
|
|
<svg width="100%" height="100%" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M3 1H7V3H3V1ZM1 3H9V9H1V3ZM4 4H6V5H4V4Z" fill="#8f80a0" class="group-hover:fill-[#e0d8f0] transition-colors"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Corner Accents -->
|
|
|
|
|
<div class="absolute top-0 left-0 w-1 h-1 border-t border-l border-[#4a3b5e] opacity-50"></div>
|
|
|
|
|
<div class="absolute top-0 right-0 w-1 h-1 border-t border-r border-[#4a3b5e] opacity-50"></div>
|
|
|
|
|
<div class="absolute bottom-0 left-0 w-1 h-1 border-b border-l border-[#4a3b5e] opacity-50"></div>
|
|
|
|
|
<div class="absolute bottom-0 right-0 w-1 h-1 border-b border-r border-[#4a3b5e] opacity-50"></div>
|
|
|
|
|
</button>
|
2025-11-26 06:53:44 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-11-26 15:31:46 +00:00
|
|
|
import { computed, ref } from 'vue';
|
|
|
|
|
import { Trophy, Trash2, Sparkles, Star, Gem, Circle, Leaf, HelpCircle } from 'lucide-vue-next';
|
2025-11-26 06:53:44 +00:00
|
|
|
import PixelFrame from './PixelFrame.vue';
|
|
|
|
|
import RetroResourceBar from './RetroResourceBar.vue';
|
|
|
|
|
import RetroCounter from './RetroCounter.vue';
|
|
|
|
|
import PixelAvatar from './PixelAvatar.vue';
|
2025-11-26 09:53:03 +00:00
|
|
|
import { FATE_TIERS, FATES } from '../../../data/fates.js';
|
|
|
|
|
|
|
|
|
|
type EntityStats = any;
|
2025-11-26 06:53:44 +00:00
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
stats: EntityStats;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 09:53:03 +00:00
|
|
|
const props = defineProps<Props>();
|
|
|
|
|
defineEmits(['openAchievements', 'deletePet', 'openInventory']);
|
|
|
|
|
|
|
|
|
|
// Determine mood based on happiness
|
|
|
|
|
const mood = computed(() => {
|
2025-11-26 15:31:46 +00:00
|
|
|
if (props.stats.isDead) return { icon: '💀', text: '死亡', type: 'dead' };
|
2025-11-26 09:53:03 +00:00
|
|
|
const happiness = props.stats.happiness || 0;
|
|
|
|
|
if (happiness >= 80) return { icon: '😄', text: '開心', type: 'happy' };
|
|
|
|
|
if (happiness >= 50) return { icon: '🙂', text: '平靜', type: 'calm' };
|
|
|
|
|
if (happiness >= 20) return { icon: '😐', text: '無聊', type: 'bored' };
|
|
|
|
|
return { icon: '😭', text: '難過', type: 'sad' };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Helper to translate stage
|
|
|
|
|
function translateStage(stage: string): string {
|
|
|
|
|
if (!stage) return '';
|
|
|
|
|
const map: Record<string, string> = {
|
|
|
|
|
'egg': '蛋',
|
|
|
|
|
'baby': '幼年期',
|
|
|
|
|
'child': '成長期',
|
|
|
|
|
'adult': '成熟期',
|
|
|
|
|
'mythic': '神話期',
|
|
|
|
|
'EGG': '蛋',
|
|
|
|
|
'BABY': '幼年期',
|
|
|
|
|
'CHILD': '成長期',
|
|
|
|
|
'ADULT': '成熟期',
|
|
|
|
|
'MYTHIC': '神話期'
|
|
|
|
|
};
|
|
|
|
|
return map[stage] || stage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process fate information
|
|
|
|
|
const fateInfo = computed(() => {
|
|
|
|
|
if (!props.stats.fate) return null;
|
|
|
|
|
|
|
|
|
|
// Find fate data
|
|
|
|
|
const fateData = FATES.find(f => f.name === props.stats.fate);
|
|
|
|
|
if (!fateData) return null;
|
|
|
|
|
|
|
|
|
|
const tierData = FATE_TIERS[fateData.tier as keyof typeof FATE_TIERS];
|
|
|
|
|
|
|
|
|
|
// Format buffs into readable list
|
|
|
|
|
const buffsList: string[] = [];
|
|
|
|
|
if (fateData.buffs) {
|
|
|
|
|
for (const [key, value] of Object.entries(fateData.buffs)) {
|
|
|
|
|
let displayValue = value as number;
|
|
|
|
|
let prefix = '+';
|
|
|
|
|
|
|
|
|
|
// Handle percentage buffs
|
|
|
|
|
if (typeof value === 'number') {
|
|
|
|
|
if (value < 0) {
|
|
|
|
|
prefix = '';
|
|
|
|
|
}
|
|
|
|
|
if (Math.abs(value) < 1 && value !== 0) {
|
|
|
|
|
displayValue = Math.round(value * 100);
|
|
|
|
|
buffsList.push(`${prefix}${displayValue}% ${formatBuffKey(key)}`);
|
|
|
|
|
} else {
|
|
|
|
|
buffsList.push(`${prefix}${displayValue} ${formatBuffKey(key)}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
tier: fateData.tier,
|
|
|
|
|
tierName: tierData.name,
|
|
|
|
|
color: tierData.color,
|
|
|
|
|
description: fateData.description,
|
|
|
|
|
buffsList
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Helper to format buff keys
|
|
|
|
|
function formatBuffKey(key: string): string {
|
|
|
|
|
const keyMap: Record<string, string> = {
|
|
|
|
|
luck: '運氣',
|
|
|
|
|
attack: '攻擊',
|
|
|
|
|
defense: '防禦',
|
|
|
|
|
speed: '速度',
|
|
|
|
|
strGain: '力量成長',
|
|
|
|
|
intGain: '智力成長',
|
|
|
|
|
dexGain: '敏捷成長',
|
|
|
|
|
healthRegen: '健康恢復',
|
|
|
|
|
happinessRecovery: '快樂恢復',
|
|
|
|
|
hungerDecay: '飢餓速度',
|
|
|
|
|
sicknessReduction: '生病機率↓',
|
|
|
|
|
badEventReduction: '壞事機率↓',
|
|
|
|
|
resourceGain: '資源獲得',
|
|
|
|
|
dropRate: '掉寶率',
|
|
|
|
|
gameSuccessRate: '遊戲成功率',
|
|
|
|
|
miniGameBonus: '小遊戲獎勵',
|
|
|
|
|
breedingSuccess: '繁殖成功率'
|
|
|
|
|
};
|
|
|
|
|
return keyMap[key] || key;
|
|
|
|
|
}
|
2025-11-26 06:53:44 +00:00
|
|
|
</script>
|