Files
fs25-adsmartpickup/FS25_ADSmartPickup/adHitch.lua
T

583 lines
27 KiB
Lua

-- AD Hitch
-- Kobler redskaber på og af med AutoDrive.
-- "Connect <navn>" (eller "Kobl <navn>"): markøren ligger lige under vognens trækøje med et bakkespor ned til
-- den. Når en traktor har kørt AD "Kør til" dertil og står stille, kobles det redskab på, som spillet selv finder
-- inden for koblingsafstand (samme kald som Courseplay bruger til skærebord: updateVehiclesInAttachRange +
-- attachImplementFromInfo).
-- "Afkobl <navn>" (eller "Kobl af", "Detach", "Disconnect"): ved ankomst kobles redskaberne BAG traktoren af;
-- frontvægt og frontlæsser bliver siddende.
-- ADHitch.fetch()/ADHitch.drop() er indgangen for flows/markarbejde: kør til markøren, kobl på/af, meld tilbage.
ADHitch = {}
ADHitch.LOG_PREFIX = "ADSmartPickup: kobling: "
-- afkobling tjekkes først, så "Kobl af X" ikke læses som "Kobl <af X>"
ADHitch.MARKER_PREFIXES = {
{prefix = "afkobl", action = "detach"},
{prefix = "kobl af", action = "detach"},
{prefix = "detach", action = "detach"},
{prefix = "disconnect", action = "detach"},
{prefix = "connect", action = "attach"},
{prefix = "kobl", action = "attach"},
}
ADHitch.TIMEOUT_REASON = {attach = "noAttachable", detach = "nothingToDetach", arrive = "notArrived"}
ADHitch.ARRIVE_DISTANCE = 5 -- m fra markøren, AD standser traktoren tæt på waypointet
ADHitch.STILL_SPEED = 1 -- km/t
ADHitch.START_GRACE_MS = 3000 -- så længe må AD være om at komme i gang efter fetch/drop
ADHitch.ACT_TIMEOUT_MS = 8000 -- så længe forsøges kobling efter ankomst
ADHitch.DETACH_TIMEOUT_MS = 20000 -- afkobling: redskabet skal måske foldes ud først (plov: "unfold the tool")
ADHitch.TICK_MS = 250
ADHitch.ROUTE_WAIT_MS = 10000 -- pause ved almindelige markører midt i en tur
ADHitch.ROOMY_ATTACH_DISTANCE = 3 -- m: koblingsrækkevidde når spillets egen ikke rækker
ADHitch.CREPT_EXTRA_DISTANCE = 8 -- m: ekstra ankomstafstand når traktoren har bakket hen til vognen
-- ---- ren logik ----
-- "Connect Bredal" -> "attach", "Bredal"; "Afkobl" -> "detach", ""; andre navne -> nil
function ADHitch.parseMarker(name)
if type(name) ~= "string" then return nil end
local trimmed = name:match("^%s*(.-)%s*$")
local lower = trimmed:lower()
for _, entry in ipairs(ADHitch.MARKER_PREFIXES) do
local prefix = entry.prefix
if lower == prefix then return entry.action, "" end
if lower:sub(1, #prefix + 1) == prefix .. " " then
return entry.action, (trimmed:sub(#prefix + 2):match("^%s*(.-)$"))
end
end
return nil
end
-- implements = {{z = position i traktorens rum, ...}} -> kun dem bag traktoren
function ADHitch.rearImplements(implements)
local rear = {}
for _, implement in ipairs(implements or {}) do
if implement.z ~= nil and implement.z < 0 then table.insert(rear, implement) end
end
return rear
end
-- konsol-argumenter "Connect Bredal > Start > Afkobl Bredal > Start" -> {"Connect Bredal", "Start", ...}
function ADHitch.parseTrip(...)
local steps = {}
for part in (table.concat({...}, " ") .. ">"):gmatch("([^>]*)>") do
local name = part:match("^%s*(.-)%s*$")
if name ~= "" then table.insert(steps, name) end
end
return steps
end
-- markørnavn -> attach / detach / arrive (almindelig markør: bare køre dertil)
function ADHitch.stepAction(name)
return ADHitch.parseMarker(name) or "arrive"
end
-- Spillerens egen AD "Kør til": markørens navn (Connect/Afkobl) først, ellers er puljeredskabets egen parkering
-- stedet vognen sættes af. -> "attach" | "detach" | nil
function ADHitch.manualAction(parsedAction, isPoolImplementPark)
if parsedAction ~= nil then return parsedAction end
return isPoolImplementPark and "detach" or nil
end
-- I en tur: en almindelig markør, der er AD-parkeringen for redskabet bag traktoren, er stedet vognen sættes af.
function ADHitch.resolveAction(action, isImplementPark)
if action == "arrive" and isImplementPark then return "detach" end
return action
end
-- pause efter et trin: ved almindelige markører undervejs holdes der, ikke efter sidste trin
function ADHitch.waitAfter(action, index, count)
if action == "arrive" and index < count then return ADHitch.ROUTE_WAIT_MS end
return 0
end
-- markers = {{name, id}} -> markøren med navnet (store/små bogstaver ligegyldigt)
function ADHitch.findMarker(markers, name)
if type(name) ~= "string" then return nil end
local wanted = name:lower()
for _, marker in pairs(markers or {}) do
if type(marker.name) == "string" and marker.name:lower() == wanted then return marker end
end
return nil
end
-- state = {adActive, distance, speed, arriveDistance?} (arriveDistance: udvidet, når traktoren har bakket hen til vognen)
local function arriveDistanceOf(state)
return state.arriveDistance or ADHitch.ARRIVE_DISTANCE
end
function ADHitch.hasArrived(state)
return not state.adActive and state.distance <= arriveDistanceOf(state) and state.speed <= ADHitch.STILL_SPEED
end
function ADHitch.newJob(markerId, markerName, action)
return {markerId = markerId, markerName = markerName, action = action, state = "driving", elapsed = 0,
actElapsed = 0, seenActive = false}
end
-- Afkobling bekræftes: "done" først når intet sidder bag traktoren efter et forsøg. -> "attempt" | "done" | "nothing"
function ADHitch.detachStep(rearCount, attempted)
if rearCount > 0 then return "attempt" end
return attempted and "done" or "nothing"
end
-- Et skridt i jobbet. succeeded = lykkedes kobling/afkobling i denne tick (kun relevant i "acting").
-- Tilstande: driving -> acting -> done | failed (reason: stoppedAway / noAttachable / nothingToDetach)
function ADHitch.step(job, state, dt, succeeded)
if job.state == "driving" then
job.elapsed = job.elapsed + dt
if state.adActive then
job.seenActive = true
elseif ADHitch.hasArrived(state) then
job.state = "acting"
elseif state.distance > arriveDistanceOf(state) and (job.seenActive or job.elapsed >= ADHitch.START_GRACE_MS) then
job.state, job.reason = "failed", string.format("stoppedAway (%.0f m fra markøren)", state.distance)
end
elseif job.state == "acting" then
if succeeded then
job.state = "done"
else
job.actElapsed = job.actElapsed + dt
local limit = job.action == "detach" and ADHitch.DETACH_TIMEOUT_MS or ADHitch.ACT_TIMEOUT_MS
if job.actElapsed >= limit then
job.state, job.reason = "failed", ADHitch.TIMEOUT_REASON[job.action]
end
end
end
return job
end
-- Hjemtur: hvert puljeredskabs parkering (der sættes det af), så traktorens egen parkering.
function ADHitch.homeRoute(implementParkIds, tractorParkId)
local route, seen = {}, {}
for _, id in ipairs(implementParkIds or {}) do
if not seen[id] then seen[id] = true table.insert(route, id) end
end
if tractorParkId ~= nil then table.insert(route, tractorParkId) end
return route
end
-- ---- spil ----
local jobs = setmetatable({}, {__mode = "k"}) -- [vehicle] = job (+ onDone)
local tickTimer = 0
local function vehicleName(vehicle)
if type(vehicle) ~= "table" then return tostring(vehicle) end
return vehicle.getFullName ~= nil and vehicle:getFullName() or tostring(vehicle.configFileName)
end
local function stateModule(vehicle)
return vehicle.ad ~= nil and vehicle.ad.stateModule or nil
end
local function vehicleState(adEnv, vehicle, markerId)
local module = stateModule(vehicle)
local wayPoint = adEnv.ADGraphManager:getWayPointById(markerId)
-- nærmeste led i sættet: bakker AD med vogn, er det vognen der ender på markøren, ikke traktoren
-- (in-game 28/9: vognen stod på Bredal, traktoren et vognslæb væk -> "stoppedAway")
local distance = math.huge
local parts = {vehicle}
if vehicle.getChildVehicles ~= nil then
local ok, children = pcall(vehicle.getChildVehicles, vehicle)
if ok and children ~= nil then parts = children end
end
for _, part in pairs(parts) do
if wayPoint ~= nil and part.rootNode ~= nil then
local x, _, z = getWorldTranslation(part.rootNode)
distance = math.min(distance, math.sqrt((x - wayPoint.x) ^ 2 + (z - wayPoint.z) ^ 2))
end
end
return {
adActive = module ~= nil and module:isActive(),
distance = distance,
speed = vehicle.getLastSpeed ~= nil and vehicle:getLastSpeed() or 0,
}
end
-- Kobler det redskab på, som spillet finder inden for koblingsafstand. -> ok, redskab eller årsag
function ADHitch.tryAttach(vehicle)
local spec = vehicle.spec_attacherJoints
if spec == nil or AttacherJoints == nil or AttacherJoints.updateVehiclesInAttachRange == nil then
return false, "noJoints"
end
-- spillets egen rækkevidde først, så en rummeligere (spillet retter selv vognen ind ved koblingen)
AttacherJoints.updateVehiclesInAttachRange(vehicle, AttacherJoints.MAX_ATTACH_DISTANCE_SQ,
AttacherJoints.MAX_ATTACH_ANGLE, true)
local info = spec.attachableInfo
if (info == nil or info.attachable == nil) and ADHitch.ROOMY_ATTACH_DISTANCE ^ 2 > AttacherJoints.MAX_ATTACH_DISTANCE_SQ then
AttacherJoints.updateVehiclesInAttachRange(vehicle, ADHitch.ROOMY_ATTACH_DISTANCE ^ 2, AttacherJoints.MAX_ATTACH_ANGLE, true)
info = spec.attachableInfo
end
if info == nil or info.attachable == nil then return false, "noAttachable" end
local attacher = info.attacherVehicle or vehicle
local farmId = vehicle.getActiveFarm ~= nil and vehicle:getActiveFarm() or vehicle:getOwnerFarmId()
local allowed, warning = info.attachable:isAttachAllowed(farmId, attacher)
if not allowed then return false, tostring(warning or "notAllowed") end
attacher:attachImplementFromInfo(info)
return true, info.attachable
end
-- Redskaber koblet direkte på traktoren, med deres placering langs traktoren (z > 0 foran, z < 0 bagved).
local function attachedImplements(vehicle)
local list = {}
if vehicle.getAttachedImplements == nil then return list end
for _, implement in ipairs(vehicle:getAttachedImplements()) do
local jointDesc = vehicle.spec_attacherJoints.attacherJoints[implement.jointDescIndex]
local node = jointDesc ~= nil and (jointDesc.jointTransform or jointDesc.rootNode) or nil
if implement.object ~= nil and node ~= nil then
local _, _, z = localToLocal(node, vehicle.rootNode, 0, 0, 0)
table.insert(list, {object = implement.object, z = z, name = vehicleName(implement.object)})
end
end
return list
end
-- Er markøren (waypoint-id) AD-parkeringen for et af redskaberne bag traktoren?
local function isImplementPark(adEnv, vehicle, markerId)
for _, implement in ipairs(ADHitch.rearImplements(attachedImplements(vehicle))) do
local advd = implement.object.advd
local index = advd ~= nil and advd.parkDestination or nil
local marker = type(index) == "number" and index >= 1 and adEnv.ADGraphManager:getMapMarkerById(index) or nil
if marker ~= nil and marker.id == markerId then return true end
end
return false
end
-- Efter påkobling: fold foldbare redskaber sammen til vejen (CP folder selv ud på marken).
-- Klar til vej: s = {turnedOn, lowered, unfolded, canFold} -> liste af "turnOff" | "raise" | "fold"
function ADHitch.stowActions(s)
local actions = {}
if s.turnedOn then table.insert(actions, "turnOff") end
if s.lowered then table.insert(actions, "raise") end
if s.unfolded and s.canFold then table.insert(actions, "fold") end
return actions
end
-- Alle redskaber på maskinen (også forrest og bag et andet redskab): {object, attacher, jointDescIndex}
local function allImplements(vehicle, list)
list = list or {}
if vehicle == nil or vehicle.getAttachedImplements == nil then return list end
for _, implement in ipairs(vehicle:getAttachedImplements()) do
if implement.object ~= nil then
table.insert(list, {object = implement.object, attacher = vehicle, jointDescIndex = implement.jointDescIndex})
allImplements(implement.object, list)
end
end
return list
end
-- Stop af mark/flow: sluk, løft og fold alle redskaber til transport. -> antal redskaber der fik noget gjort
function ADHitch.stowForTransport(vehicle)
local count = 0
for _, item in ipairs(allImplements(vehicle)) do
local object = item.object
local foldDirection = object.spec_foldable ~= nil and -(object.spec_foldable.turnOnFoldDirection or 1) or nil
local actions = ADHitch.stowActions({
turnedOn = object.getIsTurnedOn ~= nil and object:getIsTurnedOn(),
lowered = object.getIsLowered ~= nil and object:getIsLowered(true),
unfolded = object.getIsUnfolded ~= nil and object:getIsUnfolded() and foldDirection ~= nil,
canFold = foldDirection ~= nil and object.setFoldDirection ~= nil
and (object.getIsFoldAllowed == nil or object:getIsFoldAllowed(foldDirection, false)),
})
for _, action in ipairs(actions) do
if action == "turnOff" and object.setIsTurnedOn ~= nil then
pcall(object.setIsTurnedOn, object, false)
elseif action == "raise" and item.attacher.setJointMoveDown ~= nil and item.jointDescIndex ~= nil then
pcall(item.attacher.setJointMoveDown, item.attacher, item.jointDescIndex, false)
elseif action == "fold" then
pcall(object.setFoldDirection, object, foldDirection)
end
end
if #actions > 0 then count = count + 1 end
end
return count
end
-- Som stowForTransport, men logget og pcall'et (til stop-vejene).
function ADHitch.stow(vehicle, why)
local ok, count = pcall(ADHitch.stowForTransport, vehicle)
if not ok then
Logging.warning(ADHitch.LOG_PREFIX .. "'%s': kunne ikke gøre redskaber klar til vej: %s", vehicleName(vehicle), tostring(count))
elseif count > 0 then
Logging.info(ADHitch.LOG_PREFIX .. "'%s': %d redskab(er) slukket, løftet og foldet (%s)", vehicleName(vehicle), count, tostring(why))
end
end
function ADHitch.foldForTransport(vehicle)
for _, implement in ipairs(ADHitch.rearImplementsOf(vehicle)) do
local object = implement.object
local unfolded = object.getIsUnfolded ~= nil and object:getIsUnfolded()
if unfolded and object.spec_foldable ~= nil and object.setFoldDirection ~= nil then
object:setFoldDirection(-(object.spec_foldable.turnOnFoldDirection or 1))
end
end
end
function ADHitch.rearImplementsOf(vehicle)
return ADHitch.rearImplements(attachedImplements(vehicle))
end
local function parkIdOf(adEnv, vehicle)
local index = vehicle.advd ~= nil and vehicle.advd.parkDestination or nil
local marker = type(index) == "number" and index >= 1 and adEnv.ADGraphManager:getMapMarkerById(index) or nil
return marker ~= nil and marker.id or nil
end
-- Kobler redskaberne bag traktoren af. -> ok, navne eller årsag. Med job bekræftes afkoblingen ved næste tick
-- (ok først når intet sidder bagpå); uden job (konsol) meldes ok straks efter kaldet.
function ADHitch.tryDetach(vehicle, job)
local rear = ADHitch.rearImplements(attachedImplements(vehicle))
local step = ADHitch.detachStep(#rear, job ~= nil and job.detachNames ~= nil)
if step == "done" then return true, job.detachNames end
if step == "nothing" then return false, "nothingToDetach" end
local names = {}
for _, implement in ipairs(rear) do
local object = implement.object
local allowed, warning = true, nil
if object.isDetachAllowed ~= nil then allowed, warning = object:isDetachAllowed() end
if not allowed and object.spec_foldable ~= nil and object.setFoldDirection ~= nil then
-- plove o.l. må kun kobles af udfoldet (in-game 28/9: "You first need to unfold the tool") — fold ud
-- og prøv igen ved næste tick, mens foldningen kører
object:setFoldDirection(object.spec_foldable.turnOnFoldDirection or 1)
return false, "unfolding: " .. tostring(warning)
end
if not allowed then return false, tostring(warning or "detachNotAllowed") end
vehicle:detachImplementByObject(object)
table.insert(names, implement.name)
end
if job == nil then return true, table.concat(names, ", ") end
if job.detachNames ~= nil then
job.detachRetries = (job.detachRetries or 0) + 1
if job.detachRetries == 1 then
Logging.warning(ADHitch.LOG_PREFIX .. "'%s': '%s' sidder der stadig efter afkobling — prøver igen", vehicleName(vehicle), table.concat(names, ", "))
end
end
job.detachNames = job.detachNames or table.concat(names, ", ")
return false, "stillAttached: " .. table.concat(names, ", ")
end
local function start(adEnv, vehicle, markerId, action, onDone)
local marker = adEnv.ADGraphManager:getMapMarkerByWayPointId(markerId)
local job = ADHitch.newJob(markerId, marker ~= nil and marker.name or tostring(markerId), action)
job.onDone = onDone
jobs[vehicle] = job
ADFieldWork.startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, markerId)
Logging.info(ADHitch.LOG_PREFIX .. "'%s' kører til '%s'", vehicleName(vehicle), job.markerName)
return job
end
-- Kørende kobl-på-job for traktoren (til bakningen de sidste meter), ellers nil.
function ADHitch.pendingAttach(vehicle)
local job = jobs[vehicle]
if job ~= nil and job.action == "attach" and job.state == "driving" then return job end
return nil
end
-- Afbryd et hente-/koble-job (Stop alt, høsten slut, spilleren overtager): intet kobles på bagefter, og
-- onDone kaldes ikke. -> true hvis der var et job.
function ADHitch.cancel(vehicle)
local had = jobs[vehicle] ~= nil
jobs[vehicle] = nil
if ADPool ~= nil and ADPool.setReturning ~= nil then ADPool.setReturning(vehicle, false) end
if had and vehicle.stopAutoDrive ~= nil then pcall(vehicle.stopAutoDrive, vehicle) end
return had
end
-- Kør til en Connect-markør og kobl på. onDone(vehicle, ok, redskab eller årsag) kaldes én gang.
function ADHitch.fetch(adEnv, vehicle, markerId, onDone)
return start(adEnv, vehicle, markerId, "attach", onDone)
end
-- Kør til en Afkobl-markør og kobl af. onDone(vehicle, ok, navne eller årsag) kaldes én gang.
function ADHitch.drop(adEnv, vehicle, markerId, onDone)
return start(adEnv, vehicle, markerId, "detach", onDone)
end
-- Tur: markørerne køres i rækkefølge. Connect = kobl på, Afkobl eller vognens egen AD-parkering = kobl af,
-- andre = kør dertil (og hold
-- ROUTE_WAIT_MS, hvis det ikke er sidste trin). Stopper ved første fejl. onDone(vehicle, ok, årsag) til sidst.
local waits = {} -- {vehicle, leftMs, continue}
function ADHitch.route(adEnv, vehicle, markerIds, onDone)
local function runStep(index)
if index > #markerIds then
Logging.info(ADHitch.LOG_PREFIX .. "'%s' er færdig med turen (%d trin)", vehicleName(vehicle), #markerIds)
if onDone ~= nil then pcall(onDone, vehicle, true) end
return
end
local marker = adEnv.ADGraphManager:getMapMarkerByWayPointId(markerIds[index])
local action = ADHitch.resolveAction(ADHitch.stepAction(marker ~= nil and marker.name or nil),
isImplementPark(adEnv, vehicle, markerIds[index]))
start(adEnv, vehicle, markerIds[index], action, function(_, ok, detail)
if not ok then
Logging.warning(ADHitch.LOG_PREFIX .. "'%s' stopper turen ved trin %d", vehicleName(vehicle), index)
if onDone ~= nil then pcall(onDone, vehicle, false, detail) end
return
end
local waitMs = ADHitch.waitAfter(action, index, #markerIds)
if waitMs > 0 then
Logging.info(ADHitch.LOG_PREFIX .. "'%s' holder %d s ved '%s'", vehicleName(vehicle), waitMs / 1000,
marker ~= nil and marker.name or "?")
table.insert(waits, {vehicle = vehicle, leftMs = waitMs, continue = function() runStep(index + 1) end})
else
runStep(index + 1)
end
end)
end
runStep(1)
end
-- Puljeredskaber bag traktoren sættes af på deres parkering, og traktoren kører hjem til sin egen.
-- Kun redskaber med en Connect-markør til deres parkering er puljeredskaber; andre bliver siddende.
-- Parkeringer (waypoint-id) for puljeredskaberne bag traktoren (tom = ingen puljeredskaber bag).
function ADHitch.poolParkIds(adEnv, tractor)
local markers = adEnv.ADGraphManager:getMapMarkers()
local parkIds = {}
for _, implement in ipairs(ADHitch.rearImplementsOf(tractor)) do
local parkId = parkIdOf(adEnv, implement.object)
local park = parkId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(parkId) or nil
if park ~= nil and ADPool.connectFor(park.name, markers) ~= nil then table.insert(parkIds, parkId) end
end
return parkIds
end
function ADHitch.returnHome(adEnv, tractor, onDone)
local parkIds = ADHitch.poolParkIds(adEnv, tractor)
if #parkIds == 0 then return false end
local route = ADHitch.homeRoute(parkIds, parkIdOf(adEnv, tractor))
ADPool.setReturning(tractor, true)
ADHitch.route(adEnv, tractor, route, function(vehicle, ok, detail)
ADPool.setReturning(vehicle, false)
if onDone ~= nil then onDone(vehicle, ok, detail) end
end)
return true
end
local function tickWaits(elapsed)
for i = #waits, 1, -1 do
local wait = waits[i]
wait.leftMs = wait.leftMs - elapsed
if wait.leftMs <= 0 then
table.remove(waits, i)
wait.continue()
end
end
end
-- Spilleren sender selv en traktor til en Connect-/Afkobl-markør med AD "Kør til": den kobles også.
local function watchManualTrips(adEnv)
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
local module = stateModule(vehicle)
if jobs[vehicle] == nil and vehicle.spec_attacherJoints ~= nil and module ~= nil and module:isActive()
and module:getMode() == adEnv.AutoDrive.MODE_DRIVETO then
local marker = module:getFirstMarker()
local action = nil
if marker ~= nil then
local isPoolPark = false
for _, parkId in ipairs(ADHitch.poolParkIds(adEnv, vehicle)) do
if parkId == marker.id then isPoolPark = true end
end
action = ADHitch.manualAction(ADHitch.parseMarker(marker.name), isPoolPark)
end
if action ~= nil then jobs[vehicle] = ADHitch.newJob(marker.id, marker.name, action) end
end
end
end
local DONE_TEXT = {attach = "koblede '%s' på", detach = "koblede '%s' af", arrive = "er fremme%s"}
local FAIL_TEXT = {attach = "kunne ikke koble på", detach = "kunne ikke koble af", arrive = "nåede ikke frem"}
local function finish(vehicle, job, ok, detail, adEnv)
jobs[vehicle] = nil
if ok then
Logging.info(ADHitch.LOG_PREFIX .. "'%s' " .. DONE_TEXT[job.action] .. " ved '%s'", vehicleName(vehicle),
vehicleName(detail), job.markerName)
else
Logging.warning(ADHitch.LOG_PREFIX .. "'%s' %s ved '%s': %s", vehicleName(vehicle), FAIL_TEXT[job.action],
job.markerName, tostring(detail))
end
if job.onDone ~= nil then
pcall(job.onDone, vehicle, ok, detail)
elseif ok and job.action == "attach" and ADHitch.onManualAttach ~= nil then
-- spillerens egen tur til Connect: lad fx flowet starte traktoren bagefter
local okHook, err = pcall(ADHitch.onManualAttach, adEnv, vehicle, detail)
if not okHook then Logging.warning(ADHitch.LOG_PREFIX .. "efter manuel kobling: %s", tostring(err)) end
end
end
local ACTIONS = {attach = ADHitch.tryAttach, detach = ADHitch.tryDetach, arrive = function() return true, "" end}
function ADHitch.tick(adEnv, dt)
if adEnv == nil or adEnv.ADGraphManager == nil then return end
tickTimer = tickTimer + dt
if tickTimer < ADHitch.TICK_MS then return end
local elapsed = tickTimer
tickTimer = 0
tickWaits(elapsed)
watchManualTrips(adEnv)
-- kopi: et færdigt trin starter næste trin (nyt job på samme maskine) midt i løkken
local current = {}
for vehicle, job in pairs(jobs) do current[vehicle] = job end
for vehicle, job in pairs(current) do
local succeeded, detail = false, nil
if job.state == "acting" then
succeeded, detail = ACTIONS[job.action](vehicle, job)
if not succeeded then job.lastReason = detail end
end
local state = vehicleState(adEnv, vehicle, job.markerId)
if job.crept then state.arriveDistance = ADHitch.ARRIVE_DISTANCE + ADHitch.CREPT_EXTRA_DISTANCE end
ADHitch.step(job, state, elapsed, succeeded)
if job.state == "done" then
finish(vehicle, job, true, detail, adEnv)
elseif job.state == "failed" then
finish(vehicle, job, false, job.lastReason or job.reason, adEnv)
end
end
end
-- Konsol: "spuConnect" / "spuDetach" på den maskine spilleren sidder i.
local function currentVehicle()
return g_localPlayer ~= nil and g_localPlayer.getCurrentVehicle ~= nil and g_localPlayer:getCurrentVehicle() or nil
end
function ADHitch:consoleConnect()
local vehicle = currentVehicle()
if vehicle == nil then return "spuConnect: sæt dig i en traktor først" end
local ok, detail = ADHitch.tryAttach(vehicle)
if ok then return "spuConnect: koblede " .. vehicleName(detail) .. " på" end
return "spuConnect: intet koblet på (" .. tostring(detail) .. ")"
end
function ADHitch:consoleDetach()
local vehicle = currentVehicle()
if vehicle == nil then return "spuDetach: sæt dig i en traktor først" end
local ok, detail = ADHitch.tryDetach(vehicle)
if ok then return "spuDetach: koblede " .. tostring(detail) .. " af" end
return "spuDetach: intet koblet af (" .. tostring(detail) .. ")"
end
-- "spuTrip Connect Bredal > Start > Bredal > Start": maskinen spilleren sidder i kører turen
-- (spilleren kan blive siddende; AD kører også med spilleren i førerhuset).
function ADHitch:consoleTrip(...)
local vehicle = currentVehicle()
if vehicle == nil then return "spuTrip: sæt dig i en traktor først" end
local adEnv = ADFieldSilos ~= nil and ADFieldSilos.adEnv or nil
if adEnv == nil then return "spuTrip: AutoDrive er ikke klar endnu" end
local names = ADHitch.parseTrip(...)
if #names == 0 then return "spuTrip: brug fx spuTrip Connect Bredal > Start > Bredal > Start" end
local markers, ids = adEnv.ADGraphManager:getMapMarkers(), {}
for _, name in ipairs(names) do
local marker = ADHitch.findMarker(markers, name)
if marker == nil then return "spuTrip: ukendt markør '" .. name .. "'" end
table.insert(ids, marker.id)
end
ADHitch.route(adEnv, vehicle, ids)
return "spuTrip: " .. vehicleName(vehicle) .. " kører " .. table.concat(names, " > ")
end
if addConsoleCommand ~= nil then
addConsoleCommand("spuConnect", "Kobler nærmeste redskab på den maskine du sidder i", "consoleConnect", ADHitch)
addConsoleCommand("spuTrip", "Kør en tur: spuTrip Connect Bredal > Start > Bredal > Start", "consoleTrip", ADHitch)
addConsoleCommand("spuDetach", "Kobler redskaberne bag den maskine du sidder i af", "consoleDetach", ADHitch)
end