@inventure71/paddockjs 4.2.0 → 4.3.1

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.
Files changed (249) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/INSTALL_AND_UPDATE.md +24 -4
  3. package/README.md +31 -2
  4. package/docs/advanced_ai_pace.md +131 -0
  5. package/docs/advanced_physics_rebuild.md +166 -0
  6. package/docs/architecture.md +109 -30
  7. package/docs/cleanup_coverage.md +284 -0
  8. package/docs/component_inventory.md +31 -15
  9. package/docs/composable-layouts.md +2 -0
  10. package/docs/concepts.md +12 -1
  11. package/docs/custom_model_controller.md +1 -1
  12. package/docs/data_contract.md +33 -10
  13. package/docs/data_model.md +4 -2
  14. package/docs/efficiency_cleanup_2026-10-13.md +80 -0
  15. package/docs/grid_walk.md +84 -0
  16. package/docs/index.md +1 -0
  17. package/docs/keyboard_driving_fix.md +118 -0
  18. package/docs/limit_driving_2026-10-14.md +88 -0
  19. package/docs/maintainability_audit_2026-09-08.md +146 -0
  20. package/docs/racing_ai_improvements.md +145 -0
  21. package/docs/rules.md +43 -13
  22. package/docs/sense_contract.md +4 -1
  23. package/docs/system_specs.md +38 -11
  24. package/docs/testing-reliability-guide.md +5 -0
  25. package/docs/v11_improvements.md +108 -0
  26. package/package.json +24 -13
  27. package/src/api/MountedSimulatorComponents.js +101 -0
  28. package/src/api/PaddockSimulatorController.js +56 -81
  29. package/src/app/BrowserExpertAdapter.js +0 -6
  30. package/src/app/BrowserExpertExternalRenderer.js +72 -50
  31. package/src/app/ExternalTrackSnapshot.js +50 -0
  32. package/src/app/F1SimulatorApp.js +242 -297
  33. package/src/app/HostLifecycleEmitter.js +93 -0
  34. package/src/app/banners/raceDataBannerController.js +9 -4
  35. package/src/app/camera/cameraConstants.js +1 -1
  36. package/src/app/camera/cameraController.js +10 -13
  37. package/src/app/camera/cameraGeometry.js +1 -1
  38. package/src/app/controls/SimulatorViewControls.js +45 -0
  39. package/src/app/controls/workbenchBindings.js +64 -0
  40. package/src/app/controls/workbenchDisclosure.js +41 -0
  41. package/src/app/domBindings.js +21 -13
  42. package/src/app/gridWalk/GridWalkController.js +180 -0
  43. package/src/app/gridWalk/gridWalkCamera.js +88 -0
  44. package/src/app/gridWalk/gridWalkControls.js +24 -0
  45. package/src/app/gridWalk/gridWalkCrew.js +246 -0
  46. package/src/app/gridWalk/gridWalkLabels.js +104 -0
  47. package/src/app/gridWalk/gridWalkLayout.js +46 -0
  48. package/src/app/gridWalk/gridWalkNavigation.js +106 -0
  49. package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
  50. package/src/app/gridWalk/gridWalkSession.js +50 -0
  51. package/src/app/layoutSupport.js +2 -0
  52. package/src/app/raceOverlayClearanceSupport.js +21 -7
  53. package/src/app/readouts/carOverviewRenderer.js +9 -4
  54. package/src/app/readouts/readoutFormatters.js +1 -8
  55. package/src/app/readouts/telemetryRenderer.js +1 -3
  56. package/src/app/readouts/timingTowerRenderer.js +48 -50
  57. package/src/app/rendering/appAssets.js +8 -10
  58. package/src/app/rendering/carRenderer.js +27 -79
  59. package/src/app/rendering/displayUtils.js +0 -6
  60. package/src/app/rendering/expertSensorRenderer.js +1 -1
  61. package/src/app/rendering/replayGhostRenderer.js +4 -1
  62. package/src/app/rendering/trackRenderer.js +2 -2
  63. package/src/app/runtime/frameLoop.js +18 -17
  64. package/src/app/runtime/pixiStartupLifecycle.js +94 -0
  65. package/src/app/runtime/runtimeTiming.js +4 -1
  66. package/src/app/runtime/runtimeVisibility.js +7 -2
  67. package/src/config/cssValues.js +1 -1
  68. package/src/config/defaultOptions.js +12 -173
  69. package/src/config/restartOptions.js +1 -0
  70. package/src/config/runtimeSeed.js +10 -0
  71. package/src/config/simulatorDefaults.js +106 -0
  72. package/src/config/telemetryModules.js +20 -0
  73. package/src/config/theme/cssTokens.js +85 -0
  74. package/src/config/theme/themeCss.js +72 -0
  75. package/src/config/theme/themeInputs.js +155 -0
  76. package/src/config/theme/themeResolver.js +172 -0
  77. package/src/config/theme/themeSelectors.js +69 -0
  78. package/src/config/theme/themeTokens.js +257 -0
  79. package/src/config/theme/themeUtils.js +15 -0
  80. package/src/config/themeOptions.js +14 -830
  81. package/src/config/timingGapMode.js +0 -2
  82. package/src/config/uiOptions.js +53 -0
  83. package/src/data/championship.js +16 -158
  84. package/src/data/championshipDefaults.js +87 -0
  85. package/src/data/driverData.js +10 -20
  86. package/src/data/entryIdentity.js +67 -0
  87. package/src/data/entryPresentation.js +23 -0
  88. package/src/data/entryTypes.d.ts +160 -0
  89. package/src/data/index.d.ts +13 -259
  90. package/src/data/ratingScale.js +14 -0
  91. package/src/data/teamTypes.d.ts +23 -0
  92. package/src/data/trackTypes.d.ts +101 -0
  93. package/src/data/vehicleData.js +12 -23
  94. package/src/environment/actions.js +3 -2
  95. package/src/environment/controllerLoop.js +57 -44
  96. package/src/environment/controllerScheduler.js +30 -0
  97. package/src/environment/driverObservations.js +260 -0
  98. package/src/environment/driverReset.js +51 -0
  99. package/src/environment/episode.js +0 -1
  100. package/src/environment/evaluation.js +6 -90
  101. package/src/environment/evaluationTracker.js +100 -0
  102. package/src/environment/events.js +23 -0
  103. package/src/environment/index.d.ts +21 -140
  104. package/src/environment/metrics.js +3 -17
  105. package/src/environment/observationSchema.js +104 -0
  106. package/src/environment/observationVector.js +16 -115
  107. package/src/environment/observations.js +16 -316
  108. package/src/environment/options.js +5 -24
  109. package/src/environment/renderFrames.js +21 -0
  110. package/src/environment/resetOptions.js +41 -0
  111. package/src/environment/results.js +125 -0
  112. package/src/environment/rewards.js +10 -16
  113. package/src/environment/runtime.js +17 -300
  114. package/src/environment/scenarioOptions.js +90 -0
  115. package/src/environment/scenarioPlacements.js +70 -0
  116. package/src/environment/scenarioPresets.js +78 -0
  117. package/src/environment/scenarios.js +5 -236
  118. package/src/environment/scratch.js +18 -0
  119. package/src/environment/sensorOptions.js +20 -0
  120. package/src/environment/sensors/index.js +2 -556
  121. package/src/environment/sensors/indexedRayBands.js +0 -18
  122. package/src/environment/sensors/nearbyCars.js +18 -15
  123. package/src/environment/sensors/opponentRadar.js +3 -2
  124. package/src/environment/sensors/pitConnectorProximity.js +20 -0
  125. package/src/environment/sensors/rayBandTrace.js +1 -37
  126. package/src/environment/sensors/rayBatch.js +81 -0
  127. package/src/environment/sensors/rayConfig.js +26 -0
  128. package/src/environment/sensors/rayGeometry.js +0 -8
  129. package/src/environment/sensors/rayGuards.js +0 -6
  130. package/src/environment/sensors/rayOutputs.js +75 -0
  131. package/src/environment/sensors/rayQuery.js +110 -0
  132. package/src/environment/sensors/rayScratch.js +146 -0
  133. package/src/environment/sensors/sensorTargets.js +12 -4
  134. package/src/environment/sensors/surfaceRays.js +6 -61
  135. package/src/environment/sensors/trackRays.js +7 -80
  136. package/src/environment/simulationSetup.js +30 -0
  137. package/src/environment/specs.js +15 -109
  138. package/src/index.d.ts +34 -259
  139. package/src/index.js +28 -6
  140. package/src/placeholder/index.js +1 -8
  141. package/src/rendering/displayLifecycle.js +6 -0
  142. package/src/rendering/proceduralTrackAsset.js +1 -1
  143. package/src/rendering/renderSnapshot.js +3 -0
  144. package/src/rendering/track/finishLineRenderer.js +1 -1
  145. package/src/rendering/track/gridRenderer.js +1 -1
  146. package/src/rendering/track/offsetStrokeSafety.js +2 -2
  147. package/src/rendering/track/pitLaneRenderer.js +1 -1
  148. package/src/rendering/track/trackMaterialRenderer.js +4 -4
  149. package/src/rendering/track/trackRenderConstants.js +0 -1
  150. package/src/rendering/track/trackRenderGeometry.js +1 -7
  151. package/src/simulation/collisionGeometry.js +5 -11
  152. package/src/simulation/driver/advancedPathControls.js +53 -0
  153. package/src/simulation/driver/advancedRacePace.js +112 -0
  154. package/src/simulation/driver/advancedRacingControls.js +61 -0
  155. package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
  156. package/src/simulation/driver/advancedRacingSteering.js +76 -0
  157. package/src/simulation/driver/advancedRacingTiming.js +16 -0
  158. package/src/simulation/driver/arcadeRacePace.js +83 -0
  159. package/src/simulation/driver/arcadeRacingControls.js +111 -0
  160. package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
  161. package/src/simulation/driver/driverControlConstants.js +0 -2
  162. package/src/simulation/driver/driverController.js +14 -3
  163. package/src/simulation/driver/edgeRecovery.js +0 -1
  164. package/src/simulation/driver/racingControls.js +10 -406
  165. package/src/simulation/driver/racingDriverProfile.js +48 -0
  166. package/src/simulation/driver/racingLinePlan.js +48 -15
  167. package/src/simulation/driver/rejoinControls.js +58 -111
  168. package/src/simulation/driver/safetyCarControls.js +12 -0
  169. package/src/simulation/driver/trafficScan.js +89 -6
  170. package/src/simulation/pit/pitCarPlacement.js +29 -0
  171. package/src/simulation/pit/pitFlow.js +8 -30
  172. package/src/simulation/pit/pitIntent.js +4 -3
  173. package/src/simulation/pit/pitOccupancy.js +2 -2
  174. package/src/simulation/pit/pitQueue.js +2 -18
  175. package/src/simulation/pit/pitRouteMovement.js +1 -36
  176. package/src/simulation/pit/pitRouting.js +5 -81
  177. package/src/simulation/pit/pitService.js +3 -32
  178. package/src/simulation/pit/pitServiceConstants.js +1 -0
  179. package/src/simulation/pit/pitServiceProgress.js +34 -0
  180. package/src/simulation/pit/pitSnapshots.js +8 -4
  181. package/src/simulation/pit/pitState.js +1 -5
  182. package/src/simulation/race/classification.js +1 -8
  183. package/src/simulation/race/raceFinish.js +3 -8
  184. package/src/simulation/race/raceOrder.js +2 -13
  185. package/src/simulation/race/raceProgress.js +6 -20
  186. package/src/simulation/race/raceSetup.js +1 -1
  187. package/src/simulation/race/raceStep.js +1 -1
  188. package/src/simulation/race/redFlag.js +2 -4
  189. package/src/simulation/race/safetyCar.js +3 -8
  190. package/src/simulation/race/stalledDnf.js +2 -4
  191. package/src/simulation/raceSimulation.js +6 -38
  192. package/src/simulation/replay/replayGhosts.js +44 -32
  193. package/src/simulation/rules/collisionSteward.js +0 -14
  194. package/src/simulation/rules/penaltyActions.js +37 -0
  195. package/src/simulation/rules/penaltyLedger.js +8 -1
  196. package/src/simulation/rules/rulesReview.js +1 -7
  197. package/src/simulation/simMath.js +4 -0
  198. package/src/simulation/snapshots/raceSnapshots.js +30 -11
  199. package/src/simulation/timing/drsState.js +8 -0
  200. package/src/simulation/timing/drsTiming.js +2 -4
  201. package/src/simulation/timing/lapTelemetry.js +7 -4
  202. package/src/simulation/timing/sectorPerformance.js +3 -3
  203. package/src/simulation/timing/timingLines.js +12 -6
  204. package/src/simulation/track/pitLaneAccess.js +2 -2
  205. package/src/simulation/track/pitLaneLayout.js +4 -135
  206. package/src/simulation/track/pitLaneModel.js +70 -0
  207. package/src/simulation/track/pitLaneState.js +0 -1
  208. package/src/simulation/track/pitServiceLayout.js +121 -0
  209. package/src/simulation/track/proceduralTrack.js +77 -0
  210. package/src/simulation/track/spatialQueries.js +1 -25
  211. package/src/simulation/track/trackBuilder.js +54 -0
  212. package/src/simulation/track/trackConstants.js +0 -22
  213. package/src/simulation/track/trackMath.js +4 -29
  214. package/src/simulation/track/trackModel.js +4 -241
  215. package/src/simulation/track/trackModelCache.js +105 -0
  216. package/src/simulation/track/trackNearestQueries.js +1218 -0
  217. package/src/simulation/track/trackQueryConstants.js +3 -0
  218. package/src/simulation/track/trackQueryConstruction.js +368 -0
  219. package/src/simulation/track/trackQueryIndex.js +12 -1933
  220. package/src/simulation/track/trackQuerySegments.js +174 -0
  221. package/src/simulation/track/trackQueryStats.js +12 -0
  222. package/src/simulation/track/trackRayQueries.js +165 -0
  223. package/src/simulation/vehicle/advancedContactResponse.js +89 -0
  224. package/src/simulation/vehicle/advancedTireForces.js +92 -0
  225. package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
  226. package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
  227. package/src/simulation/vehicle/contactResolution.js +20 -6
  228. package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
  229. package/src/simulation/vehicle/pitWheelSurface.js +0 -12
  230. package/src/simulation/vehicle/runoffResponse.js +2 -4
  231. package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
  232. package/src/simulation/vehicle/vehicleGeometry.js +0 -56
  233. package/src/simulation/vehicle/vehicleKinematics.js +10 -0
  234. package/src/simulation/vehicle/vehiclePhysics.js +35 -194
  235. package/src/simulation/vehicle/vehicleSnapshots.js +13 -4
  236. package/src/simulation/vehicle/vehicleState.js +2 -1
  237. package/src/simulation/vehicle/wheelSurface.js +13 -432
  238. package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
  239. package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
  240. package/src/styles.css +173 -19
  241. package/src/ui/bannerTemplates.js +4 -2
  242. package/src/ui/cameraControlsTemplate.js +4 -2
  243. package/src/ui/gridWalkTemplate.js +29 -0
  244. package/src/ui/htmlEscaping.js +8 -0
  245. package/src/ui/shellTemplate.js +3 -0
  246. package/src/ui/telemetryDrawerTemplate.js +4 -2
  247. package/src/ui/telemetryTemplates.js +3 -22
  248. package/src/ui/templateUtils.js +2 -8
  249. package/src/ui/viewControlsTemplate.js +21 -0
