@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.
Files changed (257) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/INSTALL_AND_UPDATE.md +31 -5
  3. package/README.md +34 -3
  4. package/docs/advanced_ai_pace.md +131 -0
  5. package/docs/advanced_physics_rebuild.md +166 -0
  6. package/docs/architecture.md +111 -30
  7. package/docs/cleanup_coverage.md +284 -0
  8. package/docs/component_inventory.md +32 -16
  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/getting-started.md +3 -0
  16. package/docs/grid_walk.md +80 -0
  17. package/docs/index.md +5 -2
  18. package/docs/keyboard_driving_fix.md +118 -0
  19. package/docs/limit_driving_2026-10-14.md +88 -0
  20. package/docs/loading.md +141 -0
  21. package/docs/maintainability_audit_2026-09-08.md +146 -0
  22. package/docs/racing_ai_improvements.md +145 -0
  23. package/docs/rules.md +43 -13
  24. package/docs/sense_contract.md +4 -1
  25. package/docs/system_specs.md +40 -12
  26. package/docs/testing-reliability-guide.md +67 -0
  27. package/docs/v11_improvements.md +108 -0
  28. package/package.json +23 -15
  29. package/src/api/MountedSimulatorComponents.js +101 -0
  30. package/src/api/PaddockSimulatorController.js +56 -81
  31. package/src/app/BrowserExpertAdapter.js +0 -6
  32. package/src/app/BrowserExpertExternalRenderer.js +72 -50
  33. package/src/app/ExternalTrackSnapshot.js +50 -0
  34. package/src/app/F1SimulatorApp.js +242 -297
  35. package/src/app/HostLifecycleEmitter.js +93 -0
  36. package/src/app/banners/raceDataBannerController.js +9 -4
  37. package/src/app/camera/cameraConstants.js +1 -1
  38. package/src/app/camera/cameraController.js +10 -13
  39. package/src/app/camera/cameraGeometry.js +1 -1
  40. package/src/app/controls/SimulatorViewControls.js +45 -0
  41. package/src/app/controls/workbenchBindings.js +64 -0
  42. package/src/app/controls/workbenchDisclosure.js +41 -0
  43. package/src/app/domBindings.js +21 -13
  44. package/src/app/gridWalk/GridWalkController.js +180 -0
  45. package/src/app/gridWalk/gridWalkCamera.js +88 -0
  46. package/src/app/gridWalk/gridWalkControls.js +24 -0
  47. package/src/app/gridWalk/gridWalkCrew.js +246 -0
  48. package/src/app/gridWalk/gridWalkLabels.js +104 -0
  49. package/src/app/gridWalk/gridWalkLayout.js +46 -0
  50. package/src/app/gridWalk/gridWalkNavigation.js +99 -0
  51. package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
  52. package/src/app/gridWalk/gridWalkSession.js +50 -0
  53. package/src/app/layoutSupport.js +2 -0
  54. package/src/app/raceOverlayClearanceSupport.js +21 -7
  55. package/src/app/readouts/carOverviewRenderer.js +9 -4
  56. package/src/app/readouts/readoutFormatters.js +1 -8
  57. package/src/app/readouts/telemetryRenderer.js +1 -3
  58. package/src/app/readouts/timingTowerRenderer.js +48 -50
  59. package/src/app/rendering/appAssets.js +8 -10
  60. package/src/app/rendering/carRenderer.js +27 -79
  61. package/src/app/rendering/displayUtils.js +0 -6
  62. package/src/app/rendering/expertSensorRenderer.js +1 -1
  63. package/src/app/rendering/replayGhostRenderer.js +4 -1
  64. package/src/app/rendering/trackRenderer.js +2 -2
  65. package/src/app/runtime/frameLoop.js +18 -17
  66. package/src/app/runtime/pixiStartupLifecycle.js +94 -0
  67. package/src/app/runtime/runtimeTiming.js +4 -1
  68. package/src/app/runtime/runtimeVisibility.js +7 -2
  69. package/src/config/cssValues.js +1 -1
  70. package/src/config/defaultOptions.js +12 -173
  71. package/src/config/restartOptions.js +1 -0
  72. package/src/config/runtimeSeed.js +10 -0
  73. package/src/config/simulatorDefaults.js +106 -0
  74. package/src/config/telemetryModules.js +20 -0
  75. package/src/config/theme/cssTokens.js +85 -0
  76. package/src/config/theme/themeCss.js +72 -0
  77. package/src/config/theme/themeInputs.js +155 -0
  78. package/src/config/theme/themeResolver.js +172 -0
  79. package/src/config/theme/themeSelectors.js +69 -0
  80. package/src/config/theme/themeTokens.js +257 -0
  81. package/src/config/theme/themeUtils.js +15 -0
  82. package/src/config/themeOptions.js +14 -830
  83. package/src/config/timingGapMode.js +0 -2
  84. package/src/config/uiOptions.js +53 -0
  85. package/src/data/championship.js +16 -158
  86. package/src/data/championshipDefaults.js +87 -0
  87. package/src/data/driverData.js +10 -20
  88. package/src/data/entryIdentity.js +67 -0
  89. package/src/data/entryPresentation.js +23 -0
  90. package/src/data/entryTypes.d.ts +160 -0
  91. package/src/data/index.d.ts +12 -195
  92. package/src/data/ratingScale.js +14 -0
  93. package/src/data/teamTypes.d.ts +23 -0
  94. package/src/data/trackTypes.d.ts +101 -0
  95. package/src/data/vehicleData.js +12 -23
  96. package/src/environment/actions.js +3 -2
  97. package/src/environment/controllerLoop.js +57 -44
  98. package/src/environment/controllerScheduler.js +30 -0
  99. package/src/environment/driverObservations.js +260 -0
  100. package/src/environment/driverReset.js +51 -0
  101. package/src/environment/episode.js +0 -1
  102. package/src/environment/evaluation.js +6 -90
  103. package/src/environment/evaluationTracker.js +100 -0
  104. package/src/environment/events.js +23 -0
  105. package/src/environment/index.d.ts +35 -91
  106. package/src/environment/metrics.js +3 -17
  107. package/src/environment/observationSchema.js +104 -0
  108. package/src/environment/observationVector.js +16 -115
  109. package/src/environment/observations.js +16 -316
  110. package/src/environment/options.js +5 -24
  111. package/src/environment/renderFrames.js +21 -0
  112. package/src/environment/resetOptions.js +41 -0
  113. package/src/environment/results.js +125 -0
  114. package/src/environment/rewards.js +10 -16
  115. package/src/environment/runtime.js +17 -300
  116. package/src/environment/scenarioOptions.js +90 -0
  117. package/src/environment/scenarioPlacements.js +70 -0
  118. package/src/environment/scenarioPresets.js +78 -0
  119. package/src/environment/scenarios.js +5 -236
  120. package/src/environment/scratch.js +18 -0
  121. package/src/environment/sensorOptions.js +20 -0
  122. package/src/environment/sensors/index.js +2 -556
  123. package/src/environment/sensors/indexedRayBands.js +0 -18
  124. package/src/environment/sensors/nearbyCars.js +18 -15
  125. package/src/environment/sensors/opponentRadar.js +3 -2
  126. package/src/environment/sensors/pitConnectorProximity.js +20 -0
  127. package/src/environment/sensors/rayBandTrace.js +1 -37
  128. package/src/environment/sensors/rayBatch.js +81 -0
  129. package/src/environment/sensors/rayConfig.js +26 -0
  130. package/src/environment/sensors/rayGeometry.js +0 -8
  131. package/src/environment/sensors/rayGuards.js +0 -6
  132. package/src/environment/sensors/rayOutputs.js +75 -0
  133. package/src/environment/sensors/rayQuery.js +110 -0
  134. package/src/environment/sensors/rayScratch.js +146 -0
  135. package/src/environment/sensors/sensorTargets.js +12 -4
  136. package/src/environment/sensors/surfaceRays.js +6 -61
  137. package/src/environment/sensors/trackRays.js +7 -80
  138. package/src/environment/simulationSetup.js +30 -0
  139. package/src/environment/specs.js +15 -109
  140. package/src/index.d.ts +43 -202
  141. package/src/index.js +28 -6
  142. package/src/placeholder/index.js +1 -8
  143. package/src/rendering/displayLifecycle.js +6 -0
  144. package/src/rendering/proceduralTrackAsset.js +1 -1
  145. package/src/rendering/renderSnapshot.js +3 -0
  146. package/src/rendering/track/finishLineRenderer.js +1 -1
  147. package/src/rendering/track/gridRenderer.js +1 -1
  148. package/src/rendering/track/offsetStrokeSafety.js +2 -2
  149. package/src/rendering/track/pitLaneRenderer.js +1 -1
  150. package/src/rendering/track/trackMaterialRenderer.js +4 -4
  151. package/src/rendering/track/trackRenderConstants.js +0 -1
  152. package/src/rendering/track/trackRenderGeometry.js +1 -7
  153. package/src/simulation/collisionGeometry.js +12 -20
  154. package/src/simulation/driver/advancedPathControls.js +53 -0
  155. package/src/simulation/driver/advancedRacePace.js +112 -0
  156. package/src/simulation/driver/advancedRacingControls.js +61 -0
  157. package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
  158. package/src/simulation/driver/advancedRacingSteering.js +76 -0
  159. package/src/simulation/driver/advancedRacingTiming.js +16 -0
  160. package/src/simulation/driver/arcadeRacePace.js +83 -0
  161. package/src/simulation/driver/arcadeRacingControls.js +111 -0
  162. package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
  163. package/src/simulation/driver/driverControlConstants.js +0 -2
  164. package/src/simulation/driver/driverController.js +16 -5
  165. package/src/simulation/driver/edgeRecovery.js +2 -3
  166. package/src/simulation/driver/racingControls.js +10 -396
  167. package/src/simulation/driver/racingDriverProfile.js +48 -0
  168. package/src/simulation/driver/racingLinePlan.js +54 -20
  169. package/src/simulation/driver/rejoinControls.js +59 -112
  170. package/src/simulation/driver/safetyCarControls.js +12 -0
  171. package/src/simulation/driver/trafficScan.js +136 -27
  172. package/src/simulation/participants/participantInteractions.js +24 -15
  173. package/src/simulation/pit/pitCarPlacement.js +29 -0
  174. package/src/simulation/pit/pitFlow.js +8 -30
  175. package/src/simulation/pit/pitIntent.js +4 -3
  176. package/src/simulation/pit/pitOccupancy.js +2 -2
  177. package/src/simulation/pit/pitQueue.js +2 -18
  178. package/src/simulation/pit/pitRouteMovement.js +1 -36
  179. package/src/simulation/pit/pitRouting.js +5 -81
  180. package/src/simulation/pit/pitService.js +3 -32
  181. package/src/simulation/pit/pitServiceConstants.js +1 -0
  182. package/src/simulation/pit/pitServiceProgress.js +34 -0
  183. package/src/simulation/pit/pitSnapshots.js +8 -4
  184. package/src/simulation/pit/pitState.js +1 -5
  185. package/src/simulation/race/classification.js +1 -8
  186. package/src/simulation/race/raceFinish.js +3 -8
  187. package/src/simulation/race/raceOrder.js +2 -13
  188. package/src/simulation/race/raceProgress.js +6 -20
  189. package/src/simulation/race/raceSetup.js +1 -1
  190. package/src/simulation/race/raceStep.js +20 -11
  191. package/src/simulation/race/redFlag.js +2 -4
  192. package/src/simulation/race/safetyCar.js +5 -9
  193. package/src/simulation/race/stalledDnf.js +2 -4
  194. package/src/simulation/raceSimulation.js +6 -38
  195. package/src/simulation/replay/replayGhosts.js +44 -32
  196. package/src/simulation/rules/collisionSteward.js +0 -14
  197. package/src/simulation/rules/penaltyActions.js +37 -0
  198. package/src/simulation/rules/penaltyLedger.js +8 -1
  199. package/src/simulation/rules/pitLaneSpeedingSteward.js +1 -1
  200. package/src/simulation/rules/rulesReview.js +20 -16
  201. package/src/simulation/rules/trackLimitsSteward.js +2 -2
  202. package/src/simulation/simMath.js +4 -0
  203. package/src/simulation/snapshots/raceSnapshots.js +30 -11
  204. package/src/simulation/timing/drsState.js +8 -0
  205. package/src/simulation/timing/drsTiming.js +2 -4
  206. package/src/simulation/timing/lapTelemetry.js +7 -4
  207. package/src/simulation/timing/sectorPerformance.js +14 -6
  208. package/src/simulation/timing/timingLines.js +12 -6
  209. package/src/simulation/track/pitLaneAccess.js +2 -2
  210. package/src/simulation/track/pitLaneLayout.js +4 -135
  211. package/src/simulation/track/pitLaneModel.js +70 -0
  212. package/src/simulation/track/pitLaneState.js +0 -1
  213. package/src/simulation/track/pitServiceLayout.js +121 -0
  214. package/src/simulation/track/proceduralTrack.js +77 -0
  215. package/src/simulation/track/spatialQueries.js +76 -31
  216. package/src/simulation/track/trackBuilder.js +54 -0
  217. package/src/simulation/track/trackConstants.js +0 -22
  218. package/src/simulation/track/trackMath.js +4 -29
  219. package/src/simulation/track/trackModel.js +12 -240
  220. package/src/simulation/track/trackModelCache.js +105 -0
  221. package/src/simulation/track/trackNearestQueries.js +1218 -0
  222. package/src/simulation/track/trackQueryConstants.js +3 -0
  223. package/src/simulation/track/trackQueryConstruction.js +368 -0
  224. package/src/simulation/track/trackQueryIndex.js +12 -1933
  225. package/src/simulation/track/trackQuerySegments.js +174 -0
  226. package/src/simulation/track/trackQueryStats.js +12 -0
  227. package/src/simulation/track/trackRayQueries.js +165 -0
  228. package/src/simulation/vehicle/advancedContactResponse.js +89 -0
  229. package/src/simulation/vehicle/advancedTireForces.js +92 -0
  230. package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
  231. package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
  232. package/src/simulation/vehicle/contactResolution.js +23 -9
  233. package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
  234. package/src/simulation/vehicle/pitWheelSurface.js +0 -12
  235. package/src/simulation/vehicle/runoffResponse.js +2 -4
  236. package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
  237. package/src/simulation/vehicle/vehicleGeometry.js +85 -103
  238. package/src/simulation/vehicle/vehicleKinematics.js +10 -0
  239. package/src/simulation/vehicle/vehiclePhysics.js +42 -196
  240. package/src/simulation/vehicle/vehicleSnapshots.js +18 -6
  241. package/src/simulation/vehicle/vehicleState.js +5 -3
  242. package/src/simulation/vehicle/wheelSurface.js +13 -432
  243. package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
  244. package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
  245. package/src/styles.css +173 -19
  246. package/src/ui/bannerTemplates.js +4 -2
  247. package/src/ui/cameraControlsTemplate.js +4 -2
  248. package/src/ui/gridWalkTemplate.js +29 -0
  249. package/src/ui/htmlEscaping.js +8 -0
  250. package/src/ui/raceCanvasTemplate.js +3 -2
  251. package/src/ui/shellTemplate.js +3 -0
  252. package/src/ui/telemetryDrawerTemplate.js +4 -2
  253. package/src/ui/telemetryTemplates.js +3 -22
  254. package/src/ui/templateUtils.js +2 -8
  255. package/src/ui/viewControlsTemplate.js +21 -0
  256. package/examples/train-basic-policy.mjs +0 -276
  257. package/examples/trainingData.mjs +0 -133
