-- AD Pool -- Maskinpulje: plove, kultivatorer, såmaskiner … holder frakoblet på deres AD-parkering "X", og en ledig -- solo-traktor henter dem via markøren "Connect X", når markarbejdet mangler en færdig kombination. -- Ren logik øverst (testes med luajit), spil-adapter nederst. ADPool = {} ADPool.LOG_PREFIX = "ADSmartPickup: pulje: " ADPool.QUARANTINE_MS = 600000 -- redskab der ikke kunne kobles på, springes over så længe -- ---- ren logik ---- -- Rolle for et frakoblet redskab ud fra dets egne specs (samme prioritet som markflåden); en ren vogn med -- tip/snegl er tømmevogn. function ADPool.roleOf(specs) local role = ADFieldFleet.roleFromSpecs(specs) if role ~= nil and role ~= "unloader" then return role end if specs.spec_trailer and specs.spec_dischargeable then return "unloader" end return role end -- Butiksdata er enten et tal eller en tabel pr. konfiguration: største tal, ellers nil. function ADPool.numberOf(value) if type(value) == "number" then return value end if type(value) ~= "table" then return nil end local best = nil for _, entry in pairs(value) do if type(entry) == "number" and (best == nil or entry > best) then best = entry end end return best end -- Mangler et af tallene, tillades det (intet må gå i stå på manglende data). function ADPool.powerOk(tractorHp, neededHp) if tractorHp == nil or neededHp == nil or tractorHp <= 0 or neededHp <= 0 then return true end return tractorHp >= neededHp end -- Markøren "Connect " for en parkering (navneregel). function ADPool.connectFor(parkName, markers) if type(parkName) ~= "string" then return nil end local wanted = parkName:lower() for _, marker in pairs(markers or {}) do local action, target = ADHitch.parseMarker(marker.name) if action == "attach" and target ~= nil and target:lower() == wanted then return marker end end return nil end local function hasRole(item, role) return item.role == role or (item.roles ~= nil and item.roles[role] == true) end local function distanceSq(ax, az, bx, bz) return (ax - bx) ^ 2 + (az - bz) ^ 2 end local function nearest(items, x, z, keep) local best, bestDistance = nil, math.huge for _, item in ipairs(items) do if keep(item) then local distance = distanceSq(item.x, item.z, x, z) if distance < bestDistance then best, bestDistance = item, distance end end end return best end -- Redskab med rollen nærmest marken, derefter nærmeste ledige traktor med nok hk til redskabet. -- blocked = {[id] = true} (reserverede og redskaber i karantæne). accept (valgfri): implement -> bool. function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, accept) blocked = blocked or {} local free = {} for _, tractor in ipairs(tractors or {}) do if not blocked[tractor.id] then table.insert(free, tractor) end end local candidates = {} for _, implement in ipairs(implements or {}) do if hasRole(implement, role) and not blocked[implement.id] and (accept == nil or accept(implement)) then table.insert(candidates, implement) end end if #candidates == 0 then return nil, "noPoolImplement" end if #free == 0 then return nil, "noPoolTractor" end table.sort(candidates, function(a, b) return distanceSq(a.x, a.z, fieldX, fieldZ) < distanceSq(b.x, b.z, fieldX, fieldZ) end) local weakest = nil for _, implement in ipairs(candidates) do local tractor = nearest(free, implement.x, implement.z, function(t) return ADPool.powerOk(t.hp, implement.neededHp) end) if tractor ~= nil then return {implement = implement, tractor = tractor} end if weakest == nil or (implement.neededHp or 0) < weakest then weakest = implement.neededHp end end return nil, "tooWeak", {weakest or 0} end -- Ventende marker først (længst ventende først, lige -> lavest id); resten bagefter i oprindelig rækkefølge. function ADPool.queueOrder(infos, waitingSince) local waiting, rest = {}, {} for _, info in ipairs(infos or {}) do if waitingSince ~= nil and waitingSince[info.id] ~= nil then table.insert(waiting, info) else table.insert(rest, info) end end table.sort(waiting, function(a, b) local wa, wb = waitingSince[a.id], waitingSince[b.id] if wa ~= wb then return wa < wb end return a.id < b.id end) for _, info in ipairs(rest) do table.insert(waiting, info) end return waiting end -- Reservationer (i hukommelsen): owner -> {id, ...}. Karantæne: id -> udløbstid. local reservations, quarantined = {}, {} function ADPool.reserve(owner, ids) reservations[owner] = ids end function ADPool.release(owner) reservations[owner] = nil end function ADPool.quarantine(id, nowMs) quarantined[id] = nowMs + ADPool.QUARANTINE_MS end function ADPool.blocked(nowMs) local result = {} for _, ids in pairs(reservations) do for _, id in ipairs(ids) do result[id] = true end end for id, untilMs in pairs(quarantined) do if nowMs < untilMs then result[id] = true end end return result end