<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>逃離神秘吸血鬼</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Creepster&family=Roboto+Mono:wght@400;700&display=swap');
:root {
--bg-color: #111;
--text-color: #ddd;
--accent-color: #700;
--wall-color: #333;
--path-color: #111;
--border-color: #444;
--highlight-color: #555;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Roboto Mono', monospace;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
position: relative;
}
#game-container {
position: relative;
width: 90vw;
max-width: 800px;
height: 90vh;
max-height: 600px;
border: 2px solid var(--border-color);
box-shadow: 0 0 20px rgba(120, 0, 0, 0.3);
overflow: hidden;
background-color: var(--bg-color);
}
#main-menu, #game-screen, #completion-screen, #game-over-screen {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: all 0.5s ease-in-out;
}
#main-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='600' viewBox='0 0 800 600'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='800' height='600' filter='url(%23noise)' opacity='0.15'/%3E%3Cpath d='M400 150 L380 200 L420 200 Z' fill='%23333'/%3E%3Cellipse cx='400' cy='250' rx='150' ry='80' fill='none' stroke='%23444' stroke-width='2'/%3E%3Cpath d='M320 250 Q400 350 480 250' fill='none' stroke='%23555' stroke-width='2'/%3E%3Ccircle cx='350' cy='230' r='5' fill='%23700'/%3E%3Ccircle cx='450' cy='230' r='5' fill='%23700'/%3E%3C/svg%3E");
background-size: cover;
z-index: 10;
}
#game-screen {
display: none;
background-color: var(--bg-color);
}
#completion-screen, #game-over-screen {
display: none;
background-color: rgba(0, 0, 0, 0.9);
z-index: 20;
}
h1 {
font-family: 'Creepster', cursive;
font-size: 3.5rem;
margin-bottom: 2rem;
text-shadow: 0 0 10px rgba(120, 0, 0, 0.7);
letter-spacing: 3px;
text-align: center;
color: #eee;
}
h2 {
font-family: 'Creepster', cursive;
font-size: 2rem;
margin-bottom: 1.5rem;
text-shadow: 0 0 8px rgba(120, 0, 0, 0.5);
letter-spacing: 2px;
text-align: center;
}
@media (max-width: 600px) {
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 1.5rem;
}
}
.menu-buttons {
display: flex;
flex-direction: column;
gap: 1rem;
}
.level-button, .menu-button {
padding: 0.8rem 2rem;
font-size: 1.2rem;
background-color: #222;
color: var(--text-color);
border: 1px solid var(--border-color);
cursor: pointer;
transition: all 0.3s;
min-width: 200px;
text-align: center;
position: relative;
overflow: hidden;
border-radius: 4px;
}
.level-button:before, .menu-button:before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: 0.5s;
}
.level-button:hover:before, .menu-button:hover:before {
left: 100%;
}
.level-button:hover, .menu-button:hover {
background-color: #333;
border-color: #666;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.level-button {
display: flex;
justify-content: space-between;
align-items: center;
}
.stars {
letter-spacing: 3px;
color: #aaa;
}
.star-filled {
color: #c00;
text-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}
#maze-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 60px 40px 40px 40px;
}
#maze {
position: relative;
border: 1px solid #333;
width: 100%;
height: 100%;
max-width: 600px;
max-height: 450px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.cell {
position: absolute;
background-color: var(--path-color);
border: 1px solid #222;
transition: background-color 0.3s;
}
.wall {
background-color: var(--wall-color);
border: 1px solid #444;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
}
#player {
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='45' fill='%23ddd'/%3E%3Ccircle cx='35' cy='40' r='8' fill='%23111'/%3E%3Ccircle cx='65' cy='40' r='8' fill='%23111'/%3E%3Cpath d='M30 65 Q50 80 70 65' stroke='%23111' stroke-width='3' fill='none'/%3E%3C/svg%3E");
background-size: contain;
z-index: 5;
transition: all 0.2s ease-out;
filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.5));
}
#exit {
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect x='10' y='10' width='80' height='80' fill='none' stroke='%23700' stroke-width='5'/%3E%3Ctext x='50' y='60' font-family='Arial' font-size='40' fill='%23700' text-anchor='middle'%3EEX%3C/text%3E%3C/svg%3E");
background-size: contain;
z-index: 2;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); filter: brightness(1); }
50% { transform: scale(1.05); filter: brightness(1.2); }
100% { transform: scale(1); filter: brightness(1); }
}
#vampire {
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 10 L30 40 L50 90 L70 40 Z' fill='%23333'/%3E%3Ccircle cx='40' cy='40' r='5' fill='%23700'/%3E%3Ccircle cx='60' cy='40' r='5' fill='%23700'/%3E%3Cpath d='M35 55 Q50 65 65 55' stroke='%23700' stroke-width='2' fill='none'/%3E%3Cpath d='M40 55 L35 45 M60 55 L65 45' stroke='%23700' stroke-width='2' fill='none'/%3E%3C/svg%3E");
background-size: contain;
z-index: 4;
transition: all 0.3s ease-in-out;
filter: drop-shadow(0 0 5px rgba(120, 0, 0, 0.7));
}
#game-header {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
background-color: rgba(0, 0, 0, 0.7);
border-bottom: 1px solid var(--border-color);
z-index: 10;
}
#game-info {
font-size: 1rem;
color: #aaa;
display: flex;
align-items: center;
gap: 10px;
}
#level-display {
color: #eee;
font-weight: bold;
}
#mini-map {
position: absolute;
top: 60px;
right: 10px;
width: 120px;
height: 120px;
background-color: rgba(0, 0, 0, 0.7);
border: 1px solid var(--border-color);
z-index: 8;
padding: 5px;
display: none;
}
#mini-map-canvas {
width: 100%;
height: 100%;
}
#controls-info {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 0.9rem;
color: #666;
background-color: rgba(0, 0, 0, 0.5);
padding: 5px 10px;
border-radius: 4px;
}
#developer-button {
position: absolute;
bottom: 10px;
right: 10px;
padding: 5px 10px;
background-color: #222;
color: #666;
border: 1px solid #444;
cursor: pointer;
font-size: 0.8rem;
border-radius: 4px;
transition: all 0.3s;
}
#developer-button:hover {
background-color: #333;
color: #888;
}
#code-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
z-index: 100;
padding: 20px;
overflow: auto;
}
#code-content {
background-color: #111;
padding: 20px;
border: 1px solid #444;
color: #aaa;
font-family: monospace;
white-space: pre-wrap;
max-width: 1000px;
margin: 20px auto;
border-radius: 4px;
max-height: 70vh;
overflow-y: auto;
}
#modal-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
#close-modal, #copy-code {
padding: 10px 20px;
background-color: #222;
color: #ddd;
border: 1px solid #444;
cursor: pointer;
border-radius: 4px;
transition: all 0.3s;
}
#close-modal:hover, #copy-code:hover {
background-color: #333;
transform: translateY(-2px);
}
.blood-splatter {
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='30' fill='%23700' opacity='0.7'/%3E%3Ccircle cx='70' cy='40' r='15' fill='%23700' opacity='0.5'/%3E%3Ccircle cx='30' cy='60' r='20' fill='%23700' opacity='0.6'/%3E%3C/svg%3E");
background-size: contain;
opacity: 0;
z-index: 3;
animation: fadeOut 2s forwards;
}
@keyframes fadeOut {
0% { opacity: 0.8; transform: scale(1); }
100% { opacity: 0; transform: scale(1.5); }
}
.fog {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='600' viewBox='0 0 800 600'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.01' numOctaves='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='800' height='600' filter='url(%23noise)' opacity='0.3'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 6;
opacity: 0.4;
}
.flash {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.1);
z-index: 7;
opacity: 0;
pointer-events: none;
}
@keyframes lightning {
0% { opacity: 0; }
10% { opacity: 0.8; }
20% { opacity: 0; }
30% { opacity: 0.6; }
40% { opacity: 0; }
100% { opacity: 0; }
}
.notification {
position: absolute;
top: 70px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.7);
color: #eee;
padding: 10px 20px;
border-radius: 4px;
font-size: 1rem;
opacity: 0;
transition: opacity 0.5s;
z-index: 20;
}
.fade-in {
animation: fadeIn 0.5s forwards;
}
.fade-out {
animation: fadeOut 0.5s forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-20px) translateX(-50%); }
to { opacity: 1; transform: translateY(0) translateX(-50%); }
}
.footprint {
position: absolute;
width: 10px;
height: 15px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 50%;
z-index: 1;
opacity: 0.5;
animation: fadeFootprint 3s forwards;
}
@keyframes fadeFootprint {
0% { opacity: 0.5; }
100% { opacity: 0; }
}
.vampire-trail {
position: absolute;
width: 10px;
height: 10px;
background-color: rgba(120, 0, 0, 0.2);
border-radius: 50%;
z-index: 1;
opacity: 0.3;
animation: fadeVampireTrail 2s forwards;
}
@keyframes fadeVampireTrail {
0% { opacity: 0.3; }
100% { opacity: 0; }
}
#sound-toggle {
background-color: transparent;
border: none;
color: #666;
cursor: pointer;
font-size: 1.2rem;
padding: 5px;
transition: color 0.3s;
}
#sound-toggle:hover {
color: #aaa;
}
.progress-container {
width: 100%;
height: 5px;
background-color: #222;
position: absolute;
bottom: 0;
left: 0;
}
.progress-bar {
height: 100%;
background-color: var(--accent-color);
width: 0%;
transition: width 0.3s;
}
.screen-transition {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: black;
z-index: 50;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s;
}
</style>
</head>
<body>
<div id="game-container">
<!-- Main Menu -->
<div id="main-menu" style="display: none;">
<h1>逃離神秘吸血鬼</h1>
<div class="menu-buttons">
<button class="level-button" onclick="startGame(1)">
<span>第一關</span>
<span class="stars">★<span class="star-empty">☆☆</span></span>
</button>
<button class="level-button" onclick="startGame(2)">
<span>第二關</span>
<span class="stars">★★<span class="star-empty">☆</span></span>
</button>
<button class="level-button" onclick="startGame(3)">
<span>第三關</span>
<span class="stars">★★★</span>
</button>
</div>
</div>
<!-- Game Screen -->
<div id="game-screen" style="display: block;">
<div id="game-header">
<div id="game-info">
<span>關卡: <span id="level-display">3</span></span>
<button id="sound-toggle">🔊</button>
</div>
<div id="timer">00:03</div>
</div>
<div id="maze-container">
<div id="maze" style="width: 740px; height: 555px;"><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 37px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 111px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 185px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 259px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 407px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 703px; top: 0px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 37px; background-color: rgb(24, 24, 24);"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 37px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 222px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 296px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 518px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 37px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 666px; top: 37px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 37px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 74px; background-color: rgb(24, 24, 24);"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 407px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 74px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 74px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 74px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 148px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 111px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 296px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 111px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 592px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 666px; top: 111px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 111px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 148px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 148px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 185px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 259px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 407px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 148px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 148px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 148px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 185px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 148px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 222px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 185px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 185px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 518px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 592px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 185px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 185px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 185px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 222px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 111px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 185px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 222px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 222px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 222px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 703px; top: 222px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 148px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 666px; top: 259px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 259px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 296px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 296px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 333px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 333px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 222px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 296px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 333px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 518px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 592px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 666px; top: 333px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 333px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 370px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 370px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 370px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 259px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 407px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 370px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 370px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 407px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 148px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 407px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 296px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 518px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 592px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 407px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 407px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 407px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 111px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 185px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 444px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 444px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 37px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 74px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 111px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 148px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 185px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 222px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 259px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 296px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 333px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 370px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 407px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 444px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 481px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 518px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 555px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 592px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 629px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 666px; top: 481px;"></div><div class="cell" style="width: 37px; height: 37px; left: 703px; top: 481px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 0px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 37px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 74px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 111px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 148px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 185px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 222px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 259px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 296px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 333px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 370px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 407px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 444px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 481px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 518px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 555px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 592px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 629px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 666px; top: 518px;"></div><div class="cell wall" style="width: 37px; height: 37px; left: 703px; top: 518px;"></div></div>
</div>
<div id="mini-map" style="display: block;">
<canvas id="mini-map-canvas"></canvas>
</div>
<div id="player" style="width: 37px; height: 37px; left: 135px; top: 157px;"></div>
<div id="exit" style="width: 37px; height: 37px; left: 764px; top: 564px;"></div>
<div id="vampire" style="display: block; width: 37px; height: 37px; left: 431px; top: 342px;"></div>
<div class="fog"></div>
<div class="flash" style="animation: auto ease 0s 1 normal none running none;"></div>
<div id="controls-info">使用方向鍵或WASD移動</div>
<div class="notification fade-in" id="notification">小心! 吸血鬼正在追捕你!</div>
<div class="footprint" style="width: 12.3333px; height: 18.5px; left: 153.5px; top: 138.5px;"></div><div class="footprint" style="width: 12.3333px; height: 18.5px; left: 153.5px; top: 138.5px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 425.25px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 388.25px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 388.25px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 351.25px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 351.25px;"></div><div class="vampire-trail" style="width: 18.5px; height: 18.5px; left: 514.25px; top: 351.25px;"></div></div>
<!-- Completion Screen -->
<div id="completion-screen" style="display: none;">
<h1>恭喜逃脫!</h1>
<h2>你成功地逃離了吸血鬼</h2>
<div class="menu-buttons">
<button class="menu-button" onclick="showMainMenu()">返回主菜單</button>
<button class="menu-button" onclick="startNextLevel()">下一關</button>
<button class="menu-button" onclick="startGame(currentLevel)">重新開始</button>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" style="display: none;">
<h1>被吸血鬼抓到了!</h1>
<h2>再試一次吧</h2>
<div class="menu-buttons">
<button class="menu-button" onclick="showMainMenu()">返回主菜單</button>
<button class="menu-button" onclick="startGame(currentLevel)">重新開始</button>
</div>
</div>
<!-- Developer Button -->
<button id="developer-button" onclick="showSourceCode()">開發者</button>
</div>
<!-- Source Code Modal -->
<div id="code-modal">
<div id="code-content"></div>
<div id="modal-buttons">
<button id="copy-code" onclick="copySourceCode()">複製代碼</button>
<button id="close-modal" onclick="closeModal()">關閉</button>
</div>
</div>
<!-- Screen Transition -->
<div class="screen-transition" id="screen-transition" style="opacity: 0;"></div>
<script>
// Game variables
let maze = [];
let cellSize = 30;
let mazeWidth = 0;
let mazeHeight = 0;
let playerPosition = { x: 0, y: 0 };
let exitPosition = { x: 0, y: 0 };
let vampirePosition = { x: 0, y: 0 };
let vampireActive = false;
let vampireSpeed = 0;
let vampireInterval;
let vampirePathfindingDelay = 0;
let currentLevel = 1;
let gameActive = false;
let lightningTimer;
let soundEnabled = true;
let gameTimer;
let gameTime = 0;
let footprintInterval;
let vampireTrailInterval;
let playerLastPosition = { x: 0, y: 0 };
let vampireLastPosition = { x: 0, y: 0 };
let playerMoving = false;
let visitedCells = {};
let miniMapEnabled = false;
// Sound effects
const sounds = {
move: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="),
wall: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="),
vampire: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="),
exit: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="),
gameOver: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="),
lightning: new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=")
};
// DOM elements
const mainMenu = document.getElementById('main-menu');
const gameScreen = document.getElementById('game-screen');
const completionScreen = document.getElementById('completion-screen');
const gameOverScreen = document.getElementById('game-over-screen');
const mazeElement = document.getElementById('maze');
const mazeContainer = document.getElementById('maze-container');
const playerElement = document.getElementById('player');
const exitElement = document.getElementById('exit');
const vampireElement = document.getElementById('vampire');
const levelDisplay = document.getElementById('level-display');
const codeModal = document.getElementById('code-modal');
const codeContent = document.getElementById('code-content');
const flashElement = document.querySelector('.flash');
const notification = document.getElementById('notification');
const soundToggle = document.getElementById('sound-toggle');
const timerElement = document.getElementById('timer');
const miniMap = document.getElementById('mini-map');
const miniMapCanvas = document.getElementById('mini-map-canvas');
const screenTransition = document.getElementById('screen-transition');
// Toggle sound
soundToggle.addEventListener('click', function() {
soundEnabled = !soundEnabled;
soundToggle.textContent = soundEnabled ? '🔊' : '🔇';
});
// Play sound effect
function playSound(sound) {
if (soundEnabled) {
sounds[sound].currentTime = 0;
sounds[sound].play().catch(e => {
// Silent error - browsers may block autoplay
});
}
}
// Show notification
function showNotification(message, duration = 3000) {
notification.textContent = message;
notification.classList.add('fade-in');
setTimeout(() => {
notification.classList.remove('fade-in');
notification.classList.add('fade-out');
setTimeout(() => {
notification.classList.remove('fade-out');
notification.textContent = '';
}, 500);
}, duration);
}
// Screen transition effect
function screenTransitionEffect(callback) {
screenTransition.style.opacity = '1';
setTimeout(() => {
if (callback) callback();
setTimeout(() => {
screenTransition.style.opacity = '0';
}, 100);
}, 400);
}
// Calculate appropriate cell size based on container dimensions
function calculateCellSize() {
const containerWidth = mazeContainer.clientWidth - 40; // Padding
const containerHeight = mazeContainer.clientHeight - 40; // Padding
// Calculate cell size based on maze dimensions and container size
return Math.min(
Math.floor(containerWidth / mazeWidth),
Math.floor(containerHeight / mazeHeight)
);
}
// Update game timer
function updateTimer() {
const minutes = Math.floor(gameTime / 60).toString().padStart(2, '0');
const seconds = (gameTime % 60).toString().padStart(2, '0');
timerElement.textContent = `${minutes}:${seconds}`;
}
// Start game timer
function startTimer() {
clearInterval(gameTimer);
gameTime = 0;
updateTimer();
gameTimer = setInterval(() => {
gameTime++;
updateTimer();
}, 1000);
}
// Stop game timer
function stopTimer() {
clearInterval(gameTimer);
}
// Show main menu
function showMainMenu() {
screenTransitionEffect(() => {
stopGame();
mainMenu.style.display = 'flex';
gameScreen.style.display = 'none';
completionScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
});
}
// Start next level
function startNextLevel() {
if (currentLevel < 3) {
startGame(currentLevel + 1);
} else {
showMainMenu();
}
}
// Start game with selected level
function startGame(level) {
screenTransitionEffect(() => {
currentLevel = level;
levelDisplay.textContent = level;
// Reset visited cells
visitedCells = {};
// Set maze dimensions based on level
switch(level) {
case 1:
mazeWidth = 10;
mazeHeight = 8;
vampireActive = false;
miniMapEnabled = false;
break;
case 2:
mazeWidth = 15;
mazeHeight = 12;
vampireActive = true;
vampireSpeed = 1000; // Slower vampire
vampirePathfindingDelay = 2; // Less aggressive pathfinding
miniMapEnabled = true;
break;
case 3:
mazeWidth = 20;
mazeHeight = 15;
vampireActive = true;
vampireSpeed = 700; // Faster vampire
vampirePathfindingDelay = 1; // More aggressive pathfinding
miniMapEnabled = true;
break;
}
// Set up game screen
mainMenu.style.display = 'none';
gameScreen.style.display = 'block';
completionScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
// Show/hide mini-map based on level
miniMap.style.display = miniMapEnabled ? 'block' : 'none';
// Start timer
startTimer();
// Generate maze after a short delay to ensure container is rendered
setTimeout(() => {
// Calculate cell size
cellSize = calculateCellSize();
// Generate maze
generateMaze();
// Start lightning effect
startLightningEffect();
// Start vampire movement if active
if (vampireActive) {
vampireElement.style.display = 'block';
startVampireMovement();
showNotification("小心! 吸血鬼正在追捕你!");
} else {
vampireElement.style.display = 'none';
}
// Start footprint effect
startFootprintEffect();
// Start vampire trail effect if active
if (vampireActive) {
startVampireTrailEffect();
}
gameActive = true;
}, 100);
});
}
// Generate maze using modified recursive backtracking algorithm to ensure multiple paths
function generateMaze() {
// Initialize maze with walls
maze = [];
for (let y = 0; y < mazeHeight; y++) {
const row = [];
for (let x = 0; x < mazeWidth; x++) {
row.push(1); // 1 represents wall
}
maze.push(row);
}
// Create primary path using recursive backtracking
const stack = [];
const startX = 1;
const startY = 1;
maze[startY][startX] = 0; // 0 represents path
stack.push({ x: startX, y: startY });
while (stack.length > 0) {
const current = stack[stack.length - 1];
const neighbors = getUnvisitedNeighbors(current.x, current.y);
if (neighbors.length === 0) {
stack.pop();
} else {
const randomIndex = Math.floor(Math.random() * neighbors.length);
const next = neighbors[randomIndex];
// Connect current cell to next cell
maze[next.y][next.x] = 0;
// Mark the wall between current and next as path
maze[current.y + Math.floor((next.y - current.y) / 2)][current.x + Math.floor((next.x - current.x) / 2)] = 0;
stack.push(next);
}
}
// Add additional paths for more escape routes (more for higher levels)
const additionalPaths = currentLevel * 3;
for (let i = 0; i < additionalPaths; i++) {
addAdditionalPath();
}
// Set player position at the start
playerPosition = { x: 1, y: 1 };
playerLastPosition = { ...playerPosition };
// Set exit position at the opposite corner
exitPosition = { x: mazeWidth - 2, y: mazeHeight - 2 };
maze[exitPosition.y][exitPosition.x] = 0; // Ensure exit is a path
// Ensure there's a path from player to exit
ensurePathToExit();
// Set vampire position (if active)
if (vampireActive) {
// Place vampire at a random position that's not near the player or exit
let placed = false;
while (!placed) {
const x = Math.floor(Math.random() * (mazeWidth - 4)) + 2;
const y = Math.floor(Math.random() * (mazeHeight - 4)) + 2;
// Check if it's a path and not too close to player or exit
const distanceToPlayer = Math.abs(x - playerPosition.x) + Math.abs(y - playerPosition.y);
const distanceToExit = Math.abs(x - exitPosition.x) + Math.abs(y - exitPosition.y);
if (maze[y][x] === 0 && distanceToPlayer > 6 && distanceToExit > 4) {
vampirePosition = { x, y };
vampireLastPosition = { ...vampirePosition };
placed = true;
}
}
}
// Mark starting position as visited
visitedCells[`${playerPosition.x},${playerPosition.y}`] = true;
// Render maze
renderMaze();
// Update mini-map if enabled
if (miniMapEnabled) {
updateMiniMap();
}
}
// Add additional path to create more escape routes
function addAdditionalPath() {
// Find two path cells that are separated by a wall
let attempts = 0;
const maxAttempts = 100;
while (attempts < maxAttempts) {
// Pick a random cell that is a path
const x = Math.floor(Math.random() * (mazeWidth - 2)) + 1;
const y = Math.floor(Math.random() * (mazeHeight - 2)) + 1;
if (maze[y][x] === 0) {
// Check all four directions
const directions = [
{ dx: 2, dy: 0 }, // Right
{ dx: -2, dy: 0 }, // Left
{ dx: 0, dy: 2 }, // Down
{ dx: 0, dy: -2 } // Up
];
// Shuffle directions for randomness
directions.sort(() => Math.random() - 0.5);
for (const dir of directions) {
const nx = x + dir.dx;
const ny = y + dir.dy;
// Check if the target cell is within bounds and is a path
if (nx >= 0 && nx < mazeWidth && ny >= 0 && ny < mazeHeight && maze[ny][nx] === 0) {
// Check if there's a wall between them
const wallX = x + dir.dx / 2;
const wallY = y + dir.dy / 2;
if (maze[wallY][wallX] === 1) {
// Create a path through the wall
maze[wallY][wallX] = 0;
return; // Successfully added a path
}
}
}
}
attempts++;
}
}
// Ensure there's a path from player to exit
function ensurePathToExit() {
const path = findPath(playerPosition, exitPosition);
// If no path found, create one
if (path.length <= 1) {
// Create a direct path
let currentX = playerPosition.x;
let currentY = playerPosition.y;
while (currentX !== exitPosition.x || currentY !== exitPosition.y) {
// Move towards exit
if (currentX < exitPosition.x) {
currentX++;
} else if (currentX > exitPosition.x) {
currentX--;
} else if (currentY < exitPosition.y) {
currentY++;
} else if (currentY > exitPosition.y) {
currentY--;
}
// Mark as path
maze[currentY][currentX] = 0;
}
}
}
// Get unvisited neighbors for maze generation
function getUnvisitedNeighbors(x, y) {
const neighbors = [];
const directions = [
{ dx: 2, dy: 0 }, // Right
{ dx: -2, dy: 0 }, // Left
{ dx: 0, dy: 2 }, // Down
{ dx: 0, dy: -2 } // Up
];
// Shuffle directions for more random mazes
directions.sort(() => Math.random() - 0.5);
for (const dir of directions) {
const nx = x + dir.dx;
const ny = y + dir.dy;
if (nx >= 0 && nx < mazeWidth && ny >= 0 && ny < mazeHeight && maze[ny][nx] === 1) {
neighbors.push({ x: nx, y: ny });
}
}
return neighbors;
}
// Render maze on screen
function renderMaze() {
// Clear previous maze
mazeElement.innerHTML = '';
// Set maze dimensions
const totalWidth = mazeWidth * cellSize;
const totalHeight = mazeHeight * cellSize;
mazeElement.style.width = `${totalWidth}px`;
mazeElement.style.height = `${totalHeight}px`;
// Create cells
for (let y = 0; y < mazeHeight; y++) {
for (let x = 0; x < mazeWidth; x++) {
const cell = document.createElement('div');
cell.className = maze[y][x] === 1 ? 'cell wall' : 'cell';
cell.style.width = `${cellSize}px`;
cell.style.height = `${cellSize}px`;
cell.style.left = `${x * cellSize}px`;
cell.style.top = `${y * cellSize}px`;
// Add visited class if cell has been visited
if (visitedCells[`${x},${y}`] && maze[y][x] === 0) {
cell.style.backgroundColor = '#181818';
}
mazeElement.appendChild(cell);
}
}
// Position player
playerElement.style.width = `${cellSize}px`;
playerElement.style.height = `${cellSize}px`;
playerElement.style.left = `${playerPosition.x * cellSize + mazeElement.offsetLeft}px`;
playerElement.style.top = `${playerPosition.y * cellSize + mazeElement.offsetTop}px`;
// Position exit
exitElement.style.width = `${cellSize}px`;
exitElement.style.height = `${cellSize}px`;
exitElement.style.left = `${exitPosition.x * cellSize + mazeElement.offsetLeft}px`;
exitElement.style.top = `${exitPosition.y * cellSize + mazeElement.offsetTop}px`;
// Position vampire (if active)
if (vampireActive) {
vampireElement.style.width = `${cellSize}px`;
vampireElement.style.height = `${cellSize}px`;
vampireElement.style.left = `${vampirePosition.x * cellSize + mazeElement.offsetLeft}px`;
vampireElement.style.top = `${vampirePosition.y * cellSize + mazeElement.offsetTop}px`;
}
}
// Update mini-map
function updateMiniMap() {
if (!miniMapEnabled) return;
const ctx = miniMapCanvas.getContext('2d');
const mapWidth = miniMapCanvas.width;
const mapHeight = miniMapCanvas.height;
const cellWidth = mapWidth / mazeWidth;
const cellHeight = mapHeight / mazeHeight;
// Clear canvas
ctx.clearRect(0, 0, mapWidth, mapHeight);
// Draw maze
for (let y = 0; y < mazeHeight; y++) {
for (let x = 0; x < mazeWidth; x++) {
// Only draw cells that have been visited or are walls adjacent to visited cells
const isVisited = visitedCells[`${x},${y}`];
let shouldDraw = false;
if (isVisited) {
shouldDraw = true;
} else if (maze[y][x] === 1) {
// Check if any adjacent cell has been visited
const directions = [
{ dx: 1, dy: 0 },
{ dx: -1, dy: 0 },
{ dx: 0, dy: 1 },
{ dx: 0, dy: -1 }
];
for (const dir of directions) {
const nx = x + dir.dx;
const ny = y + dir.dy;
if (nx >= 0 && nx < mazeWidth && ny >= 0 && ny < mazeHeight &&
visitedCells[`${nx},${ny}`]) {
shouldDraw = true;
break;
}
}
}
if (shouldDraw) {
ctx.fillStyle = maze[y][x] === 1 ? '#444' : '#111';
ctx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
}
}
}
// Draw player
ctx.fillStyle = '#fff';
ctx.fillRect(playerPosition.x * cellWidth, playerPosition.y * cellHeight, cellWidth, cellHeight);
// Draw exit
ctx.fillStyle = '#700';
ctx.fillRect(exitPosition.x * cellWidth, exitPosition.y * cellHeight, cellWidth, cellHeight);
// Draw vampire if active and visible
if (vampireActive) {
// Only show vampire on mini-map if it's close to the player
const distanceToVampire = Math.abs(playerPosition.x - vampirePosition.x) +
Math.abs(playerPosition.y - vampirePosition.y);
if (distanceToVampire < 8) {
ctx.fillStyle = '#a00';
ctx.fillRect(vampirePosition.x * cellWidth, vampirePosition.y * cellHeight, cellWidth, cellHeight);
}
}
}
// Update game elements positions
function updatePositions() {
// Update player position
playerElement.style.left = `${playerPosition.x * cellSize + mazeElement.offsetLeft}px`;
playerElement.style.top = `${playerPosition.y * cellSize + mazeElement.offsetTop}px`;
// Update vampire position if active
if (vampireActive) {
vampireElement.style.left = `${vampirePosition.x * cellSize + mazeElement.offsetLeft}px`;
vampireElement.style.top = `${vampirePosition.y * cellSize + mazeElement.offsetTop}px`;
}
}
// Start footprint effect
function startFootprintEffect() {
clearInterval(footprintInterval);
footprintInterval = setInterval(() => {
if (gameActive && playerMoving) {
createFootprint(playerLastPosition.x, playerLastPosition.y);
playerMoving = false;
}
}, 200);
}
// Create footprint
function createFootprint(x, y) {
const footprint = document.createElement('div');
footprint.className = 'footprint';
footprint.style.width = `${cellSize / 3}px`;
footprint.style.height = `${cellSize / 2}px`;
footprint.style.left = `${x * cellSize + mazeElement.offsetLeft + cellSize / 2}px`;
footprint.style.top = `${y * cellSize + mazeElement.offsetTop + cellSize / 2}px`;
gameScreen.appendChild(footprint);
// Remove footprint after animation
setTimeout(() => {
if (footprint.parentNode) {
footprint.parentNode.removeChild(footprint);
}
}, 3000);
}
// Start vampire trail effect
function startVampireTrailEffect() {
clearInterval(vampireTrailInterval);
vampireTrailInterval = setInterval(() => {
if (gameActive && vampireActive) {
createVampireTrail(vampireLastPosition.x, vampireLastPosition.y);
}
}, 300);
}
// Create vampire trail
function createVampireTrail(x, y) {
const trail = document.createElement('div');
trail.className = 'vampire-trail';
trail.style.width = `${cellSize / 2}px`;
trail.style.height = `${cellSize / 2}px`;
trail.style.left = `${x * cellSize + mazeElement.offsetLeft + cellSize / 4}px`;
trail.style.top = `${y * cellSize + mazeElement.offsetTop + cellSize / 4}px`;
gameScreen.appendChild(trail);
// Remove trail after animation
setTimeout(() => {
if (trail.parentNode) {
trail.parentNode.removeChild(trail);
}
}, 2000);
}
// Handle player movement
function movePlayer(dx, dy) {
if (!gameActive) return;
const newX = playerPosition.x + dx;
const newY = playerPosition.y + dy;
// Check if the new position is valid (not a wall and within bounds)
if (newX >= 0 && newX < mazeWidth && newY >= 0 && newY < mazeHeight && maze[newY][newX] === 0) {
// Store last position
playerLastPosition = { ...playerPosition };
// Update player position
playerPosition.x = newX;
playerPosition.y = newY;
// Mark as moving
playerMoving = true;
// Mark cell as visited
visitedCells[`${newX},${newY}`] = true;
// Update player position on screen
playerElement.style.left = `${playerPosition.x * cellSize + mazeElement.offsetLeft}px`;
playerElement.style.top = `${playerPosition.y * cellSize + mazeElement.offsetTop}px`;
// Play move sound
playSound('move');
// Update mini-map if enabled
if (miniMapEnabled) {
updateMiniMap();
}
// Check if player reached the exit
if (playerPosition.x === exitPosition.x && playerPosition.y === exitPosition.y) {
completeLevel();
return;
}
// Check if player collided with vampire
if (vampireActive && playerPosition.x === vampirePosition.x && playerPosition.y === vampirePosition.y) {
gameOver();
return;
}
// Update cell appearance
const cells = document.querySelectorAll('.cell');
const index = newY * mazeWidth + newX;
if (cells[index] && !cells[index].classList.contains('wall')) {
cells[index].style.backgroundColor = '#181818';
}
} else {
// Play wall hit sound
playSound('wall');
}
}
// Start vampire movement
function startVampireMovement() {
clearInterval(vampireInterval);
vampireInterval = setInterval(() => {
moveVampire();
}, vampireSpeed);
}
// Move vampire towards player
function moveVampire() {
if (!gameActive || !vampireActive) return;
// Store last position
vampireLastPosition = { ...vampirePosition };
// Simple A* pathfinding to move vampire towards player
const path = findPath(vampirePosition, playerPosition);
if (path.length > 1) {
// Move to the next position in the path, but with a delay based on level
// This makes the vampire less perfect in its pathfinding
const nextIndex = Math.min(vampirePathfindingDelay, path.length - 1);
vampirePosition.x = path[nextIndex].x;
vampirePosition.y = path[nextIndex].y;
// Update vampire position on screen
vampireElement.style.left = `${vampirePosition.x * cellSize + mazeElement.offsetLeft}px`;
vampireElement.style.top = `${vampirePosition.y * cellSize + mazeElement.offsetTop}px`;
// Update mini-map if enabled
if (miniMapEnabled) {
updateMiniMap();
}
// Check if vampire caught the player
if (vampirePosition.x === playerPosition.x && vampirePosition.y === playerPosition.y) {
gameOver();
}
// Play vampire sound if close to player
const distanceToPlayer = Math.abs(vampirePosition.x - playerPosition.x) +
Math.abs(vampirePosition.y - playerPosition.y);
if (distanceToPlayer < 5) {
playSound('vampire');
}
}
}
// Find path from start to end using A* algorithm
function findPath(start, end) {
const openSet = [{ x: start.x, y: start.y, g: 0, h: 0, f: 0, parent: null }];
const closedSet = [];
const directions = [
{ dx: 1, dy: 0 }, // Right
{ dx: -1, dy: 0 }, // Left
{ dx: 0, dy: 1 }, // Down
{ dx: 0, dy: -1 } // Up
];
while (openSet.length > 0) {
// Find node with lowest f score
let currentIndex = 0;
for (let i = 1; i < openSet.length; i++) {
if (openSet[i].f < openSet[currentIndex].f) {
currentIndex = i;
}
}
const current = openSet[currentIndex];
// Check if reached the end
if (current.x === end.x && current.y === end.y) {
// Reconstruct path
const path = [];
let temp = current;
while (temp !== null) {
path.unshift({ x: temp.x, y: temp.y });
temp = temp.parent;
}
return path;
}
// Move current from open to closed set
openSet.splice(currentIndex, 1);
closedSet.push(current);
// Check neighbors
for (const dir of directions) {
const nx = current.x + dir.dx;
const ny = current.y + dir.dy;
// Skip if out of bounds or is a wall
if (nx < 0 || nx >= mazeWidth || ny < 0 || ny >= mazeHeight || maze[ny][nx] === 1) {
continue;
}
// Skip if in closed set
if (closedSet.some(node => node.x === nx && node.y === ny)) {
continue;
}
// Calculate g, h, and f scores
const g = current.g + 1;
const h = Math.abs(nx - end.x) + Math.abs(ny - end.y); // Manhattan distance
const f = g + h;
// Check if already in open set with better score
const existingNode = openSet.find(node => node.x === nx && node.y === ny);
if (existingNode && g >= existingNode.g) {
continue;
}
// Add to open set
if (!existingNode) {
openSet.push({ x: nx, y: ny, g, h, f, parent: current });
} else {
existingNode.g = g;
existingNode.f = g + existingNode.h;
existingNode.parent = current;
}
}
}
// No path found
return [start];
}
// Game over when caught by vampire
function gameOver() {
gameActive = false;
stopGame();
// Create blood splatter effect
const splatter = document.createElement('div');
splatter.className = 'blood-splatter';
splatter.style.width = `${cellSize * 3}px`;
splatter.style.height = `${cellSize * 3}px`;
splatter.style.left = `${(playerPosition.x * cellSize + mazeElement.offsetLeft) - cellSize}px`;
splatter.style.top = `${(playerPosition.y * cellSize + mazeElement.offsetTop) - cellSize}px`;
gameScreen.appendChild(splatter);
// Play game over sound
playSound('gameOver');
// Show game over screen after a short delay
setTimeout(() => {
screenTransitionEffect(() => {
gameScreen.style.display = 'none';
gameOverScreen.style.display = 'flex';
});
}, 1500);
}
// Complete level
function completeLevel() {
gameActive = false;
stopGame();
// Play exit sound
playSound('exit');
// Show completion screen
screenTransitionEffect(() => {
gameScreen.style.display = 'none';
completionScreen.style.display = 'flex';
});
}
// Stop game
function stopGame() {
gameActive = false;
clearInterval(vampireInterval);
clearTimeout(lightningTimer);
clearInterval(footprintInterval);
clearInterval(vampireTrailInterval);
stopTimer();
}
// Start lightning effect
function startLightningEffect() {
function lightning() {
flashElement.style.animation = 'lightning 2s';
// Play lightning sound
playSound('lightning');
// Reset animation after it completes
setTimeout(() => {
flashElement.style.animation = 'none';
}, 2000);
// Schedule next lightning
lightningTimer = setTimeout(lightning, Math.random() * 10000 + 5000);
}
// Start first lightning after a random delay
lightningTimer = setTimeout(lightning, Math.random() * 5000 + 2000);
}
// Show source code
function showSourceCode() {
const sourceCode = document.documentElement.outerHTML;
codeContent.textContent = sourceCode;
codeModal.style.display = 'block';
}
// Close modal
function closeModal() {
codeModal.style.display = 'none';
}
// Copy source code
function copySourceCode() {
const sourceCode = document.documentElement.outerHTML;
navigator.clipboard.writeText(sourceCode)
.then(() => {
showNotification('代碼已複製到剪貼板!');
})
.catch(err => {
console.error('複製失敗:', err);
showNotification('複製失敗,請手動選擇並複製代碼。');
});
}
// Handle window resize
function handleResize() {
if (gameActive) {
// Recalculate cell size
cellSize = calculateCellSize();
// Re-render maze with new cell size
renderMaze();
// Update positions of player and vampire
updatePositions();
// Update mini-map if enabled
if (miniMapEnabled) {
updateMiniMap();
}
}
}
// Keyboard event listeners
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowUp':
case 'w':
case 'W':
movePlayer(0, -1);
break;
case 'ArrowDown':
case 's':
case 'S':
movePlayer(0, 1);
break;
case 'ArrowLeft':
case 'a':
case 'A':
movePlayer(-1, 0);
break;
case 'ArrowRight':
case 'd':
case 'D':
movePlayer(1, 0);
break;
}
});
// Close modal when clicking outside
window.addEventListener('click', (e) => {
if (e.target === codeModal) {
closeModal();
}
});
// Handle window resize
window.addEventListener('resize', handleResize);
// Initialize game
window.onload = function() {
showMainMenu();
};
</script>
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9474aad9d420f248',t:'MTc0ODUwNzgyMS4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script><iframe height="1" width="1" style="position: absolute; top: 0px; left: 0px; border: none; visibility: hidden;"></iframe>
</body></html>