🎮ArcadeLab

BloqueCraft - VideojuCan

by CosmicCobra59
355 lines13.7 KB
▶ Play
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>BloqueCraft - VideojuCan</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            background: #1a1a2e;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            font-family: 'Courier New', monospace;
            overflow: hidden;
            touch-action: manipulation;
            user-select: none;
            -webkit-user-select: none;
        }
        #gameContainer {
            position: relative;
            width: 100vw;
            height: 100vh;
            max-width: 480px;
            max-height: 700px;
            background: #87CEEB;
            overflow: hidden;
            border: 4px solid #5a3a1a;
        }
        canvas { display: block; width: 100%; height: 100%; image-rendering: pixelated; }
        #ui {
            position: absolute; top: 8px; left: 8px; right: 8px;
            display: flex; justify-content: space-between;
            pointer-events: none; color: #fff; font-size: 13px;
            font-weight: bold; text-shadow: 2px 2px 0 #000;
        }
        #controls {
            position: absolute; bottom: 12px; left: 0; right: 0;
            display: flex; justify-content: space-between;
            padding: 0 8px; pointer-events: none;
        }
        .ctrl-group { display: flex; gap: 4px; pointer-events: all; }
        .ctrl-btn {
            width: 44px; height: 44px; border-radius: 50%;
            background: rgba(0,0,0,0.5); border: 2px solid rgba(255,255,255,0.6);
            color: #fff; font-size: 18px;
            display: flex; align-items: center; justify-content: center;
            cursor: pointer; touch-action: manipulation;
        }
        .ctrl-btn:active { background: rgba(255,255,255,0.4); transform: scale(0.9); }
        #placeBtn { background: rgba(255,150,0,0.5); border-color: #f90; }
    </style>
