187 lines
8.1 KiB
Lua
187 lines
8.1 KiB
Lua
-- AD Field Fleet
|
|
-- Markflåden som ren logik: hvilken rolle et sæt har ud fra redskabernes specs, og hvilke ledige sæt der
|
|
-- sendes til en mark. Kun sæt med flueben bruges; et sæt spilleren sidder i, røres aldrig.
|
|
|
|
ADFieldFleet = {}
|
|
|
|
-- Rækkefølge = prioritet når et køretøj har flere (såmaskine med gødning er såmaskine; traktor med plov er
|
|
-- plov, ikke aflæsningsvogn). sprayRole kommer fra sprøjtens/sprederens fyld (gødning/kalk/ukrudt).
|
|
ADFieldFleet.ROLE_SPECS = {
|
|
{role = "harvester", spec = "spec_combine"},
|
|
{role = "baler", spec = "spec_baler"},
|
|
{role = "collector", spec = "spec_forageWagon"},
|
|
{role = "rake", spec = "spec_windrower"},
|
|
{role = "tedder", spec = "spec_tedder"},
|
|
{role = "mower", spec = "spec_mower"},
|
|
{role = "seeder", spec = "spec_sowingMachine"},
|
|
{role = "roller", spec = "spec_roller"},
|
|
{role = "weeder", spec = "spec_weeder"},
|
|
{role = "sprayRole"},
|
|
{role = "mulcher", spec = "spec_mulcher"},
|
|
-- plov før kultivator: FS25-plove har også spec_cultivator (in-game 28/9: POV 5 XL blev "kultivator")
|
|
{role = "plow", spec = "spec_plow"},
|
|
{role = "cultivator", spec = "spec_cultivator"},
|
|
{role = "baleCollector", spec = "canBaleCollect"},
|
|
{role = "unloader", spec = "canUnload"},
|
|
}
|
|
|
|
function ADFieldFleet.roleFromSpecs(specs)
|
|
for _, entry in ipairs(ADFieldFleet.ROLE_SPECS) do
|
|
if entry.role == "sprayRole" then
|
|
if specs.sprayRole ~= nil then return specs.sprayRole end
|
|
elseif specs[entry.spec] then
|
|
return entry.role
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Ekstra opgaver en rolle kan tage: pløjning klargør jorden lige så godt som kultivering (ikke omvendt).
|
|
ADFieldFleet.EXTRA_ROLES = {plow = {plow = true, cultivator = true}}
|
|
|
|
function ADFieldFleet.rolesFor(role)
|
|
local roles = ADFieldFleet.EXTRA_ROLES[role]
|
|
if roles == nil then return nil end
|
|
local copy = {}
|
|
for key, value in pairs(roles) do copy[key] = value end
|
|
return copy
|
|
end
|
|
|
|
-- Kandidater med rollen, sorteret nærmest først, delt i brugbare og afviste (med første grund).
|
|
local function sortCandidates(candidates, role, x, z, requiredWidth, accept)
|
|
local usable, sawRole, sawAccepted = {}, false, false
|
|
local widthReason, widthArgs = nil, nil
|
|
for _, candidate in ipairs(candidates) do
|
|
if (candidate.role == role or (candidate.roles ~= nil and candidate.roles[role])) and candidate.enabled then
|
|
sawRole = true
|
|
if accept == nil or accept(candidate) then
|
|
sawAccepted = true
|
|
if not candidate.busy and not candidate.controlled then
|
|
local ok, reason, args = ADFieldModel.checkWidth(role, candidate.width or 0, requiredWidth)
|
|
if ok then
|
|
local dx, dz = candidate.x - x, candidate.z - z
|
|
-- spreder med en anden vare på skal tømmes først: kun hvis ingen anden kan
|
|
local wrongFill = candidate.filledRole ~= nil and candidate.filledRole ~= role
|
|
-- fuld af en anden vare: bruges slet ikke til denne opgave (resten hældes kun ud når den er lille)
|
|
if not (wrongFill and candidate.dumpable == false) then
|
|
table.insert(usable, {candidate = candidate, distance = dx * dx + dz * dz, wrongFill = wrongFill})
|
|
end
|
|
elseif widthReason == nil then
|
|
widthReason, widthArgs = reason, args
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
table.sort(usable, function(a, b)
|
|
if a.wrongFill ~= b.wrongFill then return not a.wrongFill end
|
|
return a.distance < b.distance
|
|
end)
|
|
local reason, args = nil, nil
|
|
if not sawRole then
|
|
reason = "noRig"
|
|
elseif not sawAccepted then
|
|
reason = "cannotDo"
|
|
elseif widthReason ~= nil then
|
|
reason, args = widthReason, widthArgs
|
|
else
|
|
reason = "allBusy"
|
|
end
|
|
return usable, reason, args
|
|
end
|
|
|
|
-- Nærmeste brugbare sæt. Grund når intet passer: noRig (ingen med flueben og rollen), cannotDo (ingen kan
|
|
-- tage opgaven, fx afgrøden), allBusy, tooWide/tooNarrow (bredden, med tal fra den første).
|
|
-- accept (valgfri): candidate -> bool, fx "høsteren kan tage hvede".
|
|
-- Faste maskiner: rigIdsByField = {[fieldId] = {id, ...}}. En maskine der er fast på en ANDEN mark må
|
|
-- ikke bruges her. Har marken selv faste maskiner med rollen, bruges kun de (marken venter på sin egen);
|
|
-- ellers de frie. (in-game 26/9: sprøjten "til F22" blev sendt til F109.)
|
|
function ADFieldFleet.filterDedicated(candidates, fieldId, role, rigIdsByField)
|
|
local ownerOf, own = {}, {}
|
|
for otherId, ids in pairs(rigIdsByField or {}) do
|
|
for _, id in ipairs(ids or {}) do
|
|
ownerOf[id] = otherId
|
|
if otherId == fieldId then own[id] = true end
|
|
end
|
|
end
|
|
local ownWithRole = {}
|
|
for _, candidate in ipairs(candidates or {}) do
|
|
local hasRole = candidate.role == role or (candidate.roles ~= nil and candidate.roles[role])
|
|
if own[candidate.id] and hasRole then table.insert(ownWithRole, candidate) end
|
|
end
|
|
if #ownWithRole > 0 then return ownWithRole end
|
|
local free = {}
|
|
for _, candidate in ipairs(candidates or {}) do
|
|
if ownerOf[candidate.id] == nil then table.insert(free, candidate) end
|
|
end
|
|
return free
|
|
end
|
|
|
|
-- Gør en maskine fast på én mark (fjernes fra andre marker). Returnerer ny tabel {[fieldId] = ids}.
|
|
function ADFieldFleet.dedicate(rigIdsByField, fieldId, vehicleId, isOn)
|
|
local result = {}
|
|
for otherId, ids in pairs(rigIdsByField or {}) do
|
|
local copy = {}
|
|
for _, id in ipairs(ids or {}) do if id ~= vehicleId then table.insert(copy, id) end end
|
|
result[otherId] = copy
|
|
end
|
|
result[fieldId] = result[fieldId] or {}
|
|
if isOn then table.insert(result[fieldId], vehicleId) end
|
|
return result
|
|
end
|
|
|
|
function ADFieldFleet.pick(candidates, role, x, z, requiredWidth, accept)
|
|
local usable, reason, args = sortCandidates(candidates, role, x, z, requiredWidth, accept)
|
|
if usable[1] ~= nil then return usable[1].candidate end
|
|
return nil, reason, args
|
|
end
|
|
|
|
-- n sæt på én gang (multi-tool kræver alle baner): de n nærmeste, ellers ingen. notEnough har {ledige, n}.
|
|
function ADFieldFleet.pickMany(candidates, role, n, x, z, requiredWidth, accept)
|
|
local usable, reason, args = sortCandidates(candidates, role, x, z, requiredWidth, accept)
|
|
if #usable >= n then
|
|
local picked = {}
|
|
for index = 1, n do picked[index] = usable[index].candidate end
|
|
return picked
|
|
end
|
|
if #usable > 0 then return nil, "notEnough", {#usable, n} end
|
|
return nil, reason, args
|
|
end
|
|
|
|
-- Må sættet sendes ud? "all" (standard): alle ledige maskiner; "ticked": kun med flueben.
|
|
function ADFieldFleet.isEnabled(mode, enabledIds, id)
|
|
if mode == "ticked" then return (enabledIds or {})[id] == true end
|
|
return true
|
|
end
|
|
|
|
function ADFieldFleet.toggle(enabledIds, id)
|
|
local copy = {}
|
|
for key, value in pairs(enabledIds or {}) do copy[key] = value end
|
|
copy[id] = (not copy[id]) or nil
|
|
return copy
|
|
end
|
|
|
|
-- Til menuen: skjul maskiner der er faste på en ANDEN mark end den valgte. Returnerer synlige, antal
|
|
-- skjulte og de marker de er faste på (sorteret). selectedFieldId nil = vis alle.
|
|
function ADFieldFleet.visibleForField(rigs, selectedFieldId, rigIdsByField)
|
|
if selectedFieldId == nil then return rigs, 0, {} end
|
|
local ownerOf = {}
|
|
for fieldId, ids in pairs(rigIdsByField or {}) do
|
|
for _, id in ipairs(ids or {}) do ownerOf[id] = fieldId end
|
|
end
|
|
local visible, hidden, fieldSet = {}, 0, {}
|
|
for _, rig in ipairs(rigs or {}) do
|
|
local owner = ownerOf[rig.id]
|
|
if owner ~= nil and owner ~= selectedFieldId then
|
|
hidden = hidden + 1
|
|
fieldSet[owner] = true
|
|
else
|
|
table.insert(visible, rig)
|
|
end
|
|
end
|
|
local fields = {}
|
|
for fieldId in pairs(fieldSet) do table.insert(fields, fieldId) end
|
|
table.sort(fields, function(a, b) return tostring(a) < tostring(b) end)
|
|
return visible, hidden, fields
|
|
end
|