<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Minecraft Style Maze Game - 6-Story Mansion Update</title>
<!-- Tailwind CSS for UI styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Pixel Font -->
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=DotGothic16&display=swap" rel="stylesheet">
<style>
body {
font-family: 'DotGothic16', 'Press Start 2P', sans-serif;
background-color: #111116;
color: #e5e5e5;
user-select: none;
touch-action: manipulation;
}
.pixel-text {
font-family: 'Press Start 2P', cursive;
}
/* Style for Minecraft look buttons */
.mc-btn {
background-color: #4a4a4a;
border: 4px solid #000;
border-top-color: #8b8b8b;
border-left-color: #8b8b8b;
border-right-color: #2c2c2c;
border-bottom-color: #2c2c2c;
text-shadow: 2px 2px #000;
image-rendering: pixelated;
}
.mc-btn:active {
border-top-color: #2c2c2c;
border-left-color: #2c2c2c;
border-right-color: #8b8b8b;
border-bottom-color: #8b8b8b;
padding-top: 6px;
padding-bottom: 2px;
}
/* Canvas rendering optimizations */
canvas {
image-rendering: pixelated;
box-shadow: 0 15px 35px -5px rgba(0, 0, 0, 0.9);
border: 6px solid #444;
background-color: #1a1a1a;
}
/* Virtual Controller Style */
.controller-btn {
background-color: #3e3e3e;
border: 3px solid #1a1a1a;
border-radius: 8px;
box-shadow: inset 0 3px 0 rgba(255,255,255,0.2), 0 3px 5px rgba(0,0,0,0.5);
}
.controller-btn:active {
background-color: #1e1e1e;
transform: translateY(2px);
box-shadow: none;
}
.attack-btn {
background: linear-gradient(135deg, #55ffff, #00aaaa);
border: 3px solid #000;
border-radius: 50%;
box-shadow: 0 4px 0 #005555;
text-shadow: 1px 1px #000;
}
.attack-btn:active {
transform: scale(0.9) translateY(2px);
box-shadow: none;
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-between p-4 overflow-x-hidden">
<header class="w-full max-w-lg text-center mt-1">
<h1 class="text-3xl sm:text-4xl text-[#55FF55] tracking-wider font-bold mb-1 drop-shadow-[0_4px_0_rgba(0,0,0,1)] flex items-center justify-center gap-2">
<span>🏠</span> MINECRAFT MAZE
</h1>
<p class="text-xs text-cyan-400 pixel-text tracking-widest mb-3">6-STORY MANSION UPDATE</p>
</header>
<!-- Main Game Area -->
<div class="w-full max-w-2xl flex flex-col items-center">
<!-- Status Bar -->
<div class="w-full max-w-[540px] bg-[#2d2d2d] border-4 border-black p-3 mb-2 flex flex-col gap-2 text-xs sm:text-sm">
<div class="flex justify-between items-center w-full">
<!-- Hearts -->
<div class="flex items-center gap-1">
<span class="text-xs text-gray-400 mr-1">LIFE:</span>
<span id="hearts-container" class="flex gap-0.5 text-red-500 text-lg">❤️❤️❤️</span>
</div>
<!-- Diamonds collected -->
<div class="flex items-center gap-2">
<span class="text-cyan-400">💎</span>
<span id="diamond-counter" class="font-bold text-cyan-300">0 / 0</span>
</div>
<!-- Level Display -->
<div class="text-[#FFFF55] font-bold">
STAGE: <span id="stage-num">1</span>
</div>
</div>
<!-- Item & Floor slot -->
<div class="flex items-center justify-between bg-black/40 px-2 py-1 border border-gray-600 rounded text-[11px]">
<span class="text-gray-400">装備/階層:</span>
<span id="sword-status" class="text-gray-300 font-bold">❌ なし (素手)</span>
<span id="floor-status" class="text-green-400 font-bold">階層: 🏠 1F 地上</span>
<span id="respawn-status" class="text-yellow-400 text-[9px] font-bold">📍 初期地点</span>
</div>
</div>
<!-- Canvas Container - Broad 540px layout -->
<div class="relative w-full aspect-square max-w-[540px] flex items-center justify-center bg-black rounded-lg">
<canvas id="gameCanvas" class="w-full h-full max-w-full max-h-full"></canvas>
<!-- System Chat Log Overlay -->
<div id="game-chat" class="absolute left-4 top-4 bg-black/75 p-2 rounded text-[11px] text-white max-w-[85%] border border-gray-700 transition-opacity duration-300 pointer-events-none opacity-0">
[システム] ベッドでリスポーン地点を設定した!
</div>
<!-- Level Clear / Game Over Overlays -->
<div id="overlay" class="absolute inset-0 bg-black/85 flex flex-col items-center justify-center hidden p-6 text-center z-10">
<h2 id="overlay-title" class="text-4xl font-bold mb-4 drop-shadow-[0_4px_0_rgba(0,0,0,1)]"></h2>
<p id="overlay-sub" class="text-sm text-gray-300 mb-6"></p>
<button id="overlay-btn" class="mc-btn px-6 py-3 text-lg font-bold text-white rounded w-52 transition-all">
次へ進む ➔
</button>
</div>
</div>
</div>
<div class="w-full max-w-lg mt-2 flex flex-col gap-3">
<!-- Game Configuration / Info -->
<div class="flex justify-between items-center gap-1 px-1">
<div class="flex gap-1">
<button id="difficulty-btn" class="mc-btn px-2.5 py-1.5 text-xs font-bold text-white rounded">
難易度: 普通
</button>
<button id="fov-btn" class="mc-btn px-2.5 py-1.5 text-xs font-bold text-cyan-300 rounded">
🔍 視野: 広め
</button>
<button id="sound-btn" class="mc-btn px-2.5 py-1.5 text-xs font-bold text-white rounded">
🔊 音: ON
</button>
</div>
<button id="reset-btn" class="mc-btn px-2.5 py-1.5 text-xs font-bold text-yellow-300 rounded">
🔄 リセット
</button>
</div>
<!-- Touch Controls -->
<div id="touch-controls" class="flex items-center justify-between px-4 py-1">
<!-- D-Pad -->
<div class="grid grid-cols-3 gap-2 w-32 aspect-square">
<div></div>
<button id="btn-up" class="controller-btn flex items-center justify-center text-xl text-white">▲</button>
<div></div>
<button id="btn-left" class="controller-btn flex items-center justify-center text-xl text-white">◀</button>
<div class="flex items-center justify-center text-[9px] text-gray-500 font-bold">MOVE</div>
<button id="btn-right" class="controller-btn flex items-center justify-center text-xl text-white">▶</button>
<div></div>
<button id="btn-down" class="controller-btn flex items-center justify-center text-xl text-white font-bold">▼</button>
<div></div>
</div>
<!-- Attack Action Button -->
<div class="flex flex-col items-center justify-center gap-1">
<button id="btn-attack" class="attack-btn w-20 h-20 flex flex-col items-center justify-center text-white active:bg-cyan-600">
<span class="text-3xl">🗡️</span>
<span class="text-[9px] font-bold tracking-wider">ATTACK</span>
</button>
<span class="text-[9px] text-gray-400">剣を振る</span>
</div>
</div>
</div>
<!-- Footer Description -->
<footer class="text-center text-xs text-gray-500 mt-2 px-2 max-w-md">
<p>PC: <span class="bg-gray-800 px-1 py-0.5 rounded text-gray-300">WASD</span> / <span class="bg-gray-800 px-1 py-0.5 rounded text-gray-300">矢印</span> で移動 / <span class="bg-gray-800 px-1 py-0.5 rounded text-gray-300">Space</span> で攻撃</p>
<p class="mt-1 text-[10px] text-cyan-500">※3階で「エンチャントテーブル」に重なると最強の【ダイヤの剣】に覚醒!</p>
</footer>
<script>
// --- STREAMING_CHUNK: Initializing Audio Engine for custom sounds... ---
class SoundEngine {
constructor() {
this.ctx = null;
this.enabled = true;
}
init() {
if (!this.ctx) {
this.ctx = new (window.AudioContext || window.webkitAudioContext)();
}
}
playStep() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'triangle';
osc.frequency.setValueAtTime(110, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(35, this.ctx.currentTime + 0.08);
gain.gain.setValueAtTime(0.12, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.08);
osc.start();
osc.stop(this.ctx.currentTime + 0.08);
}
playStairs() {
if (!this.enabled) return;
this.init();
const now = this.ctx.currentTime;
const playClack = (time, freq) => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'triangle';
osc.frequency.setValueAtTime(freq, time);
gain.gain.setValueAtTime(0.12, time);
gain.gain.exponentialRampToValueAtTime(0.01, time + 0.08);
osc.start(time);
osc.stop(time + 0.08);
};
playClack(now, 160);
playClack(now + 0.08, 220);
playClack(now + 0.16, 280);
}
playDiamond() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(987.77, this.ctx.currentTime); // B5
osc.frequency.setValueAtTime(1318.51, this.ctx.currentTime + 0.08); // E6
gain.gain.setValueAtTime(0.1, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.3);
osc.start();
osc.stop(this.ctx.currentTime + 0.3);
}
playEnchant() {
if (!this.enabled) return;
this.init();
const now = this.ctx.currentTime;
const playTone = (freq, start, duration, type='sine') => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, start);
gain.gain.setValueAtTime(0.1, start);
gain.gain.exponentialRampToValueAtTime(0.001, start + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(start);
osc.stop(start + duration);
};
playTone(440, now, 0.1);
playTone(554, now + 0.08, 0.1);
playTone(659, now + 0.16, 0.1);
playTone(880, now + 0.24, 0.4, 'triangle');
}
playHiss() {
if (!this.enabled) return;
this.init();
const bufferSize = this.ctx.sampleRate * 0.8;
const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = this.ctx.createBufferSource();
noise.buffer = buffer;
const filter = this.ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.value = 3500;
const gain = this.ctx.createGain();
gain.gain.setValueAtTime(0.1, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.8);
noise.connect(filter);
filter.connect(gain);
gain.connect(this.ctx.destination);
noise.start();
}
playExplode() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(100, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(10, this.ctx.currentTime + 0.6);
const bufferSize = this.ctx.sampleRate * 0.6;
const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = this.ctx.createBufferSource();
noise.buffer = buffer;
const filter = this.ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(800, this.ctx.currentTime);
filter.frequency.exponentialRampToValueAtTime(80, this.ctx.currentTime + 0.5);
const noiseGain = this.ctx.createGain();
noiseGain.gain.setValueAtTime(0.35, this.ctx.currentTime);
noiseGain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.6);
gain.gain.setValueAtTime(0.25, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.5);
osc.connect(gain);
gain.connect(this.ctx.destination);
noise.connect(filter);
filter.connect(noiseGain);
noiseGain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + 0.6);
noise.start();
}
playHurt() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(160, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(80, this.ctx.currentTime + 0.2);
gain.gain.setValueAtTime(0.18, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.2);
osc.start();
osc.stop(this.ctx.currentTime + 0.2);
}
playWin() {
if (!this.enabled) return;
this.init();
const now = this.ctx.currentTime;
const playTone = (freq, start, duration) => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(freq, start);
gain.gain.setValueAtTime(0.12, start);
gain.gain.exponentialRampToValueAtTime(0.01, start + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(start);
osc.stop(start + duration);
};
playTone(523.25, now, 0.15); // C5
playTone(659.25, now + 0.15, 0.15); // E5
playTone(783.99, now + 0.3, 0.15); // G5
playTone(1046.50, now + 0.45, 0.4); // C6
}
playEquip() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(550, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(1100, this.ctx.currentTime + 0.12);
gain.gain.setValueAtTime(0.08, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.12);
osc.start();
osc.stop(this.ctx.currentTime + 0.12);
}
playDoor() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'triangle';
osc.frequency.setValueAtTime(180, this.ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(130, this.ctx.currentTime + 0.18);
gain.gain.setValueAtTime(0.08, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.18);
osc.start();
osc.stop(this.ctx.currentTime + 0.18);
}
playHeal() {
if (!this.enabled) return;
this.init();
const now = this.ctx.currentTime;
const playTone = (freq, start, duration) => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, start);
gain.gain.setValueAtTime(0.08, start);
gain.gain.exponentialRampToValueAtTime(0.01, start + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(start);
osc.stop(start + duration);
};
playTone(523.25, now, 0.18); // C5
playTone(659.25, now + 0.08, 0.18); // E5
playTone(783.99, now + 0.16, 0.35); // G5
}
playClick() {
if (!this.enabled) return;
this.init();
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(750, this.ctx.currentTime);
gain.gain.setValueAtTime(0.04, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.04);
osc.start();
osc.stop(this.ctx.currentTime + 0.04);
}
}
const sound = new SoundEngine();
// --- STREAMING_CHUNK: Setting up the layout grid values and block types... ---
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
const TILE_SIZE = 32;
const FOV_LEVELS = [
{ name: '標準 (11x11)', tiles: 11 },
{ name: '広め (15x15)', tiles: 15 },
{ name: '広域 (19x19)', tiles: 19 }
];
let currentFovIndex = 1;
let viewportTiles = FOV_LEVELS[currentFovIndex].tiles;
const DIFFICULTIES = [
{ name: 'イージー', size: 15, diamonds: 4, creepers: 1 },
{ name: 'ノーマル', size: 25, diamonds: 8, creepers: 3 },
{ name: 'ハード', size: 39, diamonds: 14, creepers: 6 }
];
// Block definition including library and enchanting tables
const BLOCK = {
GRASS: 0,
COBBLE: 1,
DIAMOND: 2,
PORTAL: 3,
WOOD_WALL: 4,
WOOD_FLOOR: 5,
DOOR: 6,
CHEST: 7,
BED: 8,
CRAFTING: 9,
GLASS: 10,
STAIRS_UP: 11,
STAIRS_DOWN: 12,
FENCE: 13,
CHEST_2F: 14,
BOOKSHELF: 15,
ENCHANT_TABLE: 16,
CHEST_3F: 17,
CHEST_4F: 18,
BREWING_STAND: 19,
CHEST_5F: 20,
NETHER_BRICK: 21,
NETHER_WART: 22,
CHEST_6F: 23,
DRAGON_EGG: 24
};
let currentDifficultyIndex = 1;
let stage = 1;
let lives = 3;
let diamondsCollected = 0;
let totalStageDiamonds = 0;
let mazeWidth = 25;
let mazeHeight = 25;
let grid = [];
let player = { x: 1, y: 1, targetX: 1, targetY: 1, moveProgress: 0, dir: 'down' };
let creepers = [];
let particles = [];
let animationFrameId = null;
let gameOver = false;
let gameClear = false;
let tickCount = 0;
// --- HOUSE & ITEM STATE VARIABLES ---
let swordLevel = 0; // 0: None, 1: Iron Sword, 2: Diamond Sword
let speedBuffTimer = 0; // Speed potion buff duration
let attackTicks = 0;
let respawnX = 1;
let respawnY = 1;
let openedChests = new Set();
let openDoors = new Set();
let houseArea = { minX: 0, maxX: 0, minY: 0, maxY: 0 };
// 6 Floors Mansion Model
let currentFloor = 1; // 1:1F, 2:2F, 3:3F, 4:4F, 5:5F, 6:6F
let grid2F = [];
let grid3F = [];
let grid4F = [];
let grid5F = [];
let grid6F = [];
let openedChests2F = new Set();
let openedChests3F = new Set();
let openedChests4F = new Set();
let openedChests5F = new Set();
let openedChests6F = new Set();
function resizeCanvas() {
canvas.width = viewportTiles * TILE_SIZE;
canvas.height = viewportTiles * TILE_SIZE;
}
function drawPixelRect(ctx, x, y, w, h, color) {
ctx.fillStyle = color;
ctx.fillRect(Math.floor(x), Math.floor(y), Math.floor(w), Math.floor(h));
}
function showSystemChat(msg) {
const chatBox = document.getElementById('game-chat');
chatBox.textContent = `[システム] ${msg}`;
chatBox.style.opacity = '1';
if (window.chatTimeout) clearTimeout(window.chatTimeout);
window.chatTimeout = setTimeout(() => {
chatBox.style.opacity = '0';
}, 3500);
}
// --- STREAMING_CHUNK: Handling particle physics and walking dust... ---
function spawnWalkParticle(x, y, blockType) {
const count = 3 + Math.floor(Math.random() * 3);
let isGrass = (blockType === BLOCK.GRASS);
let isWood = (blockType === BLOCK.WOOD_FLOOR || blockType === BLOCK.BED || blockType === BLOCK.CHEST || blockType === BLOCK.BOOKSHELF);
for (let i = 0; i < count; i++) {
let color = '#866043';
if (isGrass) {
color = Math.random() < 0.4 ? '#5c8e32' : '#866043';
} else if (isWood) {
color = Math.random() < 0.5 ? '#cd853f' : '#8b5a2b';
} else if (blockType === BLOCK.COBBLE) {
color = '#737373';
} else if (blockType === BLOCK.ENCHANT_TABLE || blockType === BLOCK.DRAGON_EGG) {
color = Math.random() < 0.5 ? '#7f1d1d' : '#a21caf'; // Magic particles
} else if (blockType === BLOCK.NETHER_BRICK || blockType === BLOCK.NETHER_WART) {
color = '#800000'; // Nether Crimson Red
}
particles.push({
x: x * TILE_SIZE + TILE_SIZE / 2 + (Math.random() - 0.5) * 16,
y: y * TILE_SIZE + TILE_SIZE - 4 + (Math.random() - 0.5) * 6,
vx: (Math.random() - 0.5) * 1.5,
vy: -Math.random() * 1.5 - 0.5,
color: color,
alpha: 1.0,
size: 2 + Math.floor(Math.random() * 3),
life: 1.0,
decay: 0.04 + Math.random() * 0.04
});
}
}
// Spawns potion swirly particles
function spawnPotionParticles(x, y) {
if (Math.random() < 0.15) {
particles.push({
x: x * TILE_SIZE + TILE_SIZE / 2 + (Math.random() - 0.5) * 20,
y: y * TILE_SIZE + TILE_SIZE / 2 + (Math.random() - 0.5) * 20,
vx: (Math.random() - 0.5) * 0.6,
vy: -Math.random() * 0.8 - 0.2,
color: '#eab308', // Yellow speed sparkles
alpha: 1.0,
size: 2 + Math.floor(Math.random() * 2),
life: 0.8,
decay: 0.03
});
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.08;
p.life -= p.decay;
if (p.life <= 0) {
particles.splice(i, 1);
}
}
}
function drawParticles(ctx, cameraX, cameraY) {
particles.forEach(p => {
ctx.save();
ctx.globalAlpha = p.life;
const rx = p.x - cameraX * TILE_SIZE;
const ry = p.y - cameraY * TILE_SIZE;
drawPixelRect(ctx, rx, ry, p.size, p.size, p.color);
ctx.restore();
});
}
// --- STREAMING_CHUNK: Rendering block textures... ---
// Cobblestone Wall
function drawCobbleWall(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#737373');
const p = size / 8;
const dark = '#4d4d4d';
const light = '#9b9b9b';
drawPixelRect(ctx, x, y, size, p, light);
drawPixelRect(ctx, x, y, p, size, light);
drawPixelRect(ctx, x, y + p*4, size, p, dark);
drawPixelRect(ctx, x, y + size - p, size, p, dark);
drawPixelRect(ctx, x + p*4, y, p, p*4, dark);
drawPixelRect(ctx, x + p*2, y + p*4, p, p*4, dark);
drawPixelRect(ctx, x + p*6, y + p*4, p, p*4, dark);
drawPixelRect(ctx, x + p*2, y + p*2, p, p, dark);
drawPixelRect(ctx, x + p*6, y + p*1, p, p, light);
drawPixelRect(ctx, x + p*1, y + p*6, p, p, light);
drawPixelRect(ctx, x + p*5, y + p*5, p, p, dark);
}
// Grass Floor
function drawGrassFloor(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#866043');
const p = size / 8;
drawPixelRect(ctx, x, y, size, p*2, '#5c8e32');
drawPixelRect(ctx, x + p, y + p*2, p, p, '#5c8e32');
drawPixelRect(ctx, x + p*3, y + p*2, p, p, '#5c8e32');
drawPixelRect(ctx, x + p*5, y + p*2, p, p, '#5c8e32');
drawPixelRect(ctx, x + p*7, y + p*2, p, p, '#5c8e32');
drawPixelRect(ctx, x + p*2, y + p*5, p, p, '#573d26');
drawPixelRect(ctx, x + p*5, y + p*6, p, p, '#573d26');
drawPixelRect(ctx, x + p*6, y + p*4, p, p, '#a0785a');
drawPixelRect(ctx, x + p*1, y + p*7, p, p, '#a0785a');
}
// Planks (Wall)
function drawWoodWall(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#8b5a2b');
const p = size / 8;
drawPixelRect(ctx, x, y + p*2, size, p, '#573d26');
drawPixelRect(ctx, x, y + p*4, size, p, '#573d26');
drawPixelRect(ctx, x, y + p*6, size, p, '#573d26');
drawPixelRect(ctx, x, y + size - p, size, p, '#573d26');
drawPixelRect(ctx, x, y, size, p/2, '#b8860b');
drawPixelRect(ctx, x, y + p*2.5, size, p/2, '#b8860b');
drawPixelRect(ctx, x, y + p*4.5, size, p/2, '#b8860b');
drawPixelRect(ctx, x, y + p*6.5, size, p/2, '#b8860b');
}
// Nether Brick (Wall)
function drawNetherBrick(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#2c1115'); // Very dark burgundy/brown
const p = size / 8;
const darkOutline = '#150508';
const lighterRed = '#401e23';
// Horizontal brick rows
drawPixelRect(ctx, x, y + p*2, size, p/2, darkOutline);
drawPixelRect(ctx, x, y + p*4, size, p/2, darkOutline);
drawPixelRect(ctx, x, y + p*6, size, p/2, darkOutline);
drawPixelRect(ctx, x, y + size - p/2, size, p/2, darkOutline);
// Brick segments
drawPixelRect(ctx, x + p*3, y, p/2, p*2, darkOutline);
drawPixelRect(ctx, x + p*5, y + p*2, p/2, p*2, darkOutline);
drawPixelRect(ctx, x + p*2, y + p*4, p/2, p*2, darkOutline);
drawPixelRect(ctx, x + p*6, y + p*6, p/2, p*2, darkOutline);
// Red highlights
drawPixelRect(ctx, x + p, y + p, p, p/2, lighterRed);
drawPixelRect(ctx, x + p*6, y + p*3, p, p/2, lighterRed);
drawPixelRect(ctx, x + p*3, y + p*5, p, p/2, lighterRed);
}
// Nether Wart (5F Garden block)
function drawNetherWart(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#4a2c2a'); // Soul sand floor look
const p = size / 8;
const cropRed = '#900c3f';
const cropBright = '#c70039';
// Draw wart stalks
drawPixelRect(ctx, x + p*2, y + p*3, p, p*4, cropRed);
drawPixelRect(ctx, x + p*5, y + p*2, p, p*5, cropRed);
drawPixelRect(ctx, x + p*1, y + p*2, p*2, p, cropBright);
drawPixelRect(ctx, x + p*4, y + p, p*3, p, cropBright);
drawPixelRect(ctx, x + p*2, y + p*4, p, p, '#ff5733'); // glowing hot point
}
// Dragon Egg (6F Ender Altar)
function drawDragonEgg(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
// Draw obsidian pedestal
drawPixelRect(ctx, x + p*2, y + p*5, p*4, p*2.5, '#190a2a');
drawPixelRect(ctx, x + p*1.5, y + p*7, p*5, p, '#0c0216');
// Float physics
const pulse = Math.sin(tickCount * 0.12) * 2;
const ex = x + p*2.5;
const ey = y + p*1.5 + pulse;
// Egg structure
drawPixelRect(ctx, ex + p, ey, p, p, '#2e0854'); // Obsidian-purple egg top
drawPixelRect(ctx, ex, ey + p, p*3, p*2.5, '#120224'); // Center body
drawPixelRect(ctx, ex + p/2, ey + p*3.5, p*2, p, '#000000'); // Bottom
drawPixelRect(ctx, ex + p, ey + p, p/2, p*2, '#d1c4e9'); // Specular highlight
}
// Birch Planks (Floor)
function drawWoodFloor(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#dfb175');
const p = size / 8;
drawPixelRect(ctx, x + p*2, y, p, size, '#c49353');
drawPixelRect(ctx, x + p*6, y, p, size, '#c49353');
drawPixelRect(ctx, x, y + p*4, p*2, p/2, '#c49353');
drawPixelRect(ctx, x + p*2, y + p*2, p*4, p/2, '#c49353');
drawPixelRect(ctx, x + p*6, y + p*6, p*2, p/2, '#c49353');
}
// Wooden Door
function drawDoorBlock(ctx, x, y, size, isOpen) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
if (isOpen) {
drawPixelRect(ctx, x, y, p*2, size, '#8b4513');
drawPixelRect(ctx, x + p*0.5, y + p, p, p*2, '#87ceeb');
drawPixelRect(ctx, x + p*1.5, y + p*4.5, p/2, p, '#ffd700');
} else {
drawPixelRect(ctx, x + p, y, p*6, size, '#a0522d');
drawPixelRect(ctx, x + p*1.5, y + p*1.5, p*3, p*2.5, '#5c310e');
drawPixelRect(ctx, x + p*1.5, y + p*4.5, p*3, p*2.5, '#5c310e');
drawPixelRect(ctx, x + p*2, y + p*2, p*2, p, '#87ceeb');
drawPixelRect(ctx, x + p*5, y + p*4.5, p, p, '#ffd700');
}
}
// Chests (Standard, Blue, Purple)
function drawChestBlock(ctx, x, y, size, isOpened) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
const cx = x + p;
const cy = y + p;
const cs = size - p*2;
if (!isOpened) {
drawPixelRect(ctx, cx, cy, cs, cs, '#7a4215');
drawPixelRect(ctx, cx, cy + p*2.5, cs, p, '#3d210a');
drawPixelRect(ctx, cx + cs/2 - p/2, cy + p*2, p, p*1.5, '#c0c0c0');
drawPixelRect(ctx, cx + cs/2 - p/2, cy + p*3.2, p, p/2, '#000000');
} else {
drawPixelRect(ctx, cx, cy + p, cs, cs - p, '#5c310e');
drawPixelRect(ctx, cx, cy - p/2, cs, p*1.5, '#7a4215');
drawPixelRect(ctx, cx + cs/2 - p/2, cy + p, p, p, '#c0c0c0');
}
}
// 2F Premium Chest (Diamond Chest with Golden Trims)
function drawChest2F(ctx, x, y, size, isOpened) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
const cx = x + p;
const cy = y + p;
const cs = size - p*2;
if (!isOpened) {
// Gold themed chest for 2F reward!
drawPixelRect(ctx, cx, cy, cs, cs, '#4682b4'); // Steel blue chest base
drawPixelRect(ctx, cx, cy, p, cs, '#ffd700'); // Gold left trim
drawPixelRect(ctx, cx + cs - p, cy, p, cs, '#ffd700'); // Gold right trim
drawPixelRect(ctx, cx, cy + p*2.5, cs, p, '#3d210a'); // partition
drawPixelRect(ctx, cx + cs/2 - p, cy + p*1.8, p*2, p*1.5, '#ffd700'); // Gold latch lock
drawPixelRect(ctx, cx + cs/2 - p/2, cy + p*2.8, p, p/2, '#000000');
} else {
drawPixelRect(ctx, cx, cy + p, cs, cs - p, '#2a4d6c'); // Open base
drawPixelRect(ctx, cx, cy - p/2, cs, p*1.5, '#4682b4'); // Open lid
drawPixelRect(ctx, cx, cy - p/2, p, p*1.5, '#ffd700'); // Open lid gold trim L
drawPixelRect(ctx, cx + cs - p, cy - p/2, p, p*1.5, '#ffd700'); // Open lid gold trim R
drawPixelRect(ctx, cx + cs/2 - p/2, cy + p, p, p, '#ffd700'); // lock hanging
}
}
// Red Bed
function drawBedBlock(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
drawPixelRect(ctx, x + p, y + p*2.5, p*6, p*4.5, '#cc2222');
drawPixelRect(ctx, x + p, y + p*3.5, p*6, p/2, '#990000');
drawPixelRect(ctx, x + p, y + p, p*6, p*1.5, '#dddddd');
drawPixelRect(ctx, x + p*1.5, y + p*1.2, p*5, p, '#ffffff');
drawPixelRect(ctx, x + p, y + p*7, p, p, '#5c310e');
drawPixelRect(ctx, x + p*6, y + p*7, p, p, '#5c310e');
}
// Crafting Table
function drawCraftingTable(ctx, x, y, size) {
drawPixelRect(ctx, x, y, size, size, '#8b5a2b');
const p = size / 8;
drawPixelRect(ctx, x + p, y + p, p*6, p*6, '#a0522d');
drawPixelRect(ctx, x + p*2, y, p/2, size, '#5c310e');
drawPixelRect(ctx, x + p*6, y, p/2, size, '#5c310e');
drawPixelRect(ctx, x, y + p*2, size, p/2, '#5c310e');
drawPixelRect(ctx, x + p*1.5, y + p*4.5, p, p*3, '#ffffff');
drawPixelRect(ctx, x + p*2.5, y + p*5.5, p, p, '#5c310e');
drawPixelRect(ctx, x + p*5.5, y + p*4.5, p*1.5, p*1.5, '#87ceeb');
}
// Glass Window
function drawGlassWindow(ctx, x, y, size) {
drawWoodWall(ctx, x, y, size);
const p = size / 8;
drawPixelRect(ctx, x + p*1.5, y + p*1.5, p*5, p*5, 'rgba(135, 206, 235, 0.7)');
drawPixelRect(ctx, x + p*2, y + p*2, p, p, '#ffffff');
drawPixelRect(ctx, x + p*3, y + p*3, p, p, '#ffffff');
drawPixelRect(ctx, x + p*5, y + p*5, p, p, '#ffffff');
}
// Stairs Up
function drawStairsUp(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
const darkWood = '#5c310e';
const lightWood = '#a0522d';
drawPixelRect(ctx, x + p, y + p*5, p*6, p*2, darkWood);
drawPixelRect(ctx, x + p*2, y + p*3, p*5, p*2, lightWood);
drawPixelRect(ctx, x + p*3, y + p, p*4, p*2, '#cd853f');
drawPixelRect(ctx, x + p, y + p*7, p*6, p, '#3d1a00');
}
// Stairs Down
function drawStairsDown(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
drawPixelRect(ctx, x + p*1.5, y + p*1.5, p*5, p*5, '#150d05');
const ladderColor = '#d2b48c';
drawPixelRect(ctx, x + p*2.5, y + p*2, p, p*4, ladderColor);
drawPixelRect(ctx, x + p*4.5, y + p*2, p, p*4, ladderColor);
drawPixelRect(ctx, x + p*2.5, y + p*2.5, p*3, p*0.5, '#a0522d');
drawPixelRect(ctx, x + p*2.5, y + p*3.5, p*3, p*0.5, '#a0522d');
drawPixelRect(ctx, x + p*2.5, y + p*4.5, p*3, p*0.5, '#a0522d');
}
// Fence / Balcony Safety Railing
function drawFence(ctx, x, y, size, backgroundGrass = false) {
if (backgroundGrass) {
drawGrassFloor(ctx, x, y, size);
} else {
drawWoodFloor(ctx, x, y, size);
}
const p = size / 8;
const darkWood = '#5c310e';
const lightWood = '#8b5a2b';
drawPixelRect(ctx, x + p, y + p, p*1.2, p*6, darkWood);
drawPixelRect(ctx, x + p*3.4, y + p, p*1.2, p*6, darkWood);
drawPixelRect(ctx, x + p*5.8, y + p, p*1.2, p*6, darkWood);
drawPixelRect(ctx, x + p, y + p*2, p*6, p, lightWood);
drawPixelRect(ctx, x + p, y + p*4.5, p*6, p, lightWood);
drawPixelRect(ctx, x + p, y + p, p*1.2, p/2, '#cd853f');
drawPixelRect(ctx, x + p*3.4, y + p, p*1.2, p/2, '#cd853f');
drawPixelRect(ctx, x + p*5.8, y + p, p*1.2, p/2, '#cd853f');
}
// Bookshelf
function drawBookshelf(ctx, x, y, size) {
drawWoodWall(ctx, x, y, size);
const p = size / 8;
const row1Y = y + p*1.2;
const row2Y = y + p*4.7;
// Row 1 Books
drawPixelRect(ctx, x + p*1.5, row1Y, p*1.1, p*2.1, '#cc2222'); // Red
drawPixelRect(ctx, x + p*2.6, row1Y, p*0.9, p*2.1, '#33cc33'); // Green
drawPixelRect(ctx, x + p*3.5, row1Y + p*0.3, p*1.2, p*1.8, '#cccc33'); // Yellow
drawPixelRect(ctx, x + p*5.5, row1Y, p*1.0, p*2.1, '#3333cc'); // Blue
// Row 2 Books
drawPixelRect(ctx, x + p*1.2, row2Y + p*0.3, p*1.3, p*1.8, '#33ccff'); // Lightblue
drawPixelRect(ctx, x + p*2.5, row2Y, p*1.0, p*2.1, '#cc22cc'); // Pink
drawPixelRect(ctx, x + p*4.5, row2Y, p*1.1, p*2.1, '#cc5522'); // Orange
drawPixelRect(ctx, x + p*5.6, row2Y + p*0.4, p*1.2, p*1.7, '#dddddd'); // White
}
// Enchanting Table (Obsidian base, diamond corners, floating purple magic book)
function drawEnchantTable(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
const cx = x + p;
const cy = y + p*2;
const cs = size - p*2;
// Obsidian Base
drawPixelRect(ctx, cx, cy, cs, cs - p, '#1c0d24');
drawPixelRect(ctx, cx + p, cy - p, cs - p*2, p, '#ab1ccf'); // Purple carpet top
// Diamond corners
drawPixelRect(ctx, cx, cy + p*2.5, p*1.2, p*1.2, '#55ffff');
drawPixelRect(ctx, cx + cs - p*1.2, cy + p*2.5, p*1.2, p*1.2, '#55ffff');
// Floating purple book
const pulse = Math.sin(tickCount * 0.1) * 3;
const bx = x + size/2;
const by = y + p*1.5 + pulse;
// Open book pages (White) and leather cover (Red/Brown)
drawPixelRect(ctx, bx - p*2, by - p, p*4, p*1.5, '#6a1b9a'); // Cover
drawPixelRect(ctx, bx - p*1.6, by - p*0.8, p*1.5, p*1.2, '#ffffff'); // Left Page
drawPixelRect(ctx, bx + p*0.1, by - p*0.8, p*1.5, p*1.2, '#ffffff'); // Right Page
drawPixelRect(ctx, bx - p*0.1, by - p*0.8, p*0.2, p*1.2, '#9c27b0'); // Bookmark ribbon
}
// 4F Brewing Stand
function drawBrewingStand(ctx, x, y, size) {
drawWoodFloor(ctx, x, y, size);
const p = size / 8;
const cx = x + size/2;
const cy = y + p*1.5;
// Stand platform
drawPixelRect(ctx, cx - p*2, y + p*6, p*4, p*1.5, '#7f8c8d'); // Cobble base
drawPixelRect(ctx, cx - p/2, y + p*2.5, p, p*4, '#95a5a6'); // Center pole
// Bubbling glowing potion flasks (yellow and pink)
const bubble = Math.sin(tickCount * 0.15) * 1;
drawPixelRect(ctx, cx - p*2.5, y + p*4 + bubble, p*1.2, p*1.5, '#eab308'); // left yellow speed
drawPixelRect(ctx, cx + p*1.3, y + p*4.5 - bubble, p*1.2, p*1.5, '#c026d3'); // right pink health
drawPixelRect(ctx, cx - p*0.6, y + p*1.5, p*1.2, p, '#ef4444'); // hot blaze rod fire
}
// 2: Diamond Ore block
function drawDiamondOre(ctx, x, y, size) {
drawCobbleWall(ctx, x, y, size);
const p = size / 8;
const oreColor = '#4dedf4';
const shadowColor = '#1fb2bf';
const veins = [
{px: 2, py: 1}, {px: 5, py: 2},
{px: 1, py: 4}, {px: 2, py: 5},
{px: 6, py: 5}, {px: 5, py: 6}
];
veins.forEach(v => {
drawPixelRect(ctx, x + v.px*p, y + v.py*p, p, p, oreColor);
drawPixelRect(ctx, x + (v.px + 1)*p, y + v.py*p, p, p, shadowColor);
});
}
// 3: Portal / Goal block
function drawGoal(ctx, x, y, size) {
const p = size / 8;
drawPixelRect(ctx, x, y, size, size, '#120b24');
const pulse = (Math.sin(tickCount * 0.15) + 1) / 2;
const portalColor = `rgba(${120 + pulse*80}, 20, ${200 + pulse*55}, 0.8)`;
drawPixelRect(ctx, x + p*1.5, y + p*1.5, p*5, p*5, portalColor);
drawPixelRect(ctx, x + p*2.5, y + p*2.5, p*3, p*3, '#e156ff');
drawPixelRect(ctx, x, y, p, p, '#351c62');
drawPixelRect(ctx, x + size - p, y, p, p, '#351c62');
drawPixelRect(ctx, x, y + size - p, p, p, '#351c62');
}
// --- STREAMING_CHUNK: Modeling player and dynamic sword upgrades... ---
// Draw Player with walking motion and sword swing (Iron or Diamond)
function drawPlayer(ctx, x, y, size, direction, moveProgress) {
const p = size / 8;
let bob = 0;
let limbSwing = 0;
if (moveProgress > 0) {
bob = Math.abs(Math.sin(moveProgress * Math.PI)) * 3;
limbSwing = Math.sin(moveProgress * Math.PI * 2);
}
const drawY = y - bob;
// Head (Hair, skin)
drawPixelRect(ctx, x + p*2, drawY, p*4, p*3, '#a17452'); // Face skin
drawPixelRect(ctx, x + p*2, drawY, p*4, p, '#533722'); // Hair top
drawPixelRect(ctx, x + p*2, drawY + p, p, p*2, '#533722');
drawPixelRect(ctx, x + p*5, drawY + p, p, p*2, '#533722');
// Face
if (direction !== 'up') {
drawPixelRect(ctx, x + p*2.5, drawY + p*1.5, p, p*0.5, '#ffffff'); // Eye L
drawPixelRect(ctx, x + p*4.5, drawY + p*1.5, p, p*0.5, '#ffffff'); // Eye R
drawPixelRect(ctx, x + p*3.5, drawY + p*1.5, p, p, '#1c12cc'); // Pupils
drawPixelRect(ctx, x + p*3, drawY + p*2, p*2, p, '#804c35'); // Beard
}
// Torso (Cyan shirt)
drawPixelRect(ctx, x + p*1.5, drawY + p*3, p*5, p*3, '#009fa2');
// Limbs Swing
const swingOffset = Math.round(limbSwing * 1.5);
if (direction === 'left' || direction === 'right') {
const isRight = direction === 'right';
const bodyCenter = x + p*3.5;
const backLimbColor = '#007072';
const backLegColor = '#1f255b';
const armSwingX = isRight ? -swingOffset : swingOffset;
const legSwingX = isRight ? swingOffset : -swingOffset;
// Back Arm
drawPixelRect(ctx, bodyCenter + armSwingX - p, drawY + p*3, p*2, p*3, backLimbColor);
drawPixelRect(ctx, bodyCenter + armSwingX - p, drawY + p*4.5, p*2, p*1.5, '#876043');
// Back Leg
drawPixelRect(ctx, bodyCenter - legSwingX - p*1.2, drawY + p*6, p*2.4, p*2, backLegColor);
drawPixelRect(ctx, bodyCenter - legSwingX - p*1.2, drawY + p*7.5, p*2.4, p*0.5, '#404040');
// Front Leg
drawPixelRect(ctx, bodyCenter + legSwingX - p*1.2, drawY + p*6, p*2.4, p*2, '#2c3580');
drawPixelRect(ctx, bodyCenter + legSwingX - p*1.2, drawY + p*7.5, p*2.4, p*0.5, '#5c5c5c');
// Front Arm
drawPixelRect(ctx, bodyCenter - armSwingX - p, drawY + p*3, p*2, p*3, '#00afb2');
drawPixelRect(ctx, bodyCenter - armSwingX - p, drawY + p*4.5, p*2, p*1.5, '#a17452');
} else {
const leftSwing = swingOffset;
const rightSwing = -swingOffset;
// Arms L & R
drawPixelRect(ctx, x + p*0.5, drawY + p*3 + leftSwing, p, p*3, '#009fa2');
drawPixelRect(ctx, x + p*6.5, drawY + p*3 + rightSwing, p, p*3, '#009fa2');
drawPixelRect(ctx, x + p*0.5, drawY + p*4.5 + leftSwing, p, p*1.5, '#a17452');
drawPixelRect(ctx, x + p*6.5, drawY + p*4.5 + rightSwing, p, p*1.5, '#a17452');
// Legs L & R
drawPixelRect(ctx, x + p*1.5, drawY + p*6, p*2.2, p*1.8 + rightSwing, '#2c3580');
drawPixelRect(ctx, x + p*4.3, drawY + p*6, p*2.2, p*1.8 + leftSwing, '#2c3580');
drawPixelRect(ctx, x + p*1.5, drawY + p*7.5 + rightSwing, p*2.2, p*0.5, '#5c5c5c');
drawPixelRect(ctx, x + p*4.3, drawY + p*7.5 + leftSwing, p*2.2, p*0.5, '#5c5c5c');
}
// --- SWORD DRAWING SYSTEM (If Equipped) ---
if (swordLevel > 0) {
ctx.save();
let swordAngle = 0;
let sx = x + p*6.5;
let sy = drawY + p*4.5 + swingOffset;
if (direction === 'left') {
sx = x + p*0.5;
sy = drawY + p*4.5 + swingOffset;
}
if (attackTicks > 0) {
const progress = (10 - attackTicks) / 10;
swordAngle = -0.5 + progress * Math.PI;
}
ctx.translate(sx, sy);
ctx.rotate(swordAngle);
const isDiamond = (swordLevel === 2);
const bladeMain = isDiamond ? '#55ffff' : '#d0e0e3';
const bladeShadow = isDiamond ? '#00aaaa' : '#95a5a6';
const bladeSpecular = isDiamond ? '#e0ffff' : '#ffffff';
// Hilt/Handle
drawPixelRect(ctx, -p/2, -p, p, p*2, '#5c310e');
drawPixelRect(ctx, -p*1.5, -p, p*3, p/2, isDiamond ? '#00aaaa' : '#4a4a4a');
// Blade
drawPixelRect(ctx, -p/2, -p*5, p, p*4, bladeMain);
drawPixelRect(ctx, -p/4, -p*5.5, p/2, p/2, bladeSpecular);
drawPixelRect(ctx, -p/2, -p*4, p/4, p*3, bladeSpecular);
drawPixelRect(ctx, +p/4, -p*4, p/4, p*3, bladeShadow);
ctx.restore();
}
}
// Draw Creeper Enemy
function drawCreeper(ctx, x, y, size, warning = false, moveProgress = 0) {
const p = size / 8;
let baseColor = '#4f9131';
let faceColor = '#1d1d1d';
if (warning && Math.floor(tickCount / 3) % 2 === 0) {
baseColor = '#ffffff';
faceColor = '#ff0000';
}
// Head and Body
drawPixelRect(ctx, x + p*1.5, y, p*5, p*6, baseColor);
if (!warning) {
drawPixelRect(ctx, x + p*2, y + p, p, p, '#386a21');
drawPixelRect(ctx, x + p*5, y + p*2, p, p, '#386a21');
drawPixelRect(ctx, x + p*4, y + p*5, p, p, '#386a21');
drawPixelRect(ctx, x + p*1.5, y + p*4, p, p, '#67ab47');
}
// Face
drawPixelRect(ctx, x + p*2.5, y + p, p, p, faceColor);
drawPixelRect(ctx, x + p*4.5, y + p, p, p, faceColor);
drawPixelRect(ctx, x + p*3.5, y + p*2, p, p, faceColor);
drawPixelRect(ctx, x + p*3, y + p*3, p*2, p*1.5, faceColor);
drawPixelRect(ctx, x + p*2.5, y + p*4, p, p, faceColor);
drawPixelRect(ctx, x + p*4.5, y + p*4, p, p, faceColor);
// Leg swing
const cycle = moveProgress > 0 ? moveProgress * Math.PI * 2 : tickCount * 0.2;
const swing = Math.round(Math.sin(cycle) * 1.5);
const flY = p*6 + swing;
const frY = p*6 - swing;
drawPixelRect(ctx, x + p*1.5, y + flY, p*2.2, p*2, baseColor);
drawPixelRect(ctx, x + p*1.5, y + flY + p*1.5, p*2.2, p*0.5, '#1d1d1d');
drawPixelRect(ctx, x + p*4.3, y + frY, p*2.2, p*2, baseColor);
drawPixelRect(ctx, x + p*4.3, y + frY + p*1.5, p*2.2, p*0.5, '#1d1d1d');
}
// --- STREAMING_CHUNK: Generating maze grid map algorithms... ---
function isInsideHouse(x, y) {
return (x >= houseArea.minX && x <= houseArea.maxX && y >= houseArea.minY && y <= houseArea.maxY);
}
function generateMaze(width, height) {
const maze = Array(height).fill(null).map(() => Array(width).fill(BLOCK.COBBLE));
const stack = [];
const startX = 1;
const startY = 1;
maze[startY][startX] = BLOCK.GRASS;
stack.push({ x: startX, y: startY });
while (stack.length > 0) {
const current = stack[stack.length - 1];
const neighbors = [];
const directions = [
{ x: 0, y: -2, wallX: 0, wallY: -1 },
{ x: 2, y: 0, wallX: 1, wallY: 0 },
{ x: 0, y: 2, wallX: 0, wallY: 1 },
{ x: -2, y: 0, wallX: -1, wallY: 0 }
];
for (const dir of directions) {
const nx = current.x + dir.x;
const ny = current.y + dir.y;
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1) {
if (maze[ny][nx] === BLOCK.COBBLE) {
neighbors.push({ nx, ny, wallX: current.x + dir.wallX, wallY: current.y + dir.wallY });
}
}
}
if (neighbors.length > 0) {
const next = neighbors[Math.floor(Math.random() * neighbors.length)];
maze[next.wallY][next.wallX] = BLOCK.GRASS;
maze[next.ny][next.nx] = BLOCK.GRASS;
stack.push({ x: next.nx, y: next.ny });
} else {
stack.pop();
}
}
maze[height - 2][width - 2] = BLOCK.PORTAL;
return maze;
}
// --- STREAMING_CHUNK: Spawning multi-floor Oak Mansion structure (6 stories, 7x7 size!)... ---
function spawnHouse(maze, width, height) {
// Expanded to massive 7x7 Oak Mansion!
const hSize = 7;
const cx = Math.floor(width / 2) - 3;
const cy = Math.floor(height / 2) - 3;
houseArea = {
minX: cx,
maxX: cx + hSize - 1,
minY: cy,
maxY: cy + hSize - 1
};
// Build House Structure on Ground (1F)
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
const hx = cx + dx;
const hy = cy + dy;
if (hx <= 0 || hx >= width - 1 || hy <= 0 || hy >= height - 1) continue;
if (dy === 0 || dy === hSize - 1 || dx === 0 || dx === hSize - 1) {
if (dy === 0 && (dx === 2 || dx === 4)) {
maze[hy][hx] = BLOCK.GLASS;
} else {
maze[hy][hx] = BLOCK.WOOD_WALL;
}
} else {
maze[hy][hx] = BLOCK.WOOD_FLOOR;
}
}
}
// Door (Bottom center - dx=3)
const doorX = cx + 3;
const doorY = cy + hSize - 1;
maze[doorY][doorX] = BLOCK.DOOR;
if (doorY + 1 < height - 1) maze[doorY + 1][doorX] = BLOCK.GRASS;
if (doorY + 2 < height - 1) maze[doorY + 2][doorX] = BLOCK.GRASS;
// Spacious Furniture Ground Floor (1F)
maze[cy + 1][cx + 1] = BLOCK.BED;
maze[cy + 1][cx + 5] = BLOCK.CHEST;
maze[cy + 5][cx + 1] = BLOCK.CRAFTING;
// Stairways (Right bottom: 1F UP to 2F)
maze[cy + 5][cx + 5] = BLOCK.STAIRS_UP;
// --- 2F Layout Setup (With Spacious Safety Fence and premium chest) ---
grid2F = Array(hSize).fill(null).map(() => Array(hSize).fill(BLOCK.WOOD_FLOOR));
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
if (dy === 0) {
grid2F[dy][dx] = (dx === 2 || dx === 4) ? BLOCK.GLASS : BLOCK.WOOD_WALL;
} else if (dx === 0 || dx === hSize - 1) {
grid2F[dy][dx] = BLOCK.WOOD_WALL;
} else if (dy === hSize - 1) {
grid2F[dy][dx] = BLOCK.FENCE; // Spacious balcony
}
}
}
grid2F[5][5] = BLOCK.STAIRS_DOWN; // Stairs down to 1F
grid2F[5][1] = BLOCK.STAIRS_UP; // Stairs leading UP to 3F (Left bottom)
grid2F[1][3] = BLOCK.CHEST_2F; // 2F Chest placement
// --- 3F Library & Enchanting Layout Setup ---
grid3F = Array(hSize).fill(null).map(() => Array(hSize).fill(BLOCK.WOOD_FLOOR));
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
if (dy === 0) {
grid3F[dy][dx] = (dx === 3) ? BLOCK.GLASS : BLOCK.WOOD_WALL;
} else if (dx === 0 || dx === hSize - 1 || dy === hSize - 1) {
grid3F[dy][dx] = BLOCK.WOOD_WALL;
}
}
}
grid3F[5][1] = BLOCK.STAIRS_DOWN; // Stairs down to 2F
grid3F[1][1] = BLOCK.STAIRS_UP; // Stairs leading UP to 4F (Left top)
grid3F[3][3] = BLOCK.ENCHANT_TABLE; // Altar Center
grid3F[2][2] = BLOCK.BOOKSHELF;
grid3F[2][4] = BLOCK.BOOKSHELF;
grid3F[4][2] = BLOCK.BOOKSHELF;
grid3F[4][4] = BLOCK.BOOKSHELF;
grid3F[1][3] = BLOCK.CHEST_3F; // 3F Lapis Chest
// --- 4F Alchemy Lab Setup ---
grid4F = Array(hSize).fill(null).map(() => Array(hSize).fill(BLOCK.WOOD_FLOOR));
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
if (dy === 0) {
grid4F[dy][dx] = (dx === 3) ? BLOCK.GLASS : BLOCK.WOOD_WALL;
} else if (dx === 0 || dx === hSize - 1 || dy === hSize - 1) {
grid4F[dy][dx] = BLOCK.WOOD_WALL;
}
}
}
grid4F[1][1] = BLOCK.STAIRS_DOWN; // Stairs down to 3F
grid4F[1][5] = BLOCK.STAIRS_UP; // Stairs leading UP to 5F (Right top)
grid4F[3][3] = BLOCK.BREWING_STAND; // Center Brewing Stand
grid4F[1][3] = BLOCK.CHEST_4F; // 4F Alchemist Chest
// --- 5F Nether Garden (Red Crimson Floor) ---
grid5F = Array(hSize).fill(null).map(() => Array(hSize).fill(BLOCK.NETHER_WART));
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
// Wall boundaries
if (dy === 0 || dx === 0 || dx === hSize - 1 || dy === hSize - 1) {
grid5F[dy][dx] = BLOCK.NETHER_BRICK;
} else if (dy === 3 && dx === 3) {
grid5F[dy][dx] = BLOCK.WOOD_FLOOR; // solid walkway inside wart garden
}
}
}
grid5F[1][5] = BLOCK.STAIRS_DOWN; // Stairs down to 4F
grid5F[5][5] = BLOCK.STAIRS_UP; // Stairs leading UP to 6F (Right bottom)
grid5F[1][3] = BLOCK.CHEST_5F; // Nether Chest
// --- 6F Ender Sky Observatory (Top Terrace Floor) ---
grid6F = Array(hSize).fill(null).map(() => Array(hSize).fill(BLOCK.WOOD_FLOOR));
for (let dy = 0; dy < hSize; dy++) {
for (let dx = 0; dx < hSize; dx++) {
if (dy === 0) {
grid6F[dy][dx] = BLOCK.WOOD_WALL;
} else if (dx === 0 || dx === hSize - 1 || dy === hSize - 1) {
grid6F[dy][dx] = BLOCK.FENCE; // open stargazing safety railing
}
}
}
grid6F[5][5] = BLOCK.STAIRS_DOWN; // Stairs down to 5F
grid6F[3][3] = BLOCK.DRAGON_EGG; // Center Dragon Egg Altar!
grid6F[1][1] = BLOCK.CHEST_6F; // Ender Chest Final Reward
// Main Road Clearance
const doorFrontY = doorY + 1;
for (let y = 1; y <= doorFrontY; y++) {
if (maze[y][1] === BLOCK.COBBLE) maze[y][1] = BLOCK.GRASS;
}
for (let x = 1; x <= width - 2; x++) {
if (maze[doorFrontY][x] === BLOCK.COBBLE) maze[doorFrontY][x] = BLOCK.GRASS;
}
for (let y = doorFrontY; y <= height - 2; y++) {
if (maze[y][width - 2] === BLOCK.COBBLE) maze[y][width - 2] = BLOCK.GRASS;
}
}
// --- STREAMING_CHUNK: Reset game states and place diamond targets... ---
function initGame() {
const conf = DIFFICULTIES[currentDifficultyIndex];
mazeWidth = conf.size;
mazeHeight = conf.size;
currentFloor = 1;
openedChests2F.clear();
openedChests3F.clear();
openedChests4F.clear();
openedChests5F.clear();
openedChests6F.clear();
speedBuffTimer = 0; // Reset Potion Buffs
resizeCanvas();
grid = generateMaze(mazeWidth, mazeHeight);
spawnHouse(grid, mazeWidth, mazeHeight);
particles = [];
openedChests.clear();
openDoors.clear();
player.x = 1;
player.y = 1;
player.targetX = 1;
player.targetY = 1;
player.moveProgress = 0;
player.dir = 'down';
swordLevel = 0;
respawnX = 1;
respawnY = 1;
diamondsCollected = 0;
// Balance Diamonds: Maze Ores + chests inside tower (1 on 2F, 2 on 3F, 2 on 4F, 3 on 5F, 4 on 6F)
totalStageDiamonds = conf.diamonds + 1 + 2 + 2 + 3 + 4;
let currentDiamondsPlaced = 0;
while (currentDiamondsPlaced < conf.diamonds) {
let rx = Math.floor(Math.random() * (mazeWidth - 2)) + 1;
let ry = Math.floor(Math.random() * (mazeHeight - 2)) + 1;
if (grid[ry][rx] === BLOCK.GRASS && (rx > 3 || ry > 3) && !isInsideHouse(rx, ry)) {
grid[ry][rx] = BLOCK.DIAMOND;
currentDiamondsPlaced++;
}
}
// Spawn Creepers
creepers = [];
let currentCreepersPlaced = 0;
while (currentCreepersPlaced < conf.creepers) {
let rx = Math.floor(Math.random() * (mazeWidth - 2)) + 1;
let ry = Math.floor(Math.random() * (mazeHeight - 2)) + 1;
if (grid[ry][rx] === BLOCK.GRASS && (rx > 5 || ry > 5) && !isInsideHouse(rx, ry)) {
creepers.push({
x: rx,
y: ry,
targetX: rx,
targetY: ry,
moveProgress: 0,
direction: 'down',
alertState: false,
alertTimer: 0,
speed: 0.03 + (stage * 0.005)
});
currentCreepersPlaced++;
}
}
gameOver = false;
gameClear = false;
updateUI();
document.getElementById('overlay').classList.add('hidden');
showSystemChat("迷路を探索し、6階建ての巨大なオークタワーを攻略しよう!");
if (animationFrameId) cancelAnimationFrame(animationFrameId);
gameLoop();
}
// --- STREAMING_CHUNK: Updating the game layout UI panel... ---
function updateUI() {
document.getElementById('diamond-counter').textContent = `${diamondsCollected} / ${totalStageDiamonds}`;
const hearts = '❤️'.repeat(Math.max(0, lives));
const deadHearts = '🖤'.repeat(Math.max(0, 3 - lives));
document.getElementById('hearts-container').innerHTML = `<span class="text-red-500">${hearts}</span><span class="text-gray-600">${deadHearts}</span>`;
document.getElementById('stage-num').textContent = stage;
const conf = DIFFICULTIES[currentDifficultyIndex];
document.getElementById('difficulty-btn').textContent = `難易度: ${conf.name}`;
const activeFov = FOV_LEVELS[currentFovIndex];
document.getElementById('fov-btn').textContent = `🔍 視野: ${activeFov.name.split(' ')[0]}`;
// Update Sword Status UI
const swordEl = document.getElementById('sword-status');
let swordText = '';
if (swordLevel === 2) {
swordText = '<span class="text-[#55ffff] font-bold drop-shadow-[0_1px_2px_rgba(85,255,255,0.4)]">✨💎 ダイヤの剣 (覚醒)</span>';
} else if (swordLevel === 1) {
swordText = '<span class="text-gray-200 font-bold">🗡️ 鉄の剣 (装備中)</span>';
} else {
swordText = '❌ なし (素手)';
}
// Add potion indicator to UI if active
if (speedBuffTimer > 0) {
swordText += ` <span class="text-yellow-400 font-bold animate-pulse text-[10px]">⚡ 俊敏 II</span>`;
}
swordEl.innerHTML = swordText;
// Update Floor display UI representing 6 levels
const floorEl = document.getElementById('floor-status');
if (currentFloor === 6) {
floorEl.innerHTML = '<span class="text-indigo-400 font-bold">階層: 🌌 6F 天空ジ・エンド</span>';
} else if (currentFloor === 5) {
floorEl.innerHTML = '<span class="text-red-500 font-bold">階層: 🔥 5F ネザーの間</span>';
} else if (currentFloor === 4) {
floorEl.innerHTML = '<span class="text-yellow-400 font-bold">階層: 🧪 4F 醸造ラボ</span>';
} else if (currentFloor === 3) {
floorEl.innerHTML = '<span class="text-purple-400 font-bold">階層: 🔮 3F 魔術図書</span>';
} else if (currentFloor === 2) {
floorEl.innerHTML = '<span class="text-cyan-400 font-bold">階層: 🏠 2F 展望ロフト</span>';
} else {
floorEl.innerHTML = '<span class="text-green-500 font-bold">階層: 🏠 1F 地上</span>';
}
const respawnEl = document.getElementById('respawn-status');
if (respawnX === 1 && respawnY === 1) {
respawnEl.textContent = '📍 初期地点';
} else {
respawnEl.textContent = '🏠 マイマニア';
}
}
// --- STREAMING_CHUNK: Implementing action swing and combat damage logic... ---
function triggerAttack() {
if (gameOver || gameClear || swordLevel === 0 || attackTicks > 0) return;
attackTicks = 10;
sound.playEquip();
let ax = player.x;
let ay = player.y;
if (player.dir === 'up') ay--;
if (player.dir === 'down') ay++;
if (player.dir === 'left') ax--;
if (player.dir === 'right') ax++;
let hit = false;
for (let i = creepers.length - 1; i >= 0; i--) {
const c = creepers[i];
if ((c.x === ax && c.y === ay) || (c.targetX === ax && c.targetY === ay)) {
const particleColor = (swordLevel === 2) ? '#55ffff' : '#ff2222';
for (let j = 0; j < 8; j++) {
particles.push({
x: ax * TILE_SIZE + TILE_SIZE / 2,
y: ay * TILE_SIZE + TILE_SIZE / 2,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 4,
color: particleColor,
alpha: 1.0,
size: 3,
life: 1.0,
decay: 0.05
});
}
sound.playHurt();
creepers.splice(i, 1);
hit = true;
const dropChance = (swordLevel === 2) ? 1.0 : 0.4;
if (Math.random() <= dropChance) {
grid[ay][ax] = BLOCK.DIAMOND;
showSystemChat("クリーパーを撃破!ダイヤモンドがドロップ! 💎");
} else {
showSystemChat("クリーパーを撃破した!🗡️");
}
}
}
}
// --- STREAMING_CHUNK: Physics simulation loop variables... ---
function update() {
tickCount++;
if (attackTicks > 0) {
attackTicks--;
}
// Manage speed buff duration timer
if (speedBuffTimer > 0) {
speedBuffTimer--;
spawnPotionParticles(player.x, player.y);
if (speedBuffTimer === 0) {
updateUI();
showSystemChat("ポーションの効果が切れた!速度が通常に戻りました。");
}
}
updateParticles();
// Movement speed based on Swiftness Potion Buff (Step: 0.25 vs 0.15)
const moveStep = (speedBuffTimer > 0) ? 0.30 : 0.15;
if (player.moveProgress < 1 && (player.x !== player.targetX || player.y !== player.targetY)) {
player.moveProgress += moveStep;
if (tickCount % 3 === 0) {
const currentInterpX = player.x + (player.targetX - player.x) * player.moveProgress;
const currentInterpY = player.y + (player.targetY - player.y) * player.moveProgress;
let currentWalkingBlock = grid[player.y][player.x];
if (isInsideHouse(player.x, player.y)) {
if (currentFloor === 2) currentWalkingBlock = grid2F[player.y - houseArea.minY][player.x - houseArea.minX];
else if (currentFloor === 3) currentWalkingBlock = grid3F[player.y - houseArea.minY][player.x - houseArea.minX];
else if (currentFloor === 4) currentWalkingBlock = grid4F[player.y - houseArea.minY][player.x - houseArea.minX];
else if (currentFloor === 5) currentWalkingBlock = grid5F[player.y - houseArea.minY][player.x - houseArea.minX];
else if (currentFloor === 6) currentWalkingBlock = grid6F[player.y - houseArea.minY][player.x - houseArea.minX];
}
spawnWalkParticle(currentInterpX, currentInterpY, currentWalkingBlock);
}
if (player.moveProgress >= 1) {
player.x = player.targetX;
player.y = player.targetY;
player.moveProgress = 0;
checkCollisionEvents();
}
}
// Door Sensor System
for (let ty = 0; ty < mazeHeight; ty++) {
for (let tx = 0; tx < mazeWidth; tx++) {
if (grid[ty][tx] === BLOCK.DOOR) {
const distToPlayer = currentFloor === 1 ? (Math.abs(player.x - tx) + Math.abs(player.y - ty)) : 99;
const key = `${tx},${ty}`;
if (distToPlayer <= 1 && !openDoors.has(key)) {
openDoors.add(key);
sound.playDoor();
} else if (distToPlayer > 1 && openDoors.has(key)) {
openDoors.delete(key);
sound.playDoor();
}
}
}
}
creepers.forEach(creeper => {
if (creeper.moveProgress < 1 && (creeper.x !== creeper.targetX || creeper.y !== creeper.targetY)) {
creeper.moveProgress += creeper.speed;
if (tickCount % 5 === 0 && Math.random() < 0.5) {
const cx = creeper.x + (creeper.targetX - creeper.x) * creeper.moveProgress;
const cy = creeper.y + (creeper.targetY - creeper.y) * creeper.moveProgress;
spawnWalkParticle(cx, cy, BLOCK.GRASS);
}
if (creeper.moveProgress >= 1) {
creeper.x = creeper.targetX;
creeper.y = creeper.targetY;
creeper.moveProgress = 0;
}
} else {
const dx = player.x - creeper.x;
const dy = player.y - creeper.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const playerSafe = isInsideHouse(player.x, player.y) || currentFloor > 1;
if (distance < 5 && !playerSafe) {
if (!creeper.alertState) {
creeper.alertState = true;
creeper.alertTimer = 45;
sound.playHiss();
}
} else {
creeper.alertState = false;
}
if (creeper.alertState) {
creeper.alertTimer--;
if (creeper.alertTimer <= 0) {
triggerExplosion(creeper);
return;
}
}
let nextStep = { x: creeper.x, y: creeper.y };
if (creeper.alertState && distance > 1 && !playerSafe) {
const possibleDirections = [];
if (Math.abs(dx) > Math.abs(dy)) {
possibleDirections.push({ x: Math.sign(dx), y: 0 });
possibleDirections.push({ x: 0, y: Math.sign(dy) });
} else {
possibleDirections.push({ x: 0, y: Math.sign(dy) });
possibleDirections.push({ x: Math.sign(dx), y: 0 });
}
possibleDirections.push({ x: 0, y: -1 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 });
for (const step of possibleDirections) {
const tx = creeper.x + step.x;
const ty = creeper.y + step.y;
if (grid[ty] && grid[ty][tx] === BLOCK.GRASS && !isInsideHouse(tx, ty)) {
nextStep = { x: tx, y: ty };
if (step.x > 0) creeper.direction = 'right';
if (step.x < 0) creeper.direction = 'left';
if (step.y > 0) creeper.direction = 'down';
if (step.y < 0) creeper.direction = 'up';
break;
}
}
} else if (Math.random() < 0.3) {
const dirs = [
{ x: 0, y: -1, d: 'up' }, { x: 1, y: 0, d: 'right' },
{ x: 0, y: 1, d: 'down' }, { x: -1, y: 0, d: 'left' }
];
const randomDir = dirs[Math.floor(Math.random() * dirs.length)];
const tx = creeper.x + randomDir.x;
const ty = creeper.y + randomDir.y;
if (grid[ty] && grid[ty][tx] === BLOCK.GRASS && !isInsideHouse(tx, ty)) {
nextStep = { x: tx, y: ty };
creeper.direction = randomDir.d;
}
}
creeper.targetX = nextStep.x;
creeper.targetY = nextStep.y;
}
});
}
// --- STREAMING_CHUNK: Checking physical object intersections and stair climbers... ---
function checkCollisionEvents() {
const px = player.x;
const py = player.y;
// Handle Ground Level (1F)
if (currentFloor === 1) {
const block = grid[py][px];
if (block === BLOCK.DIAMOND) {
grid[py][px] = BLOCK.GRASS;
diamondsCollected++;
sound.playDiamond();
updateUI();
showSystemChat(`ダイヤを獲得! 💎 (${diamondsCollected}/${totalStageDiamonds})`);
}
if (block === BLOCK.CHEST) {
const coordKey = `${px},${py}`;
if (!openedChests.has(coordKey)) {
openedChests.add(coordKey);
if (swordLevel === 0) swordLevel = 1;
sound.playEquip();
showSystemChat("チェストから【鉄の剣】を手に入れた!🗡️ [Space]キーで攻撃!");
updateUI();
}
}
if (block === BLOCK.BED) {
if (respawnX !== px || respawnY !== py || lives < 3) {
respawnX = px;
respawnY = py;
lives = 3;
sound.playHeal();
updateUI();
showSystemChat("ベッドでリスポーン地点を設定した!ライフが全回復。🛌");
}
}
if (block === BLOCK.STAIRS_UP) {
currentFloor = 2;
sound.playStairs();
updateUI();
showSystemChat("階段を上って【2階 展望ロフト】へ移動しました!🪜");
}
if (block === BLOCK.PORTAL) {
if (diamondsCollected === totalStageDiamonds) {
levelComplete();
} else {
showSystemChat(`ダイヤが足りない! 残り: ${totalStageDiamonds - diamondsCollected}個 💎`);
}
}
}
// Handle 2F Loft
else if (currentFloor === 2) {
const hx = px - houseArea.minX;
const hy = py - houseArea.minY;
const block2F = grid2F[hy][hx];
if (block2F === BLOCK.STAIRS_DOWN) {
currentFloor = 1;
sound.playStairs();
updateUI();
showSystemChat("階段を下りて【1階 地上】に戻りました。🚪");
}
if (block2F === BLOCK.STAIRS_UP) {
currentFloor = 3;
sound.playStairs();
updateUI();
showSystemChat("階段をさらに上って【3階 魔術図書室】へ到達しました!🔮");
}
if (block2F === BLOCK.CHEST_2F) {
const coordKey = `${px},${py}`;
if (!openedChests2F.has(coordKey)) {
openedChests2F.add(coordKey);
diamondsCollected = Math.min(totalStageDiamonds, diamondsCollected + 1);
sound.playDiamond();
updateUI();
showSystemChat("プレミアムチェストからダイヤモンドを発見! 💎✨");
}
}
}
// Handle 3F Magic Library
else if (currentFloor === 3) {
const hx = px - houseArea.minX;
const hy = py - houseArea.minY;
const block3F = grid3F[hy][hx];
if (block3F === BLOCK.STAIRS_DOWN) {
currentFloor = 2;
sound.playStairs();
updateUI();
showSystemChat("階段を下りて【2階 展望ロフト】に戻りました。🪜");
}
if (block3F === BLOCK.STAIRS_UP) {
currentFloor = 4;
sound.playStairs();
updateUI();
showSystemChat("階段を上って【4階 醸造ラボ】へ到着しました!🧪");
}
if (block3F === BLOCK.ENCHANT_TABLE) {
if (swordLevel === 1) {
swordLevel = 2;
sound.playEnchant();
for (let i = 0; i < 15; i++) {
particles.push({
x: px * TILE_SIZE + TILE_SIZE/2,
y: py * TILE_SIZE + TILE_SIZE/2,
vx: (Math.random() - 0.5) * 3,
vy: (Math.random() - 0.5) * 3,
color: '#a21caf',
alpha: 1.0,
size: 3,
life: 1.0,
decay: 0.04
});
}
updateUI();
showSystemChat("鉄の剣をエンチャントした!最強の【ダイヤの剣】に覚醒!✨💎");
} else if (swordLevel === 0) {
showSystemChat("エンチャントするには、まず1Fのチェストから鉄の剣を回収してください!🗡️");
}
}
if (block3F === BLOCK.CHEST_3F) {
const coordKey = `${px},${py}`;
if (!openedChests3F.has(coordKey)) {
openedChests3F.add(coordKey);
diamondsCollected = Math.min(totalStageDiamonds, diamondsCollected + 2);
sound.playDiamond();
updateUI();
showSystemChat("ラピスチェストからダイヤモンドを2つ発見! 💎💎");
}
}
}
// Handle 4F Alchemy Lab
else if (currentFloor === 4) {
const hx = px - houseArea.minX;
const hy = py - houseArea.minY;
const block4F = grid4F[hy][hx];
if (block4F === BLOCK.STAIRS_DOWN) {
currentFloor = 3;
sound.playStairs();
updateUI();
showSystemChat("階段を下りて【3階 魔術図書室】に戻りました。🔮");
}
if (block4F === BLOCK.STAIRS_UP) {
currentFloor = 5;
sound.playStairs();
updateUI();
showSystemChat("怪しい螺旋階段を上り【5階 ネザーの間】へ移動しました!🔥");
}
// Interaction with Brewing stand -> Grants speed buff!
if (block4F === BLOCK.BREWING_STAND) {
if (speedBuffTimer === 0) {
speedBuffTimer = 450; // Active speed for ~30 seconds (at 15fps engine check)
sound.playHeal();
for (let i = 0; i < 20; i++) {
particles.push({
x: px * TILE_SIZE + TILE_SIZE/2,
y: py * TILE_SIZE + TILE_SIZE/2,
vx: (Math.random() - 0.5) * 4,
vy: -Math.random() * 2 - 1,
color: '#eab308',
alpha: 1.0,
size: 2,
life: 1.0,
decay: 0.03
});
}
updateUI();
showSystemChat("醸造台でスピードポーションを飲んだ!移動速度が2倍にアップ!⚡");
}
}
if (block4F === BLOCK.CHEST_4F) {
const coordKey = `${px},${py}`;
if (!openedChests4F.has(coordKey)) {
openedChests4F.add(coordKey);
diamondsCollected = Math.min(totalStageDiamonds, diamondsCollected + 2);
sound.playDiamond();
updateUI();
showSystemChat("アルケミストの宝箱からダイヤモンドを2つ発見! 🧪💎💎");
}
}
}
// Handle 5F Nether Floor
else if (currentFloor === 5) {
const hx = px - houseArea.minX;
const hy = py - houseArea.minY;
const block5F = grid5F[hy][hx];
if (block5F === BLOCK.STAIRS_DOWN) {
currentFloor = 4;
sound.playStairs();
updateUI();
showSystemChat("階段を下りて【4階 醸造ラボ】に戻りました。🧪");
}
if (block5F === BLOCK.STAIRS_UP) {
currentFloor = 6;
sound.playStairs();
updateUI();
showSystemChat("タワーの頂点【6階 天空ジ・エンド】に到着しました!🌌🪐");
}
if (block5F === BLOCK.CHEST_5F) {
const coordKey = `${px},${py}`;
if (!openedChests5F.has(coordKey)) {
openedChests5F.add(coordKey);
diamondsCollected = Math.min(totalStageDiamonds, diamondsCollected + 3);
sound.playDiamond();
updateUI();
showSystemChat("ネザーチェストをこじ開けた!ダイヤモンドを3つ獲得! 🔥💎💎💎");
}
}
}
// Handle 6F Sky Ender Altar (Final top floor)
else if (currentFloor === 6) {
const hx = px - houseArea.minX;
const hy = py - houseArea.minY;
const block6F = grid6F[hy][hx];
if (block6F === BLOCK.STAIRS_DOWN) {
currentFloor = 5;
sound.playStairs();
updateUI();
showSystemChat("階段を下りて【5階 ネザーの間】に戻りました。🔥");
}
if (block6F === BLOCK.CHEST_6F) {
const coordKey = `${px},${py}`;
if (!openedChests6F.has(coordKey)) {
openedChests6F.add(coordKey);
// Final Ender chest gives 4 diamonds!
diamondsCollected = Math.min(totalStageDiamonds, diamondsCollected + 4);
sound.playDiamond();
updateUI();
showSystemChat("エンダーチェストが静かに開いた!ダイヤモンド4つ獲得! 🌌💎💎💎💎");
}
}
}
}
function triggerExplosion(creeper) {
sound.playExplode();
const dx = Math.abs(player.x - creeper.x);
const dy = Math.abs(player.y - creeper.y);
if (dx <= 2 && dy <= 2 && currentFloor === 1) {
lives--;
sound.playHurt();
updateUI();
if (lives <= 0) {
triggerGameOver();
} else {
showSystemChat("クリーパーの爆発!リスポーン拠点へ退避します!");
player.x = respawnX;
player.y = respawnY;
player.targetX = respawnX;
player.targetY = respawnY;
player.moveProgress = 0;
currentFloor = 1;
}
}
creepers = creepers.filter(c => c !== creeper);
}
function triggerGameOver() {
gameOver = true;
sound.playExplode();
const overlay = document.getElementById('overlay');
const title = document.getElementById('overlay-title');
const sub = document.getElementById('overlay-sub');
const btn = document.getElementById('overlay-btn');
title.innerHTML = '<span class="text-red-500 text-3xl">GAME OVER</span>';
if (respawnX === 1 && respawnY === 1) {
sub.textContent = '初期地点から再スタートします。タワー1Fのベッドで寝ると、そこから復活できます!';
btn.textContent = '最初からやり直す';
} else {
sub.textContent = 'ベッドから即時安全に復活し、タワー探索を再開できます!🛌';
btn.textContent = 'タワーベッドから復活 🛌';
}
overlay.classList.remove('hidden');
}
function levelComplete() {
gameClear = true;
sound.playWin();
const overlay = document.getElementById('overlay');
const title = document.getElementById('overlay-title');
const sub = document.getElementById('overlay-sub');
const btn = document.getElementById('overlay-btn');
title.innerHTML = '<span class="text-[#55FF55]">STAGE CLEAR!</span>';
sub.textContent = `6階建てタワーに眠るすべてのお宝と、迷路のダイヤモンド(合計 ${diamondsCollected} 個)を集めてクリア!`;
btn.textContent = '次のステージへ';
overlay.classList.remove('hidden');
}
// --- STREAMING_CHUNK: Designing multi-layered rendering engines... ---
function render() {
ctx.fillStyle = '#0a0a0f';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const drawPlayerX = player.x + (player.targetX - player.x) * player.moveProgress;
const drawPlayerY = player.y + (player.targetY - player.y) * player.moveProgress;
const cameraX = drawPlayerX - (viewportTiles / 2);
const cameraY = drawPlayerY - (viewportTiles / 2);
const startX = Math.floor(cameraX);
const startY = Math.floor(cameraY);
const endX = Math.ceil(cameraX + viewportTiles);
const endY = Math.ceil(cameraY + viewportTiles);
const offsetPxX = (cameraX - startX) * TILE_SIZE;
const offsetPxY = (cameraY - startY) * TILE_SIZE;
// Render Visible Terrain Layers
for (let ty = startY; ty <= endY; ty++) {
for (let tx = startX; tx <= endX; tx++) {
const canvasX = (tx - startX) * TILE_SIZE - offsetPxX;
const canvasY = (ty - startY) * TILE_SIZE - offsetPxY;
if (tx < 0 || tx >= mazeWidth || ty < 0 || ty >= mazeHeight) {
drawCobbleWall(ctx, canvasX, canvasY, TILE_SIZE);
continue;
}
const inHouse = isInsideHouse(tx, ty);
// --- Render 6F Sky End Terrace ---
if (currentFloor === 6 && inHouse) {
const hx = tx - houseArea.minX;
const hy = ty - houseArea.minY;
const block6F = grid6F[hy][hx];
switch (block6F) {
case BLOCK.WOOD_WALL:
drawWoodWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.DRAGON_EGG:
drawDragonEgg(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CHEST_6F:
const isEnderOpen = openedChests6F.has(`${tx},${ty}`);
drawChestBlock(ctx, canvasX, canvasY, TILE_SIZE, isEnderOpen); // classic chests look but obsidian particles spawned in checks
break;
case BLOCK.FENCE:
drawFence(ctx, canvasX, canvasY, TILE_SIZE, false);
break;
case BLOCK.WOOD_FLOOR:
default:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
continue;
}
// --- Render 5F Nether Room Layout ---
if (currentFloor === 5 && inHouse) {
const hx = tx - houseArea.minX;
const hy = ty - houseArea.minY;
const block5F = grid5F[hy][hx];
switch (block5F) {
case BLOCK.NETHER_BRICK:
drawNetherBrick(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.NETHER_WART:
drawNetherWart(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_DOWN:
drawStairsDown(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_UP:
drawStairsUp(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CHEST_5F:
const isNetherOpen = openedChests5F.has(`${tx},${ty}`);
drawChestBlock(ctx, canvasX, canvasY, TILE_SIZE, isNetherOpen);
break;
case BLOCK.WOOD_FLOOR:
default:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
continue;
}
// --- Render 4F Alchemy Lab Layout ---
if (currentFloor === 4 && inHouse) {
const hx = tx - houseArea.minX;
const hy = ty - houseArea.minY;
const block4F = grid4F[hy][hx];
switch (block4F) {
case BLOCK.WOOD_WALL:
drawWoodWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.GLASS:
drawGlassWindow(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_DOWN:
drawStairsDown(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_UP:
drawStairsUp(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.BREWING_STAND:
drawBrewingStand(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CHEST_4F:
const isAlchemistOpen = openedChests4F.has(`${tx},${ty}`);
drawChestBlock(ctx, canvasX, canvasY, TILE_SIZE, isAlchemistOpen);
break;
case BLOCK.WOOD_FLOOR:
default:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
continue;
}
// --- Render 3F Library Layout ---
if (currentFloor === 3 && inHouse) {
const hx = tx - houseArea.minX;
const hy = ty - houseArea.minY;
const block3F = grid3F[hy][hx];
switch (block3F) {
case BLOCK.WOOD_WALL:
drawWoodWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.GLASS:
drawGlassWindow(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_DOWN:
drawStairsDown(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_UP:
drawStairsUp(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.BOOKSHELF:
drawBookshelf(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.ENCHANT_TABLE:
drawEnchantTable(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CHEST_3F:
const isLapisOpen = openedChests3F.has(`${tx},${ty}`);
drawChestBlock(ctx, canvasX, canvasY, TILE_SIZE, isLapisOpen);
break;
case BLOCK.WOOD_FLOOR:
default:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
continue;
}
// --- Render 2F Loft Layout ---
if (currentFloor === 2 && inHouse) {
const hx = tx - houseArea.minX;
const hy = ty - houseArea.minY;
const block2F = grid2F[hy][hx];
switch (block2F) {
case BLOCK.WOOD_WALL:
drawWoodWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.GLASS:
drawGlassWindow(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_DOWN:
drawStairsDown(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_UP:
drawStairsUp(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CHEST_2F:
const isPremiumOpen = openedChests2F.has(`${tx},${ty}`);
drawChest2F(ctx, canvasX, canvasY, TILE_SIZE, isPremiumOpen);
break;
case BLOCK.FENCE:
drawFence(ctx, canvasX, canvasY, TILE_SIZE, false);
break;
case BLOCK.WOOD_FLOOR:
default:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
continue;
}
// Standard 1F rendering
const blockType = grid[ty][tx];
switch (blockType) {
case BLOCK.COBBLE:
drawCobbleWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.WOOD_WALL:
drawWoodWall(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.GLASS:
drawGlassWindow(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.WOOD_FLOOR:
drawWoodFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.DOOR:
const isDoorOpen = openDoors.has(`${tx},${ty}`);
drawDoorBlock(ctx, canvasX, canvasY, TILE_SIZE, isDoorOpen);
break;
case BLOCK.CHEST:
const isChestOpen = openedChests.has(`${tx},${ty}`);
drawChestBlock(ctx, canvasX, canvasY, TILE_SIZE, isChestOpen);
break;
case BLOCK.BED:
drawBedBlock(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.CRAFTING:
drawCraftingTable(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.STAIRS_UP:
drawStairsUp(ctx, canvasX, canvasY, TILE_SIZE);
break;
case BLOCK.GRASS:
default:
drawGrassFloor(ctx, canvasX, canvasY, TILE_SIZE);
break;
}
if (blockType === BLOCK.DIAMOND) {
drawGrassFloor(ctx, canvasX, canvasY, TILE_SIZE);
const bounceOffset = Math.sin(tickCount * 0.1 + tx) * 4;
drawDiamondOre(ctx, canvasX, canvasY + bounceOffset, TILE_SIZE);
} else if (blockType === BLOCK.PORTAL) {
drawGoal(ctx, canvasX, canvasY, TILE_SIZE);
}
}
}
drawParticles(ctx, cameraX, cameraY);
// Hide creepers if player is on safe elevated floors inside the house tower
creepers.forEach(creeper => {
const drawCreeperX = creeper.x + (creeper.targetX - creeper.x) * creeper.moveProgress;
const drawCreeperY = creeper.y + (creeper.targetY - creeper.y) * creeper.moveProgress;
const cx = (drawCreeperX - cameraX) * TILE_SIZE;
const cy = (drawCreeperY - cameraY) * TILE_SIZE;
drawCreeper(ctx, cx, cy, TILE_SIZE, creeper.alertState, creeper.moveProgress);
});
const px = (drawPlayerX - cameraX) * TILE_SIZE;
const py = (drawPlayerY - cameraY) * TILE_SIZE;
drawPlayer(ctx, px, py, TILE_SIZE, player.dir, player.moveProgress);
}
function gameLoop() {
if (!gameOver && !gameClear) {
update();
render();
animationFrameId = requestAnimationFrame(gameLoop);
}
}
// --- STREAMING_CHUNK: Wiring controller buttons and arrow keyboard bindings... ---
function movePlayer(dx, dy, dirName) {
if (gameOver || gameClear) return;
if (player.moveProgress > 0) return;
player.dir = dirName;
const nextX = player.x + dx;
const nextY = player.y + dy;
if (nextX >= 0 && nextX < mazeWidth && nextY >= 0 && nextY < mazeHeight) {
// Elevated Floor Movement Rules
if (currentFloor > 1) {
const isNextInHouse = isInsideHouse(nextX, nextY);
if (isNextInHouse) {
const hx = nextX - houseArea.minX;
const hy = nextY - houseArea.minY;
let blockAtNext = BLOCK.WOOD_FLOOR;
if (currentFloor === 2) blockAtNext = grid2F[hy][hx];
else if (currentFloor === 3) blockAtNext = grid3F[hy][hx];
else if (currentFloor === 4) blockAtNext = grid4F[hy][hx];
else if (currentFloor === 5) blockAtNext = grid5F[hy][hx];
else if (currentFloor === 6) blockAtNext = grid6F[hy][hx];
const canPassTower = (
blockAtNext === BLOCK.WOOD_FLOOR ||
blockAtNext === BLOCK.STAIRS_DOWN ||
blockAtNext === BLOCK.STAIRS_UP ||
blockAtNext === BLOCK.CHEST_2F ||
blockAtNext === BLOCK.CHEST_3F ||
blockAtNext === BLOCK.CHEST_4F ||
blockAtNext === BLOCK.CHEST_5F ||
blockAtNext === BLOCK.CHEST_6F ||
blockAtNext === BLOCK.ENCHANT_TABLE ||
blockAtNext === BLOCK.BREWING_STAND ||
blockAtNext === BLOCK.DRAGON_EGG ||
blockAtNext === BLOCK.FENCE ||
blockAtNext === BLOCK.NETHER_WART
);
if (canPassTower) {
player.targetX = nextX;
player.targetY = nextY;
player.moveProgress = 0;
sound.playStep();
}
}
return;
}
// Standard 1F Movement Rules
const block = grid[nextY][nextX];
const canPass = (
block === BLOCK.GRASS ||
block === BLOCK.WOOD_FLOOR ||
block === BLOCK.DOOR ||
block === BLOCK.CHEST ||
block === BLOCK.BED ||
block === BLOCK.CRAFTING ||
block === BLOCK.DIAMOND ||
block === BLOCK.PORTAL ||
block === BLOCK.STAIRS_UP
);
if (canPass) {
player.targetX = nextX;
player.targetY = nextY;
player.moveProgress = 0;
sound.playStep();
}
}
}
// Key listeners
window.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if (key === 'arrowup' || key === 'w') movePlayer(0, -1, 'up');
if (key === 'arrowdown' || key === 's') movePlayer(0, 1, 'down');
if (key === 'arrowleft' || key === 'a') movePlayer(-1, 0, 'left');
if (key === 'arrowright' || key === 'd') movePlayer(1, 0, 'right');
if (e.key === ' ') {
e.preventDefault();
triggerAttack();
}
sound.init();
});
// Touch Control Listeners
document.getElementById('btn-up').addEventListener('touchstart', (e) => { e.preventDefault(); movePlayer(0, -1, 'up'); });
document.getElementById('btn-down').addEventListener('touchstart', (e) => { e.preventDefault(); movePlayer(0, 1, 'down'); });
document.getElementById('btn-left').addEventListener('touchstart', (e) => { e.preventDefault(); movePlayer(-1, 0, 'left'); });
document.getElementById('btn-right').addEventListener('touchstart', (e) => { e.preventDefault(); movePlayer(1, 0, 'right'); });
document.getElementById('btn-attack').addEventListener('touchstart', (e) => { e.preventDefault(); triggerAttack(); });
document.getElementById('btn-up').addEventListener('mousedown', () => movePlayer(0, -1, 'up'));
document.getElementById('btn-down').addEventListener('mousedown', () => movePlayer(0, 1, 'down'));
document.getElementById('btn-left').addEventListener('mousedown', () => movePlayer(-1, 0, 'left'));
document.getElementById('btn-right').addEventListener('mousedown', () => movePlayer(1, 0, 'right'));
document.getElementById('btn-attack').addEventListener('mousedown', () => triggerAttack());
// Settings Buttons
document.getElementById('difficulty-btn').addEventListener('click', () => {
currentDifficultyIndex = (currentDifficultyIndex + 1) % DIFFICULTIES.length;
stage = 1;
lives = 3;
initGame();
});
document.getElementById('fov-btn').addEventListener('click', () => {
currentFovIndex = (currentFovIndex + 1) % FOV_LEVELS.length;
viewportTiles = FOV_LEVELS[currentFovIndex].tiles;
sound.playClick();
resizeCanvas();
updateUI();
showSystemChat(`視野を調整しました: ${FOV_LEVELS[currentFovIndex].name}`);
});
document.getElementById('sound-btn').addEventListener('click', () => {
sound.enabled = !sound.enabled;
document.getElementById('sound-btn').textContent = `🔊 音: ${sound.enabled ? 'ON' : 'OFF'}`;
sound.init();
});
document.getElementById('reset-btn').addEventListener('click', () => {
stage = 1;
lives = 3;
initGame();
});
document.getElementById('overlay-btn').addEventListener('click', () => {
if (gameOver) {
lives = 3;
player.x = respawnX;
player.y = respawnY;
player.targetX = respawnX;
player.targetY = respawnY;
player.moveProgress = 0;
currentFloor = 1;
if (respawnX === 1 && respawnY === 1) {
stage = 1;
}
initGame();
} else if (gameClear) {
stage++;
initGame();
}
});
window.onload = () => {
initGame();
};
</script>
</body>
</html>
めいろをしながらお家に入ったり二階に行ったりできるのが楽しいゲームです。