Create src/multiplayer/startGrid.js
Browse files- src/multiplayer/startGrid.js +75 -0
src/multiplayer/startGrid.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as THREE from 'three';
|
| 2 |
+
|
| 3 |
+
export const START_GRID_Y = 0.28;
|
| 4 |
+
export const START_GRID_LATERAL_OFFSET = 2.35;
|
| 5 |
+
export const START_GRID_ROW_SPACING = 6.0;
|
| 6 |
+
export const START_GRID_INITIAL_OFFSET = 8.0;
|
| 7 |
+
export const MULTIPLAYER_COUNTDOWN_MS = 3600;
|
| 8 |
+
export const START_SYNC_GRACE_MS = 5000;
|
| 9 |
+
export const START_SYNC_ANCHOR_TOLERANCE = 24;
|
| 10 |
+
export const CENTER_FALLBACK_RADIUS = 18;
|
| 11 |
+
|
| 12 |
+
const TRACK_POINTS = [
|
| 13 |
+
new THREE.Vector3(0, 0, 100),
|
| 14 |
+
new THREE.Vector3(50, 0, 90),
|
| 15 |
+
new THREE.Vector3(80, 0, 60),
|
| 16 |
+
new THREE.Vector3(120, 0, 40),
|
| 17 |
+
new THREE.Vector3(120, 0, -20),
|
| 18 |
+
new THREE.Vector3(105, 0, -50),
|
| 19 |
+
new THREE.Vector3(120, 0, -80),
|
| 20 |
+
new THREE.Vector3(120, 0, -140),
|
| 21 |
+
new THREE.Vector3(70, 0, -140),
|
| 22 |
+
new THREE.Vector3(70, 0, -70),
|
| 23 |
+
new THREE.Vector3(10, 0, -70),
|
| 24 |
+
new THREE.Vector3(10, 0, -140),
|
| 25 |
+
new THREE.Vector3(-40, 0, -140),
|
| 26 |
+
new THREE.Vector3(-80, 0, -120),
|
| 27 |
+
new THREE.Vector3(-140, 0, -80),
|
| 28 |
+
new THREE.Vector3(-150, 0, -20),
|
| 29 |
+
new THREE.Vector3(-110, 0, 30),
|
| 30 |
+
new THREE.Vector3(-140, 0, 70),
|
| 31 |
+
new THREE.Vector3(-90, 0, 110),
|
| 32 |
+
new THREE.Vector3(-40, 0, 100),
|
| 33 |
+
];
|
| 34 |
+
|
| 35 |
+
const SHARED_CURVE = new THREE.CatmullRomCurve3(TRACK_POINTS, true, 'centripetal', 0.5);
|
| 36 |
+
const SHARED_TRACK_LENGTH = SHARED_CURVE.getLength();
|
| 37 |
+
const UP = new THREE.Vector3(0, 1, 0);
|
| 38 |
+
|
| 39 |
+
function normalizeSlotIndex(slotIndex = 0) {
|
| 40 |
+
if (!Number.isFinite(slotIndex)) return 0;
|
| 41 |
+
return Math.max(0, Math.floor(slotIndex));
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
export function getStartTransformForSlot(slotIndex = 0) {
|
| 45 |
+
const normalizedSlot = normalizeSlotIndex(slotIndex);
|
| 46 |
+
const row = Math.floor(normalizedSlot / 2);
|
| 47 |
+
const side = normalizedSlot % 2 === 0 ? -1 : 1;
|
| 48 |
+
const baseT = (1 - ((row * START_GRID_ROW_SPACING + START_GRID_INITIAL_OFFSET) / SHARED_TRACK_LENGTH) + 1) % 1;
|
| 49 |
+
const point = SHARED_CURVE.getPointAt(baseT);
|
| 50 |
+
const tangent = SHARED_CURVE.getTangentAt(baseT).normalize();
|
| 51 |
+
const lateral = new THREE.Vector3().crossVectors(UP, tangent).normalize();
|
| 52 |
+
const heading = Math.atan2(tangent.x, tangent.z);
|
| 53 |
+
const position = point.clone().addScaledVector(lateral, side * START_GRID_LATERAL_OFFSET).setY(START_GRID_Y);
|
| 54 |
+
return {
|
| 55 |
+
slotIndex: normalizedSlot,
|
| 56 |
+
progress: baseT,
|
| 57 |
+
heading,
|
| 58 |
+
position: { x: position.x, y: position.y, z: position.z },
|
| 59 |
+
};
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
export function isNearCenterPosition(position, radius = CENTER_FALLBACK_RADIUS) {
|
| 63 |
+
if (!position) return true;
|
| 64 |
+
const x = Number(position.x ?? 0);
|
| 65 |
+
const z = Number(position.z ?? 0);
|
| 66 |
+
return Number.isFinite(x) && Number.isFinite(z) ? Math.hypot(x, z) <= radius : true;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
export function distanceBetweenPositions(a, b) {
|
| 70 |
+
if (!a || !b) return Number.POSITIVE_INFINITY;
|
| 71 |
+
const dx = Number(a.x ?? 0) - Number(b.x ?? 0);
|
| 72 |
+
const dy = Number(a.y ?? 0) - Number(b.y ?? 0);
|
| 73 |
+
const dz = Number(a.z ?? 0) - Number(b.z ?? 0);
|
| 74 |
+
return Math.hypot(dx, dy, dz);
|
| 75 |
+
}
|