diff --git a/docs/research/vehicle-probe.md b/docs/research/vehicle-probe.md index bdc040a..70017a7 100644 --- a/docs/research/vehicle-probe.md +++ b/docs/research/vehicle-probe.md @@ -91,3 +91,10 @@ The human confirmed in game what the log showed: rebuilding the segment under a - So the error is not about callbacks or threads: **the exchange refuses a `MovePath`**, consistent with its doc comment (`EmissionEmitter` or `ModelInstanceList` only). - **Conclusion: no script API can write a road vehicle's position or route.** Moving a vehicle past another (jump, flip, teleport) is impossible for line vehicles. The only exception is `jumpToDepotEntity`, which can only target a depot. - The probe retried every frame (5951 errors) because the error came before the counter was incremented. The experiment is removed, and the probe is logging-only again. + +## Overlaid passing lanes (ticket "Can a street type with overlaid passing lanes fake overtaking on single-lane roads?") + +- Prototype: `mod/content/streets/overtake_proto_{a,b}.street_template.lua`, copies of `country_new_small` with a 0.5 m passing lane per direction at the centre line, offset ±2.5 into the oncoming lane (A and B use opposite signs). A mod street template sits anywhere under `content/` and refers to base resources with `::/…`. +- First in-game run (human, 2026-10-05): the template loads, the lanes are visibly offset as intended, and vehicles can drive on the passing lanes. **Town (non-player) cars sometimes choose the passing lane.** **A faster bus behind a slower vehicle does not switch to the empty lane**: the switching logic is missing. +- Changes for the next run: passing lanes allow only `BUS`/`TRUCK` (keeps private `CAR` town traffic out), and the probe logs the road template per vehicle (`road=`), since lane indices 2/3 also mean ordinary lanes on `country_new_medium`. +- Hypothesis for the switch: the engine only re-chooses lanes at segment boundaries (`GetNextEdgeTrafficPenaltyAndAlternatives`), so short segments give a blocked bus more chances to pick the passing lane. Fallback trigger: `replaceSegment` ahead of the pair (made the leader re-plan lanes on multilane roads). diff --git a/mod/content/overtake_probe.script.tl b/mod/content/overtake_probe.script.tl index b91e9b1..59f2f41 100644 --- a/mod/content/overtake_probe.script.tl +++ b/mod/content/overtake_probe.script.tl @@ -26,6 +26,13 @@ local function turnaround(edge : EdgeId) : integer return tn.turnaroundEdges[edge.index + 1] end +-- short street template name of a segment, so passing-lane use on the prototype road can be told apart +local function roadOf(seg : integer) : string + local be = api.engine.getComponent(seg, api.type.ComponentType.BASE_EDGE) as Engine.Component.BaseEdge + if be == nil then return "?" end + return (tostring(be.roadTemplate):match("([^/]+)%.street_template$")) or tostring(be.roadTemplate) +end + local function dump(dt : number) local player = api.engine.util.getPlayer() local vehicles = api.engine.getEntitiesWithComponent(api.type.ComponentType.TRANSPORT_VEHICLE, { requireOwnedByPlayer = player }) @@ -37,8 +44,8 @@ local function dump(dt : number) if lv ~= nil and not lv.isTram and mp ~= nil then local edge, dir = edgeAt(mp) p(string.format( - "veh=%d seg=%d lane=%s dir=%s turnaround=%s edgeIndex=%d/%d pos=%.1f speed=%.2f state=%s tvState=%s line=%s standing=%.1f", - v, edge and edge.entity or -1, tostring(edge and edge.index), tostring(dir), tostring(edge and turnaround(edge)), + "veh=%d road=%s seg=%d lane=%s dir=%s turnaround=%s edgeIndex=%d/%d pos=%.1f speed=%.2f state=%s tvState=%s line=%s standing=%.1f", + v, edge and roadOf(edge.entity) or "-", edge and edge.entity or -1, tostring(edge and edge.index), tostring(dir), tostring(edge and turnaround(edge)), mp.dyn.pathPos.edgeIndex, #(mp.path.edges as {any}), mp.dyn.pathPos.pos, mp.dyn.speed, tostring(mp.state), tostring(tv and tv.state), tostring(tv and tv.line), mp.dyn.timeStanding)) end diff --git a/mod/content/streets/overtake_proto_a.street_template.lua b/mod/content/streets/overtake_proto_a.street_template.lua index a8695fa..8d4e1af 100644 --- a/mod/content/streets/overtake_proto_a.street_template.lua +++ b/mod/content/streets/overtake_proto_a.street_template.lua @@ -20,10 +20,10 @@ return { laneConfigs = { { forward = false, height = 0, offset = -1, speed = 22.22, transportModes = { "PERSON", "CARGO" }, width = 2 }, { forward = false, height = 0, offset = -0.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 5 }, - -- backward passing lane, meant to sit over the forward driving lane - { forward = false, height = 0, offset = 2.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 0.5 }, - -- forward passing lane, meant to sit over the backward (oncoming) driving lane - { forward = true, height = 0, offset = -2.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 0.5 }, + -- backward passing lane, meant to sit over the forward driving lane (BUS/TRUCK only: keeps town cars out) + { forward = false, height = 0, offset = 2.5, speed = 22.22, transportModes = { "BUS", "TRUCK" }, width = 0.5 }, + -- forward passing lane, meant to sit over the backward (oncoming) driving lane (BUS/TRUCK only) + { forward = true, height = 0, offset = -2.5, speed = 22.22, transportModes = { "BUS", "TRUCK" }, width = 0.5 }, { forward = true, height = 0, offset = 0.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 5 }, { forward = true, height = 0, offset = 1, speed = 22.22, transportModes = { "PERSON", "CARGO" }, width = 2 }, }, diff --git a/mod/content/streets/overtake_proto_b.street_template.lua b/mod/content/streets/overtake_proto_b.street_template.lua index 70a1cc6..4021d91 100644 --- a/mod/content/streets/overtake_proto_b.street_template.lua +++ b/mod/content/streets/overtake_proto_b.street_template.lua @@ -20,10 +20,10 @@ return { laneConfigs = { { forward = false, height = 0, offset = -1, speed = 22.22, transportModes = { "PERSON", "CARGO" }, width = 2 }, { forward = false, height = 0, offset = -0.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 5 }, - -- backward passing lane, meant to sit over the forward driving lane - { forward = false, height = 0, offset = -2.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 0.5 }, - -- forward passing lane, meant to sit over the backward (oncoming) driving lane - { forward = true, height = 0, offset = 2.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 0.5 }, + -- backward passing lane, meant to sit over the forward driving lane (BUS/TRUCK only: keeps town cars out) + { forward = false, height = 0, offset = -2.5, speed = 22.22, transportModes = { "BUS", "TRUCK" }, width = 0.5 }, + -- forward passing lane, meant to sit over the backward (oncoming) driving lane (BUS/TRUCK only) + { forward = true, height = 0, offset = 2.5, speed = 22.22, transportModes = { "BUS", "TRUCK" }, width = 0.5 }, { forward = true, height = 0, offset = 0.5, speed = 22.22, transportModes = { "CAR", "BUS", "TRUCK" }, width = 5 }, { forward = true, height = 0, offset = 1, speed = 22.22, transportModes = { "PERSON", "CARGO" }, width = 2 }, },