@@ -1,7 +1,7 @@
1
+ import { clearCurrentDrsState } from '../timing/drsState.js';
1
2
  import { pitLaneStatusSnapshot } from './pitSnapshots.js';
2
3
  import {
3
4
  firstDifferentCompound,
4
- normalizePitIntent,
5
5
  shouldStartPitStopForSimulation,
6
6
  updateAutomaticPitIntentForSimulation,
7
7
  } from './pitIntent.js';
@@ -17,7 +17,7 @@ import {
17
17
  isPitServiceQueueOccupied,
18
18
  releasePitQueue,
19
19
  } from './pitService.js';
20
- import { syncPitCarKinematics } from './pitKinematics.js';
20
+ import { holdPitCarAtBox } from './pitCarPlacement.js';
21
21
  import {
22
22
  createPitApproachPoints,
23
23
  createRoute,
@@ -27,13 +27,12 @@ import {
27
27
  } from './pitRouting.js';
28
28
  import { metersToSimUnits } from '../units.js';
29
29
  import { applyWheelSurfaceState } from '../vehicle/wheelSurface.js';
30
+ import { PIT_ENTRY_APPROACH_DISTANCE, PIT_BOX_APPROACH_DISTANCE } from './pitServiceConstants.js';
30
31
 
31
- const PIT_ENTRY_APPROACH_DISTANCE = metersToSimUnits(250);
32
- const PIT_BOX_APPROACH_DISTANCE = metersToSimUnits(34);
33
32
  const PIT_SERVICE_QUEUE_FALLBACK_GAP = metersToSimUnits(48);
34
33
  const PIT_ENTRY_LATE_THRESHOLD_DISTANCE = metersToSimUnits(90);
35
34
 
36
- export function isCarInActivePitStop(sim, car) {
35
+ export function isCarInActivePitStop(car) {
37
36
  return Boolean(car?.pitStop && car.pitStop.status !== 'pending' && car.pitStop.status !== 'completed');
38
37
  }
39
38
 
@@ -82,7 +81,7 @@ export function canStartPitStop(sim, car) {
82
81
  let activeCount = 0;
83
82
  for (let index = 0; index < sim.cars.length; index += 1) {
84
83
  const candidate = sim.cars[index];
85
- if (candidate === car || !isCarInActivePitStop(sim, candidate)) continue;
84
+ if (candidate === car || !isCarInActivePitStop(candidate)) continue;
86
85
  activeCount += 1;
87
86
  if (activeCount >= maxConcurrentPitLaneCars) return false;
88
87
  }
@@ -96,7 +95,7 @@ export function canStartPitStop(sim, car) {
96
95
  const candidateDistance = car.raceDistance ?? 0;
97
96
  for (let index = 0; index < sim.cars.length; index += 1) {
98
97
  const candidate = sim.cars[index];
99
- if (candidate === car || !isCarInActivePitStop(sim, candidate)) continue;
98
+ if (candidate === car || !isCarInActivePitStop(candidate)) continue;
100
99
  const gap = (candidate.raceDistance ?? 0) - candidateDistance;
101
100
  if (gap >= 0 && gap < minimumGap) return false;
102
101
  }
@@ -126,10 +125,7 @@ export function startPitStop(sim, car) {
126
125
  if (!stop || !box || !pitLane?.enabled) return false;
127
126
 
128
127
  car.gridLocked = false;
129
- car.drsActive = false;
130
- car.drsEligible = false;
131
- car.drsZoneId = null;
132
- car.drsZoneEnabled = false;
128
+ clearCurrentDrsState(car);
133
129
  car.canAttack = false;
134
130
  stop.targetTire = stop.targetTire ?? firstDifferentCompound(car.tire, sim.rules.modules?.tireStrategy?.compounds);
135
131
 
@@ -213,25 +209,7 @@ export function advancePitStopCar(sim, car, delta) {
213
209
  if (stop.status === 'queued') {
214
210
  const box = getPitStopBox(sim, stop);
215
211
  if (!box) return true;
216
- car.previousX = car.x;
217
- car.previousY = car.y;
218
- car.previousHeading = car.heading;
219
- car.x = box.queuePoint.x;
220
- car.y = box.queuePoint.y;
221
- car.heading = sim.track.pitLane.mainLane.heading;
222
- car.speed = 0;
223
- car.throttle = 0;
224
- car.brake = 1;
225
- car.steeringAngle = 0;
226
- car.yawRate = 0;
227
- car.turnRadius = Infinity;
228
- syncPitCarKinematics(car, { physicsMode: sim.physicsMode });
229
- applyWheelSurfaceState(car, sim.track);
230
- car.progress = car.trackState.distance;
231
- car.raceDistance = getPitBoxRaceDistance(sim, stop, {
232
- ...box,
233
- distanceAlongLane: box.queueDistanceAlongLane ?? box.distanceAlongLane,
234
- });
212
+ holdPitCarAtBox(sim, car, box, { queue: true, updatePreviousPose: true });
235
213
  if (!isPitServiceBusy(sim, car, box)) releasePitQueue(sim, car, box);
236
214
  return true;
237
215
  }
@@ -1,6 +1,4 @@
1
1
  export const PIT_INTENT_NONE = 0;
2
- export const PIT_INTENT_IF_FREE = 1;
3
- export const PIT_INTENT_COMMITTED = 2;
4
2
 
5
3
  export function firstDifferentCompound(currentTire, compounds) {
6
4
  const available = Array.isArray(compounds) && compounds.length ? compounds : ['S', 'M', 'H'];
@@ -87,6 +85,9 @@ export function updateAutomaticPitIntentForSimulation(sim, car) {
87
85
  if (nextIntent === 0 || nextIntent <= (normalizePitIntent(stop.intent) ?? 0)) return;
88
86
 
89
87
  stop.intent = nextIntent;
90
- if (stop.status === 'completed') sim.rearmCompletedPitStop(car, stop);
88
+ if (stop.status === 'completed') {
89
+ stop.targetTire = firstDifferentCompound(car.tire, sim.rules.modules?.tireStrategy?.compounds);
90
+ sim.rearmCompletedPitStop(car, stop);
91
+ }
91
92
  else sim.schedulePitStopAtNextEntry(car, stop);
92
93
  }
@@ -7,7 +7,7 @@ export function getPitStopBox(sim, stop) {
7
7
  null;
8
8
  }
9
9
 
10
- export function isPitServiceAreaOccupied(sim, candidate, box, clearDistance) {
10
+ export function isPitServiceAreaOccupied(candidate, box, clearDistance) {
11
11
  if (!blocksPitLane(candidate)) return false;
12
12
  const status = candidate?.pitStop?.status;
13
13
  const phase = candidate?.pitStop?.phase;
@@ -23,7 +23,7 @@ export function isPitServiceBusy(sim, car, box, clearDistance = PIT_SERVICE_CLEA
23
23
  candidate !== car &&
24
24
  blocksPitLane(candidate) &&
25
25
  candidate.pitStop?.boxId === box?.id &&
26
- isPitServiceAreaOccupied(sim, candidate, box, clearDistance)
26
+ isPitServiceAreaOccupied(candidate, box, clearDistance)
27
27
  ));
28
28
  }
29
29
 
@@ -1,6 +1,5 @@
1
- import { applyWheelSurfaceState } from '../vehicle/wheelSurface.js';
2
1
  import { createRoute, routePoint } from './pitRouting.js';
3
- import { syncPitCarKinematics } from './pitKinematics.js';
2
+ import { holdPitCarAtBox } from './pitCarPlacement.js';
4
3
  import { PIT_QUEUE_RELEASE_SPEED } from './pitServiceConstants.js';
5
4
  import { getPitBoxRaceDistance } from './pitOccupancy.js';
6
5
 
@@ -12,22 +11,7 @@ export function beginPitQueue(sim, car, box) {
12
11
  stop.route = null;
13
12
  stop.routeProgress = 0;
14
13
  stop.queueingForService = false;
15
- car.x = box.queuePoint.x;
16
- car.y = box.queuePoint.y;
17
- car.heading = sim.track.pitLane.mainLane.heading;
18
- car.speed = 0;
19
- car.throttle = 0;
20
- car.brake = 1;
21
- car.steeringAngle = 0;
22
- car.yawRate = 0;
23
- car.turnRadius = Infinity;
24
- syncPitCarKinematics(car, { physicsMode: sim.physicsMode });
25
- applyWheelSurfaceState(car, sim.track);
26
- car.progress = car.trackState.distance;
27
- car.raceDistance = getPitBoxRaceDistance(sim, stop, {
28
- ...box,
29
- distanceAlongLane: box.queueDistanceAlongLane ?? box.distanceAlongLane,
30
- });
14
+ holdPitCarAtBox(sim, car, box, { queue: true });
31
15
  return true;
32
16
  }
33
17
 
@@ -9,9 +9,6 @@ import {
9
9
  sampleRouteInto,
10
10
  } from './pitRouting.js';
11
11
  import { syncPitCarKinematics } from './pitKinematics.js';
12
- import { completePitPenaltyService } from './pitPenaltyService.js';
13
- import { completePitService } from './pitTireService.js';
14
- import { getPitBoxRaceDistance, getPitStopBox } from './pitOccupancy.js';
15
12
  import {
16
13
  PIT_BOX_STOP_SPEED,
17
14
  PIT_ENTRY_BOX_CAPTURE_DISTANCE,
@@ -137,36 +134,4 @@ export function applyPitRoutePosition(sim, car, delta) {
137
134
  return remainingAfter <= PIT_ROUTE_FINISH_DISTANCE;
138
135
  }
139
136
 
140
- export function advancePitService(sim, car, delta) {
141
- const stop = car.pitStop;
142
- const box = getPitStopBox(sim, stop);
143
- if (!box) return true;
144
- if (stop.phase === 'penalty') {
145
- stop.penaltyServiceRemaining = Math.max(0, (stop.penaltyServiceRemaining ?? 0) - delta);
146
- stop.serviceRemaining = stop.penaltyServiceRemaining;
147
- } else {
148
- stop.serviceRemaining = Math.max(0, (stop.serviceRemaining ?? 0) - delta);
149
- }
150
- car.previousX = car.x;
151
- car.previousY = car.y;
152
- car.previousHeading = car.heading;
153
- car.x = box.center.x;
154
- car.y = box.center.y;
155
- car.heading = sim.track.pitLane.mainLane.heading;
156
- car.speed = 0;
157
- car.throttle = 0;
158
- car.brake = 1;
159
- car.steeringAngle = 0;
160
- car.yawRate = 0;
161
- car.turnRadius = Infinity;
162
- syncPitCarKinematics(car, { physicsMode: sim.physicsMode });
163
- applyWheelSurfaceState(car, sim.track);
164
- car.progress = car.trackState.distance;
165
- car.raceDistance = getPitBoxRaceDistance(sim, stop, box);
166
- if (stop.phase === 'penalty' && stop.penaltyServiceRemaining <= 0) {
167
- completePitPenaltyService(sim, car);
168
- } else if (stop.serviceRemaining <= 0) {
169
- completePitService(sim, car);
170
- }
171
- return true;
172
- }
137
+ export { advancePitService } from './pitServiceProgress.js';
@@ -1,79 +1,14 @@
1
- import { clamp, normalizeAngle } from '../simMath.js';
1
+ import { clamp } from '../simMath.js';
2
2
  import { metersToSimUnits } from '../units.js';
3
3
  import { nearestTrackState, offsetTrackPoint, pointAt } from '../track/trackModel.js';
4
+ import { PIT_ENTRY_APPROACH_DISTANCE } from './pitServiceConstants.js';
4
5
 
5
- const PIT_ENTRY_APPROACH_DISTANCE = metersToSimUnits(250);
6
+ export { clonePitLaneModel } from '../track/pitLaneModel.js';
7
+ export { shiftPreviousRenderPose } from '../vehicle/vehicleKinematics.js';
8
+ export { pointDistance } from './pitServiceConstants.js';
6
9
 
7
- function clonePointLike(point) {
8
- if (!point) return point;
9
- return { ...point };
10
- }
11
10
 
12
- function clonePointArray(points) {
13
- return Array.isArray(points) ? points.map(clonePointLike) : points;
14
- }
15
11
 
16
- export function clonePitLaneModel(pitLane) {
17
- if (!pitLane) return pitLane;
18
- return {
19
- ...pitLane,
20
- entry: pitLane.entry ? {
21
- ...pitLane.entry,
22
- trackPoint: clonePointLike(pitLane.entry.trackPoint),
23
- edgePoint: clonePointLike(pitLane.entry.edgePoint),
24
- trackConnectPoint: clonePointLike(pitLane.entry.trackConnectPoint),
25
- lanePoint: clonePointLike(pitLane.entry.lanePoint),
26
- roadCenterline: clonePointArray(pitLane.entry.roadCenterline),
27
- connector: clonePointArray(pitLane.entry.connector),
28
- } : pitLane.entry,
29
- exit: pitLane.exit ? {
30
- ...pitLane.exit,
31
- trackPoint: clonePointLike(pitLane.exit.trackPoint),
32
- edgePoint: clonePointLike(pitLane.exit.edgePoint),
33
- trackConnectPoint: clonePointLike(pitLane.exit.trackConnectPoint),
34
- lanePoint: clonePointLike(pitLane.exit.lanePoint),
35
- roadCenterline: clonePointArray(pitLane.exit.roadCenterline),
36
- connector: clonePointArray(pitLane.exit.connector),
37
- } : pitLane.exit,
38
- mainLane: pitLane.mainLane ? {
39
- ...pitLane.mainLane,
40
- start: clonePointLike(pitLane.mainLane.start),
41
- end: clonePointLike(pitLane.mainLane.end),
42
- points: clonePointArray(pitLane.mainLane.points),
43
- } : pitLane.mainLane,
44
- workingLane: pitLane.workingLane ? {
45
- ...pitLane.workingLane,
46
- start: clonePointLike(pitLane.workingLane.start),
47
- end: clonePointLike(pitLane.workingLane.end),
48
- points: clonePointArray(pitLane.workingLane.points),
49
- } : pitLane.workingLane,
50
- fastLane: pitLane.fastLane ? { ...pitLane.fastLane } : pitLane.fastLane,
51
- serviceNormal: clonePointLike(pitLane.serviceNormal),
52
- boxes: Array.isArray(pitLane.boxes)
53
- ? pitLane.boxes.map((box) => ({
54
- ...box,
55
- laneTarget: clonePointLike(box.laneTarget),
56
- center: clonePointLike(box.center),
57
- corners: clonePointArray(box.corners),
58
- }))
59
- : pitLane.boxes,
60
- serviceAreas: Array.isArray(pitLane.serviceAreas)
61
- ? pitLane.serviceAreas.map((area) => ({
62
- ...area,
63
- laneTarget: clonePointLike(area.laneTarget),
64
- center: clonePointLike(area.center),
65
- queuePoint: clonePointLike(area.queuePoint),
66
- corners: clonePointArray(area.corners),
67
- queueCorners: clonePointArray(area.queueCorners),
68
- garageBoxIds: [...(area.garageBoxIds ?? [])],
69
- pitCrew: area.pitCrew ? { ...area.pitCrew } : area.pitCrew,
70
- }))
71
- : pitLane.serviceAreas,
72
- teams: Array.isArray(pitLane.teams)
73
- ? pitLane.teams.map((team) => ({ ...team, boxIds: [...(team.boxIds ?? [])], pitCrew: team.pitCrew ? { ...team.pitCrew } : team.pitCrew }))
74
- : pitLane.teams,
75
- };
76
- }
77
12
 
78
13
  export function routePoint(point, heading = point?.heading, options = {}) {
79
14
  if (!point) return null;
@@ -114,18 +49,7 @@ function sameRoutePoint(first, second) {
114
49
  return first && second && Math.hypot(first.x - second.x, first.y - second.y) < 0.001;
115
50
  }
116
51
 
117
- export function shiftPreviousRenderPose(car, dx, dy, dheading = 0) {
118
- car.previousX = Number.isFinite(car.previousX) ? car.previousX + dx : car.x + dx;
119
- car.previousY = Number.isFinite(car.previousY) ? car.previousY + dy : car.y + dy;
120
- car.previousHeading = normalizeAngle(
121
- (Number.isFinite(car.previousHeading) ? car.previousHeading : car.heading) + dheading,
122
- );
123
- }
124
52
 
125
- export function pointDistance(first, second) {
126
- if (!first || !second) return Infinity;
127
- return Math.hypot(first.x - second.x, first.y - second.y);
128
- }
129
53
 
130
54
  export function createRoute(points) {
131
55
  const routePoints = [];
@@ -1,11 +1,4 @@
1
- import { applyWheelSurfaceState } from '../vehicle/wheelSurface.js';
2
- import { normalizePitCrewStats } from './pitState.js';
3
- import { beginPitPenaltyService } from './pitPenaltyService.js';
4
- import { beginTireService } from './pitTireService.js';
5
- import { syncPitCarKinematics } from './pitKinematics.js';
6
- import { getPitBoxRaceDistance } from './pitOccupancy.js';
7
-
8
- export { normalizePitCrewStats };
1
+ export { normalizePitCrewStats } from './pitState.js';
9
2
  export {
10
3
  getPitBoxRaceDistance,
11
4
  getPitStopBox,
@@ -17,27 +10,5 @@ export { beginPitQueue, releasePitQueue } from './pitQueue.js';
17
10
  export { calculatePitServiceProfile } from './pitServiceProfile.js';
18
11
  export { beginPitPenaltyService, completePitPenaltyService, getPitServicePenalties } from './pitPenaltyService.js';
19
12
  export { beginTireService, completePitService, finishPitExit } from './pitTireService.js';
20
- export { advancePitService, applyPitRoutePosition } from './pitRouteMovement.js';
21
-
22
- export function beginPitService(sim, car, box) {
23
- const stop = car.pitStop;
24
- stop.status = 'servicing';
25
- stop.route = null;
26
- stop.routeProgress = 0;
27
- car.x = box.center.x;
28
- car.y = box.center.y;
29
- car.heading = sim.track.pitLane.mainLane.heading;
30
- car.speed = 0;
31
- car.throttle = 0;
32
- car.brake = 1;
33
- car.steeringAngle = 0;
34
- car.yawRate = 0;
35
- car.turnRadius = Infinity;
36
- syncPitCarKinematics(car, { physicsMode: sim.physicsMode });
37
- applyWheelSurfaceState(car, sim.track);
38
- car.progress = car.trackState.distance;
39
- car.raceDistance = getPitBoxRaceDistance(sim, stop, box);
40
-
41
- if (beginPitPenaltyService(sim, car)) return;
42
- beginTireService(sim, car);
43
- }
13
+ export { beginPitService, advancePitService } from './pitServiceProgress.js';
14
+ export { applyPitRoutePosition } from './pitRouteMovement.js';
@@ -1,6 +1,7 @@
1
1
  import { kphToSimSpeed, metersToSimUnits } from '../units.js';
2
2
  import { VEHICLE_LIMITS } from '../vehicle/vehiclePhysics.js';
3
3
 
4
+ export const PIT_ENTRY_APPROACH_DISTANCE = metersToSimUnits(250);
4
5
  export const PIT_ROUTE_FINISH_DISTANCE = metersToSimUnits(8.5);
5
6
  export const PIT_QUEUE_RELEASE_FINISH_DISTANCE = metersToSimUnits(4);
6
7
  export const PIT_QUEUE_CAPTURE_DISTANCE = metersToSimUnits(2.5);
@@ -0,0 +1,34 @@
1
+ import { beginPitPenaltyService, completePitPenaltyService } from './pitPenaltyService.js';
2
+ import { beginTireService, completePitService } from './pitTireService.js';
3
+ import { getPitStopBox } from './pitOccupancy.js';
4
+ import { holdPitCarAtBox } from './pitCarPlacement.js';
5
+
6
+ export function beginPitService(sim, car, box) {
7
+ const stop = car.pitStop;
8
+ stop.status = 'servicing';
9
+ stop.route = null;
10
+ stop.routeProgress = 0;
11
+ holdPitCarAtBox(sim, car, box);
12
+
13
+ if (beginPitPenaltyService(sim, car)) return;
14
+ beginTireService(sim, car);
15
+ }
16
+
17
+ export function advancePitService(sim, car, delta) {
18
+ const stop = car.pitStop;
19
+ const box = getPitStopBox(sim, stop);
20
+ if (!box) return true;
21
+ if (stop.phase === 'penalty') {
22
+ stop.penaltyServiceRemaining = Math.max(0, (stop.penaltyServiceRemaining ?? 0) - delta);
23
+ stop.serviceRemaining = stop.penaltyServiceRemaining;
24
+ } else {
25
+ stop.serviceRemaining = Math.max(0, (stop.serviceRemaining ?? 0) - delta);
26
+ }
27
+ holdPitCarAtBox(sim, car, box, { updatePreviousPose: true });
28
+ if (stop.phase === 'penalty' && stop.penaltyServiceRemaining <= 0) {
29
+ completePitPenaltyService(sim, car);
30
+ } else if (stop.serviceRemaining <= 0) {
31
+ completePitService(sim, car);
32
+ }
33
+ return true;
34
+ }
@@ -1,4 +1,5 @@
1
- import { finiteOrNull } from '../vehicle/vehicleSnapshots.js';
1
+ import { finiteOrNull } from '../simMath.js';
2
+ import { normalizePitIntent, PIT_INTENT_NONE } from './pitIntent.js';
2
3
 
3
4
  export function pitLaneStatusSnapshot(raceControl, pitLane, pitStops) {
4
5
  const enabled = Boolean(pitLane?.enabled && pitStops?.enabled);
@@ -13,7 +14,7 @@ export function pitLaneStatusSnapshot(raceControl, pitLane, pitStops) {
13
14
  };
14
15
  }
15
16
 
16
- export function serializePitStop(pitStop, normalizePitIntent, PIT_INTENT_NONE) {
17
+ export function serializePitStop(pitStop) {
17
18
  if (!pitStop) return null;
18
19
  return {
19
20
  status: pitStop.status,
@@ -34,7 +35,10 @@ export function serializePitStop(pitStop, normalizePitIntent, PIT_INTENT_NONE) {
34
35
  penaltyServiceTotalSeconds: finiteOrNull(pitStop.penaltyServiceTotal),
35
36
  servingPenaltyIds: [...(pitStop.servingPenaltyIds ?? [])],
36
37
  targetTire: pitStop.targetTire ?? null,
37
- serviceProfile: pitStop.serviceProfile ? { ...pitStop.serviceProfile } : null,
38
+ serviceProfile: pitStop.serviceProfile ? {
39
+ ...pitStop.serviceProfile,
40
+ ...(pitStop.serviceProfile.pitCrew ? { pitCrew: { ...pitStop.serviceProfile.pitCrew } } : {}),
41
+ } : null,
38
42
  };
39
43
  }
40
44
 
@@ -47,7 +51,7 @@ export function serializeRenderPitStop(pitStop) {
47
51
  };
48
52
  }
49
53
 
50
- export function serializeObservationPitStop(pitStop, normalizePitIntent, PIT_INTENT_NONE) {
54
+ export function serializeObservationPitStop(pitStop) {
51
55
  if (!pitStop) return null;
52
56
  return {
53
57
  status: pitStop.status,
@@ -1,7 +1,5 @@
1
- import { metersToSimUnits } from '../units.js';
2
1
  import { firstDifferentCompound } from './pitIntent.js';
3
-
4
- const PIT_ENTRY_APPROACH_DISTANCE = metersToSimUnits(250);
2
+ import { PIT_ENTRY_APPROACH_DISTANCE } from './pitServiceConstants.js';
5
3
 
6
4
  export function normalizePitCrewStats(value) {
7
5
  const source = value && typeof value === 'object' ? value : {};
@@ -73,7 +71,6 @@ export function initializePitStops({ cars, pitLane, pitStops, totalLaps, trackLe
73
71
 
74
72
  cars.forEach((car, index) => {
75
73
  const stopLapBase = trackLength * (1 + (index % pitWindowLapCount));
76
- const trainPosition = Math.floor(index / pitWindowLapCount);
77
74
  const teamIndex = Math.floor(index / boxesPerTeam);
78
75
  const teamBoxIndex = index % boxesPerTeam;
79
76
  const garageBoxIndex = Math.min(pitLane.boxes.length - 1, teamIndex * boxesPerTeam + teamBoxIndex);
@@ -94,7 +91,6 @@ export function initializePitStops({ cars, pitLane, pitStops, totalLaps, trackLe
94
91
  entryRaceDistance: stopLapBase + pitLane.entry.distanceFromStart,
95
92
  plannedRaceDistance: stopLapBase + pitLane.entry.distanceFromStart -
96
93
  PIT_ENTRY_APPROACH_DISTANCE,
97
- trainPosition,
98
94
  lapBase: stopLapBase,
99
95
  serviceRemaining: 0,
100
96
  penaltyServiceRemaining: 0,
@@ -1,14 +1,7 @@
1
1
  import { clamp } from '../simMath.js';
2
2
  import { simUnitsToMeters } from '../units.js';
3
3
  import { isRaceDnf, raceDnfCars } from './retirements.js';
4
-
5
- function getPenaltyStats(statsByDriver, driverId) {
6
- return statsByDriver.get(driverId) ?? {
7
- seconds: 0,
8
- positionDrop: 0,
9
- disqualified: false,
10
- };
11
- }
4
+ import { getPenaltyStats } from '../rules/penaltyStats.js';
12
5
 
13
6
  export function applyOutstandingServicePenalties(sim, applyUnservedServicePenalty) {
14
7
  sim.penalties.forEach((penalty) => {
@@ -1,3 +1,4 @@
1
+ import { clearCurrentDrsState } from '../timing/drsState.js';
1
2
  import { isRaceDnf } from './retirements.js';
2
3
 
3
4
  export function evaluateRaceFinishForSimulation(sim, orderedCars = sim.orderedCars()) {
@@ -23,10 +24,7 @@ export function evaluateRaceFinishForSimulation(sim, orderedCars = sim.orderedCa
23
24
  car.classifiedRank = car.finishRank;
24
25
  sim.raceControl.finishOrder.push(car.id);
25
26
  if (!sim.raceControl.winnerId) sim.raceControl.winnerId = car.id;
26
- car.drsActive = false;
27
- car.drsEligible = false;
28
- car.drsZoneId = null;
29
- car.drsZoneEnabled = false;
27
+ clearCurrentDrsState(car);
30
28
  car.canAttack = false;
31
29
  sim.events.unshift({
32
30
  type: 'car-finish',
@@ -67,10 +65,7 @@ export function evaluateRaceFinishForSimulation(sim, orderedCars = sim.orderedCa
67
65
  const classified = classification.find((entry) => entry.id === car.id);
68
66
  car.classifiedRank = classified?.rank ?? null;
69
67
  car.desiredOffset = 0;
70
- car.drsActive = false;
71
- car.drsEligible = false;
72
- car.drsZoneId = null;
73
- car.drsZoneEnabled = false;
68
+ clearCurrentDrsState(car);
74
69
  car.canAttack = false;
75
70
  });
76
71
  sim.events.unshift({
@@ -47,7 +47,7 @@ function collectLiveRaceOrderedCars(orderedCars, scratch) {
47
47
  return physicalCars;
48
48
  }
49
49
 
50
- function sortPhysicalCarsInTrackOrder(sim, physicalCars) {
50
+ function sortPhysicalCarsInTrackOrder(physicalCars) {
51
51
  physicalCars.sort((a, b) => {
52
52
  const progressDelta = (b.progress ?? b.raceDistance ?? 0) - (a.progress ?? a.raceDistance ?? 0);
53
53
  if (progressDelta !== 0) return progressDelta;
@@ -121,7 +121,7 @@ function buildDrsReferenceArrayForSimulation(sim, orderedCars = orderedCarsForSi
121
121
  sim.runtimeBenchmarkStats.drsReferenceOrderedFastPathCalls = (sim.runtimeBenchmarkStats.drsReferenceOrderedFastPathCalls ?? 0) + 1;
122
122
  }
123
123
  } else {
124
- sortPhysicalCarsInTrackOrder(sim, physicalCars);
124
+ sortPhysicalCarsInTrackOrder(physicalCars);
125
125
  }
126
126
 
127
127
  const trackLength = sim.track.length;
@@ -219,17 +219,6 @@ export function driverRaceContextForSimulation(sim, orderedCars = orderedCarsFor
219
219
  };
220
220
  }
221
221
 
222
- export function buildDrsReferenceByCarId(sim, orderedCars = orderedCarsForSimulation(sim)) {
223
- const referencesByIndex = buildDrsReferenceArrayForSimulation(sim, orderedCars);
224
- const references = new Map();
225
- for (let index = 0; index < orderedCars.length; index += 1) {
226
- const car = orderedCars[index];
227
- if (!affectsRaceOrder(car) || isRaceDnf(car)) continue;
228
- references.set(car.id, referencesByIndex[car.index] ?? null);
229
- }
230
- return references;
231
- }
232
-
233
222
  export function assignDrsReferenceCarsForSimulation(sim, orderedCars = orderedCarsForSimulation(sim)) {
234
223
  return buildDrsReferenceArrayForSimulation(sim, orderedCars);
235
224
  }
@@ -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.drsEligible = false;
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.drsEligible = false;
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.drsEligible = false;
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.drsEligible = false;
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.drsEligible = false;
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 '../pit/pitRouting.js';
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,4 +1,4 @@
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
4
  import { integrateVehiclePhysics, isSimulatorPhysicsMode } from '../vehicle/vehiclePhysics.js';
@@ -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.drsActive = false;
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,3 +1,4 @@
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';
@@ -21,10 +22,7 @@ export function setSafetyCarState(sim, deployed) {
21
22
  }
22
23
  sim.cars.forEach((car) => {
23
24
  car.desiredOffset = 0;
24
- car.drsActive = false;
25
- car.drsEligible = false;
26
- car.drsZoneId = null;
27
- car.drsZoneEnabled = false;
25
+ clearCurrentDrsState(car);
28
26
  });
29
27
  }
30
28
  sim.events.unshift({ type: next ? 'safety-car' : 'green-flag', at: sim.time });
@@ -63,10 +61,7 @@ export function setRedFlagState(sim, deployed) {
63
61
  sim.raceControl.frozenOrder = sim.orderedCars().map((car) => car.id);
64
62
  sim.cars.forEach((car) => {
65
63
  freezeVehicleMotion(car, { physicsMode: sim.physicsMode });
66
- car.drsActive = false;
67
- car.drsEligible = false;
68
- car.drsZoneId = null;
69
- car.drsZoneEnabled = false;
64
+ clearCurrentDrsState(car);
70
65
  car.canAttack = false;
71
66
  });
72
67
  } else {