feat(foder): parallelt foder (høns: korn 80 %/roesnitter 20 %) — gruppeandele af truget, ingen 'bedste foder først'-spærring (v1.19.7.0)

This commit is contained in:
2026-09-24 21:39:13 +02:00
parent a4c3d6fb91
commit e785439605
7 changed files with 96 additions and 3 deletions
+36
View File
@@ -498,6 +498,27 @@ local function getHusbandryFoodFillTypes(husbandry)
return fillTypes return fillTypes
end end
-- Parallelt foder (animalFood consumptionType PARALLEL, fx høns: korn + roesnitter): dyrene spiser fra alle
-- grupper samtidig. Returnerer grupperne {fillTypes, eatWeight} eller nil (serielt = bedste foder først).
local function getParallelFoodGroups(husbandry)
local foodSpec = husbandry.spec_husbandryFood
local foodSystem = g_currentMission ~= nil and g_currentMission.animalFoodSystem or nil
if foodSpec == nil or foodSystem == nil or foodSpec.animalTypeIndex == nil or foodSystem.getAnimalFood == nil then
return nil
end
local animalFood = foodSystem:getAnimalFood(foodSpec.animalTypeIndex)
if animalFood == nil then return nil end
local consumption = animalFood.consumptionType
local parallelConstant = AnimalFoodSystem ~= nil and AnimalFoodSystem.FOOD_CONSUME_TYPE_PARALLEL or nil
local isParallel = (parallelConstant ~= nil and consumption == parallelConstant) or string.upper(tostring(consumption)) == "PARALLEL"
if not isParallel then return nil end
local groups = {}
for _, group in pairs(animalFood.groups or {}) do
table.insert(groups, {fillTypes = group.fillTypes or {}, eatWeight = tonumber(group.eatWeight) or tonumber(group.productionWeight) or 1})
end
return groups
end
-- Trugets fælles trang (fri/kapacitet) og foderets vægt for en vare; nil når varen ikke er foder. -- 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 function getFoodPriority(station, fillType)
local husbandry = getHusbandry(station) local husbandry = getHusbandry(station)
@@ -505,6 +526,10 @@ local function getFoodPriority(station, fillType)
if foodFillTypes == nil or foodFillTypes[fillType] == nil then if foodFillTypes == nil or foodFillTypes[fillType] == nil then
return nil return nil
end end
-- parallelt foder: grupperne supplerer hinanden, så "bedste foder først" gælder ikke
if getParallelFoodGroups(husbandry) ~= nil then
return nil
end
local foodSpec = husbandry.spec_husbandryFood local foodSpec = husbandry.spec_husbandryFood
local total = 0 local total = 0
for _, level in pairs(foodSpec.fillLevels or {}) do for _, level in pairs(foodSpec.fillLevels or {}) do
@@ -542,6 +567,17 @@ end
-- Foder deler ét trug: varens eget niveau + trugets fri plads = den kapacitet varen kan nå. -- Foder deler ét trug: varens eget niveau + trugets fri plads = den kapacitet varen kan nå.
local function getHusbandryFoodLevelAndCapacity(husbandry, fillType) local function getHusbandryFoodLevelAndCapacity(husbandry, fillType)
local foodSpec = husbandry.spec_husbandryFood local foodSpec = husbandry.spec_husbandryFood
local groups = getParallelFoodGroups(husbandry)
if groups ~= nil then
local level, capacity = ADSupplyPlanner.parallelFoodShare(groups, foodSpec.fillLevels, foodSpec.capacity, fillType)
if level ~= nil then
-- andelen kan ikke overstige den plads truget faktisk har fri (fuldt af korn = ingen plads til snitter)
local total = 0
for _, value in pairs(foodSpec.fillLevels or {}) do total = total + (value or 0) end
local actualFree = math.max(0, (foodSpec.capacity or 0) - total)
return level, math.max(level, math.min(capacity, level + actualFree))
end
end
local total = 0 local total = 0
for _, level in pairs(foodSpec.fillLevels or {}) do for _, level in pairs(foodSpec.fillLevels or {}) do
total = total + (level or 0) total = total + (level or 0)
+22
View File
@@ -323,3 +323,25 @@ function ADSupplyPlanner.findTriggerNodes(root, maxDepth)
visit(root, 0) visit(root, 0)
return nodes return nodes
end end
-- Parallelt foder (animalFood consumptionType PARALLEL, fx høns): dyrene spiser fra alle grupper samtidig,
-- så hver gruppe skal have sin andel af truget efter eatWeight. Returnerer gruppens niveau og kapacitet
-- (trugets kapacitet × gruppens andel) for gruppen der indeholder fillType; nil hvis varen ikke er foder.
function ADSupplyPlanner.parallelFoodShare(groups, fillLevels, capacity, fillType)
local totalWeight = 0
for _, group in ipairs(groups or {}) do totalWeight = totalWeight + (tonumber(group.eatWeight) or 0) end
if totalWeight <= 0 then return nil end
for _, group in ipairs(groups or {}) do
local isMember = false
for _, member in pairs(group.fillTypes or {}) do
if member == fillType then isMember = true end
end
if isMember then
local level = 0
for _, member in pairs(group.fillTypes or {}) do level = level + ((fillLevels or {})[member] or 0) end
return level, (capacity or 0) * (tonumber(group.eatWeight) or 0) / totalWeight
end
end
return nil
end
+1 -1
View File
@@ -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.19.6.0</version> <version>1.19.7.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+5 -1
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — brugermanual # AD Smart Pickup — brugermanual
Version 1.19.6 · Farming Simulator 25 · tillæg til AutoDrive Version 1.19.7 · Farming Simulator 25 · tillæg til AutoDrive
*English version: [USER_MANUAL.md](USER_MANUAL.md)* *English version: [USER_MANUAL.md](USER_MANUAL.md)*
@@ -238,6 +238,10 @@ Foder i et stald-trug deler plads. Står bedre foder som **TMR** på listen, hen
ensilage og hø kun, når truget er **mindst 75 % tomt**. Så fylder silage ikke truget op, mens der er ensilage og hø kun, når truget er **mindst 75 % tomt**. Så fylder silage ikke truget op, mens der er
TMR at få. TMR at få.
Dyr, der spiser fra flere fodergrupper **samtidig**, fx høns med korn og roesnitter, følger ikke den regel.
Hver gruppe får sin andel af krybben efter hvor meget dyrene spiser af den. For høns er det korn 80 % og
roesnitter 20 %. Begge grupper skal være med under **Vælg**, for at hønsene når 100 %.
### Flere traktorer til samme stald ### Flere traktorer til samme stald
Moden husker, hvad der allerede er **på vej** til hver stald. Har du fire vogne på samme stald, Moden husker, hvad der allerede er **på vej** til hver stald. Har du fire vogne på samme stald,
+6 -1
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — user manual # AD Smart Pickup — user manual
Version 1.19.6 · Farming Simulator 25 · add-on for AutoDrive Version 1.19.7 · Farming Simulator 25 · add-on for AutoDrive
*Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)*
@@ -240,6 +240,11 @@ Feed in a barn trough shares space. If better feed such as **TMR** is on the lis
as silage and hay is only fetched when the trough is **at least 75 % empty**. That way silage does as silage and hay is only fetched when the trough is **at least 75 % empty**. That way silage does
not fill the trough while TMR is available. not fill the trough while TMR is available.
Animals that eat from several food groups **at the same time**, such as chickens with grain and root
crops, do not follow that rule. Each group gets its share of the trough by how much the animals eat of
it. For chickens that is grain 80 % and root crops 20 %. Both groups must be included under **Select**
for the chickens to reach 100 %.
### Several tractors on the same barn ### Several tractors on the same barn
The mod remembers what is already **on the way** to each barn. With four trailers on the same barn The mod remembers what is already **on the way** to each barn. With four trailers on the same barn
+14
View File
@@ -806,6 +806,20 @@ check("E6 nødlidende trug: hø direkte", manager:getNextPickup(e5Driver, false)
ADOutbound = nil ADOutbound = nil
ADRunsController.store = ADRuns.newStore() ADRunsController.store = ADRuns.newStore()
testVehicles = {} testVehicles = {}
-- E7 (parallelt foder, høns): korn og roesnitter spises SAMTIDIG (80 %/20 %) — truget har sin kornandel,
-- mangler roesnitter -> de hentes, "bedste foder først" spærrer ikke (in-game 24/9: kun 70 % produktion)
g_currentMission.animalFoodSystem = {
getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {consumptionType = "PARALLEL", groups = {
{fillTypes = {TMR}, productionWeight = 0.7, eatWeight = 0.8}, {fillTypes = {HAY}, productionWeight = 0.3, eatWeight = 0.2}}} end end,
getMixturesByAnimalTypeIndex = function() return {} end,
}
e5Straw.level = 100000
e5Food[TMR], e5Food[HAY], e5Food[GRASS] = 24000, 0, 0 -- kornandel (80 % af 30000) fuld, 20 % fri
stations[1].getFillLevel = function(_, fillType) return fillType == TMR and 30000 or 0 end -- korn findes: serielt ville det vælges
check("E7 parallelt: roesnitter hentes selv om truget kun er 20 % tomt", manager:getNextPickup(e5Driver, false), 23)
e5Food[TMR] = 30000 -- truget helt fuldt af korn: ingen plads til snitter -> intet behov for dem
local hayLevel, hayCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], HAY, 1)
check("E7 fuldt trug: snitternes plads = 0", hayCapacity - hayLevel, 0)
g_currentMission.animalFoodSystem = nil g_currentMission.animalFoodSystem = nil
-- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel -- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel
+12
View File
@@ -232,5 +232,17 @@ check("AP intet på vej -> samme værdier", ADSupplyPlanner.applyPending(pending
local ranked = ADSupplyPlanner.rankNeeds(adjusted, 8000) local ranked = ADSupplyPlanner.rankNeeds(adjusted, 8000)
check("AP halm fuld (på vej) -> kun trug-varer tilbage", #ranked, 2) check("AP halm fuld (på vej) -> kun trug-varer tilbage", #ranked, 2)
-- PF: parallelt foder (høns: korn 80 % / roesnitter 20 % af det de spiser) — hver gruppe har sin andel af truget
do
local WHEAT, BARLEY, BEETCUT = 1, 2, 3
local groups = {{fillTypes = {WHEAT, BARLEY}, eatWeight = 0.8}, {fillTypes = {BEETCUT}, eatWeight = 0.2}}
local levels = {[WHEAT] = 60000, [BARLEY] = 20000, [BEETCUT] = 0}
local lvl, cap = ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, BEETCUT)
check("PF roesnitter: andel 20k, har 0", lvl .. "/" .. cap, "0/20000")
lvl, cap = ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, BARLEY)
check("PF korn-gruppen samlet: 80k af 80k", lvl .. "/" .. cap, "80000/80000")
check("PF ukendt vare -> nil", ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, 99), nil)
end
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)