chore(prototype): try re-path via makeVehicleSetLineCmd; record failed rebuild

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
haylanandClaude Bot committed 2026-10-05 22:51:26 +02:00
1 parent ea23aa1dc5
commit e77fabb772
2 files changed
+41 -3

No files matched your search

+6
View File
@@ -46,3 +46,9 @@ Ticket "Probe vehicle data and native passing in game" (part of map "Map: TpF3 o
## Decision
Overtaking on two-way roads (one lane per direction) is dropped (decided by the user, 2026-10-05). The effort continues on multilane roads only: a held-back vehicle should switch to a lane the slower vehicle isn't in.
## Segment rebuild (ticket "Can rebuilding a road segment move a passer past its blocker?")
- The script sent 3 `makeWorldBuildProposalCmd` proposals (remove segment + re-add a copy of its `BaseEdge`/`BaseEdgeStreet`, `ignoreErrors = true`) for segments 29701 and 29699 (`country_new_medium`). **None took effect**: both segment ids stayed in use by vehicles until the end of the log, and 29699 was "rebuilt" twice. The game logged no error, and there's no callback in engine state. The copied `BaseEdge.distance` read `0.0`, so the copy is probably incomplete. A next attempt would need `api.engine.util.proposal.makeProposalData` to see the proposal's errors.
- The human's manual segment deletion left no trace in the probe data: every vehicle stayed `tvState=1` (en route), with only normal path trimming.
- Related find: `api.cmd.makeVehicleSendToDepotCmd(vehicle, sellOnArrival, jumpToDepotEntity)` "teleports" a vehicle to a depot. The engine can relocate vehicles instantly, but only to depots.
+35 -3
View File
@@ -8,7 +8,8 @@
local TAG = "[overtake_probe] "
local INTERVAL = 2.0
local MAX_GAP = 40.0
local MAX_REBUILDS = 3 -- per save; counted in GameScriptState because module locals don't persist
local MAX_REBUILDS = 0 -- rebuild disabled: proposals were silently rejected (segments kept their ids)
local MAX_SETLINES = 5 -- EXPERIMENT: re-assign the follower to its own line + current stop to force a re-path
local elapsed = 0.0
local ticks = 0
@@ -38,6 +39,7 @@ end
local record ProbeState
rebuilds : integer
setlines : integer
watch : {integer} -- vehicles involved in the last rebuild, flagged in later dumps
end
@@ -78,16 +80,34 @@ local function rebuild(seg : integer)
api.cmd.sendCommand(api.cmd.makeWorldBuildProposalCmd(proposal, nil, true, false))
end
-- lanes of the next few path edges, to see whether a re-path changes the lane choice
local function lanesAhead(mp : Engine.Component.MovePath) : string
local out : {string} = {}
local edges = mp.path.edges as {any}
for k = mp.dyn.pathPos.edgeIndex + 1, math.min(#edges, mp.dyn.pathPos.edgeIndex + 4) do
local e = edges[k] as UtilPathfinding.EdgeIdDir
table.insert(out, e.edgeId.entity .. ":" .. e.edgeId.index)
end
return table.concat(out, " ")
end
local function setLine(v : integer)
local tv = api.engine.getComponent(v, api.type.ComponentType.TRANSPORT_VEHICLE) as Engine.Component.TransportVehicle
p(string.format("SETLINE_TEST send veh=%d line=%s stopIndex=%s", v, tostring(tv.line), tostring(tv.stopIndex)))
api.cmd.sendCommand(api.cmd.makeVehicleSetLineCmd(v, tv.line, tv.stopIndex))
end
local function dump(dt : number, state : GameScriptState<ProbeState>)
local st = state:get()
st.rebuilds = st.rebuilds or 0
st.setlines = st.setlines or 0
st.watch = st.watch or {}
local watched : {integer : boolean} = {}
for _, w in ipairs(st.watch) do watched[w] = true end
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 rebuilds=%d", ticks, tostring(dt), #vehicles, st.rebuilds))
p(string.format("tick=%d dt=%s playerVehicles=%d rebuilds=%d setlines=%d", ticks, tostring(dt), #vehicles, st.rebuilds, st.setlines))
local byLane : {string : {Car}} = {}
local laneSeg : {string : integer} = {}
@@ -103,6 +123,7 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
watched[v] and "WATCH " or "", v, seg, 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 watched[v] then p("WATCH lanesAhead veh=" .. v .. " " .. lanesAhead(mp)) end
if edge ~= nil then
local key = seg .. ":" .. edge.index
byLane[key] = byLane[key] or {}
@@ -112,7 +133,7 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
end
end
if st.rebuilds >= MAX_REBUILDS then return end
if st.rebuilds >= MAX_REBUILDS and st.setlines >= MAX_SETLINES then return end
for key, cars in pairs(byLane) do
for i = 1, #cars do
for j = 1, #cars do
@@ -122,6 +143,17 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
-- leader must be moving, otherwise it's a queue at a stop or junction, not a slow vehicle
if i ~= j and ahead and math.abs(b.pos - a.pos) < MAX_GAP and b.speed > 1.0 then
local seg = laneSeg[key]
if st.setlines < MAX_SETLINES then
local amp = api.engine.getComponent(a.v, api.type.ComponentType.MOVE_PATH) as Engine.Component.MovePath
p(string.format("SETLINE_TEST candidate seg=%d follower=%d (v=%.2f) leader=%d (v=%.2f) lanesAhead before: %s",
seg, a.v, a.speed, b.v, b.speed, lanesAhead(amp)))
setLine(a.v)
st.setlines = st.setlines + 1
st.watch = { a.v, b.v }
state:set(st)
return -- one experiment per dump
end
if st.rebuilds >= MAX_REBUILDS then return end
local ok, why = rebuildable(seg)
p(string.format("REBUILD_TEST candidate seg=%d follower=%d (pos=%.1f v=%.2f) leader=%d (pos=%.1f v=%.2f) ok=%s %s",
seg, a.v, a.pos, a.speed, b.v, b.pos, b.speed, tostring(ok), why))