feat: foderprioritering — trug som én trang, spillets fodervægt (TMR 1,0 > hø/ensilage 0,8 > græs 0,4) afgør rækkefølgen i truget; fanen viser samme rangering (v1.10.3.0)
This commit is contained in:
@@ -450,6 +450,14 @@ function ADRunsController.getTargetLevels(adEnv, run)
|
|||||||
table.insert(fillTypes, fillType.index)
|
table.insert(fillTypes, fillType.index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- samme rangering som valget bruger (trug som én trang, fodervægt som prioritet); fulde sidst
|
||||||
|
if ADSmartPickup.getTargetNeedsRanked ~= nil then
|
||||||
|
local levels = {}
|
||||||
|
for _, need in ipairs(ADSmartPickup.getTargetNeedsRanked(target, fillTypes, farmId)) do
|
||||||
|
table.insert(levels, {fillType = need.fillType, level = need.level, capacity = need.capacity, free = need.freeSpace})
|
||||||
|
end
|
||||||
|
return levels
|
||||||
|
end
|
||||||
local levels = {}
|
local levels = {}
|
||||||
for _, fillType in ipairs(fillTypes) do
|
for _, fillType in ipairs(fillTypes) do
|
||||||
local level, capacity = ADSmartPickup.getStationLevelAndCapacity(target, fillType, farmId)
|
local level, capacity = ADSmartPickup.getStationLevelAndCapacity(target, fillType, farmId)
|
||||||
|
|||||||
@@ -377,23 +377,43 @@ local function getHusbandryFoodFillTypes(husbandry)
|
|||||||
if foodSpec == nil or foodSystem == nil or foodSpec.animalTypeIndex == nil then
|
if foodSpec == nil or foodSystem == nil or foodSpec.animalTypeIndex == nil then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
-- værdien er foderets vægt (spillets productionWeight/eatWeight: TMR 1,0, hø/ensilage
|
||||||
|
-- 0,8, græs 0,4) — bruges som prioritet inden for truget; ukendt = 1
|
||||||
local fillTypes = {}
|
local fillTypes = {}
|
||||||
local animalFood = foodSystem.getAnimalFood ~= nil and foodSystem:getAnimalFood(foodSpec.animalTypeIndex) or nil
|
local animalFood = foodSystem.getAnimalFood ~= nil and foodSystem:getAnimalFood(foodSpec.animalTypeIndex) or nil
|
||||||
for _, group in pairs(animalFood ~= nil and animalFood.groups or {}) do
|
for _, group in pairs(animalFood ~= nil and animalFood.groups or {}) do
|
||||||
|
local weight = tonumber(group.productionWeight) or tonumber(group.eatWeight) or 1
|
||||||
for _, fillType in pairs(group.fillTypes or {}) do
|
for _, fillType in pairs(group.fillTypes or {}) do
|
||||||
fillTypes[fillType] = true
|
fillTypes[fillType] = math.max(fillTypes[fillType] or 0, weight)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local mixtures = foodSystem.getMixturesByAnimalTypeIndex ~= nil and foodSystem:getMixturesByAnimalTypeIndex(foodSpec.animalTypeIndex) or nil
|
local mixtures = foodSystem.getMixturesByAnimalTypeIndex ~= nil and foodSystem:getMixturesByAnimalTypeIndex(foodSpec.animalTypeIndex) or nil
|
||||||
for _, mixture in pairs(mixtures or {}) do
|
for _, mixture in pairs(mixtures or {}) do
|
||||||
local fillType = type(mixture) == "table" and (mixture.fillType or mixture.fillTypeIndex) or mixture
|
local fillType = type(mixture) == "table" and (mixture.fillType or mixture.fillTypeIndex) or mixture
|
||||||
if type(fillType) == "number" then
|
if type(fillType) == "number" then
|
||||||
fillTypes[fillType] = true
|
fillTypes[fillType] = math.max(fillTypes[fillType] or 0, 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return fillTypes
|
return fillTypes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Trugets fælles trang (fri/kapacitet) og foderets vægt for en vare; nil når varen ikke er foder.
|
||||||
|
local function getFoodPriority(station, fillType)
|
||||||
|
local husbandry = getHusbandry(station)
|
||||||
|
local foodFillTypes = husbandry ~= nil and getHusbandryFoodFillTypes(husbandry) or nil
|
||||||
|
if foodFillTypes == nil or foodFillTypes[fillType] == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local foodSpec = husbandry.spec_husbandryFood
|
||||||
|
local total = 0
|
||||||
|
for _, level in pairs(foodSpec.fillLevels or {}) do
|
||||||
|
total = total + (level or 0)
|
||||||
|
end
|
||||||
|
local capacity = foodSpec.capacity or 0
|
||||||
|
local troughScore = capacity > 0 and math.max(0, capacity - total) / capacity or 0
|
||||||
|
return troughScore, foodFillTypes[fillType]
|
||||||
|
end
|
||||||
|
|
||||||
-- En stald kan MODTAGE foder, halm og vand — ikke sine egne outputs (mælk, gødning, gylle),
|
-- En stald kan MODTAGE foder, halm og vand — ikke sine egne outputs (mælk, gødning, gylle),
|
||||||
-- selv om placeable'n "understøtter" dem som lager. Uden foder-spec (ældre/mod-stalde)
|
-- selv om placeable'n "understøtter" dem som lager. Uden foder-spec (ældre/mod-stalde)
|
||||||
-- spørges staldens egen understøttelse.
|
-- spørges staldens egen understøttelse.
|
||||||
@@ -463,12 +483,27 @@ local function getTargetNeeds(station, ingredients, farmId, demand)
|
|||||||
for _, fillType in ipairs(ingredients) do
|
for _, fillType in ipairs(ingredients) do
|
||||||
local level, capacity = getTargetLevelAndCapacity(station, fillType, farmId)
|
local level, capacity = getTargetLevelAndCapacity(station, fillType, farmId)
|
||||||
if level ~= nil then
|
if level ~= nil then
|
||||||
table.insert(needs, {fillType = fillType, level = level, capacity = capacity, litersPerMinute = demand[fillType]})
|
local troughScore, priority = getFoodPriority(station, fillType)
|
||||||
|
table.insert(needs, {fillType = fillType, level = level, capacity = capacity, litersPerMinute = demand[fillType], scoreOverride = troughScore, priority = priority})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return needs
|
return needs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Til fanen: samme rangering som valget bruger (uden vogn-størrelse). Fulde varer sidst.
|
||||||
|
function ADSmartPickup.getTargetNeedsRanked(station, fillTypes, farmId)
|
||||||
|
local needs = getTargetNeeds(station, fillTypes, farmId, {})
|
||||||
|
local ranked = ADSupplyPlanner.rankNeeds(needs)
|
||||||
|
local seen = {}
|
||||||
|
for _, need in ipairs(ranked) do seen[need.fillType] = true end
|
||||||
|
for _, need in ipairs(needs) do
|
||||||
|
if not seen[need.fillType] then
|
||||||
|
table.insert(ranked, {fillType = need.fillType, level = need.level, capacity = need.capacity, freeSpace = need.capacity - need.level, fits = false, score = 0, priority = need.priority or 0})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ranked
|
||||||
|
end
|
||||||
|
|
||||||
-- Vognens samlede kapacitet (liter); nil hvis AutoDrive ikke kan opgøre den.
|
-- Vognens samlede kapacitet (liter); nil hvis AutoDrive ikke kan opgøre den.
|
||||||
local function getLoadCapacity(adEnv, driver)
|
local function getLoadCapacity(adEnv, driver)
|
||||||
if adEnv.AutoDrive.getAllUnits == nil or adEnv.AutoDrive.getAllFillLevels == nil then
|
if adEnv.AutoDrive.getAllUnits == nil or adEnv.AutoDrive.getAllFillLevels == nil then
|
||||||
|
|||||||
@@ -168,10 +168,13 @@ function ADSupplyPlanner.canUnloadRest(freeSpace, rest, capacity, freeFraction)
|
|||||||
return freeSpace >= needed
|
return freeSpace >= needed
|
||||||
end
|
end
|
||||||
|
|
||||||
-- needs: {{fillType, level, capacity[, litersPerMinute]}, ...}. Returnerer NY liste,
|
-- needs: {{fillType, level, capacity[, litersPerMinute][, scoreOverride][, priority]}, ...}.
|
||||||
-- mest trængende først; ingredienser uden plads i målet er sorteret fra.
|
-- Returnerer NY liste, mest trængende først; ingredienser uden plads i målet er sorteret fra.
|
||||||
-- loadCapacity (valgfri): vognens kapacitet — varer hvor et FULDT læs kan komme af
|
-- loadCapacity (valgfri): vognens kapacitet — varer hvor et FULDT læs kan komme af
|
||||||
-- går forud (så vognen ikke ender med rest-læs); passer intet, gælder ren trang.
|
-- går forud (så vognen ikke ender med rest-læs); passer intet, gælder ren trang.
|
||||||
|
-- scoreOverride: varer der deler ét lager (staldens fodertrug) får trugets fælles score,
|
||||||
|
-- så truget konkurrerer som ÉN trang; priority (fx spillets fodervægt: TMR 1,0 > hø 0,8 >
|
||||||
|
-- græs 0,4) afgør så rækkefølgen inden for lageret.
|
||||||
function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
||||||
local ranked = {}
|
local ranked = {}
|
||||||
for _, need in ipairs(needs) do
|
for _, need in ipairs(needs) do
|
||||||
@@ -186,7 +189,8 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
|||||||
litersPerMinute = need.litersPerMinute,
|
litersPerMinute = need.litersPerMinute,
|
||||||
minutesToEmpty = minutesToEmpty,
|
minutesToEmpty = minutesToEmpty,
|
||||||
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
|
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
|
||||||
score = ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
score = need.scoreOverride or ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
||||||
|
priority = need.priority or 0,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -197,6 +201,9 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
|||||||
if a.score ~= b.score then
|
if a.score ~= b.score then
|
||||||
return a.score > b.score
|
return a.score > b.score
|
||||||
end
|
end
|
||||||
|
if a.priority ~= b.priority then
|
||||||
|
return a.priority > b.priority
|
||||||
|
end
|
||||||
return a.fillType < b.fillType
|
return a.fillType < b.fillType
|
||||||
end)
|
end)
|
||||||
return ranked
|
return ranked
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.10.2.0</version>
|
<version>1.10.3.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
|
|||||||
@@ -731,6 +731,42 @@ UnloadTask.update(ts, 16)
|
|||||||
check("W12 omdirigeret trods isActiveAtTrigger", ts.destinationID, 40)
|
check("W12 omdirigeret trods isActiveAtTrigger", ts.destinationID, 40)
|
||||||
check("W12 AD's aflæsning stoppet før kørsel", stopped, true)
|
check("W12 AD's aflæsning stoppet før kørsel", stopped, true)
|
||||||
|
|
||||||
|
-- E5: fodertrug = én trang; inden for truget vælges efter spillets fodervægt (TMR 1,0 > hø 0,8 > græs 0,4);
|
||||||
|
-- et tommere halmlager vinder over truget
|
||||||
|
buildCowWorld()
|
||||||
|
addMarker(23, "Høsilo", "Lader", 700, 0); table.insert(stations, makeStation(123, 700, 15, {[HAY] = 80000}, false, {[HAY] = 80000}))
|
||||||
|
addMarker(24, "Græssilo", "Lader", 800, 0); table.insert(stations, makeStation(124, 800, 15, {[GRASS] = 80000}, false, {[GRASS] = 80000}))
|
||||||
|
local e5Food = {[TMR] = 0, [HAY] = 0, [GRASS] = 0}
|
||||||
|
local e5Straw = {level = 20000}
|
||||||
|
local e5Barn = {
|
||||||
|
getName = function() return "Prioritets-stald" end,
|
||||||
|
spec_husbandryFood = {fillLevels = e5Food, capacity = 30000, animalTypeIndex = 7},
|
||||||
|
spec_husbandryStraw = {},
|
||||||
|
getHusbandryIsFillTypeSupported = function(_, fillType) return fillType == STRAW end,
|
||||||
|
getHusbandryFillLevel = function(_, fillType) if fillType == STRAW then return e5Straw.level end return 0 end,
|
||||||
|
getHusbandryCapacity = function(_, fillType) if fillType == STRAW then return 100000 end return 0 end,
|
||||||
|
}
|
||||||
|
g_currentMission.animalFoodSystem = {
|
||||||
|
getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {groups = {
|
||||||
|
{fillTypes = {TMR}, productionWeight = 1.0}, {fillTypes = {HAY}, productionWeight = 0.8}, {fillTypes = {GRASS}, productionWeight = 0.4}}} end end,
|
||||||
|
getMixturesByAnimalTypeIndex = function() return {} end,
|
||||||
|
}
|
||||||
|
unloadStations[1].targetStorages = nil
|
||||||
|
unloadStations[1].owningPlaceable = e5Barn
|
||||||
|
local e5Driver = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, HAY, GRASS, STRAW})
|
||||||
|
e5Driver.carriable = {TMR, HAY, GRASS, STRAW}
|
||||||
|
e5Driver.trailers = {level = 0, capacity = 8000}
|
||||||
|
-- trug tomt (100 % trang) vs. halm 80 % tomt -> truget først, og TMR før hø/græs
|
||||||
|
check("E5 tomt trug: TMR først", manager:getNextPickup(e5Driver, false), 20)
|
||||||
|
-- ingen TMR-kilde -> hø (0,8) før græs (0,4)
|
||||||
|
stations[1].getFillLevel = function() return 0 end
|
||||||
|
stations[1].owningPlaceable = nil
|
||||||
|
check("E5 uden TMR-kilde: hø før græs", manager:getNextPickup(e5Driver, false), 23)
|
||||||
|
-- trug 70 % fuldt (30 % trang) vs. halm 80 % tomt -> halm
|
||||||
|
e5Food[HAY] = 21000
|
||||||
|
check("E5 halmlager tommere end truget -> halm", manager:getNextPickup(e5Driver, false), 21)
|
||||||
|
g_currentMission.animalFoodSystem = nil
|
||||||
|
|
||||||
-- ==== I: run-integration — ingredienser fra run i stedet for multi-valg; Wait-pulje pr. run ====
|
-- ==== I: run-integration — ingredienser fra run i stedet for multi-valg; Wait-pulje pr. run ====
|
||||||
-- I1: traktor i et run med ÉN valgt vare i AD får alligevel behovsstyring (run'ets liste)
|
-- I1: traktor i et run med ÉN valgt vare i AD får alligevel behovsstyring (run'ets liste)
|
||||||
local runDriver = buildCowWorld()
|
local runDriver = buildCowWorld()
|
||||||
|
|||||||
@@ -178,5 +178,20 @@ check("tærskel 30 %: 70 % fri er nok", ADSupplyPlanner.canUnloadRest(70000, 900
|
|||||||
check("hele resten passer vinder altid", ADSupplyPlanner.canUnloadRest(5000, 5000, 100000, 0.7), true)
|
check("hele resten passer vinder altid", ADSupplyPlanner.canUnloadRest(5000, 5000, 100000, 0.7), true)
|
||||||
check("uden tærskel = 50 %-reglen", ADSupplyPlanner.canUnloadRest(50000, 90000, 100000), true)
|
check("uden tærskel = 50 %-reglen", ADSupplyPlanner.canUnloadRest(50000, 90000, 100000), true)
|
||||||
|
|
||||||
|
-- rankNeeds: fælles trug (scoreOverride) + fodervægt (priority): TMR før hø før græs,
|
||||||
|
-- men et separat halmlager der er tommere vinder over truget
|
||||||
|
local TMR, HAYX = 40, 41
|
||||||
|
local trough = ADSupplyPlanner.rankNeeds({
|
||||||
|
{fillType = GRASS, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 0.4},
|
||||||
|
{fillType = TMR, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 1.0},
|
||||||
|
{fillType = HAYX, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 0.8},
|
||||||
|
{fillType = HAY, level = 20000, capacity = 100000}, -- halm-agtigt separat lager, 80 % tomt
|
||||||
|
})
|
||||||
|
check("separat lager 80 % tomt vinder over trug 30 % tomt", trough[1].fillType, HAY)
|
||||||
|
check("i truget: TMR (1.0) først", trough[2].fillType, TMR)
|
||||||
|
check("så hø (0.8)", trough[3].fillType, HAYX)
|
||||||
|
check("så græs (0.4)", trough[4].fillType, GRASS)
|
||||||
|
check("scoreOverride bruges som score", trough[2].score, 0.3)
|
||||||
|
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user