1028 lines
26 KiB
Vue
1028 lines
26 KiB
Vue
<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 || 0}%` }"></div>
|
||
<span class="stat-value">{{ Math.round(petState.health || 0) }}</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.weight || 0) }} g</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">命格</span>
|
||
<span class="info-value">{{ petState.destiny ? petState.destiny.name : '無' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">力量 (STR)</span>
|
||
<span class="info-value">{{ Math.round(petState.str || 0) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">智力 (INT)</span>
|
||
<span class="info-value">{{ Math.round(petState.int || 0) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">敏捷 (DEX)</span>
|
||
<span class="info-value">{{ Math.round(petState.dex || 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-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="!isReady">🍽️ 餵食</button>
|
||
<button @click="handlePlay" :disabled="!isReady">🎮 玩耍</button>
|
||
<button @click="handleClean" :disabled="!isReady">🧹 清理</button>
|
||
<button @click="handleHeal" :disabled="!isReady">💊 治療</button>
|
||
<button @click="handleSleep" :disabled="!isReady">
|
||
{{ petState?.isSleeping ? '⏰ 起床' : '😴 睡覺' }}
|
||
</button>
|
||
<button @click="handleShowStatus" :disabled="!isReady">📊 查看狀態</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="handleTriggerEvent('lucky_find')" :disabled="!isReady">✨ 觸發好事件</button>
|
||
</div>
|
||
</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>
|
||
<button @click="handleSwitchDeity('mazu')" :disabled="!isReady">媽祖</button>
|
||
<button @click="handleSwitchDeity('earthgod')" :disabled="!isReady">土地公</button>
|
||
<button @click="handleSwitchDeity('guanyin')" :disabled="!isReady">觀音</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Console 輸出 -->
|
||
<div class="console-output" id="console-output">
|
||
<div class="log-entry">✅ 系統載入中...</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<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 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 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)
|
||
|
||
// 安全獲取數值,避免 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:${buffs.length > 0 ? buffs.map(b => b.name).join(',') : '無'}`)
|
||
}
|
||
|
||
// 啟動遊戲循環
|
||
function start() {
|
||
if (isRunning.value) {
|
||
console.log('⚠️ 遊戲循環已在運行中')
|
||
return
|
||
}
|
||
|
||
isRunning.value = true
|
||
// 移除 tick 回調中的自動輸出,只在有事件時才輸出
|
||
petSystem.startTickLoop(() => {
|
||
// 更新狀態顯示
|
||
updatePetState()
|
||
})
|
||
|
||
eventSystem.startEventCheck()
|
||
|
||
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} 飢餓`)
|
||
showStatus()
|
||
} else {
|
||
console.log(`❌ ${result.message}`)
|
||
}
|
||
}
|
||
|
||
// 玩耍
|
||
async function play(amount = 15) {
|
||
const result = await petSystem.play(amount)
|
||
if (result.success) {
|
||
console.log(`✅ 玩耍 +${amount} 快樂`)
|
||
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) {
|
||
console.log(`✅ 治療 +${amount} 健康${result.cured ? ' (已治癒)' : ''}`)
|
||
showStatus()
|
||
} else {
|
||
console.log(`❌ ${result.message}`)
|
||
}
|
||
}
|
||
|
||
// 睡覺/起床
|
||
async function sleep() {
|
||
const result = await petSystem.toggleSleep()
|
||
if (result.success) {
|
||
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📋 可用事件列表:')
|
||
console.log('='.repeat(50))
|
||
events.forEach(e => {
|
||
console.log(`\n${e.id} (${e.type})`)
|
||
console.log(` 權重: ${e.weight}`)
|
||
console.log(` 效果數: ${e.effects.length}`)
|
||
})
|
||
console.log('='.repeat(50) + '\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) {
|
||
console.log(`✅ 祈福 +${result.favorIncrease} 好感 → ${result.newFavor} | ${result.dialogue}`)
|
||
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('✅ 系統已初始化 | 輸入 help() 查看命令')
|
||
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() {
|
||
await play(15)
|
||
updatePetState()
|
||
}
|
||
|
||
async function handleClean() {
|
||
await clean()
|
||
updatePetState()
|
||
}
|
||
|
||
async function handleHeal() {
|
||
await heal(20)
|
||
updatePetState()
|
||
}
|
||
|
||
async function handleSleep() {
|
||
await sleep()
|
||
updatePetState()
|
||
}
|
||
|
||
function handleShowStatus() {
|
||
showStatus()
|
||
}
|
||
|
||
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(' - 所有數值操作都會同步到 API(mock 模式使用 localStorage)')
|
||
console.log(' - 事件每 10 秒自動檢查(10% 機率觸發)')
|
||
console.log(' - 遊戲循環每 3 秒執行一次 tick')
|
||
console.log(' - 輸入 help() 再次查看此列表')
|
||
console.log('='.repeat(50) + '\n')
|
||
}
|
||
|
||
// 掛載到 window
|
||
onMounted(async () => {
|
||
// 檢查是否已有寵物
|
||
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
|
||
} 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.85em;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
</style>
|