gta / index.html
Robetx's picture
Add 3 files
e54c1e0 verified
Raw History Blame Contribute Delete
50.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Urban Sandbox</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://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> <style> body { margin: 0; overflow: hidden; font-family: 'Arial', sans-serif; } #container { position: relative; width: 100vw; height: 100vh; } #ui { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } #crosshair { position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; transform: translate(-50%, -50%); background-color: rgba(255, 255, 255, 0.7); border-radius: 50%; } #start-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)), url('https://images.unsplash.com/photo-1564501049412-61c2a3083791?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80'); background-size: cover; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; z-index: 100; transition: all 0.5s ease; } #pause-menu { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); display: none; flex-direction: column; justify-content: center; align-items: center; color: white; z-index: 100; pointer-events: auto; } #map { position: absolute; bottom: 20px; right: 20px; width: 200px; height: 200px; background-color: rgba(0, 0, 0, 0.5); border: 2px solid #fff; border-radius: 5px; overflow: hidden; } #player-marker { position: absolute; width: 6px; height: 6px; background-color: red; border-radius: 50%; transform: translate(-50%, -50%); } #stats { position: absolute; top: 20px; left: 20px; color: white; background-color: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px; } #stamina-bar { position: absolute; top: 60px; left: 20px; width: 100px; height: 10px; background-color: rgba(0, 0, 0, 0.5); border-radius: 5px; overflow: hidden; } #stamina-fill { height: 100%; width: 100%; background: linear-gradient(90deg, #00ff00, #ffff00, #ff0000); transition: width 0.2s; } .btn { pointer-events: auto; background: linear-gradient(135deg, #ff8a00, #e52e71); color: white; border: none; padding: 15px 30px; margin: 10px; font-size: 18px; border-radius: 5px; cursor: pointer; transition: all 0.3s; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); min-width: 200px; text-align: center; } .btn:hover { transform: translateY(-2px); box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15); } .btn:active { transform: translateY(0); } #mission { position: absolute; bottom: 20px; left: 20px; color: white; background-color: rgba(0, 0, 0, 0.7); padding: 15px; border-radius: 5px; max-width: 300px; } .menu-title { font-size: 4rem; font-weight: bold; margin-bottom: 2rem; text-shadow: 0 0 10px rgba(255,255,255,0.5); background: linear-gradient(to right, #ff8a00, #e52e71); -webkit-background-clip: text; background-clip: text; color: transparent; } .menu-subtitle { font-size: 1.5rem; margin-bottom: 3rem; opacity: 0.8; } .menu-section { display: flex; flex-direction: column; align-items: center; margin-bottom: 2rem; } .menu-options { display: flex; flex-direction: column; align-items: center; } .footstep { position: absolute; width: 20px; height: 20px; background-color: rgba(255,255,255,0.3); border-radius: 50%; pointer-events: none; opacity: 0; transition: opacity 0.5s; } .settings-option { display: flex; justify-content: space-between; width: 300px; margin: 10px 0; align-items: center; } .settings-slider { width: 150px; } .key-bind { display: inline-block; background-color: rgba(0,0,0,0.5); padding: 5px 10px; border-radius: 4px; margin: 0 5px; font-family: monospace; } </style> </head> <body> <div id="container"> <div id="ui"> <div id="crosshair"></div> <div id="stats"> <div>Health: <span id="health">100</span>%</div> <div>Money: $<span id="money">500</span></div> <div>Wanted: <span id="wanted">0</span>★</div> </div> <div id="stamina-bar"> <div id="stamina-fill"></div> </div> <div id="map"> <div id="player-marker"></div> </div> <div id="mission"> <h3 class="font-bold text-lg mb-2">Current Mission</h3> <p>Explore the city and find the red marker to start your first job.</p> </div> </div> <!-- Start Menu --> <div id="start-screen"> <h1 class="menu-title">URBAN SANDBOX</h1> <p class="menu-subtitle">The Ultimate City Experience</p> <div class="menu-section"> <div class="menu-options"> <button id="start-btn" class="btn">START GAME</button> <button id="settings-btn" class="btn">SETTINGS</button> <button id="quit-btn" class="btn">QUIT</button> </div> </div> <div id="settings-menu" style="display: none;"> <div class="settings-option"> <span>Master Volume</span> <input type="range" class="settings-slider" min="0" max="100" value="100"> </div> <div class="settings-option"> <span>Mouse Sensitivity</span> <input type="range" class="settings-slider" min="50" max="200" value="100"> </div> <div class="settings-option"> <span>Graphics Quality</span> <select class="bg-gray-800 text-white p-2 rounded"> <option>Low</option> <option selected>Medium</option> <option>High</option> <option>Ultra</option> </select> </div> <button id="back-btn" class="btn mt-4">BACK</button> </div> <div class="mt-8 text-gray-400 text-center"> <p class="mb-2">Controls:</p> <p><span class="key-bind">W</span><span class="key-bind">A</span><span class="key-bind">S</span><span class="key-bind">D</span> - Move | <span class="key-bind">SPACE</span> - Jump</p> <p><span class="key-bind">SHIFT</span> - Sprint | <span class="key-bind">CTRL</span> - Crouch</p> <p>Mouse - Look | <span class="key-bind">LMB</span> - Shoot | <span class="key-bind">ESC</span> - Pause</p> <p><span class="key-bind">E</span> - Enter Vehicle | <span class="key-bind">F</span> - Flashlight</p> </div> </div> <!-- Pause Menu --> <div id="pause-menu"> <h1 class="menu-title">GAME PAUSED</h1> <div class="menu-section"> <div class="menu-options"> <button id="resume-btn" class="btn">RESUME</button> <button id="settings-btn-pause" class="btn">SETTINGS</button> <button id="quit-btn-pause" class="btn">QUIT TO MENU</button> </div> </div> <div id="settings-menu-pause" style="display: none;"> <div class="settings-option"> <span>Master Volume</span> <input type="range" class="settings-slider" min="0" max="100" value="100"> </div> <div class="settings-option"> <span>Mouse Sensitivity</span> <input type="range" class="settings-slider" min="50" max="200" value="100"> </div> <div class="settings-option"> <span>Graphics Quality</span> <select class="bg-gray-800 text-white p-2 rounded"> <option>Low</option> <option selected>Medium</option> <option>High</option> <option>Ultra</option> </select> </div> <button id="back-btn-pause" class="btn mt-4">BACK</button> </div> </div> </div> <script> // Game variables let scene, camera, renderer, controls; let objects = []; let vehicles = []; let player = { health: 100, money: 500, wanted: 0, stamina: 100, position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 }, isJumping: false, inVehicle: false, flashlight: false, isMoving: false, isRunning: false, isCrouching: false, lastStepTime: 0, lastStaminaUpdate: 0, staminaRegenTimeout: null }; let clock = new THREE.Clock(); let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let canJump = true; let isPaused = false; let footstepInterval; let footstepSounds = []; let mouseSensitivity = 1.0; let originalCameraHeight = 1.6; let crouchCameraHeight = 0.8; // Initialize game function init() { // Create scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x87CEEB); // Sky blue // Add fog scene.fog = new THREE.FogExp2(0x87CEEB, 0.002); // Create camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.y = originalCameraHeight; // Eye level // Create renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.getElementById('container').prepend(renderer.domElement); // Add controls controls = new THREE.PointerLockControls(camera, document.body); // Add event listeners document.addEventListener('keydown', onKeyDown, false); document.addEventListener('keyup', onKeyUp, false); document.addEventListener('click', onMouseClick, false); document.getElementById('start-btn').addEventListener('click', startGame); document.getElementById('settings-btn').addEventListener('click', showSettings); document.getElementById('back-btn').addEventListener('click', hideSettings); document.getElementById('quit-btn').addEventListener('click', quitGame); document.getElementById('resume-btn').addEventListener('click', resumeGame); document.getElementById('settings-btn-pause').addEventListener('click', showSettingsPause); document.getElementById('back-btn-pause').addEventListener('click', hideSettingsPause); document.getElementById('quit-btn-pause').addEventListener('click', quitToMenu); // Initialize footstep sounds (would be actual audio files in a real game) for (let i = 0; i < 4; i++) { footstepSounds.push({ play: function() { console.log("Footstep sound " + i); } }); } // Create world createWorld(); // Add ambient light const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); // Add directional light (sun) const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(100, 100, 50); directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; directionalLight.shadow.camera.near = 0.5; directionalLight.shadow.camera.far = 500; directionalLight.shadow.camera.left = -50; directionalLight.shadow.camera.right = 50; directionalLight.shadow.camera.top = 50; directionalLight.shadow.camera.bottom = -50; scene.add(directionalLight); // Add flashlight flashlight = new THREE.SpotLight(0xffffff, 1, 100, Math.PI / 4, 0.5); flashlight.position.set(0, 1, 0); flashlight.target.position.set(0, 0, -1); flashlight.castShadow = true; flashlight.shadow.mapSize.width = 1024; flashlight.shadow.mapSize.height = 1024; flashlight.visible = false; scene.add(flashlight); scene.add(flashlight.target); // Start animation loop animate(); } // Create game world (same as before) function createWorld() { // Ground const groundGeometry = new THREE.PlaneGeometry(1000, 1000); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x3a5f0b, roughness: 0.8 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); objects.push(ground); // Roads createRoads(); // Buildings createBuildings(); // Vehicles createVehicles(); // Mission marker createMissionMarker(); } function createRoads() { const roadMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 }); const lineMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 }); // Main roads for (let i = -400; i <= 400; i += 100) { // Horizontal roads const roadH = new THREE.Mesh( new THREE.BoxGeometry(800, 1, 20), roadMaterial ); roadH.position.set(0, 0.1, i); roadH.receiveShadow = true; scene.add(roadH); objects.push(roadH); // Road lines for (let j = -390; j <= 390; j += 30) { const lineH = new THREE.Mesh( new THREE.BoxGeometry(10, 1.1, 1), lineMaterial ); lineH.position.set(j, 0.2, i); scene.add(lineH); objects.push(lineH); } // Vertical roads if (i !== 0) { const roadV = new THREE.Mesh( new THREE.BoxGeometry(20, 1, 800), roadMaterial ); roadV.position.set(i, 0.1, 0); roadV.receiveShadow = true; scene.add(roadV); objects.push(roadV); // Road lines for (let j = -390; j <= 390; j += 30) { const lineV = new THREE.Mesh( new THREE.BoxGeometry(1, 1.1, 10), lineMaterial ); lineV.position.set(i, 0.2, j); scene.add(lineV); objects.push(lineV); } } } // Sidewalks const sidewalkMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc }); for (let i = -450; i <= 450; i += 100) { // Horizontal sidewalks const sidewalkH = new THREE.Mesh( new THREE.BoxGeometry(900, 1, 10), sidewalkMaterial ); sidewalkH.position.set(0, 0.1, i + (i < 0 ? -50 : 50)); sidewalkH.receiveShadow = true; scene.add(sidewalkH); objects.push(sidewalkH); // Vertical sidewalks if (i !== 0) { const sidewalkV = new THREE.Mesh( new THREE.BoxGeometry(10, 1, 900), sidewalkMaterial ); sidewalkV.position.set(i + (i < 0 ? -50 : 50), 0.1, 0); sidewalkV.receiveShadow = true; scene.add(sidewalkV); objects.push(sidewalkV); } } } function createBuildings() { const buildingColors = [ 0x8B4513, 0xA0522D, 0xCD853F, 0xD2691E, 0xB8860B, 0xDAA520, 0x808000, 0x556B2F, 0x6B8E23, 0x2E8B57 ]; // Create buildings in a grid pattern for (let x = -400; x <= 400; x += 120) { for (let z = -400; z <= 400; z += 120) { // Skip roads if (Math.abs(x) % 100 === 0 || Math.abs(z) % 100 === 0) continue; const height = Math.random() * 30 + 10; const width = Math.random() * 40 + 20; const depth = Math.random() * 40 + 20; const building = new THREE.Mesh( new THREE.BoxGeometry(width, height, depth), new THREE.MeshStandardMaterial({ color: buildingColors[Math.floor(Math.random() * buildingColors.length)], roughness: 0.7 }) ); building.position.set( x + (Math.random() * 20 - 10), height / 2, z + (Math.random() * 20 - 10) ); building.castShadow = true; building.receiveShadow = true; scene.add(building); objects.push(building); // Add windows if (height > 15) { const windowColor = Math.random() > 0.5 ? 0xADD8E6 : 0xF5F5DC; const windowMaterial = new THREE.MeshStandardMaterial({ color: windowColor, emissive: windowColor, emissiveIntensity: Math.random() > 0.7 ? 0.3 : 0 }); for (let y = 5; y < height - 2; y += 5) { for (let wx = -width/2 + 5; wx < width/2 - 5; wx += 10) { for (let wz = -depth/2 + 5; wz < depth/2 - 5; wz += 10) { const window = new THREE.Mesh( new THREE.BoxGeometry(4, 3, 0.1), windowMaterial ); window.position.set( building.position.x + wx, building.position.y - height/2 + y, building.position.z + wz ); scene.add(window); objects.push(window); } } } } } } // Add some special buildings createSpecialBuildings(); } function createSpecialBuildings() { // Police station const policeStation = new THREE.Mesh( new THREE.BoxGeometry(80, 30, 60), new THREE.MeshStandardMaterial({ color: 0x4682B4 }) ); policeStation.position.set(-200, 15, 200); scene.add(policeStation); objects.push(policeStation); // Add police sign const signGeometry = new THREE.BoxGeometry(60, 5, 2); const signMaterial = new THREE.MeshStandardMaterial({ color: 0x0000FF }); const policeSign = new THREE.Mesh(signGeometry, signMaterial); policeSign.position.set(-200, 35, 200); scene.add(policeSign); objects.push(policeSign); // Hospital const hospital = new THREE.Mesh( new THREE.BoxGeometry(70, 35, 70), new THREE.MeshStandardMaterial({ color: 0xFFFFFF }) ); hospital.position.set(200, 17.5, -200); scene.add(hospital); objects.push(hospital); // Add red cross const crossGeometry1 = new THREE.BoxGeometry(50, 5, 10); const crossGeometry2 = new THREE.BoxGeometry(10, 5, 50); const crossMaterial = new THREE.MeshStandardMaterial({ color: 0xFF0000 }); const cross1 = new THREE.Mesh(crossGeometry1, crossMaterial); const cross2 = new THREE.Mesh(crossGeometry2, crossMaterial); cross1.position.set(200, 40, -200); cross2.position.set(200, 40, -200); scene.add(cross1); scene.add(cross2); objects.push(cross1, cross2); // Bank const bank = new THREE.Mesh( new THREE.BoxGeometry(60, 25, 60), new THREE.MeshStandardMaterial({ color: 0xDAA520 }) ); bank.position.set(-200, 12.5, -200); scene.add(bank); objects.push(bank); // Add columns for (let i = -1; i <= 1; i += 2) { for (let j = -1; j <= 1; j += 2) { const column = new THREE.Mesh( new THREE.CylinderGeometry(2, 2, 20, 8), new THREE.MeshStandardMaterial({ color: 0xFFFFFF }) ); column.position.set( -200 + i * 30, 10, -200 + j * 30 ); scene.add(column); objects.push(column); } } } function createVehicles() { const carColors = [ 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 0x000000, 0x888888, 0xFFA500 ]; // Create parked cars for (let i = 0; i < 20; i++) { const x = Math.random() * 800 - 400; const z = Math.random() * 800 - 400; // Make sure cars are on roads const roadX = Math.round(x / 100) * 100; const roadZ = Math.round(z / 100) * 100; if (Math.abs(roadX) % 100 === 0 && Math.abs(roadZ) % 100 === 0) continue; const car = createCar( roadX + (Math.random() * 30 - 15), 0.5, roadZ + (Math.random() * 30 - 15), carColors[Math.floor(Math.random() * carColors.length)] ); // Random rotation car.rotation.y = Math.random() * Math.PI * 2; scene.add(car); vehicles.push(car); } // Add some emergency vehicles const policeCar = createCar(-180, 0.5, 180, 0x0000FF); policeCar.rotation.y = Math.PI / 2; scene.add(policeCar); vehicles.push(policeCar); const ambulance = createCar(180, 0.5, -180, 0xFFFFFF); ambulance.rotation.y = Math.PI; scene.add(ambulance); vehicles.push(ambulance); } function createCar(x, y, z, color) { const car = new THREE.Group(); // Car body const bodyGeometry = new THREE.BoxGeometry(5, 2, 10); const bodyMaterial = new THREE.MeshStandardMaterial({ color: color }); const body = new THREE.Mesh(bodyGeometry, bodyMaterial); body.position.y = y + 1; body.castShadow = true; car.add(body); // Car roof const roofGeometry = new THREE.BoxGeometry(4, 1, 8); const roofMaterial = new THREE.MeshStandardMaterial({ color: color }); const roof = new THREE.Mesh(roofGeometry, roofMaterial); roof.position.y = y + 2.5; car.add(roof); // Windows const windowMaterial = new THREE.MeshStandardMaterial({ color: 0x87CEEB, transparent: true, opacity: 0.7 }); // Front window const frontWindow = new THREE.Mesh( new THREE.BoxGeometry(4, 1, 0.1), windowMaterial ); frontWindow.position.set(0, y + 2, z + 4.5); car.add(frontWindow); // Rear window const rearWindow = new THREE.Mesh( new THREE.BoxGeometry(4, 1, 0.1), windowMaterial ); rearWindow.position.set(0, y + 2, z - 4.5); car.add(rearWindow); // Side windows const sideWindowL = new THREE.Mesh( new THREE.BoxGeometry(0.1, 1, 8), windowMaterial ); sideWindowL.position.set(-2, y + 2, 0); car.add(sideWindowL); const sideWindowR = new THREE.Mesh( new THREE.BoxGeometry(0.1, 1, 8), windowMaterial ); sideWindowR.position.set(2, y + 2, 0); car.add(sideWindowR); // Wheels const wheelGeometry = new THREE.CylinderGeometry(1, 1, 0.5, 16); const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 }); for (let i = -1; i <= 1; i += 2) { for (let j = -1; j <= 1; j += 2) { const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial); wheel.rotation.z = Math.PI / 2; wheel.position.set( i * 2.5, y + 0.5, j * 3.5 ); car.add(wheel); } } car.position.set(x, y, z); return car; } function createMissionMarker() { const markerGeometry = new THREE.CylinderGeometry(5, 5, 2, 32); const markerMaterial = new THREE.MeshStandardMaterial({ color: 0xFF0000, emissive: 0xFF0000, emissiveIntensity: 0.5 }); const marker = new THREE.Mesh(markerGeometry, markerMaterial); marker.position.set(150, 1, 150); scene.add(marker); objects.push(marker); // Add pulsing animation let pulseDirection = 1; function pulse() { if (marker.scale.x >= 1.2) pulseDirection = -1; if (marker.scale.x <= 0.8) pulseDirection = 1; marker.scale.x += 0.01 * pulseDirection; marker.scale.y += 0.01 * pulseDirection; marker.scale.z += 0.01 * pulseDirection; requestAnimationFrame(pulse); } pulse(); } // Start game function function startGame() { document.getElementById('start-screen').style.display = 'none'; controls.lock(); // Start footstep sounds when moving footstepInterval = setInterval(() => { if (player.isMoving && canJump) { const randomSound = footstepSounds[Math.floor(Math.random() * footstepSounds.length)]; randomSound.play(); // Create visual footstep effect createFootstep(); } }, player.isRunning ? 300 : (player.isCrouching ? 700 : 500)); } function createFootstep() { const footstep = document.createElement('div'); footstep.className = 'footstep'; // Position at player's feet const playerPos = controls.getObject().position; const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(controls.getObject().quaternion); // Offset slightly behind and to the side const offsetRight = new THREE.Vector3().crossVectors( forward, new THREE.Vector3(0, 1, 0) ).normalize().multiplyScalar(0.2); const footPos = new THREE.Vector3().copy(playerPos) .add(forward.clone().multiplyScalar(-0.2)) .add(offsetRight); // Convert to screen coordinates const vector = footPos.clone().project(camera); vector.x = (vector.x + 1) / 2 * window.innerWidth; vector.y = -(vector.y - 1) / 2 * window.innerHeight; footstep.style.left = `${vector.x}px`; footstep.style.top = `${vector.y}px`; footstep.style.opacity = '1'; document.body.appendChild(footstep); // Fade out and remove setTimeout(() => { footstep.style.opacity = '0'; setTimeout(() => { document.body.removeChild(footstep); }, 500); }, 1000); } function showSettings() { document.querySelector('.menu-options').style.display = 'none'; document.getElementById('settings-menu').style.display = 'block'; } function hideSettings() { document.querySelector('.menu-options').style.display = 'flex'; document.getElementById('settings-menu').style.display = 'none'; } function showSettingsPause() { document.querySelector('.menu-options').style.display = 'none'; document.getElementById('settings-menu-pause').style.display = 'block'; } function hideSettingsPause() { document.querySelector('.menu-options').style.display = 'flex'; document.getElementById('settings-menu-pause').style.display = 'none'; } function quitGame() { window.close(); } function quitToMenu() { isPaused = false; document.getElementById('pause-menu').style.display = 'none'; document.getElementById('start-screen').style.display = 'flex'; controls.unlock(); clearInterval(footstepInterval); } function pauseGame() { isPaused = true; document.getElementById('pause-menu').style.display = 'flex'; controls.unlock(); } function resumeGame() { isPaused = false; document.getElementById('pause-menu').style.display = 'none'; controls.lock(); } function onMouseClick(event) { if (controls.isLocked && !isPaused) { // Shooting mechanic console.log("Bang!"); // Add muzzle flash effect const flash = document.createElement('div'); flash.style.position = 'absolute'; flash.style.top = '50%'; flash.style.left = '50%'; flash.style.width = '50px'; flash.style.height = '50px'; flash.style.borderRadius = '50%'; flash.style.backgroundColor = 'rgba(255, 200, 0, 0.7)'; flash.style.transform = 'translate(-50%, -50%)'; flash.style.pointerEvents = 'none'; document.body.appendChild(flash); setTimeout(() => { flash.style.opacity = '0'; flash.style.transition = 'opacity 0.1s'; setTimeout(() => { document.body.removeChild(flash); }, 100); }, 50); } } // Handle keyboard input function onKeyDown(event) { if (isPaused && event.code !== 'Escape') return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; player.isMoving = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; player.isMoving = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; player.isMoving = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; player.isMoving = true; break; case 'Space': if (canJump && !player.isCrouching) { player.velocity.y = 15; canJump = false; player.isJumping = true; // Jump sound console.log("Jump sound"); } break; case 'ShiftLeft': case 'ShiftRight': if (!player.isCrouching && player.stamina > 0) { player.isRunning = true; } break; case 'ControlLeft': case 'ControlRight': toggleCrouch(true); break; case 'KeyE': tryEnterVehicle(); break; case 'KeyF': toggleFlashlight(); break; case 'Escape': if (controls.isLocked) { pauseGame(); } else if (!isPaused) { controls.lock(); } break; } } function onKeyUp(event) { switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; player.isMoving = moveBackward || moveLeft || moveRight; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; player.isMoving = moveForward || moveBackward || moveRight; break; case 'ArrowDown': case 'KeyS': moveBackward = false; player.isMoving = moveForward || moveLeft || moveRight; break; case 'ArrowRight': case 'KeyD': moveRight = false; player.isMoving = moveForward || moveBackward || moveLeft; break; case 'ShiftLeft': case 'ShiftRight': player.isRunning = false; startStaminaRegen(); break; case 'ControlLeft': case 'ControlRight': toggleCrouch(false); break; } } function toggleCrouch(crouch) { player.isCrouching = crouch; if (crouch) { // If running, stop running when crouching if (player.isRunning) { player.isRunning = false; startStaminaRegen(); } // Lower camera camera.position.y = crouchCameraHeight; } else { // Raise camera if not obstructed const ray = new THREE.Raycaster( controls.getObject().position, new THREE.Vector3(0, 1, 0), 0, originalCameraHeight - crouchCameraHeight ); const intersects = ray.intersectObjects(objects); if (intersects.length === 0) { camera.position.y = originalCameraHeight; } else { // Can't stand up, stay crouched player.isCrouching = true; } } } function startStaminaRegen() { // Clear any existing timeout if (player.staminaRegenTimeout) { clearTimeout(player.staminaRegenTimeout); } // Start regenerating after a short delay player.staminaRegenTimeout = setTimeout(() => { const staminaRegenInterval = setInterval(() => { if (player.stamina < 100 && !player.isRunning) { player.stamina = Math.min(100, player.stamina + 0.5); updateStaminaBar(); } else { clearInterval(staminaRegenInterval); } }, 100); }, 2000); // 2 second delay before regen starts } function tryEnterVehicle() { // Simple vehicle entry logic const playerPos = controls.getObject().position; for (const vehicle of vehicles) { const distance = playerPos.distanceTo(vehicle.position); if (distance < 5) { player.inVehicle = !player.inVehicle; if (player.inVehicle) { // Attach camera to vehicle vehicle.add(camera); camera.position.set(0, 3, -5); camera.lookAt(0, 2, 10); // Engine sound console.log("Engine start sound"); } else { // Detach camera from vehicle scene.add(camera); camera.position.copy(vehicle.position); camera.position.y += 1.6; camera.rotation.copy(vehicle.rotation); // Door close sound console.log("Door close sound"); } break; } } } function toggleFlashlight() { player.flashlight = !player.flashlight; flashlight.visible = player.flashlight; if (player.flashlight) { // Attach flashlight to camera camera.add(flashlight); flashlight.position.set(0, 0, 0); // Flashlight sound console.log("Flashlight on sound"); } else { // Detach flashlight from camera scene.add(flashlight); // Flashlight sound console.log("Flashlight off sound"); } } function updateStaminaBar() { document.getElementById('stamina-fill').style.width = `${player.stamina}%`; } // Animation loop function animate() { requestAnimationFrame(animate); if (isPaused) return; const delta = clock.getDelta(); if (controls.isLocked) { // Player movement let speed; if (player.isRunning && player.stamina > 0) { speed = 40; // Consume stamina player.stamina = Math.max(0, player.stamina - 10 * delta); updateStaminaBar(); // If stamina runs out, stop running if (player.stamina <= 0) { player.isRunning = false; startStaminaRegen(); } } else if (player.isCrouching) { speed = 10; } else { speed = 20; } // Smooth acceleration/deceleration const acceleration = player.isRunning ? 15.0 : 10.0; const deceleration = player.isRunning ? 12.0 : 8.0; player.velocity.x -= player.velocity.x * deceleration * delta; player.velocity.z -= player.velocity.z * deceleration * delta; if (moveForward) player.velocity.z -= speed * delta * acceleration; if (moveBackward) player.velocity.z += speed * delta * acceleration; if (moveLeft) player.velocity.x -= speed * delta * acceleration; if (moveRight) player.velocity.x += speed * delta * acceleration; // Limit maximum speed const maxSpeed = player.isRunning ? 30 : 20; const currentSpeed = Math.sqrt(player.velocity.x * player.velocity.x + player.velocity.z * player.velocity.z); if (currentSpeed > maxSpeed) { player.velocity.x = (player.velocity.x / currentSpeed) * maxSpeed; player.velocity.z = (player.velocity.z / currentSpeed) * maxSpeed; } // Head bobbing effect when moving if (player.isMoving && canJump && !player.isCrouching) { const time = performance.now() * 0.001; const bobSpeed = player.isRunning ? 0.002 : 0.0015; const bobIntensity = player.isRunning ? 0.1 : 0.05; camera.position.y = originalCameraHeight + Math.sin(time * 10 * bobSpeed) * bobIntensity; } else if (!player.isCrouching) { camera.position.y = originalCameraHeight; } if (!player.inVehicle) { controls.moveRight(-player.velocity.x * delta); controls.moveForward(-player.velocity.z * delta); // Improved jumping physics with air control const airControlFactor = canJump ? 1.0 : 0.2; player.velocity.x *= airControlFactor; player.velocity.z *= airControlFactor; // Gravity with more realistic feel player.velocity.y -= 30.0 * delta; // Increased gravity for better feel controls.getObject().position.y += player.velocity.y * delta; // Check if player is on ground if (controls.getObject().position.y < (player.isCrouching ? crouchCameraHeight : originalCameraHeight)) { controls.getObject().position.y = player.isCrouching ? crouchCameraHeight : originalCameraHeight; // Landing effect if (player.isJumping) { player.isJumping = false; // Landing sound console.log("Landing sound"); // Small camera shake on landing camera.position.y -= 0.1; setTimeout(() => { camera.position.y += 0.1; }, 100); } player.velocity.y = 0; canJump = true; } } // Update player position for UI player.position = { x: controls.getObject().position.x, y: controls.getObject().position.y, z: controls.getObject().position.z }; // Update player rotation player.rotation = { x: controls.getObject().rotation.x, y: controls.getObject().rotation.y, z: controls.getObject().rotation.z }; // Update UI updateUI(); } renderer.render(scene, camera); } function updateUI() { // Update stats document.getElementById('health').textContent = player.health; document.getElementById('money').textContent = player.money; document.getElementById('wanted').textContent = player.wanted; // Update minimap player marker const map = document.getElementById('map'); const marker = document.getElementById('player-marker'); // Convert world coordinates to minimap coordinates const mapX = (player.position.x / 1000) * map.offsetWidth + map.offsetWidth / 2; const mapY = (player.position.z / 1000) * map.offsetHeight + map.offsetHeight / 2; marker.style.left = `${mapX}px`; marker.style.top = `${mapY}px`; // Rotate marker to match player rotation marker.style.transform = `translate(-50%, -50%) rotate(${-player.rotation.y * (180/Math.PI)}deg)`; } // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Start the game init(); </script> <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Robetx/gta" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Urban Sandbox</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://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Arial', sans-serif;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
}
#ui {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.7);
border-radius: 50%;
}
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)), url('https://images.unsplash.com/photo-1564501049412-61c2a3083791?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80');
background-size: cover;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 100;
transition: all 0.5s ease;
}
#pause-menu {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 100;
pointer-events: auto;
}
#map {
position: absolute;
bottom: 20px;
right: 20px;
width: 200px;
height: 200px;
background-color: rgba(0, 0, 0, 0.5);
border: 2px solid #fff;
border-radius: 5px;
overflow: hidden;
}
#player-marker {
position: absolute;
width: 6px;
height: 6px;
background-color: red;
border-radius: 50%;
transform: translate(-50%, -50%);
}
#stats {
position: absolute;
top: 20px;
left: 20px;
color: white;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 5px;
}
#stamina-bar {
position: absolute;
top: 60px;