JOHN 2: THE GAME | ULTIMATE EDITION | Monster 2016
by QuantumBolt10731 lines34.3 KB🛠️ Three.js (3D graphics)
<!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.