Download index.html from Robetx/gta: direct link, hf CLI and curl.
- Browser
- Download file 50.5 kB
-
https://huggingface.co/spaces/Robetx/gta/resolve/main/index.html
- Command line
-
hf download hf://spaces/Robetx/gta/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Robetx/gta/resolve/main/index.html
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>
<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;