feat: v1.12.1.0 — prisregel via sæsonkurven, reserve til gårdens eget forbrug, paller til palleskur, fanen Lagre (idéer fra Distribution Redux)

This commit is contained in:
2026-09-23 10:34:41 +02:00
parent 43483bda76
commit 2d6e6dd7bf
16 changed files with 595 additions and 43 deletions
+275
View File
@@ -317,6 +317,281 @@ function ADSources.getSellMarkers(adEnv, farmId)
return result
end
-- ---- paller til lager: objektlager (palleskur) som mål ----
function ADSources.objectStorageAccepts(placeable, fillType)
local spec = placeable ~= nil and placeable.spec_objectStorage or nil
if spec == nil or spec.supportsPallets == false then
return false
end
if placeable.getObjectStorageSupportsFillType ~= nil then
local ok, value = pcall(placeable.getObjectStorageSupportsFillType, placeable, fillType)
return ok and value == true
end
return true
end
-- Ledige pladser (objekter); uendeligt når lageret ingen kapacitet opgiver.
function ADSources.objectStorageFreeSlots(placeable)
local spec = placeable ~= nil and placeable.spec_objectStorage or nil
if spec == nil then
return 0
end
local capacity = spec.capacity or 0
if capacity <= 0 then
return math.huge
end
local stored = spec.storedObjects ~= nil and #spec.storedObjects or (spec.numStoredObjects or 0)
return math.max(0, capacity - stored)
end
-- Nærmeste markør ved et objektlager der tager varen og har plads, med rute fra traktoren;
-- kilderne selv udelukkes. {wayPointId, name, free = pladser} eller nil.
function ADSources.findPalletStore(adEnv, vehicle, fillType, excludedPlaceables, isReachable, farmId)
local x, z = getNodePosition(vehicle.rootNode)
if x == nil then
return nil
end
local candidates = {}
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local source = ADSources.identify(adEnv, marker.id, farmId)
if source ~= nil and source.kind == "objectStorage" and not (excludedPlaceables ~= nil and excludedPlaceables[source.placeable])
and ADSources.objectStorageAccepts(source.placeable, fillType) and ADSources.objectStorageFreeSlots(source.placeable) >= 1 then
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
table.insert(candidates, {marker = marker, source = source, distance = distance2(x, z, wayPoint.x, wayPoint.z)})
end
end
table.sort(candidates, function(a, b) return a.distance < b.distance end)
for _, candidate in ipairs(candidates) do
if isReachable == nil or isReachable(candidate.marker.id) then
return {wayPointId = candidate.marker.id, name = tostring(candidate.marker.name), free = ADSources.objectStorageFreeSlots(candidate.source.placeable)}
end
end
return nil
end
-- ---- reserve: hvad gårdens egne fabrikker og stalde skal bruge det næste døgn ----
ADSources.RESERVE_HOURS = 24
local function isOwnedBy(placeable, farmId)
if placeable.getOwnerFarmId ~= nil then
local ok, owner = pcall(placeable.getOwnerFarmId, placeable)
return ok and owner == farmId
end
return placeable.ownerFarmId == farmId
end
local function isProductionWanted(productionPoint, production)
local isEnabled = false
if productionPoint.getIsProductionEnabled ~= nil then
isEnabled = productionPoint:getIsProductionEnabled(production.id) == true
else
for _, active in ipairs(productionPoint.activeProductions or {}) do
if active == production then isEnabled = true end
end
end
local missingInputs = ProductionPoint ~= nil and ProductionPoint.PROD_STATUS ~= nil and ProductionPoint.PROD_STATUS.MISSING_INPUTS or nil
return isEnabled or (missingInputs ~= nil and production.status == missingInputs)
end
-- Fabrik: døgnforbrug af varen i ønskede produktioner minus det den har, højst fri plads.
local function getProductionReserve(placeable, fillType)
local productionPoint = getProductionPoint(placeable)
if productionPoint == nil or productionPoint.storage == nil then
return 0
end
local perMinute = 0
for _, production in ipairs(productionPoint.productions or {}) do
if isProductionWanted(productionPoint, production) then
for _, input in ipairs(production.inputs or {}) do
if input.type == fillType then
local cyclesPerMinute = production.cyclesPerMinute or ((production.cyclesPerHour or 0) / 60)
perMinute = perMinute + (input.amount or 0) * cyclesPerMinute
end
end
end
end
if perMinute <= 0 then
return 0
end
local storage = productionPoint.storage
local level = storage.getFillLevel ~= nil and (storage:getFillLevel(fillType) or 0) or ((storage.fillLevels or {})[fillType] or 0)
local free = storage.getFreeCapacity ~= nil and storage:getFreeCapacity(fillType) or nil
if free == nil then
local capacity = storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0
free = math.max(0, capacity - level)
end
local need = perMinute * 60 * ADSources.RESERVE_HOURS - level
return math.max(0, math.min(need, free))
end
-- Stald: foder/halm/vand-rate × timer minus beholdning, højst fri plads (spec-felter som
-- Distribution Redux læser dem); automatisk vandforsyning = intet behov.
local function getHusbandryReserve(placeable, fillType)
if placeable.getHusbandryIsFillTypeSupported == nil or placeable.getHusbandryFillLevel == nil then
return 0
end
local okSupported, supported = pcall(placeable.getHusbandryIsFillTypeSupported, placeable, fillType)
if not okSupported or supported ~= true then
return 0
end
local name = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or ""
local rate, level = 0, placeable:getHusbandryFillLevel(fillType) or 0
local food, straw, water = placeable.spec_husbandryFood, placeable.spec_husbandryStraw, placeable.spec_husbandryWater
if name == "WATER" then
if water == nil or water.automaticWaterSupply then return 0 end
rate = water.litersPerHour or 0
elseif name == "STRAW" and straw ~= nil then
rate = straw.inputLitersPerHour or 0
elseif food ~= nil then
rate = food.litersPerHour or 0
level = 0
for _, value in pairs(food.fillLevels or {}) do level = level + (value or 0) end
end
if rate <= 0 then
return 0
end
local free = placeable.getHusbandryFreeCapacity ~= nil and (placeable:getHusbandryFreeCapacity(fillType) or 0) or math.huge
return math.max(0, math.min(rate * ADSources.RESERVE_HOURS - level, free))
end
-- {liters, consumers = {navne}} — gårdens samlede behov for varen; udelukkede placeables (kilderne) tæller ikke.
function ADSources.getFarmDemand(adEnv, fillType, farmId, excludedPlaceables)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local total, consumers = 0, {}
for _, placeable in pairs(placeables) do
if isOwnedBy(placeable, farmId) and not (excludedPlaceables ~= nil and excludedPlaceables[placeable]) then
local reserve = getProductionReserve(placeable, fillType) + getHusbandryReserve(placeable, fillType)
if reserve > 0 then
total = total + reserve
table.insert(consumers, getPlaceableName(placeable, "?"))
end
end
end
return {liters = total, consumers = consumers}
end
-- ---- inventar: alt gården har liggende, pr. bygning ----
local function addEntry(entries, fillType, level, capacity, extra)
local entry = newProduct(fillType, level, level, capacity, false)
entry.isOutput = extra ~= nil and extra.isOutput or false
entry.count = extra ~= nil and extra.count or nil
table.insert(entries, entry)
end
local function factoryEntries(placeable)
local productionPoint = getProductionPoint(placeable)
local storage = productionPoint ~= nil and productionPoint.storage or nil
local entries = {}
if storage == nil then return entries end
local outputs = {}
for _, production in ipairs(productionPoint.productions or {}) do
for _, output in ipairs(production.outputs or {}) do outputs[output.type] = true end
end
for fillType, level in pairs(storage.fillLevels or {}) do
if (level or 0) > 0 then
local capacity = storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0
addEntry(entries, fillType, level, capacity, {isOutput = outputs[fillType] == true})
end
end
return entries
end
local function siloEntries(placeable)
local entries, levels, capacities = {}, {}, {}
for _, storage in pairs(placeable.spec_silo.storages or {}) do
for fillType, level in pairs(storage.fillLevels or {}) do
if (level or 0) > 0 then
levels[fillType] = (levels[fillType] or 0) + level
capacities[fillType] = (capacities[fillType] or 0) + (storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0)
end
end
end
for fillType, level in pairs(levels) do addEntry(entries, fillType, level, capacities[fillType] or 0) end
return entries
end
local function objectStorageEntries(placeable)
local entries = {}
local spec = placeable.spec_objectStorage
for _, objectInfo in ipairs(spec.objectInfos or {}) do
local fillType, perObject = getObjectInfoContent(objectInfo)
local count = objectInfo.numObjects or 0
if fillType ~= nil and count > 0 then
addEntry(entries, fillType, count * perObject, spec.capacity or 0, {count = count})
end
end
return entries
end
local function husbandryEntries(placeable)
local entries, seen = {}, {}
local food = placeable.spec_husbandryFood
if food ~= nil then
local total = 0
for _, level in pairs(food.fillLevels or {}) do total = total + (level or 0) end
for fillType, level in pairs(food.fillLevels or {}) do
if (level or 0) > 0 then
seen[fillType] = true
addEntry(entries, fillType, level, level + math.max(0, (food.capacity or 0) - total))
end
end
end
for _, name in ipairs({"STRAW", "WATER", "MILK", "LIQUIDMANURE", "MANURE"}) do
local fillType = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeIndexByName ~= nil and g_fillTypeManager:getFillTypeIndexByName(name) or nil
if fillType == nil and g_fillTypeManager ~= nil then
-- test-fakes uden getFillTypeIndexByName: slå navnet op baglæns
for index = 1, 64 do
if g_fillTypeManager:getFillTypeNameByIndex(index) == name then fillType = index break end
end
end
if fillType ~= nil and not seen[fillType] and placeable.getHusbandryIsFillTypeSupported ~= nil and placeable:getHusbandryIsFillTypeSupported(fillType) then
local level = placeable.getHusbandryFillLevel ~= nil and (placeable:getHusbandryFillLevel(fillType) or 0) or 0
if level > 0 then
local free = placeable.getHusbandryFreeCapacity ~= nil and (placeable:getHusbandryFreeCapacity(fillType) or 0) or 0
addEntry(entries, fillType, level, level + free)
end
end
end
return entries
end
-- { {name, kind = "factory"|"silo"|"objectStorage"|"husbandry", entries = {{fillType, name, title, level, capacity, isOutput, count}}} }
-- kun gårdens bygninger med indhold; sorteret på slags, dernæst navn.
function ADSources.getFarmInventory(adEnv, farmId)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local result = {}
for _, placeable in pairs(placeables) do
if isOwnedBy(placeable, farmId) then
local kind, entries = nil, nil
if getProductionPoint(placeable) ~= nil then
kind, entries = "factory", factoryEntries(placeable)
elseif placeable.spec_silo ~= nil then
kind, entries = "silo", siloEntries(placeable)
elseif placeable.spec_objectStorage ~= nil then
kind, entries = "objectStorage", objectStorageEntries(placeable)
elseif placeable.getHusbandryFillLevel ~= nil then
kind, entries = "husbandry", husbandryEntries(placeable)
end
if entries ~= nil and #entries > 0 then
table.sort(entries, function(a, b)
if a.isOutput ~= b.isOutput then return a.isOutput end
return tostring(a.title) < tostring(b.title)
end)
table.insert(result, {name = getPlaceableName(placeable, "?"), kind = kind, entries = entries, placeable = placeable})
end
end
end
local order = {factory = 1, husbandry = 2, objectStorage = 3, silo = 4}
table.sort(result, function(a, b)
if order[a.kind] ~= order[b.kind] then return order[a.kind] < order[b.kind] end
return a.name < b.name
end)
return result
end
function ADSources.getSourcePlaceables(sources)
local set = {}
for _, source in ipairs(sources or {}) do