good version

This commit is contained in:
王性驊 2025-11-22 23:50:28 +08:00
parent 53bdcb8a9d
commit 34adb9c15c
6 changed files with 816 additions and 49 deletions

View File

@ -175,6 +175,9 @@ function triggerDebugAction(action, payload = null) {
<div class="btn-group"> <div class="btn-group">
<button @click="unlockAllAchievements()">🏆 Unlock All Achievements</button> <button @click="unlockAllAchievements()">🏆 Unlock All Achievements</button>
</div> </div>
<div class="btn-group">
<button @click="stats.dailyPrayerCount = 0">🙏 Reset Prayer Count</button>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -2,7 +2,7 @@
<div class="action-menu"> <div class="action-menu">
<button class="icon-btn icon-clean" @click="$emit('clean')" :disabled="disabled || poopCount === 0" title="清理"></button> <button class="icon-btn icon-clean" @click="$emit('clean')" :disabled="disabled || poopCount === 0" title="清理"></button>
<button class="icon-btn icon-medicine" @click="$emit('medicine')" :disabled="disabled || !isSick" title="治療"></button> <button class="icon-btn icon-medicine" @click="$emit('medicine')" :disabled="disabled || !isSick" title="治療"></button>
<button class="icon-btn icon-training" @click="$emit('training')" :disabled="disabled" title="祈禱"></button> <button class="icon-btn icon-sleep" @click="$emit('sleep')" :disabled="disabled" title="關燈"></button>
<button class="icon-btn icon-backpack" @click="$emit('inventory')" :disabled="disabled" title="背包"></button> <button class="icon-btn icon-backpack" @click="$emit('inventory')" :disabled="disabled" title="背包"></button>
</div> </div>
</template> </template>
@ -27,7 +27,7 @@ const props = defineProps({
} }
}); });
defineEmits(['clean', 'medicine', 'training', 'inventory']); defineEmits(['clean', 'medicine', 'sleep', 'inventory']);
</script> </script>
<style scoped> <style scoped>
@ -90,30 +90,27 @@ defineEmits(['clean', 'medicine', 'training', 'inventory']);
0px 4px 0 #ff4444, 0px 6px 0 #ff4444; 0px 4px 0 #ff4444, 0px 6px 0 #ff4444;
} }
/* Training Icon (Praying Hands) */ /* Sleep Icon (Light Bulb/燈泡) */
.icon-training::before { .icon-sleep::before {
content: ''; content: '';
position: absolute; position: absolute;
width: 2px; width: 2px;
height: 2px; height: 2px;
background: #d4a574; /* 手的膚色 */ background: #ffd700; /* 燈泡顏色 */
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
box-shadow: box-shadow:
/* 光芒 - 頂部 */ /* 燈泡主體 (圓形) */
0px -8px 0 #ffcc00, -2px -6px 0 #ffd700, 0px -6px 0 #ffd700, 2px -6px 0 #ffd700,
-2px -6px 0 #ffcc00, 2px -6px 0 #ffcc00, -4px -4px 0 #ffd700, -2px -4px 0 #ffd700, 0px -4px 0 #ffd700, 2px -4px 0 #ffd700, 4px -4px 0 #ffd700,
-4px -2px 0 #ffd700, -2px -2px 0 #ffd700, 0px -2px 0 #ffd700, 2px -2px 0 #ffd700, 4px -2px 0 #ffd700,
/* 合掌的手 - 簡化版 */ -4px 0px 0 #ffd700, -2px 0px 0 #ffd700, 0px 0px 0 #ffd700, 2px 0px 0 #ffd700, 4px 0px 0 #ffd700,
-2px -4px 0 #d4a574, 0px -4px 0 #d4a574, 2px -4px 0 #d4a574, -2px 2px 0 #ffd700, 0px 2px 0 #ffd700, 2px 2px 0 #ffd700,
-2px -2px 0 #d4a574, 0px -2px 0 #d4a574, 2px -2px 0 #d4a574, /* 燈泡底部 (螺旋) */
-2px 0px 0 #d4a574, 0px 0px 0 #d4a574, 2px 0px 0 #d4a574, -1px 4px 0 #8B4513, 0px 4px 0 #8B4513, 1px 4px 0 #8B4513,
0px 2px 0 #d4a574, 0px 4px 0 #d4a574, /* 光線 (向下) */
0px 6px 0 #ffd700, 0px 8px 0 #ffd700;
/* 光芒 - 左右 */
-6px -2px 0 #ffcc00, 6px -2px 0 #ffcc00,
-6px 0px 0 #ffcc00, 6px 0px 0 #ffcc00;
} }
/* Backpack Icon */ /* Backpack Icon */

View File

