diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 91586ae..723e05d 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -13,6 +13,9 @@ ADSmartPickup = {} -- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et -- vogntog bagved. Siloer tættere end dette regnes som "markørens silo". ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE = 40 +-- I tætte silo-rækker ligger nabosiloens trigger FORAN næsen og er ofte nærmere +-- end markørens egen. Triggere mere end dette foran næsen hører til naboen. +ADSmartPickup.MAX_TRIGGER_AHEAD_OF_MARKER = 2 -- Under dette niveau regnes siloen som tom (undgår ture efter støv-rester). ADSmartPickup.MIN_USEFUL_LITERS = 1 ADSmartPickup.LOG_PREFIX = "ADSmartPickup: " @@ -68,41 +71,67 @@ local function getWantedFillTypes(driver) return wanted end -local function getDistanceToTriggers(triggers, nodeFields, x, z) - local closest = math.huge +-- Ankomstretning ved markøren (enhedsvektor) ud fra forrige waypoint, ellers nil. +local function getArrivalDirection(adEnv, wayPoint) + local previousId = wayPoint.incoming ~= nil and wayPoint.incoming[1] or nil + local previous = previousId ~= nil and adEnv.ADGraphManager:getWayPointById(previousId) or nil + if previous == nil then + return nil + end + local dx, dz = wayPoint.x - previous.x, wayPoint.z - previous.z + local length = math.sqrt(dx * dx + dz * dz) + if length < 0.01 then + return nil + end + return {x = dx / length, z = dz / length} +end + +-- Nærmeste trigger-afstand for en station, samlet og blandt triggere BAG næsen. +local function getDistancesToTriggers(triggers, nodeFields, wayPoint, direction) + local closest, closestBehind = math.huge, math.huge for _, trigger in pairs(triggers or {}) do for _, field in ipairs(nodeFields) do local node = trigger[field] if node ~= nil and entityExists(node) then local tx, _, tz = getWorldTranslation(node) - closest = math.min(closest, MathUtil.vector2Length(x - tx, z - tz)) + local distance = MathUtil.vector2Length(wayPoint.x - tx, wayPoint.z - tz) + closest = math.min(closest, distance) + -- > 0: triggeren ligger bag næsen set i køreretningen + local behind = direction ~= nil and ((wayPoint.x - tx) * direction.x + (wayPoint.z - tz) * direction.z) or nil + if behind ~= nil and behind >= -ADSmartPickup.MAX_TRIGGER_AHEAD_OF_MARKER then + closestBehind = math.min(closestBehind, distance) + end end end end - return closest + return closest, closestBehind end -local function getClosestStation(candidateStations, triggersField, nodeFields, wayPoint, farmId) - local bestStation = nil - local bestDistance = ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE +-- Markørens station: nærmeste trigger bag næsen; findes ingen (eller er +-- retningen ukendt), nærmeste trigger overhovedet. +local function getClosestStation(adEnv, candidateStations, triggersField, nodeFields, wayPoint, farmId) + local direction = getArrivalDirection(adEnv, wayPoint) + local best = {any = {distance = ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE}, behind = {distance = ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE}} for _, station in pairs(candidateStations) do if g_currentMission.accessHandler:canFarmAccess(farmId, station) then - local distance = getDistanceToTriggers(station[triggersField], nodeFields, wayPoint.x, wayPoint.z) - if distance < bestDistance then - bestDistance = distance - bestStation = station + local closest, closestBehind = getDistancesToTriggers(station[triggersField], nodeFields, wayPoint, direction) + if closest < best.any.distance then + best.any = {distance = closest, station = station} + end + if closestBehind < best.behind.distance then + best.behind = {distance = closestBehind, station = station} end end end - return bestStation + return best.behind.station or best.any.station end -local function getLoadStationAtMarker(wayPoint, farmId) - return getClosestStation(g_currentMission.storageSystem:getLoadingStations(), "loadTriggers", LOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) +local function getLoadStationAtMarker(adEnv, wayPoint, farmId) + return getClosestStation(adEnv, g_currentMission.storageSystem:getLoadingStations(), "loadTriggers", LOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) end -local function getUnloadStationAtMarker(wayPoint, farmId) - return getClosestStation(g_currentMission.storageSystem:getUnloadingStations(), "unloadTriggers", UNLOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) +local function getUnloadStationAtMarker(adEnv, wayPoint, farmId) + return getClosestStation(adEnv, g_currentMission.storageSystem:getUnloadingStations(), "unloadTriggers", UNLOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) end -- Siloens EGET indhold. Udvidelses-siloer (isExtension) deler lager med naboer @@ -143,7 +172,7 @@ local function collectMarkerStations(adEnv, candidateMarkers, farmId, excludedPl local markerStations = {} for _, marker in pairs(candidateMarkers) do local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id) - local station = wayPoint ~= nil and getLoadStationAtMarker(wayPoint, farmId) or nil + local station = wayPoint ~= nil and getLoadStationAtMarker(adEnv, wayPoint, farmId) or nil local isExcluded = station ~= nil and excludedPlaceable ~= nil and station.owningPlaceable == excludedPlaceable if station ~= nil and not isExcluded then table.insert(markerStations, {marker = marker, station = station}) @@ -235,7 +264,7 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients) return nil end local targetWayPoint = adEnv.ADGraphManager:getWayPointById(secondMarker.id) - local target = targetWayPoint ~= nil and getUnloadStationAtMarker(targetWayPoint, farmId) or nil + local target = targetWayPoint ~= nil and getUnloadStationAtMarker(adEnv, targetWayPoint, farmId) or nil if target == nil then return nil end diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 09b8b9a..cd4060a 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.2.0.0 + 1.3.0.0 <en>AD Smart Pickup</en> diff --git a/README.md b/README.md index 8521979..8d3255d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ Slå fra ved at vælge en anden enkelt vare. - Siloens eget lager rangeres før nabo-lån (udvidelses-siloer deler lager inden for `storageRadius`). - Intet fund, intet behov eller fejl i et spil-API → AutoDrives originale rotation. Valg logges som `ADSmartPickup:` i log.txt. - Kræver i AutoDrive: "Brug mapper" + "Rotér mål: pålæsning" på køretøjet. +- **Markør ↔ silo (v1.3):** markøren er dér hvor trækkerens næse stopper; vognen holder et vogntog + bagved. Markørens silo er derfor nærmeste trigger **bag næsen** set i ankomstretningen (forrige + waypoint), højst 2 m foran. I tætte rækker (15 m mellem siloerne) er nabosiloens trigger ellers + nærmere end markørens egen. Ingen trigger bag næsen/ukendt retning → nærmeste trigger som før. - **Står og læsser (v1.2):** med rotation + mapper afslutter AutoDrive normalt pålæsningen i det øjeblik markøren nås (læsning skal være startet på vej ind, ellers kører den tom videre). Når moden har valgt siloen, ser AutoDrive under `LoadAtDestinationTask.update` rotationen som slået fra for netop det diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index a0c6abc..33ed85a 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -61,8 +61,13 @@ g_currentMission = { -- ---- AutoDrive-mocks ---- local markers, wayPoints = {}, {} -local function addMarker(index, name, group, x, z) - wayPoints[index] = {x = x, z = z} +-- fromX/fromZ: forrige waypoint på ruten ind til markøren (giver ankomstretningen) +local function addMarker(index, name, group, x, z, fromX, fromZ) + wayPoints[index] = {x = x, z = z, incoming = {}} + if fromX ~= nil then + wayPoints[1000 + index] = {x = fromX, z = fromZ, incoming = {}} + wayPoints[index].incoming = {1000 + index} + end table.insert(markers, {id = index, markerIndex = index, name = name, group = group}) end @@ -306,6 +311,23 @@ manager:getNextPickup(loader, false) FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) check("L5 AutoDrives eget valg -> rotation uberørt", task.seenRotate, RT_ONLYPICKUP) +-- G1: tæt silo-række, kørsel mod -x. Næsen stopper ved markøren, vognen holder et +-- vogntog BAGVED -> markørens silo er den bag næsen, ikke den (nærmere) foran. +resetWorld() +addMarker(1, "1 Silo", "Siloer", 1243, 0, 1260, 0) +addMarker(2, "2 Silo", "Siloer", 1228, 0, 1243, 0) +table.insert(stations, makeStation(101, 1255, 1, {[HAY] = 9000}, false, {[HAY] = 9000})) -- bag markør 1 +table.insert(stations, makeStation(102, 1239, 1, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000})) -- foran markør 1, bag markør 2 +table.insert(stations, makeStation(103, 1224, 1, {}, false, {})) -- foran markør 2 +check("G1 roer ligger i siloen bag markør 2", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 2) +check("G1 hø ligger i siloen bag markør 1", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1) + +-- G2: ingen trigger bag næsen (eller ukendt retning) -> nærmeste trigger som før +resetWorld() +addMarker(1, "Ensom silo", "Siloer", 100, 0, 120, 0) +table.insert(stations, makeStation(101, 95, 1, {[HAY] = 9000})) +check("G2 kun trigger foran -> nærmeste bruges", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1) + -- L7: AD stopper -> grebet ryddes, næste opgave venter ikke på en gammel silo resetWorld() addMarker(1, "Silo 1", "Andet", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 5000}))