From c936946eb4702948c9f903f35643a824ea7f8681 Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Mon, 5 Oct 2026 22:06:26 +0200 Subject: [PATCH] chore(prototype): add in-game vehicle probe game script Throwaway probe for "Probe vehicle data and native passing in game": logs every player-owned road vehicle's MovePath/LandVehicle/move-info state every 5s and groups vehicles by street segment to expose lanes and oncoming traffic. Co-Authored-By: Claude Opus 5.5 --- mod/content/overtake_probe.gs.lua | 8 ++++ mod/content/overtake_probe.script.tl | 70 ++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 mod/content/overtake_probe.gs.lua create mode 100644 mod/content/overtake_probe.script.tl diff --git a/mod/content/overtake_probe.gs.lua b/mod/content/overtake_probe.gs.lua new file mode 100644 index 0000000..1b9d2c5 --- /dev/null +++ b/mod/content/overtake_probe.gs.lua @@ -0,0 +1,8 @@ +-- PROTOTYPE (ticket "Probe vehicle data and native passing in game"): throwaway, lives only on branch prototype/vehicle-probe. +function data() + return { + updateScript = { + fileName = "overtake_probe.script@update", + }, + } +end diff --git a/mod/content/overtake_probe.script.tl b/mod/content/overtake_probe.script.tl new file mode 100644 index 0000000..e2f65eb --- /dev/null +++ b/mod/content/overtake_probe.script.tl @@ -0,0 +1,70 @@ +-- PROTOTYPE (ticket "Probe vehicle data and native passing in game"): throwaway, lives only on branch prototype/vehicle-probe. +-- Every INTERVAL it dumps every player-owned road vehicle, then groups them by street segment +-- so lanes (edge index) and oncoming traffic (opposite direction / turnaround edge) are visible in stdout.txt. +-- ponytail: module-local timer, not GameScriptState; fine for a probe, resets on reload. + +local TAG = "[overtake_probe] " +local INTERVAL = 5.0 +local elapsed = 0.0 +local ticks = 0 + +local function p(s : string) + print(TAG .. s) +end + +local function edgeAt(mp : Engine.Component.MovePath) : EdgeId, boolean + local e = mp.path.edges[mp.dyn.pathPos.edgeIndex + 1] + if e == nil then return nil, nil end + return e[1], e[2] +end + +local function turnaround(edge : EdgeId) : integer + local tn = api.engine.getComponent(edge.entity, api.type.ComponentType.TRANSPORT_NETWORK) as Engine.Component.TransportNetwork + if tn == nil or tn.turnaroundEdges == nil then return nil end + return tn.turnaroundEdges[edge.index + 1] +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 bySegment : {integer : {string}} = {} + 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 + if lv ~= nil and not lv.isTram and mp ~= nil then + local edge, dir = edgeAt(mp) + local info = api.engine.system.landVehicleMoveSystem.getVehicleInfo(v) + local seg = edge and edge.entity or -1 + local line = string.format( + "veh=%d seg=%d lane=%s dir=%s turnaround=%s pos=%.1f speed=%.2f accel=%.2f blocked=%s state=%s standing=%.1f timeToIgnore=%.2f timeUntilAccel=%.2f occ=%s..%s brake=%.1f power=%s traction=%s", + v, seg, tostring(edge and edge.index), tostring(dir), tostring(edge and turnaround(edge)), + mp.dyn.pathPos.pos, mp.dyn.speed, mp.dyn.accel, tostring(mp.blocked), tostring(mp.state), + mp.dyn.timeStanding, mp.dyn.timeToIgnore, mp.dyn.timeUntilAccel, + tostring(mp.occupiedBackIndex), tostring(mp.occupiedFrontIndex), lv.dyn.brakingTimer, + tostring(info and info.availPower), tostring(info and info.traction)) + p(line) + bySegment[seg] = bySegment[seg] or {} + table.insert(bySegment[seg], string.format("%d(lane=%s,dir=%s,v=%.1f,blk=%s)", v, tostring(edge and edge.index), tostring(dir), mp.dyn.speed, tostring(mp.blocked))) + end + end + for seg, list in pairs(bySegment) do + if #list > 1 then + p(string.format("SHARED seg=%d: %s", seg, table.concat(list, " "))) + end + end +end + +local ret : GameScript = { + update = function(_userParams : table, _state : GameScriptState
, dt : number) : nil + ticks = ticks + 1 + elapsed = elapsed + dt + if ticks == 1 then p("update running, first dt=" .. tostring(dt)) end + if elapsed < INTERVAL then return end + elapsed = 0.0 + xpcall(dump, function(err : any) p("ERROR " .. tostring(err)) end, dt) + end, +} + +return ret