@ -0,0 +1,637 @@
<template>
<div class="deity-temple-overlay">
<div class="temple-container" @click.stop>
<!-- Header -->
<div class="temple-header">
<button class="close-btn" @click="$emit('close')">×</button>
<h2 class="temple-title">神廟</h2>
</div>
<!-- Scrollable Content -->
<div class="temple-content">
<!-- Deity Display -->
<div class="deity-display">
<div class="deity-icon" :class="currentDeity.icon"></div>
<div class="deity-name">{{ currentDeity.name }}</div>
<div class="deity-subtitle">{{ currentDeity.personality }}</div>
</div>
<!-- Dialogue Bubble -->
<div class="dialogue-bubble">
<p>{{ currentDialogue }}</p>
</div>
<!-- Favor Progress -->
<div class="favor-section">
<div class="favor-label">
好感度: {{ favorStars }} ({{ currentFavor }}/100)
</div>
<div class="favor-bar">
<div class="favor-fill" :style="{ width: currentFavor + '%' }"></div>
</div>
</div>
<!-- Active Buffs -->
<div class="buffs-section" v-if="activeBuffs.length > 0">
<div class="buff-title">當前加成</div>
<div class="buff-list">
<div v-for="buff in activeBuffs" :key="buff.type" class="buff-item">
{{ buff.description }}
</div>
</div>
</div>
<!-- Prayer Options -->
<div class="prayer-section">
<!-- 祈福按鈕 -->
<button
class="prayer-btn"
:disabled="dailyPrayerCount >= 3"
@click="handlePrayer"
>
🙏 祈福 (今日 {{ dailyPrayerCount }}/3)
</button>
<div class="prayer-divider"></div>
<div class="prayer-options">
<!-- 擲筊選項 -->
<button
class="prayer-option"
@click="handlePrayerSelect('jiaobei')"
>
<div class="option-icon icon-jiaobei"></div>
<span class="option-label">擲筊</span>
</button>
<!-- 求籤選項 -->
<button
class="prayer-option"
@click="handlePrayerSelect('fortune')"
>
<div class="option-icon icon-fortune"></div>
<span class="option-label">求籤</span>
</button>
</div>
</div>
</div>
<!-- Bottom Actions -->
<div class="bottom-actions">
<button class="action-btn" @click="showDeitySelector = !showDeitySelector">
切換神明
</button>
</div>
<!-- Deity Selector (Overlay on top of content) -->
<div v-if="showDeitySelector" class="deity-selector-overlay" @click="showDeitySelector = false">
<div class="deity-selector-menu" @click.stop>
<div class="selector-header">選擇神明</div>
<div
v-for="deity in DEITIES"
:key="deity.id"
class="deity-option"
:class="{ active: deity.id === currentDeity.id }"
@click="selectDeity(deity.id)"
>
<div class="deity-icon-small" :class="deity.icon"></div>
<span>{{ deity.name }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
const props = defineProps({
deityFavors: {
type: Object,
required: true
},
currentDeityId: {
type: String,
default: 'mazu'
},
dailyPrayerCount: {
type: Number,
default: 0
}
});
const emit = defineEmits(['close', 'prayer', 'change-deity', 'prayer-select']);
// Deity Data
const DEITIES = [
{
id: 'mazu',
name: '媽祖',
personality: '溫柔守護',
buffs: {
gameSuccessRate: 0.1,
sicknessReduction: 0.15
},
buffDescriptions: [
'小遊戲成功率 +10%',
'生病機率 -15%'
],
dialogues: [
"好孩子,媽祖保佑你平安喔",
"海上無風浪,心中有媽祖",
"要好好照顧寵物啊"
],
icon: 'deity-mazu'
},
{
id: 'earthgod',
name: '土地公',
personality: '碎念管家',
buffs: {
itemDropRate: 0.2,
resourceGain: 0.15
},
buffDescriptions: [
'掉落物品機率 +20%',
'資源獲得 +15%'
],
dialogues: [
"又來啦?今天有好好餵寵物嗎?",
"欸,地上那個便便怎麼不清一清",
"拜我就對了,土地公最靈驗"
],
icon: 'deity-earthgod'
},
{
id: 'matchmaker',
name: '月老',
personality: '八卦熱情',
buffs: {
happinessRecovery: 0.25,
goodEventRate: 0.1
},
buffDescriptions: [
'Happiness 回復 +25%',
'好事件機率 +10%'
],
dialogues: [
"哎呀~你的寵物今天心情不錯喔",
"要不要幫你牽條紅線?咦,寵物也需要嗎",
"姻緣天注定,開心最重要!"
],
icon: 'deity-matchmaker'
},
{
id: 'wenchang',
name: '文昌帝君',
personality: '嚴肅學者',
buffs: {
intGrowth: 0.3,
guessingReward: 0.2
},
buffDescriptions: [
'INT 成長 +30%',
'猜拳獎勵 +20%'
],
dialogues: [
"學海無涯,勤能補拙",
"多動腦,少偷懶",
"智慧是一切的根本"
],
icon: 'deity-wenchang'
},
{
id: 'guanyin',
name: '觀音菩薩',
personality: '慈悲救苦',
buffs: {
healthRecovery: 0.2,
autoHeal: true
},
buffDescriptions: [
'Health 回復 +20%',
'自動治療 (1次/天)'
],
dialogues: [
"阿彌陀佛,施主請安心",
"救苦救難,觀音保佑",
"..."
],
icon: 'deity-guanyin'
}
];
const showDeitySelector = ref(false);
const currentDialogue = ref('');
const activeBuffs = ref([]);
const currentDeity = computed(() => {
return DEITIES.find(d => d.id === props.currentDeityId) || DEITIES[0];
});
const currentFavor = computed(() => {
return props.deityFavors[props.currentDeityId] || 0;
});
const favorStars = computed(() => {
const level = Math.floor(currentFavor.value / 20);
return '★'.repeat(level) + '☆'.repeat(5 - level);
});
function selectDeity(deityId) {
emit('change-deity', deityId);
showDeitySelector.value = false;
updateDialogue();
}
function updateDialogue() {
const dialogues = currentDeity.value.dialogues;
currentDialogue.value = dialogues[Math.floor(Math.random() * dialogues.length)];
}
function handlePrayer() {
if (props.dailyPrayerCount >= 3) return;
emit('prayer', currentDeity.value.id);
updateDialogue();
// Update active buffs display
activeBuffs.value = currentDeity.value.buffDescriptions.map(desc => ({
type: currentDeity.value.id,
description: desc
}));
}
function handlePrayerSelect(mode) {
emit('prayer-select', mode);
}
onMounted(() => {
updateDialogue();
// Load active buffs if favor > 0
if (currentFavor.value > 0) {
activeBuffs.value = currentDeity.value.buffDescriptions.map(desc => ({
type: currentDeity.value.id,
description: desc
}));
}
});
</script>
<style scoped>
.deity-temple-overlay {
position: absolute; /* Absolute to parent (DeviceScreen) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.85);
z-index: 50; /* Same as PetInfoScreen */
display: flex;
align-items: center;
justify-content: center;
}
.temple-container {
background: linear-gradient(to bottom, #8B4513 0%, #A0522D 100%);
border: 3px solid #654321;
border-radius: 6px;
width: calc(100% - 8px);
max-width: 198px; /* Fit within 210px screen with padding */
height: calc(100% - 8px);
max-height: 152px; /* Fit within 160px screen with padding */
box-shadow: 0 4px 8px rgba(0,0,0,0.5);
position: relative;
display: flex;
flex-direction: column;
overflow: hidden; /* Contain children */
pointer-events: auto; /* Ensure container can receive clicks */
}
.temple-container::-webkit-scrollbar {
width: 6px;
}
.temple-container::-webkit-scrollbar-track {
background: rgba(0,0,0,0.2);
border-radius: 3px;
}
.temple-container::-webkit-scrollbar-thumb {
background: #FFD700;
border-radius: 3px;
}
.temple-header {
background: #654321;
padding: 6px;
text-align: center;
border-bottom: 2px solid #4a3216;
flex-shrink: 0; /* Don't shrink header */
position: relative;
}
.temple-content {
flex: 1;
overflow-y: auto; /* Scrollable content area */
overflow-x: hidden;
padding-bottom: 8px;
-webkit-overflow-scrolling: touch;
}
.close-btn {
position: absolute;
right: 4px;
top: 4px;
background: #8B0000;
border: 2px solid #fff;
color: #fff;
width: 18px;
height: 18px;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
line-height: 1;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.temple-title {
font-family: 'DotGothic16', monospace;
color: #FFD700;
margin: 0;
font-size: 12px;
}
.deity-display { padding: 6px; text-align: center; }
.deity-icon { width: 32px; height: 32px; margin: 0 auto 4px; background: #FFD700; border: 2px solid #FFA500; border-radius: 50%; }
.deity-name { font-family: 'DotGothic16', monospace; font-size: 11px; color: #FFD700; font-weight: bold; margin-bottom: 2px; }
.deity-subtitle { font-family: 'DotGothic16', monospace; font-size: 9px; color: #DEB887; }
.dialogue-bubble {
margin: 0 6px 6px;
background: #FFF8DC;
border: 2px solid #654321;
border-radius: 4px;
padding: 6px;
position: relative;
min-height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.dialogue-bubble::before {
content: ''; position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
border-width: 0 6px 6px 6px; border-style: solid; border-color: transparent transparent #654321 transparent;
}
.dialogue-bubble p { font-family: 'DotGothic16', monospace; font-size: 9px; color: #333; margin: 0; text-align: center; line-height: 1.3; }
.favor-section, .buffs-section { padding: 0 6px 6px; }
.prayer-section {
padding: 0 6px 6px;
position: relative;
z-index: 5;
display: flex;
flex-direction: column;
gap: 4px;
}
.prayer-btn {
width: 100%;
padding: 6px;
background: #FFD700;
border: 2px solid #FFA500;
border-radius: 4px;
font-family: 'DotGothic16', monospace;
font-size: 10px;
font-weight: bold;
color: #8B4513;
cursor: pointer;
position: relative;
z-index: 10;
user-select: none;
-webkit-user-select: none;
}
.prayer-btn:hover:not(:disabled) {
background: #FFA500;
transform: translateY(-1px);
}
.prayer-btn:active:not(:disabled) {
transform: translateY(0);
}
.prayer-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
background: #ccc;
border-color: #999;
color: #666;
}
.prayer-divider {
font-family: 'DotGothic16', monospace;
font-size: 8px;
color: #DEB887;
text-align: center;
margin: 2px 0;
}
.bottom-actions {
padding: 4px 6px;
flex-shrink: 0;
border-top: 2px solid #4a3216;
background: rgba(101, 67, 33, 0.5);
position: relative;
z-index: 5;
}
.favor-label { font-family: 'DotGothic16', monospace; font-size: 9px; color: #FFD700; margin-bottom: 2px; }
.favor-bar { height: 8px; background: rgba(0,0,0,0.3); border: 1px solid #654321; border-radius: 4px; overflow: hidden; }
.favor-fill { height: 100%; background: linear-gradient(to right, #FFD700, #FFA500); transition: width 0.3s ease; }
.buffs-section { background: rgba(255,255,255,0.1); border-radius: 3px; padding: 4px; }
.buff-title { font-family: 'DotGothic16', monospace; font-size: 9px; color: #FFD700; margin-bottom: 2px; }
.buff-list { font-family: 'DotGothic16', monospace; font-size: 8px; color: #FFF8DC; }
.prayer-options {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
margin-bottom: 4px;
}
.prayer-option {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
background: rgba(255, 255, 255, 0.9);
border: 2px solid #654321;
border-radius: 4px;
padding: 4px 6px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
min-width: 50px;
position: relative;
z-index: 10;
user-select: none;
-webkit-user-select: none;
}
.prayer-option:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
background: #FFD700;
border-color: #FFA500;
}
.prayer-option:active {
transform: translateY(0);
}
.option-icon {
width: 16px;
height: 16px;
position: relative;
transform: scale(0.6);
}
.option-label {
font-family: 'DotGothic16', monospace;
font-size: 8px;
font-weight: bold;
color: #333;
}
.prayer-count-info {
font-family: 'DotGothic16', monospace;
font-size: 8px;
color: #FFD700;
text-align: center;
margin-top: 2px;
}
.action-btn {
width: 100%; padding: 6px; background: #654321; border: 2px solid #4a3216; border-radius: 3px;
font-family: 'DotGothic16', monospace; font-size: 9px; color: #FFD700; cursor: pointer;
position: relative;
z-index: 10;
user-select: none;
-webkit-user-select: none;
}
.action-btn:active {
transform: translateY(1px);
background: #4a3216;
}
/* Deity Selector Overlay */
.deity-selector-overlay {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.7);
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
}
.deity-selector-menu {
background: #4a3216;
border: 2px solid #FFD700;
border-radius: 4px;
padding: 6px;
width: 85%;
max-height: 75%;
overflow-y: auto;
}
.selector-header {
text-align: center; color: #FFD700; font-family: 'DotGothic16', monospace; margin-bottom: 6px; font-weight: bold; font-size: 10px;
}
.deity-option {
display: flex; align-items: center; gap: 6px; padding: 4px; margin: 3px 0;
background: #654321; border: 1px solid transparent; border-radius: 3px; cursor: pointer;
position: relative;
z-index: 25;
user-select: none;
-webkit-user-select: none;
}
.deity-option:hover {
border-color: #FFD700;
background: #7a5238;
}
.deity-option:active {
transform: scale(0.98);
}
.deity-option.active { background: #8B4513; border-color: #FFD700; }
.deity-option span { font-family: 'DotGothic16', monospace; font-size: 9px; color: #FFD700; }
.deity-icon-small { width: 16px; height: 16px; background: #FFD700; border: 1px solid #FFA500; border-radius: 50%; }
/* Deity Icons - Pixel Art Placeholders */
.deity-mazu,
.deity-earthgod,
.deity-matchmaker,
.deity-wenchang,
.deity-guanyin {
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.deity-mazu::after { content: '媽'; }
.deity-earthgod::after { content: '土'; }
.deity-matchmaker::after { content: '月'; }
.deity-wenchang::after { content: '文'; }
.deity-guanyin::after { content: '觀'; }
/* Prayer Option Icons */
.icon-jiaobei::before {
content: '';
position: absolute;
width: 2px;
height: 2px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow:
/* 左邊筊杯 */
-7px -4px 0 #ff5252, -5px -4px 0 #ff5252, -3px -4px 0 #ff5252,
-9px -2px 0 #ff5252, -7px -2px 0 #ff8a80, -5px -2px 0 #ff5252, -3px -2px 0 #ff5252, -1px -2px 0 #ff5252,
-9px 0px 0 #ff5252, -7px 0px 0 #ff5252, -5px 0px 0 #ff5252, -3px 0px 0 #ff5252, -1px 0px 0 #ff5252,
-7px 2px 0 #ff5252, -5px 2px 0 #ff5252, -3px 2px 0 #ff5252,
-5px 4px 0 #d32f2f,
/* 右邊筊杯 */
3px -4px 0 #ff5252, 5px -4px 0 #ff5252, 7px -4px 0 #ff5252,
1px -2px 0 #ff5252, 3px -2px 0 #ff5252, 5px -2px 0 #ff5252, 7px -2px 0 #ff8a80, 9px -2px 0 #ff5252,
1px 0px 0 #ff5252, 3px 0px 0 #ff5252, 5px 0px 0 #ff5252, 7px 0px 0 #ff5252, 9px 0px 0 #ff5252,
3px 2px 0 #ff5252, 5px 2px 0 #ff5252, 7px 2px 0 #ff5252,
5px 4px 0 #d32f2f;
}
.icon-fortune::before {
content: '';
position: absolute;
width: 2px;
height: 2px;
background: #8B4513;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow:
/* 籤筒本體 */
-4px -4px 0 #8B4513, -2px -4px 0 #8B4513, 0px -4px 0 #8B4513, 2px -4px 0 #8B4513, 4px -4px 0 #8B4513,
-4px -2px 0 #8B4513, -2px -2px 0 #8B4513, 0px -2px 0 #8B4513, 2px -2px 0 #8B4513, 4px -2px 0 #8B4513,
-4px 0px 0 #8B4513, -2px 0px 0 #8B4513, 0px 0px 0 #8B4513, 2px 0px 0 #8B4513, 4px 0px 0 #8B4513,
-4px 2px 0 #8B4513, -2px 2px 0 #8B4513, 0px 2px 0 #8B4513, 2px 2px 0 #8B4513, 4px 2px 0 #8B4513,
-4px 4px 0 #8B4513, -2px 4px 0 #8B4513, 0px 4px 0 #8B4513, 2px 4px 0 #8B4513, 4px 4px 0 #8B4513,
/* 突出的籤條 */
-2px -8px 0 #d4522e, 0px -8px 0 #d4522e,
-2px -6px 0 #d4522e, 0px -6px 0 #d4522e,
2px -6px 0 #d4522e;
}
</style>

View File

@ -6,7 +6,7 @@
@info="showPetInfo = !showPetInfo" @info="showPetInfo = !showPetInfo"
@feed="$emit('action', 'feed')" @feed="$emit('action', 'feed')"
@playMenu="showPlayMenu = true" @playMenu="showPlayMenu = true"
@sleep="$emit('action', 'sleep')" @temple="showTemple = true"
/> />
<!-- Stats Dashboard (Toggelable) --> <!-- Stats Dashboard (Toggelable) -->
@ -177,12 +177,11 @@
</div> </div>
</div> </div>
<!-- Prayer Menu (覆蓋整個遊戲區域) --> <!-- 關燈黑色遮罩 (原本 PrayerMenu 的位置) -->
<PrayerMenu <div
v-if="showPrayerMenu" v-if="state === 'sleep'"
@select="handlePrayerSelect" class="dark-overlay-fullscreen"
@close="showPrayerMenu = false" ></div>
/>
<!-- Jiaobei Animation (覆蓋遊戲區域) --> <!-- Jiaobei Animation (覆蓋遊戲區域) -->
<JiaobeiAnimation <JiaobeiAnimation
@ -268,6 +267,18 @@
@update:inventory="handleInventoryUpdate" @update:inventory="handleInventoryUpdate"
/> />
<!-- Deity Temple -->
<DeityTemple
v-if="showTemple"
:deityFavors="stats?.deityFavors || {}"
:currentDeityId="stats?.currentDeity || 'mazu'"
:dailyPrayerCount="stats?.dailyPrayerCount || 0"
@close="showTemple = false"
@prayer="handlePrayer"
@change-deity="handleChangeDeity"
@prayer-select="handlePrayerSelect"
/>
<!-- Action Menu (Bottom) --> <!-- Action Menu (Bottom) -->
<ActionMenu <ActionMenu
:disabled="stage === 'egg'" :disabled="stage === 'egg'"
@ -276,7 +287,7 @@
:isSick="state === 'sick'" :isSick="state === 'sick'"
@clean="$emit('action', 'clean')" @clean="$emit('action', 'clean')"
@medicine="$emit('action', 'medicine')" @medicine="$emit('action', 'medicine')"
@training="showPrayerMenu = true" @sleep="$emit('action', 'sleep')"
@inventory="showInventory = !showInventory" @inventory="showInventory = !showInventory"
/> />
@ -286,17 +297,17 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'; import { ref, computed, onMounted, onUnmounted, watch, nextTick, toRef } from 'vue';
import { SPRITE_PRESETS_FLAT as SPRITE_PRESETS, SPRITE_PRESETS as FULL_PRESETS } from '../data/petPresets.js'; import { SPRITE_PRESETS_FLAT as SPRITE_PRESETS, SPRITE_PRESETS as FULL_PRESETS } from '../data/petPresets.js';
import { FOOD_OPTIONS } from '../data/foodOptions.js'; import { FOOD_OPTIONS } from '../data/foodOptions.js';
import StatsBar from './StatsBar.vue'; import StatsBar from './StatsBar.vue';
import ActionMenu from './ActionMenu.vue'; import ActionMenu from './ActionMenu.vue';
import TopMenu from './TopMenu.vue'; import TopMenu from './TopMenu.vue';
import PrayerMenu from './PrayerMenu.vue';
import JiaobeiAnimation from './JiaobeiAnimation.vue'; import JiaobeiAnimation from './JiaobeiAnimation.vue';
import FortuneStickAnimation from './FortuneStickAnimation.vue'; import FortuneStickAnimation from './FortuneStickAnimation.vue';
import FortuneResult from './FortuneResult.vue'; import FortuneResult from './FortuneResult.vue';
import PetInfoScreen from './PetInfoScreen.vue'; import PetInfoScreen from './PetInfoScreen.vue';
import DeityTemple from './DeityTemple.vue';
import InventoryScreen from './InventoryScreen.vue'; import InventoryScreen from './InventoryScreen.vue';
import PlayMenu from './PlayMenu.vue'; import PlayMenu from './PlayMenu.vue';
import GuessingGame from './GuessingGame.vue'; import GuessingGame from './GuessingGame.vue';
@ -341,8 +352,26 @@ const props = defineProps({
const emit = defineEmits(['update:state', 'action']); const emit = defineEmits(['update:state', 'action']);
// Prayer Menu State // Create local ref for stats to match usage pattern in component
const showPrayerMenu = ref(false); const stats = toRef(props, 'stats');
// Helper function to temporarily change pet state
function triggerState(tempState, duration) {
const previousState = props.state;
emit('update:state', tempState);
// Store timeout ID to potentially clear it if needed
const timeoutId = setTimeout(() => {
// Revert to previous state (or idle if previous was sleep)
const revertState = previousState === 'sleep' ? 'idle' : (previousState === 'dead' ? 'dead' : 'idle');
emit('update:state', revertState);
}, duration);
// Return timeout ID in case we need to clear it
return timeoutId;
}
// Prayer State
const fortuneMode = ref('jiaobei'); const fortuneMode = ref('jiaobei');
const showJiaobeiAnimation = ref(false); const showJiaobeiAnimation = ref(false);
const showFortuneStick = ref(false); const showFortuneStick = ref(false);
@ -354,6 +383,7 @@ const lastJiaobeiResult = ref(null); // 記錄最後的擲筊結果用於招魂
const showPetInfo = ref(false); const showPetInfo = ref(false);
const showInventory = ref(false); const showInventory = ref(false);
const showTemple = ref(false);
const showPlayMenu = ref(false); const showPlayMenu = ref(false);
const currentGame = ref(''); // '', 'training', 'guessing', 'ball' const currentGame = ref(''); // '', 'training', 'guessing', 'ball'
const inventory = ref(new Array(16).fill(null)); const inventory = ref(new Array(16).fill(null));
@ -395,7 +425,8 @@ watch(() => props.debugAction, (action) => {
}); });
const handlePrayerSelect = (mode) => { const handlePrayerSelect = (mode) => {
showPrayerMenu.value = false; // Close temple when selecting prayer mode
showTemple.value = false;
if (mode === 'jiaobei') { if (mode === 'jiaobei') {
fortuneMode.value = 'normal'; fortuneMode.value = 'normal';
@ -482,9 +513,13 @@ function handleGameComplete(won) {
if (gameType === 'training') { if (gameType === 'training') {
stats.value.str = (stats.value.str || 0) + statGain; stats.value.str = (stats.value.str || 0) + statGain;
// Training also gives Deity Favor slightly? Maybe not.
} else if (gameType === 'guessing') { } else if (gameType === 'guessing') {
stats.value.int = (stats.value.int || 0) + statGain; let intGain = statGain;
// Deity Buff: - INT +30%
if (stats.value.currentDeity === 'wenchang' && stats.value.deityFavors?.wenchang > 0) {
intGain = Math.ceil(intGain * 1.3);
}
stats.value.int = (stats.value.int || 0) + intGain;
} }
} }
} }
@ -530,6 +565,7 @@ function handleStickComplete(number) {
// //
consecutiveSaintCount.value = 0; consecutiveSaintCount.value = 0;
fortuneMode.value = 'fortune'; //
showJiaobeiAnimation.value = true; showJiaobeiAnimation.value = true;
} }
@ -602,6 +638,7 @@ function handleRetryFortune() {
// //
showJiaobeiAnimation.value = false; showJiaobeiAnimation.value = false;
consecutiveSaintCount.value = 0; consecutiveSaintCount.value = 0;
fortuneMode.value = 'fortune'; //
showFortuneStick.value = true; showFortuneStick.value = true;
} }
@ -659,6 +696,56 @@ function handleCloseResult() {
fortuneMode.value = 'normal'; fortuneMode.value = 'normal';
} }
function handlePrayer(deityId) {
// Initialize deityFavors if it doesn't exist
if (!stats.value.deityFavors) {
stats.value.deityFavors = {
mazu: 0,
earthgod: 0,
matchmaker: 0,
wenchang: 0,
guanyin: 0
};
}
// Initialize the specific deity favor if it doesn't exist
if (stats.value.deityFavors[deityId] === undefined) {
stats.value.deityFavors[deityId] = 0;
}
// Increase favor for the deity
stats.value.deityFavors[deityId] = Math.min(100, (stats.value.deityFavors[deityId] || 0) + 5);
// Increase daily prayer count
stats.value.dailyPrayerCount = (stats.value.dailyPrayerCount || 0) + 1;
// Apply temporary buff based on deity
// (Buffs would be applied in the game loop or stat calculations)
console.log(`Prayed to ${deityId}, favor: ${stats.value.deityFavors[deityId]}, count: ${stats.value.dailyPrayerCount}`);
}
function handleChangeDeity(deityId) {
// Initialize deityFavors if it doesn't exist
if (!stats.value.deityFavors) {
stats.value.deityFavors = {
mazu: 0,
earthgod: 0,
matchmaker: 0,
wenchang: 0,
guanyin: 0
};
}
// Initialize the specific deity favor if it doesn't exist
if (stats.value.deityFavors[deityId] === undefined) {
stats.value.deityFavors[deityId] = 0;
}
stats.value.currentDeity = deityId;
console.log(`Changed deity to ${deityId}`);
}
function handleUseItem(item) { function handleUseItem(item) {
console.log('Used item:', item.name); console.log('Used item:', item.name);
@ -1549,7 +1636,7 @@ defineExpose({
50% { opacity: 1; } 50% { opacity: 1; }
} }
/* Dark Overlay (關燈效果) */ /* Dark Overlay (關燈效果 - 覆蓋整個遊戲容器) */
.dark-overlay { .dark-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
@ -1559,6 +1646,19 @@ defineExpose({
background: #000; background: #000;
z-index: 7; /* Above poop (5), Below ZZZ (10) */ z-index: 7; /* Above poop (5), Below ZZZ (10) */
pointer-events: none; pointer-events: none;
border-radius: 10px; /* 與螢幕邊框一致 */
}
/* Fullscreen Dark Overlay (原本 PrayerMenu 的位置) */
.dark-overlay-fullscreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
z-index: 100; /* Same as PrayerMenu was */
pointer-events: none;
} }
/* Sleep ZZZ */ /* Sleep ZZZ */
@ -1570,7 +1670,7 @@ defineExpose({
z-index: 10; /* Above dark overlay */ z-index: 10; /* Above dark overlay */
} }
.sleep-zzz.dark-mode { .sleep-zzz.dark-mode {
color: #fff; /* 顏色顛倒:在黑色背景下變白色 */ color: #fff !important; /* 強制白色:在黑色背景下變白色 */
text-shadow: 0 0 4px rgba(255, 255, 255, 0.8); /* 添加發光效果讓它更明顯 */ text-shadow: 0 0 4px rgba(255, 255, 255, 0.8); /* 添加發光效果讓它更明顯 */
} }
.sleep-zzz span { .sleep-zzz span {

View File

@ -3,7 +3,7 @@
<button class="icon-btn icon-stats" @click="$emit('info')" title="Status"></button> <button class="icon-btn icon-stats" @click="$emit('info')" title="Status"></button>
<button class="icon-btn icon-feed" @click="$emit('feed')" :disabled="disabled" title="Feed"></button> <button class="icon-btn icon-feed" @click="$emit('feed')" :disabled="disabled" title="Feed"></button>
<button class="icon-btn icon-play" @click="$emit('playMenu')" :disabled="disabled" title="Play"></button> <button class="icon-btn icon-play" @click="$emit('playMenu')" :disabled="disabled" title="Play"></button>
<button class="icon-btn icon-sleep" @click="$emit('sleep')" :disabled="disabled" title="Sleep"></button> <button class="icon-btn icon-temple" @click="$emit('temple')" :disabled="disabled" title="Temple"></button>
</div> </div>
</template> </template>
@ -15,7 +15,7 @@ const props = defineProps({
} }
}); });
defineEmits(['info', 'feed', 'playMenu', 'sleep']); defineEmits(['info', 'feed', 'playMenu', 'temple']);
</script> </script>
<style scoped> <style scoped>
@ -96,23 +96,26 @@ defineEmits(['info', 'feed', 'playMenu', 'sleep']);
-2px 4px 0 #4444ff, 0px 4px 0 #4444ff, 2px 4px 0 #4444ff; -2px 4px 0 #4444ff, 0px 4px 0 #4444ff, 2px 4px 0 #4444ff;
} }
/* Sleep Icon (Moon) */ /* Temple Icon (廟宇) */
.icon-sleep::before { .icon-temple::before {
content: ''; content: '';
position: absolute; position: absolute;
width: 2px; width: 2px;
height: 2px; height: 2px;
background: #ffcc00; background: #D2691E;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
box-shadow: box-shadow:
0px -6px 0 #ffcc00, 2px -6px 0 #ffcc00, /* 屋頂 */
-2px -4px 0 #ffcc00, 0px -4px 0 #ffcc00, 2px -4px 0 #ffcc00, 4px -4px 0 #ffcc00, -4px -6px 0 #8B4513, -2px -6px 0 #8B4513, 0px -6px 0 #8B4513, 2px -6px 0 #8B4513, 4px -6px 0 #8B4513,
-2px -2px 0 #ffcc00, 0px -2px 0 #ffcc00, 4px -2px 0 #ffcc00, /* 屋簷 */
-2px 0px 0 #ffcc00, 0px 0px 0 #ffcc00, 4px 0px 0 #ffcc00, -6px -4px 0 #D2691E, -4px -4px 0 #D2691E, -2px -4px 0 #D2691E, 0px -4px 0 #D2691E, 2px -4px 0 #D2691E, 4px -4px 0 #D2691E, 6px -4px 0 #D2691E,
-2px 2px 0 #ffcc00, 0px 2px 0 #ffcc00, 4px 2px 0 #ffcc00, /* 柱子與牆 */
-2px 4px 0 #ffcc00, 0px 4px 0 #ffcc00, 2px 4px 0 #ffcc00, 4px 4px 0 #ffcc00, -4px -2px 0 #8B4513, 4px -2px 0 #8B4513,
0px 6px 0 #ffcc00, 2px 6px 0 #ffcc00; -4px 0px 0 #8B4513, -2px 0px 0 #FFD700, 0px 0px 0 #FFD700, 2px 0px 0 #FFD700, 4px 0px 0 #8B4513,
-4px 2px 0 #8B4513, 4px 2px 0 #8B4513,
/* 底座 */
-4px 4px 0 #654321, -2px 4px 0 #654321, 0px 4px 0 #654321, 2px 4px 0 #654321, 4px 4px 0 #654321;
} }
</style> </style>

View File

@ -30,7 +30,17 @@ export function usePetSystem() {
dex: 0, // 敏捷 (Catch Ball) dex: 0, // 敏捷 (Catch Ball)
generation: 1, // 輪迴世代 generation: 1, // 輪迴世代
deityFavor: 0, // 神明好感度 deityFavor: 0, // 神明好感度
destiny: null // 天生命格 (Object) destiny: null, // 天生命格 (Object)
// Deity System
currentDeity: 'mazu',
deityFavors: {
mazu: 0,
earthgod: 0,
matchmaker: 0,
wenchang: 0,
guanyin: 0
},
dailyPrayerCount: 0
}); });
const achievements = ref([ const achievements = ref([
@ -189,10 +199,14 @@ export function usePetSystem() {
// Sickness Check (更低的生病機率) // Sickness Check (更低的生病機率)
// Destiny Effect: Purification (淨化) - Sickness chance -20% // Destiny Effect: Purification (淨化) - Sickness chance -20%
// Deity Buff: 媽祖 - Sickness chance -15%
let sickChance = 0.1; let sickChance = 0.1;
if (stats.value.destiny?.id === 'purification') { if (stats.value.destiny?.id === 'purification') {
sickChance *= 0.8; sickChance *= 0.8;
} }
if (stats.value.currentDeity === 'mazu' && stats.value.deityFavors?.mazu > 0) {
sickChance *= 0.85;
}
if (stats.value.health < 30 && state.value !== 'sick') { if (stats.value.health < 30 && state.value !== 'sick') {
if (Math.random() < sickChance) { if (Math.random() < sickChance) {
@ -202,8 +216,21 @@ export function usePetSystem() {
// Health Recovery (健康值可以緩慢恢復) // Health Recovery (健康值可以緩慢恢復)
// 如果沒有便便、飢餓值和快樂值都高,健康值會緩慢恢復 // 如果沒有便便、飢餓值和快樂值都高,健康值會緩慢恢復
let healthRecovery = 0.05;
// Deity Buff: 觀音 - Health 回復 +20%
if (stats.value.currentDeity === 'guanyin' && stats.value.deityFavors?.guanyin > 0) {
healthRecovery *= 1.2;
}
// Deity Buff: 月老 - Happiness 回復 +25%
if (stats.value.currentDeity === 'matchmaker' && stats.value.deityFavors?.matchmaker > 0) {
// Apply to happiness decay reduction (slower decay = faster recovery)
happinessDecay *= 0.75;
}
if (stats.value.poopCount === 0 && stats.value.hunger > 50 && stats.value.happiness > 50 && stats.value.health < 100 && state.value !== 'sick') { if (stats.value.poopCount === 0 && stats.value.hunger > 50 && stats.value.happiness > 50 && stats.value.health < 100 && state.value !== 'sick') {
stats.value.health = Math.min(100, stats.value.health + 0.05); stats.value.health = Math.min(100, stats.value.health + healthRecovery);
} }
// Death Check (移除死亡機制,依照之前的討論) // Death Check (移除死亡機制,依照之前的討論)