summaryrefslogtreecommitdiff
path: root/serve/_carmod.js
blob: da70fd3241f689188e3432b0618383f1ff3cb066 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* ===== АВТО-РЕЖИМ: выбор транспорта + езда на ported reVC-физике (client) ===== */
var carMode = false, carObj = null, carMesh = null, carGround = null, carAcc = 0, carPreset = 'brovenosec';
var carMesh_wheels = [];
var carHeadlights = [];
var CAR_SPAWN = { x: 70, z: -40 };

function buildCarMesh() {
  var h = carObj.h;
  var g = new THREE.Group();
  var L = h.Dimension.y, Wd = h.Dimension.x, Ht = h.Dimension.z;
  var body = new THREE.Mesh(new THREE.BoxGeometry(Wd, Ht * 0.6, L),
    new THREE.MeshStandardMaterial({ color: 0xc81e3a, roughness: 0.45, metalness: 0.3 }));
  body.position.y = Ht * 0.15; g.add(body);
  var cab = new THREE.Mesh(new THREE.BoxGeometry(Wd * 0.86, Ht * 0.5, L * 0.5),
    new THREE.MeshStandardMaterial({ color: 0x20242c, roughness: 0.3, metalness: 0.4 }));
  cab.position.set(0, Ht * 0.55, -L * 0.05); g.add(cab);
  var nose = new THREE.Mesh(new THREE.BoxGeometry(Wd * 0.5, Ht * 0.2, 0.3),
    new THREE.MeshStandardMaterial({ color: 0xffd23a, emissive: 0x442200 }));
  nose.position.set(0, Ht * 0.1, L * 0.5); g.add(nose);   // жёлтый нос = вперёд (+Z)
  // фары — area lights (LTC, как у дрона): светят вперёд (+Z) и чуть вниз на дорогу
  var hlX = Wd * 0.34, hlY = Ht * 0.16, hlZ = L * 0.5 + 0.04;
  var lensMat = new THREE.MeshStandardMaterial({ color: 0xfff6d8, emissive: 0xfff0b0, emissiveIntensity: 2.6, roughness: 0.25, metalness: 0.0 });
  carHeadlights = [];
  for (var hi = 0; hi < 2; hi++) {
    var sx = hi === 0 ? -1 : 1;
    var lens = new THREE.Mesh(new THREE.BoxGeometry(Wd * 0.26, Ht * 0.18, 0.10), lensMat);
    lens.position.set(sx * hlX, hlY, hlZ); g.add(lens);
    var hl = new THREE.RectAreaLight(0xfff2cc, 11.0, Wd * 0.55, Ht * 0.42);
    hl.position.set(sx * hlX, hlY, hlZ + 0.06);
    hl.rotation.set(Math.PI + 0.16, 0, 0);   // локальный -Z -> +Z (вперёд), лёгкий наклон вниз
    g.add(hl); carHeadlights.push(hl);
  }
  var wr = carObj.wheelRadius;
  var wheelGeo = new THREE.CylinderGeometry(wr, wr, 0.3, 16);
  var wheelMat = new THREE.MeshStandardMaterial({ color: 0x111214, roughness: 0.85 });
  carMesh_wheels = [];
  for (var i = 0; i < 4; i++) {
    var w = new THREE.Mesh(wheelGeo, wheelMat);
    var pb = carObj.m_wheelPosBody[i];
    w.position.set(pb.x, pb.y, pb.z);
    w.rotation.set(0, 0, Math.PI / 2);   // ось колеса вдоль X
    g.add(w); carMesh_wheels.push(w);
  }
  return g;
}

function spawnCar() {
  var CP = window.PZCarPhys;
  var hd = CP.makeHandling(CP.PRESETS[carPreset]);
  carGround = CP.makeGround(groundH, WATER_Y, { patch: 1.5 });
  var gy = groundH(CAR_SPAWN.x, CAR_SPAWN.z);
  carObj = new CP.Car(hd, { x: CAR_SPAWN.x, y: gy + 2.0, z: CAR_SPAWN.z, heading: 0 });
  carMesh = buildCarMesh();
  scene.add(carMesh);
  try { window.PZCAR = carObj; } catch(e){}
}

function readCarInput() {
  var th = 0, st = 0;
  if (keys['KeyW'] || keys['ArrowUp']) th += 1;
  if (keys['KeyS'] || keys['ArrowDown']) th -= 1;
  if (keys['KeyA'] || keys['ArrowLeft']) st += 1;   // руль влево (исправлено: влево/вправо было перепутано)
  if (keys['KeyD'] || keys['ArrowRight']) st -= 1;   // руль вправо
  return { throttle: th, steer: st, handbrake: !!keys['Space'], brakeHeld: (th < 0) };
}

