/* CZ原创水印 */
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2D 我的世界 - Web版</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');

        body {
            margin: 0;
            overflow: hidden;
            background-color: #87CEEB; /* 天空蓝 */
            font-family: 'VT323', monospace;
            user-select: none;
        }

        #game-container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        canvas {
            image-rendering: pixelated; /* 像素风格渲染 */
            box-shadow: 0 0 20px rgba(0,0,0,0.5);
            background-color: #87CEEB;
        }

        .ui-layer {
            position: absolute;
            pointer-events: none;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            padding: 20px;
        }

        .hud-panel {
            pointer-events: auto;
            background: rgba(0, 0, 0, 0.6);
            border: 4px solid #333;
            padding: 10px;
            color: white;
            border-radius: 4px;
        }

        .hotbar {
            display: flex;
            gap: 5px;
            justify-content: center;
            margin-top: 10px;
        }

        .slot {
            width: 50px;
            height: 50px;
            background: rgba(0, 0, 0, 0.5);
            border: 3px solid #555;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            position: relative;
            transition: transform 0.1s;
        }

        .slot.active {
            border-color: #fff;
            transform: scale(1.1);
            box-shadow: 0 0 10px rgba(255,255,255,0.5);
        }

        .slot img {
            width: 32px;
            height: 32px;
            image-rendering: pixelated;
        }

        .slot-number {
            position: absolute;
            bottom: 2px;
            left: 4px;
            font-size: 12px;
            color: #ddd;
            text-shadow: 1px 1px 0 #000;
        }

        /* 简单的方块颜色模拟 */
        .block-dirt { background-color: #5d4037; }
        .block-grass { background: linear-gradient(to bottom, #4caf50 30%, #5d4037 30%); }
        .block-stone { background-color: #757575; }
        .block-wood { background-color: #795548; }
        .block-leaves { background-color: #2e7d32; }
        .block-brick { background-color: #a1887f; }
        .block-sand { background-color: #fdd835; }
        .block-glass { background-color: rgba(200, 240, 255, 0.6); border: 1px solid #fff; }

        .controls-hint {
            position: absolute;
            top: 20px;
            right: 20px;
            text-align: right;
            font-size: 14px;
            line-height: 1.5;
            color: rgba(255,255,255,0.8);
            text-shadow: 1px 1px 2px black;
        }

        #loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-size: 24px;
            background: rgba(0,0,0,0.8);
            padding: 20px;
            border-radius: 10px;
            z-index: 100;
        }
    </style>
</head>
<body>

<div id="game-container">
    <canvas id="gameCanvas"></canvas>
    
    <div id="loading">正在生成世界...</div>

    <div class="ui-layer">
        <div class="controls-hint hud-panel">
            <div><span class="font-bold text-yellow-400">WASD</span> 移动 / 跳跃</div>
            <div><span class="font-bold text-yellow-400">鼠标左键</span> 破坏方块</div>
            <div><span class="font-bold text-yellow-400">鼠标右键</span> 放置方块</div>
            <div><span class="font-bold text-yellow-400">1-9</span> 选择物品</div>
            <div><span class="font-bold text-yellow-400">R</span> 重置世界</div>
        </div>

        <div class="hud-panel" style="align-self: center; margin-bottom: 20px;">
            <div class="text-center text-sm mb-2 text-gray-300">物品栏</div>
            <div class="hotbar" id="hotbar">
                <!-- 由JS生成 -->
            </div>
        </div>
    </div>
</div>

<script>
/**
 * 2D Minecraft Core Logic
 * 使用 Canvas API 实现基于 Tile 的沙盒游戏
 */

// --- 配置常量 ---
const TILE_SIZE = 40;
const CHUNK_WIDTH = 200; // 世界宽度(块)
const CHUNK_HEIGHT = 64; // 世界高度(块)
const GRAVITY = 0.5;
const TERMINAL_VELOCITY = 12;
const PLAYER_SPEED = 5;
const JUMP_FORCE = 9;

// 方块ID定义
const BLOCKS = {
    AIR: 0,
    DIRT: 1,
    GRASS: 2,
    STONE: 3,
    WOOD: 4,
    LEAVES: 5,
    BRICK: 6,
    SAND: 7,
    GLASS: 8,
    BEDROCK: 99
};

// 方块属性映射
const BLOCK_PROPS = {
    [BLOCKS.AIR]:   { color: null, solid: false },
    [BLOCKS.DIRT]:  { color: '#5d4037', solid: true, name: '泥土' },
    [BLOCKS.GRASS]: { color: '#4caf50', solid: true, name: '草方块', topColor: '#4caf50' },
    [BLOCKS.STONE]: { color: '#757575', solid: true, name: '石头' },
    [BLOCKS.WOOD]:  { color: '#795548', solid: true, name: '木头' },
    [BLOCKS.LEAVES]:{ color: '#2e7d32', solid: true, name: '树叶' },
    [BLOCKS.BRICK]: { color: '#a1887f', solid: true, name: '砖块' },
    [BLOCKS.SAND]:  { color: '#fdd835', solid: true, name: '沙子' },
    [BLOCKS.GLASS]: { color: 'rgba(200, 240, 255, 0.3)', solid: true, name: '玻璃', transparent: true },
    [BLOCKS.BEDROCK]:{ color: '#212121', solid: true, name: '基岩' }
};

// 玩家初始物品栏
const INVENTORY = [
    BLOCKS.DIRT, BLOCKS.GRASS, BLOCKS.STONE, BLOCKS.WOOD, 
    BLOCKS.LEAVES, BLOCKS.BRICK, BLOCKS.SAND, BLOCKS.GLASS
];

// --- 游戏状态 ---
const state = {
    canvas: null,
    ctx: null,
    world: [], // 二维数组 [x][y]
    camera: { x: 0, y: 0 },
    player: {
        x: 0, y: 0,
        vx: 0, vy: 0,
        width: 20, height: 56, // 略小于2个格子高
        grounded: false,
        selectedSlot: 0
    },
    keys: {},
    mouse: { x: 0, y: 0, leftDown: false, rightDown: false },
    lastTime: 0
};

// --- 初始化 ---
window.onload = () => {
    state.canvas = document.getElementById('gameCanvas');
    state.ctx = state.canvas.getContext('2d');
    
    resize();
    window.addEventListener('resize', resize);
    
    initInput();
    generateWorld();
    initUI();
    
    document.getElementById('loading').style.display = 'none';
    
    requestAnimationFrame(gameLoop);
};

function resize() {
    state.canvas.width = window.innerWidth;
    state.canvas.height = window.innerHeight;
    state.ctx.imageSmoothingEnabled = false; // 保持像素风
}

// --- 世界生成 ---
function generateWorld() {
    state.world = new Array(CHUNK_WIDTH).fill(null).map(() => new Array(CHUNK_HEIGHT).fill(BLOCKS.AIR));
    
    // 简单的地形生成算法 (正弦波叠加)
    const surfaceLevel = [];
    for (let x = 0; x < CHUNK_WIDTH; x++) {
        // 基础高度 + 噪声
        let h = 20 + Math.sin(x * 0.05) * 5 + Math.sin(x * 0.1) * 2;
        surfaceLevel[x] = Math.floor(h);
    }

    for (let x = 0; x < CHUNK_WIDTH; x++) {
        let groundY = surfaceLevel[x];
        
        for (let y = 0; y < CHUNK_HEIGHT; y++) {
            if (y >= CHUNK_HEIGHT - 2) {
                state.world[x][y] = BLOCKS.BEDROCK;
            } else if (y > groundY + 5) {
                // 深层石头
                state.world[x][y] = Math.random() > 0.1 ? BLOCKS.STONE : BLOCKS.DIRT;
            } else if (y > groundY) {
                // 浅层泥土
                state.world[x][y] = BLOCKS.DIRT;
            } else if (y === groundY) {
                // 地表
                state.world[x][y] = BLOCKS.GRASS;
                
                // 随机生成树木
                if (x > 5 && x < CHUNK_WIDTH - 5 && Math.random() < 0.05) {
                    generateTree(x, y - 1);
                }
            }
        }
    }

    // 设置玩家出生点
    state.player.x = (CHUNK_WIDTH / 2) * TILE_SIZE;
    state.player.y = (surfaceLevel[Math.floor(CHUNK_WIDTH / 2)] - 3) * TILE_SIZE;
}

function generateTree(x, y) {
    const height = 3 + Math.floor(Math.random() * 3);
    // 树干
    for (let i = 0; i < height; i++) {
        if (y - i >= 0) state.world[x][y - i] = BLOCKS.WOOD;
    }
    // 树叶
    for (let lx = x - 2; lx <= x + 2; lx++) {
        for (let ly = y - height - 2; ly <= y - height; ly++) {
            if (lx >= 0 && lx < CHUNK_WIDTH && ly >= 0 && Math.abs(lx - x) + Math.abs(ly - (y - height)) < 4) {
                if (state.world[lx][ly] === BLOCKS.AIR) {
                    state.world[lx][ly] = BLOCKS.LEAVES;
                }
            }
        }
    }
}

// --- 输入处理 ---
function initInput() {
    window.addEventListener('keydown', e => {
        state.keys[e.code] = true;
        // 物品栏快捷键 1-9
        if (e.key >= '1' && e.key <= '9') {
            const idx = parseInt(e.key) - 1;
            if (idx < INVENTORY.length) {
                state.player.selectedSlot = idx;
                updateHotbarUI();
            }
        }
        if (e.code === 'KeyR') {
            generateWorld();
        }
    });
    
    window.addEventListener('keyup', e => state.keys[e.code] = false);
    
    state.canvas.addEventListener('mousemove', e => {
        const rect = state.canvas.getBoundingClientRect();
        state.mouse.x = e.clientX - rect.left;
        state.mouse.y = e.clientY - rect.top;
    });
    
    state.canvas.addEventListener('mousedown', e => {
        if (e.button === 0) state.mouse.leftDown = true;
        if (e.button === 2) state.mouse.rightDown = true;
        handleInteraction();
    });
    
    state.canvas.addEventListener('mouseup', e => {
        if (e.button === 0) state.mouse.leftDown = false;
        if (e.button === 2) state.mouse.rightDown = false;
    });
    
    // 阻止右键菜单
    state.canvas.addEventListener('contextmenu', e => e.preventDefault());
}

// --- 游戏逻辑 ---
function update(dt) {
    // 1. 玩家物理
    // 水平移动
    if (state.keys['KeyA'] || state.keys['ArrowLeft']) state.player.vx = -PLAYER_SPEED;
    else if (state.keys['KeyD'] || state.keys['ArrowRight']) state.player.vx = PLAYER_SPEED;
    else state.player.vx = 0;

    // 跳跃
    if ((state.keys['KeyW'] || state.keys['ArrowUp'] || state.keys['Space']) && state.player.grounded) {
        state.player.vy = -JUMP_FORCE;
        state.player.grounded = false;
    }

    // 重力
    state.player.vy += GRAVITY;
    if (state.player.vy > TERMINAL_VELOCITY) state.player.vy = TERMINAL_VELOCITY;

    // 应用速度并检测碰撞
    movePlayer(state.player.vx, 0); // X轴
    movePlayer(0, state.player.vy); // Y轴

    // 2. 相机跟随
    // 目标相机位置
    const targetCamX = state.player.x - state.canvas.width / 2 + state.player.width / 2;
    const targetCamY = state.player.y - state.canvas.height / 2 + state.player.height / 2;
    
    // 平滑插值
    state.camera.x += (targetCamX - state.camera.x) * 0.1;
    state.camera.y += (targetCamY - state.camera.y) * 0.1;

    // 限制相机范围
    state.camera.x = Math.max(0, Math.min(state.camera.x, CHUNK_WIDTH * TILE_SIZE - state.canvas.width));
    state.camera.y = Math.max(0, Math.min(state.camera.y, CHUNK_HEIGHT * TILE_SIZE - state.canvas.height));

    // 3. 持续交互 (按住鼠标时)
    if (state.mouse.leftDown || state.mouse.rightDown) {
        handleInteraction();
    }
}

function movePlayer(vx, vy) {
    const p = state.player;
    p.x += vx;
    checkCollision(p, 'x');
    
    p.y += vy;
    p.grounded = false; // 假设在空中,除非碰撞检测证明在地面
    checkCollision(p, 'y');
}

function checkCollision(p, axis) {
    // 计算玩家占据的网格坐标范围
    const startX = Math.floor(p.x / TILE_SIZE);
    const endX = Math.floor((p.x + p.width - 0.1) / TILE_SIZE);
    const startY = Math.floor(p.y / TILE_SIZE);
    const endY = Math.floor((p.y + p.height - 0.1) / TILE_SIZE);

    for (let y = startY; y <= endY; y++) {
        for (let x = startX; x <= endX; x++) {
            if (x < 0 || x >= CHUNK_WIDTH || y < 0 || y >= CHUNK_HEIGHT) continue;
            
            const block = state.world[x][y];
            if (BLOCK_PROPS[block].solid) {
                if (axis === 'x') {
                    if (p.vx > 0) { // 向右撞
                        p.x = x * TILE_SIZE - p.width;
                    } else if (p.vx < 0) { // 向左撞
                        p.x = (x + 1) * TILE_SIZE;
                    }
                    p.vx = 0;
                } else { // axis === 'y'
                    if (p.vy > 0) { // 向下落
                        p.y = y * TILE_SIZE - p.height;
                        p.grounded = true;
                        p.vy = 0;
                    } else if (p.vy < 0) { // 向上顶
                        p.y = (y + 1) * TILE_SIZE;
                        p.vy = 0;
                    }
                }
                return; // 解决了一个方向的碰撞就返回,防止卡住
            }
        }
    }
}

function handleInteraction() {
    // 计算鼠标在世界中的坐标
    const worldMX = state.mouse.x + state.camera.x;
    const worldMY = state.mouse.y + state.camera.y;
    
    const tx = Math.floor(worldMX / TILE_SIZE);
    const ty = Math.floor(worldMY / TILE_SIZE);
    
    // 检查边界
    if (tx < 0 || tx >= CHUNK_WIDTH || ty < 0 || ty >= CHUNK_HEIGHT) return;
    
    // 距离检查 (只能操作附近方块)
    const dist = Math.hypot((state.player.x + state.player.width/2) - (tx * TILE_SIZE + TILE_SIZE/2), 
                            (state.player.y + state.player.height/2) - (ty * TILE_SIZE + TILE_SIZE/2));
    if (dist > TILE_SIZE * 4) return;

    if (state.mouse.leftDown) {
        // 破坏方块
        if (state.world[tx][ty] !== BLOCKS.BEDROCK) {
            state.world[tx][ty] = BLOCKS.AIR;
        }
    } else if (state.mouse.rightDown) {
        // 放置方块
        if (state.world[tx][ty] === BLOCKS.AIR) {
            // 检查是否与玩家重叠
            const p = state.player;
            const blockRect = { x: tx * TILE_SIZE, y: ty * TILE_SIZE, w: TILE_SIZE, h: TILE_SIZE };
            if (!(p.x < blockRect.x + blockRect.w &&
                  p.x + p.width > blockRect.x &&
                  p.y < blockRect.y + blockRect.h &&
                  p.y + p.height > blockRect.y)) {
                
                const blockToPlace = INVENTORY[state.player.selectedSlot];
                state.world[tx][ty] = blockToPlace;
            }
        }
    }
}

// --- 渲染 ---
function draw() {
    // 清空画布
    state.ctx.fillStyle = '#87CEEB';
    state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);

    state.ctx.save();
    state.ctx.translate(-Math.floor(state.camera.x), -Math.floor(state.camera.y));

    // 视锥剔除:只绘制屏幕内的方块
    const startCol = Math.floor(state.camera.x / TILE_SIZE);
    const endCol = startCol + (state.canvas.width / TILE_SIZE) + 1;
    const startRow = Math.floor(state.camera.y / TILE_SIZE);
    const endRow = startRow + (state.canvas.height / TILE_SIZE) + 1;

    for (let x = startCol; x <= endCol; x++) {
        for (let y = startRow; y <= endRow; y++) {
            if (x >= 0 && x < CHUNK_WIDTH && y >= 0 && y < CHUNK_HEIGHT) {
                const blockId = state.world[x][y];
                if (blockId !== BLOCKS.AIR) {
                    drawBlock(x, y, blockId);
                }
            }
        }
    }

    // 绘制玩家
    drawPlayer();

    // 绘制鼠标高亮框
    const worldMX = state.mouse.x + state.camera.x;
    const worldMY = state.mouse.y + state.camera.y;
    const tx = Math.floor(worldMX / TILE_SIZE);
    const ty = Math.floor(worldMY / TILE_SIZE);
    
    state.ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
    state.ctx.lineWidth = 2;
    state.ctx.strokeRect(tx * TILE_SIZE, ty * TILE_SIZE, TILE_SIZE, TILE_SIZE);

    state.ctx.restore();
}

function drawBlock(x, y, id) {
    const prop = BLOCK_PROPS[id];
    const px = x * TILE_SIZE;
    const py = y * TILE_SIZE;

    state.ctx.fillStyle = prop.color;
    state.ctx.fillRect(px, py, TILE_SIZE, TILE_SIZE);

    // 简单的纹理效果
    if (id === BLOCKS.GRASS) {
        state.ctx.fillStyle = '#5d4037'; // 泥土部分
        state.ctx.fillRect(px, py + 10, TILE_SIZE, TILE_SIZE - 10);
        state.ctx.fillStyle = '#4caf50'; // 草皮
        state.ctx.fillRect(px, py, TILE_SIZE, 10);
    } else if (id === BLOCKS.BRICK) {
        state.ctx.fillStyle = 'rgba(0,0,0,0.1)';
        state.ctx.fillRect(px, py + 10, TILE_SIZE, 2);
        state.ctx.fillRect(px + 20, py, 2, 10);
        state.ctx.fillRect(px + 10, py + 12, 2, 28);
    } else if (id === BLOCKS.WOOD) {
        state.ctx.fillStyle = 'rgba(0,0,0,0.1)';
        state.ctx.fillRect(px + 5, py, 4, TILE_SIZE);
        state.ctx.fillRect(px + 25, py, 4, TILE_SIZE);
    } else if (id === BLOCKS.LEAVES) {
         state.ctx.fillStyle = 'rgba(0,0,0,0.1)';
         state.ctx.fillRect(px+5, py+5, 10, 10);
         state.ctx.fillRect(px+20, py+20, 10, 10);
    }
    
    // 边框
    state.ctx.strokeStyle = 'rgba(0,0,0,0.1)';
    state.ctx.lineWidth = 1;
    state.ctx.strokeRect(px, py, TILE_SIZE, TILE_SIZE);
}

function drawPlayer() {
    const p = state.player;
    
    // 身体
    state.ctx.fillStyle = '#3b82f6'; // 蓝色衣服
    state.ctx.fillRect(p.x, p.y + 20, p.width, p.height - 20);
    
    // 头
    state.ctx.fillStyle = '#fca5a5'; // 肤色
    state.ctx.fillRect(p.x + 2, p.y, p.width - 4, 20);
    
    // 眼睛
    state.ctx.fillStyle = 'white';
    state.ctx.fillRect(p.x + 4, p.y + 6, 4, 4);
    state.ctx.fillRect(p.x + 12, p.y + 6, 4, 4);
    state.ctx.fillStyle = 'black';
    state.ctx.fillRect(p.x + 6, p.y + 6, 2, 2);
    state.ctx.fillRect(p.x + 14, p.y + 6, 2, 2);

    // 腿
    state.ctx.fillStyle = '#1e3a8a'; // 深色裤子
    state.ctx.fillRect(p.x + 2, p.y + p.height - 15, 6, 15);
    state.ctx.fillRect(p.x + 12, p.y + p.height - 15, 6, 15);
}

// --- UI 管理 ---
function initUI() {
    const hotbar = document.getElementById('hotbar');
    hotbar.innerHTML = '';
    
    INVENTORY.forEach((blockId, index) => {
        const slot = document.createElement('div');
        slot.className = `slot ${index === 0 ? 'active' : ''}`;
        slot.onclick = () => {
            state.player.selectedSlot = index;
            updateHotbarUI();
        };
        
        // 创建方块预览
        const preview = document.createElement('div');
        preview.style.width = '32px';
        preview.style.height = '32px';
        preview.className = `block-${getBlockName(blockId).toLowerCase()}`;
        if(blockId === BLOCKS.GRASS) preview.className = 'block-grass'; // 特殊处理
        
        const num = document.createElement('span');
        num.className = 'slot-number';
        num.innerText = index + 1;
        
        slot.appendChild(preview);
        slot.appendChild(num);
        hotbar.appendChild(slot);
    });
}

function updateHotbarUI() {
    const slots = document.querySelectorAll('.slot');
    slots.forEach((slot, idx) => {
        if (idx === state.player.selectedSlot) slot.classList.add('active');
        else slot.classList.remove('active');
    });
}

function getBlockName(id) {
    return Object.keys(BLOCKS).find(key => BLOCKS[key] === id);
}

// --- 主循环 ---
function gameLoop(timestamp) {
    const dt = timestamp - state.lastTime;
    state.lastTime = timestamp;

    update(dt);
    draw();

    requestAnimationFrame(gameLoop);
}
</script>
</body>
</html>