// FULL GAME SCRIPT WITH FIXES APPLIED function startGame() { document.body.innerHTML = ''; let s = document.createElement('script'); s.src = 'https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js'; s.onload = () => { /* --------------------- SCENE ---------------------- */ const scene = new THREE.Scene(), camHolder = new THREE.Object3D(); const cam = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000); cam.position.y = 2; camHolder.add(cam); scene.add(camHolder); const r = new THREE.WebGLRenderer(); r.setSize(innerWidth, innerHeight); document.body.appendChild(r.domElement); /* --------------------- SKY ---------------------- */ scene.add(new THREE.Mesh(new THREE.SphereGeometry(500,32,32), new THREE.MeshBasicMaterial({color:0x87CEEB, side: THREE.BackSide}))); /* --------------------- LIGHT ---------------------- */ const light = new THREE.DirectionalLight(0xffffff,1); light.position.set(10,20,10); scene.add(light); /* --------------------- GROUND ---------------------- */ const ground = new THREE.Mesh(new THREE.PlaneGeometry(100,100), new THREE.MeshLambertMaterial({color:0x228B22})); ground.rotation.x=-Math.PI/2; scene.add(ground); /* --------------------- VARIABLES ---------------------- */ const obs = [], bullets = [], npcs = [], medkits = [], keys = {}; let health = 100, dead=false, yaw=0, pitch=0, clock=Date.now(); let wave=1, npcsToSpawn=5; /* --------------------- OBSTACLES ---------------------- */ for(let i=0;i<10;i++){ let o=new THREE.Mesh(new THREE.BoxGeometry(3,4,1), new THREE.MeshLambertMaterial({color:0x444})); o.position.set(Math.random()*80-40,2,Math.random()*80-40); scene.add(o); obs.push(o); } /* --------------------- MEDKITS ---------------------- */ function spawnMedkit(){ let m=new THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.MeshLambertMaterial({color:0x00ff00})); m.position.set(Math.random()*80-40,0.5,Math.random()*80-40); scene.add(m); medkits.push(m); } /* --------------------- GUN ---------------------- */ const gun = new THREE.Group(); gun.add(new THREE.Mesh(new THREE.BoxGeometry(0.2,0.15,0.6), new THREE.MeshLambertMaterial({color:0x222}))); const barrel = new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,0.6,12), new THREE.MeshLambertMaterial({color:0x111})); barrel.rotation.x=Math.PI/2; barrel.position.z=-0.6; gun.add(barrel); const muzzle = new THREE.Object3D(); muzzle.position.set(0,0,-0.9); gun.add(muzzle); gun.position.set(.3,-.3,-.5); cam.add(gun); const flash = new THREE.Mesh(new THREE.SphereGeometry(.07), new THREE.MeshBasicMaterial({color:0xff0})); flash.visible=false; cam.add(flash); /* --------------------- UI ---------------------- */ function createUI(id,html=""){ const d=document.createElement("div"); d.id=id; d.style.position="absolute"; d.style.color="white"; d.style.fontSize="20px"; d.style.userSelect="none"; d.innerHTML=html; document.body.appendChild(d); return d; } const healthUI=createUI("health","❤️ Health: 100"); healthUI.style.top="10px"; healthUI.style.left="10px"; const timerUI=createUI("timer",""); timerUI.style.bottom="10px"; timerUI.style.left="10px"; const waveUI=createUI("wave",`📈 Wave: ${wave}`); waveUI.style.top="10px"; waveUI.style.right="10px"; const deathUI=createUI("death",""); deathUI.style.display="none"; /* RESTART BUTTON */ const restartBtn=document.createElement("button"); restartBtn.textContent="🔁 Restart"; restartBtn.style.position="absolute"; restartBtn.style.top="60%"; restartBtn.style.left="50%"; restartBtn.style.transform="translateX(-50%)"; restartBtn.style.fontSize="20px"; restartBtn.style.display="none"; restartBtn.onclick=()=>{document.head.innerHTML=''; startGame();}; document.body.appendChild(restartBtn); /* CROSSHAIR */ const crosshair=document.createElement("div"); crosshair.style.position="absolute"; crosshair.style.top="50%"; crosshair.style.left="50%"; crosshair.style.transform="translate(-50%,-50%)"; crosshair.style.fontSize="30px"; crosshair.style.color="white"; crosshair.style.userSelect="none"; crosshair.innerHTML="+"; document.body.appendChild(crosshair); /* --------------------- WAVE SYSTEM ---------------------- */ function spawnNPC(){ let n = new THREE.Group(); const body = new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.3,1.2,8), new THREE.MeshLambertMaterial({color:0xff0000})); body.position.y=0.6; n.add(body); const head = new THREE.Mesh(new THREE.SphereGeometry(0.25,8,8), new THREE.MeshLambertMaterial({color:0xffccaa})); head.position.y=1.6; n.add(head); const lArm = new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.8,6), new THREE.MeshLambertMaterial({color:0xff0000})); lArm.position.set(-0.35,1,0); lArm.rotation.z=Math.PI/6; n.add(lArm); const rArm = new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.8,6), new THREE.MeshLambertMaterial({color:0xff0000})); rArm.position.set(0.35,1,0); rArm.rotation.z=-Math.PI/6; n.add(rArm); n.position.set(Math.random()*50-25,0,Math.random()*50-25); n.userData={hp:100+wave*20,cool:0}; scene.add(n); npcs.push(n); } function startWave(){ waveUI.innerHTML=`📈 Wave: ${wave}`; for(let i=0;i { keys[e.key.toLowerCase()] = true; if (["Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.code)) e.preventDefault(); if (e.code === "Space" && !dead) { flash.position.copy(muzzle.position); flash.visible = true; setTimeout(() => flash.visible = false, 100); let pos = new THREE.Vector3(); muzzle.getWorldPosition(pos); let dir = new THREE.Vector3(0,0,-1).applyQuaternion(cam.getWorldQuaternion(new THREE.Quaternion())); shoot(pos, dir, "p"); } }; document.onkeyup = e => { keys[e.key.toLowerCase()] = false; if (["Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.code)) e.preventDefault(); }; /* --------------------- ANIMATE ---------------------- */ function animate(){ requestAnimationFrame(animate); if(dead) return; let now=Date.now(); let t=Math.floor((now-clock)/1000); timerUI.innerHTML=`⏱️ Time: ${t}s`; let dx=0,dz=0,speed=.2; if(keys['w']) dz-=speed; if(keys['s']) dz+=speed; if(keys['a']) dx-=speed; if(keys['d']) dx+=speed; if(keys['arrowleft']) yaw+=.03; if(keys['arrowright']) yaw-=.03; if(keys['arrowup']) pitch+=.02; if(keys['arrowdown']) pitch-=.02; pitch=Math.max(-Math.PI/2,Math.min(Math.PI/2,pitch)); cam.rotation.x=pitch; camHolder.rotation.y=yaw; camHolder.position.add(new THREE.Vector3(dx,0,dz).applyAxisAngle(new THREE.Vector3(0,1,0),yaw)); npcs.forEach(n=>{ if(n.userData.hp<=0) return; let toP=camHolder.position.clone().sub(n.position).normalize(); n.lookAt(camHolder.position.x,1,camHolder.position.z); n.position.add(toP.multiplyScalar(.02)); if(--n.userData.cool<=0){ let predicted=camHolder.position.clone().add(camHolder.getWorldDirection(new THREE.Vector3()).multiplyScalar(0.5)); let dir=predicted.clone().sub(n.position.clone().add(new THREE.Vector3(0,1,0))).normalize(); let distance=n.position.distanceTo(camHolder.position); let speed=0.5+Math.min(distance/20,1); let b=new THREE.Mesh(new THREE.SphereGeometry(.05),new THREE.MeshBasicMaterial({color:0xf0f})); b.position.copy(n.position.clone().add(new THREE.Vector3(0,1,0))); b.userData={v:dir.clone().multiplyScalar(speed),o:"n"}; scene.add(b); bullets.push(b); n.userData.cool=30+Math.floor(Math.random()*30); } }); bullets.forEach((b,i)=>{ b.position.add(b.userData.v); for(let o of obs) if(b.position.distanceTo(o.position)<1.5) return scene.remove(b),bullets.splice(i,1); if(b.userData.o=="p"){ npcs.forEach(n=>{ if(n.userData.hp>0 && b.position.distanceTo(n.position)<1.8){ n.userData.hp-=50; if(n.userData.hp<=0){scene.remove(n); npcs.splice(npcs.indexOf(n),1); if(npcs.length===0){wave++;startWave();}} scene.remove(b); bullets.splice(i,1); } }); } else { if(b.position.distanceTo(camHolder.position)<1){ health-=1; // reduced damage healthUI.innerHTML=`❤️ Health: ${health}`; scene.remove(b); bullets.splice(i,1); if(health<=0){ dead=true; deathUI.innerHTML=`💀 You Died
📈 Wave: ${wave}`; deathUI.style.display="block"; restartBtn.style.display="block"; } } } }); medkits.forEach((m,i)=>{ if(camHolder.position.distanceTo(m.position)<1.5){ health=Math.min(health+30,100); healthUI.innerHTML=`❤️ Health: ${health}`; scene.remove(m); medkits.splice(i,1); } }); r.render(scene,cam); } animate(); }; document.head.appendChild(s); } startGame();