feat: fabriksstyret forsyning — målets tændte produktioner styrer ingredienslisten (v1.6.0.0)

Er aflæsningsmålet et produktionspunkt, erstattes ingredienslisten af inputs fra
ønskede produktioner (tændte, eller auto-slukkede af kortet med status
MISSING_INPUTS — Dakotas ExtendedProductionPoint), begrænset til det vognen kan
bære. Brugerens multi-valg er kun tændknappen og gendannes ved stop som hidtil.

Prioritet: tid til tom = lager / (Σ amount × cyclesPerMinute); varer med kendt
forbrug scorer i ]1,2] og går altid forud for relativ tomhed [0,1]. Loggen viser
forbrug og minutter; fabrik uden ønskede produktioner logges én gang.

API-navne verificeret mod EasyDevControls + Dakota16x ExtendedProductionPoint.
This commit is contained in:
2026-09-22 20:04:08 +02:00
parent 8c76790ece
commit 795c03663c
6 changed files with 257 additions and 14 deletions
+49 -5
View File
@@ -48,29 +48,73 @@ function ADSupplyPlanner.copyList(list)
return copy
end
-- Ønsker en produktion input? Tændt, eller slukket af kortet fordi den MANGLEDE
-- input (Dakotas ExtendedProductionPoint slår den fra automatisk, men lader
-- status stå på MISSING_INPUTS). Manuelt slukket = status INACTIVE -> nej.
function ADSupplyPlanner.isProductionWanted(isEnabled, status, missingInputsStatus)
if isEnabled then
return true
end
return status ~= nil and status == missingInputsStatus
end
-- productions: produktionspunktets liste ({id, cyclesPerMinute, inputs = {{type, amount}}}).
-- isWanted(production) afgør om den tæller; canCarry[fillType] = det vognen kan bære.
-- Returnerer {[fillType] = liter pr. minut} summeret over ønskede produktioner.
function ADSupplyPlanner.collectProductionDemand(productions, isWanted, canCarry)
local demand = {}
for _, production in ipairs(productions) do
if isWanted(production) then
local cyclesPerMinute = production.cyclesPerMinute or 0
for _, input in ipairs(production.inputs or {}) do
if canCarry[input.type] then
demand[input.type] = (demand[input.type] or 0) + (input.amount or 0) * cyclesPerMinute
end
end
end
end
return demand
end
-- Minutter før målets lager af varen er tomt ved nuværende forbrug; nil uden forbrug.
function ADSupplyPlanner.minutesToEmpty(level, litersPerMinute)
if litersPerMinute == nil or litersPerMinute <= 0 then
return nil
end
return level / litersPerMinute
end
-- Hvor meget trænger målet til denne ingrediens? Højere = hentes først.
-- Alle tal er liter i aflæsningsmålets lager. Standard: relativ tomhed (0..1),
-- så en lille næsten-tom tank vinder over en stor halvfuld.
function ADSupplyPlanner.scoreNeed(level, capacity, freeSpace)
-- så en lille næsten-tom tank vinder over en stor halvfuld. Kendes forbruget
-- (fabriksstyret), tæller i stedet tid til tom: score i ]1, 2], så varer med
-- forbrug altid går forud for varer uden, og kortest tid vinder.
function ADSupplyPlanner.scoreNeed(level, capacity, freeSpace, minutesToEmpty)
if minutesToEmpty ~= nil then
return 1 + 1 / (1 + minutesToEmpty)
end
if capacity <= 0 then
return 0
end
return freeSpace / capacity
end
-- needs: {{fillType, level, capacity}, ...}. Returnerer NY liste, mest trængende først;
-- ingredienser uden plads i målet er sorteret fra.
-- needs: {{fillType, level, capacity[, litersPerMinute]}, ...}. Returnerer NY liste,
-- mest trængende først; ingredienser uden plads i målet er sorteret fra.
function ADSupplyPlanner.rankNeeds(needs)
local ranked = {}
for _, need in ipairs(needs) do
local freeSpace = need.capacity - need.level
if freeSpace >= ADSupplyPlanner.MIN_FREE_LITERS then
local minutesToEmpty = ADSupplyPlanner.minutesToEmpty(need.level, need.litersPerMinute)
table.insert(ranked, {
fillType = need.fillType,
level = need.level,
capacity = need.capacity,
freeSpace = freeSpace,
score = ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace),
litersPerMinute = need.litersPerMinute,
minutesToEmpty = minutesToEmpty,
score = ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
})
end
end