Files
tpf3-overtake/mod/content/overtake_probe.script.tl
T
2026-10-05 22:26:39 +02:00

113 lines
4.8 KiB
Plaintext

-- 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
-- path.edges is 1-based, elements are EdgeIdDir userdata (tealdef says {EdgeId, boolean})
local e = (mp.path.edges as {any})[mp.dyn.pathPos.edgeIndex + 1] as UtilPathfinding.EdgeIdDir
if e == nil then return nil, nil end
return e.edgeId, e.dir
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
-- EXPERIMENT: does api.cmd.debug.makeComponentExchangeCmd accept a MovePath, and does a positive
-- dyn.timeToIgnore make the follower ignore the vehicle ahead? Hypothesis only; nothing documents either.
local IGNORE_SECONDS = 10.0
local MAX_GAP = 40.0
local record Car
v : integer
pos : number
speed : number
dir : boolean
mp : Engine.Component.MovePath
end
local function tryIgnore(follower : Car, leader : Car)
local mp = follower.mp
mp.dyn.timeToIgnore = IGNORE_SECONDS
p(string.format("IGNORE_TEST send veh=%d leader=%d gap=%.1f v=%.2f leaderV=%.2f", follower.v, leader.v, math.abs(leader.pos - follower.pos), follower.speed, leader.speed))
local cmd = api.cmd.debug.makeComponentExchangeCmd(follower.v, mp as Engine.Component)
api.cmd.sendCommand(cmd, function(_data : any, success : boolean)
p(string.format("IGNORE_TEST result veh=%d success=%s", follower.v, tostring(success)))
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 bySegment : {integer : {string}} = {}
local byLane : {string : {Car}} = {}
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)
if edge ~= nil then
local key = seg .. ":" .. edge.index
byLane[key] = byLane[key] or {}
table.insert(byLane[key], { v = v, pos = mp.dyn.pathPos.pos, speed = mp.dyn.speed, dir = dir, mp = mp })
end
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
for _, cars in pairs(byLane) do
for i = 1, #cars do
for j = 1, #cars do
local a, b = cars[i], cars[j]
-- ponytail: assumes pos grows in driving direction when dir=true; the log shows whether that holds
local ahead = (a.dir and b.pos > a.pos) or (not a.dir and b.pos < a.pos)
if i ~= j and ahead and math.abs(b.pos - a.pos) < MAX_GAP then
tryIgnore(a, b)
end
end
end
end
end
local ret : GameScript<table> = {
update = function(_userParams : table, _state : GameScriptState<table>, 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