var _cbR = new THREE.Vector3(), _cbU = new THREE.Vector3(), _cbF = new THREE.Vector3(), _cbM = new THREE.Matrix4();
function carRespawn() {
  var CP = window.PZCarPhys, gy = groundH(CAR_SPAWN.x, CAR_SPAWN.z);
  carObj.pos = CP._v(CAR_SPAWN.x, gy + 2.0, CAR_SPAWN.z);
  carObj.moveSpeed = CP._v(0, 0, 0); carObj.turnSpeed = CP._v(0, 0, 0);
  carObj.setHeading(0);
}
function carStep(dt) {
  if (!carObj) return;
  if (keys['KeyR']) carRespawn();
  var inp = readCarInput(); carAcc += dt; var n = 0;
  while (carAcc >= 0.02 && n < 6) { carObj.step(inp, carGround); carAcc -= 0.02; n++; }   // фиксированные 50 Гц
  carMesh.position.set(carObj.pos.x, carObj.pos.y, carObj.pos.z);
  _cbR.set(carObj.right.x, carObj.right.y, carObj.right.z);
  _cbU.set(carObj.up.x, carObj.up.y, carObj.up.z);
  _cbF.set(carObj.fwd.x, carObj.fwd.y, carObj.fwd.z);
  _cbM.makeBasis(_cbR, _cbU, _cbF); carMesh.quaternion.setFromRotationMatrix(_cbM);
  for (var i = 0; i < 4; i++) {
    var w = carMesh_wheels[i]; if (!w) continue;
    var pb = carObj.m_wheelPosBody[i]; w.position.set(pb.x, pb.y, pb.z);
    var steer = (i < 2) ? carObj.m_fSteerAngle : 0;   // FL,FR поворачиваются
    w.rotation.set(0, -steer, Math.PI / 2);
  }
}

var _carGoal = new THREE.Vector3(), _carLook = new THREE.Vector3();
function carCamera() {
  controls.enabled = false;
  _cbF.set(carObj.fwd.x, 0, carObj.fwd.z); if (_cbF.lengthSq() < 1e-4) _cbF.set(0, 0, 1); _cbF.normalize();
  _carGoal.set(carObj.pos.x, carObj.pos.y, carObj.pos.z).addScaledVector(_cbF, -12).addScaledVector(WUP, 5.5);
  camera.position.lerp(_carGoal, 0.12);
  _carLook.set(carObj.pos.x, carObj.pos.y + 1.3, carObj.pos.z);
  _camLook.lerp(_carLook, 0.3); camera.lookAt(_camLook);
}

/* --- стартовый выбор транспорта --- */
(function vehicleSelect() {
  var ov = document.createElement('div');
  ov.id = 'vsOverlay';
  ov.style.cssText = 'position:fixed;inset:0;z-index:10050;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(4,6,12,.88);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);font-family:system-ui,Segoe UI,Roboto,sans-serif;color:#eef3ff';
  ov.innerHTML =
    '<div style="font:800 30px system-ui;margin-bottom:6px;text-align:center;letter-spacing:1px">ПЛАНЕТА ЖОПА</div>' +
    '<div style="opacity:.85;margin-bottom:24px">На чём поедешь?</div>' +
    '<div style="display:flex;gap:18px;flex-wrap:wrap;justify-content:center">' +
      '<button id="vsDrone" style="cursor:pointer;border:0;border-radius:16px;padding:24px 28px;min-width:190px;line-height:1.35;background:linear-gradient(180deg,#4aa3ff,#2166c8);color:#fff;font:800 20px system-ui;box-shadow:0 12px 40px rgba(0,0,0,.5)">🚁<br>ДРОН<br><span style="font:500 12px system-ui;opacity:.85">полёт · акро · WASD</span></button>' +
      '<button id="vsCar" style="cursor:pointer;border:0;border-radius:16px;padding:24px 28px;min-width:190px;line-height:1.35;background:linear-gradient(180deg,#ff6a4a,#c8291e);color:#fff;font:800 20px system-ui;box-shadow:0 12px 40px rgba(0,0,0,.5)">🚗<br>МАШИНА<br><span style="font:500 12px system-ui;opacity:.85">физика reVC · газ/руль</span></button>' +
    '</div>' +
    '<div style="margin-top:22px;opacity:.6;font-size:12px">сменить транспорт — перезагрузка страницы</div>';
  document.body.appendChild(ov);
  ['pointerdown', 'touchstart', 'mousedown', 'keydown'].forEach(function (ev) { ov.addEventListener(ev, function (e) { e.stopPropagation(); }); });
  document.getElementById('vsDrone').onclick = function () { carMode = false; ov.remove(); };
  document.getElementById('vsCar').onclick = function () {
    carMode = true;
    try { spawnCar(); } catch (e) { console.error('spawnCar', e); }
    try { if (typeof drone !== 'undefined' && drone) drone.visible = false; } catch (e) {}
    try { sd.style.display = 'none'; } catch (e) {}
    try { dp.style.display = 'none'; } catch (e) {}
    var hp = document.getElementById('help');
    if (hp) hp.innerHTML = '<b>🚗 МАШИНА (физика reVC):</b> <kbd>W</kbd> газ · <kbd>S</kbd> тормоз/задний · <kbd>A</kbd>/<kbd>D</kbd> руль · <kbd>Space</kbd> ручник · <kbd>R</kbd> сброс';
    ov.remove();
  };
})();