diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 18519f9..717a9dc 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -498,6 +498,27 @@ local function getHusbandryFoodFillTypes(husbandry) return fillTypes 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. local function getFoodPriority(station, fillType) local husbandry = getHusbandry(station) @@ -505,6 +526,10 @@ local function getFoodPriority(station, fillType) if foodFillTypes == nil or foodFillTypes[fillType] == nil then return nil 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 total = 0 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å. local function getHusbandryFoodLevelAndCapacity(husbandry, fillType) 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 for _, level in pairs(foodSpec.fillLevels or {}) do total = total + (level or 0) diff --git a/FS25_ADSmartPickup/adSupplyPlanner.lua b/FS25_ADSmartPickup/adSupplyPlanner.lua index 5bbedfb..b479ab8 100644 --- a/FS25_ADSmartPickup/adSupplyPlanner.lua +++ b/FS25_ADSmartPickup/adSupplyPlanner.lua @@ -323,3 +323,25 @@ function ADSupplyPlanner.findTriggerNodes(root, maxDepth) visit(root, 0) return nodes 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 diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index ea70003..82eceba 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.19.6.0 + 1.19.7.0 <en>AD Smart Pickup</en> diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index 624e71d..1d9251c 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # 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)* @@ -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 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 Moden husker, hvad der allerede er **på vej** til hver stald. Har du fire vogne på samme stald, diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 8cf0284..82e6df1 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # 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)* @@ -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 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 The mod remembers what is already **on the way** to each barn. With four trailers on the same barn diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index 1702bdb..9ed925a 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -806,6 +806,20 @@ check("E6 nødlidende trug: hø direkte", manager:getNextPickup(e5Driver, false) ADOutbound = nil ADRunsController.store = ADRuns.newStore() 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 -- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel diff --git a/tests/test_adSupplyPlanner.lua b/tests/test_adSupplyPlanner.lua index ee3cd08..e9009aa 100644 --- a/tests/test_adSupplyPlanner.lua +++ b/tests/test_adSupplyPlanner.lua @@ -232,5 +232,17 @@ check("AP intet på vej -> samme værdier", ADSupplyPlanner.applyPending(pending local ranked = ADSupplyPlanner.rankNeeds(adjusted, 8000) 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")) os.exit(failures == 0 and 0 or 1)