@inventure71/paddockjs 4.1.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/INSTALL_AND_UPDATE.md +31 -5
- package/README.md +34 -3
- package/docs/advanced_ai_pace.md +131 -0
- package/docs/advanced_physics_rebuild.md +166 -0
- package/docs/architecture.md +111 -30
- package/docs/cleanup_coverage.md +284 -0
- package/docs/component_inventory.md +32 -16
- package/docs/composable-layouts.md +2 -0
- package/docs/concepts.md +12 -1
- package/docs/custom_model_controller.md +1 -1
- package/docs/data_contract.md +33 -10
- package/docs/data_model.md +4 -2
- package/docs/efficiency_cleanup_2026-10-13.md +80 -0
- package/docs/getting-started.md +3 -0
- package/docs/grid_walk.md +80 -0
- package/docs/index.md +5 -2
- package/docs/keyboard_driving_fix.md +118 -0
- package/docs/limit_driving_2026-10-14.md +88 -0
- package/docs/loading.md +141 -0
- package/docs/maintainability_audit_2026-09-08.md +146 -0
- package/docs/racing_ai_improvements.md +145 -0
- package/docs/rules.md +43 -13
- package/docs/sense_contract.md +4 -1
- package/docs/system_specs.md +40 -12
- package/docs/testing-reliability-guide.md +67 -0
- package/docs/v11_improvements.md +108 -0
- package/package.json +23 -15
- package/src/api/MountedSimulatorComponents.js +101 -0
- package/src/api/PaddockSimulatorController.js +56 -81
- package/src/app/BrowserExpertAdapter.js +0 -6
- package/src/app/BrowserExpertExternalRenderer.js +72 -50
- package/src/app/ExternalTrackSnapshot.js +50 -0
- package/src/app/F1SimulatorApp.js +242 -297
- package/src/app/HostLifecycleEmitter.js +93 -0
- package/src/app/banners/raceDataBannerController.js +9 -4
- package/src/app/camera/cameraConstants.js +1 -1
- package/src/app/camera/cameraController.js +10 -13
- package/src/app/camera/cameraGeometry.js +1 -1
- package/src/app/controls/SimulatorViewControls.js +45 -0
- package/src/app/controls/workbenchBindings.js +64 -0
- package/src/app/controls/workbenchDisclosure.js +41 -0
- package/src/app/domBindings.js +21 -13
- package/src/app/gridWalk/GridWalkController.js +180 -0
- package/src/app/gridWalk/gridWalkCamera.js +88 -0
- package/src/app/gridWalk/gridWalkControls.js +24 -0
- package/src/app/gridWalk/gridWalkCrew.js +246 -0
- package/src/app/gridWalk/gridWalkLabels.js +104 -0
- package/src/app/gridWalk/gridWalkLayout.js +46 -0
- package/src/app/gridWalk/gridWalkNavigation.js +99 -0
- package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
- package/src/app/gridWalk/gridWalkSession.js +50 -0
- package/src/app/layoutSupport.js +2 -0
- package/src/app/raceOverlayClearanceSupport.js +21 -7
- package/src/app/readouts/carOverviewRenderer.js +9 -4
- package/src/app/readouts/readoutFormatters.js +1 -8
- package/src/app/readouts/telemetryRenderer.js +1 -3
- package/src/app/readouts/timingTowerRenderer.js +48 -50
- package/src/app/rendering/appAssets.js +8 -10
- package/src/app/rendering/carRenderer.js +27 -79
- package/src/app/rendering/displayUtils.js +0 -6
- package/src/app/rendering/expertSensorRenderer.js +1 -1
- package/src/app/rendering/replayGhostRenderer.js +4 -1
- package/src/app/rendering/trackRenderer.js +2 -2
- package/src/app/runtime/frameLoop.js +18 -17
- package/src/app/runtime/pixiStartupLifecycle.js +94 -0
- package/src/app/runtime/runtimeTiming.js +4 -1
- package/src/app/runtime/runtimeVisibility.js +7 -2
- package/src/config/cssValues.js +1 -1
- package/src/config/defaultOptions.js +12 -173
- package/src/config/restartOptions.js +1 -0
- package/src/config/runtimeSeed.js +10 -0
- package/src/config/simulatorDefaults.js +106 -0
- package/src/config/telemetryModules.js +20 -0
- package/src/config/theme/cssTokens.js +85 -0
- package/src/config/theme/themeCss.js +72 -0
- package/src/config/theme/themeInputs.js +155 -0
- package/src/config/theme/themeResolver.js +172 -0
- package/src/config/theme/themeSelectors.js +69 -0
- package/src/config/theme/themeTokens.js +257 -0
- package/src/config/theme/themeUtils.js +15 -0
- package/src/config/themeOptions.js +14 -830
- package/src/config/timingGapMode.js +0 -2
- package/src/config/uiOptions.js +53 -0
- package/src/data/championship.js +16 -158
- package/src/data/championshipDefaults.js +87 -0
- package/src/data/driverData.js +10 -20
- package/src/data/entryIdentity.js +67 -0
- package/src/data/entryPresentation.js +23 -0
- package/src/data/entryTypes.d.ts +160 -0
- package/src/data/index.d.ts +12 -195
- package/src/data/ratingScale.js +14 -0
- package/src/data/teamTypes.d.ts +23 -0
- package/src/data/trackTypes.d.ts +101 -0
- package/src/data/vehicleData.js +12 -23
- package/src/environment/actions.js +3 -2
- package/src/environment/controllerLoop.js +57 -44
- package/src/environment/controllerScheduler.js +30 -0
- package/src/environment/driverObservations.js +260 -0
- package/src/environment/driverReset.js +51 -0
- package/src/environment/episode.js +0 -1
- package/src/environment/evaluation.js +6 -90
- package/src/environment/evaluationTracker.js +100 -0
- package/src/environment/events.js +23 -0
- package/src/environment/index.d.ts +35 -91
- package/src/environment/metrics.js +3 -17
- package/src/environment/observationSchema.js +104 -0
- package/src/environment/observationVector.js +16 -115
- package/src/environment/observations.js +16 -316
- package/src/environment/options.js +5 -24
- package/src/environment/renderFrames.js +21 -0
- package/src/environment/resetOptions.js +41 -0
- package/src/environment/results.js +125 -0
- package/src/environment/rewards.js +10 -16
- package/src/environment/runtime.js +17 -300
- package/src/environment/scenarioOptions.js +90 -0
- package/src/environment/scenarioPlacements.js +70 -0
- package/src/environment/scenarioPresets.js +78 -0
- package/src/environment/scenarios.js +5 -236
- package/src/environment/scratch.js +18 -0
- package/src/environment/sensorOptions.js +20 -0
- package/src/environment/sensors/index.js +2 -556
- package/src/environment/sensors/indexedRayBands.js +0 -18
- package/src/environment/sensors/nearbyCars.js +18 -15
- package/src/environment/sensors/opponentRadar.js +3 -2
- package/src/environment/sensors/pitConnectorProximity.js +20 -0
- package/src/environment/sensors/rayBandTrace.js +1 -37
- package/src/environment/sensors/rayBatch.js +81 -0
- package/src/environment/sensors/rayConfig.js +26 -0
- package/src/environment/sensors/rayGeometry.js +0 -8
- package/src/environment/sensors/rayGuards.js +0 -6
- package/src/environment/sensors/rayOutputs.js +75 -0
- package/src/environment/sensors/rayQuery.js +110 -0
- package/src/environment/sensors/rayScratch.js +146 -0
- package/src/environment/sensors/sensorTargets.js +12 -4
- package/src/environment/sensors/surfaceRays.js +6 -61
- package/src/environment/sensors/trackRays.js +7 -80
- package/src/environment/simulationSetup.js +30 -0
- package/src/environment/specs.js +15 -109
- package/src/index.d.ts +43 -202
- package/src/index.js +28 -6
- package/src/placeholder/index.js +1 -8
- package/src/rendering/displayLifecycle.js +6 -0
- package/src/rendering/proceduralTrackAsset.js +1 -1
- package/src/rendering/renderSnapshot.js +3 -0
- package/src/rendering/track/finishLineRenderer.js +1 -1
- package/src/rendering/track/gridRenderer.js +1 -1
- package/src/rendering/track/offsetStrokeSafety.js +2 -2
- package/src/rendering/track/pitLaneRenderer.js +1 -1
- package/src/rendering/track/trackMaterialRenderer.js +4 -4
- package/src/rendering/track/trackRenderConstants.js +0 -1
- package/src/rendering/track/trackRenderGeometry.js +1 -7
- package/src/simulation/collisionGeometry.js +12 -20
- package/src/simulation/driver/advancedPathControls.js +53 -0
- package/src/simulation/driver/advancedRacePace.js +112 -0
- package/src/simulation/driver/advancedRacingControls.js +61 -0
- package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
- package/src/simulation/driver/advancedRacingSteering.js +76 -0
- package/src/simulation/driver/advancedRacingTiming.js +16 -0
- package/src/simulation/driver/arcadeRacePace.js +83 -0
- package/src/simulation/driver/arcadeRacingControls.js +111 -0
- package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
- package/src/simulation/driver/driverControlConstants.js +0 -2
- package/src/simulation/driver/driverController.js +16 -5
- package/src/simulation/driver/edgeRecovery.js +2 -3
- package/src/simulation/driver/racingControls.js +10 -396
- package/src/simulation/driver/racingDriverProfile.js +48 -0
- package/src/simulation/driver/racingLinePlan.js +54 -20
- package/src/simulation/driver/rejoinControls.js +59 -112
- package/src/simulation/driver/safetyCarControls.js +12 -0
- package/src/simulation/driver/trafficScan.js +136 -27
- package/src/simulation/participants/participantInteractions.js +24 -15
- package/src/simulation/pit/pitCarPlacement.js +29 -0
- package/src/simulation/pit/pitFlow.js +8 -30
- package/src/simulation/pit/pitIntent.js +4 -3
- package/src/simulation/pit/pitOccupancy.js +2 -2
- package/src/simulation/pit/pitQueue.js +2 -18
- package/src/simulation/pit/pitRouteMovement.js +1 -36
- package/src/simulation/pit/pitRouting.js +5 -81
- package/src/simulation/pit/pitService.js +3 -32
- package/src/simulation/pit/pitServiceConstants.js +1 -0
- package/src/simulation/pit/pitServiceProgress.js +34 -0
- package/src/simulation/pit/pitSnapshots.js +8 -4
- package/src/simulation/pit/pitState.js +1 -5
- package/src/simulation/race/classification.js +1 -8
- package/src/simulation/race/raceFinish.js +3 -8
- package/src/simulation/race/raceOrder.js +2 -13
- package/src/simulation/race/raceProgress.js +6 -20
- package/src/simulation/race/raceSetup.js +1 -1
- package/src/simulation/race/raceStep.js +20 -11
- package/src/simulation/race/redFlag.js +2 -4
- package/src/simulation/race/safetyCar.js +5 -9
- package/src/simulation/race/stalledDnf.js +2 -4
- package/src/simulation/raceSimulation.js +6 -38
- package/src/simulation/replay/replayGhosts.js +44 -32
- package/src/simulation/rules/collisionSteward.js +0 -14
- package/src/simulation/rules/penaltyActions.js +37 -0
- package/src/simulation/rules/penaltyLedger.js +8 -1
- package/src/simulation/rules/pitLaneSpeedingSteward.js +1 -1
- package/src/simulation/rules/rulesReview.js +20 -16
- package/src/simulation/rules/trackLimitsSteward.js +2 -2
- package/src/simulation/simMath.js +4 -0
- package/src/simulation/snapshots/raceSnapshots.js +30 -11
- package/src/simulation/timing/drsState.js +8 -0
- package/src/simulation/timing/drsTiming.js +2 -4
- package/src/simulation/timing/lapTelemetry.js +7 -4
- package/src/simulation/timing/sectorPerformance.js +14 -6
- package/src/simulation/timing/timingLines.js +12 -6
- package/src/simulation/track/pitLaneAccess.js +2 -2
- package/src/simulation/track/pitLaneLayout.js +4 -135
- package/src/simulation/track/pitLaneModel.js +70 -0
- package/src/simulation/track/pitLaneState.js +0 -1
- package/src/simulation/track/pitServiceLayout.js +121 -0
- package/src/simulation/track/proceduralTrack.js +77 -0
- package/src/simulation/track/spatialQueries.js +76 -31
- package/src/simulation/track/trackBuilder.js +54 -0
- package/src/simulation/track/trackConstants.js +0 -22
- package/src/simulation/track/trackMath.js +4 -29
- package/src/simulation/track/trackModel.js +12 -240
- package/src/simulation/track/trackModelCache.js +105 -0
- package/src/simulation/track/trackNearestQueries.js +1218 -0
- package/src/simulation/track/trackQueryConstants.js +3 -0
- package/src/simulation/track/trackQueryConstruction.js +368 -0
- package/src/simulation/track/trackQueryIndex.js +12 -1933
- package/src/simulation/track/trackQuerySegments.js +174 -0
- package/src/simulation/track/trackQueryStats.js +12 -0
- package/src/simulation/track/trackRayQueries.js +165 -0
- package/src/simulation/vehicle/advancedContactResponse.js +89 -0
- package/src/simulation/vehicle/advancedTireForces.js +92 -0
- package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
- package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
- package/src/simulation/vehicle/contactResolution.js +23 -9
- package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
- package/src/simulation/vehicle/pitWheelSurface.js +0 -12
- package/src/simulation/vehicle/runoffResponse.js +2 -4
- package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
- package/src/simulation/vehicle/vehicleGeometry.js +85 -103
- package/src/simulation/vehicle/vehicleKinematics.js +10 -0
- package/src/simulation/vehicle/vehiclePhysics.js +42 -196
- package/src/simulation/vehicle/vehicleSnapshots.js +18 -6
- package/src/simulation/vehicle/vehicleState.js +5 -3
- package/src/simulation/vehicle/wheelSurface.js +13 -432
- package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
- package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
- package/src/styles.css +173 -19
- package/src/ui/bannerTemplates.js +4 -2
- package/src/ui/cameraControlsTemplate.js +4 -2
- package/src/ui/gridWalkTemplate.js +29 -0
- package/src/ui/htmlEscaping.js +8 -0
- package/src/ui/raceCanvasTemplate.js +3 -2
- package/src/ui/shellTemplate.js +3 -0
- package/src/ui/telemetryDrawerTemplate.js +4 -2
- package/src/ui/telemetryTemplates.js +3 -22
- package/src/ui/templateUtils.js +2 -8
- package/src/ui/viewControlsTemplate.js +21 -0
- package/examples/train-basic-policy.mjs +0 -276
- package/examples/trainingData.mjs +0 -133
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from '../timing/drsState.js';
|
|
1
2
|
import { pointAt } from '../track/trackModel.js';
|
|
2
3
|
import { applyWheelSurfaceState } from '../vehicle/wheelSurface.js';
|
|
3
4
|
import {
|
|
@@ -30,10 +31,7 @@ export function refreshLocalRaceStateForSimulation(sim) {
|
|
|
30
31
|
stabilityState: car.destroyed ? 'destroyed' : car.stabilityState,
|
|
31
32
|
});
|
|
32
33
|
car.canAttack = false;
|
|
33
|
-
car
|
|
34
|
-
car.drsActive = false;
|
|
35
|
-
car.drsZoneId = null;
|
|
36
|
-
car.drsZoneEnabled = false;
|
|
34
|
+
clearCurrentDrsState(car);
|
|
37
35
|
return;
|
|
38
36
|
}
|
|
39
37
|
const centerState = takeValidRunoffCenterState(car);
|
|
@@ -98,10 +96,7 @@ export function recalculateRaceStateForSimulation(sim, { updateDrs = true, refre
|
|
|
98
96
|
stabilityState: car.destroyed ? 'destroyed' : car.stabilityState,
|
|
99
97
|
});
|
|
100
98
|
car.canAttack = false;
|
|
101
|
-
car
|
|
102
|
-
car.drsActive = false;
|
|
103
|
-
car.drsZoneId = null;
|
|
104
|
-
car.drsZoneEnabled = false;
|
|
99
|
+
clearCurrentDrsState(car);
|
|
105
100
|
recordTimingStateForCar(car, sim.time, sim.track, previousRaceDistanceForTiming, sim.runtimeBenchmarkStats);
|
|
106
101
|
return;
|
|
107
102
|
}
|
|
@@ -149,10 +144,7 @@ export function recalculateRaceStateForSimulation(sim, { updateDrs = true, refre
|
|
|
149
144
|
car.gapAheadSeconds = Infinity;
|
|
150
145
|
car.intervalAheadSeconds = Infinity;
|
|
151
146
|
car.leaderGapSeconds = Infinity;
|
|
152
|
-
car
|
|
153
|
-
car.drsActive = false;
|
|
154
|
-
car.drsZoneId = null;
|
|
155
|
-
car.drsZoneEnabled = false;
|
|
147
|
+
clearCurrentDrsState(car);
|
|
156
148
|
car.canAttack = false;
|
|
157
149
|
});
|
|
158
150
|
} else if (sim?.runtimeBenchmarkStats) {
|
|
@@ -175,10 +167,7 @@ export function recalculateRaceStateForSimulation(sim, { updateDrs = true, refre
|
|
|
175
167
|
car.gapAheadSeconds = Infinity;
|
|
176
168
|
car.intervalAheadSeconds = Infinity;
|
|
177
169
|
car.leaderGapSeconds = Infinity;
|
|
178
|
-
car
|
|
179
|
-
car.drsActive = false;
|
|
180
|
-
car.drsZoneId = null;
|
|
181
|
-
car.drsZoneEnabled = false;
|
|
170
|
+
clearCurrentDrsState(car);
|
|
182
171
|
car.canAttack = false;
|
|
183
172
|
return;
|
|
184
173
|
}
|
|
@@ -210,10 +199,7 @@ export function recalculateRaceStateForSimulation(sim, { updateDrs = true, refre
|
|
|
210
199
|
car.canAttack = !sim.safetyCar.deployed && !car.finished && !activePitStop;
|
|
211
200
|
car.aggression = sim.computeAggression(car, index, aggressionFieldDepth);
|
|
212
201
|
if (activePitStop) {
|
|
213
|
-
car
|
|
214
|
-
car.drsActive = false;
|
|
215
|
-
car.drsZoneId = null;
|
|
216
|
-
car.drsZoneEnabled = false;
|
|
202
|
+
clearCurrentDrsState(car);
|
|
217
203
|
} else if (updateDrs) sim.updateDrsLatch(car, drsReference, Boolean(drsReference));
|
|
218
204
|
});
|
|
219
205
|
sim.evaluateRaceFinish(ordered);
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '../timing/raceTiming.js';
|
|
14
14
|
import { normalizePitIntent, PIT_INTENT_NONE } from '../pit/pitIntent.js';
|
|
15
15
|
import { assignPitLaneTeams, initializePitStops } from '../pit/pitState.js';
|
|
16
|
-
import { clonePitLaneModel } from '../
|
|
16
|
+
import { clonePitLaneModel } from '../track/pitLaneModel.js';
|
|
17
17
|
import { createRaceControlState, createSafetyCarState } from './raceControlState.js';
|
|
18
18
|
import { normalizeTotalLaps } from './raceDistance.js';
|
|
19
19
|
import { normalizeRaceRules } from '../rulesConfig.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { decideDriverControls } from '../driverController.js';
|
|
1
|
+
import { decideDriverControls } from '../driver/driverController.js';
|
|
2
2
|
import { clamp } from '../simMath.js';
|
|
3
3
|
import { updateReplayGhosts } from '../replay/replayGhosts.js';
|
|
4
|
-
import { integrateVehiclePhysics } from '../vehicle/vehiclePhysics.js';
|
|
4
|
+
import { integrateVehiclePhysics, isSimulatorPhysicsMode } from '../vehicle/vehiclePhysics.js';
|
|
5
5
|
import { applyWheelSurfaceState } from '../vehicle/wheelSurface.js';
|
|
6
6
|
import { applyRedFlagHoldForSimulation } from './redFlag.js';
|
|
7
7
|
import { freezeRetiredCar, updateStalledDnfForSimulation } from './stalledDnf.js';
|
|
@@ -38,19 +38,28 @@ export function runRaceStep(simulation, dt) {
|
|
|
38
38
|
|
|
39
39
|
const orderedCars = simulation.orderedCars();
|
|
40
40
|
const raceContext = simulation.driverRaceContext(orderedCars);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
for (let index = 0; index < simulation.cars.length; index += 1) {
|
|
42
|
+
simulation.cars[index]._driveOrderIndex = -1;
|
|
43
|
+
}
|
|
44
|
+
for (let index = 0; index < orderedCars.length; index += 1) {
|
|
45
|
+
orderedCars[index]._driveOrderIndex = index;
|
|
46
|
+
}
|
|
47
|
+
let driveCars = orderedCars;
|
|
48
|
+
if (orderedCars.length !== simulation.cars.length) {
|
|
49
|
+
driveCars = simulation._driveCarsScratch ??= [];
|
|
50
|
+
driveCars.length = 0;
|
|
51
|
+
driveCars.push(...orderedCars);
|
|
52
|
+
for (let index = 0; index < simulation.cars.length; index += 1) {
|
|
53
|
+
const car = simulation.cars[index];
|
|
54
|
+
if (car._driveOrderIndex === -1) driveCars.push(car);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
48
57
|
driveCars.forEach((car) => {
|
|
49
58
|
if (car.destroyed || car.outOfRace) {
|
|
50
59
|
freezeRetiredCar(car, { physicsMode: simulation.physicsMode });
|
|
51
60
|
return;
|
|
52
61
|
}
|
|
53
|
-
const orderIndex =
|
|
62
|
+
const orderIndex = car._driveOrderIndex >= 0 ? car._driveOrderIndex : undefined;
|
|
54
63
|
car.previousX = car.x;
|
|
55
64
|
car.previousY = car.y;
|
|
56
65
|
car.previousHeading = car.heading;
|
|
@@ -66,7 +75,7 @@ export function runRaceStep(simulation, dt) {
|
|
|
66
75
|
throttle: controls.throttle ?? 0,
|
|
67
76
|
brake: controls.brake ?? 0,
|
|
68
77
|
};
|
|
69
|
-
if (simulation.physicsMode
|
|
78
|
+
if (isSimulatorPhysicsMode(simulation.physicsMode)) {
|
|
70
79
|
measureRuntimePhase(simulation, 'prePhysicsWheelSurface', () => {
|
|
71
80
|
applyWheelSurfaceState(car, simulation.track);
|
|
72
81
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from '../timing/drsState.js';
|
|
1
2
|
import { freezeVehicleMotion } from '../vehicle/vehicleKinematics.js';
|
|
2
3
|
|
|
3
4
|
export function applyRedFlagHoldForSimulation(sim) {
|
|
@@ -6,10 +7,7 @@ export function applyRedFlagHoldForSimulation(sim) {
|
|
|
6
7
|
car.previousY = car.y;
|
|
7
8
|
car.previousHeading = car.heading;
|
|
8
9
|
freezeVehicleMotion(car, { physicsMode: sim.physicsMode });
|
|
9
|
-
car
|
|
10
|
-
car.drsEligible = false;
|
|
11
|
-
car.drsZoneId = null;
|
|
12
|
-
car.drsZoneEnabled = false;
|
|
10
|
+
clearCurrentDrsState(car);
|
|
13
11
|
car.canAttack = false;
|
|
14
12
|
});
|
|
15
13
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from '../timing/drsState.js';
|
|
1
2
|
import { pointAt } from '../track/trackModel.js';
|
|
2
3
|
import { kphToSimSpeed } from '../units.js';
|
|
3
4
|
import { freezeVehicleMotion, syncVehicleVelocityToHeading } from '../vehicle/vehicleKinematics.js';
|
|
5
|
+
import { isSimulatorPhysicsMode } from '../vehicle/vehiclePhysics.js';
|
|
4
6
|
|
|
5
7
|
export function setSafetyCarState(sim, deployed) {
|
|
6
8
|
const next = Boolean(deployed);
|
|
@@ -20,10 +22,7 @@ export function setSafetyCarState(sim, deployed) {
|
|
|
20
22
|
}
|
|
21
23
|
sim.cars.forEach((car) => {
|
|
22
24
|
car.desiredOffset = 0;
|
|
23
|
-
car
|
|
24
|
-
car.drsEligible = false;
|
|
25
|
-
car.drsZoneId = null;
|
|
26
|
-
car.drsZoneEnabled = false;
|
|
25
|
+
clearCurrentDrsState(car);
|
|
27
26
|
});
|
|
28
27
|
}
|
|
29
28
|
sim.events.unshift({ type: next ? 'safety-car' : 'green-flag', at: sim.time });
|
|
@@ -62,10 +61,7 @@ export function setRedFlagState(sim, deployed) {
|
|
|
62
61
|
sim.raceControl.frozenOrder = sim.orderedCars().map((car) => car.id);
|
|
63
62
|
sim.cars.forEach((car) => {
|
|
64
63
|
freezeVehicleMotion(car, { physicsMode: sim.physicsMode });
|
|
65
|
-
car
|
|
66
|
-
car.drsEligible = false;
|
|
67
|
-
car.drsZoneId = null;
|
|
68
|
-
car.drsZoneEnabled = false;
|
|
64
|
+
clearCurrentDrsState(car);
|
|
69
65
|
car.canAttack = false;
|
|
70
66
|
});
|
|
71
67
|
} else {
|
|
@@ -82,7 +78,7 @@ export function setRedFlagState(sim, deployed) {
|
|
|
82
78
|
});
|
|
83
79
|
} else if (!car.finished) {
|
|
84
80
|
car.speed = Math.max(car.speed, kphToSimSpeed(60));
|
|
85
|
-
if (sim.physicsMode
|
|
81
|
+
if (isSimulatorPhysicsMode(sim.physicsMode)) syncVehicleVelocityToHeading(car);
|
|
86
82
|
car.brake = 0;
|
|
87
83
|
car.throttle = Math.max(car.throttle ?? 0, 0.35);
|
|
88
84
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from '../timing/drsState.js';
|
|
1
2
|
import { markCarDnf } from './retirements.js';
|
|
2
3
|
import { simSpeedToKph } from '../units.js';
|
|
3
4
|
import { freezeVehicleMotion } from '../vehicle/vehicleKinematics.js';
|
|
@@ -66,10 +67,7 @@ export function freezeRetiredCar(car, { physicsMode = 'arcade' } = {}) {
|
|
|
66
67
|
stabilityState: 'destroyed',
|
|
67
68
|
});
|
|
68
69
|
car.canAttack = false;
|
|
69
|
-
car
|
|
70
|
-
car.drsEligible = false;
|
|
71
|
-
car.drsZoneId = null;
|
|
72
|
-
car.drsZoneEnabled = false;
|
|
70
|
+
clearCurrentDrsState(car);
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
function resetStallTimer(car) {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applyUnservedServicePenalty,
|
|
3
|
-
cancelPenaltyRecord,
|
|
4
|
-
createPenaltyEvent,
|
|
5
|
-
createPenaltyRecord,
|
|
6
|
-
servePenaltyRecord,
|
|
7
3
|
} from './rules/penaltyLedger.js';
|
|
8
|
-
import {
|
|
4
|
+
import { recordPenaltyForSimulation, servePenaltyForSimulation, cancelPenaltyForSimulation } from './rules/penaltyActions.js';
|
|
9
5
|
import { buildPenaltyStatsByDriver, getPenaltyStats } from './rules/penaltyStats.js';
|
|
10
6
|
import {
|
|
11
7
|
reviewCollisionForSimulation,
|
|
@@ -93,7 +89,6 @@ import { applyGridDropForSimulation } from './race/gridPenalties.js';
|
|
|
93
89
|
import { initializeRaceSimulation, normalizePitIntentForRace } from './race/raceSetup.js';
|
|
94
90
|
import { runRaceStep } from './race/raceStep.js';
|
|
95
91
|
import {
|
|
96
|
-
createVehicleSnapshotDependencies,
|
|
97
92
|
getRaceWinnerSnapshot as getRaceWinnerSnapshotForSimulation,
|
|
98
93
|
snapshotRace,
|
|
99
94
|
snapshotRaceObservation,
|
|
@@ -209,7 +204,7 @@ export class F1RaceSimulation {
|
|
|
209
204
|
}
|
|
210
205
|
|
|
211
206
|
isCarInActivePitStop(car) {
|
|
212
|
-
return isCarInActivePitStopForSimulation(
|
|
207
|
+
return isCarInActivePitStopForSimulation(car);
|
|
213
208
|
}
|
|
214
209
|
|
|
215
210
|
schedulePitStopAtNextEntry(car, stop) {
|
|
@@ -249,7 +244,7 @@ export class F1RaceSimulation {
|
|
|
249
244
|
}
|
|
250
245
|
|
|
251
246
|
isPitServiceAreaOccupied(candidate, box) {
|
|
252
|
-
return isPitServiceAreaOccupiedForSimulation(
|
|
247
|
+
return isPitServiceAreaOccupiedForSimulation(candidate, box);
|
|
253
248
|
}
|
|
254
249
|
|
|
255
250
|
isPitServiceQueueOccupied(car, box) {
|
|
@@ -317,38 +312,15 @@ export class F1RaceSimulation {
|
|
|
317
312
|
}
|
|
318
313
|
|
|
319
314
|
recordPenalty(penalty) {
|
|
320
|
-
|
|
321
|
-
const entry = createPenaltyRecord({
|
|
322
|
-
sequence: this.nextPenaltyId,
|
|
323
|
-
time: this.time,
|
|
324
|
-
lap: this.computeLap(car?.raceDistance ?? 0),
|
|
325
|
-
penalty,
|
|
326
|
-
});
|
|
327
|
-
this.nextPenaltyId += 1;
|
|
328
|
-
this.penalties.push(entry);
|
|
329
|
-
if (entry.gridDrop > 0 && this.raceControl.mode === 'pre-start') {
|
|
330
|
-
this.applyGridDrop(entry.driverId, entry.gridDrop);
|
|
331
|
-
}
|
|
332
|
-
this.events.unshift(createPenaltyEvent(entry));
|
|
333
|
-
return entry;
|
|
315
|
+
return recordPenaltyForSimulation(this, penalty);
|
|
334
316
|
}
|
|
335
317
|
|
|
336
318
|
servePenalty(penaltyId) {
|
|
337
|
-
|
|
338
|
-
const result = servePenaltyRecord(penalty, this.time);
|
|
339
|
-
if (result) {
|
|
340
|
-
this.events.unshift(createPenaltyServedEvent(result, this.time));
|
|
341
|
-
}
|
|
342
|
-
return result;
|
|
319
|
+
return servePenaltyForSimulation(this, penaltyId);
|
|
343
320
|
}
|
|
344
321
|
|
|
345
322
|
cancelPenalty(penaltyId) {
|
|
346
|
-
|
|
347
|
-
const result = cancelPenaltyRecord(penalty, this.time);
|
|
348
|
-
if (result) {
|
|
349
|
-
this.events.unshift(createPenaltyCancelledEvent(result, this.time));
|
|
350
|
-
}
|
|
351
|
-
return result;
|
|
323
|
+
return cancelPenaltyForSimulation(this, penaltyId);
|
|
352
324
|
}
|
|
353
325
|
|
|
354
326
|
getDriverPenaltySeconds(driverId) {
|
|
@@ -389,10 +361,6 @@ export class F1RaceSimulation {
|
|
|
389
361
|
reviewPitLaneSpeedingForSimulation(this);
|
|
390
362
|
}
|
|
391
363
|
|
|
392
|
-
vehicleSnapshotDependencies() {
|
|
393
|
-
return createVehicleSnapshotDependencies(this);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
364
|
snapshot() {
|
|
397
365
|
return snapshotRace(this);
|
|
398
366
|
}
|
|
@@ -11,25 +11,17 @@ export function normalizeReplayGhosts(replayGhosts = []) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function updateReplayGhosts(replayGhosts = [], timeSeconds = 0) {
|
|
14
|
-
replayGhosts.
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
x: sample.x,
|
|
26
|
-
y: sample.y,
|
|
27
|
-
heading: sample.heading,
|
|
28
|
-
speedKph: sample.speedKph,
|
|
29
|
-
progressMeters: sample.progressMeters,
|
|
30
|
-
timeSeconds,
|
|
31
|
-
});
|
|
32
|
-
});
|
|
14
|
+
for (let index = 0; index < replayGhosts.length; index += 1) {
|
|
15
|
+
const ghost = replayGhosts[index];
|
|
16
|
+
const previousX = ghost.x;
|
|
17
|
+
const previousY = ghost.y;
|
|
18
|
+
const previousHeading = ghost.heading;
|
|
19
|
+
interpolateTrajectoryInto(ghost, ghost.trajectory, timeSeconds);
|
|
20
|
+
ghost.previousX = Number.isFinite(previousX) ? previousX : ghost.x;
|
|
21
|
+
ghost.previousY = Number.isFinite(previousY) ? previousY : ghost.y;
|
|
22
|
+
ghost.previousHeading = Number.isFinite(previousHeading) ? previousHeading : ghost.heading;
|
|
23
|
+
ghost.timeSeconds = timeSeconds;
|
|
24
|
+
}
|
|
33
25
|
}
|
|
34
26
|
|
|
35
27
|
export function serializeReplayGhosts(replayGhosts = []) {
|
|
@@ -57,7 +49,7 @@ function normalizeReplayGhost(ghost, index) {
|
|
|
57
49
|
const trajectory = normalizeTrajectory(ghost.trajectory);
|
|
58
50
|
if (trajectory.length === 0) return null;
|
|
59
51
|
const id = String(ghost.id ?? `replay-ghost-${index + 1}`);
|
|
60
|
-
const initial =
|
|
52
|
+
const initial = interpolateTrajectoryInto({}, trajectory, 0);
|
|
61
53
|
return {
|
|
62
54
|
id,
|
|
63
55
|
label: String(ghost.label ?? id),
|
|
@@ -103,23 +95,43 @@ function normalizeTrajectory(trajectory) {
|
|
|
103
95
|
.sort((a, b) => a.timeSeconds - b.timeSeconds);
|
|
104
96
|
}
|
|
105
97
|
|
|
106
|
-
function
|
|
107
|
-
if (trajectory.length === 1 || timeSeconds <= trajectory[0].timeSeconds)
|
|
98
|
+
function interpolateTrajectoryInto(target, trajectory, timeSeconds) {
|
|
99
|
+
if (trajectory.length === 1 || timeSeconds <= trajectory[0].timeSeconds) {
|
|
100
|
+
return writeTrajectorySample(target, trajectory[0]);
|
|
101
|
+
}
|
|
108
102
|
const last = trajectory[trajectory.length - 1];
|
|
109
|
-
if (timeSeconds >= last.timeSeconds) return
|
|
110
|
-
const nextIndex = trajectory
|
|
103
|
+
if (timeSeconds >= last.timeSeconds) return writeTrajectorySample(target, last);
|
|
104
|
+
const nextIndex = lowerBoundTrajectoryTime(trajectory, timeSeconds);
|
|
111
105
|
const previous = trajectory[Math.max(0, nextIndex - 1)];
|
|
112
106
|
const next = trajectory[nextIndex];
|
|
113
107
|
const span = next.timeSeconds - previous.timeSeconds || 1;
|
|
114
108
|
const amount = clamp((timeSeconds - previous.timeSeconds) / span, 0, 1);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
target.x = lerp(previous.x, next.x, amount);
|
|
110
|
+
target.y = lerp(previous.y, next.y, amount);
|
|
111
|
+
target.heading = previous.heading + normalizeAngle(next.heading - previous.heading) * amount;
|
|
112
|
+
target.speedKph = lerp(previous.speedKph, next.speedKph, amount);
|
|
113
|
+
target.progressMeters = lerp(previous.progressMeters, next.progressMeters, amount);
|
|
114
|
+
return target;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function lowerBoundTrajectoryTime(trajectory, timeSeconds) {
|
|
118
|
+
let low = 0;
|
|
119
|
+
let high = trajectory.length;
|
|
120
|
+
while (low < high) {
|
|
121
|
+
const middle = Math.floor((low + high) / 2);
|
|
122
|
+
if (trajectory[middle].timeSeconds < timeSeconds) low = middle + 1;
|
|
123
|
+
else high = middle;
|
|
124
|
+
}
|
|
125
|
+
return low;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function writeTrajectorySample(target, sample) {
|
|
129
|
+
target.x = sample.x;
|
|
130
|
+
target.y = sample.y;
|
|
131
|
+
target.heading = sample.heading;
|
|
132
|
+
target.speedKph = sample.speedKph;
|
|
133
|
+
target.progressMeters = sample.progressMeters;
|
|
134
|
+
return target;
|
|
123
135
|
}
|
|
124
136
|
|
|
125
137
|
function finiteNumber(value) {
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import { simSpeedToKph } from '../units.js';
|
|
2
2
|
|
|
3
|
-
export function calculateCollisionPenalties({ first, second, collision = null, rule }) {
|
|
4
|
-
const penalties = [];
|
|
5
|
-
emitCollisionPenalties({
|
|
6
|
-
first,
|
|
7
|
-
second,
|
|
8
|
-
collision,
|
|
9
|
-
rule,
|
|
10
|
-
emit: (penalty) => {
|
|
11
|
-
penalties.push(penalty);
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
return penalties;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
3
|
export function emitCollisionPenalties({ first, second, collision = null, rule, emit, emitContext = null }) {
|
|
18
4
|
if (!rule || typeof emit !== 'function') return 0;
|
|
19
5
|
const severity = collision?.depth ?? 0;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { cancelPenaltyRecord, createPenaltyEvent, createPenaltyRecord, servePenaltyRecord } from './penaltyLedger.js';
|
|
2
|
+
import { createPenaltyCancelledEvent, createPenaltyServedEvent } from './penaltyEvents.js';
|
|
3
|
+
|
|
4
|
+
export function recordPenaltyForSimulation(sim, penalty) {
|
|
5
|
+
const car = sim.cars.find((item) => item.id === penalty.driverId);
|
|
6
|
+
const entry = createPenaltyRecord({
|
|
7
|
+
sequence: sim.nextPenaltyId,
|
|
8
|
+
time: sim.time,
|
|
9
|
+
lap: sim.computeLap(car?.raceDistance ?? 0),
|
|
10
|
+
penalty,
|
|
11
|
+
});
|
|
12
|
+
sim.nextPenaltyId += 1;
|
|
13
|
+
sim.penalties.push(entry);
|
|
14
|
+
if (entry.gridDrop > 0 && sim.raceControl.mode === 'pre-start') {
|
|
15
|
+
sim.applyGridDrop(entry.driverId, entry.gridDrop);
|
|
16
|
+
}
|
|
17
|
+
sim.events.unshift(createPenaltyEvent(entry));
|
|
18
|
+
return entry;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function servePenaltyForSimulation(sim, penaltyId) {
|
|
22
|
+
const penalty = sim.penalties.find((entry) => entry.id === penaltyId);
|
|
23
|
+
const result = servePenaltyRecord(penalty, sim.time);
|
|
24
|
+
if (result) {
|
|
25
|
+
sim.events.unshift(createPenaltyServedEvent(result, sim.time));
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function cancelPenaltyForSimulation(sim, penaltyId) {
|
|
31
|
+
const penalty = sim.penalties.find((entry) => entry.id === penaltyId);
|
|
32
|
+
const result = cancelPenaltyRecord(penalty, sim.time);
|
|
33
|
+
if (result) {
|
|
34
|
+
sim.events.unshift(createPenaltyCancelledEvent(result, sim.time));
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
@@ -48,7 +48,14 @@ export function createPenaltyEvent(entry) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function serializePenalty(penalty) {
|
|
51
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
...penalty,
|
|
53
|
+
...(Array.isArray(penalty.consequences) ? {
|
|
54
|
+
consequences: penalty.consequences.map((consequence) => ({ ...consequence })),
|
|
55
|
+
} : {}),
|
|
56
|
+
...(Array.isArray(penalty.usedCompounds) ? { usedCompounds: [...penalty.usedCompounds] } : {}),
|
|
57
|
+
...(Array.isArray(penalty.wheelOffsets) ? { wheelOffsets: [...penalty.wheelOffsets] } : {}),
|
|
58
|
+
};
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
export function servePenaltyRecord(penalty, time) {
|
|
@@ -20,7 +20,7 @@ export function calculatePitLaneSpeedingReview({ car, rule, stewardState }) {
|
|
|
20
20
|
|
|
21
21
|
if (!isViolation) {
|
|
22
22
|
return {
|
|
23
|
-
nextState: { ...current, active: false },
|
|
23
|
+
nextState: current.active === false ? current : { ...current, active: false },
|
|
24
24
|
event: null,
|
|
25
25
|
penalty: null,
|
|
26
26
|
};
|
|
@@ -5,6 +5,7 @@ import { calculatePitLaneSpeedingReview } from './pitLaneSpeedingSteward.js';
|
|
|
5
5
|
import { getPenaltyRule } from '../rulesConfig.js';
|
|
6
6
|
import { simSpeedToKph } from '../units.js';
|
|
7
7
|
import { VEHICLE_LIMITS } from '../vehicle/vehiclePhysics.js';
|
|
8
|
+
import { progressDelta } from '../race/raceDistance.js';
|
|
8
9
|
|
|
9
10
|
function velocityComponentX(car) {
|
|
10
11
|
return Number.isFinite(car.velocityX) && Number.isFinite(car.velocityY)
|
|
@@ -18,13 +19,6 @@ function velocityComponentY(car) {
|
|
|
18
19
|
: Math.sin(car.heading) * car.speed;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
function progressDelta(a, b, trackLength) {
|
|
22
|
-
let delta = a - b;
|
|
23
|
-
if (delta < -trackLength / 2) delta += trackLength;
|
|
24
|
-
if (delta > trackLength / 2) delta -= trackLength;
|
|
25
|
-
return delta;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
22
|
function prepareCollisionStewardContext(scratch = null) {
|
|
29
23
|
const target = scratch
|
|
30
24
|
? (scratch.collisionStewardContext ??= { sharedFaultDriverIds: [] })
|
|
@@ -129,7 +123,9 @@ export function reviewTrackLimitsForSimulation(sim) {
|
|
|
129
123
|
sim.cars.forEach((car) => {
|
|
130
124
|
if (isLegallyInsidePitLaneForTrackLimits(car)) {
|
|
131
125
|
const current = sim.stewardState.trackLimits[car.id];
|
|
132
|
-
|
|
126
|
+
if (!current || current.active !== false) {
|
|
127
|
+
sim.stewardState.trackLimits[car.id] = { ...(current ?? { violations: 0 }), active: false };
|
|
128
|
+
}
|
|
133
129
|
return;
|
|
134
130
|
}
|
|
135
131
|
|
|
@@ -146,18 +142,26 @@ export function reviewTrackLimitsForSimulation(sim) {
|
|
|
146
142
|
});
|
|
147
143
|
}
|
|
148
144
|
|
|
145
|
+
const PIT_SPEED_REVIEW_SCRATCH = {
|
|
146
|
+
car: { id: null, speedKph: 0, trackState: null, pitLanePart: null },
|
|
147
|
+
rule: null,
|
|
148
|
+
stewardState: null,
|
|
149
|
+
};
|
|
150
|
+
|
|
149
151
|
export function reviewPitLaneSpeedingForSimulation(sim) {
|
|
150
152
|
const rule = getPenaltyRule(sim.rules, 'pitLaneSpeeding');
|
|
151
153
|
sim.cars.forEach((car) => {
|
|
152
154
|
const currentState = sim.stewardState.pitLaneSpeeding[car.id];
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
// The steward only reads the speed and pit-lane location, so feed it a
|
|
156
|
+
// pooled view instead of spreading the whole car every step.
|
|
157
|
+
const reviewCar = PIT_SPEED_REVIEW_SCRATCH.car;
|
|
158
|
+
reviewCar.id = car.id;
|
|
159
|
+
reviewCar.speedKph = simSpeedToKph(car.speed);
|
|
160
|
+
reviewCar.trackState = car.trackState;
|
|
161
|
+
reviewCar.pitLanePart = car.pitLanePart;
|
|
162
|
+
PIT_SPEED_REVIEW_SCRATCH.rule = rule;
|
|
163
|
+
PIT_SPEED_REVIEW_SCRATCH.stewardState = currentState;
|
|
164
|
+
const review = calculatePitLaneSpeedingReview(PIT_SPEED_REVIEW_SCRATCH);
|
|
161
165
|
sim.stewardState.pitLaneSpeeding[car.id] = review.nextState;
|
|
162
166
|
if (review.event) sim.events.unshift({ ...review.event, at: sim.time });
|
|
163
167
|
if (review.penalty) sim.recordPenalty(review.penalty);
|
|
@@ -22,7 +22,7 @@ export function calculateTrackLimitReview({ car, rule, track, stewardState }) {
|
|
|
22
22
|
const wheelStates = wheelSurfaceState.wheels;
|
|
23
23
|
if (wheelStates.some((wheelState) => wheelState.inPitLane)) {
|
|
24
24
|
return {
|
|
25
|
-
nextState: { ...current, active: false },
|
|
25
|
+
nextState: current.active === false ? current : { ...current, active: false },
|
|
26
26
|
event: null,
|
|
27
27
|
penalty: null,
|
|
28
28
|
};
|
|
@@ -34,7 +34,7 @@ export function calculateTrackLimitReview({ car, rule, track, stewardState }) {
|
|
|
34
34
|
|
|
35
35
|
if (!isViolation) {
|
|
36
36
|
return {
|
|
37
|
-
nextState: { ...current, active: false },
|
|
37
|
+
nextState: current.active === false ? current : { ...current, active: false },
|
|
38
38
|
event: null,
|
|
39
39
|
penalty: null,
|
|
40
40
|
};
|
|
@@ -38,12 +38,8 @@ export function createVehicleSnapshotDependencies(sim) {
|
|
|
38
38
|
normalizePitIntent,
|
|
39
39
|
PIT_INTENT_NONE,
|
|
40
40
|
serializeLapTelemetry,
|
|
41
|
-
serializeObservationPitStop
|
|
42
|
-
|
|
43
|
-
normalizePitIntent,
|
|
44
|
-
PIT_INTENT_NONE,
|
|
45
|
-
),
|
|
46
|
-
serializePitStop: (pitStop) => serializePitStop(pitStop, normalizePitIntent, PIT_INTENT_NONE),
|
|
41
|
+
serializeObservationPitStop,
|
|
42
|
+
serializePitStop,
|
|
47
43
|
serializeRenderPitStop,
|
|
48
44
|
simSpeedToKph,
|
|
49
45
|
simUnitsToMeters,
|
|
@@ -220,11 +216,34 @@ function carsForSnapshot(sim, ordered) {
|
|
|
220
216
|
return [...ranked, ...unranked];
|
|
221
217
|
}
|
|
222
218
|
|
|
219
|
+
const RENDER_CAR_ORDER_SCRATCH = new WeakMap();
|
|
220
|
+
|
|
223
221
|
function renderCarsForSnapshotInto(targetCars, sim, ordered, dependencies) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
222
|
+
let scratch = RENDER_CAR_ORDER_SCRATCH.get(targetCars);
|
|
223
|
+
if (!scratch) {
|
|
224
|
+
scratch = { orderedIds: new Set(), unranked: [] };
|
|
225
|
+
RENDER_CAR_ORDER_SCRATCH.set(targetCars, scratch);
|
|
226
|
+
}
|
|
227
|
+
const { orderedIds, unranked } = scratch;
|
|
228
|
+
orderedIds.clear();
|
|
229
|
+
unranked.length = 0;
|
|
230
|
+
let count = 0;
|
|
231
|
+
for (const car of ordered) {
|
|
232
|
+
orderedIds.add(car.id);
|
|
233
|
+
targetCars[count] = serializeRenderCarInto(targetCars[count] ?? {}, car, dependencies);
|
|
234
|
+
count += 1;
|
|
235
|
+
}
|
|
236
|
+
for (const car of sim.cars) {
|
|
237
|
+
if (!orderedIds.has(car.id)) unranked.push(car);
|
|
238
|
+
}
|
|
239
|
+
unranked.sort((a, b) => a.index - b.index);
|
|
240
|
+
for (const car of unranked) {
|
|
241
|
+
targetCars[count] = serializeRenderCarInto(targetCars[count] ?? {}, car, dependencies);
|
|
242
|
+
count += 1;
|
|
243
|
+
}
|
|
244
|
+
targetCars.length = count;
|
|
245
|
+
// Scratch owns ordering only; do not retain live participants between frames.
|
|
246
|
+
orderedIds.clear();
|
|
247
|
+
unranked.length = 0;
|
|
229
248
|
return targetCars;
|
|
230
249
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Suppress the current latch without erasing passage history or next-zone hints.
|
|
2
|
+
// External placement uses the separate full reset in vehicleState.js.
|
|
3
|
+
export function clearCurrentDrsState(car) {
|
|
4
|
+
car.drsActive = false;
|
|
5
|
+
car.drsEligible = false;
|
|
6
|
+
car.drsZoneId = null;
|
|
7
|
+
car.drsZoneEnabled = false;
|
|
8
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from './drsState.js';
|
|
1
2
|
import { wrapDistance } from '../simMath.js';
|
|
2
3
|
|
|
3
4
|
function wrapProgress(value, length) {
|
|
@@ -106,10 +107,7 @@ function updateDrsLatchCore(
|
|
|
106
107
|
runtimeBenchmarkStats = null,
|
|
107
108
|
) {
|
|
108
109
|
if (safetyCarDeployed || car.finished) {
|
|
109
|
-
car
|
|
110
|
-
car.drsActive = false;
|
|
111
|
-
car.drsZoneId = null;
|
|
112
|
-
car.drsZoneEnabled = false;
|
|
110
|
+
clearCurrentDrsState(car);
|
|
113
111
|
car._drsZoneRef = null;
|
|
114
112
|
return;
|
|
115
113
|
}
|