@@ -1,5 +1,3 @@
1
- export const TIMING_GAP_MODES = new Set(['interval', 'leader']);
2
-
3
1
  export function normalizeTimingGapMode(value) {
4
2
  return value === 'leader' ? 'leader' : 'interval';
5
3
  }
@@ -0,0 +1,53 @@
1
+ import { DEFAULT_F1_SIMULATOR_OPTIONS } from './simulatorDefaults.js';
2
+ import { normalizeTelemetryModules } from './telemetryModules.js';
3
+ import { normalizeTimingGapMode } from './timingGapMode.js';
4
+
5
+ export function resolveSimulatorUiOptions(requestedUi = {}, presetUi = {}, initialCameraMode = 'leader') {
6
+ const ui = {
7
+ ...DEFAULT_F1_SIMULATOR_OPTIONS.ui,
8
+ ...presetUi,
9
+ ...requestedUi,
10
+ raceDataBanners: {
11
+ ...DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBanners,
12
+ ...(presetUi.raceDataBanners ?? {}),
13
+ ...(requestedUi.raceDataBanners ?? {}),
14
+ },
15
+ };
16
+ ui.driverCamera = Boolean(ui.driverCamera || initialCameraMode === 'driver');
17
+ ui.viewControls = ui.viewControls === 'toggle' ? 'toggle' : 'switch';
18
+ ui.raceDataBanners.enabled = normalizeEnabledBanners(ui.raceDataBanners.enabled);
19
+ ui.telemetryModules = normalizeTelemetryModules(ui.telemetryModules, DEFAULT_F1_SIMULATOR_OPTIONS.ui.telemetryModules);
20
+ if (!['project', 'radio', 'hidden'].includes(ui.raceDataBanners.initial)) {
21
+ ui.raceDataBanners.initial = DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBanners.initial;
22
+ }
23
+ if (ui.raceDataBanners.initial !== 'hidden' && !ui.raceDataBanners.enabled.includes(ui.raceDataBanners.initial)) {
24
+ ui.raceDataBanners.initial = 'hidden';
25
+ }
26
+ if (!['expand-race-view', 'scroll'].includes(ui.timingTowerVerticalFit)) {
27
+ ui.timingTowerVerticalFit = DEFAULT_F1_SIMULATOR_OPTIONS.ui.timingTowerVerticalFit;
28
+ }
29
+ ui.timingGapMode = normalizeTimingGapMode(ui.timingGapMode);
30
+ ui.timingGapModeToggle = ui.timingGapModeToggle !== false;
31
+ ui.timingEntryVerticalPadding = normalizeNonNegativeFiniteNumber(
32
+ ui.timingEntryVerticalPadding,
33
+ DEFAULT_F1_SIMULATOR_OPTIONS.ui.timingEntryVerticalPadding,
34
+ );
35
+ ui.responsiveNarrowLayout = ui.responsiveNarrowLayout !== false;
36
+ if (!['auto', 'custom'].includes(ui.raceDataBannerSize)) {
37
+ ui.raceDataBannerSize = DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBannerSize;
38
+ }
39
+ ui.raceDataTelemetryDetail = Boolean(ui.raceDataTelemetryDetail);
40
+ return ui;
41
+ }
42
+
43
+ function normalizeNonNegativeFiniteNumber(value, fallback) {
44
+ const numericValue = Number(value);
45
+ return Number.isFinite(numericValue) && numericValue >= 0 ? numericValue : fallback;
46
+ }
47
+
48
+ function normalizeEnabledBanners(value) {
49
+ if (value === false) return [];
50
+ if (value === true || value == null) return [...DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBanners.enabled];
51
+ if (!Array.isArray(value)) return [...DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBanners.enabled];
52
+ return [...new Set(value.filter((item) => item === 'project' || item === 'radio'))];
53
+ }
@@ -1,147 +1,26 @@
1
1
  import { DriverData } from './driverData.js';
2
2
  import { DEMO_PROJECT_DRIVERS } from './demoDrivers.js';
3
3
  import { VehicleData } from './vehicleData.js';
4
- import { DEFAULT_DARK_THEME_TOKENS, normalizeCssColorToken } from '../config/themeOptions.js';
4
+ import { normalizeEntryPresentation } from './entryPresentation.js';
5
+ import { DEFAULT_DARK_THEME_TOKENS } from '../config/theme/themeTokens.js';
6
+ import { normalizeCssColorToken } from '../config/theme/cssTokens.js';
7
+ import { CHAMPIONSHIP, CHAMPIONSHIP_ENTRY_BLUEPRINTS, CHAMPIONSHIP_DRIVER_ENTRIES } from './championshipDefaults.js';
8
+ import { normalizeTeam, pickUniqueTimingCode } from './entryIdentity.js';
5
9
 
6
10
  export { DriverData, DRIVER_STAT_DEFINITIONS } from './driverData.js';
7
11
  export { VehicleData, VEHICLE_STAT_DEFINITIONS } from './vehicleData.js';
8
-
9
- export const CHAMPIONSHIP = {
10
- id: 'project-race-2026',
11
- name: 'Project Race',
12
- season: 2026,
13
- };
14
-
15
- export const CHAMPIONSHIP_ENTRY_BLUEPRINTS = [
16
- {
17
- driverId: 'budget',
18
- driverNumber: 71,
19
- timingName: 'Budget',
20
- driver: new DriverData({ pace: 52, racecraft: 74, aggression: 38, riskTolerance: 47, patience: 81, consistency: 86 }),
21
- vehicle: new VehicleData({ id: 'budget-bb01', name: 'BB-01 Ledger', power: 48, braking: 72, aero: 55, dragEfficiency: 66, mechanicalGrip: 63, weightControl: 58, tireCare: 82 }),
22
- },
23
- {
24
- driverId: 'noir',
25
- driverNumber: 13,
26
- timingName: 'Noir',
27
- driver: new DriverData({ pace: 68, racecraft: 83, aggression: 61, riskTolerance: 72, patience: 57, consistency: 64 }),
28
- vehicle: new VehicleData({ id: 'noir-nn13', name: 'NN-13 Shadow', power: 64, braking: 60, aero: 73, dragEfficiency: 57, mechanicalGrip: 69, weightControl: 54, tireCare: 51 }),
29
- },
30
- {
31
- driverId: 'vinyl',
32
- driverNumber: 33,
33
- timingName: 'HoloVinyl',
34
- driver: new DriverData({ pace: 71, racecraft: 66, aggression: 52, riskTolerance: 59, patience: 62, consistency: 58 }),
35
- vehicle: new VehicleData({ id: 'vinyl-hv33', name: 'HV-33 Groove', power: 70, braking: 49, aero: 58, dragEfficiency: 74, mechanicalGrip: 52, weightControl: 68, tireCare: 46 }),
36
- },
37
- {
38
- driverId: 'drsorriso',
39
- driverNumber: 55,
40
- timingName: 'DrSorriso',
41
- driver: new DriverData({ pace: 46, racecraft: 69, aggression: 44, riskTolerance: 43, patience: 76, consistency: 73 }),
42
- vehicle: new VehicleData({ id: 'sorriso-ds55', name: 'DS-55 Intake', power: 54, braking: 67, aero: 62, dragEfficiency: 49, mechanicalGrip: 71, weightControl: 45, tireCare: 69 }),
43
- },
44
- {
45
- driverId: 'victoria',
46
- driverNumber: 11,
47
- timingName: 'VictorIA',
48
- driver: new DriverData({ pace: 78, racecraft: 79, aggression: 58, riskTolerance: 65, patience: 55, consistency: 68 }),
49
- vehicle: new VehicleData({ id: 'victoria-vi11', name: 'VI-11 Vector', power: 76, braking: 58, aero: 65, dragEfficiency: 63, mechanicalGrip: 61, weightControl: 60, tireCare: 48 }),
50
- },
51
- {
52
- driverId: 'reminderz',
53
- driverNumber: 7,
54
- timingName: 'ReminderZ',
55
- driver: new DriverData({ pace: 41, racecraft: 61, aggression: 34, riskTolerance: 39, patience: 85, consistency: 80 }),
56
- vehicle: new VehicleData({ id: 'reminderz-rz07', name: 'RZ-07 Recall', power: 42, braking: 76, aero: 50, dragEfficiency: 72, mechanicalGrip: 58, weightControl: 63, tireCare: 88 }),
57
- },
58
- {
59
- driverId: 'clipclop',
60
- driverNumber: 14,
61
- timingName: 'ClipClop',
62
- driver: new DriverData({ pace: 73, racecraft: 88, aggression: 76, riskTolerance: 78, patience: 48, consistency: 61 }),
63
- vehicle: new VehicleData({ id: 'clipclop-cc14', name: 'CC-14 Sync', power: 68, braking: 63, aero: 59, dragEfficiency: 61, mechanicalGrip: 78, weightControl: 56, tireCare: 44 }),
64
- },
65
- {
66
- driverId: 'evolve',
67
- driverNumber: 23,
68
- timingName: 'Evolve',
69
- driver: new DriverData({ pace: 56, racecraft: 70, aggression: 63, riskTolerance: 61, patience: 58, consistency: 52 }),
70
- vehicle: new VehicleData({ id: 'evolve-ev23', name: 'EV-23 Morph', power: 60, braking: 53, aero: 79, dragEfficiency: 46, mechanicalGrip: 66, weightControl: 49, tireCare: 57 }),
71
- },
72
- {
73
- driverId: 'clash',
74
- driverNumber: 47,
75
- timingName: 'Clash',
76
- driver: new DriverData({ pace: 44, racecraft: 67, aggression: 70, riskTolerance: 69, patience: 42, consistency: 59 }),
77
- vehicle: new VehicleData({ id: 'clash-cr47', name: 'CR-47 Arena', power: 57, braking: 48, aero: 52, dragEfficiency: 55, mechanicalGrip: 73, weightControl: 52, tireCare: 64 }),
78
- },
79
- {
80
- driverId: 'core',
81
- driverNumber: 91,
82
- timingName: 'Core',
83
- driver: new DriverData({ pace: 82, racecraft: 85, aggression: 67, riskTolerance: 74, patience: 53, consistency: 72 }),
84
- vehicle: new VehicleData({ id: 'core-ai91', name: 'AI-91 Core', power: 80, braking: 61, aero: 72, dragEfficiency: 59, mechanicalGrip: 75, weightControl: 51, tireCare: 50 }),
85
- },
86
- ];
87
-
88
- export const CHAMPIONSHIP_DRIVER_ENTRIES = CHAMPIONSHIP_ENTRY_BLUEPRINTS.map((entry) => ({
89
- driverId: entry.driverId,
90
- driverNumber: entry.driverNumber,
91
- timingName: entry.timingName,
92
- }));
93
-
94
- const LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
95
-
96
- function normalizeLetters(value) {
97
- return String(value ?? '')
98
- .normalize('NFKD')
99
- .replace(/[\u0300-\u036f]/g, '')
100
- .replace(/[^a-zA-Z]/g, '')
101
- .toUpperCase();
102
- }
103
-
104
- function timingCodeCandidates(source) {
105
- const letters = normalizeLetters(source);
106
- if (!letters) return ['XXX'];
107
- if (letters.length <= 3) return [letters.padEnd(3, 'X')];
108
-
109
- const candidates = [letters.slice(0, 3)];
110
- for (let index = 1; index <= letters.length - 3; index += 1) {
111
- candidates.push(letters.slice(index, index + 3));
112
- }
113
- candidates.push(`${letters[0]}${letters[1]}${letters[letters.length - 1]}`);
114
- return [...new Set(candidates)];
115
- }
116
-
117
- function pickUniqueTimingCode(source, usedCodes) {
118
- for (const candidate of timingCodeCandidates(source)) {
119
- if (!usedCodes.has(candidate)) {
120
- usedCodes.add(candidate);
121
- return candidate;
122
- }
123
- }
124
-
125
- const letters = normalizeLetters(source).padEnd(2, 'X');
126
- for (const suffix of LETTERS) {
127
- const candidate = `${letters[0]}${letters[1]}${suffix}`;
128
- if (!usedCodes.has(candidate)) {
129
- usedCodes.add(candidate);
130
- return candidate;
131
- }
132
- }
133
-
134
- throw new Error(`Unable to create a unique three-letter timing code for "${source}"`);
135
- }
12
+ export { CHAMPIONSHIP, CHAMPIONSHIP_ENTRY_BLUEPRINTS, CHAMPIONSHIP_DRIVER_ENTRIES } from './championshipDefaults.js';
13
+ export { formatDriverNumber } from './entryIdentity.js';
136
14
 
137
15
  function assertUniqueDriverNumbers(entries) {
138
- const seen = new Set();
16
+ const seen = new Map();
139
17
  entries.forEach((entry) => {
140
18
  if (entry.driverNumber == null) return;
19
+ const driverId = entry.driverId ?? entry.id;
141
20
  if (seen.has(entry.driverNumber)) {
142
- throw new Error(`Duplicate championship driver number: ${entry.driverNumber}`);
21
+ throw new Error(`Duplicate championship driver number: ${entry.driverNumber} (${seen.get(entry.driverNumber)} and ${driverId})`);
143
22
  }
144
- seen.add(entry.driverNumber);
23
+ seen.set(entry.driverNumber, driverId);
145
24
  });
146
25
  }
147
26
 
@@ -170,29 +49,6 @@ function buildVehicleConstructorArgs(entry) {
170
49
  return vehicle.toConstructorArgs();
171
50
  }
172
51
 
173
- export function formatDriverNumber(driverNumber) {
174
- return String(driverNumber ?? '').padStart(2, '0');
175
- }
176
-
177
- function normalizeTeamIcon(source) {
178
- const letters = normalizeLetters(source);
179
- return letters.slice(0, 2).padEnd(2, 'X');
180
- }
181
-
182
- function normalizeTeam(team, driver, timingCode) {
183
- const source = team ?? driver.team;
184
- if (!source) return null;
185
- const name = source.name ?? source.id ?? `${driver.name ?? timingCode} Team`;
186
- const id = (source.id ?? normalizeLetters(name).toLowerCase()) || `${driver.id}-team`;
187
- return {
188
- ...source,
189
- id,
190
- name,
191
- color: normalizeCssColorToken(source.color ?? driver.color, driver.color),
192
- icon: source.icon ?? normalizeTeamIcon(source.name ?? source.id ?? timingCode ?? driver.name),
193
- };
194
- }
195
-
196
52
  export function buildChampionshipDriverGrid(drivers = DEMO_PROJECT_DRIVERS, entries = CHAMPIONSHIP_DRIVER_ENTRIES) {
197
53
  assertUniqueEntryDriverIds(entries);
198
54
  assertUniqueDriverNumbers(entries);
@@ -201,7 +57,7 @@ export function buildChampionshipDriverGrid(drivers = DEMO_PROJECT_DRIVERS, entr
201
57
  const blueprintByDriverId = new Map(CHAMPIONSHIP_ENTRY_BLUEPRINTS.map((entry) => [entry.driverId, entry]));
202
58
  const usedTimingCodes = new Set();
203
59
 
204
- return drivers.map((driver, index) => {
60
+ const grid = drivers.map((driver, index) => {
205
61
  const safeDriver = {
206
62
  ...driver,
207
63
  color: normalizeCssColorToken(driver.color, DEFAULT_DARK_THEME_TOKENS.primary),
@@ -217,6 +73,7 @@ export function buildChampionshipDriverGrid(drivers = DEMO_PROJECT_DRIVERS, entr
217
73
 
218
74
  return {
219
75
  ...safeDriver,
76
+ presentation: normalizeEntryPresentation(safeDriver, entry.presentation ?? safeDriver.presentation),
220
77
  code: timingCode,
221
78
  timingCode,
222
79
  raceName: timingCode,
@@ -226,6 +83,7 @@ export function buildChampionshipDriverGrid(drivers = DEMO_PROJECT_DRIVERS, entr
226
83
  racecraft: driverArgs.racecraft,
227
84
  consistency: driverArgs.consistency,
228
85
  personality: driverArgs.personality,
86
+ driverModel: safeDriver.driverModel ?? driverArgs.driverModel ?? vehicleArgs.driverModel ?? null,
229
87
  vehicle: vehicleArgs,
230
88
  constructorArgs: {
231
89
  driver: driverArgs,
@@ -238,6 +96,6 @@ export function buildChampionshipDriverGrid(drivers = DEMO_PROJECT_DRIVERS, entr
238
96
  },
239
97
  };
240
98
  });
99
+ assertUniqueDriverNumbers(grid);
100
+ return grid;
241
101
  }
242
-
243
- export const CHAMPIONSHIP_PROJECT_DRIVERS = buildChampionshipDriverGrid();
@@ -0,0 +1,87 @@
1
+ import { DriverData } from './driverData.js';
2
+ import { VehicleData } from './vehicleData.js';
3
+
4
+ export const CHAMPIONSHIP = {
5
+ id: 'project-race-2026',
6
+ name: 'Project Race',
7
+ season: 2026,
8
+ };
9
+
10
+ export const CHAMPIONSHIP_ENTRY_BLUEPRINTS = [
11
+ {
12
+ driverId: 'budget',
13
+ driverNumber: 71,
14
+ timingName: 'Budget',
15
+ driver: new DriverData({ pace: 52, racecraft: 74, aggression: 38, riskTolerance: 47, patience: 81, consistency: 86 }),
16
+ vehicle: new VehicleData({ id: 'budget-bb01', name: 'BB-01 Ledger', power: 48, braking: 72, aero: 55, dragEfficiency: 66, mechanicalGrip: 63, weightControl: 58, tireCare: 82 }),
17
+ },
18
+ {
19
+ driverId: 'noir',
20
+ driverNumber: 13,
21
+ timingName: 'Noir',
22
+ driver: new DriverData({ pace: 68, racecraft: 83, aggression: 61, riskTolerance: 72, patience: 57, consistency: 64 }),
23
+ vehicle: new VehicleData({ id: 'noir-nn13', name: 'NN-13 Shadow', power: 64, braking: 60, aero: 73, dragEfficiency: 57, mechanicalGrip: 69, weightControl: 54, tireCare: 51 }),
24
+ },
25
+ {
26
+ driverId: 'vinyl',
27
+ driverNumber: 33,
28
+ timingName: 'HoloVinyl',
29
+ driver: new DriverData({ pace: 71, racecraft: 66, aggression: 52, riskTolerance: 59, patience: 62, consistency: 58 }),
30
+ vehicle: new VehicleData({ id: 'vinyl-hv33', name: 'HV-33 Groove', power: 70, braking: 49, aero: 58, dragEfficiency: 74, mechanicalGrip: 52, weightControl: 68, tireCare: 46 }),
31
+ },
32
+ {
33
+ driverId: 'drsorriso',
34
+ driverNumber: 55,
35
+ timingName: 'DrSorriso',
36
+ driver: new DriverData({ pace: 46, racecraft: 69, aggression: 44, riskTolerance: 43, patience: 76, consistency: 73 }),
37
+ vehicle: new VehicleData({ id: 'sorriso-ds55', name: 'DS-55 Intake', power: 54, braking: 67, aero: 62, dragEfficiency: 49, mechanicalGrip: 71, weightControl: 45, tireCare: 69 }),
38
+ },
39
+ {
40
+ driverId: 'victoria',
41
+ driverNumber: 11,
42
+ timingName: 'VictorIA',
43
+ driver: new DriverData({ pace: 78, racecraft: 79, aggression: 58, riskTolerance: 65, patience: 55, consistency: 68 }),
44
+ vehicle: new VehicleData({ id: 'victoria-vi11', name: 'VI-11 Vector', power: 76, braking: 58, aero: 65, dragEfficiency: 63, mechanicalGrip: 61, weightControl: 60, tireCare: 48 }),
45
+ },
46
+ {
47
+ driverId: 'reminderz',
48
+ driverNumber: 7,
49
+ timingName: 'ReminderZ',
50
+ driver: new DriverData({ pace: 41, racecraft: 61, aggression: 34, riskTolerance: 39, patience: 85, consistency: 80 }),
51
+ vehicle: new VehicleData({ id: 'reminderz-rz07', name: 'RZ-07 Recall', power: 42, braking: 76, aero: 50, dragEfficiency: 72, mechanicalGrip: 58, weightControl: 63, tireCare: 88 }),
52
+ },
53
+ {
54
+ driverId: 'clipclop',
55
+ driverNumber: 14,
56
+ timingName: 'ClipClop',
57
+ driver: new DriverData({ pace: 73, racecraft: 88, aggression: 76, riskTolerance: 78, patience: 48, consistency: 61 }),
58
+ vehicle: new VehicleData({ id: 'clipclop-cc14', name: 'CC-14 Sync', power: 68, braking: 63, aero: 59, dragEfficiency: 61, mechanicalGrip: 78, weightControl: 56, tireCare: 44 }),
59
+ },
60
+ {
61
+ driverId: 'evolve',
62
+ driverNumber: 23,
63
+ timingName: 'Evolve',
64
+ driver: new DriverData({ pace: 56, racecraft: 70, aggression: 63, riskTolerance: 61, patience: 58, consistency: 52 }),
65
+ vehicle: new VehicleData({ id: 'evolve-ev23', name: 'EV-23 Morph', power: 60, braking: 53, aero: 79, dragEfficiency: 46, mechanicalGrip: 66, weightControl: 49, tireCare: 57 }),
66
+ },
67
+ {
68
+ driverId: 'clash',
69
+ driverNumber: 47,
70
+ timingName: 'Clash',
71
+ driver: new DriverData({ pace: 44, racecraft: 67, aggression: 70, riskTolerance: 69, patience: 42, consistency: 59 }),
72
+ vehicle: new VehicleData({ id: 'clash-cr47', name: 'CR-47 Arena', power: 57, braking: 48, aero: 52, dragEfficiency: 55, mechanicalGrip: 73, weightControl: 52, tireCare: 64 }),
73
+ },
74
+ {
75
+ driverId: 'core',
76
+ driverNumber: 91,
77
+ timingName: 'Core',
78
+ driver: new DriverData({ pace: 82, racecraft: 85, aggression: 67, riskTolerance: 74, patience: 53, consistency: 72 }),
79
+ vehicle: new VehicleData({ id: 'core-ai91', name: 'AI-91 Core', power: 80, braking: 61, aero: 72, dragEfficiency: 59, mechanicalGrip: 75, weightControl: 51, tireCare: 50 }),
80
+ },
81
+ ];
82
+
83
+ export const CHAMPIONSHIP_DRIVER_ENTRIES = CHAMPIONSHIP_ENTRY_BLUEPRINTS.map((entry) => ({
84
+ driverId: entry.driverId,
85
+ driverNumber: entry.driverNumber,
86
+ timingName: entry.timingName,
87
+ }));
@@ -1,8 +1,6 @@
1
1
  import { normalizeCustomFields } from './customFields.js';
2
2
 
3
- const RATING_MINIMUM = 0;
4
- const RATING_NEUTRAL = 50;
5
- const RATING_MAXIMUM = 100;
3
+ import { RATING_MINIMUM, RATING_NEUTRAL, RATING_MAXIMUM, clampRating, applyRating } from './ratingScale.js';
6
4
 
7
5
  export const DRIVER_STAT_DEFINITIONS = {
8
6
  pace: { minimum: RATING_MINIMUM, neutral: RATING_NEUTRAL, maximum: RATING_MAXIMUM, base: 1, variance: 0.08 },
@@ -13,17 +11,6 @@ export const DRIVER_STAT_DEFINITIONS = {
13
11
  consistency: { minimum: RATING_MINIMUM, neutral: RATING_NEUTRAL, maximum: RATING_MAXIMUM, base: 0.75, variance: 0.16 },
14
12
  };
15
13
 
16
- function clampRating(value, label) {
17
- const rating = Number(value);
18
- if (!Number.isFinite(rating)) throw new Error(`Invalid driver rating for ${label}: ${value}`);
19
- return Math.min(Math.max(rating, RATING_MINIMUM), RATING_MAXIMUM);
20
- }
21
-
22
- function applyRating(definition, value) {
23
- const normalized = (value - definition.neutral) / (definition.maximum - definition.neutral);
24
- return definition.base + normalized * definition.variance;
25
- }
26
-
27
14
  export class DriverData {
28
15
  constructor({
29
16
  pace = RATING_NEUTRAL,
@@ -33,14 +20,16 @@ export class DriverData {
33
20
  patience = RATING_NEUTRAL,
34
21
  consistency = RATING_NEUTRAL,
35
22
  customFields = [],
23
+ driverModel = null,
36
24
  } = {}) {
37
- this.pace = clampRating(pace, 'pace');
38
- this.racecraft = clampRating(racecraft, 'racecraft');
39
- this.aggression = clampRating(aggression, 'aggression');
40
- this.riskTolerance = clampRating(riskTolerance, 'riskTolerance');
41
- this.patience = clampRating(patience, 'patience');
42
- this.consistency = clampRating(consistency, 'consistency');
25
+ this.pace = clampRating(pace, 'pace', 'driver');
26
+ this.racecraft = clampRating(racecraft, 'racecraft', 'driver');
27
+ this.aggression = clampRating(aggression, 'aggression', 'driver');
28
+ this.riskTolerance = clampRating(riskTolerance, 'riskTolerance', 'driver');
29
+ this.patience = clampRating(patience, 'patience', 'driver');
30
+ this.consistency = clampRating(consistency, 'consistency', 'driver');
43
31
  this.customFields = normalizeCustomFields(customFields);
32
+ this.driverModel = driverModel;
44
33
  }
45
34
 
46
35
  ratings() {
@@ -58,6 +47,7 @@ export class DriverData {
58
47
  return {
59
48
  ratings: this.ratings(),
60
49
  customFields: this.customFields,
50
+ driverModel: this.driverModel,
61
51
  pace: applyRating(DRIVER_STAT_DEFINITIONS.pace, this.pace),
62
52
  racecraft: applyRating(DRIVER_STAT_DEFINITIONS.racecraft, this.racecraft),
63
53
  consistency: applyRating(DRIVER_STAT_DEFINITIONS.consistency, this.consistency),
@@ -0,0 +1,67 @@
1
+ import { normalizeCssColorToken } from '../config/theme/cssTokens.js';
2
+
3
+ const LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
4
+
5
+ function normalizeLetters(value) {
6
+ return String(value ?? '')
7
+ .normalize('NFKD')
8
+ .replace(/[\u0300-\u036f]/g, '')
9
+ .replace(/[^a-zA-Z]/g, '')
10
+ .toUpperCase();
11
+ }
12
+
13
+ function timingCodeCandidates(source) {
14
+ const letters = normalizeLetters(source);
15
+ if (!letters) return ['XXX'];
16
+ if (letters.length <= 3) return [letters.padEnd(3, 'X')];
17
+
18
+ const candidates = [letters.slice(0, 3)];
19
+ for (let index = 1; index <= letters.length - 3; index += 1) {
20
+ candidates.push(letters.slice(index, index + 3));
21
+ }
22
+ candidates.push(`${letters[0]}${letters[1]}${letters[letters.length - 1]}`);
23
+ return [...new Set(candidates)];
24
+ }
25
+
26
+ export function pickUniqueTimingCode(source, usedCodes) {
27
+ for (const candidate of timingCodeCandidates(source)) {
28
+ if (!usedCodes.has(candidate)) {
29
+ usedCodes.add(candidate);
30
+ return candidate;
31
+ }
32
+ }
33
+
34
+ const letters = normalizeLetters(source).padEnd(2, 'X');
35
+ for (const suffix of LETTERS) {
36
+ const candidate = `${letters[0]}${letters[1]}${suffix}`;
37
+ if (!usedCodes.has(candidate)) {
38
+ usedCodes.add(candidate);
39
+ return candidate;
40
+ }
41
+ }
42
+
43
+ throw new Error(`Unable to create a unique three-letter timing code for "${source}"`);
44
+ }
45
+
46
+ export function formatDriverNumber(driverNumber) {
47
+ return String(driverNumber ?? '').padStart(2, '0');
48
+ }
49
+
50
+ function normalizeTeamIcon(source) {
51
+ const letters = normalizeLetters(source);
52
+ return letters.slice(0, 2).padEnd(2, 'X');
53
+ }
54
+
55
+ export function normalizeTeam(team, driver, timingCode) {
56
+ const source = team ?? driver.team;
57
+ if (!source) return null;
58
+ const name = source.name ?? source.id ?? `${driver.name ?? timingCode} Team`;
59
+ const id = (source.id ?? normalizeLetters(name).toLowerCase()) || `${driver.id}-team`;
60
+ return {
61
+ ...source,
62
+ id,
63
+ name,
64
+ color: normalizeCssColorToken(source.color ?? driver.color, driver.color),
65
+ icon: source.icon ?? normalizeTeamIcon(source.name ?? source.id ?? timingCode ?? driver.name),
66
+ };
67
+ }
@@ -0,0 +1,23 @@
1
+ import { normalizePublicUrlOption } from '../config/urlOptions.js';
2
+
3
+ function text(value, fallback = '') {
4
+ return typeof value === 'string' ? value : fallback;
5
+ }
6
+
7
+ function lines(value) {
8
+ return Array.isArray(value) ? value.filter((line) => typeof line === 'string' && line.trim()) : [];
9
+ }
10
+
11
+ export function normalizeEntryPresentation(driver, source = driver.presentation ?? {}) {
12
+ const image = source?.image;
13
+ const imageSrc = normalizePublicUrlOption(image?.src, '');
14
+ return {
15
+ name: text(source?.name).trim() || driver.name,
16
+ summary: text(source?.summary, text(driver.raceData?.[0])),
17
+ technologies: lines(source?.technologies),
18
+ details: lines(source?.details ?? driver.raceData?.slice(1)),
19
+ image: imageSrc ? { src: imageSrc, alt: text(image.alt) } : null,
20
+ href: normalizePublicUrlOption(source?.href ?? driver.link, ''),
21
+ linkLabel: text(source?.linkLabel).trim() || 'Open project',
22
+ };
23
+ }
@@ -0,0 +1,160 @@
1
+ import type { TeamData } from './teamTypes.js';
2
+
3
+ export type TireCompound = 'S' | 'M' | 'H';
4
+ export interface CustomField {
5
+ label: string;
6
+ value: string;
7
+ }
8
+
9
+ export type CustomFieldInput = CustomField[] | Record<string, string>;
10
+ export interface EntryPresentation {
11
+ name?: string;
12
+ summary?: string;
13
+ technologies?: string[];
14
+ details?: string[];
15
+ image?: { src: string; alt?: string } | null;
16
+ href?: string;
17
+ linkLabel?: string;
18
+ }
19
+
20
+ export interface SimulatorDriver {
21
+ presentation?: EntryPresentation;
22
+ id: string;
23
+ name: string;
24
+ color: string;
25
+ link?: string;
26
+ icon?: string;
27
+ code?: string;
28
+ timingName?: string;
29
+ tire?: TireCompound;
30
+ raceData?: string[];
31
+ customFields?: CustomFieldInput;
32
+ team?: TeamData | null;
33
+ driverNumber?: number;
34
+ driverModel?: unknown;
35
+ }
36
+
37
+ export interface DriverRatings {
38
+ pace: number;
39
+ racecraft: number;
40
+ aggression: number;
41
+ riskTolerance: number;
42
+ patience: number;
43
+ consistency: number;
44
+ }
45
+
46
+ export interface VehicleRatings {
47
+ power: number;
48
+ braking: number;
49
+ aero: number;
50
+ dragEfficiency: number;
51
+ mechanicalGrip: number;
52
+ weightControl: number;
53
+ tireCare: number;
54
+ }
55
+
56
+ export interface DriverBlueprint extends Partial<DriverRatings> {
57
+ customFields?: CustomFieldInput;
58
+ driverModel?: unknown;
59
+ }
60
+
61
+ export interface VehicleBlueprint extends Partial<VehicleRatings> {
62
+ id?: string | null;
63
+ name?: string | null;
64
+ customFields?: CustomFieldInput;
65
+ driverModel?: unknown;
66
+ }
67
+
68
+ export interface DriverConstructorArgs {
69
+ ratings: DriverRatings;
70
+ customFields: CustomField[];
71
+ driverModel: unknown | null;
72
+ pace: number;
73
+ racecraft: number;
74
+ consistency: number;
75
+ personality: {
76
+ aggression: number;
77
+ riskTolerance: number;
78
+ patience: number;
79
+ };
80
+ }
81
+
82
+ export interface VehicleConstructorArgs {
83
+ id: string | null;
84
+ name: string | null;
85
+ ratings: VehicleRatings;
86
+ customFields: CustomField[];
87
+ driverModel: unknown | null;
88
+ powerNewtons: number;
89
+ brakeNewtons: number;
90
+ downforceCoefficient: number;
91
+ dragCoefficient: number;
92
+ tireGrip: number;
93
+ mass: number;
94
+ tireCare: number;
95
+ }
96
+
97
+ export class DriverData {
98
+ constructor(input?: DriverBlueprint);
99
+ readonly pace: number;
100
+ readonly racecraft: number;
101
+ readonly aggression: number;
102
+ readonly riskTolerance: number;
103
+ readonly patience: number;
104
+ readonly consistency: number;
105
+ readonly customFields: CustomField[];
106
+ readonly driverModel: unknown | null;
107
+ ratings(): DriverRatings;
108
+ toConstructorArgs(): DriverConstructorArgs;
109
+ }
110
+
111
+ export class VehicleData {
112
+ constructor(input?: VehicleBlueprint);
113
+ readonly id: string | null;
114
+ readonly name: string | null;
115
+ readonly power: number;
116
+ readonly braking: number;
117
+ readonly aero: number;
118
+ readonly dragEfficiency: number;
119
+ readonly mechanicalGrip: number;
120
+ readonly weightControl: number;
121
+ readonly tireCare: number;
122
+ readonly customFields: CustomField[];
123
+ readonly driverModel: unknown | null;
124
+ ratings(): VehicleRatings;
125
+ toConstructorArgs(): VehicleConstructorArgs;
126
+ }
127
+
128
+ export interface ChampionshipEntryBlueprint {
129
+ presentation?: EntryPresentation;
130
+ driverId: string;
131
+ driverNumber?: number;
132
+ timingName?: string;
133
+ driver?: DriverData | DriverBlueprint;
134
+ vehicle?: VehicleData | VehicleBlueprint;
135
+ team?: TeamData;
136
+ }
137
+
138
+ export interface NormalizedSimulatorDriver extends SimulatorDriver {
139
+ code: string;
140
+ timingCode: string;
141
+ raceName: string;
142
+ driverNumber: number;
143
+ team: TeamData | null;
144
+ pace: number;
145
+ racecraft: number;
146
+ consistency: number;
147
+ personality: DriverConstructorArgs['personality'];
148
+ vehicle: VehicleConstructorArgs;
149
+ constructorArgs: {
150
+ driver: DriverConstructorArgs;
151
+ vehicle: VehicleConstructorArgs;
152
+ };
153
+ championship: {
154
+ id: string;
155
+ name: string;
156
+ season: number;
157
+ entryIndex: number;
158
+ vehicleId: string | null;
159
+ };
160
+ }