Per-junction medal targets, measured; fix the last unplayable junction

Medal targets are no longer one uniform 15/35/60k guess across every event.
tools/tune_junctions.gd plays each junction 12 times with varied launches and
sets bronze/silver/gold at the 35th/65th/88th percentile of actual damage, so
they now range from $4k at Cliffs Corner to $73k gold at Melbourne x Merivale.
The old numbers made half the events impossible and the other half trivial.

Grey x Glenelg scored $0 in 12/12 runs and took three fixes, each found by
measuring rather than reading:
- The launch sat on the street centreline, so the car drove cleanly BETWEEN the
  two convoy lanes -- closest pass 2.9 m, almost exactly the 2.7 m lane offset.
  It now launches in a lane. (The first attempt offset the wrong way and widened
  the pass to 5.5 m, which is how the sign error announced itself.)
- int(L / headway) floors to one car on a short clipped stream, so that junction
  fielded 9 convoy cars against the usual 20-25. Minimum 3 per stream, evenly
  spaced to fit.
Result: $0 -> $91k, convoy 9 -> 18, and 0 of 9 junctions now unplayable.

The tuner has turned out to be the event validator as much as a balance tool --
"scores nothing in every run" is an unplayable event, not a tuning problem.

Also from the review: the deformer no longer crumples det_* panels after they've
been shed onto their own body, and notes that rebuilding an ArrayMesh drops any
imported LODs.

Honest gap: Cliffs Corner and Bridge Approach show median == max at ~$4.7k --
winnable and correctly tuned, but the run ends on scenery before the convoy is
reached. Both are curved approaches; they want better crossings picked by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-29 00:15:05 +10:00
parent 0c13c82eac
commit aac4384c05
10 changed files with 800 additions and 687 deletions

View File

