good version
This commit is contained in:
commit
ebe90953fe
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Nuxt dev/build outputs
|
||||||
|
.output
|
||||||
|
.data
|
||||||
|
.nuxt
|
||||||
|
.nitro
|
||||||
|
.cache
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Node dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.fleet
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
# 虛擬寵物系統 - Nuxt 版本
|
||||||
|
|
||||||
|
基於 PRD v2.1 的資料驅動虛擬寵物系統,支援事件觸發、Buff 管理、神明系統等核心功能。
|
||||||
|
|
||||||
|
## 🚀 快速開始
|
||||||
|
|
||||||
|
### 安裝依賴
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 啟動開發伺服器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 Console 互動版
|
||||||
|
|
||||||
|
1. 訪問:`http://localhost:3000/console-demo.html`
|
||||||
|
2. 打開瀏覽器開發者工具(F12)
|
||||||
|
3. 在 Console 中輸入命令,例如:
|
||||||
|
- `help()` - 查看所有命令
|
||||||
|
- `showStatus()` - 顯示寵物狀態
|
||||||
|
- `start()` - 啟動遊戲循環
|
||||||
|
- `feed(20)` - 餵食
|
||||||
|
- `play(15)` - 玩耍
|
||||||
|
- `pray()` - 祈福
|
||||||
|
|
||||||
|
詳細使用說明請參考 [docs/USAGE.md](./docs/USAGE.md)
|
||||||
|
|
||||||
|
## 📁 專案結構
|
||||||
|
|
||||||
|
- `data/` - 資料配置(寵物種族、神明、事件、道具、籤詩)
|
||||||
|
- `core/` - 核心系統(API 服務、寵物系統、事件系統、神明系統)
|
||||||
|
- `console-demo.js` - Console 互動介面
|
||||||
|
- `public/console-demo.html` - 瀏覽器版本
|
||||||
|
- `docs/` - 文檔(API 結構、使用說明)
|
||||||
|
|
||||||
|
## 🔌 API 整合
|
||||||
|
|
||||||
|
系統支援 Mock 和真實 API 切換:
|
||||||
|
|
||||||
|
- **Mock 模式**(預設):資料儲存在 localStorage
|
||||||
|
- **真實 API**:修改 `console-demo.js` 中的 `useMock: false`
|
||||||
|
|
||||||
|
API 端點結構詳見 [docs/API_STRUCTURE.md](./docs/API_STRUCTURE.md)
|
||||||
|
|
||||||
|
## 📚 文檔
|
||||||
|
|
||||||
|
- [使用說明](./docs/USAGE.md) - 完整的使用指南和命令列表
|
||||||
|
- [API 結構](./docs/API_STRUCTURE.md) - API 端點設計文檔
|
||||||
|
|
||||||
|
## 🎮 核心功能
|
||||||
|
|
||||||
|
- ✅ 寵物狀態管理(飢餓、快樂、健康等)
|
||||||
|
- ✅ Tick 循環系統(每 3 秒)
|
||||||
|
- ✅ 事件系統(資料驅動,10% 機率觸發)
|
||||||
|
- ✅ Buff 系統(flat + percent 加成)
|
||||||
|
- ✅ 神明系統(5 位神明,祈福、抽籤)
|
||||||
|
- ✅ API 服務層(支援 mock/real 切換)
|
||||||
|
- ✅ Console 互動介面
|
||||||
|
|
||||||
|
## 🛠️ 開發
|
||||||
|
|
||||||
|
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Make sure to install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Server
|
||||||
|
|
||||||
|
Start the development server on `http://localhost:3000`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
Build the application for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Locally preview production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run preview
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm preview
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn preview
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,371 @@
|
||||||
|
// Console 互動版本 - 可點擊操作所有功能
|
||||||
|
// 使用方式:在瀏覽器 console 或 Node.js 環境執行
|
||||||
|
|
||||||
|
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 服務(可切換 mock/real)
|
||||||
|
const apiService = new ApiService({
|
||||||
|
useMock: true, // 設為 false 可切換到真實 API
|
||||||
|
baseUrl: 'http://localhost:3000/api',
|
||||||
|
mockDelay: 100
|
||||||
|
})
|
||||||
|
|
||||||
|
// 全局系統實例
|
||||||
|
let petSystem, eventSystem, templeSystem
|
||||||
|
let isRunning = false
|
||||||
|
|
||||||
|
// 初始化系統
|
||||||
|
async function init() {
|
||||||
|
console.log('=== 虛擬寵物系統初始化 ===\n')
|
||||||
|
|
||||||
|
// 創建系統實例
|
||||||
|
petSystem = new PetSystem(apiService)
|
||||||
|
eventSystem = new EventSystem(petSystem, apiService)
|
||||||
|
templeSystem = new TempleSystem(petSystem, apiService)
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
await petSystem.initialize()
|
||||||
|
await eventSystem.initialize()
|
||||||
|
await templeSystem.initialize()
|
||||||
|
|
||||||
|
console.log('✅ 系統初始化完成!')
|
||||||
|
console.log('📝 輸入 help() 查看所有可用命令\n')
|
||||||
|
|
||||||
|
// 顯示初始狀態
|
||||||
|
showStatus()
|
||||||
|
|
||||||
|
return { petSystem, eventSystem, templeSystem }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 顯示狀態
|
||||||
|
function showStatus() {
|
||||||
|
const state = petSystem.getState()
|
||||||
|
const buffs = eventSystem.getBuffManager().getActiveBuffs()
|
||||||
|
const currentDeity = templeSystem.getCurrentDeity()
|
||||||
|
const favorStars = templeSystem.getFavorStars(state.currentDeityId)
|
||||||
|
|
||||||
|
console.log('\n' + '='.repeat(50))
|
||||||
|
console.log('🐾 寵物狀態')
|
||||||
|
console.log('='.repeat(50))
|
||||||
|
console.log(`種類: ${state.speciesId}`)
|
||||||
|
console.log(`階段: ${state.stage}`)
|
||||||
|
console.log(`年齡: ${Math.floor(state.ageSeconds)} 秒`)
|
||||||
|
console.log(`\n📊 基礎數值:`)
|
||||||
|
console.log(` 飢餓: ${state.hunger.toFixed(1)}/100`)
|
||||||
|
console.log(` 快樂: ${state.happiness.toFixed(1)}/100`)
|
||||||
|
console.log(` 健康: ${state.health.toFixed(1)}/100`)
|
||||||
|
console.log(` 體重: ${state.weight.toFixed(1)}`)
|
||||||
|
console.log(`\n💪 屬性:`)
|
||||||
|
console.log(` 力量: ${state.str.toFixed(1)}`)
|
||||||
|
console.log(` 智力: ${state.int.toFixed(1)}`)
|
||||||
|
console.log(` 敏捷: ${state.dex.toFixed(1)}`)
|
||||||
|
console.log(` 運勢: ${state.luck.toFixed(1)}`)
|
||||||
|
console.log(`\n🎭 狀態:`)
|
||||||
|
console.log(` 睡覺: ${state.isSleeping ? '是' : '否'}`)
|
||||||
|
console.log(` 生病: ${state.isSick ? '是' : '否'}`)
|
||||||
|
console.log(` 死亡: ${state.isDead ? '是' : '否'}`)
|
||||||
|
console.log(` 便便: ${state.poopCount}/4`)
|
||||||
|
console.log(`\n🙏 神明:`)
|
||||||
|
console.log(` 當前: ${currentDeity.name}`)
|
||||||
|
console.log(` 好感: ${favorStars} (${state.deityFavors[state.currentDeityId]}/100)`)
|
||||||
|
console.log(` 今日祈福: ${state.dailyPrayerCount}/3`)
|
||||||
|
console.log(`\n✨ 當前 Buff:`)
|
||||||
|
if (buffs.length === 0) {
|
||||||
|
console.log(' (無)')
|
||||||
|
} else {
|
||||||
|
buffs.forEach(b => {
|
||||||
|
const duration = b.durationTicks === Infinity ? '永久' : `${b.currentTicks} ticks`
|
||||||
|
console.log(` - ${b.name} (${duration})`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log('='.repeat(50) + '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 啟動遊戲循環
|
||||||
|
function start() {
|
||||||
|
if (isRunning) {
|
||||||
|
console.log('⚠️ 遊戲循環已在運行中')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isRunning = true
|
||||||
|
petSystem.startTickLoop((state) => {
|
||||||
|
console.log(`\n⏰ Tick: ${new Date().toLocaleTimeString()}`)
|
||||||
|
showStatus()
|
||||||
|
})
|
||||||
|
|
||||||
|
eventSystem.startEventCheck()
|
||||||
|
|
||||||
|
console.log('✅ 遊戲循環已啟動(每 3 秒 tick,每 10 秒檢查事件)')
|
||||||
|
console.log('💡 輸入 stop() 停止循環\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止遊戲循環
|
||||||
|
function stop() {
|
||||||
|
if (!isRunning) {
|
||||||
|
console.log('⚠️ 遊戲循環未運行')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
petSystem.stopTickLoop()
|
||||||
|
eventSystem.stopEventCheck()
|
||||||
|
isRunning = 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)}`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 玩耍
|
||||||
|
async function play(amount = 15) {
|
||||||
|
const result = await petSystem.play(amount)
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`✅ 玩耍成功!快樂 +${amount},敏捷 +0.5`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理便便
|
||||||
|
async function clean() {
|
||||||
|
const result = await petSystem.cleanPoop()
|
||||||
|
if (result.success) {
|
||||||
|
console.log('✅ 清理成功!快樂 +10')
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 治療
|
||||||
|
async function heal(amount = 20) {
|
||||||
|
const result = await petSystem.heal(amount)
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`✅ 治療成功!健康 +${amount}${result.cured ? ',疾病已治癒' : ''}`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 睡覺/起床
|
||||||
|
async function sleep() {
|
||||||
|
const result = await petSystem.toggleSleep()
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`✅ ${result.isSleeping ? '寵物已入睡' : '寵物已醒來'}`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 觸發事件(測試用)
|
||||||
|
async function triggerEvent(eventId) {
|
||||||
|
const result = await eventSystem.triggerEvent(eventId)
|
||||||
|
if (result) {
|
||||||
|
console.log(`✅ 事件 ${eventId} 觸發成功`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ 事件 ${eventId} 觸發失敗或條件不滿足`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看事件列表
|
||||||
|
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 history() {
|
||||||
|
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(`✅ 祈福成功!`)
|
||||||
|
console.log(` 好感度 +${result.favorIncrease} → ${result.newFavor}`)
|
||||||
|
console.log(` ${result.dialogue}`)
|
||||||
|
} else {
|
||||||
|
console.log(`❌ ${result.message}`)
|
||||||
|
}
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽籤
|
||||||
|
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(每 tick 自動執行,也可手動)
|
||||||
|
async function applyBuffs() {
|
||||||
|
await eventSystem.applyBuffs()
|
||||||
|
eventSystem.getBuffManager().tick()
|
||||||
|
console.log('✅ Buff 已應用並更新')
|
||||||
|
showStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 幫助
|
||||||
|
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(' history() - 查看事件歷史')
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匯出到全局(瀏覽器環境)
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
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.history = history
|
||||||
|
window.pray = pray
|
||||||
|
window.drawFortune = drawFortune
|
||||||
|
window.switchDeity = switchDeity
|
||||||
|
window.listDeities = listDeities
|
||||||
|
window.applyBuffs = applyBuffs
|
||||||
|
window.help = help
|
||||||
|
window.init = init
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node.js 環境自動初始化
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
init().then(() => {
|
||||||
|
console.log('\n💡 提示:在瀏覽器環境中,這些函數會自動掛載到 window 物件')
|
||||||
|
console.log(' 在 Node.js 環境中,請使用 await 呼叫這些函數\n')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
init,
|
||||||
|
showStatus,
|
||||||
|
start,
|
||||||
|
stop,
|
||||||
|
feed,
|
||||||
|
play,
|
||||||
|
clean,
|
||||||
|
heal,
|
||||||
|
sleep,
|
||||||
|
triggerEvent,
|
||||||
|
listEvents,
|
||||||
|
history,
|
||||||
|
pray,
|
||||||
|
drawFortune,
|
||||||
|
switchDeity,
|
||||||
|
listDeities,
|
||||||
|
applyBuffs,
|
||||||
|
help
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
// API 服務層 - 支援 mock 和真實 API 切換
|
||||||
|
export class ApiService {
|
||||||
|
constructor(config = {}) {
|
||||||
|
this.baseUrl = config.baseUrl || 'http://localhost:3000/api'
|
||||||
|
this.useMock = config.useMock !== false // 預設使用 mock
|
||||||
|
this.mockDelay = config.mockDelay || 100 // mock 延遲模擬網路請求
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用請求方法
|
||||||
|
async request(endpoint, options = {}) {
|
||||||
|
if (this.useMock) {
|
||||||
|
return this.mockRequest(endpoint, options)
|
||||||
|
}
|
||||||
|
return this.realRequest(endpoint, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock 請求(使用本地資料)
|
||||||
|
async mockRequest(endpoint, options) {
|
||||||
|
await this.delay(this.mockDelay)
|
||||||
|
|
||||||
|
const [resource, action] = endpoint.split('/').filter(Boolean)
|
||||||
|
|
||||||
|
switch (`${resource}/${action}`) {
|
||||||
|
case 'pet/state':
|
||||||
|
return this.getMockPetState()
|
||||||
|
case 'pet/update':
|
||||||
|
return this.updateMockPetState(options.body)
|
||||||
|
case 'events/list':
|
||||||
|
return this.getMockEvents()
|
||||||
|
case 'events/trigger':
|
||||||
|
return this.triggerMockEvent(options.body)
|
||||||
|
case 'buffs/apply':
|
||||||
|
return this.applyMockBuff(options.body)
|
||||||
|
case 'deities/list':
|
||||||
|
return this.getMockDeities()
|
||||||
|
case 'deities/pray':
|
||||||
|
return this.mockPrayToDeity(options.body)
|
||||||
|
case 'fortune/draw':
|
||||||
|
return this.mockDrawFortune()
|
||||||
|
case 'items/list':
|
||||||
|
return this.getMockItems()
|
||||||
|
case 'items/use':
|
||||||
|
return this.mockUseItem(options.body)
|
||||||
|
case 'pet/save':
|
||||||
|
return this.saveMockPetState(options.body)
|
||||||
|
case 'pet/delete':
|
||||||
|
return this.deleteMockPetState()
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown endpoint: ${endpoint}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 真實 API 請求
|
||||||
|
async realRequest(endpoint, options = {}) {
|
||||||
|
const url = `${this.baseUrl}/${endpoint}`
|
||||||
|
const config = {
|
||||||
|
method: options.method || 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.body) {
|
||||||
|
config.body = JSON.stringify(options.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, config)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API Error: ${response.status} ${response.statusText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延遲模擬
|
||||||
|
delay(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== API 端點方法 ==========
|
||||||
|
|
||||||
|
// 寵物狀態相關
|
||||||
|
async getPetState() {
|
||||||
|
return this.request('pet/state')
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePetState(updates) {
|
||||||
|
return this.request('pet/update', {
|
||||||
|
method: 'POST',
|
||||||
|
body: updates
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async savePetState(state) {
|
||||||
|
return this.request('pet/save', {
|
||||||
|
method: 'POST',
|
||||||
|
body: state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePetState() {
|
||||||
|
return this.request('pet/delete', {
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件相關
|
||||||
|
async getEvents() {
|
||||||
|
return this.request('events/list')
|
||||||
|
}
|
||||||
|
|
||||||
|
async triggerEvent(eventId, petState) {
|
||||||
|
return this.request('events/trigger', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { eventId, petState }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buff 相關
|
||||||
|
async applyBuff(buffData) {
|
||||||
|
return this.request('buffs/apply', {
|
||||||
|
method: 'POST',
|
||||||
|
body: buffData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 神明相關
|
||||||
|
async getDeities() {
|
||||||
|
return this.request('deities/list')
|
||||||
|
}
|
||||||
|
|
||||||
|
async prayToDeity(deityId, petState) {
|
||||||
|
return this.request('deities/pray', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { deityId, petState }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 籤詩相關
|
||||||
|
async drawFortune() {
|
||||||
|
return this.request('fortune/draw', {
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 道具相關
|
||||||
|
async getItems() {
|
||||||
|
return this.request('items/list')
|
||||||
|
}
|
||||||
|
|
||||||
|
async useItem(itemId, count = 1) {
|
||||||
|
return this.request('items/use', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { itemId, count }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Mock 資料方法 ==========
|
||||||
|
|
||||||
|
getMockPetState() {
|
||||||
|
// 從 localStorage 或預設值讀取
|
||||||
|
const stored = localStorage.getItem('petState')
|
||||||
|
if (stored) {
|
||||||
|
return JSON.parse(stored)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMockPetState(updates) {
|
||||||
|
const current = this.getMockPetState()
|
||||||
|
const updated = { ...current, ...updates }
|
||||||
|
localStorage.setItem('petState', JSON.stringify(updated))
|
||||||
|
return { success: true, data: updated }
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMockPetState(state) {
|
||||||
|
localStorage.setItem('petState', JSON.stringify(state))
|
||||||
|
return { success: true, message: '狀態已儲存' }
|
||||||
|
}
|
||||||
|
|
||||||
|
getMockEvents() {
|
||||||
|
// 動態載入事件配置
|
||||||
|
return import('../data/events.js').then(m => m.EVENT_CONFIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerMockEvent({ eventId, petState }) {
|
||||||
|
// 模擬事件觸發邏輯
|
||||||
|
return { success: true, eventId, effects: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMockBuff(buffData) {
|
||||||
|
return { success: true, buff: buffData }
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMockDeities() {
|
||||||
|
const { DEITIES } = await import('../data/deities.js')
|
||||||
|
return DEITIES
|
||||||
|
}
|
||||||
|
|
||||||
|
mockPrayToDeity({ deityId, petState }) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
favorIncrease: 5,
|
||||||
|
message: '祈福成功'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDrawFortune() {
|
||||||
|
// 模擬抽籤邏輯
|
||||||
|
const grades = ['上上', '上', '中', '下', '下下']
|
||||||
|
const weights = [0.05, 0.15, 0.40, 0.30, 0.10]
|
||||||
|
const random = Math.random()
|
||||||
|
let sum = 0
|
||||||
|
let selectedGrade = '中'
|
||||||
|
|
||||||
|
for (let i = 0; i < weights.length; i++) {
|
||||||
|
sum += weights[i]
|
||||||
|
if (random <= sum) {
|
||||||
|
selectedGrade = grades[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
lot: {
|
||||||
|
id: Math.floor(Math.random() * 100) + 1,
|
||||||
|
grade: selectedGrade,
|
||||||
|
poem1: '示例籤詩',
|
||||||
|
meaning: '示例解釋'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMockItems() {
|
||||||
|
const { ITEMS } = await import('../data/items.js')
|
||||||
|
return Object.values(ITEMS)
|
||||||
|
}
|
||||||
|
|
||||||
|
mockUseItem({ itemId, count }) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
itemId,
|
||||||
|
count,
|
||||||
|
effects: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteMockPetState() {
|
||||||
|
localStorage.removeItem('petState')
|
||||||
|
return { success: true, message: '寵物已刪除' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 預設實例
|
||||||
|
export const apiService = new ApiService({
|
||||||
|
useMock: true, // 開發時使用 mock
|
||||||
|
mockDelay: 100
|
||||||
|
})
|
||||||
|
|
||||||
|
|
@ -0,0 +1,323 @@
|
||||||
|
// 事件系統核心 - 與 API 整合
|
||||||
|
import { apiService } from './api-service.js'
|
||||||
|
|
||||||
|
export class EventSystem {
|
||||||
|
constructor(petSystem, api = apiService) {
|
||||||
|
this.petSystem = petSystem
|
||||||
|
this.api = api
|
||||||
|
this.events = []
|
||||||
|
this.buffManager = new BuffManager()
|
||||||
|
this.eventHistory = []
|
||||||
|
this.eventCheckInterval = null
|
||||||
|
this.lastEventCheckTime = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(從 API 載入事件配置)
|
||||||
|
async initialize() {
|
||||||
|
try {
|
||||||
|
this.events = await this.api.getEvents()
|
||||||
|
console.log(`[EventSystem] 載入 ${this.events.length} 個事件`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[EventSystem] 載入事件失敗:', error)
|
||||||
|
// 降級到本地載入
|
||||||
|
const { EVENT_CONFIG } = await import('../data/events.js')
|
||||||
|
this.events = EVENT_CONFIG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 啟動事件檢查循環(每 10 秒檢查一次)
|
||||||
|
startEventCheck() {
|
||||||
|
if (this.eventCheckInterval) this.stopEventCheck()
|
||||||
|
|
||||||
|
this.eventCheckInterval = setInterval(() => {
|
||||||
|
this.checkTriggers()
|
||||||
|
}, 10000) // 每 10 秒
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEventCheck() {
|
||||||
|
if (this.eventCheckInterval) {
|
||||||
|
clearInterval(this.eventCheckInterval)
|
||||||
|
this.eventCheckInterval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隨機選擇事件(依權重)
|
||||||
|
selectRandomEvent() {
|
||||||
|
const totalWeight = this.events.reduce((sum, e) => sum + e.weight, 0)
|
||||||
|
let rand = Math.random() * totalWeight
|
||||||
|
|
||||||
|
for (const event of this.events) {
|
||||||
|
rand -= event.weight
|
||||||
|
if (rand <= 0) return event
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.events[0] // fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查觸發條件(10% 機率 + 條件檢查)
|
||||||
|
async checkTriggers() {
|
||||||
|
const petState = this.petSystem.getState()
|
||||||
|
|
||||||
|
if (petState.isDead) return
|
||||||
|
|
||||||
|
// 10% 機率觸發
|
||||||
|
if (Math.random() >= 0.1) return
|
||||||
|
|
||||||
|
const event = this.selectRandomEvent()
|
||||||
|
|
||||||
|
// 檢查條件
|
||||||
|
if (event.condition && !event.condition(petState)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 觸發事件
|
||||||
|
await this.triggerEvent(event.id, petState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 觸發事件(同步到 API)
|
||||||
|
async triggerEvent(eventId, petState = null) {
|
||||||
|
const event = this.events.find(e => e.id === eventId)
|
||||||
|
if (!event) {
|
||||||
|
console.warn(`[EventSystem] 找不到事件: ${eventId}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = petState || this.petSystem.getState()
|
||||||
|
|
||||||
|
// 檢查條件
|
||||||
|
if (event.condition && !event.condition(currentState)) {
|
||||||
|
console.log(`[EventSystem] 事件 ${eventId} 條件不滿足`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n[事件觸發] ${event.id} (${event.type}): 機率檢查通過!`)
|
||||||
|
|
||||||
|
// 執行效果
|
||||||
|
const results = []
|
||||||
|
for (const effect of event.effects) {
|
||||||
|
const result = await this.executeEffect(effect, currentState)
|
||||||
|
results.push(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 記錄歷史
|
||||||
|
this.eventHistory.push({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
eventId: event.id,
|
||||||
|
eventType: event.type,
|
||||||
|
effects: results
|
||||||
|
})
|
||||||
|
|
||||||
|
// 同步到 API
|
||||||
|
try {
|
||||||
|
await this.api.triggerEvent(eventId, currentState)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[EventSystem] API 同步失敗:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { event, results }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 執行效果
|
||||||
|
async executeEffect(effect, petState) {
|
||||||
|
switch (effect.type) {
|
||||||
|
case 'modifyStats':
|
||||||
|
return await this.modifyStats(effect.payload, petState)
|
||||||
|
|
||||||
|
case 'addBuff':
|
||||||
|
return await this.addBuff(effect.payload)
|
||||||
|
|
||||||
|
case 'spawnPoop':
|
||||||
|
return await this.spawnPoop(effect.payload)
|
||||||
|
|
||||||
|
case 'templeFavor':
|
||||||
|
return await this.modifyTempleFavor(effect.payload)
|
||||||
|
|
||||||
|
case 'logMessage':
|
||||||
|
console.log(`[訊息] ${effect.payload}`)
|
||||||
|
return { type: 'logMessage', message: effect.payload }
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`[EventSystem] 未知效果類型: ${effect.type}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改屬性
|
||||||
|
async modifyStats(payload, petState) {
|
||||||
|
const updates = {}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(payload)) {
|
||||||
|
if (key === 'isSleeping' || key === 'isSick' || key === 'isDead') {
|
||||||
|
updates[key] = value
|
||||||
|
} else {
|
||||||
|
const current = petState[key] || 0
|
||||||
|
const newValue = Math.max(0, Math.min(100, current + value))
|
||||||
|
updates[key] = newValue
|
||||||
|
console.log(`[效果] ${key} ${value > 0 ? '+' : ''}${value} → 新值: ${newValue}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.petSystem.updateState(updates)
|
||||||
|
return { type: 'modifyStats', updates }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 Buff
|
||||||
|
async addBuff(buffData) {
|
||||||
|
this.buffManager.addBuff(buffData)
|
||||||
|
|
||||||
|
// 同步到 API
|
||||||
|
try {
|
||||||
|
await this.api.applyBuff(buffData)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[EventSystem] Buff API 同步失敗:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type: 'addBuff', buff: buffData }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成便便
|
||||||
|
async spawnPoop(payload) {
|
||||||
|
const count = payload?.count || 1
|
||||||
|
const currentPoop = this.petSystem.getState().poopCount
|
||||||
|
const newPoop = Math.min(4, currentPoop + count)
|
||||||
|
|
||||||
|
await this.petSystem.updateState({ poopCount: newPoop })
|
||||||
|
console.log(`[效果] 生成便便: ${count} 個,總數: ${newPoop}`)
|
||||||
|
|
||||||
|
return { type: 'spawnPoop', count, total: newPoop }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改神明好感度
|
||||||
|
async modifyTempleFavor(payload) {
|
||||||
|
const { deityId, amount } = payload
|
||||||
|
const currentState = this.petSystem.getState()
|
||||||
|
const currentFavor = currentState.deityFavors[deityId] || 0
|
||||||
|
const newFavor = Math.max(0, Math.min(100, currentFavor + amount))
|
||||||
|
|
||||||
|
await this.petSystem.updateState({
|
||||||
|
deityFavors: {
|
||||||
|
...currentState.deityFavors,
|
||||||
|
[deityId]: newFavor
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`[效果] ${deityId} 好感度 ${amount > 0 ? '+' : ''}${amount} → ${newFavor}`)
|
||||||
|
|
||||||
|
return { type: 'templeFavor', deityId, favor: newFavor }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 應用 Buff 到狀態(每 tick 呼叫)
|
||||||
|
async applyBuffs() {
|
||||||
|
const petState = this.petSystem.getState()
|
||||||
|
const buffs = this.buffManager.getActiveBuffs()
|
||||||
|
|
||||||
|
// 計算最終屬性(考慮 Buff)
|
||||||
|
const finalStats = this.calculateFinalStats(petState, buffs)
|
||||||
|
|
||||||
|
// 更新狀態
|
||||||
|
await this.petSystem.updateState(finalStats)
|
||||||
|
|
||||||
|
return finalStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// 計算最終屬性(Base + Flat + Percent)
|
||||||
|
calculateFinalStats(baseState, buffs) {
|
||||||
|
const stats = { ...baseState }
|
||||||
|
|
||||||
|
// 收集所有 Buff 的 flat 和 percent 加成
|
||||||
|
const flatMods = {}
|
||||||
|
const percentMods = {}
|
||||||
|
|
||||||
|
buffs.forEach(buff => {
|
||||||
|
if (buff.flat) {
|
||||||
|
Object.entries(buff.flat).forEach(([key, value]) => {
|
||||||
|
flatMods[key] = (flatMods[key] || 0) + value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (buff.percent) {
|
||||||
|
Object.entries(buff.percent).forEach(([key, value]) => {
|
||||||
|
percentMods[key] = (percentMods[key] || 0) + value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 應用加成
|
||||||
|
Object.keys(stats).forEach(key => {
|
||||||
|
if (typeof stats[key] === 'number' && key !== 'ageSeconds' && key !== 'weight' && key !== 'generation') {
|
||||||
|
const base = stats[key]
|
||||||
|
const flat = flatMods[key] || 0
|
||||||
|
const percent = percentMods[key] || 0
|
||||||
|
stats[key] = Math.max(0, Math.min(100, (base + flat) * (1 + percent)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取事件歷史
|
||||||
|
getHistory() {
|
||||||
|
return [...this.eventHistory]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取 Buff 管理器
|
||||||
|
getBuffManager() {
|
||||||
|
return this.buffManager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buff 管理器
|
||||||
|
class BuffManager {
|
||||||
|
constructor() {
|
||||||
|
this.buffs = []
|
||||||
|
}
|
||||||
|
|
||||||
|
addBuff(buff) {
|
||||||
|
// 檢查是否可堆疊
|
||||||
|
const existing = this.buffs.find(b => b.id === buff.id)
|
||||||
|
|
||||||
|
if (existing && !buff.stacks) {
|
||||||
|
// 不可堆疊,重置持續時間
|
||||||
|
existing.currentTicks = buff.durationTicks
|
||||||
|
console.log(`[Buff] 重置: ${buff.name} (持續 ${buff.durationTicks} ticks)`)
|
||||||
|
} else {
|
||||||
|
// 可堆疊或新 Buff
|
||||||
|
this.buffs.push({
|
||||||
|
...buff,
|
||||||
|
currentTicks: buff.durationTicks,
|
||||||
|
currentStacks: (existing?.currentStacks || 0) + 1
|
||||||
|
})
|
||||||
|
console.log(`[Buff] 新增: ${buff.name} (持續 ${buff.durationTicks} ticks)`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
this.buffs = this.buffs.filter(b => {
|
||||||
|
if (b.durationTicks === Infinity) return true // 永久 Buff
|
||||||
|
|
||||||
|
b.currentTicks--
|
||||||
|
if (b.currentTicks <= 0) {
|
||||||
|
console.log(`[Buff] 過期: ${b.name}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getActiveBuffs() {
|
||||||
|
return this.buffs.filter(b => b.currentTicks > 0 || b.durationTicks === Infinity)
|
||||||
|
}
|
||||||
|
|
||||||
|
getFinalModifier(statKey) {
|
||||||
|
const activeBuffs = this.getActiveBuffs()
|
||||||
|
let flatTotal = 0
|
||||||
|
let percentTotal = 0
|
||||||
|
|
||||||
|
activeBuffs.forEach(b => {
|
||||||
|
if (b.flat?.[statKey]) flatTotal += b.flat[statKey]
|
||||||
|
if (b.percent?.[statKey]) percentTotal += b.percent[statKey]
|
||||||
|
})
|
||||||
|
|
||||||
|
return { flat: flatTotal, percent: percentTotal }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
// 寵物系統核心 - 與 API 整合
|
||||||
|
import { apiService } from './api-service.js'
|
||||||
|
import { PET_SPECIES } from '../data/pet-species.js'
|
||||||
|
|
||||||
|
export class PetSystem {
|
||||||
|
constructor(api = apiService) {
|
||||||
|
this.api = api
|
||||||
|
this.state = null
|
||||||
|
this.speciesConfig = null
|
||||||
|
this.tickInterval = null
|
||||||
|
this.eventCheckInterval = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化寵物(從 API 載入或創建新寵物)
|
||||||
|
async initialize(speciesId = 'tinyTigerCat') {
|
||||||
|
try {
|
||||||
|
// 從 API 載入現有狀態
|
||||||
|
this.state = await this.api.getPetState()
|
||||||
|
|
||||||
|
if (!this.state) {
|
||||||
|
// 創建新寵物
|
||||||
|
this.state = this.createInitialState(speciesId)
|
||||||
|
await this.api.savePetState(this.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 載入種族配置
|
||||||
|
this.speciesConfig = PET_SPECIES[this.state.speciesId] || PET_SPECIES[speciesId]
|
||||||
|
|
||||||
|
return this.state
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[PetSystem] 初始化失敗:', error)
|
||||||
|
// 降級到本地狀態
|
||||||
|
this.state = this.createInitialState(speciesId)
|
||||||
|
this.speciesConfig = PET_SPECIES[speciesId]
|
||||||
|
return this.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 創建初始狀態
|
||||||
|
createInitialState(speciesId) {
|
||||||
|
const config = PET_SPECIES[speciesId]
|
||||||
|
return {
|
||||||
|
speciesId,
|
||||||
|
stage: 'baby',
|
||||||
|
hunger: 100,
|
||||||
|
happiness: 100,
|
||||||
|
health: 100,
|
||||||
|
weight: 500,
|
||||||
|
ageSeconds: 0,
|
||||||
|
poopCount: 0,
|
||||||
|
str: 0,
|
||||||
|
int: 0,
|
||||||
|
dex: 0,
|
||||||
|
luck: config.baseStats.luck || 10,
|
||||||
|
isSleeping: false,
|
||||||
|
isSick: false,
|
||||||
|
isDead: false,
|
||||||
|
currentDeityId: 'mazu',
|
||||||
|
deityFavors: {
|
||||||
|
mazu: 0,
|
||||||
|
earthgod: 0,
|
||||||
|
yuelao: 0,
|
||||||
|
wenchang: 0,
|
||||||
|
guanyin: 0
|
||||||
|
},
|
||||||
|
dailyPrayerCount: 0,
|
||||||
|
destiny: null,
|
||||||
|
buffs: [],
|
||||||
|
inventory: [],
|
||||||
|
generation: 1,
|
||||||
|
lastTickTime: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新狀態(同步到 API)
|
||||||
|
async updateState(updates) {
|
||||||
|
this.state = { ...this.state, ...updates }
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.updatePetState(updates)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[PetSystem] API 更新失敗,使用本地狀態:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取當前狀態
|
||||||
|
getState() {
|
||||||
|
return { ...this.state }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刪除寵物
|
||||||
|
async deletePet() {
|
||||||
|
try {
|
||||||
|
// 停止所有循環
|
||||||
|
this.stopTickLoop()
|
||||||
|
|
||||||
|
// 從 API 刪除
|
||||||
|
await this.api.deletePetState()
|
||||||
|
|
||||||
|
// 重置狀態
|
||||||
|
this.state = null
|
||||||
|
this.speciesConfig = null
|
||||||
|
|
||||||
|
return { success: true, message: '寵物已刪除' }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[PetSystem] 刪除寵物失敗:', error)
|
||||||
|
// 即使 API 失敗,也清除本地狀態
|
||||||
|
this.state = null
|
||||||
|
this.speciesConfig = null
|
||||||
|
return { success: true, message: '寵物已刪除(本地)' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick 循環(每 3 秒)
|
||||||
|
startTickLoop(callback) {
|
||||||
|
if (this.tickInterval) this.stopTickLoop()
|
||||||
|
|
||||||
|
this.tickInterval = setInterval(async () => {
|
||||||
|
await this.tick()
|
||||||
|
if (callback) callback(this.getState())
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
stopTickLoop() {
|
||||||
|
if (this.tickInterval) {
|
||||||
|
clearInterval(this.tickInterval)
|
||||||
|
this.tickInterval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 單次 Tick 執行
|
||||||
|
async tick() {
|
||||||
|
if (!this.state || this.state.isDead) return
|
||||||
|
|
||||||
|
const now = Date.now()
|
||||||
|
const deltaTime = (now - (this.state.lastTickTime || now)) / 1000
|
||||||
|
this.state.lastTickTime = now
|
||||||
|
|
||||||
|
// 年齡增長
|
||||||
|
this.state.ageSeconds += deltaTime
|
||||||
|
|
||||||
|
// 檢查階段轉換
|
||||||
|
this.checkStageTransition()
|
||||||
|
|
||||||
|
// 基礎數值衰減
|
||||||
|
const config = this.speciesConfig.baseStats
|
||||||
|
this.state.hunger = Math.max(0, this.state.hunger - config.hungerDecayPerTick * 100)
|
||||||
|
this.state.happiness = Math.max(0, this.state.happiness - config.happinessDecayPerTick * 100)
|
||||||
|
|
||||||
|
// 便便機率
|
||||||
|
if (Math.random() < config.poopChancePerTick) {
|
||||||
|
this.state.poopCount = Math.min(4, this.state.poopCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生病機率
|
||||||
|
if (!this.state.isSick && Math.random() < config.sicknessChance) {
|
||||||
|
this.state.isSick = true
|
||||||
|
await this.updateState({ isSick: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 健康檢查
|
||||||
|
if (this.state.health <= 0) {
|
||||||
|
this.state.isDead = true
|
||||||
|
await this.updateState({ isDead: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步到 API
|
||||||
|
await this.updateState({
|
||||||
|
ageSeconds: this.state.ageSeconds,
|
||||||
|
hunger: this.state.hunger,
|
||||||
|
happiness: this.state.happiness,
|
||||||
|
poopCount: this.state.poopCount,
|
||||||
|
isSick: this.state.isSick,
|
||||||
|
isDead: this.state.isDead
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查階段轉換
|
||||||
|
checkStageTransition() {
|
||||||
|
const config = this.speciesConfig
|
||||||
|
const currentStage = this.state.stage
|
||||||
|
const ageSeconds = this.state.ageSeconds
|
||||||
|
|
||||||
|
for (const stageConfig of config.lifecycle) {
|
||||||
|
if (stageConfig.stage === currentStage) {
|
||||||
|
// 找到下一個階段
|
||||||
|
const currentIndex = config.lifecycle.findIndex(s => s.stage === currentStage)
|
||||||
|
if (currentIndex < config.lifecycle.length - 1) {
|
||||||
|
const nextStage = config.lifecycle[currentIndex + 1]
|
||||||
|
if (ageSeconds >= nextStage.durationSeconds && nextStage.durationSeconds > 0) {
|
||||||
|
this.state.stage = nextStage.stage
|
||||||
|
this.updateState({ stage: nextStage.stage })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 餵食
|
||||||
|
async feed(amount = 20) {
|
||||||
|
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
|
||||||
|
|
||||||
|
const newHunger = Math.min(100, this.state.hunger + amount)
|
||||||
|
const newWeight = this.state.weight + (amount * 0.5)
|
||||||
|
|
||||||
|
await this.updateState({
|
||||||
|
hunger: newHunger,
|
||||||
|
weight: newWeight
|
||||||
|
})
|
||||||
|
|
||||||
|
return { success: true, hunger: newHunger, weight: newWeight }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 玩耍
|
||||||
|
async play(amount = 15) {
|
||||||
|
if (this.state.isDead || this.state.isSleeping) {
|
||||||
|
return { success: false, message: '寵物無法玩耍' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const newHappiness = Math.min(100, this.state.happiness + amount)
|
||||||
|
const newDex = this.state.dex + 0.5
|
||||||
|
|
||||||
|
await this.updateState({
|
||||||
|
happiness: newHappiness,
|
||||||
|
dex: newDex
|
||||||
|
})
|
||||||
|
|
||||||
|
return { success: true, happiness: newHappiness, dex: newDex }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理便便
|
||||||
|
async cleanPoop() {
|
||||||
|
if (this.state.poopCount === 0) {
|
||||||
|
return { success: false, message: '沒有便便需要清理' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPoopCount = 0
|
||||||
|
const newHappiness = Math.min(100, this.state.happiness + 10)
|
||||||
|
|
||||||
|
await this.updateState({
|
||||||
|
poopCount: newPoopCount,
|
||||||
|
happiness: newHappiness
|
||||||
|
})
|
||||||
|
|
||||||
|
return { success: true, poopCount: newPoopCount, happiness: newHappiness }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 治療
|
||||||
|
async heal(amount = 20) {
|
||||||
|
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
|
||||||
|
|
||||||
|
const newHealth = Math.min(100, this.state.health + amount)
|
||||||
|
const wasSick = this.state.isSick
|
||||||
|
|
||||||
|
await this.updateState({
|
||||||
|
health: newHealth,
|
||||||
|
isSick: false
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
health: newHealth,
|
||||||
|
isSick: false,
|
||||||
|
cured: wasSick && !this.state.isSick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 睡覺/起床
|
||||||
|
async toggleSleep() {
|
||||||
|
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
|
||||||
|
|
||||||
|
const newIsSleeping = !this.state.isSleeping
|
||||||
|
const healthChange = newIsSleeping ? 5 : 0
|
||||||
|
|
||||||
|
await this.updateState({
|
||||||
|
isSleeping: newIsSleeping,
|
||||||
|
health: Math.min(100, this.state.health + healthChange)
|
||||||
|
})
|
||||||
|
|
||||||
|
return { success: true, isSleeping: newIsSleeping }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
// 神明系統 - 與 API 整合
|
||||||
|
import { apiService } from './api-service.js'
|
||||||
|
|
||||||
|
export class TempleSystem {
|
||||||
|
constructor(petSystem, api = apiService) {
|
||||||
|
this.petSystem = petSystem
|
||||||
|
this.api = api
|
||||||
|
this.deities = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(從 API 載入神明資料)
|
||||||
|
async initialize() {
|
||||||
|
try {
|
||||||
|
this.deities = await this.api.getDeities()
|
||||||
|
console.log(`[TempleSystem] 載入 ${this.deities.length} 位神明`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TempleSystem] 載入神明失敗:', error)
|
||||||
|
// 降級到本地載入
|
||||||
|
const { DEITIES } = await import('../data/deities.js')
|
||||||
|
this.deities = DEITIES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取所有神明
|
||||||
|
getDeities() {
|
||||||
|
return [...this.deities]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取當前神明
|
||||||
|
getCurrentDeity() {
|
||||||
|
const state = this.petSystem.getState()
|
||||||
|
return this.deities.find(d => d.id === state.currentDeityId) || this.deities[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切換神明
|
||||||
|
async switchDeity(deityId) {
|
||||||
|
const deity = this.deities.find(d => d.id === deityId)
|
||||||
|
if (!deity) {
|
||||||
|
return { success: false, message: '找不到該神明' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.petSystem.updateState({ currentDeityId: deityId })
|
||||||
|
return { success: true, deity }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 祈福(每日上限 3 次)
|
||||||
|
async pray() {
|
||||||
|
const state = this.petSystem.getState()
|
||||||
|
|
||||||
|
if (state.dailyPrayerCount >= 3) {
|
||||||
|
return { success: false, message: '今日祈福次數已用完' }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.api.prayToDeity({
|
||||||
|
deityId: state.currentDeityId,
|
||||||
|
petState: state
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新好感度
|
||||||
|
const currentFavor = state.deityFavors[state.currentDeityId] || 0
|
||||||
|
const newFavor = Math.min(100, currentFavor + result.favorIncrease)
|
||||||
|
|
||||||
|
await this.petSystem.updateState({
|
||||||
|
deityFavors: {
|
||||||
|
...state.deityFavors,
|
||||||
|
[state.currentDeityId]: newFavor
|
||||||
|
},
|
||||||
|
dailyPrayerCount: state.dailyPrayerCount + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// 獲取神明對話
|
||||||
|
const deity = this.getCurrentDeity()
|
||||||
|
const dialogue = deity.dialogues[Math.floor(Math.random() * deity.dialogues.length)]
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
favorIncrease: result.favorIncrease,
|
||||||
|
newFavor,
|
||||||
|
dialogue,
|
||||||
|
message: result.message
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TempleSystem] 祈福失敗:', error)
|
||||||
|
return { success: false, message: '祈福失敗' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取好感度星級(每 20 點一星)
|
||||||
|
getFavorStars(deityId) {
|
||||||
|
const state = this.petSystem.getState()
|
||||||
|
const favor = state.deityFavors[deityId] || 0
|
||||||
|
const stars = Math.floor(favor / 20)
|
||||||
|
return '★'.repeat(stars) + '☆'.repeat(5 - stars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽籤
|
||||||
|
async drawFortune() {
|
||||||
|
try {
|
||||||
|
const result = await this.api.drawFortune()
|
||||||
|
|
||||||
|
// 根據籤詩等級應用效果
|
||||||
|
const { FORTUNE_LOTS } = await import('../data/fortune-lots.js')
|
||||||
|
const lot = FORTUNE_LOTS.find(l => l.grade === result.lot.grade) || FORTUNE_LOTS[0]
|
||||||
|
|
||||||
|
if (lot.effects?.addBuff) {
|
||||||
|
// 應用 Buff(需要透過 eventSystem)
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
lot: result.lot,
|
||||||
|
buff: lot.effects.addBuff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
lot: result.lot
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[TempleSystem] 抽籤失敗:', error)
|
||||||
|
return { success: false, message: '抽籤失敗' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 神明資料配置
|
||||||
|
export const DEITIES = [
|
||||||
|
{
|
||||||
|
id: 'mazu',
|
||||||
|
name: '媽祖',
|
||||||
|
personality: '溫柔守護',
|
||||||
|
buffs: {
|
||||||
|
gameSuccessRate: 0.1,
|
||||||
|
sicknessReduction: 0.15,
|
||||||
|
happinessRecovery: 0.25
|
||||||
|
},
|
||||||
|
buffDescriptions: ['小遊戲 +10%', '生病機率 -15%', '快樂恢復 +25%'],
|
||||||
|
dialogues: [
|
||||||
|
'好孩子,媽祖保佑你平安喔',
|
||||||
|
'海上無風浪,心中有媽祖',
|
||||||
|
'要好好照顧寵物啊',
|
||||||
|
'心誠則靈,媽祖會守護你的'
|
||||||
|
],
|
||||||
|
icon: 'deity-mazu'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'earthgod',
|
||||||
|
name: '土地公',
|
||||||
|
personality: '親切和藹',
|
||||||
|
buffs: {
|
||||||
|
dropRate: 0.2,
|
||||||
|
resourceGain: 0.15
|
||||||
|
},
|
||||||
|
buffDescriptions: ['掉落率 +20%', '資源獲得 +15%'],
|
||||||
|
dialogues: [
|
||||||
|
'土地公保佑,財源廣進',
|
||||||
|
'好好照顧寵物,會有福報的',
|
||||||
|
'心善之人,必有善報'
|
||||||
|
],
|
||||||
|
icon: 'deity-earthgod'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yuelao',
|
||||||
|
name: '月老',
|
||||||
|
personality: '浪漫溫和',
|
||||||
|
buffs: {
|
||||||
|
happinessRecovery: 0.3,
|
||||||
|
breedingSuccess: 0.2
|
||||||
|
},
|
||||||
|
buffDescriptions: ['快樂恢復 +30%', '繁殖成功率 +20%'],
|
||||||
|
dialogues: [
|
||||||
|
'有緣千里來相會',
|
||||||
|
'好好對待寵物,感情會更深厚',
|
||||||
|
'愛心滿滿,幸福滿滿'
|
||||||
|
],
|
||||||
|
icon: 'deity-yuelao'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'wenchang',
|
||||||
|
name: '文昌',
|
||||||
|
personality: '智慧嚴謹',
|
||||||
|
buffs: {
|
||||||
|
intGain: 0.25,
|
||||||
|
miniGameBonus: 0.15
|
||||||
|
},
|
||||||
|
buffDescriptions: ['智力成長 +25%', '小遊戲獎勵 +15%'],
|
||||||
|
dialogues: [
|
||||||
|
'勤學不輟,智慧增長',
|
||||||
|
'好好學習,寵物也會變聰明',
|
||||||
|
'知識就是力量'
|
||||||
|
],
|
||||||
|
icon: 'deity-wenchang'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'guanyin',
|
||||||
|
name: '觀音',
|
||||||
|
personality: '慈悲寬容',
|
||||||
|
buffs: {
|
||||||
|
healthRecovery: 0.2,
|
||||||
|
badEventReduction: 0.15
|
||||||
|
},
|
||||||
|
buffDescriptions: ['健康恢復 +20%', '壞事件機率 -15%'],
|
||||||
|
dialogues: [
|
||||||
|
'慈悲為懷,萬物皆靈',
|
||||||
|
'好好照顧,觀音會保佑',
|
||||||
|
'心善之人,處處有福'
|
||||||
|
],
|
||||||
|
icon: 'deity-guanyin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
// 事件配置(資料驅動)
|
||||||
|
export const EVENT_CONFIG = [
|
||||||
|
// Good 事件
|
||||||
|
{
|
||||||
|
id: 'lucky_find',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.3,
|
||||||
|
condition: (state) => state.happiness > 50 && state.luck > 10,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: +10, luck: +5 } },
|
||||||
|
{ type: 'addBuff', payload: { id: 'fortune', name: '福運加持', durationTicks: 5, percent: { luck: 0.2 } } },
|
||||||
|
{ type: 'logMessage', payload: '寵物撿到符咒!好運來了~' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'find_treat',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.25,
|
||||||
|
condition: (state) => state.hunger < 80,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { hunger: +15, happiness: +5 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物發現了美味的點心!' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'playful_moment',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.2,
|
||||||
|
condition: (state) => !state.isSleeping && state.happiness < 90,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: +12, dex: +1 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物玩得很開心,敏捷度提升了!' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deity_blessing',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.15,
|
||||||
|
condition: (state) => {
|
||||||
|
const currentDeity = state.currentDeityId
|
||||||
|
return state.deityFavors[currentDeity] > 30
|
||||||
|
},
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { health: +10, happiness: +8 } },
|
||||||
|
{ type: 'addBuff', payload: { id: 'blessing', name: '神明祝福', durationTicks: 10, percent: { health: 0.1 } } },
|
||||||
|
{ type: 'logMessage', payload: '感受到神明的祝福,寵物精神煥發!' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rare_treasure',
|
||||||
|
type: 'rare',
|
||||||
|
weight: 0.1,
|
||||||
|
condition: (state) => state.luck > 20 && Math.random() > 0.7,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { luck: +10, str: +2, int: +2, dex: +2 } },
|
||||||
|
{ type: 'addBuff', payload: { id: 'treasure_luck', name: '寶物運', durationTicks: 15, percent: { dropRate: 0.3 } } },
|
||||||
|
{ type: 'logMessage', payload: '✨ 發現稀有寶物!運勢大爆發!' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bad 事件
|
||||||
|
{
|
||||||
|
id: 'over_eat',
|
||||||
|
type: 'bad',
|
||||||
|
weight: 0.4,
|
||||||
|
condition: (state) => state.weight > 600 && state.hunger < 20,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { health: -15, weight: +20 } },
|
||||||
|
{ type: 'addBuff', payload: { id: 'hungry_penalty', name: '飢餓懲罰', durationTicks: 3, flat: { hungerDecay: 0.1 } } },
|
||||||
|
{ type: 'logMessage', payload: '寵物偷吃太多,胃痛了!' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'catch_cold',
|
||||||
|
type: 'bad',
|
||||||
|
weight: 0.3,
|
||||||
|
condition: (state) => state.health < 70 && !state.isSick,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { health: -10, happiness: -5 } },
|
||||||
|
{ type: 'addBuff', payload: { id: 'sick', name: '生病', durationTicks: 5, flat: { healthDecay: 0.05 } } },
|
||||||
|
{ type: 'modifyStats', payload: { isSick: true } },
|
||||||
|
{ type: 'logMessage', payload: '寵物感冒了,需要好好休息...' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'accident',
|
||||||
|
type: 'bad',
|
||||||
|
weight: 0.2,
|
||||||
|
condition: (state) => state.dex < 30 && Math.random() > 0.5,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { health: -8, happiness: -10 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物不小心摔了一跤,受傷了...' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'lonely',
|
||||||
|
type: 'bad',
|
||||||
|
weight: 0.1,
|
||||||
|
condition: (state) => state.happiness < 30 && state.hunger > 50,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: -15 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物感到孤單,心情低落...' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Weird 事件
|
||||||
|
{
|
||||||
|
id: 'stare_wall',
|
||||||
|
type: 'weird',
|
||||||
|
weight: 0.3,
|
||||||
|
condition: (state) => state.isSleeping === false && Math.random() > 0.7,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: -5 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物盯著牆壁發呆... 怪怪的。' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'sudden_sleep',
|
||||||
|
type: 'weird',
|
||||||
|
weight: 0.25,
|
||||||
|
condition: (state) => !state.isSleeping && state.happiness < 50,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { isSleeping: true } },
|
||||||
|
{ type: 'modifyStats', payload: { health: +5 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物突然睡著了... 可能是太累了?' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mysterious_glow',
|
||||||
|
type: 'weird',
|
||||||
|
weight: 0.2,
|
||||||
|
condition: (state) => state.luck > 15 && Math.random() > 0.6,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { luck: +3, int: +1 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物身上發出神秘的光芒... 智力提升了?' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'random_dance',
|
||||||
|
type: 'weird',
|
||||||
|
weight: 0.15,
|
||||||
|
condition: (state) => state.happiness > 60 && !state.isSleeping,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: +8, dex: +1 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物突然開始跳舞!看起來很開心~' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'philosophy_moment',
|
||||||
|
type: 'weird',
|
||||||
|
weight: 0.1,
|
||||||
|
condition: (state) => state.int > 20 && Math.random() > 0.7,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { int: +2, happiness: +5 } },
|
||||||
|
{ type: 'logMessage', payload: '寵物似乎在思考人生... 智力提升了!' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
// 籤詩資料(簡化版,實際應從 JSON 載入)
|
||||||
|
export const FORTUNE_LOTS = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
grade: '上上',
|
||||||
|
palace: '子宮',
|
||||||
|
poem1: '天開地闢萬物全 人力回天事事全',
|
||||||
|
poem2: '若問前途與運泰 唯有善德鬼神欽',
|
||||||
|
meaning: '大吉大利,萬事亨通',
|
||||||
|
explanation: '此籤象徵開天闢地之意,預示大吉',
|
||||||
|
oracle: '家宅:平安、事業:順利',
|
||||||
|
story: '宋太祖黃袍加身',
|
||||||
|
effects: {
|
||||||
|
addBuff: {
|
||||||
|
id: 'fortune_blessing',
|
||||||
|
name: '上上籤祝福',
|
||||||
|
durationTicks: 20,
|
||||||
|
percent: { luck: 0.3, dropRate: 0.25 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
grade: '上',
|
||||||
|
palace: '丑宮',
|
||||||
|
poem1: '鯤化為鵬海浪翻 陰陽交泰太平間',
|
||||||
|
poem2: '庶人驀有凌霄志 平地雷聲震山川',
|
||||||
|
meaning: '時來運轉,大展宏圖',
|
||||||
|
explanation: '此籤預示時機已到,可大展身手',
|
||||||
|
oracle: '家宅:興旺、事業:順利',
|
||||||
|
story: '鯤鵬展翅',
|
||||||
|
effects: {
|
||||||
|
addBuff: {
|
||||||
|
id: 'good_fortune',
|
||||||
|
name: '上籤祝福',
|
||||||
|
durationTicks: 15,
|
||||||
|
percent: { luck: 0.2 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '50',
|
||||||
|
grade: '中',
|
||||||
|
palace: '午宮',
|
||||||
|
poem1: '五湖四海任君行 高掛帆篷自在撐',
|
||||||
|
poem2: '若得順風隨即至 滿船寶貝喜層層',
|
||||||
|
meaning: '順風順水,收穫豐碩',
|
||||||
|
explanation: '此籤預示順境,但需把握時機',
|
||||||
|
oracle: '家宅:平穩、事業:順利',
|
||||||
|
story: '順風行船',
|
||||||
|
effects: {
|
||||||
|
addBuff: {
|
||||||
|
id: 'normal_fortune',
|
||||||
|
name: '中籤祝福',
|
||||||
|
durationTicks: 10,
|
||||||
|
percent: { luck: 0.1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '80',
|
||||||
|
grade: '下',
|
||||||
|
palace: '申宮',
|
||||||
|
poem1: '炎炎烈火焰連天 焰裡還生一朵蓮',
|
||||||
|
poem2: '到底永成根不壞 依然生葉長枝根',
|
||||||
|
meaning: '雖有困難,終能克服',
|
||||||
|
explanation: '此籤預示雖有困難,但能化險為夷',
|
||||||
|
oracle: '家宅:需注意、事業:需努力',
|
||||||
|
story: '火中蓮',
|
||||||
|
effects: {
|
||||||
|
addBuff: {
|
||||||
|
id: 'bad_fortune',
|
||||||
|
name: '下籤影響',
|
||||||
|
durationTicks: 5,
|
||||||
|
percent: { luck: -0.1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 抽籤機率配置
|
||||||
|
export const FORTUNE_GRADE_WEIGHTS = {
|
||||||
|
'上上': 0.05,
|
||||||
|
'上': 0.15,
|
||||||
|
'中': 0.40,
|
||||||
|
'下': 0.30,
|
||||||
|
'下下': 0.10
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 道具配置
|
||||||
|
export const ITEMS = {
|
||||||
|
cookie: {
|
||||||
|
id: 'cookie',
|
||||||
|
name: '幸運餅乾',
|
||||||
|
type: 'consumable',
|
||||||
|
effects: { modifyStats: { hunger: +20, happiness: +10 } },
|
||||||
|
description: '增加飢餓和快樂'
|
||||||
|
},
|
||||||
|
water: {
|
||||||
|
id: 'water',
|
||||||
|
name: '神水',
|
||||||
|
type: 'consumable',
|
||||||
|
effects: { modifyStats: { health: +15, happiness: +5 } },
|
||||||
|
description: '恢復健康'
|
||||||
|
},
|
||||||
|
amulet: {
|
||||||
|
id: 'amulet',
|
||||||
|
name: '平安符',
|
||||||
|
type: 'equipment',
|
||||||
|
effects: { addBuff: { id: 'amulet_protection', name: '平安符保護', durationTicks: Infinity, flat: { health: 20 } } },
|
||||||
|
description: '永久增加健康上限'
|
||||||
|
},
|
||||||
|
sunglasses: {
|
||||||
|
id: 'sunglasses',
|
||||||
|
name: '酷酷墨鏡',
|
||||||
|
type: 'equipment',
|
||||||
|
effects: { addBuff: { id: 'cool', name: '酷炫', durationTicks: Infinity, percent: { happiness: 0.1 } } },
|
||||||
|
description: '永久增加快樂 10%'
|
||||||
|
},
|
||||||
|
training_manual: {
|
||||||
|
id: 'training_manual',
|
||||||
|
name: '訓練手冊',
|
||||||
|
type: 'equipment',
|
||||||
|
effects: { addBuff: { id: 'training', name: '訓練加成', durationTicks: Infinity, percent: { str: 0.15, dex: 0.15 } } },
|
||||||
|
description: '永久增加力量和敏捷成長'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 寵物種族配置
|
||||||
|
export const PET_SPECIES = {
|
||||||
|
tinyTigerCat: {
|
||||||
|
id: 'tinyTigerCat',
|
||||||
|
name: '小虎斑貓',
|
||||||
|
description: '活潑可愛的小貓咪',
|
||||||
|
baseStats: {
|
||||||
|
hungerDecayPerTick: 0.05,
|
||||||
|
happinessDecayPerTick: 0.08,
|
||||||
|
poopChancePerTick: 0.02,
|
||||||
|
sicknessChance: 0.01,
|
||||||
|
dropRate: 0.1,
|
||||||
|
luck: 10
|
||||||
|
},
|
||||||
|
lifecycle: [
|
||||||
|
{ stage: 'egg', durationSeconds: 0, unlockedFeatures: [] },
|
||||||
|
{ stage: 'baby', durationSeconds: 1800, unlockedFeatures: [] },
|
||||||
|
{ stage: 'child', durationSeconds: 3600, unlockedFeatures: ['miniGames'] },
|
||||||
|
{ stage: 'adult', durationSeconds: Infinity, unlockedFeatures: ['miniGames', 'breeding'] }
|
||||||
|
],
|
||||||
|
personality: ['活潑', '黏人']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,335 @@
|
||||||
|
# API 端點結構設計
|
||||||
|
|
||||||
|
本文檔定義了虛擬寵物系統的 API 端點結構,用於前端與後端整合。
|
||||||
|
|
||||||
|
## 基礎配置
|
||||||
|
|
||||||
|
- **Base URL**: `http://localhost:3000/api` (開發環境)
|
||||||
|
- **認證**: 未來可加入 JWT Token
|
||||||
|
- **資料格式**: JSON
|
||||||
|
|
||||||
|
## API 端點列表
|
||||||
|
|
||||||
|
### 1. 寵物狀態相關
|
||||||
|
|
||||||
|
#### GET `/pet/state`
|
||||||
|
獲取當前寵物狀態
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"speciesId": "tinyTigerCat",
|
||||||
|
"stage": "baby",
|
||||||
|
"hunger": 80.5,
|
||||||
|
"happiness": 60.2,
|
||||||
|
"health": 100,
|
||||||
|
"weight": 500,
|
||||||
|
"ageSeconds": 120,
|
||||||
|
"poopCount": 2,
|
||||||
|
"str": 5,
|
||||||
|
"int": 3,
|
||||||
|
"dex": 8,
|
||||||
|
"luck": 15,
|
||||||
|
"isSleeping": false,
|
||||||
|
"isSick": false,
|
||||||
|
"isDead": false,
|
||||||
|
"currentDeityId": "mazu",
|
||||||
|
"deityFavors": {
|
||||||
|
"mazu": 20,
|
||||||
|
"earthgod": 10,
|
||||||
|
"yuelao": 0,
|
||||||
|
"wenchang": 0,
|
||||||
|
"guanyin": 5
|
||||||
|
},
|
||||||
|
"dailyPrayerCount": 1,
|
||||||
|
"destiny": null,
|
||||||
|
"buffs": [],
|
||||||
|
"inventory": [],
|
||||||
|
"generation": 1,
|
||||||
|
"lastTickTime": 1234567890
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST `/pet/update`
|
||||||
|
更新寵物狀態(部分更新)
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hunger": 85,
|
||||||
|
"happiness": 70
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": { /* 完整狀態物件 */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST `/pet/save`
|
||||||
|
儲存完整寵物狀態
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{ /* 完整 PetState 物件 */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "狀態已儲存"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 事件系統相關
|
||||||
|
|
||||||
|
#### GET `/events/list`
|
||||||
|
獲取所有事件配置
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "lucky_find",
|
||||||
|
"type": "good",
|
||||||
|
"weight": 0.3,
|
||||||
|
"condition": "function_string_or_null",
|
||||||
|
"effects": [
|
||||||
|
{
|
||||||
|
"type": "modifyStats",
|
||||||
|
"payload": { "happiness": 10, "luck": 5 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST `/events/trigger`
|
||||||
|
觸發事件
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"eventId": "lucky_find",
|
||||||
|
"petState": { /* 當前狀態 */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"eventId": "lucky_find",
|
||||||
|
"effects": [
|
||||||
|
{
|
||||||
|
"type": "modifyStats",
|
||||||
|
"updates": { "happiness": 10, "luck": 5 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Buff 系統相關
|
||||||
|
|
||||||
|
#### POST `/buffs/apply`
|
||||||
|
應用 Buff
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "fortune",
|
||||||
|
"name": "福運加持",
|
||||||
|
"durationTicks": 5,
|
||||||
|
"percent": { "luck": 0.2 }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"buff": { /* Buff 物件 */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 神明系統相關
|
||||||
|
|
||||||
|
#### GET `/deities/list`
|
||||||
|
獲取所有神明資料
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "mazu",
|
||||||
|
"name": "媽祖",
|
||||||
|
"personality": "溫柔守護",
|
||||||
|
"buffs": {
|
||||||
|
"gameSuccessRate": 0.1,
|
||||||
|
"sicknessReduction": 0.15
|
||||||
|
},
|
||||||
|
"buffDescriptions": ["小遊戲 +10%", "生病機率 -15%"],
|
||||||
|
"dialogues": ["好孩子,媽祖保佑你平安喔"],
|
||||||
|
"icon": "deity-mazu"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST `/deities/pray`
|
||||||
|
祈福
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"deityId": "mazu",
|
||||||
|
"petState": { /* 當前狀態 */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"favorIncrease": 5,
|
||||||
|
"newFavor": 25,
|
||||||
|
"message": "祈福成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. 籤詩系統相關
|
||||||
|
|
||||||
|
#### POST `/fortune/draw`
|
||||||
|
抽籤
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"deityId": "guanyin" // 可選
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"lot": {
|
||||||
|
"id": "1",
|
||||||
|
"grade": "上上",
|
||||||
|
"palace": "子宮",
|
||||||
|
"poem1": "天開地闢萬物全 人力回天事事全",
|
||||||
|
"poem2": "若問前途與運泰 唯有善德鬼神欽",
|
||||||
|
"meaning": "大吉大利,萬事亨通",
|
||||||
|
"explanation": "此籤象徵開天闢地之意,預示大吉",
|
||||||
|
"oracle": "家宅:平安、事業:順利",
|
||||||
|
"story": "宋太祖黃袍加身"
|
||||||
|
},
|
||||||
|
"buff": {
|
||||||
|
"id": "fortune_blessing",
|
||||||
|
"name": "上上籤祝福",
|
||||||
|
"durationTicks": 20,
|
||||||
|
"percent": { "luck": 0.3, "dropRate": 0.25 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. 道具系統相關
|
||||||
|
|
||||||
|
#### GET `/items/list`
|
||||||
|
獲取所有道具
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "cookie",
|
||||||
|
"name": "幸運餅乾",
|
||||||
|
"type": "consumable",
|
||||||
|
"effects": {
|
||||||
|
"modifyStats": { "hunger": 20, "happiness": 10 }
|
||||||
|
},
|
||||||
|
"description": "增加飢餓和快樂"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST `/items/use`
|
||||||
|
使用道具
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"itemId": "cookie",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"itemId": "cookie",
|
||||||
|
"count": 1,
|
||||||
|
"effects": {
|
||||||
|
"modifyStats": { "hunger": 20, "happiness": 10 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 錯誤處理
|
||||||
|
|
||||||
|
所有 API 錯誤應返回以下格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": {
|
||||||
|
"code": "ERROR_CODE",
|
||||||
|
"message": "錯誤訊息"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
常見錯誤碼:
|
||||||
|
- `PET_NOT_FOUND`: 寵物不存在
|
||||||
|
- `INVALID_STATE`: 狀態無效
|
||||||
|
- `EVENT_CONDITION_FAILED`: 事件條件不滿足
|
||||||
|
- `DAILY_LIMIT_EXCEEDED`: 每日限制已達上限
|
||||||
|
- `ITEM_NOT_FOUND`: 道具不存在
|
||||||
|
- `INSUFFICIENT_ITEM`: 道具數量不足
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 實作建議
|
||||||
|
|
||||||
|
### 後端實作
|
||||||
|
1. 使用 RESTful API 設計
|
||||||
|
2. 實作資料驗證(Joi/Yup)
|
||||||
|
3. 加入認證中介層(未來)
|
||||||
|
4. 實作速率限制(rate limiting)
|
||||||
|
5. 使用資料庫儲存狀態(PostgreSQL/MongoDB)
|
||||||
|
|
||||||
|
### 前端整合
|
||||||
|
1. 使用 `ApiService` 類別統一管理 API 呼叫
|
||||||
|
2. 實作錯誤處理和重試機制
|
||||||
|
3. 使用狀態管理(Pinia/Vuex)同步狀態
|
||||||
|
4. 實作樂觀更新(Optimistic Updates)
|
||||||
|
|
||||||
|
### Mock 模式
|
||||||
|
開發階段可使用 `ApiService` 的 mock 模式,所有資料儲存在 `localStorage`,方便測試和開發。
|
||||||
|
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
# 虛擬寵物系統 - 使用說明
|
||||||
|
|
||||||
|
## 🚀 快速開始
|
||||||
|
|
||||||
|
### 方式一:瀏覽器 Console(推薦)
|
||||||
|
|
||||||
|
1. 啟動 Nuxt 開發伺服器:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 打開瀏覽器,訪問:`http://localhost:3000/console-demo.html`
|
||||||
|
|
||||||
|
3. 打開開發者工具(F12 或 Cmd+Option+I),切換到 **Console** 標籤
|
||||||
|
|
||||||
|
4. 系統會自動初始化,然後你可以輸入命令:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 查看幫助
|
||||||
|
help()
|
||||||
|
|
||||||
|
// 顯示狀態
|
||||||
|
showStatus()
|
||||||
|
|
||||||
|
// 啟動遊戲循環
|
||||||
|
start()
|
||||||
|
|
||||||
|
// 餵食
|
||||||
|
feed(20)
|
||||||
|
|
||||||
|
// 玩耍
|
||||||
|
play(15)
|
||||||
|
|
||||||
|
// 祈福
|
||||||
|
pray()
|
||||||
|
|
||||||
|
// 抽籤
|
||||||
|
drawFortune()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:Node.js 環境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node console-demo.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 專案結構
|
||||||
|
|
||||||
|
```
|
||||||
|
nuxt-app/
|
||||||
|
├── data/ # 資料配置(資料驅動)
|
||||||
|
│ ├── pet-species.js # 寵物種族配置
|
||||||
|
│ ├── deities.js # 神明資料
|
||||||
|
│ ├── events.js # 事件配置
|
||||||
|
│ ├── items.js # 道具配置
|
||||||
|
│ └── fortune-lots.js # 籤詩資料
|
||||||
|
├── core/ # 核心系統
|
||||||
|
│ ├── api-service.js # API 服務層(支援 mock/real)
|
||||||
|
│ ├── pet-system.js # 寵物系統
|
||||||
|
│ ├── event-system.js # 事件系統
|
||||||
|
│ └── temple-system.js # 神明系統
|
||||||
|
├── console-demo.js # Console 互動介面
|
||||||
|
├── public/
|
||||||
|
│ └── console-demo.html # 瀏覽器版本
|
||||||
|
└── docs/
|
||||||
|
├── API_STRUCTURE.md # API 端點文檔
|
||||||
|
└── USAGE.md # 本文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎮 可用命令
|
||||||
|
|
||||||
|
### 遊戲控制
|
||||||
|
- `init()` - 初始化系統
|
||||||
|
- `start()` - 啟動遊戲循環(每 3 秒 tick,每 10 秒檢查事件)
|
||||||
|
- `stop()` - 停止遊戲循環
|
||||||
|
- `showStatus()` - 顯示當前寵物狀態
|
||||||
|
|
||||||
|
### 寵物互動
|
||||||
|
- `feed(amount)` - 餵食(預設 +20 飢餓)
|
||||||
|
- `play(amount)` - 玩耍(預設 +15 快樂,+0.5 敏捷)
|
||||||
|
- `clean()` - 清理便便(+10 快樂)
|
||||||
|
- `heal(amount)` - 治療(預設 +20 健康,可治癒疾病)
|
||||||
|
- `sleep()` - 睡覺/起床(睡覺時 +5 健康)
|
||||||
|
|
||||||
|
### 事件系統
|
||||||
|
- `triggerEvent(eventId)` - 手動觸發事件(測試用)
|
||||||
|
- `listEvents()` - 查看所有可用事件
|
||||||
|
- `history()` - 查看事件歷史記錄
|
||||||
|
- `applyBuffs()` - 手動應用 Buff(通常自動執行)
|
||||||
|
|
||||||
|
### 神明系統
|
||||||
|
- `pray()` - 祈福(每日上限 3 次,+5 好感度)
|
||||||
|
- `drawFortune()` - 抽籤(獲得 Buff)
|
||||||
|
- `switchDeity(deityId)` - 切換神明('mazu', 'earthgod', 'yuelao', 'wenchang', 'guanyin')
|
||||||
|
- `listDeities()` - 查看所有神明及其好感度
|
||||||
|
|
||||||
|
## 🔌 API 整合
|
||||||
|
|
||||||
|
### 切換到真實 API
|
||||||
|
|
||||||
|
在 `console-demo.js` 或使用時修改:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { ApiService } from './core/api-service.js'
|
||||||
|
|
||||||
|
const apiService = new ApiService({
|
||||||
|
useMock: false, // 切換到真實 API
|
||||||
|
baseUrl: 'https://your-api-domain.com/api',
|
||||||
|
mockDelay: 0
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 端點結構
|
||||||
|
|
||||||
|
詳見 `docs/API_STRUCTURE.md`,包含:
|
||||||
|
- 寵物狀態相關端點
|
||||||
|
- 事件系統端點
|
||||||
|
- Buff 系統端點
|
||||||
|
- 神明系統端點
|
||||||
|
- 籤詩系統端點
|
||||||
|
- 道具系統端點
|
||||||
|
|
||||||
|
## 📊 資料結構
|
||||||
|
|
||||||
|
### PetState(寵物狀態)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
speciesId: 'tinyTigerCat',
|
||||||
|
stage: 'baby',
|
||||||
|
hunger: 80,
|
||||||
|
happiness: 60,
|
||||||
|
health: 100,
|
||||||
|
weight: 500,
|
||||||
|
ageSeconds: 0,
|
||||||
|
str: 0,
|
||||||
|
int: 0,
|
||||||
|
dex: 0,
|
||||||
|
luck: 15,
|
||||||
|
isSleeping: false,
|
||||||
|
isSick: false,
|
||||||
|
isDead: false,
|
||||||
|
currentDeityId: 'mazu',
|
||||||
|
deityFavors: { mazu: 20, ... },
|
||||||
|
dailyPrayerCount: 0,
|
||||||
|
buffs: [],
|
||||||
|
inventory: []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GameEvent(事件配置)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 'lucky_find',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.3,
|
||||||
|
condition: (state) => state.happiness > 50,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { happiness: +10 } },
|
||||||
|
{ type: 'addBuff', payload: { ... } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buff(Buff 效果)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 'fortune',
|
||||||
|
name: '福運加持',
|
||||||
|
durationTicks: 5,
|
||||||
|
flat: { hunger: -10 }, // 固定值加成
|
||||||
|
percent: { luck: 0.2 } // 百分比加成
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 核心機制
|
||||||
|
|
||||||
|
### Tick 循環
|
||||||
|
- 每 3 秒執行一次
|
||||||
|
- 自動衰減:飢餓 -5%,快樂 -8%
|
||||||
|
- 便便機率:2%
|
||||||
|
- 生病機率:1%
|
||||||
|
|
||||||
|
### 事件觸發
|
||||||
|
- 每 10 秒檢查一次
|
||||||
|
- 10% 機率觸發
|
||||||
|
- 依權重隨機選擇事件
|
||||||
|
- 檢查條件函數
|
||||||
|
- 執行效果(修改屬性、添加 Buff 等)
|
||||||
|
|
||||||
|
### Buff 系統
|
||||||
|
- 每 tick 自動應用
|
||||||
|
- 計算公式:`(base + flat) * (1 + percent)`
|
||||||
|
- 持續時間倒數(Infinity 為永久)
|
||||||
|
- 可堆疊(需設定 `stacks: true`)
|
||||||
|
|
||||||
|
### 神明系統
|
||||||
|
- 5 位神明:媽祖、土地公、月老、文昌、觀音
|
||||||
|
- 每日祈福上限 3 次
|
||||||
|
- 好感度 0-100,每 20 點一星
|
||||||
|
- 不同神明有不同 Buff 加成
|
||||||
|
|
||||||
|
## 🛠️ 擴展開發
|
||||||
|
|
||||||
|
### 添加新事件
|
||||||
|
|
||||||
|
編輯 `data/events.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 'new_event',
|
||||||
|
type: 'good',
|
||||||
|
weight: 0.2,
|
||||||
|
condition: (state) => state.luck > 20,
|
||||||
|
effects: [
|
||||||
|
{ type: 'modifyStats', payload: { luck: +5 } },
|
||||||
|
{ type: 'logMessage', payload: '新事件觸發!' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加新道具
|
||||||
|
|
||||||
|
編輯 `data/items.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
newItem: {
|
||||||
|
id: 'new_item',
|
||||||
|
name: '新道具',
|
||||||
|
type: 'consumable',
|
||||||
|
effects: { modifyStats: { health: +30 } },
|
||||||
|
description: '恢復健康'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加新神明
|
||||||
|
|
||||||
|
編輯 `data/deities.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 'new_deity',
|
||||||
|
name: '新神明',
|
||||||
|
personality: '個性描述',
|
||||||
|
buffs: { gameSuccessRate: 0.15 },
|
||||||
|
buffDescriptions: ['小遊戲 +15%'],
|
||||||
|
dialogues: ['對話 1', '對話 2'],
|
||||||
|
icon: 'deity-new'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 除錯技巧
|
||||||
|
|
||||||
|
1. **查看狀態**:`showStatus()`
|
||||||
|
2. **查看事件歷史**:`history()`
|
||||||
|
3. **手動觸發事件**:`triggerEvent('lucky_find')`
|
||||||
|
4. **檢查 Buff**:`eventSystem.getBuffManager().getActiveBuffs()`
|
||||||
|
5. **檢查 API 模式**:`apiService.useMock`
|
||||||
|
|
||||||
|
## 📝 注意事項
|
||||||
|
|
||||||
|
1. **Mock 模式**:資料儲存在 `localStorage`,刷新頁面會保留
|
||||||
|
2. **API 同步**:所有狀態更新都會嘗試同步到 API(失敗時降級到本地)
|
||||||
|
3. **事件條件**:條件函數必須返回 boolean
|
||||||
|
4. **Buff 計算**:每 tick 自動應用,無需手動呼叫(除非測試)
|
||||||
|
5. **每日限制**:祈福次數每日重置(需實作時間檢查)
|
||||||
|
|
||||||
|
## 🔮 未來擴展
|
||||||
|
|
||||||
|
- [ ] 實作真實 API 後端
|
||||||
|
- [ ] 加入認證系統(JWT)
|
||||||
|
- [ ] 實作多人互動
|
||||||
|
- [ ] 加入小遊戲系統
|
||||||
|
- [ ] 實作繁殖系統
|
||||||
|
- [ ] 加入成就系統
|
||||||
|
- [ ] 實作排行榜
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// @ts-check
|
||||||
|
import withNuxt from './.nuxt/eslint.config.mjs'
|
||||||
|
|
||||||
|
export default withNuxt(
|
||||||
|
// Your custom configs here
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
compatibilityDate: '2025-07-15',
|
||||||
|
devtools: { enabled: true },
|
||||||
|
|
||||||
|
modules: [
|
||||||
|
'@nuxt/content',
|
||||||
|
'@nuxt/eslint',
|
||||||
|
'@nuxt/hints',
|
||||||
|
'@nuxt/image',
|
||||||
|
'@nuxt/scripts',
|
||||||
|
'@nuxt/test-utils',
|
||||||
|
'@nuxt/ui'
|
||||||
|
]
|
||||||
|
})
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "nuxt-app",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxt build",
|
||||||
|
"dev": "nuxt dev",
|
||||||
|
"generate": "nuxt generate",
|
||||||
|
"preview": "nuxt preview",
|
||||||
|
"postinstall": "nuxt prepare"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/content": "^3.8.2",
|
||||||
|
"@nuxt/eslint": "^1.10.0",
|
||||||
|
"@nuxt/hints": "^1.0.0-alpha.2",
|
||||||
|
"@nuxt/image": "^2.0.0",
|
||||||
|
"@nuxt/scripts": "^0.13.0",
|
||||||
|
"@nuxt/test-utils": "^3.20.1",
|
||||||
|
"@nuxt/ui": "^4.2.1",
|
||||||
|
"@unhead/vue": "^2.0.19",
|
||||||
|
"better-sqlite3": "^12.4.6",
|
||||||
|
"eslint": "^9.39.1",
|
||||||
|
"nuxt": "^4.2.1",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"vue": "^3.5.24",
|
||||||
|
"vue-router": "^4.6.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,230 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-TW">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>虛擬寵物系統 - Console 互動版</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #d4d4d4;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #4ec9b0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #4ec9b0;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background: #252526;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-left: 4px solid #007acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info h2 {
|
||||||
|
color: #4ec9b0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-area {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 2px solid #3c3c3c;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
min-height: 400px;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-list {
|
||||||
|
background: #252526;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-list h3 {
|
||||||
|
color: #4ec9b0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-list code {
|
||||||
|
color: #ce9178;
|
||||||
|
background: #1e1e1e;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-list ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-list li {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-display {
|
||||||
|
background: #252526;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-left: 4px solid #4ec9b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: #f48771;
|
||||||
|
background: #3c1e1e;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: #4ec9b0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🐾 虛擬寵物系統 - Console 互動版</h1>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h2>📖 使用說明</h2>
|
||||||
|
<p>1. 打開瀏覽器的開發者工具(F12 或 Cmd+Option+I)</p>
|
||||||
|
<p>2. 切換到 <strong>Console</strong> 標籤</p>
|
||||||
|
<p>3. 系統會自動初始化,然後你可以在 console 中輸入命令</p>
|
||||||
|
<p>4. 輸入 <code>help()</code> 查看所有可用命令</p>
|
||||||
|
<p>5. 輸入 <code>start()</code> 開始遊戲循環</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
⚠️ <strong>注意:</strong>此版本使用 Mock API(資料儲存在 localStorage),未來可切換到真實 API。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-display">
|
||||||
|
<h3>📊 系統狀態</h3>
|
||||||
|
<p id="system-status">正在載入...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="command-list">
|
||||||
|
<h3>🎮 快速命令參考</h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>init()</code> - 初始化系統</li>
|
||||||
|
<li><code>showStatus()</code> - 顯示寵物狀態</li>
|
||||||
|
<li><code>start()</code> - 啟動遊戲循環</li>
|
||||||
|
<li><code>stop()</code> - 停止遊戲循環</li>
|
||||||
|
<li><code>feed(20)</code> - 餵食</li>
|
||||||
|
<li><code>play(15)</code> - 玩耍</li>
|
||||||
|
<li><code>clean()</code> - 清理便便</li>
|
||||||
|
<li><code>heal(20)</code> - 治療</li>
|
||||||
|
<li><code>pray()</code> - 祈福</li>
|
||||||
|
<li><code>drawFortune()</code> - 抽籤</li>
|
||||||
|
<li><code>help()</code> - 查看完整幫助</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="console-area" id="console-output">
|
||||||
|
<div class="success">✅ 系統載入中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
// 動態載入模組(使用絕對路徑,從專案根目錄)
|
||||||
|
const basePath = window.location.origin
|
||||||
|
const modulePath = basePath + '/console-demo.js'
|
||||||
|
|
||||||
|
let init, showStatus, start, stop, help
|
||||||
|
|
||||||
|
// 載入模組
|
||||||
|
import(modulePath).then(module => {
|
||||||
|
init = module.init
|
||||||
|
showStatus = module.showStatus
|
||||||
|
start = module.start
|
||||||
|
stop = module.stop
|
||||||
|
help = module.help
|
||||||
|
|
||||||
|
// 掛載到 window 供 console 使用
|
||||||
|
window.init = init
|
||||||
|
window.showStatus = showStatus
|
||||||
|
window.start = start
|
||||||
|
window.stop = stop
|
||||||
|
window.help = help
|
||||||
|
|
||||||
|
// 初始化系統
|
||||||
|
initializeSystem()
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('載入模組失敗:', error)
|
||||||
|
updateStatus('❌ 載入模組失敗,請確認路徑正確')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新狀態顯示
|
||||||
|
function updateStatus(message) {
|
||||||
|
const statusEl = document.getElementById('system-status')
|
||||||
|
const outputEl = document.getElementById('console-output')
|
||||||
|
if (statusEl) statusEl.textContent = message
|
||||||
|
if (outputEl) {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
div.className = 'success'
|
||||||
|
div.textContent = message
|
||||||
|
outputEl.appendChild(div)
|
||||||
|
outputEl.scrollTop = outputEl.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化系統
|
||||||
|
async function initializeSystem() {
|
||||||
|
try {
|
||||||
|
updateStatus('正在初始化系統...')
|
||||||
|
await init()
|
||||||
|
updateStatus('✅ 系統初始化完成!輸入 help() 查看所有命令')
|
||||||
|
|
||||||
|
// 覆蓋 console.log 以顯示在頁面上
|
||||||
|
const originalLog = console.log
|
||||||
|
console.log = function(...args) {
|
||||||
|
originalLog.apply(console, args)
|
||||||
|
const outputEl = document.getElementById('console-output')
|
||||||
|
if (outputEl) {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
div.textContent = args.join(' ')
|
||||||
|
outputEl.appendChild(div)
|
||||||
|
outputEl.scrollTop = outputEl.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
updateStatus('❌ 初始化失敗: ' + error.message)
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待 DOM 載入
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// 模組載入完成後會自動初始化
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,2 @@
|
||||||
|
User-Agent: *
|
||||||
|
Disallow:
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./.nuxt/tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./.nuxt/tsconfig.server.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./.nuxt/tsconfig.shared.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./.nuxt/tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue