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