anycoder-dd1ce949 / index.html
Gonfrist8's picture
Upload folder using huggingface_hub
7fcb540 verified
Raw
History Blame Contribute Delete
34.1 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hytale - Voxel Adventure</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Inter:wght@300;400;600&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
font-family: 'Inter', sans-serif;
background: #0a0a0f;
}
.hytale-font {
font-family: 'Cinzel', serif;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
}
#canvas-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to bottom, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
}
.glass-panel {
background: rgba(20, 20, 35, 0.85);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
.hytale-btn {
background: linear-gradient(135deg, #e94560 0%, #c23a51 100%);
border: 2px solid #ff6b6b;
color: white;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 700;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.hytale-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(233, 69, 96, 0.4);
border-color: #ff8585;
}
.hytale-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
transition: left 0.5s;
}
.hytale-btn:hover::before {
left: 100%;
}
.hytale-btn.secondary {
background: linear-gradient(135deg, #533483 0%, #3d2463 100%);
border-color: #7b68ee;
}
.hytale-btn.secondary:hover {
box-shadow: 0 10px 20px rgba(83, 52, 131, 0.4);
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
}
#crosshair::before,
#crosshair::after {
content: '';
position: absolute;
background: rgba(255, 255, 255, 0.9);
}
#crosshair::before {
width: 2px;
height: 20px;
left: 50%;
transform: translateX(-50%);
}
#crosshair::after {
width: 20px;
height: 2px;
top: 50%;
transform: translateY(-50%);
}
.hotbar {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.hotbar-slot {
width: 60px;
height: 60px;
background: rgba(0, 0, 0, 0.6);
border: 3px solid #444;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
position: relative;
}
.hotbar-slot.active {
border-color: #e94560;
transform: scale(1.1);
box-shadow: 0 0 20px rgba(233, 69, 96, 0.5);
}
.hotbar-slot:hover {
border-color: #666;
}
.block-icon {
width: 40px;
height: 40px;
border-radius: 4px;
}
#block-highlight {
position: absolute;
border: 2px solid rgba(255, 255, 255, 0.5);
pointer-events: none;
display: none;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #0a0a0f;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: opacity 0.5s;
}
.loading-bar {
width: 300px;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
margin-top: 20px;
}
.loading-progress {
height: 100%;
background: linear-gradient(90deg, #e94560, #ff6b6b);
width: 0%;
transition: width 0.3s;
box-shadow: 0 0 10px rgba(233, 69, 96, 0.5);
}
.title-glow {
text-shadow: 0 0 20px rgba(233, 69, 96, 0.5), 0 0 40px rgba(233, 69, 96, 0.3);
}
.floating {
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
pointer-events: none;
}
#anycoder-link {
position: absolute;
top: 20px;
right: 20px;
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
font-size: 0.9rem;
transition: color 0.3s;
z-index: 100;
}
#anycoder-link:hover {
color: #e94560;
}
.controls-hint {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
text-align: center;
opacity: 0;
transition: opacity 0.3s;
}
.inventory-grid {
display: grid;
grid-template-columns: repeat(9, 1fr);
gap: 10px;
padding: 20px;
}
.health-bar {
position: absolute;
bottom: 110px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
opacity: 0;
transition: opacity 0.3s;
}
.heart {
width: 24px;
height: 24px;
background: #e94560;
transform: rotate(45deg);
position: relative;
box-shadow: 0 0 10px rgba(233, 69, 96, 0.5);
}
.heart::before,
.heart::after {
content: '';
width: 24px;
height: 24px;
position: absolute;
background: #e94560;
border-radius: 50%;
}
.heart::before {
left: -12px;
}
.heart::after {
top: -12px;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="canvas-container"></canvas>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" id="anycoder-link">Built with anycoder</a>
<div id="crosshair"></div>
<div class="health-bar" id="health-bar">
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
<div class="heart"></div>
</div>
<div class="hotbar" id="hotbar">
<div class="hotbar-slot active" data-block="grass">
<div class="block-icon" style="background: linear-gradient(135deg, #567d46, #3d5c2d);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">1</span>
</div>
<div class="hotbar-slot" data-block="dirt">
<div class="block-icon" style="background: linear-gradient(135deg, #8b4513, #654321);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">2</span>
</div>
<div class="hotbar-slot" data-block="stone">
<div class="block-icon" style="background: linear-gradient(135deg, #808080, #606060);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">3</span>
</div>
<div class="hotbar-slot" data-block="wood">
<div class="block-icon" style="background: linear-gradient(135deg, #8b5a2b, #6b4423);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">4</span>
</div>
<div class="hotbar-slot" data-block="leaves">
<div class="block-icon" style="background: linear-gradient(135deg, #228b22, #196f19);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">5</span>
</div>
<div class="hotbar-slot" data-block="sand">
<div class="block-icon" style="background: linear-gradient(135deg, #f4a460, #d2691e);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">6</span>
</div>
<div class="hotbar-slot" data-block="water">
<div class="block-icon" style="background: linear-gradient(135deg, #4682b4, #2f5680);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">7</span>
</div>
<div class="hotbar-slot" data-block="gold">
<div class="block-icon" style="background: linear-gradient(135deg, #ffd700, #daa520);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">8</span>
</div>
<div class="hotbar-slot" data-block="diamond">
<div class="block-icon" style="background: linear-gradient(135deg, #00ffff, #00ced1);"></div>
<span style="position: absolute; bottom: 2px; left: 4px; font-size: 10px; color: white; font-weight: bold;">9</span>
</div>
</div>
<div class="controls-hint" id="controls-hint">
<p>Click to break/place blocks • WASD to move • SPACE to jump</p>
</div>
<div id="main-menu" class="absolute inset-0 flex items-center justify-center z-50">
<div class="glass-panel p-12 rounded-2xl max-w-2xl w-full mx-4 floating">
<div class="text-center mb-8">
<h1 class="hytale-font text-6xl font-black text-white mb-2 title-glow tracking-wider">HYTALE</h1>
<p class="text-gray-400 text-lg tracking-widest uppercase">Voxel Adventure Engine</p>
</div>
<div class="space-y-4">
<button onclick="startGame()" class="hytale-btn w-full py-4 rounded-lg text-lg font-bold">
Play Solo
</button>
<button class="hytale-btn secondary w-full py-4 rounded-lg text-lg font-bold">
Multiplayer
</button>
<div class="grid grid-cols-2 gap-4 mt-6">
<button class="hytale-btn secondary py-3 rounded-lg text-sm">
Settings
</button>
<button class="hytale-btn secondary py-3 rounded-lg text-sm">
Mods
</button>
</div>
</div>
<div class="mt-8 pt-6 border-t border-gray-700">
<div class="flex justify-between text-sm text-gray-500">
<span>Version 1.0.0</span>
<span>Not affiliated with Hypixel Studios</span>
</div>
</div>
</div>
</div>
<div class="loading-screen" id="loading-screen" style="display: none;">
<h2 class="hytale-font text-4xl text-white mb-4">Generating World...</h2>
<div class="loading-bar">
<div class="loading-progress" id="loading-progress"></div>
</div>
<p class="text-gray-400 mt-4" id="loading-text">Preparing chunks...</p>
</div>
<div id="pause-menu" class="absolute inset-0 flex items-center justify-center z-50 hidden" style="background: rgba(0,0,0,0.7);">
<div class="glass-panel p-8 rounded-2xl max-w-md w-full mx-4">
<h2 class="hytale-font text-3xl text-white text-center mb-6">Game Menu</h2>
<div class="space-y-3">
<button onclick="resumeGame()" class="hytale-btn w-full py-3 rounded-lg">Resume</button>
<button onclick="returnToMenu()" class="hytale-btn secondary w-full py-3 rounded-lg">Save & Quit</button>
</div>
</div>
</div>
</div>
<script>
// Game State
let scene, camera, renderer;
let world = {};
let chunks = {};
let player = {
x: 0,
y: 30,
z: 0,
vx: 0,
vy: 0,
vz: 0,
onGround: false,
height: 1.8,
width: 0.6
};
let keys = {};
let selectedBlock = 'grass';
let isPaused = true;
let isLocked = false;
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let highlightMesh;
let simplex = new SimplexNoise();
const BLOCK_SIZE = 1;
const CHUNK_SIZE = 16;
const RENDER_DISTANCE = 4;
const blockTypes = {
grass: { color: 0x567d46, name: 'Grass' },
dirt: { color: 0x8b4513, name: 'Dirt' },
stone: { color: 0x808080, name: 'Stone' },
wood: { color: 0x8b5a2b, name: 'Wood' },
leaves: { color: 0x228b22, name: 'Leaves', transparent: true },
sand: { color: 0xf4a460, name: 'Sand' },
water: { color: 0x4682b4, name: 'Water', transparent: true },
gold: { color: 0xffd700, name: 'Gold Ore' },
diamond: { color: 0x00ffff, name: 'Diamond' }
};
let materials = {};
let geometry = new THREE.BoxGeometry(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
// Initialize Three.js
function init() {
const canvas = document.getElementById('canvas-container');
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(player.x, player.y, player.z);
renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 100, 50);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -50;
directionalLight.shadow.camera.right = 50;
directionalLight.shadow.camera.top = 50;
directionalLight.shadow.camera.bottom = -50;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 200;
scene.add(directionalLight);
// Create materials
for (let key in blockTypes) {
materials[key] = new THREE.MeshLambertMaterial({
color: blockTypes[key].color,
transparent: blockTypes[key].transparent || false,
opacity: blockTypes[key].transparent ? 0.8 : 1.0
});
}
// Highlight mesh
const highlightGeo = new THREE.BoxGeometry(BLOCK_SIZE + 0.05, BLOCK_SIZE + 0.05, BLOCK_SIZE + 0.05);
const highlightMat = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true,
transparent: true,
opacity: 0.5
});
highlightMesh = new THREE.Mesh(highlightGeo, highlightMat);
scene.add(highlightMesh);
highlightMesh.visible = false;
// Event listeners
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
document.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('pointerlockchange', onPointerLockChange);
// Hotbar selection
document.querySelectorAll('.hotbar-slot').forEach((slot, index) => {
slot.addEventListener('click', () => selectSlot(index));
});
window.addEventListener('wheel', (e) => {
const slots = document.querySelectorAll('.hotbar-slot');
let current = Array.from(slots).findIndex(s => s.classList.contains('active'));
let next = current + (e.deltaY > 0 ? 1 : -1);
if (next < 0) next = slots.length - 1;
if (next >= slots.length) next = 0;
selectSlot(next);
});
}
function selectSlot(index) {
document.querySelectorAll('.hotbar-slot').forEach(s => s.classList.remove('active'));
const slots = document.querySelectorAll('.hotbar-slot');
slots[index].classList.add('active');
selectedBlock = slots[index].dataset.block;
}
// World Generation
function generateChunk(cx, cz) {
const chunkKey = `${cx},${cz}`;
if (chunks[chunkKey]) return;
const chunk = new THREE.Group();
chunk.position.set(cx * CHUNK_SIZE * BLOCK_SIZE, 0, cz * CHUNK_SIZE * BLOCK_SIZE);
for (let x = 0; x < CHUNK_SIZE; x++) {
for (let z = 0; z < CHUNK_SIZE; z++) {
const worldX = cx * CHUNK_SIZE + x;
const worldZ = cz * CHUNK_SIZE + z;
// Terrain height using noise
const noise = simplex.noise2D(worldX * 0.05, worldZ * 0.05);
const height = Math.floor(20 + noise * 10);
// Generate column
for (let y = 0; y <= height; y++) {
let type = 'stone';
if (y === height) type = 'grass';
else if (y > height - 3) type = 'dirt';
// Caves
if (y < height - 5) {
const caveNoise = simplex.noise3D(worldX * 0.1, y * 0.1, worldZ * 0.1);
if (caveNoise > 0.7) continue;
}
// Ores
if (type === 'stone' && y < height - 10) {
const oreNoise = simplex.noise3D(worldX * 0.2, y * 0.2, worldZ * 0.2);
if (oreNoise > 0.8) type = 'gold';
else if (oreNoise < -0.8) type = 'diamond';
}
createBlock(worldX, y, worldZ, type, chunk);
}
// Trees
if (Math.random() < 0.02 && height > 15) {
generateTree(worldX, height + 1, worldZ, chunk);
}
// Water
if (height < 18) {
for (let wy = height + 1; wy <= 18; wy++) {
createBlock(worldX, wy, worldZ, 'water', chunk);
}
}
}
}
scene.add(chunk);
chunks[chunkKey] = chunk;
}
function generateTree(x, y, z, chunk) {
// Trunk
for (let i = 0; i < 4; i++) {
createBlock(x, y + i, z, 'wood', chunk);
}
// Leaves
for (let lx = -2; lx <= 2; lx++) {
for (let lz = -2; lz <= 2; lz++) {
for (let ly = 3; ly <= 5; ly++) {
if (Math.abs(lx) + Math.abs(lz) + Math.abs(ly - 4) < 4) {
createBlock(x + lx, y + ly, z + lz, 'leaves', chunk);
}
}
}
}
}
function createBlock(x, y, z, type, chunk) {
const mesh = new THREE.Mesh(geometry, materials[type]);
mesh.position.set(x * BLOCK_SIZE, y * BLOCK_SIZE, z * BLOCK_SIZE);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.userData = { type: type, x, y, z };
chunk.add(mesh);
const key = `${x},${y},${z}`;
world[key] = { type, mesh, chunk };
}
// Game Loop
function animate() {
requestAnimationFrame(animate);
if (!isPaused && isLocked) {
updatePhysics();
updateChunks();
updateHighlight();
}
renderer.render(scene, camera);
}
function updatePhysics() {
// Gravity
player.vy -= 0.02;
// Movement
const speed = 0.15;
const direction = new THREE.Vector3();
if (keys['w']) direction.z -= 1;
if (keys['s']) direction.z += 1;
if (keys['a']) direction.x -= 1;
if (keys['d']) direction.x += 1;
direction.normalize().multiplyScalar(speed);
direction.applyEuler(new THREE.Euler(0, camera.rotation.y, 0));
player.vx = direction.x;
player.vz = direction.z;
// Apply velocity with collision
tryMove(player.vx, 0, 0);
tryMove(0, 0, player.vz);
tryMove(0, player.vy, 0);
// Jump
if (keys[' '] && player.onGround) {
player.vy = 0.4;
player.onGround = false;
}
camera.position.set(player.x, player.y + player.height, player.z);
}
function tryMove(dx, dy, dz) {
const newX = player.x + dx;
const newY = player.y + dy;
const newZ = player.z + dz;
if (!checkCollision(newX, newY, newZ)) {
player.x = newX;
player.y = newY;
player.z = newZ;
player.onGround = dy === 0 && player.vy <= 0;
} else if (dy < 0) {
player.vy = 0;
player.onGround = true;
} else if (dy > 0) {
player.vy = 0;
}
}
function checkCollision(x, y, z) {
const minX = Math.floor(x - player.width/2);
const maxX = Math.floor(x + player.width/2);
const minY = Math.floor(y);
const maxY = Math.floor(y + player.height);
const minZ = Math.floor(z - player.width/2);
const maxZ = Math.floor(z + player.width/2);
for (let bx = minX; bx <= maxX; bx++) {
for (let by = minY; by <= maxY; by++) {
for (let bz = minZ; bz <= maxZ; bz++) {
const key = `${bx},${by},${bz}`;
if (world[key] && world[key].type !== 'water' && world[key].type !== 'leaves') {
return true;
}
}
}
}
return false;
}
function updateChunks() {
const cx = Math.floor(player.x / CHUNK_SIZE);
const cz = Math.floor(player.z / CHUNK_SIZE);
for (let dx = -RENDER_DISTANCE; dx <= RENDER_DISTANCE; dx++) {
for (let dz = -RENDER_DISTANCE; dz <= RENDER_DISTANCE; dz++) {
generateChunk(cx + dx, cz + dz);
}
}
}
function updateHighlight() {
raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);
const intersects = raycaster.intersectObjects(scene.children, true);
for (let hit of intersects) {
if (hit.object !== highlightMesh && hit.object.geometry) {
highlightMesh.position.copy(hit.object.position);
highlightMesh.visible = true;
return;
}
}
highlightMesh.visible = false;
}
// Input Handling
function onKeyDown(e) {
keys[e.key.toLowerCase()] = true;
if (e.key === 'Escape') {
if (isLocked) {
pauseGame();
}
}
if (e.key >= '1' && e.key <= '9') {
selectSlot(parseInt(e.key) - 1);
}
}
function onKeyUp(e) {
keys[e.key.toLowerCase()] = false;
}
function onMouseDown(e) {
if (!isLocked || isPaused) return;
raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const hit = intersects[0];
if (hit.object === highlightMesh) return;
const pos = hit.object.position;
if (e.button === 0) { // Break
const key = `${pos.x/BLOCK_SIZE},${pos.y/BLOCK_SIZE},${pos.z/BLOCK_SIZE}`;
if (world[key]) {
hit.object.parent.remove(hit.object);
delete world[key];
createParticles(pos, hit.object.material.color);
}
} else if (e.button === 2) { // Place
const normal = hit.face.normal;
const newX = pos.x + normal.x * BLOCK_SIZE;
const newY = pos.y + normal.y * BLOCK_SIZE;
const newZ = pos.z + normal.z * BLOCK_SIZE;
// Don't place inside player
const px = Math.floor(player.x);
const py = Math.floor(player.y);
const pz = Math.floor(player.z);
const nx = newX / BLOCK_SIZE;
const ny = newY / BLOCK_SIZE;
const nz = newZ / BLOCK_SIZE;
if (Math.abs(px - nx) < 1 && Math.abs(py - ny) < 2 && Math.abs(pz - nz) < 1) {
return;
}
const cx = Math.floor(nx / CHUNK_SIZE);
const cz = Math.floor(nz / CHUNK_SIZE);
const chunkKey = `${cx},${cz}`;
if (chunks[chunkKey]) {
createBlock(nx, ny, nz, selectedBlock, chunks[chunkKey]);
}
}
}
}
function createParticles(pos, color) {
for (let i = 0; i < 8; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = (window.innerWidth / 2) + 'px';
particle.style.top = (window.innerHeight / 2) + 'px';
particle.style.background = '#' + color.getHexString();
document.body.appendChild(particle);
const angle = (Math.PI * 2 * i) / 8;
const velocity = 5;
let x = 0, y = 0;
let opacity = 1;
const animate = () => {
x += Math.cos(angle) * velocity;
y += Math.sin(angle) * velocity + 2;
opacity -= 0.02;
particle.style.transform = `translate(${x}px, ${y}px)`;
particle.style.opacity = opacity;
if (opacity > 0) {
requestAnimationFrame(animate);
} else {
particle.remove();
}
};
requestAnimationFrame(animate);
}
}
function onMouseMove(e) {
if (!isLocked) return;
camera.rotation.y -= e.movementX * 0.002;
camera.rotation.x -= e.movementY * 0.002;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
}
function onPointerLockChange() {
isLocked = document.pointerLockElement === document.body;
if (!isLocked && !isPaused) {
pauseGame();
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Game Flow
async function startGame() {
document.getElementById('main-menu').style.display = 'none';
document.getElementById('loading-screen').style.display = 'flex';
init();
// Simulate loading
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
setTimeout(() => {
document.getElementById('loading-screen').style.display = 'none';
document.body.requestPointerLock();
isPaused = false;
document.getElementById('crosshair').style.opacity = 1;
document.getElementById('hotbar').style.opacity = 1;
document.getElementById('controls-hint').style.opacity = 1;
document.getElementById('health-bar').style.opacity = 1;
animate();
}, 500);
}
document.getElementById('loading-progress').style.width = progress + '%';
}, 200);
}
function pauseGame() {
isPaused = true;
document.exitPointerLock();
document.getElementById('pause-menu').classList.remove('hidden');
document.getElementById('crosshair').style.opacity = 0;
}
function resumeGame() {
isPaused = false;
document.body.requestPointerLock();
document.getElementById('pause-menu').classList.add('hidden');
document.getElementById('crosshair').style.opacity = 1;
}
function returnToMenu() {
location.reload();
}
// Prevent context menu
document.addEventListener('contextmenu', e => e.preventDefault());
</script>
</body>
</html>