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:
1 parent
ea23aa1dc5
commit
e77fabb772
2 files changed
+41
-3
No files matched your search
@@ -46,3 +46,9 @@ Ticket "Probe vehicle data and native passing in game" (part of map "Map: TpF3 o
|
|||||||
## Decision
|
## 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.
|
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.
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
local TAG = "[overtake_probe] "
|
local TAG = "[overtake_probe] "
|
||||||
local INTERVAL = 2.0
|
local INTERVAL = 2.0
|
||||||
local MAX_GAP = 40.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 elapsed = 0.0
|
||||||
local ticks = 0
|
local ticks = 0
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ end
|
|||||||
|
|
||||||
local record ProbeState
|
local record ProbeState
|
||||||
rebuilds : integer
|
rebuilds : integer
|
||||||
|
setlines : integer
|
||||||
watch : {integer} -- vehicles involved in the last rebuild, flagged in later dumps
|
watch : {integer} -- vehicles involved in the last rebuild, flagged in later dumps
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -78,16 +80,34 @@ local function rebuild(seg : integer)
|
|||||||
api.cmd.sendCommand(api.cmd.makeWorldBuildProposalCmd(proposal, nil, true, false))
|
api.cmd.sendCommand(api.cmd.makeWorldBuildProposalCmd(proposal, nil, true, false))
|
||||||
end
|
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 function dump(dt : number, state : GameScriptState<ProbeState>)
|
||||||
local st = state:get()
|
local st = state:get()
|
||||||
st.rebuilds = st.rebuilds or 0
|
st.rebuilds = st.rebuilds or 0
|
||||||
|
st.setlines = st.setlines or 0
|
||||||
st.watch = st.watch or {}
|
st.watch = st.watch or {}
|
||||||
local watched : {integer : boolean} = {}
|
local watched : {integer : boolean} = {}
|
||||||
for _, w in ipairs(st.watch) do watched[w] = true end
|
for _, w in ipairs(st.watch) do watched[w] = true end
|
||||||
|
|
||||||
local player = api.engine.util.getPlayer()
|
local player = api.engine.util.getPlayer()
|
||||||
local vehicles = api.engine.getEntitiesWithComponent(api.type.ComponentType.TRANSPORT_VEHICLE, { requireOwnedByPlayer = player })
|
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 byLane : {string : {Car}} = {}
|
||||||
local laneSeg : {string : integer} = {}
|
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)),
|
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),
|
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))
|
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
|
if edge ~= nil then
|
||||||
local key = seg .. ":" .. edge.index
|
local key = seg .. ":" .. edge.index
|
||||||
byLane[key] = byLane[key] or {}
|
byLane[key] = byLane[key] or {}
|
||||||
@@ -112,7 +133,7 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
|
|||||||
end
|
end
|
||||||
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 key, cars in pairs(byLane) do
|
||||||
for i = 1, #cars do
|
for i = 1, #cars do
|
||||||
for j = 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
|
-- 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
|
if i ~= j and ahead and math.abs(b.pos - a.pos) < MAX_GAP and b.speed > 1.0 then
|
||||||
local seg = laneSeg[key]
|
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)
|
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",
|
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))
|
seg, a.v, a.pos, a.speed, b.v, b.pos, b.speed, tostring(ok), why))
|
||||||
|
|||||||
Reference in new issue
Block a user