⭐ 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();
})();