⭐ Built in the Agent OS — get it inside the AI Profit BoardroomJoin AIPB →, 0, 1)); /* wheel spin + steer, engine flare pulse, practical rim lights */ var wh = hero.userData.wheels, spin = S.speed * dt * 1.9; for (var i=0;i 0) S.invul -= dt; } /* ---- world recycling + skybox follow ---- */ function updateWorld(dt){ var pz = S.z, px = S.x, i; for (i=0;i pz + SEG_LEN * 5) layoutSegment(i, segZ[i] - TRACK_LEN); } for (i=0;i pz + 60) layoutPalm(i, palmData[i].z - TRACK_LEN); } for (i=0;i pz + 60) layoutRock(i, rockData[i].z - TRACK_LEN); } for (i=0;i pz + 120) layoutCity(i, cityData[i].z - CITY_N * 85); } for (i=0;i pz + 40) placeArch(i, arches[i].userData.z - 180 * arches.length); } for (i=0;i pz + 60) placeBoard(i, boards[i].userData.z - 260 * boards.length); } /* neon grid snaps along with the car */ grid.position.x = Math.round(px / GRID_STEP) * GRID_STEP; grid.position.z = Math.round(pz / GRID_STEP) * GRID_STEP; ground.position.x = px; ground.position.z = pz; /* infinite-distance layers: fixed offset + micro parallax */ backdrop.position.set(px * 0.02, 320, pz - 1900); sun.position.set(px * 0.03 - 30, 132, pz - 1720); sunHalo.position.set(sun.position.x, sun.position.y, sun.position.z - 6); mtnGroup.position.set(px * 0.08, 0, pz - 1000); /* key light rides with the hero so shadows stay in the shadow camera */ key.position.set(px - 70, 90, pz - 110); key.target.position.set(px, 0, pz - 20); key.target.updateMatrixWorld(); /* practicals track the car */ rimA.position.set(px - 3, 2.2, pz + 3.4); rimB.position.set(px + 3, 2.2, pz - 3.4); rimA.intensity = 1.2 + Math.sin(S.t * 5) * 0.25; rimB.intensity = 1.6 + Math.sin(S.t * 3.4 + 1) * 0.3; } /* ---- shooting + bolt physics ---- */ function updateCombat(dt){ aimYaw = lerp(aimYaw, -mouse.nx * 0.5, clamp(9 * dt, 0, 1)); S.fireCd -= dt; if (mouse.down && !S.dead && S.fireCd <= 0){ S.fireCd = 0.11; var fy = S.yaw + aimYaw; var dx = Math.sin(fy), dz = -Math.cos(fy); var side = (S.t * 9 | 0) % 2 === 0 ? -0.92 : 0.92; var ox = S.x + Math.cos(S.yaw) * side + dx * 3.2; var oz = S.z + Math.sin(S.yaw) * side + dz * 3.2; fireBolt(ox, 1.0, oz, dx * 330, dz * 330, false); burst(ox, 1.0, oz, 2, "c", 3); S.shake = Math.max(S.shake, 0.06); } for (var i=0;i=0; e--){ var en = enemies[e]; var ex = b.mesh.position.x - en.x, ez = b.mesh.position.z - en.z; var ey = b.mesh.position.y - (en.y + 1.0); if (ex*ex + ez*ez < 10.5 && Math.abs(ey) < 4.2){ b.live = false; b.mesh.visible = false; en.hp -= 13; en.flash = 0.16; burst(en.x, en.y + 1.1, en.z, 5, "c", 6); S.score += 8; if (en.hp <= 0) killEnemy(en, e); break; } } } } } /* ---- enemy AI ---- */ function updateEnemies(dt){ S.spawnCd -= dt; var cap = Math.min(9, 3 + Math.floor(S.dist / 1400)); if (!S.dead && S.spawnCd <= 0 && enemies.length < cap){ S.spawnCd = rnd(1.4, 3.0); spawnEnemy(); } for (var i=enemies.length-1; i>=0; i--){ var e = enemies[i]; /* despawn far behind / far ahead */ if (e.z > S.z + 130 || e.z < S.z - 520){ scene.remove(e.g); enemies.splice(i,1); continue; } var dz = e.z - S.z, dx = e.x - S.x; var d = Math.sqrt(dx*dx + dz*dz); if (e.drone){ /* hovers ahead, strafes across the road, shoots */ e.bob += dt * 2.2; e.y = 5.5 + Math.sin(e.bob) * 1.6; var want = S.z - 62; e.z += clamp((want - e.z), -e.spd, e.spd) * dt * 1.3 - S.speed * 0 * dt; e.z -= (S.speed * 0.0) * dt; e.x = lerp(e.x, S.x + Math.sin(S.t * 1.3 + e.bob) * 12, clamp(1.3 * dt, 0, 1)); e.g.rotation.y = Math.atan2(-dx, -dz) * 0.6; e.g.userData.rings[0].rotation.z += dt * 2.4; e.g.userData.rings[1].rotation.z -= dt * 3.6; e.fireCd -= dt; if (!S.dead && e.fireCd <= 0 && d < 190){ e.fireCd = rnd(1.1, 2.1); var bl = Math.sqrt(dx*dx + dz*dz) || 1; fireBolt(e.x, e.y, e.z, (-dx/bl) * 130, (-dz/bl) * 130 - S.speed * 0.35, true); burst(e.x, e.y, e.z - 1.2, 3, "r", 4); } } else { /* ground chaser: matches speed, closes in, rams */ var chase = e.spd + (dz < 0 ? 26 : -18); e.z -= chase * dt; e.z += S.speed * dt * 0.0; var lane = clamp(S.x, roadX(e.z) - HALF_W + 3, roadX(e.z) + HALF_W - 3); e.x = lerp(e.x, lane, clamp(1.9 * dt, 0, 1)); e.y = 0; e.g.rotation.y = Math.atan(-roadSlope(e.z)) + (lane - e.x) * 0.04; var ws = e.g.userData.wheels; for (var w=0; w 0){ e.flash -= dt; var f = e.flash > 0 ? 1 : 0; e.g.userData.mats[0].emissive.setHex(0xffffff); e.g.userData.mats[0].emissiveIntensity = f * 2.2; } else { e.g.userData.mats[0].emissiveIntensity = 0; } /* contact damage */ if (!S.dead && d < (e.drone ? 4.4 : 5.0) && Math.abs(e.y) < 4.0){ hurt(e.drone ? 12 : 18, "ram"); burst(e.x, 1.4, e.z, 14, "r", 11); e.hp -= 20; e.z -= 22; e.x += (dx >= 0 ? 6 : -6); S.speed *= 0.78; S.invul = 0.35; if (e.hp <= 0) killEnemy(e, i); } } } /* ---- particles ---- */ function updateParticles(dt){ for (var i=0;i S.z + 40){ p.live = false; p.g.visible = false; continue; } var dx = p.x - S.x, dz = p.z - S.z; if (!S.dead && dx*dx + dz*dz < 20){ p.live = false; p.g.visible = false; S.hp = Math.min(100, S.hp + 22); S.turbo = Math.min(100, S.turbo + 34); S.score += 90; burst(p.x, 2.2, p.z, 18, "g", 9); burst(p.x, 2.2, p.z, 8, "c", 6); screenFlash("rgba(90,255,190,0.35)", 0.5); banner("REPAIR +22 HULL", "#38ff9a"); } } if (Math.random() < dt * 0.35) spawnPickup(S.z - rnd(200, 340)); } /* ---- camera ---- */ function updateCamera(dt){ var back = 19 + S.speed * 0.055; var tx = S.x - Math.sin(S.yaw) * back * 0.35 - aimYaw * 9; var tz = S.z + Math.cos(S.yaw) * back; var ty = 9.2 + S.speed * 0.012; camPos.x = lerp(camPos.x, tx, clamp(5.2 * dt, 0, 1)); camPos.y = lerp(camPos.y, ty, clamp(4.0 * dt, 0, 1)); camPos.z = lerp(camPos.z, tz, clamp(7.0 * dt, 0, 1)); if (S.shake > 0){ S.shake = Math.max(0, S.shake - dt * 1.9); var a = S.shake * 1.5; camera.position.set(camPos.x + rnd(-a,a), camPos.y + rnd(-a,a), camPos.z + rnd(-a,a)); } else { camera.position.copy(camPos); } var ahead = S.z - 62; camLook.set(lerp(S.x, roadX(ahead), 0.45) - aimYaw * 16, 3.2 - mouse.ny * 2.2, ahead); camera.lookAt(camLook); var wantFov = 66 + clamp((S.speed - MAX_SPEED) * 0.16, 0, 12); camera.fov = lerp(camera.fov, wantFov, clamp(3.5 * dt, 0, 1)); camera.updateProjectionMatrix(); } /* ---- HUD + radar ---- */ function updateHUD(dt){ var hp = Math.round(S.hp); hpFill.style.width = hp + "%"; hpNum.textContent = (hp < 10 ? "00" : hp < 100 ? "0" : "") + hp + "%"; hpState.textContent = hp > 66 ? "NOMINAL" : hp > 33 ? "DAMAGED" : "CRITICAL"; hpState.style.color = hp > 66 ? "#9dffd0" : hp > 33 ? "#ffd166" : "#ff5470"; var kmh = Math.round(S.speed * 2.4); spNum.textContent = (kmh < 10 ? "00" : kmh < 100 ? "0" : "") + kmh; spFill.style.width = clamp(kmh / (TURBO_SPD * 2.4) * 100, 0, 100) + "%"; btFill.style.width = Math.round(S.turbo) + "%"; scoreV.textContent = Math.round(S.score).toLocaleString(); killsEl.textContent = S.kills; aliveEl.textContent = enemies.length; distEl.textContent = (S.dist / 1000).toFixed(2) + " KM"; if (bannerT > 0){ bannerT -= dt; var k = clamp(bannerT / 1.25, 0, 1); bannerEl.style.opacity = Math.min(1, k * 2.4); bannerEl.style.transform = "translateX(-50%) scale(" + (1 + (1-k) * 0.12) + ")"; } else bannerEl.style.opacity = 0; if (flashT > 0){ flashT = Math.max(0, flashT - dt * 2.6); flashEl.style.background = flashCol; flashEl.style.opacity = flashT; } else flashEl.style.opacity = 0; crossEl.style.opacity = S.dead ? 0 : 1; /* threat radar */ var W = radar.width, H = radar.height; rctx.clearRect(0,0,W,H); rctx.strokeStyle = "rgba(255,79,216,0.28)"; rctx.lineWidth = 2; for (var g=1; g<4; g++){ rctx.beginPath(); rctx.moveTo(0, H*g/4); rctx.lineTo(W, H*g/4); rctx.stroke(); } rctx.strokeStyle = "rgba(47,240,255,0.35)"; rctx.beginPath(); rctx.moveTo(W/2, 0); rctx.lineTo(W/2, H); rctx.stroke(); /* sweep */ var sw = (S.t * 0.55 % 1) * H; rctx.fillStyle = "rgba(47,240,255,0.10)"; rctx.fillRect(0, sw - 18, W, 18); /* player */ rctx.fillStyle = "#5ffbf1"; rctx.beginPath(); rctx.moveTo(W/2, H - 14); rctx.lineTo(W/2 - 9, H - 2); rctx.lineTo(W/2 + 9, H - 2); rctx.closePath(); rctx.fill(); /* enemies */ for (var i=0;i W-3 || ry < 3 || ry > H-3) continue; rctx.fillStyle = e.drone ? "#ff9a3c" : "#ff2f5e"; rctx.beginPath(); rctx.arc(rx, ry, e.drone ? 4.5 : 5.5, 0, 6.283); rctx.fill(); } /* pickups */ rctx.fillStyle = "#38ff9a"; for (i=0;i W-3 || qy < 3 || qy > H-3) continue; rctx.fillRect(qx - 3, qy - 3, 6, 6); } } /* GO — no click gate, first frame is already lit and moving */ tick(); })();