untitled

Pasted Oct 24, 2026, 3:37 PM1 file248 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgsn0jn9e67y9td
untitled
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Pure Red Room - Survival Mode</title>
    <style>
        body { margin: 0; padding: 0; overflow: hidden; background-color: #000; font-family: sans-serif; user-select: none; }
        canvas { display: block; width: 100vw; height: 100vh; image-rendering: pixelated; }
        #instructions {
            position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
            color: #ff3333; font-size: 20px; text-align: center;
            text-shadow: 2px 2px 4px #000; font-weight: bold; pointer-events: none;
            z-index: 10;
        }
        #crosshair {
            position: absolute; top: 50%; left: 50%;
            width: 6px; height: 6px; background: rgba(255,255,255,0.4);
            transform: translate(-50%, -50%); border-radius: 50%; pointer-events: none; z-index: 5;
        }
        #timer {
            position: absolute; top: 70px; left: 50%; transform: translateX(-50%);
            color: #ffffff; font-size: 32px; font-family: monospace; font-weight: bold;
            text-shadow: 2px 2px 4px #000; pointer-events: none; z-index: 10;
        }
    </style>
</head>
<body>
    <div id="crosshair"></div>
    <div id="instructions">3D RED ROOM (Survival)<br>WASD to Move | Move Mouse to Look Around | SPACEBAR to Jump</div>
    <div id="timer">Time: 0.0s / 10.0s</div>
    <canvas id="gameCanvas"></canvas>

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

        function resize() {
            canvas.width = window.innerWidth / 2;
            canvas.height = window.innerHeight / 2;
        }
        window.addEventListener('resize', resize);
        resize();

        const player = {
            x: 12.0, 
            y: 12.0,
            dirX: 1,
            dirY: 0,
            planeX: 0,
            planeY: 1.2, 
            moveSpeed: 5.0, 
            pitch: 0,
            z: 0,            
            zVelocity: 0,    
            isJumping: false
        };

        // Monster properties matching the Raycaster engine setup
        const monster = {
            x: 2.0, // Spawn far away near the top corner of the room
            y: 2.0,
            moveSpeed: 3.5, 
            size: 0.4 // Collision/visual thickness factor
        };

        // Game State Engine variables
        let survivalTime = 0;
        const timeLimit = 10.0;
        let gameOver = false;
        let gameWon = false;

        const gravity = -22.0;  
        const jumpForce = 7.0;  

        const mapLayout = [
            "111111111111111111111111",
            "100000000000000000000001",
            "102002002002002002002001",
            "100000000000000000000001",
            "100000000000000000000001",
            "100200020002000200020001",
            "100000000000000000000001",
            "100000000000000000000001",
            "102002002002002002002001",
            "100000000000000000000001",
            "100000000000000000000001",
            "100200020002000200020001",
            "100000000000000000000001",
            "100000000000000000000001",
            "102002002002002002002001",
            "100000000000000000000001",
            "100000000000000000000001",
            "100200020002000200020001",
            "100000000000000000000001",
            "100000000000000000000001",
            "102002002002002002002001",
            "100000000000000000000001",
            "100000000000000000000001",
            "111111111111111111111111"
        ];

        const map = mapLayout.map(row => row.split('').map(Number));
        const mapMaxIdx = map.length - 1;
        const keys = {};

        window.addEventListener('keydown', e => {
            keys[e.key.toLowerCase()] = true;
            if (e.key === ' ' && !player.isJumping && !gameOver && !gameWon) {
                player.zVelocity = jumpForce;
                player.isJumping = true;
            }
            // Press R to restart game seamlessly
            if (e.key.toLowerCase() === 'r' && (gameOver || gameWon)) {
                resetGame();
            }
        });
        window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);

        canvas.addEventListener('click', () => {
            canvas.requestPointerLock();
        });

        window.addEventListener('mousemove', e => {
            if (document.pointerLockElement !== canvas) return;
            if (gameOver || gameWon) return;
            
            const rotation = e.movementX * 0.003; 
            if (rotation !== 0) {
                const oldDirX = player.dirX;
                player.dirX = player.dirX * Math.cos(rotation) - player.dirY * Math.sin(rotation);
                player.dirY = oldDirX * Math.sin(rotation) + player.dirY * Math.cos(rotation);
                const oldPlaneX = player.planeX;
                player.planeX = player.planeX * Math.cos(rotation) - player.planeY * Math.sin(rotation);
                player.planeY = oldPlaneX * Math.sin(rotation) + player.planeY * Math.cos(rotation);
            }

            player.pitch -= e.movementY * 0.4;
            const pitchLimit = canvas.height * 0.75;
            player.pitch = Math.max(-pitchLimit, Math.min(pitchLimit, player.pitch));
        });

        function resetGame() {
            player.x = 12.0;
            player.y = 12.0;
            player.z = 0;
            player.zVelocity = 0;
            player.isJumping = false;
            monster.x = 2.0;
            monster.y = 2.0;
            survivalTime = 0;
            gameOver = false;
            gameWon = false;
            timerElement.style.color = '#ffffff';
            timerElement.innerHTML = `Time: 0.0s / ${timeLimit.toFixed(1)}s`;
        }

        let lastTime = performance.now();
        const zBuffer = new Array(canvas.width); // Depth array tracking for correct sprite rendering

        function loop() {
            requestAnimationFrame(loop);

            const now = performance.now();
            const delta = Math.min((now - lastTime) / 1000, 0.1);
            lastTime = now;

            // --- Game State Tracking & AI Engine Run ---
            if (!gameOver && !gameWon) {
                survivalTime += delta;
                timerElement.innerHTML = `Time: ${Math.min(survivalTime, timeLimit).toFixed(1)}s / ${timeLimit.toFixed(1)}s`;

                if (survivalTime >= timeLimit) {
                    gameWon = true;
                    timerElement.innerHTML = 'YOU SURVIVED! [Press R to Restart]';
                    timerElement.style.color = '#00ff00';
                }

                // AI Tracking Vector Arithmetic (Move towards player coordinate values)
                let toPlayerX = player.x - monster.x;
                let toPlayerY = player.y - monster.y;
                let distToPlayer = Math.sqrt(toPlayerX * toPlayerX + toPlayerY * toPlayerY);

                if (distToPlayer > 0) {
                    monster.x += (toPlayerX / distToPlayer) * monster.moveSpeed * delta;
                    monster.y += (toPlayerY / distToPlayer) * monster.moveSpeed * delta;
                }

                // Check Instant Kill Distance Condition (Radius hit box test)
                if (distToPlayer < 0.6) {
                    gameOver = true;
                    timerElement.innerHTML = 'YOU DIED! [Press R to Restart]';
                    timerElement.style.color = '#ff0000';
                }
            }

            // Player physics updating loops
            if (player.isJumping) {
                player.zVelocity += gravity * delta; 
                player.z += player.zVelocity * delta;

                if (player.z <= 0) {
                    player.z = 0;
                    player.zVelocity = 0;
                    player.isJumping = false;
                }
            }

            let moveX = 0;
            let moveY = 0;

            if (!gameOver && !gameWon) {
                if (keys['w'] || keys['arrowup']) {
                    moveX += player.dirX * player.moveSpeed * delta;
                    moveY += player.dirY * player.moveSpeed * delta;
                }
                if (keys['s'] || keys['arrowdown']) {
                    moveX -= player.dirX * player.moveSpeed * delta;
                    moveY -= player.dirY * player.moveSpeed * delta;
                }
                if (keys['a'] || keys['arrowleft']) {
                    moveX -= player.planeX * player.moveSpeed * delta;
                    moveY -= player.planeY * player.moveSpeed * delta;
                }
                if (keys['d'] || keys['arrowright']) {
                    moveX += player.planeX * player.moveSpeed * delta;
                    moveY += player.planeY * player.moveSpeed * delta;
                }
            }

            const buffer = 0.3;
            const signX = moveX > 0 ? buffer : -buffer;
            const signY = moveY > 0 ? buffer : -buffer;

            const nextGridX = Math.max(0, Math.min(mapMaxIdx, Math.floor(player.x + moveX + signX)));
            const currentGridY = Math.max(0, Math.min(mapMaxIdx, Math.floor(player.y)));
            const tileX = map[nextGridX][currentGridY];
            if (tileX === 0 || (tileX === 2 && player.z > 0.35)) player.x += moveX;

            const currentGridX = Math.max(0, Math.min(mapMaxIdx, Math.floor(player.x)));
            const nextGridY = Math.max(0, Math.min(mapMaxIdx, Math.floor(player.y + moveY + signY)));
            const tileY = map[currentGridX][nextGridY];
            if (tileY === 0 || (tileY === 2 && player.z > 0.35)) player.y += moveY;

            const jumpHeightOffset = Math.floor(player.z * (canvas.height * 0.4));
            const horizon = Math.floor(canvas.height / 2 + player.pitch + jumpHeightOffset);

9,982 chars · codepastes.com