</head>
<body>

    <div id="gameContainer">
        <canvas id="gameCanvas" width="480" height="700"></canvas>
        <div id="ui">
            <span>❤️ <span id="hpDisplay">10</span></span>
            <span>🪵 <span id="woodDisplay">0</span></span>
            <span>🪨 <span id="stoneDisplay">0</span></span>
        </div>
        <div id="controls">
            <div class="ctrl-group">
                <button class="ctrl-btn" id="btnLeft">◀</button>
                <button class="ctrl-btn" id="btnRight">▶</button>
            </div>
            <button class="ctrl-btn" id="placeBtn">🧱</button>
            <div class="ctrl-group">
                <button class="ctrl-btn" id="btnJump">▲</button>
                <button class="ctrl-btn" id="btnBreak">⛏️</button>
            </div>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = false;

        const W = 480, H = 700;
        canvas.width = W;
        canvas.height = H;

        const BLOCK = 30;
        const COLS = Math.floor(W / BLOCK);  // 16
        const ROWS = Math.floor(H / BLOCK);  // ~23

        // Mundo: 0=aire, 1=tierra, 2=piedra, 3=madera, 4=hoja
        let world = [];

        function generateWorld() {
            world = [];
            for (let y = 0; y < ROWS; y++) {
                world[y] = [];
                for (let x = 0; x < COLS; x++) {
                    // Suelo sólido en las últimas 5 filas
                    if (y >= ROWS - 5) {
                        world[y][x] = (y === ROWS - 1 || y === ROWS - 2) ? 2 : 1; // piedra abajo, tierra encima
                    }
                    // Algunos bloques de tierra sueltos
                    else if (y >= ROWS - 8 && Math.random() < 0.25) {
                        world[y][x] = 1;
                    } else {
                        world[y][x] = 0;
                    }
                }
            }
            // Árboles
            for (let i = 0; i < 5; i++) {
                let tx = 2 + Math.floor(Math.random() * (COLS - 4));
                let ty = ROWS - 6; // sobre el suelo
                if (world[ty][tx] === 1 || world[ty][tx] === 2) {
                    for (let h = 1; h <= 4; h++) {
                        if (ty - h >= 0) world[ty - h][tx] = 3; // tronco
                    }
                    // Hojas
                    for (let dy = -1; dy <= 1; dy++) {
                        for (let dx = -1; dx <= 1; dx++) {
                            let lx = tx + dx, ly = ty - 4 + dy;
                            if (lx >= 0 && lx < COLS && ly >= 0 && ly < ROWS && world[ly][lx] === 0) {
                                world[ly][lx] = 4;
                            }
                        }
                    }
                }
            }
        }

        // Jugador
        let player = {
            x: W / 2, y: 100,
            w: 20, h: 34,
            vx: 0, vy: 0,
            onGround: false,
            hp: 10,
            inv: 0
        };

        // Inventario
        let inventory = { wood: 0, stone: 0 };
        let selectedBlock = 3; // 3=madera

        // Controles
        let keys = { left: false, right: false, jump: false, place: false, break: false };

        // Bindeo móvil
        function bind(id, key) {
            let el = document.getElementById(id);
            if (!el) return;
            el.addEventListener('pointerdown', e => { e.preventDefault(); keys[key] = true; });
            el.addEventListener('pointerup', e => { keys[key] = false; });
            el.addEventListener('pointerleave', e => { keys[key] = false; });
        }
        bind('btnLeft', 'left');
        bind('btnRight', 'right');
        bind('btnJump', 'jump');
        bind('placeBtn', 'place');
        bind('btnBreak', 'break');

        // Teclado PC
        window.addEventListener('keydown', e => {
            if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = true;
            if (e.key === 'ArrowRight' || e.key === 'd') keys.right = true;
            if (e.key === 'ArrowUp' || e.key === 'w' || e.key === ' ') { e.preventDefault(); keys.jump = true; }
            if (e.key === 'e') keys.place = true;
            if (e.key === 'q') keys.break = true;
        });
        window.addEventListener('keyup', e => {
            if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = false;
            if (e.key === 'ArrowRight' || e.key === 'd') keys.right = false;
            if (e.key === 'ArrowUp' || e.key === 'w' || e.key === ' ') keys.jump = false;
            if (e.key === 'e') keys.place = false;
            if (e.key === 'q') keys.break = false;
        });

        function updateUI() {
            document.getElementById('hpDisplay').textContent = player.hp;
            document.getElementById('woodDisplay').textContent = inventory.wood;
            document.getElementById('stoneDisplay').textContent = inventory.stone;
        }

        function getBlock(x, y) {
            let bx = Math.floor(x / BLOCK);
            let by = Math.floor(y / BLOCK);
            if (bx < 0 || bx >= COLS || by < 0 || by >= ROWS) return 999; // fuera = sólido
            return world[by][bx];
        }

        function update() {
            // Movimiento horizontal
            if (keys.left) player.vx = -4;
            else if (keys.right) player.vx = 4;
            else player.vx *= 0.7;

            // Saltar
            if (keys.jump && player.onGround) {
                player.vy = -9;
                player.onGround = false;
                keys.jump = false;
            }

            // Gravedad
            player.vy += 0.5;
            if (player.vy > 12) player.vy = 12;

            // Mover X
            player.x += player.vx;
            // Colisión X (pies)
            let footY = player.y + player.h / 2 - 2;
            if (getBlock(player.x - player.w / 2, footY) !== 0 || getBlock(player.x + player.w / 2, footY) !== 0) {
                player.x -= player.vx;
                player.vx = 0;
            }
            // Colisión X (cabeza)
            let headY = player.y - player.h / 2 + 2;
            if (getBlock(player.x - player.w / 2, headY) !== 0 || getBlock(player.x + player.w / 2, headY) !== 0) {
                player.x -= player.vx;
                player.vx = 0;
            }
            // Limitar a pantalla
            player.x = Math.max(player.w / 2, Math.min(W - player.w / 2, player.x));

            // Mover Y
            player.y += player.vy;
            // Colisión Y (pies)
            let leftFoot = getBlock(player.x - player.w / 2 + 3, player.y + player.h / 2);
            let rightFoot = getBlock(player.x + player.w / 2 - 3, player.y + player.h / 2);
            player.onGround = false;
            if (leftFoot !== 0 || rightFoot !== 0) {
                if (player.vy > 0) {
                    player.y = Math.floor((player.y + player.h / 2) / BLOCK) * BLOCK - player.h / 2;
                    player.vy = 0;
                    player.onGround = true;
                }
            }
            // Colisión Y (cabeza)
            let leftHead = getBlock(player.x - player.w / 2 + 3, player.y - player.h / 2);
            let rightHead = getBlock(player.x + player.w / 2 - 3, player.y - player.h / 2);
            if (leftHead !== 0 || rightHead !== 0) {
                if (player.vy < 0) {
                    player.y = (Math.floor((player.y - player.h / 2) / BLOCK) + 1) * BLOCK + player.h / 2;
                    player.vy = 0;
                }
            }
            // Limitar Y
            player.y = Math.max(player.h / 2, Math.min(H - player.h / 2, player.y));

            // Acciones (romper/colocar)
            let ax = Math.floor(player.x / BLOCK);
            let ay = Math.floor((player.y + player.h / 2 + 5) / BLOCK);
            if (ax >= 0 && ax < COLS && ay >= 0 && ay < ROWS) {
                // Romper
                if (keys.break && world[ay][ax] !== 0) {
                    let t = world[ay][ax];
                    world[ay][ax] = 0;
                    if (t === 1 || t === 3) inventory.wood += (t === 3 ? 2 : 1);
                    if (t === 2) inventory.stone += 1;
                    keys.break = false;
                }
                // Colocar
                if (keys.place && world[ay][ax] === 0) {
                    if (selectedBlock === 3 && inventory.wood > 0) { world[ay][ax] = 3; inventory.wood--; keys.place = false; }
                    if (selectedBlock === 2 && inventory.stone > 0) { world[ay][ax] = 2; inventory.stone--; keys.place = false; }
                }
            }

            // Daño por caída
            if (player.onGround && player.vy > 10 && player.inv <= 0) {
                player.hp--;
                player.inv = 30;
            }
            if (player.inv > 0) player.inv--;
            if (player.hp <= 0) {
                player.hp = 10;
                player.x = W / 2;
                player.y = 80;
                player.vy = 0;
            }
            updateUI();
        }

        function draw() {
            // Cielo
            let grad = ctx.createLinearGradient(0, 0, 0, H);
            grad.addColorStop(0, '#4a90d9');
            grad.addColorStop(1, '#87CEEB');
            ctx.fillStyle = grad;
            ctx.fillRect(0, 0, W, H);

            // Sol
            ctx.fillStyle = '#f1c40f';
            ctx.beginPath();
            ctx.arc(400, 60, 30, 0, Math.PI * 2);
            ctx.fill();

            // Nubes
            ctx.fillStyle = '#fff';
            [
                [100, 50],
                [280, 70]
            ].forEach(([cx, cy]) => {
                ctx.beginPath();
                ctx.arc(cx, cy, 20, 0, Math.PI * 2);
                ctx.arc(cx + 20, cy - 8, 16, 0, Math.PI * 2);
                ctx.arc(cx + 35, cy, 18, 0, Math.PI * 2);
                ctx.fill();
            });

            // Bloques
            for (let y = 0; y < ROWS; y++) {
                for (let x = 0; x < COLS; x++) {
                    let b = world[y][x];
                    if (b === 0) continue;
                    let bx = x * BLOCK,
                        by = y * BLOCK;
                    if (b === 1) { ctx.fillStyle = '#8B5E3C';
                        ctx.fillRect(bx, by, BLOCK, BLOCK); } // tierra
                    if (b === 2) { ctx.fillStyle = '#777';
                        ctx.fillRect(bx, by, BLOCK, BLOCK);
                        ctx.fillStyle = '#999';
                        ctx.fillRect(bx + 3, by + 3, BLOCK - 6, BLOCK - 6); } // piedra
                    if (b === 3) { ctx.fillStyle = '#8B6914';
                        ctx.fillRect(bx, by, BLOCK, BLOCK);
                        ctx.fillStyle = '#6B4914';
                        ctx.fillRect(bx + 2, by, BLOCK - 4, BLOCK); } // madera
                    if (b === 4) { ctx.fillStyle = '#2d8c2d';
                        ctx.fillRect(bx, by, BLOCK, BLOCK); } // hoja
                }
            }

            // Jugador
            ctx.fillStyle = '#3a4a8a';
            ctx.fillRect(player.x - 5, player.y + 2, 10, 14); // piernas
            ctx.fillStyle = '#4a8a3a';
            ctx.fillRect(player.x - 7, player.y - 7, 14, 13); // cuerpo
            ctx.fillStyle = '#c69c6d';
            ctx.fillRect(player.x - 6, player.y - 18, 12, 13); // cabeza
            ctx.fillStyle = '#fff';
            ctx.fillRect(player.x + 1, player.y - 15, 3, 3); // ojo
            ctx.fillStyle = '#000';
            ctx.fillRect(player.x + 2, player.y - 14, 1, 1); // pupila
            ctx.fillStyle = '#4a3520';
            ctx.fillRect(player.x - 7, player.y - 22, 14, 6); // pelo
        }

        function loop() { update();
            draw();
            requestAnimationFrame(loop); }
        generateWorld();
        loop();
    </script>
</body>
</html>

Game Source: BloqueCraft - VideojuCan

Creator: CosmicCobra59

Libraries: none

Complexity: complex (355 lines, 13.7 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: bloquecraft-videojucan-cosmiccobra59" to link back to the original. Then publish at arcadelab.ai/publish.