Compare commits
6
Commits
9623abb868
...
1afd1f2e61
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1afd1f2e61 | ||
|
|
6ed16fb1ab | ||
|
|
3f14583ab4 | ||
|
|
e807744bf7 | ||
|
|
0424e66c6f | ||
|
|
f7254a7709 |
No files matched your search
@@ -91,3 +91,20 @@ 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).
|
||||
- Second run (2026-10-05 ~21:50 UTC, variant B, passing lanes BUS/TRUCK only). The human judged **variant B correct** (backward passing lane offset -2.5, forward +2.5), so variant A was removed. On the prototype road, lane 4 is the normal forward lane and lane 3 the forward passing lane.
|
||||
- **An overtake happened**: fast 29251 (lane 4, 22.22 m/s) got past slow 29851 over segments 29707 → 30211 (side by side) → 30215 → 29708 → 29042 and stayed ahead. But the *slow* vehicle was in the passing lane (lane 3), and the passer went past in the normal lane.
|
||||
- The engine treats both lanes as equal: some vehicles cruise in the passing lane almost the whole way (29796: 28/28 samples in lane 3, 27775: 29/29). Others mix (29251: 33 in lane 4, 14 in lane 3).
|
||||
- Vehicles slowed to 9–10 m/s and briefly stopped on segment 27290 (presumably the end of the prototype stretch): a merge from two lanes into one.
|
||||
- Visually the lane change is abrupt (human: "the lanes need a smoother transition"): the switch happens at the node, which has almost zero length on a straight, and the jump is about 5–6 m sideways.
|
||||
- Next ideas: a lower speed limit on the passing lane (prefer the normal lane unless it's blocked), a smaller offset or transition segments for a smoother change.
|
||||
- Third run setup: passing lanes at 20 m/s (normal lanes 22.22 m/s), everything else unchanged. Question: does a lower limit keep vehicles in the normal lane and send them to the passing lane only when the normal lane is blocked?
|
||||
- Third run (20 m/s passing lanes): **no change.** Slow buses still sat in the left passing lane (29849 and 29851: 29/29 samples in lane 3); fast 29251 split 8/11. The engine doesn't pick lanes by speed limit (the slow buses top out at 16.67 < 20 anyway). Observed pattern: a vehicle gets a lane when it enters the stretch and keeps it; it doesn't move over when blocked; passes happen only when fast and slow happen to be in different lanes.
|
||||
- Lane connections are editable: `BaseNodeConfig.laneConnections` (`LaneConnection{segment0, lane0, segment1, lane1, withRoad, withTram}`, lanes zero-based left-to-right seen from the node) plus `userModifiedLaneConnections`, scriptable via `SimpleStreetProposal.nodeConfigsToAdd`. The game also has a native **crossing tool** ("allows to change the road and tram lanes at crossings", `UI::LaneModifier` in the executable). Next test (option a, by hand): at the stretch's entry junction, connect the normal road only to the right lane, to see whether entry control works and whether a blocked fast bus moves left inside the stretch.
|
||||
- Fourth run (after the human's crossing-tool edits): **entry control not effective.** Slow buses 29849, 29851 and 29852 still entered prototype stretches (segments 21304, 29012) in the left lane 3 and stayed there (12–14/12–14 samples). 29850, 29848 and the fast 29251 entered in lane 4. No vehicle switched lanes inside a stretch. It's unknown whether or where the edits were saved, so the probe now logs the lane connections (`NODE … userModified= conns=`) at both nodes of every occupied prototype segment.
|
||||
@@ -26,10 +26,38 @@ 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
|
||||
|
||||
-- lane connections at both nodes of a segment, to check whether crossing-tool edits exist where vehicles enter
|
||||
local function nodeLog(seg : integer)
|
||||
local be = api.engine.getComponent(seg, api.type.ComponentType.BASE_EDGE) as Engine.Component.BaseEdge
|
||||
if be == nil then return end
|
||||
for _, node in ipairs({ be.node0, be.node1 }) do
|
||||
local cfg = api.engine.getComponent(node, api.type.ComponentType.BASE_NODE_CONFIG) as Engine.Component.BaseNodeConfig
|
||||
if cfg == nil then
|
||||
p(string.format("NODE seg=%d node=%d no BaseNodeConfig (default connections)", seg, node))
|
||||
else
|
||||
local conns : {string} = {}
|
||||
for _, lc in ipairs(cfg.laneConnections) do
|
||||
if lc.segment0 == seg or lc.segment1 == seg then
|
||||
table.insert(conns, string.format("%d:%d->%d:%d", lc.segment0, lc.lane0, lc.segment1, lc.lane1))
|
||||
end
|
||||
end
|
||||
p(string.format("NODE seg=%d node=%d userModified=%s conns=%s", seg, node, tostring(cfg.userModifiedLaneConnections), table.concat(conns, " ")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dump(dt : number)
|
||||
local player = api.engine.util.getPlayer()
|
||||
local vehicles = api.engine.getEntitiesWithComponent(api.type.ComponentType.TRANSPORT_VEHICLE, { requireOwnedByPlayer = player })
|
||||
p(string.format("tick=%d dt=%s playerVehicles=%d", ticks, tostring(dt), #vehicles))
|
||||
local protoSegs : {integer : boolean} = {}
|
||||
for _, v in ipairs(vehicles) do
|
||||
local lv = api.engine.getComponent(v, api.type.ComponentType.LAND_VEHICLE) as Engine.Component.LandVehicle
|
||||
local mp = api.engine.getComponent(v, api.type.ComponentType.MOVE_PATH) as Engine.Component.MovePath
|
||||
@@ -37,12 +65,14 @@ 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))
|
||||
if edge ~= nil and roadOf(edge.entity) == "overtake_proto_b" then protoSegs[edge.entity] = true end
|
||||
end
|
||||
end
|
||||
for seg, _ in pairs(protoSegs) do nodeLog(seg) end
|
||||
end
|
||||
|
||||
local ret : GameScript<table> = {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
-- PROTOTYPE (ticket "Can a street type with overlaid passing lanes fake overtaking on single-lane roads?"):
|
||||
-- throwaway, lives only on branch prototype/vehicle-probe.
|
||||
-- Looks like country_new_small (same style, one visible lane per direction), but each direction gets a narrow
|
||||
-- extra passing lane at the centre line, pushed by `offset` over the oncoming lane. Passing lanes run at 20 m/s
|
||||
-- (normal lanes 22.22) to test whether a lower limit keeps vehicles in the normal lane unless it's blocked.
|
||||
-- Offsets B -2.5 / F +2.5 put the passing lanes over the oncoming lanes (variant B; the opposite signs, variant A,
|
||||
-- were wrong and are removed).
|
||||
function data()
|
||||
return {
|
||||
availability = { yearFrom = 0, yearTo = 0 },
|
||||
cost = 180,
|
||||
country = true,
|
||||
defaultWithCrosswalk = false,
|
||||
description = {
|
||||
description = "PROTOTYPE overtaking road, variant B: country small road with overlaid passing lanes (offset B -2.5 / F +2.5).",
|
||||
icon = "::/infrastructure/street/country/country_new_small.tga",
|
||||
name = "Overtake proto B (passing lanes B -2.5 / F +2.5)",
|
||||
previewIcon = "::/infrastructure/street/country/country_new_small_preview.tga",
|
||||
},
|
||||
emissions = { noise = 0, pollution = 0, pollutionRadius = 0, radius = 0 },
|
||||
fillGroundTex = "::/infrastructure/street/shared/street_fill.gtex",
|
||||
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 (BUS/TRUCK only: keeps town cars out)
|
||||
{ forward = false, height = 0, offset = -2.5, speed = 20, 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 = 20, 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 },
|
||||
},
|
||||
maintenanceCost = 30,
|
||||
menuCategory = { categories = { { category = "roads_country", filterCategories = { }, order = 442 } } },
|
||||
modifiers = { maxSpeedModifier = 0, noiseModifier = 0, pollutionModifier = 0 },
|
||||
pedestrianWalkPenalty = true,
|
||||
priority = 200,
|
||||
roadType = "STREET",
|
||||
sidewalkFillGroundTex = "::/infrastructure/shared/none.gtex",
|
||||
simBuildable = false, -- towns must not build the prototype
|
||||
streetStyle = "::/infrastructure/street/country/country_new_small.street",
|
||||
builderAudioRes = "::/gui/construction/sound/buildoze_street.builder_audio",
|
||||
}
|
||||
end
|
||||
Reference in new issue
Block a user