@ -42,6 +42,22 @@ Controls: WASD/arrows drive, Shift drift, Space boost, R reset (or retry after C
runs**: a second Godot instance contends for the `.godot` import lock, and a level
rebuild mid-flight silently feeds it half-written levels, which reads as "this event
scores zero" rather than as an error.
The tuner is also the event *validator*. An event that scores nothing across every
run isn't badly tuned, it's unplayable, and it prints `!!` for exactly that. Three
separate causes turned up that way, none visible by reading the generator:
convoy streams clipped so wide that you thread the intersection through a 20 m gap;
a launch point on the street centreline threading cleanly *between* the two convoy
lanes; and `int(L / headway)` flooring to one car on a short stream, leaving nine
convoy cars where the working events have twenty. Re-run it after any change to the
generator, the launch geometry, or convoy spacing — all three feed the numbers.
Read the spread it prints, not just the targets. **Cliffs Corner** and **Bridge
Approach** both show median == max at a low ceiling (~$4.7k), which means the run
reliably ends on the first thing you hit and the convoy is never reached — they're
winnable and honestly tuned, but they're scenery crashes rather than pile-ups. Both
sit on curved approaches where the launch street bends away from the intersection.
Worth hand-picking better crossings for those two.
- **Race** — 3 laps vs 3 rubber-banded rivals on the level's `RacePath`.
- **Drag** — NFSU2's event, our code: a straight-line run down the level's longest
genuinely straight stretch of wide road (the builder finds it and drops
@ -227,6 +243,18 @@ Make a folder `levels/<id>/` containing `level.glb` (or `level.tscn`). Blender c
/Applications/Godot.app/Contents/MacOS/Godot --headless --path . --script tests/smoke.gd
```
Covers all seven modes (cruise, crash, junction, outrun, pursuit, drag, derby,
race) in about 30 s, printing a `.. stage` line as it enters each one.
Failures go through `_check()`, never bare `assert()`. A failed assert in a
headless `SceneTree` script halts the script but leaves the tree running, so the
process spins forever at 100% CPU instead of failing — it looks exactly like a
slow test. That cost 53 minutes of wall clock to spot once; the stage lines and
`_check()` exist so it can't happen twice.
Only one Godot instance may touch the project at a time: a second contends for
the `.godot` import lock and can kill the first mid-run.
Visual check (opens a window, saves a PNG):
```

View File

@ -2,445 +2,445 @@
{
"name": "Alice x George",
"spawn": {
"x": -165.3192678228276,
"z": 159.9955423589618,
"dx": 0.7668331983120437,
"dz": -0.6418464348787193
"dx": 0.766833198312044,
"dz": -0.641846434878719,
"x": -163.586282448655,
"z": 162.065991994404
},
"streams": [
{
"interval": 1.5,
"pts": [
[
-71.62300756706763,
-71.6230075670676,
85.0918821008208
],
[
-72.10279819575109,
85.49061753866586
-72.1027981957511,
85.4906175386659
],
[
-82.55345192912239,
94.80588029839768
-82.5534519291224,
94.8058802983977
],
[
-104.44928325520195,
113.36472803055571
-104.449283255202,
113.364728030556
],
[
-114.55295338424757,
122.93815788447793
-114.552953384248,
122.938157884478
],
[
-118.63551709634598,
126.33214688818488
-118.635517096346,
126.332146888185
],
[
-124.17972594270637,
130.60060399325408
-124.179725942706,
130.600603993254
],
[
-132.4126578466066,
136.84137198262476
-132.412657846607,
136.841371982625
],
[
-135.42619497476394,
139.13115862719584
-135.426194974764,
139.131158627196
],
[
-137.0173915026139,
140.32992242527928
-137.017391502614,
140.329922425279
],
[
-142.22691046279974,
144.18129435873087
-142.2269104628,
144.181294358731
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-75.361941989609,
84.82292365250544
84.8229236525054
],
[
-80.93953886827424,
78.61163770120406
-80.9395388682742,
78.6116377012041
],
[
-82.56599989409156,
-82.5659998940916,
76.7348960854059
],
[
-115.40558143561937,
38.74155522137386
-115.405581435619,
38.7415552213739
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-145.4370805808313,
139.83908204087984
-145.437080580831,
139.83908204088
],
[
-140.2367304705485,
135.99450357474848
-140.236730470549,
135.994503574748
],
[
-138.6871885076824,
134.82698377267795
-138.687188507682,
134.826983772678
],
[
-135.67603860636424,
132.53900681717874
-135.676038606364,
132.539006817179
],
[
-127.45466133363814,
126.30702760692736
-127.454661333638,
126.307027606927
],
[
-121.99640893818653,
122.1055163120108
-121.996408938187,
122.105516312011
],
[
-118.19749682812089,
118.9535245154313
-118.197496828121,
118.953524515431
],
[
-108.01476176313997,
109.30919316947609
-108.01476176314,
109.309193169476
],
[
-86.07852578965569,
90.71517690194068
-86.0785257896557,
90.7151769019407
],
[
-75.68968831102165,
81.45400766143295
-75.6896883110217,
81.454007661433
],
[
-75.08305996358739,
80.94603629889595
-75.0830599635874,
80.9460362988959
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
-111.32018174165945,
35.21033597859207
-111.320181741659,
35.2103359785921
],
[
-78.48079808538708,
73.20344791473178
-78.4807980853871,
73.2034479147318
],
[
-76.9072614072839,
75.01987789888024
75.0198778988802
],
[
-71.34412554104603,
-71.344125541046,
81.2149947472113
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
-72.11668883510822,
80.61865688274891
-72.1166888351082,
80.6186568827489
],
[
-63.31551687082244,
85.23196646473183
-63.3155168708224,
85.2319664647318
],
[
-54.55178175870453,
95.53822905343011
-54.5517817587045,
95.5382290534301
],
[
-39.78320194928109,
112.96784568335697
-39.7832019492811,
112.967845683357
],
[
-38.24953472930413,
114.64142478239386
-38.2495347293041,
114.641424782394
],
[
-37.466790819459995,
115.77089209241056
-37.46679081946,
115.770892092411
],
[
-36.90754547102499,
116.86830840435711
-36.907545471025,
116.868308404357
],
[
-36.64154284861633,
117.96012844030032
-36.6415428486163,
117.9601284403
],
[
-36.54962586770791,
118.92829018795193
-36.5496258677079,
118.928290187952
],
[
-36.58380403095183,
120.63163096695364
-36.5838040309518,
120.631630966954
],
[
-36.733516672801336,
121.77324800253669
-36.7335166728013,
121.773248002537
],
[
-36.949600973076684,
122.55506784741175
-36.9496009730767,
122.555067847412
],
[
-37.31118692587169,
123.40422578212599
-37.3111869258717,
123.404225782126
],
[
-38.02064964168787,
124.38554265247478
-38.0206496416879,
124.385542652475
],
[
-39.45793344555888,
125.80100694689808
-39.4579334455589,
125.801006946898
],
[
-41.96015977939887,
127.8781767661959
-41.9601597793989,
127.878176766196
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-45.40767874247836,
123.72190283402932
-45.4076787424784,
123.721902834029
],
[
-43.03089623055088,
121.75206425317394
-43.0308962305509,
121.752064253174
],
[
-41.9792929338678,
120.7127857471952
120.712785747195
],
[
-41.99739811738572,
120.72105141795794
-41.9973981173857,
120.721051417958
],
[
-42.06268798731611,
120.81830615252781
-42.0626879873161,
120.818306152528
],
[
-42.041735423545425,
120.78187839726885
-42.0417354235454,
120.781878397269
],
[
-41.974164266802894,
120.30911503284231
-41.9741642668029,
120.309115032842
],
[
-41.94908321684239,
119.00484301205215
-41.9490832168424,
119.004843012052
],
[
-41.975684667956976,
118.80092035936316
-41.975684667957,
118.800920359363
],
[
-41.94671891520851,
118.80911519521383
-41.9467189152085,
118.809115195214
],
[
-42.0788310934576,
118.57964350736775
118.579643507368
],
[
-42.38250615411705,
118.1168452176333
-42.3825061541171,
118.116845217633
],
[
-43.89081206390128,
116.47320431640294
-43.8908120639013,
116.473204316403
],
[
-58.67282832403665,
99.02778134654227
-58.6728283240367,
99.0277813465423
],
[
-66.81772305118318,
-66.8177230511832,
89.3422647349603
],
[
-74.5893786955468,
85.41926151696784
85.4192615169678
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
}
],
"targets": [
15000,
35000,
60000
26500,
29000,
46500
]
},
{
"name": "Margaret x Edward",
"spawn": {
"x": 276.7158840463856,
"z": -371.11587218595514,
"dx": -0.7427057931255263,
"dz": 0.6696178797327494
"dx": -0.742705793125526,
"dz": 0.669617879732749,
"x": 274.907915771107,
"z": -373.121177827394
},
"streams": [
{
"interval": 1.5,
"pts": [
[
185.8338152967176,
-292.8126451679154
185.833815296718,
-292.812645167915
],
[
193.65593395098287,
-299.88251164434115
193.655933950983,
-299.882511644341
],
[
211.37786867540518,
-315.2307257048046
211.377868675405,
-315.230725704805
],
[
216.02310568686988,
-319.25459820267537
216.02310568687,
-319.254598202675
],
[
237.6429961947087,
-338.58229333857753
237.642996194709,
-338.582293338578
],
[
239.66099718191472,
-341.2883463335457
239.660997181915,
-341.288346333546
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
179.81812157632743,
-304.16623584225226
179.818121576327,
-304.166235842252
],
[
181.35983927566346,
-302.4069925620872
181.359839275663,
-302.406992562087
],
[
184.2133034529848,
-299.1448137854387
184.213303452985,
-299.144813785439
],
[
189.71555797124375,
-292.5416508365839
189.715557971244,
-292.541650836584
],
[
194.9234531376747,
-286.3366793395667
194.923453137675,
-286.336679339567
],
[
234.05675616721896,
-241.1315253602194
234.056756167219,
-241.131525360219
],
[
244.50993125963066,
-229.32934036140196
244.509931259631,
-229.329340361402
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
243.95347049022743,
-338.01196526635846
243.953470490227,
-338.011965266358
],
[
241.32949950132615,
-334.63644826175323
241.329499501326,
-334.636448261753
],
[
219.61114951437548,
-315.21901379742513
219.611149514375,
-315.219013797425
],
[
214.91318335480702,
-311.1488694949895
214.913183354807,
-311.14886949499
],
[
197.21785955435436,
-295.8238559560563
197.217859554354,
-295.823855956056
],
[
189.45480420951895,
-288.8065948320647
189.454804209519,
-288.806594832065
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
240.4674984004257,
-225.74901403845038
240.467498400426,
-225.74901403845
],
[
229.982416914263,
-237.5875502401286
-237.587550240129
],
[
190.83429465784906,
-282.80981346079136
190.834294657849,
-282.809813460791
],
[
185.5730615349928,
-289.07758916339617
185.573061534993,
-289.077589163396
],
[
180.09260788812207,
-295.6548470145767
180.092607888122,
-295.654847014577
],
[
177.29669340310514,
-298.8501898379666
177.296693403105,
-298.850189837967
],
[
175.7569318305437,
-300.6071997580377
175.756931830544,
-300.607199758038
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
}
],
"targets": [
15000,
35000,
60000
22500,
25000,
30000
]
}
]
]

View File

@ -2,149 +2,149 @@
{
"name": "Cliffs Corner",
"spawn": {
"x": 19.82910866163153,
"z": -91.88863639399733,
"dx": 0.7116289476302671,
"dz": -0.7025555073406219
"dx": 0.711628947630267,
"dz": -0.702555507340622,
"x": 21.7260085314512,
"z": -89.9672382353956
},
"streams": [
{
"interval": 1.5,
"pts": [
[
63.92619388342862,
-109.98937518013152
63.9261938834286,
-109.989375180132
],
[
78.3750416498091,
-148.8340213803462
-148.834021380346
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
106.13660245700423,
-179.68219050814878
106.136602457004,
-179.682190508149
],
[
105.14898470103478,
-213.87167130817437
105.148984701035,
-213.871671308174
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
83.43625143590378,
-146.95142862004474
83.4362514359038,
-146.951428620045
],
[
68.98740366952329,
-108.10678241983003
68.9874036695233,
-108.10678241983
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
110.54673313420321,
-214.02759389225906
110.546733134203,
-214.027593892259
],
[
111.53435089017266,
-179.83811309223347
111.534350890173,
-179.838113092233
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
110.03793689684629,
-168.49583563849748
110.037936896846,
-168.495835638497
],
[
105.07701815835642,
-166.64105720795808
105.077018158356,
-166.641057207958
],
[
102.76621908994164,
-165.6886852292976
102.766219089942,
-165.688685229298
],
[
100.0562323833477,
-163.76233037299872
100.056232383348,
-163.762330372999
],
[
96.9873892973125,
-160.78656603278262
-160.786566032783
],
[
94.50499850150788,
-158.03413164414619
94.5049985015079,
-158.034131644146
],
[
91.4342291831665,
-153.71805517412884
-153.718055174129
],
[
82.70848350683406,
-145.88280488205245
82.7084835068341,
-145.882804882052
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
79.10280957887883,
-149.90264511833848
79.1028095788788,
-149.902645118338
],
[
87.56161285248508,
-157.4814104264721
87.5616128524851,
-157.481410426472
],
[
90.25853122720883,
90.2585312272088,
-161.369924756482
],
[
93.10927630970947,
-164.5442567671812
93.1092763097095,
-164.544256767181
],
[
96.57846276532892,
-167.89332522702423
96.5784627653289,
-167.893325227024
],
[
100.07071556655998,
-170.36781557108517
100.07071556656,
-170.367815571085
],
[
103.11280473039093,
-171.67115319207213
103.112804730391,
-171.671153192072
],
[
108.14657768085227,
-173.5537759619962
108.146577680852,
-173.553775961996
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
}
],
"targets": [
15000,
35000,
60000
4000,
6500,
10500
]
}
]
]

View File

@ -2,397 +2,397 @@
{
"name": "Albert x Adelaide",
"spawn": {
"x": 18.671705882497893,
"z": -22.919507778110138,
"dx": -0.6633868531476461,
"dz": -0.7482766086621067
"dx": -0.663386853147646,
"dz": -0.748276608662107,
"x": 20.6920527258856,
"z": -24.7106522816088
},
"streams": [
{
"interval": 1.5,
"pts": [
[
-58.84614846630014,
-114.42417420924829
-58.8461484663001,
-114.424174209248
],
[
-53.880474905008,
-108.39185473942744
-108.391854739427
],
[
-51.24051503498836,
-105.52578654474546
-51.2405150349884,
-105.525786544745
],
[
-20.352984240690695,
-70.76962163514554
-20.3529842406907,
-70.7696216351455
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-86.70325250389301,
-94.08771386994844
-86.703252503893,
-94.0877138699484
],
[
-77.60701968874399,
-101.92094169090129
-77.607019688744,
-101.920941690901
],
[
-72.06522503228508,
-106.94786296530025
-72.0652250322851,
-106.9478629653
],
[
-68.79209061830386,
-109.50838129624599
-68.7920906183039,
-109.508381296246
],
[
-62.692661313561906,
-114.75385541597699
-62.6926613135619,
-114.753855415977
],
[
-56.45081914916183,
-120.13849830634709
-56.4508191491618,
-120.138498306347
],
[
-53.14418135326541,
-122.9809049049611
-53.1441813532654,
-122.980904904961
],
[
-38.75486932340002,
-135.5677577748611
-38.7548693234,
-135.567757774861
],
[
-8.138880164463979,
-163.05430391717795
-8.13888016446398,
-163.054303917178
],
[
7.537426213222782,
-176.88629254786764
7.53742621322278,
-176.886292547868
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-24.38938905950134,
-67.18250076477023
-24.3893890595013,
-67.1825007647702
],
[
-55.272013374374126,
-101.93315225504524
-55.2720133743741,
-101.933152255045
],
[
-57.98533525129133,
-104.88327646076375
-57.9853352512913,
-104.883276460764
],
[
-63.01508813331892,
-110.99198219061685
-63.0150881333189,
-110.991982190617
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
11.110167779562063,
-172.83715465195058
11.1101677795621,
-172.837154651951
],
[
-4.543064055336218,
-159.02564328250827
-4.54306405533622,
-159.025643282508
],
[
-35.16387720923361,
-131.5347966250365
-35.1638772092336,
-131.534796625037
],
[
-49.59536988091847,
-118.91077749494814
-49.5953698809185,
-118.910777494948
],
[
-52.92601130212681,
-116.04756569366813
-52.9260113021268,
-116.047565693668
],
[
-59.168575286057155,
-110.66230098388816
-59.1685752860572,
-110.662300983888
],
[
-65.33626025215173,
-105.35901550369637
-65.3362602521517,
-105.359015503696
],
[
-68.5423745958537,
-102.85524463438601
-102.855244634386
],
[
-74.0429581637501,
-97.86416150877923
-97.8641615087792
],
[
-83.17948939242227,
-83.1794893924223,
-89.9958813300908
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
-63.049013950748005,
-111.03404120330288
-63.049013950748,
-111.034041203303
],
[
-67.67311197746825,
-116.88556835863362
-67.6731119774683,
-116.885568358634
],
[
-69.33640432781439,
-119.01651536179543
-69.3364043278144,
-119.016515361795
],
[
-72.74965354025018,
-122.55658461736515
-72.7496535402502,
-122.556584617365
],
[
-98.24408864239251,
-142.51997682349207
-98.2440886423925,
-142.519976823492
],
[
-106.03262882232805,
-149.46281622691504
-106.032628822328,
-149.462816226915
],
[
-127.2548410984026,
-167.5083127878196
-127.254841098403,
-167.50831278782
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-123.7567869560363,
-171.62214521234466
-123.756786956036,
-171.622145212345
],
[
-102.50831375382124,
-153.55417337313145
-102.508313753821,
-153.554173373131
],
[
-94.84937670776534,
-146.71949237661244
-94.8493767077653,
-146.719492376612
],
[
-69.3394816295263,
-126.74355578269574
-126.743555782696
],
[
-65.32529933336386,
-122.63190423762802
-65.3252993333639,
-122.631904237628
],
[
-63.432906391639634,
-120.22931724123647
-63.4329063916396,
-120.229317241236
],
[
-58.81222264887106,
-114.38211519656227
-58.8122226488711,
-114.382115196562
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
}
],
"targets": [
15000,
35000,
60000
23000,
25500,
29500
]
},
{
"name": "Edward x Elizabeth",
"spawn": {
"x": 173.87950117253465,
"z": -177.50987452854054,
"dx": 0.688388502507091,
"dz": 0.7253421741606128
"dz": 0.725342174160613,
"x": 171.921077302301,
"z": -175.651225571771
},
"streams": [
{
"interval": 1.5,
"pts": [
[
228.7426016851313,
-123.80938841141814
228.742601685131,
-123.809388411418
],
[
250.48785179439338,
-101.12418691157934
250.487851794393,
-101.124186911579
],
[
253.19184475779528,
-98.13216139604938
253.191844757795,
-98.1321613960494
],
[
258.48528328991137,
-92.28343983096748
258.485283289911,
-92.2834398309675
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
184.8196819904455,
-31.59973983603264
184.819681990446,
-31.5997398360326
],
[
200.9901724381037,
-45.56696118289086
200.990172438104,
-45.5669611828909
],
[
206.29822476676847,
-50.115127103271014
206.298224766768,
-50.115127103271
],
[
208.01277161716473,
-51.59585061763732
208.012771617165,
-51.5958506176373
],
[
243.56749157860247,
-82.55471207644285
243.567491578602,
-82.5547120764429
],
[
246.1313684904348,
-84.84463727526342
246.131368490435,
-84.8446372752634
],
[
254.68470539632878,
-92.48524564052305
254.684705396329,
-92.485245640523
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
254.4815819963581,
-88.65985376851496
254.481581996358,
-88.659853768515
],
[
249.1869798707451,
-94.50986140343801
249.186979870745,
-94.509861403438
],
[
246.5771986992584,
-97.40037268829819
246.577198699258,
-97.4003726882982
],
[
224.8443191668579,
-120.07262598818004
224.844319166858,
-120.07262598818
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
258.28215988994066,
258.282159889941,
-88.4580479589594
],
[
249.72869976275985,
-80.81732952477397
249.72869976276,
-80.817329524774
],
[
247.11707918748664,
-78.48526152300741
247.117079187487,
-78.4852615230074
],
[
211.55809324014456,
-47.52268298180012
211.558093240145,
-47.5226829818001
],
[
209.81572383420533,
-46.017908496633005
209.815723834205,
-46.017908496633
],
[
204.51598169911696,
-41.47689161686228
204.515981699117,
-41.4768916168623
],
[
188.34948976520272,
-27.51312056415722
188.349489765203,
-27.5131205641572
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
258.4805413787254,
-92.28866542876725
258.480541378725,
-92.2886654287672
],
[
262.2737122591504,
-88.11954201221562
262.27371225915,
-88.1195420122156
],
[
266.8273793037693,
266.827379303769,
-83.1100839184909
],
[
270.87333374760516,
-78.65784777138674
270.873333747605,
-78.6578477713867
],
[
298.08928934155523,
-47.30427629341134
298.089289341555,
-47.3042762934113
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
254.70925209424783,
-92.50690682734246
254.709252094248,
-92.5069068273425
],
[
259.70303711688206,
259.703037116882,
-96.8600914480509
],
[
263.66545095997486,
263.665450959975,
-100.3447248693
],
[
288.61557906385735,
-122.1176793727558
288.615579063857,
-122.117679372756
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
}
],
"targets": [
15000,
35000,
60000
22000,
24500,
28500
]
}
]
]

View File

@ -2,345 +2,345 @@
{
"name": "Melbourne x Merivale",
"spawn": {
"x": -598.2786454342968,
"z": -212.65545062278278,
"dx": 0.8528773292725991,
"dz": -0.5221113494388324
"dx": 0.852877329272599,
"dz": -0.522111349438832,
"x": -596.868944790812,
"z": -210.352681833747
},
"streams": [
{
"interval": 1.5,
"pts": [
[
-522.2431469994345,
-263.2916099068527
-522.243146999435,
-263.291609906853
],
[
-513.2984743659174,
-268.5646201058227
-513.298474365917,
-268.564620105823
],
[
-505.76645618454586,
-272.8477143314658
-505.766456184546,
-272.847714331466
],
[
-497.90899041561073,
-277.2629244179048
-497.908990415611,
-277.262924417905
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-518.3319604245614,
-316.20816625786284
-518.331960424561,
-316.208166257863
],
[
-502.53998920863535,
-289.11772214349674
-502.539989208635,
-289.117722143497
],
[
-501.0310720126822,
-286.5339989119062
-501.031072012682,
-286.533998911906
],
[
-499.3074920997309,
-283.76972490400925
-499.307492099731,
-283.769724904009
],
[
-498.93054530357944,
-283.2413778417831
-498.930545303579,
-283.241377841783
],
[
-494.34501447411924,
-494.345014474119,
-276.414559193545
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-495.26368453007734,
-272.55523318206934
-495.263684530077,
-272.555233182069
],
[
-503.1094123444767,
-268.14663806831595
-503.109412344477,
-268.146638068316
],
[
-510.58925218160783,
-263.8934194939837
-510.589252181608,
-263.893419493984
],
[
-519.5007896114952,
-258.6397848932893
-519.500789611495,
-258.639784893289
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
-498.82766047156883,
-273.4035984064291
-498.827660471569,
-273.403598406429
],
[
-503.4072640108861,
-280.2216113581537
-503.407264010886,
-280.221611358154
],
[
-503.85991989590116,
-280.8653486954825
-503.859919895901,
-280.865348695483
],
[
-505.6522751490968,
-283.7403486882755
-505.652275149097,
-283.740348688276
],
[
-507.20498688872703,
-286.3978330566516
-507.204986888727,
-286.397833056652
],
[
-522.997174842978,
-313.4886489421985
-313.488648942198
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
-497.7097209821479,
-277.3642792991366
-497.709720982148,
-277.364279299137
],
[
-489.2653413897242,
-281.21700470668776
-489.265341389724,
-281.217004706688
],
[
-470.16395422466655,
-292.9741741951012
-470.163954224667,
-292.974174195101
],
[
-453.3973200149641,
-303.3696498434199
-453.397320014964,
-303.36964984342
],
[
-438.7171383481014,
-313.0237718470059
-438.717138348101,
-313.023771847006
],
[
-428.17568968279613,
-319.6952218182481
-428.175689682796,
-319.695221818248
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-490.9127030755138,
-271.2416907037699
-490.912703075514,
-271.24169070377
],
[
-487.07948135301893,
-265.2321814476218
-487.079481353019,
-265.232181447622
],
[
-460.79611403958603,
-221.7057389459239
-460.796114039586,
-221.705738945924
],
[
-449.78766148749827,
-203.62165684766288
-449.787661487498,
-203.621656847663
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
}
],
"targets": [
15000,
35000,
60000
35500,
38000,
73000
]
},
{
"name": "Grey x Glenelg",
"spawn": {
"x": -135.06485953132193,
"z": -97.50127625771249,
"dx": 0.5279471843098005,
"dz": 0.8492772047920241
"dx": 0.5279471843098,
"dz": 0.849277204792024,
"x": -137.35790798426,
"z": -96.075818860076
},
"streams": [
{
"interval": 1.5,
"pts": [
[
-66.8941396578206,
18.021454924938546
18.0214549249385
],
[
-68.93721227192415,
14.729250272207434
-68.9372122719242,
14.7292502722074
],
[
-74.03417645108553,
5.788135121643277
-74.0341764510855,
5.78813512164328
],
[
-78.66584427851572,
-1.692678191469608
-78.6658442785157,
-1.69267819146961
],
[
-82.82069253186671,
-8.344352365976752
-82.8206925318667,
-8.34435236597675
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-33.05020815528413,
-14.758620186231095
-33.0502081552841,
-14.7586201862311
],
[
-35.06751528318885,
-14.599409411489315
-35.0675152831888,
-14.5994094114893
],
[
-37.27001626186296,
-13.996146050544787
-37.270016261863,
-13.9961460505448
],
[
-43.35852481930847,
-10.760668273887504
-43.3585248193085,
-10.7606682738875
],
[
-53.694918113870564,
-4.204520462618362
-53.6949181138706,
-4.20452046261836
],
[
-56.331462588402815,
-2.4702458690280507
-56.3314625884028,
-2.47024586902805
],
[
-63.091151948462084,
2.2185465904455017
-63.0911519484621,
2.2185465904455
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
-78.24074203456223,
-11.205130833848836
-78.2407420345622,
-11.2051308338488
],
[
-74.07981104388904,
-4.543695408341438
-74.079811043889,
-4.54369540834144
],
[
-69.38832494823436,
3.0356700785490762
-69.3883249482344,
3.03567007854908
],
[
-64.27327518677029,
12.007542928328153
-64.2732751867703,
12.0075429283282
],
[
-62.30660151137585,
15.172859875635417
-62.3066015113759,
15.1728598756354
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
-66.1688465571084,
-2.2185465904455017
-2.2185465904455
],
[
-59.37835881390333,
-6.9285441308125515
-59.3783588139033,
-6.92854413081255
],
[
-56.602733425687326,
-8.754752337189586
-56.6027334256873,
-8.75475233718959
],
[
-46.12004993919494,
-15.401140126214159
-46.1200499391949,
-15.4011401262142
],
[
-39.50773651812935,
-18.910676349026538
-39.5077365181293,
-18.9106763490265
],
[
-36.1005431869604,
-19.899678588618844
-19.8996785886188
],
[
-33.51553068408273,
-20.138534213814854
-33.5155306840827,
-20.1385342138149
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
-62.42866919418658,
-1.5633764655737972
-62.4286691941866,
-1.5633764655738
],
[
-56.22534149367183,
7.176315693408943
-56.2253414936718,
7.17631569340894
],
[
-31.608294574960304,
46.399012606383145
-31.6082945749603,
46.3990126063831
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
-105.44415325837058,
17.29338138921854
-105.444153258371,
17.2933813892185
],
[
-124.85549758561747,
29.02803771609806
-124.855497585617,
29.0280377160981
],
[
-145.9651989732041,
41.680234308480806
-145.965198973204,
41.6802343084808
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
}
],
"targets": [
15000,
35000,
60000
22500,
25000,
29000
]
}
]
]

View File

@ -2,185 +2,185 @@
{
"name": "Bridge Approach",
"spawn": {
"x": 32.83372074498513,
"z": -118.28842427027045,
"dx": -0.11409585864571734,
"dz": -0.9934697454074263
"dx": -0.114095858645717,
"dz": -0.993469745407426,
"x": 35.5160890575852,
"z": -118.596483088614
},
"streams": [
{
"interval": 1.5,
"pts": [
[
21.82446567810751,
-237.81387509878263
21.8244656781075,
-237.813875098783
],
[
23.145720027383163,
-226.45520770652828
23.1457200273832,
-226.455207706528
],
[
24.788209612143817,
-213.84815371354867
24.7882096121438,
-213.848153713549
],
[
26.145569174952232,
-200.29741976993301
26.1455691749522,
-200.297419769933
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
2.199941588527042,
-240.32717119002737
2.19994158852704,
-240.327171190027
],
[
-2.5248172223755736,
-243.91950536774928
-2.52481722237557,
-243.919505367749
],
[
-6.325349530807717,
-6.32534953080772,
-247.60965512377
],
[
-15.183983237537262,
-255.2613583327868
-15.1839832375373,
-255.261358332787
],
[
-19.822722086570074,
-258.6729952966697
-19.8227220865701,
-258.67299529667
],
[
-24.279515332983824,
-261.70335578638884
-24.2795153329838,
-261.703355786389
],
[
-29.011437974400625,
-263.89076106496833
-29.0114379744006,
-263.890761064968
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.8,
"pts": [
[
20.772447151780117,
-199.75931223039953
20.7724471517801,
-199.7593122304
],
[
19.423377981138138,
-213.23286388675078
19.4233779811381,
-213.232863886751
],
[
17.78654515972297,
-225.7924522932427
17.786545159723,
-225.792452293243
],
[
16.46063564037367,
-237.18991410136036
16.4606356403737,
-237.18991410136
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.8,
"pts": [
[
-26.737042902514784,
-268.7884265350654
-26.7370429025148,
-268.788426535065
],
[
-21.61124337794858,
-266.3980682135746
-21.6112433779486,
-266.398068213575
],
[
-16.704188313209045,
-263.0814811029762
-16.704188313209,
-263.081481102976
],
[
-11.761774350471743,
-259.43849686691397
-11.7617743504717,
-259.438496866914
],
[
-2.7224195143774796,
-251.63195487598963
-2.72241951437748,
-251.63195487599
],
[
0.9839307874462617,
-248.02422063236523
0.983930787446262,
-248.024220632365
],
[
5.4649806575202335,
-244.6282780101317
5.46498065752023,
-244.628278010132
]
],
"speed": 11.0,
"interval": 1.8
"speed": 11.0
},
{
"interval": 1.5,
"pts": [
[
11.27070084290225,
-197.84784324166512
11.2707008429023,
-197.847843241665
],
[
10.896175804718348,
-204.76641770228525
10.8961758047183,
-204.766417702285
],
[
10.476477879675205,
-211.20975788808624
10.4764778796752,
-211.209757888086
],
[
9.92304078347784,
-216.20700696593718
-216.207006965937
],
[
8.932958301853615,
-221.62531766816238
8.93295830185362,
-221.625317668162
],
[
7.508738699199866,
-226.8653717079938
7.50873869919987,
-226.865371707994
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
},
{
"interval": 1.5,
"pts": [
[
28.875188928696993,
-229.49242546244793
28.875188928697,
-229.492425462448
],
[
28.72548025209394,
-214.05366159277472
28.7254802520939,
-214.053661592775
],
[
28.150874632761848,
-209.45956029627345
28.1508746327618,
-209.459560296273
],
[
27.279147353059784,
-204.7812989955361
27.2791473530598,
-204.781298995536
],
[
26.094879529030933,
-199.44340263065618
26.0948795290309,
-199.443402630656
]
],
"speed": 12.0,
"interval": 1.5
"speed": 12.0
}
],
"targets": [
15000,
35000,
60000
5000,
7500,
11500
]
}
]
]

View File

@ -1,13 +1,74 @@
[
{
"name": "Trolley Run",
"spawn": {"x": 33.0, "z": 52.0, "dx": 0.0, "dz": -1.0},
"spawn": {
"dx": 0.0,
"dz": -1.0,
"x": 33.0,
"z": 52.0
},
"streams": [
{"pts": [[-70.0, 15.0], [70.0, 15.0]], "speed": 11.0, "interval": 2.0},
{"pts": [[70.0, 12.0], [-70.0, 12.0]], "speed": 12.0, "interval": 2.4},
{"pts": [[-70.0, -21.0], [70.0, -21.0]], "speed": 10.0, "interval": 2.2},
{"pts": [[70.0, -24.0], [-70.0, -24.0]], "speed": 12.0, "interval": 2.8}
{
"interval": 2.0,
"pts": [
[
-70.0,
15.0
],
[
70.0,
15.0
]
],
"speed": 11.0
},
{
"interval": 2.4,
"pts": [
[
70.0,
12.0
],
[
-70.0,
12.0
]
],
"speed": 12.0
},
{
"interval": 2.2,
"pts": [
[
-70.0,
-21.0
],
[
70.0,
-21.0
]
],
"speed": 10.0
},
{
"interval": 2.8,
"pts": [
[
70.0,
-24.0
],
[
-70.0,
-24.0
]
],
"speed": 12.0
}
],
"targets": [8000, 20000, 40000]
"targets": [
12500,
15000,
46000
]
}
]

View File

@ -16,6 +16,10 @@ extends RefCounted
##
## ponytail: normals are left alone. Recomputing them per impact costs more than
## it buys at this crumple scale; revisit if the shading reads flat.
## ponytail: _write() rebuilds a whole ArrayMesh via add_surface_from_arrays(),
## which takes no LOD or shadow-mesh argument -- a deformable car therefore loses
## any LODs its import generated. Harmless for the procedural fleet (no LODs to
## lose) but worth knowing before wiring the deformer to a dense imported model.
const ALPHA := 2.0 # distance falloff exponent; PDF suggests 1.0-3.0
const NEAR := 4 # control points blended per vertex
@ -34,6 +38,7 @@ var _meshes: Array = [] # one entry per MeshInstance3D, all its surfaces toget
var _cp := PackedVector3Array()
var _disp := PackedVector3Array()
var _ok := false
var _root: Node3D = null # the car model; panels reparented away stop deforming
var _skins: Array = [] # {mat, albedo, rough}: per-car material copies to dirty up
var damage := 0.0 # 0..1, drives the paint getting scratched and dulled
@ -126,6 +131,7 @@ func setup(root: Node3D) -> bool:
(m["mi"] as MeshInstance3D).mesh = copy
m["mesh"] = copy
_meshes.append(m)
_root = root
_ok = true
_write()
return true
@ -198,6 +204,12 @@ func _wear() -> void:
m.detail_enabled = damage > 0.35
func _write() -> void:
# car.gd reparents det_* panels onto their own RigidBody when shed and frees
# them seconds later; left in the list they'd be debris still being crumpled
# by a car they've come off, and eventually freed nodes being written to
if is_instance_valid(_root):
_meshes = _meshes.filter(func(m) -> bool:
return is_instance_valid(m["mi"]) and _root.is_ancestor_of(m["mi"]))
for m in _meshes:
var mesh: ArrayMesh = m["mesh"]
var inv: Transform3D = (m["xform"] as Transform3D).affine_inverse()

View File

@ -269,11 +269,16 @@ func _ready() -> void:
add_child(path)
var L := curve.get_baked_length()
var headway: float = stream["speed"] * stream["interval"]
var n: int = clampi(int(L / headway), 1, 14)
# At least 3 per stream, spaced to fit. int(L / headway) floors to 1
# on a short clipped stream, which is how Grey x Glenelg ended up
# with 9 convoy cars against the usual 20-25 and gaps wide enough to
# drive clean through -- it scored nothing in 12 of 12 tuning runs.
var n: int = clampi(int(L / headway), 3, 14)
var spacing := L / float(n)
for i in n:
var t := TrafficCar.new()
t.path = path
t.offset = fposmod(i * headway, L)
t.offset = fposmod(i * spacing, L)
t.speed = stream["speed"]
t.model_idx = midx
midx += 1

View File

@ -87,7 +87,14 @@ def launch_point(pts, jpt):
end = [end[0] + dx / L * grow, end[1] + dz / L * grow]
dx, dz = jpt[0] - end[0], jpt[1] - end[1]
L = math.hypot(dx, dz) or 1.0
return {"x": end[0], "z": end[1], "dx": dx / L, "dz": dz / L}
dx, dz = dx / L, dz / L
# Sit in a lane, not on the centreline. Convoys are offset +/-LANE either
# side, so a centreline launch threads straight between them -- Grey x
# Glenelg passed within 2.9 m of a convoy car and never touched one,
# scoring nothing in 12 of 12 tuning runs. Same transform as offset().
# sign matters: offsetting the other way moved the launch AWAY from the
# convoy lane and widened Grey x Glenelg's closest pass from 2.9 m to 5.5 m
return {"x": end[0] - dz * LANE, "z": end[1] + dx * LANE, "dx": dx, "dz": dz}
events = []
for spec in sys.argv[5:]: