pet_data/app/app-original.vue.backup

1815 lines
50 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="pet-system-container">
<!-- 名字輸入對話框 -->
<div v-if="showNameInput" class="name-input-modal">
<div class="modal-content">
<h2>🐾 為你的寵物命名</h2>
<p>請為你的新寵物取一個名字</p>
<input
v-model="inputPetName"
@keyup.enter="confirmName"
type="text"
placeholder="輸入寵物名字..."
maxlength="20"
class="name-input"
/>
<div class="modal-buttons">
<button @click="confirmName" :disabled="!inputPetName || inputPetName.trim().length === 0">
確認
</button>
</div>
</div>
</div>
<div class="header">
<h1>🐾 虛擬寵物系統</h1>
<p class="subtitle">{{ systemStatus }}</p>
</div>
<!-- 寵物狀態顯示 -->
<div class="pet-status" v-if="petState">
<!-- 基礎屬性 -->
<div class="status-section">
<h3>基礎屬性</h3>
<div class="status-grid">
<div class="stat-item">
<span class="stat-label">飢餓</span>
<div class="stat-bar">
<div class="stat-fill" :style="{ width: `${petState.hunger || 0}%` }"></div>
<span class="stat-value">{{ Math.round(petState.hunger || 0) }}</span>
</div>
</div>
<div class="stat-item">
<span class="stat-label">快樂</span>
<div class="stat-bar">
<div class="stat-fill" :style="{ width: `${petState.happiness || 0}%` }"></div>
<span class="stat-value">{{ Math.round(petState.happiness || 0) }}</span>
</div>
</div>
<div class="stat-item">
<span class="stat-label">健康</span>
<div class="stat-bar">
<div class="stat-fill" :style="{ width: `${(petState.health / getMaxHealth(petState)) * 100 || 0}%` }"></div>
<span class="stat-value">{{ Math.round(petState.health || 0) }}/{{ getMaxHealth(petState) }}</span>
</div>
</div>
</div>
</div>
<!-- 寵物資料 -->
<div class="status-section">
<h3>═ 寵物資料 ═</h3>
<div class="pet-info-grid">
<div class="info-item">
<span class="info-label">名字</span>
<span class="info-value">{{ petState.name || '未命名' }}</span>
</div>
<div class="info-item">
<span class="info-label">階段</span>
<span class="info-value">{{ getStageName(petState.stage) }}</span>
</div>
<div class="info-item">
<span class="info-label">年齡</span>
<span class="info-value">{{ formatAge(petState.ageSeconds) }}</span>
</div>
<div class="info-item">
<span class="info-label">身高</span>
<span class="info-value">{{ Math.round(petState.height || 0) }} cm</span>
</div>
<div class="info-item">
<span class="info-label">身高</span>
<span class="info-value">{{ Math.round(petState.height || 0) }} cm</span>
</div>
<div class="info-item">
<span class="info-label">體重</span>
<span class="info-value">{{ Math.round(petState.weight || 0) }} g</span>
</div>
<div class="info-item">
<span class="info-label">命格</span>
<div class="info-value-group">
<span class="info-value highlight">{{ petState.destiny ? petState.destiny.name : '無' }}</span>
<span v-if="petState.destiny" class="info-sub">{{ petState.destiny.description }}</span>
</div>
</div>
<div class="info-item">
<span class="info-label">力量 (STR)</span>
<span class="info-value">{{ Math.round(petState.effectiveStr || petState.str || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">智力 (INT)</span>
<span class="info-value">{{ Math.round(petState.effectiveInt || petState.int || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">敏捷 (DEX)</span>
<span class="info-value">{{ Math.round(petState.effectiveDex || petState.dex || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">運勢 (LUCK)</span>
<span class="info-value">{{ Math.round(petState.effectiveLuck || petState.luck || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">世代</span>
<span class="info-value">第 {{ petState.generation || 1 }} 代</span>
</div>
<div class="info-item">
<span class="info-label">神明好感</span>
<span class="info-value">{{ getDeityFavorDisplay(petState) }}</span>
</div>
<div class="info-item">
<span class="info-label">HP</span>
<span class="info-value">{{ Math.round(petState.hp || petState.health || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">攻擊</span>
<span class="info-value">{{ Math.round(petState.attack || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">防禦</span>
<span class="info-value">{{ Math.round(petState.defense || 0) }}</span>
</div>
<div class="info-item">
<span class="info-label">速度</span>
<span class="info-value">{{ Math.round(petState.speed || 0) }}</span>
</div>
</div>
</div>
<div class="status-section">
<h3>═ 加成與隱藏數值 ═</h3>
<div class="bonus-grid">
<div
class="bonus-item"
v-for="(value, key) in getAllBonuses(petState)"
:key="key"
:class="{ 'not-implemented': !isImplemented(key) }"
>
<span class="bonus-label">{{ getBonusName(key) }}</span>
<span class="bonus-value" :class="{ positive: value > 0, negative: value < 0 }">
{{ value > 0 ? '+' : '' }}{{ value }}
</span>
</div>
</div>
</div>
<!-- 狀態標籤 -->
<div class="status-badges">
<span v-if="petState.isSleeping" class="badge">😴 睡覺中</span>
<span v-if="petState.isSick" class="badge sick">🤒 生病</span>
<span v-if="petState.poopCount > 0" class="badge">💩 便便 x{{ petState.poopCount }}</span>
</div>
</div>
<!-- 操作按鈕區 -->
<div class="action-panel">
<div class="action-group">
<h3>🐾 寵物互動</h3>
<div class="button-grid">
<button @click="handleFeed"
:disabled="!canDoAction('feed')"
:title="!canDoAction('feed') ? getDisabledReason('feed') : '增加飢餓值 (+體重)'">
🍽️ 餵食
</button>
<!-- 玩耍選項 -->
<div class="play-group" style="grid-column: span 2; display: flex; gap: 5px;">
<button @click="handlePlay('normal')"
:disabled="!canDoAction('play')"
:title="!canDoAction('play') ? getDisabledReason('play') : '一般玩耍 (體重 -1g)'"
style="flex: 1;">
🎮 玩耍
</button>
<button @click="handlePlay('training')"
:disabled="!canDoAction('play')"
:title="!canDoAction('play') ? getDisabledReason('play') : '體能訓練 (體重 -3g)'"
style="flex: 1;">
🏃 訓練
</button>
<button @click="handlePlay('puzzle')"
:disabled="!canDoAction('play')"
:title="!canDoAction('play') ? getDisabledReason('play') : '益智遊戲 (體重不變)'"
style="flex: 1;">
🧩 益智
</button>
</div>
<button @click="handleClean"
:disabled="!canDoAction('clean')"
:title="!canDoAction('clean') ? getDisabledReason('clean') : '清理便便'">
🧹 清理
</button>
<button @click="handleHeal"
:disabled="!canDoAction('heal')"
:title="!canDoAction('heal') ? getDisabledReason('heal') : '恢復健康'">
💊 治療
</button>
<button @click="handleSleep"
:disabled="!canDoAction('sleep')"
:title="!canDoAction('sleep') ? getDisabledReason('sleep') : '睡覺恢復健康'">
{{ petState?.isSleeping ? '⏰ 起床' : '😴 睡覺' }}
</button>
<button @click="handleShowStatus" :disabled="!isReady">📊 查看狀態</button>
<button @click="handleCheckEvolution" :disabled="!isReady" title="檢查進化條件">🔍 進化檢查</button>
<button @click="handleDeletePet" :disabled="!isReady" class="danger-button">🗑️ 刪除寵物</button>
</div>
</div>
<div class="action-group">
<h3>🎲 事件系統</h3>
<div class="button-grid">
<button @click="handleStart" :disabled="!isReady || isRunning">▶️ 啟動循環</button>
<button @click="handleStop" :disabled="!isRunning">⏹️ 停止循環</button>
<button @click="handleListEvents" :disabled="!isReady">📋 事件列表</button>
<button @click="handleEventHistory" :disabled="!isReady">📜 事件歷史</button>
<button @click="handleTestAllEvents" :disabled="!isReady">🧪 測試所有事件</button>
</div>
<!-- 快速觸發事件 -->
<details class="event-list">
<summary>✨ 快速觸發事件</summary>
<div class="button-grid small">
<button @click="handleTriggerEvent('lucky_find')" :disabled="!isReady">幸運發現</button>
<button @click="handleTriggerEvent('find_treat')" :disabled="!isReady">發現點心</button>
<button @click="handleTriggerEvent('playful_moment')" :disabled="!isReady">玩耍時光</button>
<button @click="handleTriggerEvent('deity_blessing')" :disabled="!isReady">神明祝福</button>
<button @click="handleTriggerEvent('over_eat')" :disabled="!isReady">吃太多</button>
<button @click="handleTriggerEvent('catch_cold')" :disabled="!isReady">感冒</button>
</div>
</details>
</div>
<div class="action-group">
<h3>🙏 神明系統</h3>
<div class="button-grid">
<button @click="handlePray" :disabled="!isReady">🙏 祈福</button>
<button @click="handleDrawFortune" :disabled="!isReady">🎴 抽籤</button>
<button @click="handleListDeities" :disabled="!isReady">👥 神明列表</button>
</div>
<!-- 切換神明 -->
<details class="deity-list">
<summary>🔄 切換神明</summary>
<div class="button-grid small">
<button @click="handleSwitchDeity('mazu')" :disabled="!isReady"
:class="{ active: petState?.currentDeityId === 'mazu' }">媽祖</button>
<button @click="handleSwitchDeity('earthgod')" :disabled="!isReady"
:class="{ active: petState?.currentDeityId === 'earthgod' }">土地公</button>
<button @click="handleSwitchDeity('yuelao')" :disabled="!isReady"
:class="{ active: petState?.currentDeityId === 'yuelao' }">月老</button>
<button @click="handleSwitchDeity('wenchang')" :disabled="!isReady"
:class="{ active: petState?.currentDeityId === 'wenchang' }">文昌</button>
<button @click="handleSwitchDeity('guanyin')" :disabled="!isReady"
:class="{ active: petState?.currentDeityId === 'guanyin' }">觀音</button>
</div>
</details>
</div>
</div>
<!-- Console 輸出 -->
<div class="console-output" id="console-output">
<div class="log-entry">✅ 系統載入中...</div>
</div>
<!-- Debug 面板 -->
<div class="debug-toggle" @click="showDebug = !showDebug">🔧</div>
<div v-if="showDebug" class="debug-panel">
<h3>🛠️ Debug 工具</h3>
<div class="debug-group">
<h4>神明系統</h4>
<button @click="debugResetPrayer">重置祈福次數</button>
<button @click="debugAddFavor(10)">好感度 +10</button>
<button @click="debugAddFavor(50)">好感度 +50</button>
</div>
<div class="debug-group">
<h4>事件系統</h4>
<button @click="debugTriggerEvent">隨機觸發事件</button>
</div>
<div class="debug-group">
<h4>寵物狀態</h4>
<button @click="debugFillStats">填滿飢餓/快樂</button>
<button @click="debugMaxAttributes">屬性全滿 (100)</button>
<button @click="debugAddPoop">💩 便便 x4</button>
<button @click="debugKill">一鍵瀕死</button>
</div>
</div>
</div>
</template>
<style scoped>
/* Debug 面板樣式 */
.debug-toggle {
position: fixed;
bottom: 10px;
right: 10px;
width: 40px;
height: 40px;
background: rgba(0, 0, 0, 0.5);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 9999;
font-size: 20px;
}
.debug-panel {
position: fixed;
bottom: 60px;
right: 10px;
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 15px;
border-radius: 10px;
z-index: 9999;
width: 200px;
border: 1px solid #444;
}
.debug-panel h3 {
margin: 0 0 10px 0;
font-size: 16px;
border-bottom: 1px solid #444;
padding-bottom: 5px;
}
.debug-group {
margin-bottom: 15px;
}
.debug-group h4 {
margin: 0 0 5px 0;
font-size: 12px;
color: #aaa;
}
.debug-panel button {
display: block;
width: 100%;
margin-bottom: 5px;
padding: 5px;
background: #333;
border: 1px solid #555;
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.debug-panel button:hover {
background: #555;
}
</style>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { PetSystem } from '../core/pet-system.js'
import { EventSystem } from '../core/event-system.js'
import { TempleSystem } from '../core/temple-system.js'
import { ApiService } from '../core/api-service.js'
// 創建 API 服務
const apiService = new ApiService({
useMock: true,
baseUrl: 'http://localhost:3000/api',
mockDelay: 100
})
// 響應式狀態
const petState = ref(null)
const systemStatus = ref('正在初始化...')
const isReady = ref(false)
const isRunning = ref(false)
const showNameInput = ref(false)
const inputPetName = ref('')
// 系統實例
let petSystem, eventSystem, templeSystem
// 更新狀態顯示
function updatePetState() {
if (petSystem) {
petState.value = petSystem.getState()
}
}
// 檢查當前階段是否可以執行某個動作
function canDoAction(action) {
if (!isReady.value || !petSystem) return false
return petSystem.isActionAllowed(action)
}
// 獲取按鈕禁用的原因
function getDisabledReason(action) {
if (!isReady.value || !petSystem) return '系統尚未就緒'
if (!petSystem.isActionAllowed(action)) {
const stage = petState.value?.stage || 'unknown'
const stageNames = {
'egg': '蛋階段',
'baby': '幼體',
'child': '幼年',
'adult': '成年'
}
return `${stageNames[stage] || stage}不能執行此操作`
}
return ''
}
// 格式化年齡
function formatAge(ageSeconds) {
if (!ageSeconds) return '0 秒'
const days = Math.floor(ageSeconds / 86400)
const hours = Math.floor((ageSeconds % 86400) / 3600)
const minutes = Math.floor((ageSeconds % 3600) / 60)
const seconds = Math.floor(ageSeconds % 60)
if (days > 0) return `${days} 天 ${hours} 小時`
if (hours > 0) return `${hours} 小時 ${minutes} 分鐘`
if (minutes > 0) return `${minutes} 分鐘 ${seconds} 秒`
return `${seconds} 秒`
}
// 獲取階段名稱
function getStageName(stage) {
const stageNames = {
'egg': '蛋',
'baby': '幼體',
'child': '幼年',
'adult': '成年'
}
return stageNames[stage] || stage
}
// 獲取神明好感顯示
function getDeityFavorDisplay(state) {
if (!state || !state.currentDeityId) return '無'
const favor = state.deityFavors?.[state.currentDeityId] || 0
const deityNames = {
'mazu': '媽祖',
'earthgod': '土地公',
'yuelao': '月老',
'wenchang': '文昌',
'guanyin': '觀音'
}
return `${deityNames[state.currentDeityId] || state.currentDeityId}: ${favor}/100`
}
// 獲取所有加成
// 獲取所有加成
function getAllBonuses(state) {
if (!state) return {}
const bonuses = {}
// 1. 命格加成
if (state.destiny && state.destiny.buffs) {
for (const [key, value] of Object.entries(state.destiny.buffs)) {
bonuses[key] = (bonuses[key] || 0) + value
}
}
// 獲取當前神明(如果在神明系統範圍內)
let currentDeity = null
if (templeSystem) {
currentDeity = templeSystem.getCurrentDeity()
}
// 2. 神明基礎加成
if (currentDeity && currentDeity.buffs) {
for (const [key, value] of Object.entries(currentDeity.buffs)) {
bonuses[key] = (bonuses[key] || 0) + value
}
}
// 3. 臨時 Buff (需要從 eventSystem 獲取)
if (eventSystem) {
const activeBuffs = eventSystem.getBuffManager().getActiveBuffs()
for (const buff of activeBuffs) {
if (buff.effects) {
for (const [key, value] of Object.entries(buff.effects)) {
bonuses[key] = (bonuses[key] || 0) + value
}
}
}
}
// 4. 神明好感度等級加成
if (currentDeity && currentDeity.favorLevelBuffs) {
const favor = state.deityFavors?.[state.currentDeityId] || 0
const level = Math.floor(favor / currentDeity.favorLevelBuffs.interval)
if (level > 0 && currentDeity.favorLevelBuffs.buffsPerLevel) {
for (const [key, valuePerLevel] of Object.entries(currentDeity.favorLevelBuffs.buffsPerLevel)) {
bonuses[key] = (bonuses[key] || 0) + (valuePerLevel * level)
}
}
}
// 5. 神明滿級特殊 Buff
if (currentDeity && currentDeity.maxFavorBuff) {
const favor = state.deityFavors?.[state.currentDeityId] || 0
if (favor >= 100 && currentDeity.maxFavorBuff.effects) {
for (const [key, value] of Object.entries(currentDeity.maxFavorBuff.effects)) {
// 排除非數值效果(如 sicknessImmune
if (typeof value === 'number') {
bonuses[key] = (bonuses[key] || 0) + value
}
}
}
}
return bonuses
}
// 检查加成是否已实现
function isImplemented(key) {
const implemented = [
'str', 'int', 'dex', 'luck', 'health',
'strGain', 'intGain', 'dexGain',
'happinessRecovery', 'healthRecovery'
]
return implemented.includes(key)
}
// 獲取加成名稱
function getBonusName(key) {
const names = {
// 基礎屬性(影响战斗数值)
str: '力量',
int: '智力',
dex: '敏捷',
luck: '運勢',
health: '最大健康',
// 成長效率
strGain: '力量成長',
intGain: '智力成長',
dexGain: '敏捷成長',
// 状态恢复
happinessRecovery: '快樂恢復',
healthRecovery: '健康恢復',
// 未实现的加成
gameSuccessRate: '小遊戲成功率',
sicknessReduction: '生病抗性',
resourceGain: '資源獲得',
breedingSuccess: '繁殖成功率',
dropRate: '掉落率',
miniGameBonus: '小遊戲獎勵',
badEventReduction: '壞事件減少'
}
return names[key] || key
}
// 获取最大健康值
function getMaxHealth(state) {
if (!state) return 100
const bonuses = getAllBonuses(state)
return 100 + (bonuses.health || 0)
}
// 顯示狀態(簡潔版)
function showStatus() {
if (!petSystem || !eventSystem || !templeSystem) {
console.log('系統尚未初始化')
return
}
const state = petSystem.getState()
if (!state) {
console.log('狀態尚未載入')
return
}
updatePetState()
const buffs = eventSystem.getBuffManager().getActiveBuffs()
const currentDeity = templeSystem.getCurrentDeity()
const favorStars = templeSystem.getFavorStars(state.currentDeityId)
const buffNames = buffs.map(b => b.name)
// 檢查神明滿級 Buff
if (currentDeity && state.deityFavors?.[state.currentDeityId] >= 100 && currentDeity.maxFavorBuff) {
const buff = currentDeity.maxFavorBuff
buffNames.push(`✨${buff.name}(${buff.description})`)
}
// 安全獲取數值,避免 undefined
const safeNum = (val) => (typeof val === 'number' && !isNaN(val) ? val.toFixed(0) : '0')
console.log(`🐾 狀態 | 飢餓:${safeNum(state.hunger)} 快樂:${safeNum(state.happiness)} 健康:${safeNum(state.health)} | 力量:${safeNum(state.str)} 智力:${safeNum(state.int)} 敏捷:${safeNum(state.dex)} 運勢:${safeNum(state.luck)} | ${currentDeity?.name || '未知'}${favorStars} | Buff:${buffNames.length > 0 ? buffNames.join(', ') : '無'}`)
}
// 啟動遊戲循環
function start() {
if (isRunning.value) {
console.log('⚠️ 遊戲循環已在運行中')
return
}
isRunning.value = true
// 移除 tick 回調中的自動輸出,只在有事件時才輸出
petSystem.startTickLoop(() => {
// 更新狀態顯示
updatePetState()
})
eventSystem.startEventCheck()
// 初始化全局 Debug 工具
window.debug = {
// 重置祈福次數
resetPrayer: async () => {
await templeSystem.debugResetDailyPrayer()
console.log('✅ 祈福次數已重置')
showStatus()
},
// 設置好感度
setFavor: async (deityId, amount) => {
const state = petSystem.getState()
await petSystem.updateState({
deityFavors: {
...state.deityFavors,
[deityId]: amount
}
})
console.log(`✅ ${deityId} 好感度已設置為 ${amount}`)
showStatus()
},
// 觸發事件
triggerEvent: async (eventId) => {
await eventSystem.debugTriggerEvent(eventId)
showStatus()
},
// 設置屬性
setStat: async (stat, value) => {
await petSystem.debugSetStat(stat, value)
showStatus()
},
// 幫助
help: () => {
console.log(`
🛠️ Debug 工具列表:
- debug.resetPrayer() 重置每日祈福次數
- debug.setFavor(id, amount) 設置神明好感度 (例如: 'mazu', 99)
- debug.triggerEvent(id) 觸發事件 (不填 ID 則隨機)
- debug.setStat(stat, value) 設置屬性 (例如: 'hunger', 100)
`)
}
}
console.log('🔧 Debug 工具已就緒,輸入 debug.help() 查看指令')
console.log('✅ 遊戲循環已啟動(靜默模式,只在事件觸發時輸出)')
}
// 停止遊戲循環
function stop() {
if (!isRunning.value) {
console.log('⚠️ 遊戲循環未運行')
return
}
petSystem.stopTickLoop()
eventSystem.stopEventCheck()
isRunning.value = false
console.log('⏹️ 遊戲循環已停止')
}
// 餵食
async function feed(amount = 20) {
const result = await petSystem.feed(amount)
if (result.success) {
console.log(`✅ 餵食 飢餓+${amount} 體重+${(amount * 0.5).toFixed(1)}g | STR+${result.strGain?.toFixed(2)}`)
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 玩耍
async function play(amount = 15) {
const result = await petSystem.play(amount)
if (result.success) {
const baseAmount = amount
const actualGain = result.happinessGain
const bonusPercent = actualGain > baseAmount ? `+${((actualGain / baseAmount - 1) * 100).toFixed(0)}%` : ''
console.log(`✅ 玩耍 快樂 ${actualGain.toFixed(1)} ${bonusPercent} | DEX+${result.dexGain.toFixed(2)} INT+${result.intGain.toFixed(2)}`)
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 清理便便
async function clean() {
const result = await petSystem.cleanPoop()
if (result.success) {
console.log('✅ 清理便便 +10 快樂')
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 治療
async function heal(amount = 20) {
const result = await petSystem.heal(amount)
if (result.success) {
const actualHeal = result.healAmount?.toFixed(1) || amount
const bonusText = result.healAmount > amount ? ` (+${((result.healAmount / amount - 1) * 100).toFixed(0)}%)` : ''
console.log(`✅ 治療 健康+${actualHeal}${bonusText}${result.cured ? ' (已治癒)' : ''}`)
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 睡覺/起床
async function sleep() {
const result = await petSystem.toggleSleep()
if (result.success) {
if (result.message) {
console.log(`✅ ${result.message}`)
} else {
console.log(`✅ ${result.isSleeping ? '已入睡' : '已醒來'}`)
}
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 觸發事件
async function triggerEvent(eventId) {
const result = await eventSystem.triggerEvent(eventId)
if (result) {
console.log(`✅ 事件 ${eventId} 觸發`)
showStatus()
} else {
console.log(`❌ 事件 ${eventId} 條件不滿足`)
}
}
// 查看事件列表
async function listEvents() {
const events = await apiService.getEvents()
console.log('\n📋 可用事件列表 (共 ' + events.length + ' 個):')
console.log('='.repeat(60))
// 按類型分組
const grouped = {
good: events.filter(e => e.type === 'good'),
bad: events.filter(e => e.type === 'bad'),
weird: events.filter(e => e.type === 'weird'),
rare: events.filter(e => e.type === 'rare')
}
for (const [type, list] of Object.entries(grouped)) {
if (list.length === 0) continue
const typeEmoji = {
good: '✨',
bad: '💀',
weird: '👻',
rare: '🌟'
}
console.log(`\n${typeEmoji[type]} ${type.toUpperCase()} 事件 (${list.length}個):`)
list.forEach(e => {
const effectsDesc = e.effects.map(eff => {
if (eff.type === 'modifyStats') {
const stats = Object.entries(eff.payload)
.map(([k, v]) => `${k}${v > 0 ? '+' : ''}${v}`)
.join(', ')
return stats
}
return eff.type
}).join(' | ')
console.log(` ${e.id}`)
console.log(` 效果: ${effectsDesc}`)
console.log(` 測試: triggerEvent('${e.id}')`)
})
}
console.log('\n' + '='.repeat(60))
console.log('💡 快速測試所有事件: testAllEvents()')
console.log('='.repeat(60) + '\n')
}
// 查看事件歷史
function eventHistory() {
const history = eventSystem.getHistory()
console.log('\n📜 事件歷史:')
console.log('='.repeat(50))
if (history.length === 0) {
console.log(' (無)')
} else {
history.forEach((h, i) => {
const time = new Date(h.timestamp).toLocaleTimeString()
console.log(`${i + 1}. [${time}] ${h.eventId} (${h.eventType})`)
})
}
console.log('='.repeat(50) + '\n')
}
// 祈福
async function pray() {
const result = await templeSystem.pray()
if (result.success) {
let message = `✅ 祈福 +${result.favorIncrease} 好感 → ${result.newFavor}/100 | ${result.dialogue}`
// 等级提升提示
if (result.levelUp) {
message += `\n🌟 好感度等級提升Lv.${result.oldLevel} → Lv.${result.newLevel}`
if (result.deity.favorLevelBuffs) {
const buffs = Object.entries(result.deity.favorLevelBuffs.buffsPerLevel)
.map(([key, value]) => `${key.toUpperCase()}+${value}`)
.join(', ')
message += ` (${buffs})`
}
}
// 滿級特殊 Buff 提示
if (result.reachedMax && result.deity.maxFavorBuff) {
message += `\n✨✨ 滿級祝福!獲得特殊 Buff: ${result.deity.maxFavorBuff.name}`
message += `\n ${result.deity.maxFavorBuff.description}`
}
console.log(message)
showStatus()
} else {
console.log(`❌ ${result.message}`)
}
}
// 抽籤
async function drawFortune() {
const result = await templeSystem.drawFortune()
if (result.success) {
console.log('\n🎴 抽籤結果:')
console.log('='.repeat(50))
console.log(`等級: ${result.lot.grade}`)
console.log(`籤詩: ${result.lot.poem1}`)
console.log(` ${result.lot.poem2}`)
console.log(`解釋: ${result.lot.meaning}`)
if (result.buff) {
console.log(`\n✨ 獲得 Buff: ${result.buff.name}`)
}
console.log('='.repeat(50) + '\n')
} else {
console.log(`❌ ${result.message}`)
}
}
// 切換神明
async function switchDeity(deityId) {
const result = await templeSystem.switchDeity(deityId)
if (result.success) {
console.log(`✅ 已切換到 ${result.deity.name}`)
} else {
console.log(`❌ ${result.message}`)
}
showStatus()
}
// 查看神明列表
function listDeities() {
const deities = templeSystem.getDeities()
console.log('\n🙏 神明列表:')
console.log('='.repeat(50))
deities.forEach(d => {
const state = petSystem.getState()
const favor = state.deityFavors[d.id] || 0
const stars = templeSystem.getFavorStars(d.id)
console.log(`\n${d.id}: ${d.name}`)
console.log(` 個性: ${d.personality}`)
console.log(` 好感: ${stars} (${favor}/100)`)
console.log(` 加成: ${d.buffDescriptions.join(', ')}`)
})
console.log('='.repeat(50) + '\n')
}
// 應用 Buff
async function applyBuffs() {
await eventSystem.applyBuffs()
eventSystem.getBuffManager().tick()
console.log('✅ Buff 已更新')
showStatus()
}
// 確認名字並初始化
async function confirmName() {
if (!inputPetName.value || inputPetName.value.trim().length === 0) {
return
}
showNameInput.value = false
const name = inputPetName.value.trim()
await init(name)
}
// 初始化系統
async function init(petName = null) {
petSystem = new PetSystem(apiService)
eventSystem = new EventSystem(petSystem, apiService)
templeSystem = new TempleSystem(petSystem, apiService)
try {
await petSystem.initialize('tinyTigerCat')
// 如果有提供名字,更新寵物名字
if (petName) {
await petSystem.updateState({ name: petName })
}
await eventSystem.initialize()
await templeSystem.initialize()
updatePetState()
isReady.value = true
systemStatus.value = '✅ 系統已就緒'
console.log('✅ 系統已初始化')
console.log('🔄 自動啟動遊戲循環...')
// 自動啟動遊戲循環
start()
showStatus()
} catch (error) {
// 如果沒有名字,顯示輸入對話框
if (error.message && error.message.includes('名字')) {
showNameInput.value = true
systemStatus.value = '請為寵物命名'
} else {
systemStatus.value = '❌ 初始化失敗: ' + error.message
console.error(error)
}
}
return { petSystem, eventSystem, templeSystem }
}
// 按鈕處理函數
async function handleFeed() {
await feed(20)
updatePetState()
}
async function handlePlay(gameType = 'normal') {
const result = await play({ amount: 15, gameType })
if (result && result.success) {
console.log(`${result.gameType} 體重變化: ${result.weightChange > 0 ? '+' : ''}${result.weightChange}g`)
}
updatePetState()
}
async function handleClean() {
await clean()
updatePetState()
}
async function handleHeal() {
await heal(20)
updatePetState()
}
async function handleSleep() {
await sleep()
updatePetState()
}
function handleShowStatus() {
showStatus()
}
function handleCheckEvolution() {
if (window.checkEvolution) {
window.checkEvolution()
}
}
function handleTestAllEvents() {
if (window.testAllEvents) {
window.testAllEvents()
}
}
function handleStart() {
start()
}
function handleStop() {
stop()
}
async function handleListEvents() {
await listEvents()
}
function handleEventHistory() {
eventHistory()
}
async function handleTriggerEvent(eventId) {
await triggerEvent(eventId)
updatePetState()
}
async function handlePray() {
await pray()
updatePetState()
}
async function handleDrawFortune() {
await drawFortune()
}
function handleListDeities() {
listDeities()
}
async function handleSwitchDeity(deityId) {
await switchDeity(deityId)
updatePetState()
}
// 刪除寵物
async function handleDeletePet() {
if (!confirm('確定要刪除當前寵物嗎?此操作無法復原!')) {
return
}
try {
// 停止遊戲循環
if (isRunning.value) {
stop()
}
// 刪除寵物
const result = await petSystem.deletePet()
if (result.success) {
console.log('✅ ' + result.message)
// 重置所有狀態
petState.value = null
isReady.value = false
isRunning.value = false
systemStatus.value = '寵物已刪除,請重新建立'
// 清除事件系統歷史
if (eventSystem) {
eventSystem.eventHistory = []
eventSystem.getBuffManager().buffs = []
}
// 顯示名字輸入對話框
inputPetName.value = ''
showNameInput.value = true
} else {
console.log('❌ 刪除失敗')
}
} catch (error) {
console.error('刪除寵物時發生錯誤:', error)
alert('刪除寵物時發生錯誤,請重試')
}
}
// 幫助
function help() {
console.log('\n' + '='.repeat(50))
console.log('📖 可用命令列表')
console.log('='.repeat(50))
console.log('\n🎮 遊戲控制:')
console.log(' start() - 啟動遊戲循環')
console.log(' stop() - 停止遊戲循環')
console.log(' showStatus() - 顯示當前狀態')
console.log('\n🐾 寵物互動:')
console.log(' feed(amount) - 餵食(預設 +20')
console.log(' play(amount) - 玩耍(預設 +15')
console.log(' clean() - 清理便便')
console.log(' heal(amount) - 治療(預設 +20')
console.log(' sleep() - 睡覺/起床')
console.log('\n🎲 事件系統:')
console.log(' triggerEvent(id) - 手動觸發事件')
console.log(' listEvents() - 查看所有事件')
console.log(' eventHistory() - 查看事件歷史')
console.log(' applyBuffs() - 手動應用 Buff')
console.log('\n🙏 神明系統:')
console.log(' pray() - 祈福(每日 3 次)')
console.log(' drawFortune() - 抽籤')
console.log(' switchDeity(id) - 切換神明')
console.log(' listDeities() - 查看神明列表')
console.log('\n💡 提示:')
console.log(' - 所有數值操作都會同步到 APImock 模式使用 localStorage')
console.log(' - 事件每 10 秒自動檢查10% 機率觸發)')
console.log(' - 遊戲循環每 3 秒執行一次 tick')
console.log(' - 輸入 help() 再次查看此列表')
console.log('='.repeat(50) + '\n')
}
// 測試加成系統
function testBonuses() {
if (!petSystem || !petState.value) {
console.log('❌ 系統尚未初始化')
return
}
console.log('\n' + '='.repeat(60))
console.log('🔍 加成系統測試報告')
console.log('='.repeat(60))
const state = petSystem.getState()
// 1. 命格信息
console.log('\n📜 命格信息:')
if (state.destiny) {
console.log(` 名稱: ${state.destiny.name}`)
console.log(` 描述: ${state.destiny.description}`)
console.log(` 加成:`, state.destiny.buffs)
} else {
console.log(' ❌ 未分配命格')
}
// 1.5 神明信息
console.log('\n🙏 當前神明:')
if (templeSystem) {
const deity = templeSystem.getCurrentDeity()
if (deity) {
console.log(` 名稱: ${deity.name}`)
console.log(` 加成:`, deity.buffs)
}
}
// 2. 所有加成(合計)
const bonuses = petSystem.getAllBonuses()
console.log('\n💫 總加成(命格+神明):')
if (Object.keys(bonuses).length === 0) {
console.log(' 無')
} else {
for (const [key, value] of Object.entries(bonuses)) {
const sign = value > 0 ? '+' : ''
const percent = (value * 100).toFixed(0)
console.log(` ${key}: ${sign}${percent}%`)
}
}
// 3. 當前屬性
console.log('\n📊 當前屬性:')
console.log(` STR: ${state.str.toFixed(1)} | INT: ${state.int.toFixed(1)} | DEX: ${state.dex.toFixed(1)}`)
console.log(` 攻擊: ${state.attack?.toFixed(1)} | 防禦: ${state.defense?.toFixed(1)} | 速度: ${state.speed?.toFixed(1)}`)
// 4. 計算示例
console.log('\n🧪 計算示例:')
const strGainBase = 0.5
const strGainWithBonus = strGainBase * (1 + (bonuses.strGain || 0))
console.log(` 餵食時 STR 增長: ${strGainBase} → ${strGainWithBonus.toFixed(2)} (${bonuses.strGain ? '+' + (bonuses.strGain * 100).toFixed(0) + '%' : '無加成'})`)
const intGainBase = 0.2
const intGainWithBonus = intGainBase * (1 + (bonuses.intGain || 0))
console.log(` 玩耍時 INT 增長: ${intGainBase} → ${intGainWithBonus.toFixed(2)} (${bonuses.intGain ? '+' + (bonuses.intGain * 100).toFixed(0) + '%' : '無加成'})`)
const happinessGainBase = 15
const happinessGainWithBonus = happinessGainBase * (1 + (bonuses.happinessRecovery || 0))
console.log(` 玩耍時快樂恢復: ${happinessGainBase} → ${happinessGainWithBonus.toFixed(1)} (${bonuses.happinessRecovery ? '+' + (bonuses.happinessRecovery * 100).toFixed(0) + '%' : '無加成'})`)
console.log('\n' + '='.repeat(60))
console.log('💡 提示: 執行 feed() 或 play() 來驗證實際效果')
}
// 掛載到 window
// Debug UI 狀態
const showDebug = ref(false)
// Debug 方法
const debugResetPrayer = async () => {
await window.debug.resetPrayer()
}
const debugAddFavor = async (amount) => {
const state = petSystem.getState()
const currentFavor = state.deityFavors?.[state.currentDeityId] || 0
await window.debug.setFavor(state.currentDeityId, Math.min(100, currentFavor + amount))
}
const debugTriggerEvent = async () => {
await window.debug.triggerEvent()
}
const debugFillStats = async () => {
await window.debug.setStat('hunger', 100)
await window.debug.setStat('happiness', 100)
}
const debugMaxAttributes = async () => {
await window.debug.setStat('str', 100)
await window.debug.setStat('int', 100)
await window.debug.setStat('dex', 100)
}
const debugKill = async () => {
await window.debug.setStat('health', 0)
}
const debugAddPoop = async () => {
await window.debug.setStat('poopCount', 4)
}
// 處理鍵盤輸入(可選)
const handleKeydown = (e) => {
// Ctrl + Shift + D 開關 Debug 面板
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
showDebug.value = !showDebug.value
}
}
onMounted(async () => {
window.addEventListener('keydown', handleKeydown)
// 檢查是否已有寵物
try {
const existingState = localStorage.getItem('petState')
if (existingState) {
// 已有寵物,直接初始化
systemStatus.value = '正在載入寵物...'
await init()
} else {
// 新寵物,顯示名字輸入
systemStatus.value = '歡迎!請為你的寵物命名'
showNameInput.value = true
}
// 將所有函數掛載到 window
window.petSystem = petSystem
window.eventSystem = eventSystem
window.templeSystem = templeSystem
window.showStatus = showStatus
window.start = start
window.stop = stop
window.feed = feed
window.play = play
window.clean = clean
window.heal = heal
window.sleep = sleep
window.triggerEvent = triggerEvent
window.listEvents = listEvents
window.eventHistory = eventHistory
window.pray = pray
window.drawFortune = drawFortune
window.switchDeity = switchDeity
window.listDeities = listDeities
window.applyBuffs = applyBuffs
window.help = help
window.init = init
window.testBonuses = testBonuses
// 事件測試函數
window.testEvent = async (eventId) => {
const events = await apiService.getEvents()
const event = events.find(e => e.id === eventId)
if (!event) {
console.log(`❌ 找不到事件: ${eventId}`)
return
}
console.log(`\n🧪 測試: ${event.id} (${event.type})`)
showStatus()
await triggerEvent(eventId)
await new Promise(r => setTimeout(r, 100))
showStatus()
}
window.testAllEvents = async () => {
const events = await apiService.getEvents()
console.log(`\n🧪 測試 ${events.length} 個事件...\n`)
for (const e of events) {
console.log(`▶️ ${e.id}`)
await triggerEvent(e.id)
await new Promise(r => setTimeout(r, 50))
}
console.log('\n✅ 完成')
showStatus()
}
// 檢查進化狀態
window.checkEvolution = () => {
const state = petSystem.getState()
const species = petSystem.speciesConfig
console.log('\n' + '='.repeat(60))
console.log('🔍 進化系統檢查')
console.log('='.repeat(60))
console.log(`\n當前階段: ${state.stage}`)
console.log(`年齡: ${state.ageSeconds.toFixed(1)} 秒`)
console.log(`屬性: STR ${state.str.toFixed(1)} | INT ${state.int.toFixed(1)} | DEX ${state.dex.toFixed(1)}`)
// 找到當前階段
const currentIdx = species.lifecycle.findIndex(s => s.stage === state.stage)
if (currentIdx < 0) {
console.log('\n❌ 找不到當前階段配置')
return
}
const currentStage = species.lifecycle[currentIdx]
if (currentIdx < species.lifecycle.length - 1) {
const nextStage = species.lifecycle[currentIdx + 1]
console.log(`\n下一階段: ${nextStage.stage}`)
// 檢查當前階段是否已達到結束時間
const timeRequirement = currentStage.durationSeconds
const timeMet = state.ageSeconds >= timeRequirement
if (timeRequirement === Infinity) {
console.log(`年齡條件: 已達到最終階段,無需再進化`)
} else {
console.log(`年齡條件: ${state.ageSeconds.toFixed(1)}/${timeRequirement} 秒 ${timeMet ? '✅' : '❌'}`)
}
// 檢查屬性條件
if (nextStage.conditions && Object.keys(nextStage.conditions).length > 0) {
console.log('屬性條件:')
if (nextStage.conditions.str) {
const met = state.str >= nextStage.conditions.str
console.log(` STR: ${state.str.toFixed(1)}/${nextStage.conditions.str} ${met ? '✅' : '❌'}`)
}
if (nextStage.conditions.int) {
const met = state.int >= nextStage.conditions.int
console.log(` INT: ${state.int.toFixed(1)}/${nextStage.conditions.int} ${met ? '✅' : '❌'}`)
}
if (nextStage.conditions.dex) {
const met = state.dex >= nextStage.conditions.dex
console.log(` DEX: ${state.dex.toFixed(1)}/${nextStage.conditions.dex} ${met ? '✅' : '❌'}`)
}
} else {
console.log('屬性條件: 無')
}
// 顯示進化建議
if (timeMet && nextStage.conditions) {
const allStatsMet = Object.entries(nextStage.conditions).every(([key, value]) => state[key] >= value)
if (allStatsMet) {
console.log('\n✨ 條件已滿足!應該會自動進化')
} else {
console.log('\n💡 提示: 年齡已足夠,再提升屬性即可進化')
}
} else if (!timeMet) {
const remaining = timeRequirement - state.ageSeconds
console.log(`\n⏳ 還需等待 ${remaining.toFixed(1)} 秒`)
}
} else {
console.log('\n🎉 已達到最終階段')
}
console.log('='.repeat(60) + '\n')
}
} catch (error) {
systemStatus.value = '❌ 初始化失敗: ' + error.message
console.error(error)
}
})
onUnmounted(() => {
if (isRunning.value) {
stop()
}
})
</script>
<style scoped>
.pet-system-container {
min-height: 100vh;
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
font-family: 'Courier New', monospace;
}
.header {
text-align: center;
margin-bottom: 30px;
border-bottom: 2px solid #4ec9b0;
padding-bottom: 20px;
}
.header h1 {
color: #4ec9b0;
font-size: 2.5em;
margin-bottom: 10px;
}
.subtitle {
color: #858585;
font-size: 1.2em;
}
/* 寵物狀態顯示 */
.pet-status {
background: #252526;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #4ec9b0;
}
.status-section {
margin-bottom: 25px;
}
.status-section h3 {
color: #4ec9b0;
margin-bottom: 15px;
font-size: 1.1em;
text-align: center;
padding-bottom: 10px;
border-bottom: 1px solid #3c3c3c;
}
.status-grid {
display: grid;
gap: 15px;
margin-bottom: 15px;
}
.stat-item {
display: flex;
align-items: center;
gap: 10px;
}
.stat-label {
min-width: 60px;
color: #4ec9b0;
font-weight: bold;
}
.stat-bar {
flex: 1;
height: 24px;
background: #1e1e1e;
border-radius: 12px;
position: relative;
overflow: hidden;
}
.stat-fill {
height: 100%;
background: linear-gradient(90deg, #4ec9b0, #007acc);
transition: width 0.3s ease;
border-radius: 12px;
}
.stat-value {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
color: #fff;
font-size: 0.8em;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
.info-value-group {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.info-sub {
font-size: 0.8em;
color: #858585;
}
.highlight {
color: #ce9178;
font-weight: bold;
}
/* 加成網格 */
.bonus-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 10px;
}
.bonus-item {
background: #1e1e1e;
padding: 8px 12px;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #3c3c3c;
}
.bonus-label {
color: #9cdcfe;
font-size: 0.9em;
}
.bonus-value {
font-weight: bold;
}
.bonus-value.positive {
color: #4ec9b0;
}
.bonus-value.negative {
color: #f44747;
}
.bonus-item.not-implemented {
opacity: 0.6;
}
.bonus-item.not-implemented .bonus-label {
color: #f44747;
text-decoration: line-through;
}
.bonus-item.not-implemented .bonus-value {
color: #888 !important;
}
.pet-info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: #1e1e1e;
border-radius: 6px;
border-left: 3px solid #4ec9b0;
}
.info-label {
color: #858585;
font-size: 0.9em;
}
.info-value {
color: #4ec9b0;
font-weight: bold;
font-size: 1em;
}
.status-badges {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.badge {
background: #3c3c3c;
padding: 5px 10px;
border-radius: 12px;
font-size: 0.85em;
}
.badge.sick {
background: #5c1e1e;
color: #f48771;
}
/* 操作按鈕區 */
.action-panel {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.action-group {
background: #252526;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #4ec9b0;
}
.action-group h3 {
color: #4ec9b0;
margin-bottom: 15px;
font-size: 1.1em;
}
.button-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 10px;
}
button {
background: #007acc;
color: #fff;
border: none;
padding: 12px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9em;
transition: all 0.2s;
font-weight: 500;
}
button:hover:not(:disabled) {
background: #0098ff;
transform: translateY(-2px);
}
button:active:not(:disabled) {
transform: translateY(0);
}
button:disabled {
background: #3c3c3c;
color: #858585;
cursor: not-allowed;
opacity: 0.6;
}
.danger-button {
background: #d32f2f !important;
}
.danger-button:hover:not(:disabled) {
background: #f44336 !important;
}
.console-output {
background: #1e1e1e;
border: 2px solid #3c3c3c;
border-radius: 8px;
padding: 15px;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.log-entry {
margin: 5px 0;
padding: 5px;
border-left: 2px solid transparent;
padding-left: 10px;
}
.log-entry:first-child {
color: #4ec9b0;
}
/* 名字輸入對話框 */
.name-input-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #252526;
padding: 30px;
border-radius: 12px;
border: 2px solid #4ec9b0;
min-width: 400px;
max-width: 90%;
}
.modal-content h2 {
color: #4ec9b0;
margin-bottom: 10px;
text-align: center;
}
.modal-content p {
color: #d4d4d4;
margin-bottom: 20px;
text-align: center;
}
.name-input {
width: 100%;
padding: 12px;
background: #1e1e1e;
border: 2px solid #3c3c3c;
border-radius: 6px;
color: #d4d4d4;
font-size: 1.1em;
margin-bottom: 20px;
text-align: center;
}
.name-input:focus {
outline: none;
border-color: #4ec9b0;
}
.modal-buttons {
display: flex;
justify-content: center;
gap: 10px;
}
.modal-buttons button {
background: #007acc;
color: #fff;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 1em;
font-weight: 500;
}
.modal-buttons button:hover:not(:disabled) {
background: #0098ff;
}
.modal-buttons button:disabled {
background: #3c3c3c;
color: #858585;
cursor: not-allowed;
opacity: 0.6;
}
@media (max-width: 768px) {
.action-panel {
grid-template-columns: 1fr;
}
.modal-content {
min-width: 90%;
padding: 20px;
}
}
/* 折疊面板樣式 */
details.event-list, details.deity-list {
margin-top: 10px;
border: 1px solid #3c3c3c;
border-radius: 4px;
padding: 10px;
}
details summary {
cursor: pointer;
user-select: none;
color: #4ec9b0;
font-weight: bold;
padding: 5px;
list-style-position: inside;
}
details summary:hover {
background: rgba(78, 201, 176, 0.1);
border-radius: 4px;
}
details[open] summary {
margin-bottom: 10px;
border-bottom: 1px solid #3c3c3c;
}
/* 小按鈕網格 */
.button-grid.small {
gap: 8px;
}
.button-grid.small button {
padding: 8px 12px;
font-size: 0.85em;
}
/* 激活狀態的按鈕 */
button.active {
background: #4ec9b0 !important;
color: #1e1e1e !important;
font-weight: bold;
border: 2px solid #6ee2c4;
}
button.active:hover {
background: #6ee2c4 !important;
}
</style>