feat(udkørsel): 'Sælg altid når kilden er over X %' (sælger uanset pris, bedste salgssted der kan nås); fix: flydende varer lagres ikke automatisk i en fabriks rist (gyllevogn holdt ved AIO) (v1.33.0.0)

This commit is contained in:
2026-09-26 21:37:20 +02:00
parent 10838779bc
commit 954dd25c51
11 changed files with 74 additions and 6 deletions
+14 -3
View File
@@ -65,7 +65,7 @@ end
local RUN_FIELDS = {
"id", "name", "kind", "sourceBuildingIds", "targetBuildingIds", "storeBuildingIds", "products",
"ingredientMode", "ingredients", "sellAtPercent", "storeAbovePercent", "collectRound", "loops",
"ingredientMode", "ingredients", "sellAtPercent", "storeAbovePercent", "sellAbovePercent", "collectRound", "loops",
"returnBelowPercent", "waitPoolGroup", "vehicleIds",
}
@@ -79,6 +79,7 @@ local function draftFrom(run, isNew)
draft.ingredientMode = draft.ingredientMode == "manual" and "manual" or "auto"
draft.sellAtPercent = draft.sellAtPercent or ADRuns.DEFAULT_SELL_AT_PERCENT
draft.storeAbovePercent = draft.storeAbovePercent or ADRuns.DEFAULT_STORE_ABOVE_PERCENT
draft.sellAbovePercent = draft.sellAbovePercent or ADRuns.DEFAULT_SELL_ABOVE_PERCENT
draft.returnBelowPercent = draft.returnBelowPercent or ADRuns.DEFAULT_RETURN_BELOW_PERCENT
draft.loops = draft.loops or 0
draft.waitPoolGroup = draft.waitPoolGroup or ""
@@ -126,7 +127,7 @@ end
-- ---- valg ----
local SET_VALUES = {
kind = true, ingredientMode = true, sellAtPercent = true, storeAbovePercent = true, loops = true,
kind = true, ingredientMode = true, sellAtPercent = true, storeAbovePercent = true, sellAbovePercent = true, loops = true,
returnBelowPercent = true, waitPoolGroup = true,
}
local BOOL_VALUES = {showAllBuildings = true, storeAuto = true, collectRound = true}
@@ -185,6 +186,13 @@ local function percentOptions(from, to, step)
return options
end
-- "Sælg altid over": Fra, 50-100 %
local function sellAboveOptions()
local options = {{label = L("spu_sellAbove_off"), value = 0}}
for _, option in ipairs(percentOptions(50, ADRuns.MAX_SELL_ABOVE_PERCENT, 5)) do table.insert(options, option) end
return options
end
local function chosenSet(list)
local set = {}
for _, value in ipairs(list or {}) do set[value] = true end
@@ -272,6 +280,7 @@ local function rowsRules(guide)
return {
choice("sellAtPercent", L("spu_sellAt"), percentOptions(ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT, 5), draft.sellAtPercent),
choice("storeAbovePercent", L("spu_storeAbove"), percentOptions(ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT, 5), draft.storeAbovePercent),
choice("sellAbovePercent", L("spu_sellAbove"), sellAboveOptions(), draft.sellAbovePercent),
toggle("collectRound", L("spu_collectRound"), draft.collectRound),
}
end
@@ -388,6 +397,7 @@ local SUMMARY = {
local draft = guide.draft
if isOut(guide) then
local parts = {L("spu_guide_flow_sumSellAt", {draft.sellAtPercent}), L("spu_guide_flow_sumStoreAbove", {draft.storeAbovePercent})}
if (draft.sellAbovePercent or 0) > 0 then table.insert(parts, L("spu_guide_flow_sumSellAbove", {draft.sellAbovePercent})) end
if draft.collectRound then table.insert(parts, L("spu_guide_flow_sumRound")) end
return parts
end
@@ -429,7 +439,8 @@ function ADFlowGuide.result(guide)
targetBuildingIds = ADGuide.deepCopy(draft.targetBuildingIds), storeBuildingIds = storeIds,
products = ADGuide.deepCopy(draft.products), ingredientMode = draft.ingredientMode,
ingredients = ADGuide.deepCopy(draft.ingredients), sellAtPercent = draft.sellAtPercent,
storeAbovePercent = draft.storeAbovePercent, collectRound = draft.collectRound, loops = draft.loops,
storeAbovePercent = draft.storeAbovePercent, sellAbovePercent = draft.sellAbovePercent or 0,
collectRound = draft.collectRound, loops = draft.loops,
returnBelowPercent = draft.returnBelowPercent, waitPoolGroup = draft.waitPoolGroup or "",
}
if draft.name ~= nil and draft.name:match("%S") then changes.name = draft.name end
+13
View File
@@ -97,6 +97,19 @@ function ADLoadSwap.rankDumpCandidates(candidates, avoidMarkers)
return ranked
end
-- Flydende varer lægges kun i tanke/siloer, aldrig automatisk i en fabriks aflæsning: fabrikkerne har ofte en
-- rist med præcis aflæsning, som en gyllevogns udløb bag på ikke rammer (in-game 26/9: 1 mio. l gylle holdt
-- ved AIO'ens rist og spærrede vejen). Vælger brugeren selv fabrikken som lager, gælder valget.
ADLoadSwap.LIQUIDS = {LIQUIDMANURE = true, DIGESTATE = true, MILK = true, BUFFALOMILK = true, GOATMILK = true,
SHEEPMILK = true, WATER = true, LIQUIDFERTILIZER = true, HERBICIDE = true, METHANE = true, DIESEL = true}
function ADLoadSwap.isLiquidName(name)
return name ~= nil and ADLoadSwap.LIQUIDS[string.upper(tostring(name))] == true
end
function ADLoadSwap.isLiquid(fillType)
local name = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeNameByIndex ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or nil
return ADLoadSwap.isLiquidName(name)
end
function ADLoadSwap.findDumpMarker(adEnv, vehicle, fillType, rest, homeWayPointId, isReachable, excludedPlaceables, avoidMarkers, allowFactories)
if vehicle.rootNode == nil then
return nil, "køretøjet har ingen position"
+10 -1
View File
@@ -186,6 +186,12 @@ local function findSellQuote(adEnv, vehicle, product, sellMarkers, sellAtPercent
if unreachableName ~= nil then
return nil, unreachableName
end
-- ingen pris over grænsen: det bedste salgssted der kan nås (bruges af "sælg altid over X %")
for _, quote in ipairs(quotes) do
if homeId == nil or ADWaitPool.hasRouteBothWays(adEnv, vehicle, quote.wayPointId, homeId) then
return quote, nil
end
end
return quotes[1], nil
end
@@ -251,7 +257,9 @@ local function findStoreMarker(adEnv, vehicle, product, capacity, sources, liter
return {wayPointId = chosen.id, name = tostring(chosen.name), free = liters}
end
local avoid = ADRunsController ~= nil and ADRunsController.getPickupMarkers ~= nil and ADRunsController.getPickupMarkers() or nil
local marker = ADLoadSwap.findDumpMarker(adEnv, vehicle, product.fillType, liters, nil, isReachable, ADSources.getSourcePlaceables(sources), avoid, true)
-- flydende varer (gylle, digestat, mælk ...) kun i tanke/siloer — ikke automatisk i en fabriks rist
local allowFactories = not (ADLoadSwap.isLiquid ~= nil and ADLoadSwap.isLiquid(product.fillType))
local marker = ADLoadSwap.findDumpMarker(adEnv, vehicle, product.fillType, liters, nil, isReachable, ADSources.getSourcePlaceables(sources), avoid, allowFactories)
if marker == nil then
return nil
end
@@ -356,6 +364,7 @@ function ADOutbound.buildPlanInput(adEnv, run, vehicle)
hasAutoload = hasAutoload,
sellAtPercent = run.sellAtPercent,
storeAbovePercent = run.storeAbovePercent,
sellAbovePercent = run.sellAbovePercent,
collectRound = run.collectRound == true,
products = products,
}
+3 -1
View File
@@ -68,6 +68,8 @@ function ADOutboundPlanner.evaluate(product, input)
storageHigh = storagePercent >= (input.storeAbovePercent or 80),
percent = percent,
priceOk = percent ~= nil and percent >= (input.sellAtPercent or 90),
-- "sælg altid over X %": kilden er så fuld, at der sælges uanset pris
overflow = (input.sellAbovePercent or 0) > 0 and storagePercent >= input.sellAbovePercent,
}
end
@@ -123,7 +125,7 @@ local function collectCandidates(input)
local e = ADOutboundPlanner.evaluate(product, input)
local added = false
if e.available >= ADOutboundPlanner.MIN_AVAILABLE_LITERS then
if wantsSell(product.policy) and e.priceOk and product.quote ~= nil and canSellWithoutReserve(e, input) then
if wantsSell(product.policy) and (e.priceOk or e.overflow) and product.quote ~= nil and canSellWithoutReserve(e, input) then
table.insert(sell, {product = product, e = e, kind = "sell", targetWayPointId = product.quote.wayPointId, targetName = product.quote.stationName, percent = e.percent})
added = true
elseif wantsStore(product.policy) and (e.fullLoad or e.storageHigh or (input.feedMode and e.available >= ADOutboundPlanner.FEED_MIN_LITERS)) and product.store ~= nil then
+8
View File
@@ -17,6 +17,9 @@ ADRuns.KINDS = {supply = true, outbound = true}
ADRuns.POLICIES = {store = true, sell = true, sellElseStore = true}
ADRuns.DEFAULT_SELL_AT_PERCENT, ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT = 90, 50, 100
ADRuns.DEFAULT_STORE_ABOVE_PERCENT, ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT = 80, 50, 95
-- "Sælg altid over X %": kildens lager over grænsen sælges uanset pris (0 = fra). Så hober f.eks. gylle
-- ved BGA'en sig ikke op, når prisen er lav.
ADRuns.DEFAULT_SELL_ABOVE_PERCENT, ADRuns.MIN_SELL_ABOVE_PERCENT, ADRuns.MAX_SELL_ABOVE_PERCENT = 0, 0, 100
local function copyList(list)
local copy = {}
@@ -51,6 +54,7 @@ local function copyRun(run)
products = copyProducts(run.products),
sellAtPercent = run.sellAtPercent,
storeAbovePercent = run.storeAbovePercent,
sellAbovePercent = run.sellAbovePercent,
collectRound = run.collectRound,
targetWayPointId = run.targetWayPointId,
targetWayPointIds = copyList(run.targetWayPointIds),
@@ -98,6 +102,7 @@ function ADRuns.createRun(store, name, kind)
products = {},
sellAtPercent = ADRuns.DEFAULT_SELL_AT_PERCENT,
storeAbovePercent = ADRuns.DEFAULT_STORE_ABOVE_PERCENT,
sellAbovePercent = ADRuns.DEFAULT_SELL_ABOVE_PERCENT,
collectRound = false,
targetWayPointId = nil,
targetWayPointIds = {},
@@ -164,6 +169,7 @@ function ADRuns.updateRun(store, runId, changes)
if changes.products ~= nil then run.products = copyProducts(changes.products) end
if changes.sellAtPercent ~= nil then run.sellAtPercent = clampRange(changes.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT, ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT) end
if changes.storeAbovePercent ~= nil then run.storeAbovePercent = clampRange(changes.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT, ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT) end
if changes.sellAbovePercent ~= nil then run.sellAbovePercent = clampRange(changes.sellAbovePercent, ADRuns.DEFAULT_SELL_ABOVE_PERCENT, ADRuns.MIN_SELL_ABOVE_PERCENT, ADRuns.MAX_SELL_ABOVE_PERCENT) end
if changes.collectRound ~= nil then run.collectRound = changes.collectRound == true end
return new
end
@@ -364,6 +370,7 @@ function ADRuns.toRecords(store)
products = ADRuns.serializeProducts(run.products),
sellAt = run.sellAtPercent,
storeAbove = run.storeAbovePercent,
sellAbove = run.sellAbovePercent or 0,
collectRound = run.collectRound and 1 or 0,
}
end
@@ -407,6 +414,7 @@ function ADRuns.fromRecords(records)
products = ADRuns.parseProducts(record.products),
sellAtPercent = clampRange(record.sellAt, ADRuns.DEFAULT_SELL_AT_PERCENT, ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT),
storeAbovePercent = clampRange(record.storeAbove, ADRuns.DEFAULT_STORE_ABOVE_PERCENT, ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT),
sellAbovePercent = clampRange(record.sellAbove, ADRuns.DEFAULT_SELL_ABOVE_PERCENT, ADRuns.MIN_SELL_ABOVE_PERCENT, ADRuns.MAX_SELL_ABOVE_PERCENT),
collectRound = tonumber(record.collectRound) == 1,
targetWayPointId = targets[1],
targetWayPointIds = targets,
+2
View File
@@ -46,6 +46,7 @@ function ADRunsStorage.writeRecords(xmlFile, records)
xmlFile:setString(key .. "#products", record.products or "")
xmlFile:setInt(key .. "#sellAt", record.sellAt or ADRuns.DEFAULT_SELL_AT_PERCENT)
xmlFile:setInt(key .. "#storeAbove", record.storeAbove or ADRuns.DEFAULT_STORE_ABOVE_PERCENT)
xmlFile:setInt(key .. "#sellAbove", record.sellAbove or 0)
xmlFile:setInt(key .. "#collectRound", record.collectRound or 0)
for vehicleIndex, vehicleId in ipairs(record.vehicleIds) do
xmlFile:setString(string.format("%s.vehicle(%d)#id", key, vehicleIndex - 1), vehicleId)
@@ -74,6 +75,7 @@ function ADRunsStorage.readRecords(xmlFile)
products = xmlFile:getString(key .. "#products"),
sellAt = xmlFile:getInt(key .. "#sellAt"),
storeAbove = xmlFile:getInt(key .. "#storeAbove"),
sellAbove = xmlFile:getInt(key .. "#sellAbove"),
collectRound = xmlFile:getInt(key .. "#collectRound"),
vehicleIds = {},
}
+3
View File
@@ -240,6 +240,8 @@
<text name="spu_sellAt" text="Sælg fra (% af årets toppris)"/>
<text name="spu_sellAt_tooltip" text="Der køres til salg når salgsstedets pris er mindst denne andel af varens toppris i årets prisforløb — uanset mængde. Stor efterspørgsel tæller altid."/>
<text name="spu_storeAbove" text="Kør til lager over"/>
<text name="spu_sellAbove" text="Sælg altid når kilden er over"/>
<text name="spu_sellAbove_off" text="Fra"/>
<text name="spu_storeAbove_tooltip" text="Varer med Lager-politik køres til silo når kildens lager er over denne fyldning (eller der er et fuldt læs)."/>
<text name="spu_collectRound" text="Saml op på én tur"/>
<text name="spu_collectRound_tooltip" text="Tipvogn: samme vare fra flere kilder til vognen er fuld. Autoload-vogn: flere pallevarer med samme mål på én tur."/>
@@ -434,6 +436,7 @@
<text name="spu_guide_flow_sumGoods" text="%d varer"/>
<text name="spu_guide_flow_sumSellAt" text="sælg fra %d %%"/>
<text name="spu_guide_flow_sumStoreAbove" text="lager over %d %%"/>
<text name="spu_guide_flow_sumSellAbove" text="sælg altid over %d %%"/>
<text name="spu_guide_flow_sumRound" text="samlerunde"/>
<text name="spu_guide_flow_sumLoops" text="%d ture"/>
<text name="spu_guide_flow_sumLoopsInf" text="uendelige ture"/>
+3
View File
@@ -240,6 +240,8 @@
<text name="spu_sellAt" text="Sell from (% of yearly top price)"/>
<text name="spu_sellAt_tooltip" text="Drive to sell when the selling point pays at least this share of the good's top price in the yearly price curve — regardless of amount. Great demand always qualifies."/>
<text name="spu_storeAbove" text="Drive to storage above"/>
<text name="spu_sellAbove" text="Always sell when source is above"/>
<text name="spu_sellAbove_off" text="Off"/>
<text name="spu_storeAbove_tooltip" text="Goods with the Store policy are driven to a silo when the source storage is above this fill level (or a full load is available)."/>
<text name="spu_collectRound" text="Collect in one trip"/>
<text name="spu_collectRound_tooltip" text="Tipper: same good from several sources until full. Autoload trailer: several pallet goods with the same destination in one trip."/>
@@ -434,6 +436,7 @@
<text name="spu_guide_flow_sumGoods" text="%d goods"/>
<text name="spu_guide_flow_sumSellAt" text="sell from %d %%"/>
<text name="spu_guide_flow_sumStoreAbove" text="store above %d %%"/>
<text name="spu_guide_flow_sumSellAbove" text="always sell above %d %%"/>
<text name="spu_guide_flow_sumRound" text="collection round"/>
<text name="spu_guide_flow_sumLoops" text="%d trips"/>
<text name="spu_guide_flow_sumLoopsInf" text="endless trips"/>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.32.5.0</version>
<version>1.33.0.0</version>
<title>
<en>AD Smart Pickup</en>
</title>