<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>好晴天咖啡館:陪伴的藝術</title>
<!-- 引入 Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 引入 Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<!-- 自定義樣式動畫 -->
<style>
@keyframes fade-in {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fade-in 0.8s ease-out forwards;
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up {
animation: slide-up 0.5s ease-out forwards;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* 避免與 WordPress 主題衝突,限制範圍 */
#clinic-game-root {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.5;
}
</style>
</head>
<body>
<!-- 遊戲掛載點 -->
<div id="clinic-game-root" class="relative w-full h-[600px] md:h-screen max-h-[800px] bg-stone-100 overflow-hidden text-slate-700 select-none mx-auto border border-gray-200 shadow-lg my-4 rounded-lg">
<!-- 全局背景 -->
<div id="game-bg" class="absolute inset-0 bg-cover bg-center z-0 transition-all duration-1000"
style="background-image: url('https://images.unsplash.com/photo-1554118811-1e0d58224f24?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80'); filter: blur(4px) brightness(0.95);">
</div>
<!-- 診所 Logo -->
<div class="absolute top-4 right-4 opacity-60 flex items-center gap-1 z-50 pointer-events-none">
<div class="relative w-8 h-8">
<i data-lucide="sun" class="w-8 h-8 text-yellow-400 fill-yellow-100"></i>
<i data-lucide="cloud" class="w-5 h-5 text-blue-300 fill-white absolute bottom-0 right-0"></i>
</div>
<span class="text-xs text-gray-500 font-medium tracking-widest">好晴天</span>
</div>
<!-- 內容容器 -->
<div id="game-content" class="relative z-10 w-full h-full">
<!-- JS 將會動態渲染這裡的內容 -->
</div>
</div>
<!-- 遊戲邏輯 Script -->
<script>
// --- 資料設定 ---
const scenarios = [
{
id: 1,
emotion: "tired",
text: "「最近...連起床刷牙都覺得好累,身體像是灌了鉛一樣,我是不是很懶惰...?」",
options: [
{ text: "你要不要去運動流流汗?動一動就會好了!", score: 0, feedback: "建議運動雖然是好意,但對於正處於能量低谷的患者來說,這聽起來像是額外的壓力與指責。" },
{ text: "別想太多,世界上比你慘的人多的是。", score: 0, feedback: "比較痛苦並不會讓對方好過,反而會讓他們覺得自己的痛苦不被接納,加深罪惡感。" },
{ text: "沒關係的,這不是懶惰。如果累了就多休息,我會陪著你。", score: 100, feedback: "正解!接納現狀並提供無壓力的陪伴,是當下最需要的支持。" }
]
},
{
id: 2,
emotion: "guilt",
text: "「對不起,約出來還要聽我發牢騷...我真的覺得自己好沒用,只會給別人添麻煩。」",
options: [
{ text: "你就是太閒才會胡思亂想,找點事情做吧。", score: 0, feedback: "將憂鬱症歸咎於「太閒」或「想太多」,是否定對方的疾病與感受。" },
{ text: "不要這樣說,這種想法是生病的症狀,不是真正的你。你對我很重要。", score: 100, feedback: "正解!幫助患者將「症狀」與「自我」分開,並重申他們的價值。" },
{ text: "你要正向思考啊!開心一點嘛!", score: 0, feedback: "「正向思考」對憂鬱症患者來說往往是做不到的要求,反而會讓他們因為做不到而更自責。" }
]
},
{
id: 3,
emotion: "appetite",
text: "看著桌上的蛋糕,她眉頭深鎖...「我真的一點食慾都沒有,看到食物就想吐。」",
options: [
{ text: "多少吃一點吧?不吃東西怎麼會好起來?", score: 0, feedback: "強迫進食會造成壓力。生理上的抗拒不是意志力能控制的。" },
{ text: "這家很有名耶,你不吃很浪費喔。", score: 0, feedback: "用罪惡感(浪費)來勒索患者,只會加重心理負擔。" },
{ text: "沒關係,不想吃就先不吃。或是要喝點溫水嗎?", score: 100, feedback: "正解!尊重對方的身體感覺,提供最低限度的關懷(溫水)即可。" }
]
},
{
id: 4,
emotion: "hopeless",
text: "她望向窗外,眼神空洞...「有的時候,我會覺得...如果你們沒有我也沒差,消失了是不是比較輕鬆...」",
options: [
{ text: "你別在那邊說傻話!你爸媽聽到會有多難過?", score: 0, feedback: "搬出親人來施壓,雖然是出於擔心,但往往會讓患者感到被勒索,反而不敢求助。" },
{ text: "這聽起來真的很痛苦...謝謝你願意告訴我。我們一起找醫生聊聊好嗎?", score: 100, feedback: "正解!不指責、不評價,同理對方的痛苦,並溫柔地引導尋求專業協助(自殺風險評估)。" },
{ text: "不要想這些負面的,明天就會變好了。", score: 0, feedback: "空泛的承諾無法解決當下的絕望感,反而讓人覺得被敷衍。" }
]
},
{
id: 5,
emotion: "future",
text: "「我已經吃藥吃了好久...我覺得我這輩子大概就這樣了,永遠不會好起來...」",
options: [
{ text: "要有耐心啊,你到底有沒有認真配合治療?", score: 0, feedback: "質疑患者的努力是非常傷人的,他們往往已經盡了最大的努力。" },
{ text: "我看誰誰誰後來也都好了,你一定也可以。", score: 0, feedback: "每個人的病程不同,過度的保證有時會變成壓力。" },
{ text: "康復的路真的很漫長又辛苦,辛苦你了。我們不看太遠,先過好今天就好,好嗎?", score: 100, feedback: "正解!同理治療的辛苦,並將目標縮小到「當下」,減少對未來的焦慮。" }
]
}
];
// --- 遊戲狀態 ---
let state = {
gameState: 'start', // start, playing, result
currentScenarioIndex: 0,
score: 0,
history: [],
imgError: false
};
const characterBaseUrl = "https://api.dicebear.com/9.x/avataaars/svg?seed=XiaoQing&clothing=collarAndSweater&clothingColor=e6e6e6&hair=bob&hairColor=4a312c&skinColor=f5d0c5";
// --- 輔助函式 ---
function getCharacterUrl(emotion) {
if (state.imgError) {
return `${characterBaseUrl}&eyebrows=default&mouth=smile&eyes=default`;
}
let params = "";
switch(emotion) {
case 'tired': params = "&eyebrows=sadConcerned&mouth=sad&eyes=closed"; break;
case 'guilt': params = "&eyebrows=sadConcerned&mouth=concerned&eyes=cry"; break;
case 'appetite': params = "&eyebrows=frown&mouth=grimace&eyes=squint"; break;
case 'hopeless': params = "&eyebrows=sadConcerned&mouth=serious&eyes=eyeRoll"; break;
case 'future': params = "&eyebrows=default&mouth=sad&eyes=default"; break;
default: params = "&eyebrows=default&mouth=smile";
}
return `${characterBaseUrl}${params}`;
}
// --- 渲染函式 ---
const container = document.getElementById('game-content');
function render() {
container.innerHTML = ''; // 清空內容
if (state.gameState === 'start') {
renderStart();
} else if (state.gameState === 'playing') {
renderPlaying();
} else if (state.gameState === 'result') {
renderResult();
}
// 每次渲染後重新初始化 Icons
lucide.createIcons();
}
function renderStart() {
container.innerHTML = `
<div class="flex flex-col items-center justify-center h-full px-6 bg-white/30 backdrop-blur-sm animate-fade-in">
<div class="bg-white/90 p-8 rounded-2xl shadow-xl border-2 border-yellow-100 max-w-sm text-center">
<div class="mb-4 flex justify-center">
<div class="w-16 h-16 bg-yellow-100 rounded-full flex items-center justify-center shadow-inner">
<i data-lucide="heart" class="text-yellow-500 w-8 h-8 fill-current"></i>
</div>
</div>
<h1 class="text-2xl font-bold text-slate-800 mb-2 tracking-wide">午後的陪伴練習</h1>
<p class="text-sm text-slate-500 mb-6 leading-relaxed">
這是一個關於「如何對話」的小遊戲。<br/>
你的朋友小晴最近深受憂鬱情緒所苦,<br/>
約在咖啡廳的午後,你會如何回應她?
</p>
<button onclick="startGame()" class="w-full py-3 px-6 bg-sky-400 hover:bg-sky-500 text-white rounded-full font-bold text-lg shadow-lg transform transition active:scale-95 flex items-center justify-center gap-2 cursor-pointer">
開始對話 <i data-lucide="arrow-right" class="w-5 h-5"></i>
</button>
<p class="mt-4 text-xs text-gray-400">本遊戲參考好晴天身心診所衛教理念設計</p>
</div>
</div>
`;
}
function renderPlaying() {
const scenario = scenarios[state.currentScenarioIndex];
const charUrl = getCharacterUrl(scenario.emotion);
let optionsHtml = scenario.options.map((opt, idx) => `
<button onclick="handleOptionClick(${idx})"
class="w-full bg-white/95 backdrop-blur-md p-3 rounded-xl shadow-md border border-sky-100 text-left text-sm md:text-base text-slate-700 hover:bg-sky-50 hover:border-sky-200 transition-all active:scale-98 animate-slide-up cursor-pointer flex"
style="animation-delay: ${idx * 100}ms">
<span class="font-bold text-sky-400 mr-2 flex-shrink-0">${String.fromCharCode(65 + idx)}.</span>
<span>${opt.text}</span>
</button>
`).join('');
container.innerHTML = `
<div class="w-full h-full flex flex-col justify-end pb-6 animate-fade-in relative">
<!-- 角色 (絕對定位在中間) -->
<div class="absolute top-0 left-0 w-full h-full pointer-events-none flex items-end justify-center pb-48 md:pb-40">
<div class="relative w-[85vw] max-w-sm transition-all duration-700 ease-in-out">
<img src="${charUrl}" alt="Character" class="w-full h-auto drop-shadow-2xl" onerror="handleImgError(this)">
</div>
</div>
<!-- 互動區 -->
<div class="w-full max-w-md mx-auto px-4 z-20 flex flex-col gap-3">
<div class="flex flex-col gap-2 mb-2">
${optionsHtml}
</div>
<div class="bg-slate-800/90 backdrop-blur-md p-4 rounded-2xl shadow-lg border-l-4 border-yellow-400 text-white min-h-[100px] flex flex-col justify-center">
<div class="flex items-center gap-2 mb-1">
<span class="text-yellow-300 font-bold text-sm tracking-wider">小晴</span>
</div>
<p class="text-base leading-relaxed tracking-wide font-medium">
${scenario.text}
</p>
</div>
</div>
</div>
`;
}
function renderResult() {
const percentage = state.score / 5;
let comment = "";
if (state.score === 500) comment = "太完美了!你是最溫暖的傾聽者。";
else if (state.score >= 300) comment = "做得很棒,再多一點點同理心會更好。";
else comment = "陪伴需要練習,讓我們看看哪裡可以調整。";
let historyHtml = state.history.map((item, idx) => {
const isCorrect = item.isCorrect;
const correctOpt = item.scenario.options.find(o => o.score > 0);
return `
<div class="bg-white p-5 rounded-2xl shadow-sm border-l-4 ${isCorrect ? 'border-green-400' : 'border-red-300'}">
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-bold text-slate-400 uppercase">Scenario ${idx + 1}</span>
${isCorrect
? `<span class="flex items-center text-green-500 text-xs font-bold bg-green-50 px-2 py-1 rounded-full"><i data-lucide="check-circle" class="w-3 h-3 mr-1"></i> 正確回應</span>`
: `<span class="flex items-center text-red-400 text-xs font-bold bg-red-50 px-2 py-1 rounded-full"><i data-lucide="x-circle" class="w-3 h-3 mr-1"></i> 需調整</span>`
}
</div>
<p class="text-slate-500 text-sm mb-2 italic">小晴:「${item.scenario.text.substring(0, 20)}...」</p>
<div class="mb-3">
<p class="text-slate-800 font-medium text-sm">你的選擇:</p>
<p class="text-sm ${isCorrect ? 'text-green-700' : 'text-red-400 line-through decoration-red-300'}">
${item.selectedOption.text}
</p>
</div>
${!isCorrect ? `
<div class="bg-yellow-50 p-3 rounded-xl flex gap-3 items-start">
<i data-lucide="info" class="w-5 h-5 text-yellow-500 flex-shrink-0 mt-0.5"></i>
<div class="text-xs text-slate-600 leading-relaxed">
<span class="font-bold block text-yellow-700 mb-1">為什麼這樣說不太好?</span>
${item.selectedOption.feedback}
<div class="mt-2 pt-2 border-t border-yellow-100 text-green-700">
<span class="font-bold">💡 建議說法:</span>
${correctOpt.text}
</div>
</div>
</div>
` : `
<div class="bg-green-50 p-3 rounded-xl text-xs text-green-800 leading-relaxed">
<span class="font-bold block mb-1">✨ 專家解析:</span>
${item.selectedOption.feedback}
</div>
`}
</div>
`;
}).join('');
container.innerHTML = `
<div class="h-full overflow-y-auto bg-stone-50 animate-fade-in no-scrollbar rounded-lg">
<div class="max-w-md mx-auto px-6 py-12">
<div class="bg-white p-6 rounded-3xl shadow-xl border border-slate-100 mb-8 text-center relative overflow-hidden">
<div class="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-yellow-200 via-sky-300 to-yellow-200"></div>
<h2 class="text-xl font-bold text-slate-600 mb-2">陪伴契合度</h2>
<div class="text-5xl font-black text-sky-500 mb-2">${percentage}%</div>
<p class="text-sm text-slate-400">${comment}</p>
</div>
<h3 class="text-lg font-bold text-slate-700 mb-4 flex items-center gap-2">
<i data-lucide="refresh-cw" class="w-5 h-5 text-yellow-500"></i> 對話回顧與解析
</h3>
<div class="space-y-6 pb-20">
${historyHtml}
<button onclick="startGame()" class="w-full py-4 bg-slate-800 text-white rounded-xl font-bold shadow-lg hover:bg-slate-700 transition-colors cursor-pointer">
重新練習
</button>
</div>
</div>
</div>
`;
}
// --- 邏輯控制 ---
window.startGame = function() {
state.gameState = 'playing';
state.score = 0;
state.currentScenarioIndex = 0;
state.history = [];
state.imgError = false;
render();
};
window.handleOptionClick = function(optionIdx) {
const scenario = scenarios[state.currentScenarioIndex];
const option = scenario.options[optionIdx];
const isCorrect = option.score > 0;
state.history.push({
scenario: scenario,
selectedOption: option,
isCorrect: isCorrect
});
state.score += option.score;
if (state.currentScenarioIndex < scenarios.length - 1) {
state.imgError = false;
state.currentScenarioIndex++;
render();
} else {
state.gameState = 'result';
render();
}
};
window.handleImgError = function(img) {
if (!state.imgError) {
console.log("Image load error, switching to fallback");
state.imgError = true;
// 重新渲染以更新網址,或者直接操作 DOM
img.src = getCharacterUrl(scenarios[state.currentScenarioIndex].emotion);
}
};
// 初始化
render();
</script>
</body>
</html>