Lashtw's picture
Upload 3 files
d5447a5 verified
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>數學探險島 - 海風港灣</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap" rel="stylesheet">
<!-- MathJax for rendering formulas -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
body {
font-family: 'Noto Sans TC', sans-serif;
overflow: hidden;
background-image: url('https://i.meee.com.tw/TDC4EZE.png');
background-size: cover;
background-position: center;
}
#game-container-bg {
background-image: url('https://i.meee.com.tw/vT2QeZF.png');
background-size: cover;
background-position: center;
}
.seaweed {
position: absolute;
bottom: -10px;
pointer-events: none;
filter: brightness(0.9);
}
.fish {
position: absolute;
cursor: pointer;
transition: transform 0.2s ease, opacity 0.15s ease;
}
.fish:hover {
transform: scale(1.1);
}
.catch-feedback {
position: absolute;
font-size: 2rem;
font-weight: bold;
pointer-events: none;
animation: fadeUp 1s forwards;
text-shadow: 1px 1px 2px black;
}
@keyframes fadeUp {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-50px); }
}
.loader {
border: 8px solid #f3f3f3;
border-radius: 50%;
border-top: 8px solid #3498db;
width: 60px;
height: 60px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.market-choice.selected {
border-color: #facc15; /* yellow-400 */
box-shadow: 0 0 15px #facc15;
}
.secret-bg {
background-image: url('https://i.meee.com.tw/EKZpYKI.png');
background-size: cover;
background-position: center;
}
</style>
</head>
<body class="flex items-center justify-center h-screen">
<div id="container" class="w-full max-w-4xl mx-auto text-white p-4 flex flex-col h-[90vh] max-h-[800px] relative">
<!-- 引導畫面 -->
<div id="start-screen" class="flex flex-col items-center justify-center text-center p-8 bg-black bg-opacity-70 rounded-lg my-auto">
<h1 class="text-5xl font-bold text-cyan-300 mb-4" style="text-shadow: 2px 2px 4px #000;">海風港灣</h1>
<p class="text-xl text-gray-200 mb-8 max-w-2xl">歡迎來到海風港灣!這次的任務是捕捉指定的魚種,並到市場賣出。有了捕捉的技術,真正要賺大錢還得靠數學呢!請你在捕捉完漁獲後,根據市場需求,將漁獲賣到最適合的市場!</p>
<button id="start-button" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-4 px-8 rounded-lg text-2xl">
開始抓魚
</button>
</div>
<!-- 載入畫面 -->
<div id="loading-screen" class="hidden flex-col items-center justify-center text-center p-8 bg-black bg-opacity-70 rounded-lg my-auto">
<div class="loader mb-6"></div>
<p id="loading-text" class="text-2xl text-gray-200">正在準備海底世界...</p>
</div>
<!-- 遊戲畫面 -->
<div id="game-screen" class="hidden flex-col h-full w-full">
<div class="flex justify-between items-center p-2 bg-black/50 rounded-t-lg">
<div>
<p class="text-lg">目標: <span id="goal-text" class="font-bold text-yellow-400"></span></p>
<p class="text-lg">已捕捉: <span id="score" class="font-bold text-xl">0</span> / <span id="goal-count"></span></p>
</div>
<p class="text-sm text-cyan-200">請適度捕撈,維持海洋永續</p>
<div>
<p class="text-lg">時間</p>
<p id="timer" class="font-bold text-3xl">20</p>
</div>
</div>
<div id="game-container-bg" class="relative flex-grow rounded-b-lg overflow-hidden border-4 border-black/50">
<div id="game-container" class="absolute inset-0">
<img src="https://i.meee.com.tw/XE9mkQQ.gif" class="seaweed" style="left: 0%; height: 45%; transform: scaleX(-1);" alt="[海草的Image]">
<img src="https://i.meee.com.tw/cTBZUgx.gif" class="seaweed" style="left: 20%; height: 35%;" alt="[海草的Image]">
<img src="https://i.meee.com.tw/KyN2GaF.gif" class="seaweed" style="left: 45%; height: 30%;" alt="[海草的Image]">
<img src="https://i.meee.com.tw/XE9mkQQ.gif" class="seaweed" style="right: 25%; height: 48%;" alt="[海草的Image]">
<img src="https://i.meee.com.tw/cTBZUgx.gif" class="seaweed" style="right: 10%; height: 55%; transform: scaleX(-1);" alt="[海草的Image]">
<img src="https://i.meee.com.tw/KyN2GaF.gif" class="seaweed" style="right: -2%; height: 38%;" alt="[海草的Image]">
</div>
</div>
</div>
<!-- 疊加畫面 (操作說明, 成功/失敗) -->
<div id="overlay-container" class="hidden absolute inset-0 flex-col items-center justify-center z-10">
<div id="instructions-screen" class="hidden bg-black bg-opacity-80 w-full h-full flex-col items-center justify-center text-center p-4">
<div id="instructions-content" class="bg-slate-800 p-8 rounded-lg">
<!-- JS will fill this -->
</div>
</div>
<div id="game-over-screen" class="hidden bg-black bg-opacity-70 w-full h-full flex-col items-center justify-center text-center p-4">
<h2 class="text-4xl font-bold text-red-500 mb-4">挑戰失敗!</h2>
<button id="restart-button" class="bg-amber-500 hover:bg-amber-600 text-gray-900 font-bold py-3 px-6 rounded-lg text-xl">
重新挑戰
</button>
</div>
<div id="win-screen" class="hidden bg-black bg-opacity-70 w-full h-full flex-col items-center justify-center text-center p-4">
<h2 class="text-4xl font-bold text-green-400 mb-4">成功!</h2>
<p class="text-xl mb-6">你抓到足夠的漁獲了!</p>
<button id="continue-button" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg text-xl">
繼續前進
</button>
</div>
</div>
<!-- 市場選擇畫面 -->
<div id="market-screen" class="hidden flex-col h-full w-full p-6 bg-black bg-opacity-70 rounded-lg">
<div id="market-instructions" class="text-center my-auto">
<h2 class="text-3xl font-bold text-amber-300 mb-6">任務說明:選擇市場</h2>
<p class="text-lg">每個地點對<span class="market-fish-name text-yellow-300 font-bold"></span>的需求都不同,需求<span class="text-yellow-300 font-bold">比例</span>越高的市場,就能賣到越好的價格!</p>
<p class="text-lg mt-2">仔細觀察下方的統計圖,找出最賺錢的市場吧!</p>
<button id="show-market-choices-button" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-8 rounded-lg text-xl mt-8">
了解,前往選擇
</button>
</div>
<div id="market-choices" class="hidden flex-col">
<h2 class="text-3xl font-bold text-amber-300 text-center mb-4">你要把<span class="market-fish-name"></span>賣到哪裡?</h2>
<div id="market-images-container" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Market images will be inserted here by JS -->
</div>
<p id="market-feedback" class="text-center text-xl font-bold min-h-[32px] mt-4"></p>
<button id="next-level-button" class="hidden bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg text-xl w-full max-w-xs mx-auto mt-4">
<!-- Button text will be set by JS -->
</button>
</div>
</div>
<!-- 攻略畫面 -->
<div id="strategy-screen" class="hidden flex-col h-full w-full p-6 bg-black bg-opacity-80 rounded-lg text-left overflow-y-auto">
<h1 class="text-3xl font-bold text-amber-300 text-center mb-4">經營之神的攻略</h1>
<p class="text-lg mb-4">在真實世界中,不同地點的調查所收到的資料不一定會一樣多,總人數不一樣的時候,我們很難用肉眼就判斷出比例的高低,因此需要「相對次數」這樣的統計數據。</p>
<div class="text-center bg-gray-900 p-3 rounded-lg text-xl mb-4">
$$\text{相對次數} = \frac{\text{需求(人)}}{\text{總人數}} \times 100\%$$
</div>
<img src="https://i.meee.com.tw/i67LLqV.png" class="rounded-lg mx-auto my-4 w-full max-w-md" alt="[包含相對次數的統計圖表]">
<p class="text-lg mb-6 text-center">有了相對次數,會不會更好判斷呢?用下一關的挑戰來試試吧!</p>
<button id="start-final-stage-button" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-lg text-xl w-full max-w-xs mx-auto mt-4">
挑戰最終關卡
</button>
</div>
<!-- 最終秘密畫面 -->
<div id="final-secret-screen" class="hidden flex-col h-full w-full p-6 bg-black bg-opacity-80 rounded-lg text-left overflow-y-auto">
<h1 class="text-3xl font-bold text-amber-300 text-center mb-6">海風港灣的秘密</h1>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<img src="https://i.meee.com.tw/rmcY2Vc.png" class="rounded-lg border-4 border-amber-400" alt="[代數之丘的統計圖表]">
<img src="https://i.meee.com.tw/qV8QrUI.png" class="rounded-lg" alt="[哲學之塔的統計圖表]">
<img src="https://i.meee.com.tw/i67LLqV.png" class="rounded-lg" alt="[寶石洞窟的統計圖表]">
</div>
<p class="text-lg mb-4">恭喜你,完成了所有挑戰!你學會了捕魚,也學會了如何分析數據來做出最好的商業決策。</p>
<p class="text-2xl font-bold text-cyan-300 mb-4">這個港灣的秘密就是:<br>「數字」本身有時候會騙人,「比例」才能揭露真相。</p>
<p class="text-lg mb-4">單看數字,哲學之塔似乎是更好的市場。但當你把「總人數」也考慮進來,計算出「相對次數」(也就是需求比例),你才會發現代數之丘的需求比例,是超過哲學之塔的。</p>
<p class="text-lg">這個智慧,不只適用於賣魚。未來在你看新聞、分析報告,甚至做人生重大決定時,記得問自己:「這個數字背後的『分母』是什麼?」看透比例,你就能做出更聰明的選擇。</p>
<a href="index.html" id="back-to-map-button" class="inline-block text-center bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-8 rounded-lg text-xl w-full max-w-xs mx-auto mt-8">
回到探險島地圖
</a>
</div>
</div>
<!-- Image Zoom Modal -->
<div id="zoom-modal" class="hidden fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4 cursor-pointer">
<img id="zoomed-image" src="" alt="放大的統計圖表" class="max-w-full max-h-full rounded-lg shadow-2xl">
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- 畫面元素 ---
const startScreen = document.getElementById('start-screen');
const loadingScreen = document.getElementById('loading-screen');
const loadingText = document.getElementById('loading-text');
const gameScreen = document.getElementById('game-screen');
const startButton = document.getElementById('start-button');
const gameContainer = document.getElementById('game-container');
const scoreDisplay = document.getElementById('score');
const timerDisplay = document.getElementById('timer');
const instructionsScreen = document.getElementById('instructions-screen');
const instructionsContent = document.getElementById('instructions-content');
const goalText = document.getElementById('goal-text');
const goalCount = document.getElementById('goal-count');
const overlayContainer = document.getElementById('overlay-container');
const gameOverScreen = document.getElementById('game-over-screen');
const winScreen = document.getElementById('win-screen');
const restartButton = document.getElementById('restart-button');
const continueButton = document.getElementById('continue-button');
const marketScreen = document.getElementById('market-screen');
const marketInstructions = document.getElementById('market-instructions');
const marketChoices = document.getElementById('market-choices');
const showMarketChoicesButton = document.getElementById('show-market-choices-button');
const marketImagesContainer = document.getElementById('market-images-container');
const marketFeedback = document.getElementById('market-feedback');
const nextLevelButton = document.getElementById('next-level-button');
const startFinalStageButton = document.getElementById('start-final-stage-button');
const finalSecretScreen = document.getElementById('final-secret-screen');
const zoomModal = document.getElementById('zoom-modal');
const zoomedImage = document.getElementById('zoomed-image');
// --- 遊戲設定 ---
const MAX_FISH_ON_SCREEN = 8;
let score = 0, timeLeft = 0;
let gameInterval, timerInterval, fishSpawnInterval;
let currentLevel = 0;
let currentLevelSettings;
const levels = [
{ goal: 10, time: 20, targetFish: '小丑魚', speedMultiplier: 1.0, spawnRate: 1200, fishSet: ['小丑魚', '螃蟹'] },
{ goal: 10, time: 20, targetFish: '水母', speedMultiplier: 1.5, spawnRate: 1000, fishSet: ['水母', '章魚'] },
{ goal: 10, time: 20, targetFish: '水母', speedMultiplier: 1.8, spawnRate: 800, fishSet: ['水母', '鯊魚'] }
];
const fishTypes = [
{ name: '小丑魚', src: 'https://i.meee.com.tw/7tfldTT.gif', width: 160, height: 120 },
{ name: '鯊魚', src: 'https://i.meee.com.tw/KCQk7C6.gif', width: 270, height: 270 },
{ name: '章魚', src: 'https://i.meee.com.tw/lBWJmMy.gif', width: 160, height: 130 },
{ name: '海馬', src: 'https://i.meee.com.tw/sMehyty.gif', width: 150, height: 150 },
{ name: '螃蟹', src: 'https://i.meee.com.tw/zx9Vg0B.gif', width: 150, height: 110 },
{ name: '水母', src: 'https://i.meee.com.tw/81gncdG.gif', width: 160, height: 160 },
];
const marketLevels = [
{
images: { A: 'https://i.meee.com.tw/jQ3A90u.png', B: 'https://i.meee.com.tw/HEx1GWE.png', C: 'https://i.meee.com.tw/199bHzA.png' },
correctAnswer: 'B',
feedback: { correct: "答對了!哲學之塔的需求比例最高,要賺大錢還是得靠數學,不能蠻幹阿!", wrong: "每個地點的調查人數都一樣多,需求數量越多,即表示需求比例越高!" }
},
{
// A: 代數之丘(左), B: 哲學之塔(中), C: 寶石洞窟(右)
images: { A: 'https://i.meee.com.tw/lTXgvxv.png', B: 'https://i.meee.com.tw/FKPNhKh.png', C: 'https://i.meee.com.tw/zdv5MtZ.png' },
correctAnswer: 'A', // 正確答案是代數之丘
feedback: {
firstClick: {
C: "你確定嗎?其他兩個地方的需求人數比較多喔!若是確定請再點選一次",
B: "你確定嗎?雖然哲學之塔的需求人數最多,但他的總人數也最多喔!你確定他是比例最高的嗎?",
A: "你確定嗎?哲學之塔的需求人數比較多喔!"
},
correct: "你做出了正確的決定!但到底是運氣好,還是數學好呢,如果是運氣好的話,可沒有辦法長久經營阿~",
wrong: "商品買賣,不是越多人買越賺錢,不是喔!因為你長期的決策錯誤,導致買賣虧損,最後造成店家倒閉..."
}
},
{
// A: 代數之丘, B: 哲學之塔, C: 寶石洞窟
images: { A: 'https://i.meee.com.tw/rmcY2Vc.png', B: 'https://i.meee.com.tw/qV8QrUI.png', C: 'https://i.meee.com.tw/i67LLqV.png' },
correctAnswer: 'A', // 正確答案是代數之丘
feedback: { correct: "有了相對次數這項統計數據,是不是就更容易做選擇了呢~", wrong: "再仔細看看,哪個市場的需求比例最高呢?" }
}
];
let firstChoice = null;
const fishesOnScreen = [];
// --- 畫面管理 ---
function showScreen(screenId) {
['start-screen', 'game-screen', 'market-screen', 'loading-screen', 'strategy-screen', 'final-secret-screen'].forEach(id => {
document.getElementById(id).style.display = (id === screenId) ? 'flex' : 'none';
});
}
function showOverlay(overlayId) {
overlayContainer.style.display = 'flex';
['instructions-screen', 'game-over-screen', 'win-screen'].forEach(id => {
document.getElementById(id).style.display = (id === overlayId) ? 'flex' : 'none';
});
}
function hideOverlay() {
overlayContainer.style.display = 'none';
}
// --- 預載入函式 ---
function preloadImages(urls, onProgress) {
return new Promise((resolve) => {
let loadedCount = 0;
const totalImages = urls.length;
if (totalImages === 0) resolve();
urls.forEach(url => {
const img = new Image();
img.src = url;
const onFinish = () => {
loadedCount++;
onProgress(loadedCount, totalImages);
if (loadedCount === totalImages) resolve();
};
img.onload = onFinish;
img.onerror = () => { console.error(`圖片載入失敗: ${url}`); onFinish(); };
});
});
}
function startGame(levelIndex) {
currentLevel = levelIndex;
currentLevelSettings = levels[currentLevel];
score = 0;
timeLeft = currentLevelSettings.time;
updateUI();
fishesOnScreen.forEach(fish => fish.element.remove());
fishesOnScreen.length = 0;
fishSpawnInterval = setInterval(spawnFish, currentLevelSettings.spawnRate);
timerInterval = setInterval(() => {
timeLeft--;
updateUI();
if (timeLeft <= 0) endGame(false);
}, 1000);
gameInterval = setInterval(updateGame, 1000 / 60);
}
function spawnFish() {
if (fishesOnScreen.length >= MAX_FISH_ON_SCREEN) return;
let fishData;
const fishSet = currentLevelSettings.fishSet.map(name => fishTypes.find(f => f.name === name));
const targetFish = fishSet.find(f => f.name === currentLevelSettings.targetFish);
const otherFish = fishSet.filter(f => f.name !== currentLevelSettings.targetFish);
const activeDistractor = otherFish.length > 0 ? otherFish[0] : targetFish;
if (Math.random() < 0.75) {
fishData = targetFish;
} else {
fishData = activeDistractor;
}
const fishElement = document.createElement('img');
fishElement.src = fishData.src;
fishElement.className = 'fish';
const direction = Math.random() < 0.5 ? 'left' : 'right';
const speed = (Math.random() * 2 + 1.5) * currentLevelSettings.speedMultiplier;
const startY = Math.random() * (gameContainer.clientHeight - fishData.height);
fishElement.style.width = `${fishData.width}px`;
fishElement.style.height = `${fishData.height}px`;
fishElement.style.top = `${startY}px`;
if (direction === 'left') {
fishElement.style.left = `${gameContainer.clientWidth}px`;
fishElement.style.transform = 'scaleX(-1)';
} else {
fishElement.style.left = `-${fishData.width}px`;
}
const fishObject = { element: fishElement, data: fishData, speed, direction, vy: (Math.random() - 0.5) * 2, lastVyChange: Date.now() };
if (currentLevel === 2 && fishData.name === '水母') {
fishObject.health = 2;
} else {
fishObject.health = 1;
}
fishElement.onclick = () => catchFish(fishObject);
fishesOnScreen.push(fishObject);
gameContainer.appendChild(fishObject.element);
}
function updateGame() {
for (let i = fishesOnScreen.length - 1; i >= 0; i--) {
const fish = fishesOnScreen[i];
let currentX = parseFloat(fish.element.style.left);
let currentY = parseFloat(fish.element.style.top);
if (fish.direction === 'left') {
currentX -= fish.speed;
if (currentX < -fish.data.width) {
fish.element.remove();
fishesOnScreen.splice(i, 1);
continue;
}
} else {
currentX += fish.speed;
if (currentX > gameContainer.clientWidth) {
fish.element.remove();
fishesOnScreen.splice(i, 1);
continue;
}
}
fish.element.style.left = `${currentX}px`;
if (currentLevel > 0) {
if (Date.now() - fish.lastVyChange > 1000) {
fish.vy = (Math.random() - 0.5) * 6;
fish.lastVyChange = Date.now();
}
currentY += fish.vy;
if (currentY < 0 || currentY > gameContainer.clientHeight - fish.data.height) {
fish.vy *= -1;
}
fish.element.style.top = `${currentY}px`;
}
}
}
function catchFish(fishObject) {
fishObject.health--;
if (fishObject.health > 0) {
showCatchFeedback('!', fishObject.element, false);
fishObject.element.style.opacity = '0.5';
setTimeout(() => {
if (fishObject.element) {
fishObject.element.style.opacity = '1';
}
}, 150);
return;
}
if (fishObject.data.name === currentLevelSettings.targetFish) {
score++;
showCatchFeedback('+1', fishObject.element, true);
} else {
score = Math.max(0, score - 1);
showCatchFeedback('-1', fishObject.element, false);
}
fishObject.element.remove();
const index = fishesOnScreen.indexOf(fishObject);
if (index > -1) fishesOnScreen.splice(index, 1);
updateUI();
if (score >= currentLevelSettings.goal) endGame(true);
}
function showCatchFeedback(text, fishElement, isCorrect) {
const feedback = document.createElement('div');
feedback.className = 'catch-feedback';
feedback.textContent = text;
feedback.style.color = isCorrect ? '#4ade80' : '#f87171';
const rect = fishElement.getBoundingClientRect();
const containerRect = gameContainer.getBoundingClientRect();
feedback.style.left = `${rect.left - containerRect.left + rect.width / 2}px`;
feedback.style.top = `${rect.top - containerRect.top + rect.height / 2}px`;
gameContainer.appendChild(feedback);
setTimeout(() => feedback.remove(), 1000);
}
function updateUI() {
scoreDisplay.textContent = score;
timerDisplay.textContent = timeLeft;
}
function endGame(isWin) {
clearInterval(gameInterval);
clearInterval(timerInterval);
clearInterval(fishSpawnInterval);
gameInterval = timerInterval = fishSpawnInterval = null;
if (isWin) {
showOverlay('win-screen');
} else {
showOverlay('game-over-screen');
}
}
function setupMarketScreen() {
const marketLevel = marketLevels[currentLevel];
document.querySelectorAll('.market-fish-name').forEach(el => el.textContent = levels[currentLevel].targetFish);
marketImagesContainer.innerHTML = '';
const marketOrder = ['A', 'B', 'C'];
marketOrder.forEach(key => {
const src = marketLevel.images[key];
const choiceContainer = document.createElement('div');
choiceContainer.className = 'market-choice relative p-2 rounded-lg border-4 border-transparent cursor-pointer transition-all duration-200';
choiceContainer.dataset.market = key;
const img = document.createElement('img');
img.src = src;
img.alt = `[統計圖表]`;
img.className = 'w-full h-auto rounded-lg shadow-lg';
const zoomIcon = document.createElement('button');
zoomIcon.className = 'absolute top-2 right-2 bg-black/50 p-2 rounded-full text-white hover:bg-black/80 transition-colors z-10';
zoomIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8zm8-3a1 1 0 011 1v2h2a1 1 0 110 2h-2v2a1 1 0 11-2 0v-2H5a1 1 0 110-2h2V6a1 1 0 011-1z" clip-rule="evenodd" /></svg>`;
zoomIcon.onclick = (e) => {
e.stopPropagation();
showEnlargedImage(src);
};
choiceContainer.appendChild(img);
choiceContainer.appendChild(zoomIcon);
choiceContainer.addEventListener('click', handleMarketChoice);
marketImagesContainer.appendChild(choiceContainer);
});
marketFeedback.textContent = '';
nextLevelButton.classList.add('hidden');
firstChoice = null;
}
function handleMarketChoice(e) {
const choiceContainer = e.target.closest('.market-choice');
if (!choiceContainer) return;
const choice = choiceContainer.dataset.market;
const marketLevel = marketLevels[currentLevel];
document.querySelectorAll('.market-choice').forEach(div => div.classList.remove('selected'));
if (currentLevel === 0) {
if (choice === marketLevel.correctAnswer) {
choiceContainer.classList.add('selected');
marketFeedback.textContent = marketLevel.feedback.correct;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-green-400';
nextLevelButton.textContent = "前往第二關";
nextLevelButton.classList.remove('hidden');
} else {
marketFeedback.textContent = marketLevel.feedback.wrong;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-yellow-400';
}
} else if (currentLevel === 1) {
if (firstChoice === choice) {
if (choice === marketLevel.correctAnswer) {
choiceContainer.classList.add('selected');
marketFeedback.textContent = marketLevel.feedback.correct;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-green-400';
nextLevelButton.textContent = "經營之神的攻略";
nextLevelButton.classList.remove('hidden');
} else {
marketFeedback.textContent = marketLevel.feedback.wrong;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-red-500';
}
firstChoice = null;
} else {
firstChoice = choice;
choiceContainer.classList.add('selected');
marketFeedback.textContent = marketLevel.feedback.firstClick[choice];
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-cyan-300';
}
} else if (currentLevel === 2) {
if (choice === marketLevel.correctAnswer) {
choiceContainer.classList.add('selected');
marketFeedback.textContent = marketLevel.feedback.correct;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-green-400';
nextLevelButton.textContent = "查看海風港灣的秘密";
nextLevelButton.classList.remove('hidden');
} else {
marketFeedback.textContent = marketLevel.feedback.wrong;
marketFeedback.className = 'text-center text-xl font-bold min-h-[32px] mt-4 text-red-500';
}
}
}
function showEnlargedImage(src) {
zoomedImage.src = src;
zoomModal.classList.remove('hidden');
}
function hideEnlargedImage() {
zoomModal.classList.add('hidden');
zoomedImage.src = '';
}
// --- 事件監聽 ---
startButton.addEventListener('click', async () => {
showScreen('loading-screen');
const imageUrls = [
...fishTypes.map(fish => fish.src),
...Object.values(marketLevels[0].images),
...Object.values(marketLevels[1].images),
...Object.values(marketLevels[2].images),
'https://i.meee.com.tw/i67LLqV.png'
];
await preloadImages(imageUrls, (loaded, total) => {
loadingText.textContent = `正在準備海底世界... (${loaded}/${total})`;
});
showScreen('game-screen');
instructionsContent.innerHTML = `
<h2 class="text-3xl font-bold text-amber-300 mb-6">第一關任務</h2>
<div class="space-y-4 text-lg">
<p>在 <span class="text-yellow-400 font-bold">${levels[0].time}</span> 秒內,捕捉 <span class="text-yellow-400 font-bold">${levels[0].goal}</span> 隻${levels[0].targetFish}!</p>
<p>點擊<span class="text-green-400 font-bold">正確的魚</span>來加分。</p>
<p>注意!抓到<span class="text-red-400 font-bold">錯誤的魚</span>會扣分喔!</p>
</div>
<button id="play-game-button" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-lg text-xl mt-8">
了解!
</button>`;
document.getElementById('play-game-button').onclick = () => { hideOverlay(); startGame(0); };
showOverlay('instructions-screen');
goalText.textContent = `抓 ${levels[0].goal}${levels[0].targetFish}`;
goalCount.textContent = levels[0].goal;
});
restartButton.addEventListener('click', () => {
hideOverlay();
startGame(currentLevel);
});
continueButton.addEventListener('click', () => {
hideOverlay();
showScreen('market-screen');
// FIX: Update the fish name immediately when showing the market screen instructions.
document.querySelectorAll('.market-fish-name').forEach(el => el.textContent = levels[currentLevel].targetFish);
marketInstructions.style.display = 'block';
marketChoices.style.display = 'none';
});
showMarketChoicesButton.addEventListener('click', () => {
marketInstructions.style.display = 'none';
marketChoices.style.display = 'flex';
setupMarketScreen();
});
nextLevelButton.addEventListener('click', () => {
if (currentLevel === 0) {
const nextLevel = currentLevel + 1;
const levelSettings = levels[nextLevel];
showScreen('game-screen');
instructionsContent.innerHTML = `
<h2 class="text-3xl font-bold text-amber-300 mb-6">第 ${nextLevel + 1} 關任務</h2>
<div class="space-y-4 text-lg">
<p>在 <span class="text-yellow-400 font-bold">${levelSettings.time}</span> 秒內,捕捉 <span class="text-yellow-400 font-bold">${levelSettings.goal}</span> 隻${levelSettings.targetFish}!</p>
<p>海洋生物的移動方式不一樣了,小心!</p>
</div>
<button id="play-next-level-button" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-lg text-xl mt-8">
挑戰!
</button>`;
document.getElementById('play-next-level-button').onclick = () => { hideOverlay(); startGame(nextLevel); };
showOverlay('instructions-screen');
goalText.textContent = `抓 ${levelSettings.goal}${levelSettings.targetFish}`;
goalCount.textContent = levelSettings.goal;
} else if (currentLevel === 1) {
showScreen('strategy-screen');
} else if (currentLevel === 2) {
showScreen('final-secret-screen');
}
});
startFinalStageButton.addEventListener('click', () => {
const nextLevel = currentLevel + 1;
const levelSettings = levels[nextLevel];
showScreen('game-screen');
instructionsContent.innerHTML = `
<h2 class="text-3xl font-bold text-amber-300 mb-6">最終挑戰!</h2>
<div class="space-y-4 text-lg">
<p>在 <span class="text-yellow-400 font-bold">${levelSettings.time}</span> 秒內,捕捉 <span class="text-yellow-400 font-bold">${levelSettings.goal}</span> 隻${levelSettings.targetFish}!</p>
<p class="text-cyan-300">注意!這次的${levelSettings.targetFish}比較頑強,需要點擊兩下才能捕捉!</p>
</div>
<button id="play-final-level-button" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-8 rounded-lg text-xl mt-8">
挑戰!
</button>`;
document.getElementById('play-final-level-button').onclick = () => { hideOverlay(); startGame(nextLevel); };
showOverlay('instructions-screen');
goalText.textContent = `抓 ${levelSettings.goal}${levelSettings.targetFish}`;
goalCount.textContent = levelSettings.goal;
});
zoomModal.addEventListener('click', () => {
hideEnlargedImage();
});
// --- 初始啟動 ---
showScreen('start-screen');
});
</script>
</body>
</html>