🎮ArcadeLab

JOHN 2: THE GAME | ULTIMATE EDITION | Monster 2016

by QuantumBolt10
731 lines34.3 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
     <title>JOHN 2: THE GAME | ULTIMATE EDITION | Monster 2016</title>
     <script src="https://cdn.tailwindcss.com"></script>
     <style>
         * { margin: 0; padding: 0; box-sizing: border-box; overflow: hidden; }
         body { 
             background: #000; color: #fff; 
             font-family: 'Segoe UI', Roboto, Arial, sans-serif; 
             touch-action: none;
             user-select: none;
         }
         
         /* ========== UI DESIGN (PLAYSTATION STYLE) ========== */
         #ui-top {
             position: absolute; top: 0; left: 0; right: 0;
             padding: 16px 24px;
             background: linear-gradient(to bottom, rgba(0,0,0,0.9), rgba(0,0,0,0.4), transparent);
             z-index: 20;
             display: flex;
             justify-content: space-between;
             align-items: center;
         }
         .logo {
             color: #66CCFF; font-size: 24px; font-weight: 700; 
             text-shadow: 0 0 12px rgba(0,153,255,0.7);
             letter-spacing: 1px;
         }
         .sub-logo {
             color: #AADDFF; font-size: 14px; opacity: 0.9; margin-top: 2px;
         }
         .ui-group { display: flex; gap: 12px; align-items: center; }
         #camera-btn, #world-btn, #multiplayer-btn {
             background: linear-gradient(135deg, #0099FF, #0066CC);
             border: none; color: white; padding: 10px 18px; border-radius: 24px;
             font-weight: 600; font-size: 14px;
             box-shadow: 0 4px 20px rgba(0,153,255,0.4);
             cursor: pointer; z-index: 30;
             transition: all 0.2s ease;
             backdrop-filter: blur(8px);
             -webkit-backdrop-filter: blur(8px);
         }
         #camera-btn:hover, #world-btn:hover, #multiplayer-btn:hover {
             transform: scale(1.05);
             box-shadow: 0 6px 25px rgba(0,153,255,0.6);
         }
         #battery {
             background: rgba(0,0,0,0.6);
             border: 2px solid #00FF99;
             border-radius: 12px;
             padding: 6px 12px;
             font-size: 14px;
             font-weight: 600;
             color: #00FF99;
             box-shadow: 0 0 10px rgba(0,255,153,0.3);
         }
         #crosshair {
             position: absolute; top: 50%; left: 50%;
             width: 28px; height: 28px;
             margin: -14px 0 0 -14px;
             color: #fff; font-size: 30px;
             text-align: center; z-index: 25;
             pointer-events: none;
             text-shadow: 0 0 8px rgba(255,255,255,0.9);
             opacity: 0.9;
         }
         /* ========== JOYSTICK (PREMIUM DESIGN) ========== */
         #joystick-container {
             position: absolute; bottom: 60px; left: 60px;
             z-index: 30;
         }
         #joystick {
             width: 150px; height: 150px;
             background: radial-gradient(circle, rgba(255,255,255,0.25), rgba(255,255,255,0.08));
             border: 2px solid rgba(255,255,255,0.3);
             border-radius: 50%;
             backdrop-filter: blur(10px);
             -webkit-backdrop-filter: blur(10px);
             box-shadow: 0 10px 40px rgba(0,0,0,0.3), inset 0 0 20px rgba(255,255,255,0.1);
             touch-action: none;
             position: relative;
         }
         #joystick-knob {
             position: absolute; top: 50px; left: 50px;
             width: 50px; height: 50px;
             background: linear-gradient(135deg, #66CCFF, #0099FF);
             border-radius: 50%;
             box-shadow: 0 0 20px rgba(0,153,255,0.8), inset 0 -3px 8px rgba(0,0,0,0.25);
             pointer-events: none;
             transition: transform 0.05s ease;
         }
         #joystick-labels {
             position: absolute;
             width: 100%; text-align: center;
             color: rgba(255,255,255,0.7);
             font-size: 11px; font-weight: 500;
         }
         .label-up { top: -20px; }
         .label-down { bottom: -20px; }
         .label-left { left: -25px; top: 50%; transform: translateY(-50%); }
         .label-right { right: -25px; top: 50%; transform: translateY(-50%); }
         canvas { 
             display: block; 
             width: 100vw; height: 100vh; 
             background: linear-gradient(180deg, #87CEEB 0%, #E0F7FF 100%);
         }
         /* ========== NOTIFICATIONS ========== */
         #notification {
             position: absolute; top: 50%; left: 50%;
             transform: translate(-50%, -50%);
             background: rgba(0,0,0,0.8);
             border: 2px solid #0099FF;
             color: #fff; padding: 16px 32px;
             border-radius: 12px; font-size: 18px; font-weight: 600;
             z-index: 40; text-align: center;
             box-shadow: 0 0 30px rgba(0,153,255,0.5);
             opacity: 0; transition: opacity 0.3s ease;
             pointer-events: none;
         }
     </style>
 </head>
 <body>
     <div id="ui-top">
         <div>
             <div class="logo">JOHN 2: THE GAME</div>
             <div class="sub-logo">Created by Monster 2016 • Ultra HD 3D</div>
         </div>
         <div class="ui-group">
             <div id="battery">🔋 BATTERY: 100%</div>
             <button id="multiplayer-btn">👥 MULTIPLAYER: ON</button>
             <button id="camera-btn">👁️ FIRST PERSON</button>
             <button id="world-btn">🌍 WORLD: FIELD</button>
         </div>
     </div>
     <div id="crosshair">+</div>
     <div id="notification"></div>
     <canvas id="gameCanvas"></canvas>
     <div id="joystick-container">
         <div id="joystick">
             <div id="joystick-labels label-up">↑</div>
             <div id="joystick-labels label-down">↓</div>
             <div id="joystick-labels label-left">←</div>
             <div id="joystick-labels label-right">→</div>
             <div id="joystick-knob"></div>
         </div>
     </div>
     <!-- 3D ENGINE -->
     <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r158/three.min.js"></script>
     <script>
         // ==================================================
         // ============ CORE GAME ENGINE SETUP ==============
         // ==================================================
         const scene = new THREE.Scene();
         scene.fog = new THREE.FogExp2(0x87CEEB, 0.007); // Realistic distance fog
         
         const camera = new THREE.PerspectiveCamera(85, window.innerWidth / window.innerHeight, 0.1, 300);
         const renderer = new THREE.WebGLRenderer({ 
             canvas: document.getElementById('gameCanvas'), 
             antialias: true, 
             powerPreference: "high-performance",
             alpha: true
         });
         
         renderer.setSize(window.innerWidth, window.innerHeight);
         renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Sharp on high-res screens
         renderer.shadowMap.enabled = true;
         renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Soft shadows like real life
         renderer.toneMapping = THREE.ACESFilmicToneMapping; // Cinematic color grading
         renderer.toneMappingExposure = 1.3;
         renderer.outputEncoding = THREE.sRGBEncoding;
         // ==================================================
         // ============ GAME STATE & SETTINGS ==============
         // ==================================================
         let gameState = {
             currentWorld: 'field', // field, cyberjunk, space, ice, lava, ocean, cybercity, forest, desert, volcano, energy
             battery: 100,
             nearSecret: false,
             shipLanded: false,
             portalActive: false,
             firstPerson: true,
             multiplayer: true,
             playersOnline: 4,
             digging: false
         };
         // UI Elements
         const camBtn = document.getElementById('camera-btn');
         const worldBtn = document.getElementById('world-btn');
         const mpBtn = document.getElementById('multiplayer-btn');
         const batteryUI = document.getElementById('battery');
         const crosshair = document.getElementById('crosshair');
         const notification = document.getElementById('notification');
         // Camera Switch Logic
         camBtn.onclick = () => {
             gameState.firstPerson = !gameState.firstPerson;
             camBtn.textContent = gameState.firstPerson ? "👁️ FIRST PERSON" : "🤖 THIRD PERSON";
             showNotification(gameState.firstPerson ? "SWITCHED TO FIRST PERSON VIEW" : "SWITCHED TO THIRD PERSON VIEW");
         };
         // World Switch Logic
         worldBtn.onclick = () => {
             const worlds = ['field', 'cyberjunk', 'space', 'ice', 'lava', 'ocean', 'cybercity', 'forest', 'desert', 'volcano', 'energy'];
             const idx = (worlds.indexOf(gameState.currentWorld) + 1) % worlds.length;
             gameState.currentWorld = worlds[idx];
             worldBtn.textContent = `🌍 WORLD: ${gameState.currentWorld.toUpperCase()}`;
             loadWorld(gameState.currentWorld);
             showNotification(`ENTERING ${gameState.currentWorld.toUpperCase()} WORLD`);
         };
         // Multiplayer Toggle
         mpBtn.onclick = () => {
             gameState.multiplayer = !gameState.multiplayer;
             gameState.playersOnline = gameState.multiplayer ? 4 : 1;
             mpBtn.textContent = gameState.multiplayer ? "👥 MULTIPLAYER: ON" : "👤 SINGLE PLAYER";
             mpBtn.style.background = gameState.multiplayer ? "linear-gradient(135deg, #00FF99, #00CC66)" : "linear-gradient(135deg, #FF9900, #CC6600)";
             showNotification(gameState.multiplayer ? "MULTIPLAYER ENABLED" : "SINGLE PLAYER MODE");
         };
         // ==================================================
         // ============ PLAYER & ROBOT MODELS ==============
         // ==================================================
         const players = [
             { 
                 id: 1, name: "PLAYER 1", color: 0x0099FF, 
                 x: 0, y: 1.8, z: 0, angle: 0, 
                 speed: 0.18, turnSpeed: 0.045,
                 mesh: null, body: null, head: null, leftEye: null, rightEye: null, antennaL: null, antennaR: null
             },
             { 
                 id: 2, name: "PLAYER 2", color: 0x00CCFF, 
                 x: 6, y: 1.8, z: -4, angle: 0, 
                 speed: 0.18, turnSpeed: 0.045,
                 mesh: null
             },
             { 
                 id: 3, name: "PLAYER 3", color: 0x0066FF, 
                 x: -6, y: 1.8, z: -4, angle: 0, 
                 speed: 0.18, turnSpeed: 0.045,
                 mesh: null
             },
             { 
                 id: 4, name: "PLAYER 4", color: 0x3399FF, 
                 x: 12, y: 1.8, z: 2, angle: 0, 
                 speed: 0.18, turnSpeed: 0.045,
                 mesh: null
             }
         ];
         let currentPlayer = players[0];
         // Build High-Quality Robot Model
         function createRobot(playerData) {
             const robot = new THREE.Group();
             
             // Main Body
             const bodyGeo = new THREE.CapsuleGeometry(0.48, 1.25, 12, 24);
             const bodyMat = new THREE.MeshStandardMaterial({ 
                 color: playerData.color, 
                 metalness: 0.75, 
                 roughness: 0.15,
                 emissive: playerData.color,
                 emissiveIntensity: 0.1
             });
             const body = new THREE.Mesh(bodyGeo, bodyMat);
             body.castShadow = true;
             body.receiveShadow = true;
             robot.add(body);
             // Head
             const headGeo = new THREE.SphereGeometry(0.38, 20, 20);
             const headMat = new THREE.MeshStandardMaterial({ 
                 color: 0x66CCFF, 
                 metalness: 0.6, 
                 roughness: 0.2
             });
             const head = new THREE.Mesh(headGeo, headMat);
             head.position.y = 1.35;
             head.castShadow = true;
             robot.add(head);
             // Eyes
             const eyeGeo = new THREE.SphereGeometry(0.09, 12, 12);
             const eyeMat = new THREE.MeshStandardMaterial({ 
                 color: 0xFFFFFF, 
                 emissive: 0xFFFFFF, 
                 emissiveIntensity: 0.8
             });
             const leftEye = new THREE.Mesh(eyeGeo, eyeMat);
             leftEye.position.set(0.16, 1.4, 0.28);
             const rightEye = new THREE.Mesh(eyeGeo, eyeMat);
             rightEye.position.set(-0.16, 1.4, 0.28);
             robot.add(leftEye, rightEye);
             // Antennas (glow near secrets)
             const antennaGeo = new THREE.CylinderGeometry(0.025, 0.025, 0.6, 8);
             const antennaMat = new THREE.MeshStandardMaterial({ 
                 color: 0x444444, 
                 metalness: 0.8 
             });
             const antennaL = new THREE.Mesh(antennaGeo, antennaMat);
             antennaL.position.set(0.22, 1.6, 0);
             antennaL.rotation.z = 0.3;
             const antennaR = new THREE.Mesh(antennaGeo, antennaMat);
             antennaR.position.set(-0.22, 1.6, 0);
             antennaR.rotation.z = -0.3;
             robot.add(antennaL, antennaR);
             // Antenna Tips
             const tipGeo = new THREE.SphereGeometry(0.05, 8, 8);
             const tipMat = new THREE.MeshStandardMaterial({ 
                 color: 0x0099FF, 
                 emissive: 0x0099FF, 
                 emissiveIntensity: 0.5 
             });
             const tipL = new THREE.Mesh(tipGeo, tipMat);
             tipL.position.y = 0.3;
             antennaL.add(tipL);
             const tipR = new THREE.Mesh(tipGeo, tipMat);
             tipR.position.y = 0.3;
             antennaR.add(tipR);
             playerData.mesh = robot;
             playerData.body = body;
             playerData.head = head;
             playerData.leftEye = leftEye;
             playerData.rightEye = rightEye;
             playerData.antennaL = antennaL;
             playerData.antennaR = antennaR;
             return robot;
         }
         // Spawn all players
         players.forEach(p => {
             const robot = createRobot(p);
             robot.position.set(p.x, 0, p.z);
             scene.add(robot);
         });
         // ==================================================
         // ============ LIGHTING & ATMOSPHERE ===============
         // ==================================================
         const sunLight = new THREE.DirectionalLight(0xFFFFFF, 1.8);
         sunLight.position.set(50, 60, 30);
         sunLight.castShadow = true;
         sunLight.shadow.mapSize.width = 8192;
         sunLight.shadow.mapSize.height = 8192;
         sunLight.shadow.camera.near = 20;
         sunLight.shadow.camera.far = 200;
         sunLight.shadow.camera.left = -100;
         sunLight.shadow.camera.right = 100;
         sunLight.shadow.camera.top = 100;
         sunLight.shadow.camera.bottom = -100;
         sunLight.shadow.bias = -0.0005;
         scene.add(sunLight);
         const ambientLight = new THREE.AmbientLight(0xB0C8E0, 0.5);
         scene.add(ambientLight);
         // Skybox
         const skyGeo = new THREE.SphereGeometry(250, 32, 32);
         const skyMat = new THREE.MeshBasicMaterial({
             color: 0x87CEEB,
             side: THREE.BackSide
         });
         const sky = new THREE.Mesh(skyGeo, skyMat);
         scene.add(sky);
         // ==================================================
         // ============ WORLD GENERATOR SYSTEM =============
         // ==================================================
         let currentWorldObjects = [];
         function clearWorld() {
             currentWorldObjects.forEach(obj => scene.remove(obj));
             currentWorldObjects = [];
             gameState.shipLanded = false;
             gameState.portalActive = false;
         }
         function loadWorld(worldName) {
             clearWorld();
             
             switch(worldName) {
                 // ---------- FIELD WORLD ----------
                 case 'field':
                     scene.fog.color.set(0x87CEEB);
                     sky.material.color.set(0x87CEEB);
                     
                     // High-detail terrain with waves
                     const groundField = new THREE.Mesh(
                         new THREE.PlaneGeometry(250, 250, 128, 128),
                         new THREE.MeshStandardMaterial({
                             color: 0x71B05F,
                             roughness: 0.95,
                             metalness: 0.0,
                             onBeforeCompile: shader => {
                                 shader.uniforms.time = { value: 0 };
                                 shader.vertexShader = `
                                     uniform float time;
                                     ${shader.vertexShader}
                                 `.replace(`#include <begin_vertex>`, `
                                     #include <begin_vertex>
                                     float wave1 = sin(position.x * 0.1 + position.z * 0.15 + time * 0.6) * 0.3;
                                     float wave2 = sin(position.x * 0.2 - position.z * 0.1 + time * 0.4) * 0.2;
                                     transformed.y += wave1 + wave2;
                                 `);
                             }
                         })
                     );
                     groundField.rotation.x = -Math.PI / 2;
                     groundField.receiveShadow = true;
                     scene.add(groundField);
                     currentWorldObjects.push(groundField);
                     // Grass clumps
                     for(let i=0; i<200; i++) {
                         const grass = new THREE.Mesh(
                             new THREE.ConeGeometry(0.15, 0.8, 4),
                             new THREE.MeshStandardMaterial({color: 0x90EE90, roughness:0.8})
                         );
                         grass.position.set(Math.random()*200-100, 0.4, Math.random()*200-100);
                         grass.rotation.y = Math.random() * Math.PI;
                         grass.castShadow = true;
                         scene.add(grass);
                         currentWorldObjects.push(grass);
                     }
                     // Trees
                     for(let i=0; i<50; i++) {
                         const trunk = new THREE.Mesh(
                             new THREE.CylinderGeometry(0.3, 0.5, 3, 12),
                             new THREE.MeshStandardMaterial({color:0x5A3E2B, roughness:0.9})
                         );
                         const leaves = new THREE.Mesh(
                             new THREE.SphereGeometry(1.8, 16, 16),
                             new THREE.MeshStandardMaterial({color:0x3A7D44, roughness:0.8})
                         );
                         trunk.position.set(Math.random()*150-75, 1.5, Math.random()*150-75);
                         leaves.position.set(trunk.position.x, 4, trunk.position.z);
                         trunk.castShadow = leaves.castShadow = true;
                         scene.add(trunk, leaves);
                         currentWorldObjects.push(trunk, leaves);
                     }
                     // Spaceship
                     const ship = new THREE.Group();
                     const shipBody = new THREE.Mesh(
                         new THREE.CapsuleGeometry(2, 4, 8, 16),
                         new THREE.MeshStandardMaterial({color:0xC0C0C0, metalness:0.9, roughness:0.1})
                     );
                     const cockpit = new THREE.Mesh(
                         new THREE.SphereGeometry(1.2, 16, 16),
                         new THREE.MeshStandardMaterial({color:0x87CEEB, transparent:true, opacity:0.7})
                     );
                     const thruster = new THREE.Mesh(
                         new THREE.CylinderGeometry(1, 1.5, 2, 12),
                         new THREE.MeshStandardMaterial({color:0xFF6600, emissive:0xFF4400, emissiveIntensity:0.5})
                     );
                     shipBody.rotation.z = Math.PI/2;
                     cockpit.position.z = 2.5;
                     thruster.position.z = -2.5;
                     ship.add(shipBody, cockpit, thruster);
                     ship.position.set(0, 10, -20);
                     ship.scale.set(1.5,1.5,1.5);
                     scene.add(ship);
                     currentWorldObjects.push(ship);
                     
                     gameState.shipLanded = true;
                     break;
                 // ---------- CYBERJUNK WORLD ----------
                 case 'cyberjunk':
                     scene.fog.color.set(0x0A0A0A);
                     sky.material.color.set(0x0A0A0A);
                     scene.fog.density = 0.015;
                     const groundCyber = new THREE.Mesh(
                         new THREE.PlaneGeometry(300,300),
                         new THREE.MeshStandardMaterial({color:0x111111, roughness:0.8})
                     );
                     groundCyber.rotation.x = -Math.PI/2;
                     groundCyber.receiveShadow = true;
                     scene.add(groundCyber);
                     currentWorldObjects.push(groundCyber);
                     // Piles of junk
                     for(let i=0; i<80; i++) {
                         const junk = new THREE.Mesh(
                             new THREE.BoxGeometry(Math.random()*8+2, Math.random()*6+1, Math.random()*8+2),
                             new THREE.MeshStandardMaterial({
                                 color: Math.random() > 0.5 ? 0x333333 : 0x222222,
                                 metalness: 0.5, roughness:0.4
                             })
                         );
                         junk.position.set(Math.random()*250-125, junk.geometry.parameters.height/2, Math.random()*250-125);
                         junk.rotation.y = Math.random()*Math.PI;
                         junk.castShadow = true;
                         scene.add(junk);
                         currentWorldObjects.push(junk);
                     }
                     // Neon lights
                     for(let i=0; i<40; i++) {
                         const light = new THREE.PointLight(
                             Math.random() > 0.5 ? 0x00FF99 : 0xFF00CC,
                             2, 30
                         );
                         light.position.set(Math.random()*250-125, Math.random()*10+2, Math.random()*250-125);
                         scene.add(light);
                         currentWorldObjects.push(light);
                     }
                     // Portal
                     const portalGeo = new THREE.TorusGeometry(5, 1.2, 16, 50);
                     const portalMat = new THREE.MeshStandardMaterial({
                         color: 0xFF00FF,
                         emissive: 0x0099FF,
                         emissiveIntensity: 1.5,
                         transparent: true,
                         opacity: 0.8
                     });
                     const portal = new THREE.Mesh(portalGeo, portalMat);
                     portal.position.set(0, 6, -15);
                     portal.rotation.x = Math.PI/2;
                     scene.add(portal);
                     currentWorldObjects.push(portal);
                     gameState.portalActive = true;
                     break;
                 // ---------- SPACE WORLD ----------
                 case 'space':
                     scene.fog.density = 0;
                     sky.material.color.set(0x000011);
                     
                     // Stars
                     for(let i=0; i<1000; i++) {
                         const star = new THREE.Mesh(
                             new THREE.SphereGeometry(Math.random()*0.5+0.1),
                             new THREE.MeshBasicMaterial({color:0xFFFFFF})
                         );
                         star.position.set(
                             (Math.random()-0.5)*500,
                             (Math.random()-0.5)*500,
                             (Math.random()-0.5)*500
                         );
                         scene.add(star);
                         currentWorldObjects.push(star);
                     }
                     // Planets
                     const planets = [
                         {name:"ICE", color:0xAADDFF, x:-40, y:5, z:-20, size:6},
                         {name:"LAVA", color:0xFF5500, x:30, y:8, z:-40, size:5},
                         {name:"OCEAN", color:0x0099FF, x:60, y:2, z:-10, size:7},
                         {name:"CYBER CITY", color:0xFF00FF, x:-20, y:10, z:-50, size:4},
                         {name:"FOREST", color:0x33CC33, x:-60, y:0, z:-30, size:6},
                         {name:"DESERT", color:0xFFCC66, x:20, y:-5, z:-60, size:5},
                         {name:"VOLCANO", color:0xCC2200, x:0, y:15, z:-70, size:4},
                         {name:"ENERGY", color:0xFFFF00, x:40, y:-10, z:-80, size:5}
                     ];
                     planets.forEach(p => {
                         const planet = new THREE.Mesh(
                             new THREE.SphereGeometry(p.size, 32, 32),
                             new THREE.MeshStandardMaterial({
                                 color: p.color,
                                 metalness: 0.3,
                                 roughness: 0.7,
                                 emissive: p.color,
                                 emissiveIntensity: 0.2
                             })
                         );
                         planet.position.set(p.x, p.y, p.z);
                         scene.add(planet);
                         currentWorldObjects.push(planet);
                     });
                     break;
                 // ---------- OTHER WORLDS (QUICK SETUP) ----------
                 default:
                     const worldColors = {
                         ice: {fog:0xAADDFF, sky:0xAADDFF, ground:0xEEEEEE},
                         lava: {fog:0x441100, sky:0x662200, ground:0x882200},
                         ocean: {fog:0x0077BB, sky:0x0099DD, ground:0x004477},
                         cybercity: {fog:0x220044, sky:0x330066, ground:0x110022},
                         forest: {fog:0x225522, sky:0x337733, ground:0x224422},
                         desert: {fog:0xDDAA66, sky:0xFFCC88, ground:0xCC9955},
                         volcano: {fog:0x330000, sky:0x550000, ground:0x440000},
                         energy: {fog:0xAAAA00, sky:0xCCCC00, ground:0x888800}
                     };
                     const c = worldColors[worldName];
                     scene.fog.color.set(c.fog);
                     sky.material.color.set(c.sky);
                     
                     const ground = new THREE.Mesh(
                         new THREE.PlaneGeometry(200,200),
                         new THREE.MeshStandardMaterial({color:c.ground, roughness:0.9})
                     );
                     ground.rotation.x = -Math.PI/2;
                     ground.receiveShadow = true;
                     scene.add(ground);
                     currentWorldObjects.push(ground);
             }
         }
         // ==================================================
         // ============ JOYSTICK & CONTROLS =================
         // ==================================================
         const joystick = document.getElementById('joystick');
         const knob = document.getElementById('joystick-knob');
         let isDragging = false, startX=0, startY=0;
         let moveForward = 0, moveTurn = 0;
         // Touch Controls
         joystick.addEventListener('touchstart', e => {
             isDragging = true;
             const t = e.touches[0];
             startX = t.clientX - joystick.getBoundingClientRect().left;
             startY = t.clientY - joystick.getBoundingClientRect().top;
         });
         document.addEventListener('touchmove', e => {
             if(!isDragging) return;
             const t = e.touches[0];
             let dx = (t.clientX - joystick.getBoundingClientRect().left) - startX;
             let dy = (t.clientY - joystick.getBoundingClientRect().top) - startY;
             
             const maxRadius = 50;
             const dist = Math.sqrt(dx*dx + dy*dy);
             if(dist > maxRadius) {
                 dx *= maxRadius / dist;
                 dy *= maxRadius / dist;
             }
             knob.style.transform = `translate(${dx}px, ${dy}px)`;
             moveForward = -dy / 120;
             moveTurn = dx / 120;
         });
         document.addEventListener('touchend', () => {
             isDragging = false;
             knob.style.transform = 'translate(0,0)';
             moveForward = 0; moveTurn = 0;
             gameState.digging = false;
         });
         // Keyboard Controls
         document.addEventListener('keydown', e => {
             if(e.key === 'ArrowUp') moveForward = 0.8;
             if(e.key === 'ArrowDown') { moveForward = -0.5; gameState.digging = true; }
             if(e.key === 'ArrowLeft') moveTurn = -0.6;
             if(e.key === 'ArrowRight') moveTurn = 0.6;
             if(e.key === '1') currentPlayer = players[0];
             if(e.key === '2') currentPlayer = players[1];
             if(e.key === '3') currentPlayer = players[2];
             if(e.key === '4') currentPlayer = players[3];
             if(e.key === 'Enter' && gameState.shipLanded) {
                 gameState.currentWorld = 'cyberjunk';
                 loadWorld('cyberjunk');
             }
         });
         document.addEventListener('keyup', () => {
             moveForward = 0; moveTurn = 0;
             gameState.digging = false;
         });
         // ==================================================
         // ============ HELPER FUNCTIONS ====================
         // ==================================================
         function showNotification(text) {
             notification.textContent = text;
             notification.style.opacity = '1';
             setTimeout(() => notification.style.opacity = '0', 2000);
         }
         // ==================================================
         // ============ MAIN GAME LOOP ======================
         // ==================================================
         const clock = new THREE.Clock();
         loadWorld('field'); // Start in Field world
         function animate() {
             requestAnimationFrame(animate);
             const elapsed = clock.getElapsedTime();
             // Update Battery
             gameState.battery = Math.max(0, gameState.battery - 0.015);
             batteryUI.textContent = `🔋 BATTERY: ${Math.floor(gameState.battery)}%`;
             batteryUI.style.color = gameState.battery < 20 ? '#FF3333' : '#00FF99';
             batteryUI.style.borderColor = gameState.battery < 20 ? '#FF3333' : '#00FF99';
             // Random secret detection
             gameState.nearSecret = Math.random() > 0.85;
             // Update all players
             players.forEach((p, idx) => {
                 if(idx >= gameState.playersOnline) return;
                 // Only control current player
                 if(p === currentPlayer) {
                     p.angle += moveTurn * p.turnSpeed;
                     
                     // Digging effect
                     const speedMult = gameState.digging ? 0.3 : 1;
                     const mx = Math.sin(p.angle) * moveForward * p.speed * speedMult;
                     const mz = Math.cos(p.angle) * moveForward * p.speed * speedMult;
                     
                     p.x += mx;
                     p.z += mz;
                     // Bounds
                     p.x = Math.max(-120, Math.min(120, p.x));
                     p.z = Math.max(-120, Math.min(120, p.z));
                 } else {
                     // AI movement for other players
                     p.angle += Math.sin(elapsed + p.id) * 0.01;
                     p.x += Math.sin(elapsed * 0.5 + p.id) * 0.02;
                     p.z += Math.cos(elapsed * 0.5 + p.id) * 0.02;
                 }
                 // Update robot position & rotation
                 p.mesh.position.set(p.x, 0, p.z);
                 p.mesh.rotation.y = p.angle;
                 // Antenna glow when near secret
                 if(p.antennaL && p.antennaR) {
                     const tipL = p.antennaL.children[0];
                     const tipR = p.antennaR.children[0];
                     tipL.material.emissiveIntensity = gameState.nearSecret ? 2.0 : 0.5;
                     tipR.material.emissiveIntensity = gameState.nearSecret ? 2.0 : 0.5;
                     tipL.material.color.set(gameState.nearSecret ? 0x00FFFF : 0x0099FF);
                     tipR.material.color.set(gameState.nearSecret ? 0x00FFFF : 0x0099FF);
                 }
                 // Digging animation
                 if(gameState.digging && p === currentPlayer) {
                     p.mesh.position.y = Math.abs(Math.sin(elapsed * 15)) * 0.3;
                 } else {
                     p.mesh.position.y = 0;
                 }
             });
             // ============== CAMERA LOGIC ==============
             if(gameState.firstPerson) {
                 // FIRST PERSON: See through robot's eyes
                 camera.position.set(
                     currentPlayer.x + Math.sin(currentPlayer.angle) * 0.2,
                     currentPlayer.y,
                     currentPlayer.z + Math.cos(currentPlayer.angle) * 0.2
                 );
                 camera.rotation.y = currentPlayer.angle;
                 crosshair.style.display = 'block';
             } else {
                 // THIRD PERSON: Follow behind, see the robot
                 const camDistance = 7;
                 const camHeight = 3;
                 camera.position.set(
                     currentPlayer.x - Math.sin(currentPlayer.angle) * camDistance,
                     currentPlayer.y + camHeight,
                     currentPlayer.z - Math.cos(currentPlayer.angle) * camDistance
                 );
                 camera.lookAt(currentPlayer.x, currentPlayer.y - 0.5, currentPlayer.z);
                 crosshair.style.display = 'none';
             }
             renderer.render(scene, camera);
         }
         animate();
         // Window Resize Handler
         window.addEventListener('resize', () => {
             camera.aspect = window.innerWidth / window.innerHeight;
             camera.updateProjectionMatrix();
             renderer.setSize(window.innerWidth, window.innerHeight);
         });
     </script>
 </body>
 </html>

Game Source: JOHN 2: THE GAME | ULTIMATE EDITION | Monster 2016

Creator: QuantumBolt10

Libraries: three

Complexity: complex (731 lines, 34.3 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: john-2-the-game-ultimate-edition-monster-quantumbolt10" to link back to the original. Then publish at arcadelab.ai/publish.