diff --git a/FS25_ADSmartPickup/adFieldSiloPlanner.lua b/FS25_ADSmartPickup/adFieldSiloPlanner.lua index 8825692..4000bec 100644 --- a/FS25_ADSmartPickup/adFieldSiloPlanner.lua +++ b/FS25_ADSmartPickup/adFieldSiloPlanner.lua @@ -39,6 +39,9 @@ local function chooseStore(stores, silo, fillType, liters, minLiters, reserved, if mostFree == nil or free > mostFree then most, mostFree = store, free end end end + -- fabrikker kun med plads til hele læsset: en fabrik tager kun det den forbruger, så en vogn med resten + -- holder ved den i timevis (in-game 28/9: 252.000 l kartofler ved en fuld snackfabrik) + if production then return best end return best or most end @@ -69,6 +72,27 @@ local function pickStore(input, farmIncoming, task, liters, minLiters, reserved) or chooseStore(input.stores, task.silo, fillType, liters, minLiters, reserved, not wantProduction) end +-- Høsten først: for hvert behov {fillType, count} (høstere i arbejde, der mangler tømmevogne) holdes så mange +-- vogne der kan bære afgrøden tilbage; resten må marksilo-flowet låne. Ny liste; input muteres ikke. +function ADFieldSiloPlanner.withoutHarvestReserve(vehicles, needs) + local held = {} + for _, need in ipairs(needs or {}) do + local left = need.count or 0 + for _, vehicle in ipairs(vehicles or {}) do + if left <= 0 then break end + if not held[vehicle.id] and canCarry(vehicle, need.fillType) then + held[vehicle.id] = true + left = left - 1 + end + end + end + local free = {} + for _, vehicle in ipairs(vehicles or {}) do + if not held[vehicle.id] then table.insert(free, vehicle) end + end + return free +end + function ADFieldSiloPlanner.plan(input) local minLiters = input.minLiters or 10000 local storesById = {} diff --git a/FS25_ADSmartPickup/adFieldSilos.lua b/FS25_ADSmartPickup/adFieldSilos.lua index 4659ba4..bf604b0 100644 --- a/FS25_ADSmartPickup/adFieldSilos.lua +++ b/FS25_ADSmartPickup/adFieldSilos.lua @@ -251,7 +251,8 @@ function ADFieldSilos.collectVehicles(adEnv) local vehicles, byId = {}, {} -- hvorfor tømmevogne ikke kan lånes (til fanens statuslinje) local why = {notEnabled = 0, busy = 0, controlled = 0, noPark = 0} - for _, rig in ipairs(ADFieldWork.getRigs(adEnv)) do + local rigs = ADFieldWork.getRigs(adEnv) + for _, rig in ipairs(rigs) do -- uden AD-parkeringspunkt bliver vognen holdende på lagerets markør og spærrer for de næste local canPark = ADFieldWork.parkMarkerId == nil or ADFieldWork.parkMarkerId(adEnv, rig.vehicle) ~= nil if rig.role == "unloader" then @@ -266,11 +267,54 @@ function ADFieldSilos.collectVehicles(adEnv) byId[rig.id] = rig.vehicle end end + local ok, needs = pcall(ADFieldSilos.harvestNeeds, adEnv, rigs) + if ok and #needs > 0 then + local before = #vehicles + vehicles = ADFieldSiloPlanner.withoutHarvestReserve(vehicles, needs) + why.harvest = before - #vehicles + elseif not ok then + Logging.warning(ADFieldSilos.LOG_PREFIX .. "høstreserve kunne ikke beregnes: %s", tostring(needs)) + end why.eligible = #vehicles ADFieldSilos.lastVehicleReasons = why return vehicles, byId end +-- Høsten først: pr. høster der arbejder (CP kører) holdes dens planlagte antal tømmevogne tilbage, minus dem +-- markarbejdet allerede har sendt til marken. (in-game 28/9: marksilo-flowet havde lånt begge kartoffelvogne, +-- og ingen kom til roeoptagerne.) +function ADFieldSilos.harvestNeeds(adEnv, rigs) + local assignments = ADFieldJobs ~= nil and ADFieldJobs.state ~= nil and ADFieldJobs.state.assignments or {} + local sent = {} + for _, assignment in pairs(assignments) do + if assignment.role == "unloader" and assignment.fieldId ~= nil then sent[assignment.fieldId] = (sent[assignment.fieldId] or 0) + 1 end + end + local needs = {} + for _, rig in ipairs(rigs) do + if rig.role == "harvester" and ADCpBridge.isWorking(rig.vehicle) then + local assignment = assignments[rig.id] + local fieldId = assignment ~= nil and assignment.fieldId or nil + local field = fieldId ~= nil and ADFieldJobs.getField(fieldId) or nil + local info = fieldId ~= nil and ADFields.getInfo(fieldId) or nil + local count = field ~= nil and ADFieldModel.maxHelpers("harvesting", field.plan) or 1 + count = count - (fieldId ~= nil and (sent[fieldId] or 0) or 0) + local fillTypes = {} + if info ~= nil and info.harvestFillType ~= nil then + fillTypes[info.harvestFillType] = true + else + for fruitIndex in pairs(rig.fruits or {}) do + local fruit = g_fruitTypeManager ~= nil and g_fruitTypeManager:getFruitTypeByIndex(fruitIndex) or nil + if fruit ~= nil and fruit.fillType ~= nil then fillTypes[fruit.fillType.index] = true end + end + end + for fillType in pairs(fillTypes) do + if count > 0 then table.insert(needs, {fillType = fillType, count = count}) end + end + end + end + return needs +end + -- ---- lån ---- local function fillTypeName(fillType) diff --git a/FS25_ADSmartPickup/gui/SmartPickupFieldSiloTab.lua b/FS25_ADSmartPickup/gui/SmartPickupFieldSiloTab.lua index ca2f990..df14d25 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFieldSiloTab.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFieldSiloTab.lua @@ -61,6 +61,10 @@ function SmartPickupFrame:fillFieldSiloRows(adEnv) self:addTextRow(string.format(i18n:getText("spu_fs_noVehicle"), why.noPark, why.busy, why.controlled, why.notEnabled)) end + if why ~= nil and (why.harvest or 0) > 0 then + self:addTextRow(string.format(i18n:getText("spu_fs_harvestHeld"), why.harvest)) + end + self:addTextRow(i18n:getText("spu_fs_silosTitle")) if plan == nil or #plan.status == 0 then self:addTextRow(" " .. i18n:getText("spu_fs_nothing")) diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index 09a66a8..0a2d595 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -501,5 +501,6 @@ + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 30ccc00..2c5cc30 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -501,5 +501,6 @@ + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index c55cec6..60ce81e 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.45.0.0 + 1.45.1.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adFieldSiloPlanner.lua b/tests/test_adFieldSiloPlanner.lua index 8a5effa..3e50742 100644 --- a/tests/test_adFieldSiloPlanner.lua +++ b/tests/test_adFieldSiloPlanner.lua @@ -111,5 +111,23 @@ check("P7 reserve mangler men ingen gård tager varen -> fabrik", r.dispatch[1]. check("P7 status ok (ikke noFarmStore)", r.status[1].state, "ok") check("P7 status viser destination", r.status[1].toProduction, true) +-- fabrik kun når hele læsset kan være der (in-game 28/9: vogn med 252.000 l ventede timevis ved en fuld fabrik) +r = P.plan(base({stores = {store("farm", 5000, {[WHEAT] = 1000000}), factory("mill", 100, {[WHEAT] = 30000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, + vehicles = {vehicle("v1", 400, 50000)}})) +check("P8 fabrik uden plads til hele læsset -> gårdlager", r.dispatch[1].storeId, "farm") +r = P.plan(base({stores = {store("farm", 5000, {[CANOLA] = 1000000}), factory("mill", 100, {[WHEAT] = 30000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, + vehicles = {vehicle("v1", 400, 50000)}})) +check("P9 kun fabrik uden plads til hele læsset -> ingen afsendelse", #r.dispatch, 0) + +-- høsten først: tømmevogne holdes tilbage til høstere der arbejder +local vs = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000, {[CANOLA] = true}), vehicle("v3", 400, 50000)} +local left = P.withoutHarvestReserve(vs, {{fillType = WHEAT, count = 1}}) +check("R1 én hvede-vogn holdt tilbage", #left, 2) +check("R1 raps-vognen er fri", left[1].id == "v2" or left[2].id == "v2", true) +left = P.withoutHarvestReserve(vs, {{fillType = WHEAT, count = 5}}) +check("R2 alle hvede-vogne holdt tilbage", #left, 1) +check("R3 intet behov -> alle fri", #P.withoutHarvestReserve(vs, {}), 3) +check("R4 input muteres ikke", #vs, 3) + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)