Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgsn0jn9e67y9tduntitledPlain Text
<!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