feat(gui): faner Oversigt/Forsyning/Udkørsel; udkørsels-run med kilder, varepolitik, regler og status
This commit is contained in:
@@ -9,8 +9,11 @@ SmartPickupFrame._mt = Class(SmartPickupFrame, TabbedMenuFrameElement)
|
||||
SmartPickupFrame.REFRESH_INTERVAL_MS = 1000
|
||||
SmartPickupFrame.LOG_PREFIX = "ADSmartPickup: "
|
||||
SmartPickupFrame.STATE_OFF, SmartPickupFrame.STATE_ON = 1, 2
|
||||
SmartPickupFrame.TAB_OVERVIEW, SmartPickupFrame.TAB_RUNS = 1, 2
|
||||
SmartPickupFrame.TAB_NAMES = {"spu_tab_overview", "spu_tab_runs"}
|
||||
SmartPickupFrame.TAB_OVERVIEW, SmartPickupFrame.TAB_SUPPLY, SmartPickupFrame.TAB_OUTBOUND = 1, 2, 3
|
||||
SmartPickupFrame.TAB_NAMES = {"spu_tab_overview", "spu_tab_supply", "spu_tab_outbound"}
|
||||
-- Hver fane viser kun runs af sin egen slags; Oversigt opretter forsynings-runs.
|
||||
SmartPickupFrame.TAB_KIND = {"supply", "supply", "outbound"}
|
||||
SmartPickupFrame.POLICIES = {"off", "store", "sell", "sellElseStore"}
|
||||
|
||||
function SmartPickupFrame.new(i18n, getAdEnv)
|
||||
local self = SmartPickupFrame:superClass().new(nil, SmartPickupFrame._mt)
|
||||
@@ -52,7 +55,7 @@ end
|
||||
|
||||
-- Prefabs skal ud af layoutet før de klones, ellers vises de selv som rækker.
|
||||
function SmartPickupFrame:initialize()
|
||||
for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "textPrefab"}) do
|
||||
for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "textPrefab", "productPrefab"}) do
|
||||
local prefab = self[name]
|
||||
if prefab ~= nil then
|
||||
prefab:unlinkElement()
|
||||
@@ -91,6 +94,34 @@ function SmartPickupFrame:initialize()
|
||||
table.insert(percentTexts, string.format("%d %%", percent))
|
||||
end
|
||||
self.returnBelowOption:setTexts(percentTexts)
|
||||
self.sellAtSteps, self.storeAboveSteps = {}, {}
|
||||
local sellTexts, storeTexts = {}, {}
|
||||
for percent = ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT, 5 do
|
||||
table.insert(self.sellAtSteps, percent)
|
||||
table.insert(sellTexts, string.format("%d %%", percent))
|
||||
end
|
||||
for percent = ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT, 5 do
|
||||
table.insert(self.storeAboveSteps, percent)
|
||||
table.insert(storeTexts, string.format("%d %%", percent))
|
||||
end
|
||||
if self.sellAtOption ~= nil then self.sellAtOption:setTexts(sellTexts) end
|
||||
if self.storeAboveOption ~= nil then self.storeAboveOption:setTexts(storeTexts) end
|
||||
if self.collectRoundOption ~= nil then self.collectRoundOption:setTexts({self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}) end
|
||||
end
|
||||
|
||||
-- Indeks i en trin-liste for en procent (største trin <= værdien).
|
||||
function SmartPickupFrame.getStepIndex(steps, value, default)
|
||||
local index = 1
|
||||
for position, percent in ipairs(steps or {}) do
|
||||
if percent <= (value or default) then
|
||||
index = position
|
||||
end
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
function SmartPickupFrame:getTabKind()
|
||||
return SmartPickupFrame.TAB_KIND[self.currentTab] or "supply"
|
||||
end
|
||||
|
||||
function SmartPickupFrame:getReturnBelowIndex(run)
|
||||
@@ -214,15 +245,35 @@ function SmartPickupFrame:onClickTab(state)
|
||||
self:switchTab(state)
|
||||
end
|
||||
|
||||
-- De faste rækker (Run, Varer, Wait-pulje, Ture, tærskel) hører til Runs-fanen.
|
||||
function SmartPickupFrame:setStaticRowsVisible(isVisible)
|
||||
for _, option in ipairs({self.runOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption, self.returnBelowOption}) do
|
||||
-- De faste rækker hører til hver sin fane: Run + Wait-pulje er fælles, Varer/Ture/tærskel er
|
||||
-- Forsyning, Sælg fra/Kør til lager/Saml op er Udkørsel. Oversigt viser ingen.
|
||||
function SmartPickupFrame:setStaticRowsVisible(tab)
|
||||
local isSupply, isOutbound = tab == SmartPickupFrame.TAB_SUPPLY, tab == SmartPickupFrame.TAB_OUTBOUND
|
||||
local visibility = {
|
||||
{self.runOption, isSupply or isOutbound},
|
||||
{self.waitPoolOption, isSupply or isOutbound},
|
||||
{self.ingredientModeOption, isSupply}, {self.loopsOption, isSupply}, {self.returnBelowOption, isSupply},
|
||||
{self.sellAtOption, isOutbound}, {self.storeAboveOption, isOutbound}, {self.collectRoundOption, isOutbound},
|
||||
}
|
||||
for _, entry in ipairs(visibility) do
|
||||
local option = entry[1]
|
||||
if option ~= nil and option.parent ~= nil then
|
||||
option.parent:setVisible(isVisible)
|
||||
option.parent:setVisible(entry[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Run'et der vises: kun runs af fanens slags; skifter fanen, vælges fanens første run.
|
||||
function SmartPickupFrame:getRunsForTab()
|
||||
local kind, result = self:getTabKind(), {}
|
||||
for _, run in ipairs(ADRunsController.store.runs) do
|
||||
if (run.kind or "supply") == kind then
|
||||
table.insert(result, run)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function SmartPickupFrame:getRun()
|
||||
return self.selectedRunId ~= nil and ADRuns.getRun(ADRunsController.store, self.selectedRunId) or nil
|
||||
end
|
||||
@@ -244,6 +295,7 @@ function SmartPickupFrame:clearRows()
|
||||
self.statusRow = nil
|
||||
self.needsRows = {}
|
||||
self.overviewRows = {}
|
||||
self.productRows, self.outboundStatusRows = {}, {}
|
||||
end
|
||||
|
||||
function SmartPickupFrame:addRow(prefab, data)
|
||||
@@ -285,12 +337,14 @@ end
|
||||
function SmartPickupFrame:rebuild()
|
||||
local adEnv = self.getAdEnv()
|
||||
local store = ADRunsController.store
|
||||
if self.selectedRunId == nil or ADRuns.getRun(store, self.selectedRunId) == nil then
|
||||
self.selectedRunId = store.runs[1] ~= nil and store.runs[1].id or nil
|
||||
local tabRuns = self:getRunsForTab()
|
||||
local selected = self.selectedRunId ~= nil and ADRuns.getRun(store, self.selectedRunId) or nil
|
||||
if selected == nil or (selected.kind or "supply") ~= self:getTabKind() then
|
||||
self.selectedRunId = tabRuns[1] ~= nil and tabRuns[1].id or nil
|
||||
end
|
||||
self:clearRows()
|
||||
local isOverview = self.currentTab == SmartPickupFrame.TAB_OVERVIEW
|
||||
self:setStaticRowsVisible(not isOverview)
|
||||
self:setStaticRowsVisible(self.currentTab)
|
||||
if isOverview then
|
||||
if adEnv ~= nil then
|
||||
self:fillOverviewRows(adEnv)
|
||||
@@ -307,20 +361,21 @@ function SmartPickupFrame:rebuild()
|
||||
self:fillRunOption(adEnv)
|
||||
local run = self:getRun()
|
||||
local hasRun = run ~= nil and adEnv ~= nil
|
||||
for _, element in ipairs({self.ingredientModeOption, self.waitPoolOption, self.loopsOption, self.returnBelowOption}) do
|
||||
for _, element in ipairs({self.ingredientModeOption, self.waitPoolOption, self.loopsOption, self.returnBelowOption, self.sellAtOption, self.storeAboveOption, self.collectRoundOption}) do
|
||||
if element ~= nil then
|
||||
element:setDisabled(not hasRun)
|
||||
end
|
||||
end
|
||||
if hasRun then
|
||||
self.ingredientModeOption:setState(run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
self:fillWaitPoolOption(adEnv, run)
|
||||
self.loopsOption:setState(run.loops + 1, false)
|
||||
self.returnBelowOption:setState(self:getReturnBelowIndex(run), false)
|
||||
self:addTextRow(self.i18n:getText("spu_targets"))
|
||||
self:fillTargetRows(adEnv, run)
|
||||
self:fillIngredientRows(adEnv, run)
|
||||
self:fillNeedsRows(adEnv, run)
|
||||
self:addTextRow(self.i18n:getText("spu_vehicles"))
|
||||
if self.currentTab == SmartPickupFrame.TAB_OUTBOUND then
|
||||
self:rebuildOutboundTab(adEnv, run)
|
||||
else
|
||||
self:rebuildSupplyTab(adEnv, run)
|
||||
end
|
||||
self:addTextRow(self.i18n:getText("spu_section_vehicles"))
|
||||
self:fillVehicleRows(adEnv, run)
|
||||
self:addTextRow(self.i18n:getText("spu_section_status"))
|
||||
end
|
||||
self.statusRow = self:addTextRow("")
|
||||
self:colorRows()
|
||||
@@ -332,7 +387,7 @@ end
|
||||
function SmartPickupFrame:fillRunOption(adEnv)
|
||||
local texts, index = {}, 1
|
||||
self.runIds = {}
|
||||
for position, run in ipairs(ADRunsController.store.runs) do
|
||||
for position, run in ipairs(self:getRunsForTab()) do
|
||||
local status = adEnv ~= nil and ADRunsController.getRunStatus(adEnv, run) or {running = 0, total = #run.vehicleIds}
|
||||
table.insert(texts, string.format("%s (%d/%d)", run.name, status.running, status.total))
|
||||
table.insert(self.runIds, run.id)
|
||||
@@ -348,32 +403,6 @@ function SmartPickupFrame:fillRunOption(adEnv)
|
||||
self.runOption:setDisabled(#self.runIds == 0)
|
||||
end
|
||||
|
||||
-- Mål = afkrydsning (flere stalde/fabrikker pr. run). Ved hver tur vælges den der mangler mest.
|
||||
function SmartPickupFrame:fillTargetRows(adEnv, run)
|
||||
local chosen = {}
|
||||
for _, id in ipairs(ADRunsController.getRunTargets(run)) do
|
||||
chosen[id] = true
|
||||
end
|
||||
for _, marker in ipairs(ADRunsController.getTargetMarkers(adEnv, g_currentMission:getFarmId())) do
|
||||
local row = self:addRow(self.ingredientPrefab, {kind = "target", wayPointId = marker.id})
|
||||
row:getDescendantByName("title"):setText(" " .. tostring(marker.name))
|
||||
row:getDescendantByName("option"):setState(chosen[marker.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
end
|
||||
end
|
||||
|
||||
-- Behovsrække pr. mål ("Cow 1 Food: Forage 0/33.750 l · …"), opdateres hvert sekund.
|
||||
function SmartPickupFrame:fillNeedsRows(adEnv, run)
|
||||
self.needsRows = {}
|
||||
local targets = ADRunsController.getTargetsLevels(adEnv, run)
|
||||
if #targets == 0 then
|
||||
self:addTextRow(self.i18n:getText("spu_needs_noTarget"))
|
||||
return
|
||||
end
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(self.needsRows, {wayPointId = target.wayPointId, title = self:addTextRow(self:describeTargetNeeds(target))})
|
||||
end
|
||||
end
|
||||
|
||||
-- Overblik: "Cow 1 food: TMR 18.0k/27.8k · Silage 3.0k/…" (mest trængende først)
|
||||
function SmartPickupFrame:describeTargetLevels(target)
|
||||
if #target.levels == 0 then
|
||||
@@ -382,40 +411,6 @@ function SmartPickupFrame:describeTargetLevels(target)
|
||||
return string.format("%s: %s", target.name, self:formatLevels(target.levels))
|
||||
end
|
||||
|
||||
function SmartPickupFrame:describeTargetNeeds(target)
|
||||
if #target.levels == 0 then
|
||||
return string.format("%s: %s", target.name, self.i18n:getText("spu_needs_none"))
|
||||
end
|
||||
return string.format(self.i18n:getText("spu_needs"), target.name .. ": " .. self:formatLevels(target.levels))
|
||||
end
|
||||
|
||||
function SmartPickupFrame:refreshNeedsRows(adEnv, run)
|
||||
local byId = {}
|
||||
for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, run)) do
|
||||
byId[target.wayPointId] = target
|
||||
end
|
||||
for _, row in ipairs(self.needsRows or {}) do
|
||||
if byId[row.wayPointId] ~= nil then
|
||||
row.title:setText(self:describeTargetNeeds(byId[row.wayPointId]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillIngredientRows(adEnv, run)
|
||||
if run.ingredientMode ~= "manual" then
|
||||
return
|
||||
end
|
||||
local chosen = {}
|
||||
for _, name in ipairs(run.ingredients) do
|
||||
chosen[name] = true
|
||||
end
|
||||
for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do
|
||||
local row = self:addRow(self.ingredientPrefab, {kind = "ingredient", name = fillType.name})
|
||||
row:getDescendantByName("title"):setText(tostring(fillType.title))
|
||||
row:getDescendantByName("option"):setState(chosen[fillType.name] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillWaitPoolOption(adEnv, run)
|
||||
local texts, index = {self.i18n:getText("spu_waitPool_all")}, 1
|
||||
self.poolGroups = {""}
|
||||
@@ -502,12 +497,18 @@ function SmartPickupFrame:syncOptionsFromModel(adEnv, run)
|
||||
option:setState(wantedIndex, false)
|
||||
end
|
||||
end
|
||||
sync(self.ingredientModeOption, run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF)
|
||||
local poolIndex = 1
|
||||
for index, group in ipairs(self.poolGroups) do
|
||||
if group == run.waitPoolGroup then poolIndex = index end
|
||||
end
|
||||
sync(self.waitPoolOption, poolIndex)
|
||||
if run.kind == "outbound" then
|
||||
sync(self.sellAtOption, SmartPickupFrame.getStepIndex(self.sellAtSteps, run.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT))
|
||||
sync(self.storeAboveOption, SmartPickupFrame.getStepIndex(self.storeAboveSteps, run.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT))
|
||||
sync(self.collectRoundOption, run.collectRound and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF)
|
||||
return
|
||||
end
|
||||
sync(self.ingredientModeOption, run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF)
|
||||
sync(self.loopsOption, run.loops + 1)
|
||||
sync(self.returnBelowOption, self:getReturnBelowIndex(run))
|
||||
end
|
||||
@@ -545,6 +546,8 @@ function SmartPickupFrame:describeActivity(adEnv, vehicle)
|
||||
return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_waiting"), place, g_i18n:formatNumber(activity.liters or 0, 0), fillTypeName))
|
||||
elseif activity.kind == "loaded" then
|
||||
return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_loaded"), g_i18n:formatNumber(activity.liters or 0, 0), fillTypeName, place))
|
||||
elseif activity.kind == "holding" then
|
||||
return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_holding"), place, tostring(activity.reason or "")))
|
||||
end
|
||||
if activity.fillType ~= nil then
|
||||
return string.format("%s: %s", label, string.format(self.i18n:getText("spu_activity_empty"), place, fillTypeName))
|
||||
@@ -573,9 +576,13 @@ function SmartPickupFrame:fillOverviewRows(adEnv)
|
||||
table.insert(entry.vehicles, {vehicle = vehicle, title = add(" " .. self:describeActivity(adEnv, vehicle))})
|
||||
end
|
||||
end
|
||||
if run.kind == "outbound" then
|
||||
entry.outbound = add(" " .. self:describeOutboundOverview(adEnv, run))
|
||||
else
|
||||
for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, run)) do
|
||||
table.insert(entry.targets, {wayPointId = target.wayPointId, title = add(" " .. self:describeTargetLevels(target))})
|
||||
end
|
||||
end
|
||||
table.insert(self.overviewRows, entry)
|
||||
end
|
||||
-- forrest i layoutet: fjern de klonede rækker bagfra og sæt dem ind i rækkefølge først
|
||||
@@ -601,6 +608,9 @@ function SmartPickupFrame:refreshOverview(adEnv)
|
||||
for _, item in ipairs(entry.vehicles) do
|
||||
item.title:setText(" " .. self:describeActivity(adEnv, item.vehicle))
|
||||
end
|
||||
if entry.outbound ~= nil then
|
||||
entry.outbound:setText(" " .. self:describeOutboundOverview(adEnv, entry.run))
|
||||
end
|
||||
local byId = {}
|
||||
for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, entry.run)) do
|
||||
byId[target.wayPointId] = target
|
||||
@@ -658,7 +668,11 @@ function SmartPickupFrame:refreshLive()
|
||||
local isRunning = false
|
||||
if run ~= nil then
|
||||
self:syncOptionsFromModel(adEnv, run)
|
||||
if run.kind == "outbound" then
|
||||
self:refreshOutboundRows(adEnv, run)
|
||||
else
|
||||
self:refreshNeedsRows(adEnv, run)
|
||||
end
|
||||
local status = ADRunsController.getRunStatus(adEnv, run)
|
||||
isRunning = status.running > 0
|
||||
local text = isRunning and string.format(self.i18n:getText("spu_status_running"), status.running, status.total)
|
||||
@@ -692,30 +706,13 @@ function SmartPickupFrame:onClickRun(state)
|
||||
self:requestRebuild()
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickTarget(state, element)
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
if run == nil or data == nil then return end
|
||||
ADRunsController.store = ADRuns.toggleTarget(ADRunsController.store, run.id, data.wayPointId)
|
||||
Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': targets=%s", tostring(run.name), table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, run.id)), " "))
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickIngredientMode(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"})
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickIngredient(state, element)
|
||||
-- mål-rækkerne deler prefab (og dermed onClick) med vare-rækkerne
|
||||
-- mål- og kilde-rækkerne deler prefab (og dermed onClick) med vare-rækkerne
|
||||
local data = self.rowData[element]
|
||||
if data ~= nil and data.kind == "target" then
|
||||
return self:onClickTarget(state, element)
|
||||
elseif data ~= nil and data.kind == "source" then
|
||||
return self:onClickSource(state, element)
|
||||
end
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
@@ -739,22 +736,6 @@ function SmartPickupFrame:onClickWaitPool(state)
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickLoops(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {loops = state - 1})
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickReturnBelow(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {returnBelowPercent = frame.returnBelowSteps[state] or ADRuns.DEFAULT_RETURN_BELOW_PERCENT})
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickVehicle(state, element)
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
@@ -807,11 +788,13 @@ function SmartPickupFrame:onNewRunName(value, clickOk)
|
||||
if not clickOk or value == nil or not value:match("%S") then
|
||||
return
|
||||
end
|
||||
local run = ADRunsController.createRun(value)
|
||||
local kind = frame:getTabKind()
|
||||
local run = ADRunsController.createRun(value, kind)
|
||||
frame.selectedRunId = run.id
|
||||
frame.statusOverride = nil
|
||||
frame.currentTab = SmartPickupFrame.TAB_RUNS
|
||||
frame:switchTab(SmartPickupFrame.TAB_RUNS)
|
||||
local tab = kind == "outbound" and SmartPickupFrame.TAB_OUTBOUND or SmartPickupFrame.TAB_SUPPLY
|
||||
frame.currentTab = tab
|
||||
frame:switchTab(tab)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -49,6 +49,25 @@
|
||||
</MultiTextOption>
|
||||
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_returnBelow"/>
|
||||
</Bitmap>
|
||||
<!-- Udkørsel: regler -->
|
||||
<Bitmap profile="fs25_multiTextOptionContainer">
|
||||
<MultiTextOption profile="fs25_settingsMultiTextOption" onClick="onClickSellAt" id="sellAtOption">
|
||||
<Text profile="fs25_multiTextOptionTooltip" name="ignore" text="$l10n_spu_sellAt_tooltip"/>
|
||||
</MultiTextOption>
|
||||
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_sellAt"/>
|
||||
</Bitmap>
|
||||
<Bitmap profile="fs25_multiTextOptionContainer">
|
||||
<MultiTextOption profile="fs25_settingsMultiTextOption" onClick="onClickStoreAbove" id="storeAboveOption">
|
||||
<Text profile="fs25_multiTextOptionTooltip" name="ignore" text="$l10n_spu_storeAbove_tooltip"/>
|
||||
</MultiTextOption>
|
||||
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_storeAbove"/>
|
||||
</Bitmap>
|
||||
<Bitmap profile="fs25_multiTextOptionContainer">
|
||||
<BinaryOption profile="fs25_settingsBinaryOption" onClick="onClickCollectRound" id="collectRoundOption">
|
||||
<Text profile="fs25_multiTextOptionTooltip" name="ignore" text="$l10n_spu_collectRound_tooltip"/>
|
||||
</BinaryOption>
|
||||
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_collectRound"/>
|
||||
</Bitmap>
|
||||
</ScrollingLayout>
|
||||
|
||||
<!-- prefabs: klones pr. vare / traktor / tekstlinje og fjernes fra layoutet ved initialize -->
|
||||
@@ -65,6 +84,13 @@
|
||||
<Bitmap profile="fs25_multiTextOptionContainer" id="textPrefab">
|
||||
<Text profile="spuInfoText" name="title"/>
|
||||
</Bitmap>
|
||||
<!-- udkørsel: én række pr. vare med politik Fra/Lager/Salg/Salg, ellers lager -->
|
||||
<Bitmap profile="fs25_multiTextOptionContainer" id="productPrefab">
|
||||
<MultiTextOption profile="fs25_settingsMultiTextOption" onClick="onClickProduct" name="option">
|
||||
<Text profile="fs25_multiTextOptionTooltip" name="ignore" text="$l10n_spu_policy_tooltip"/>
|
||||
</MultiTextOption>
|
||||
<Text profile="fs25_settingsMultiTextOptionTitle" name="title"/>
|
||||
</Bitmap>
|
||||
</GuiElement>
|
||||
|
||||
<ThreePartBitmap profile="fs25_sliderDockedBg">
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
-- Smart Pickup — fanen Udkørsel
|
||||
-- Kilder (afkrydsning), varer med politik (Fra/Lager/Salg/Salg, ellers lager) og aktuel pris i
|
||||
-- % af årets top, regler (pristærskel, lagertærskel, opsamlingsrunde) og status/advarsler.
|
||||
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
||||
|
||||
-- Bedste pris-% for varen over alle salgssteder (uanset vejnet — det er til visning; turen
|
||||
-- bruger kun nåelige). {percent, stationName} eller nil.
|
||||
function SmartPickupFrame:getProductQuote(adEnv, fillType)
|
||||
if ADSources == nil or ADPrices == nil then
|
||||
return nil
|
||||
end
|
||||
local best = nil
|
||||
for _, entry in ipairs(ADSources.getSellMarkers(adEnv, g_currentMission:getFarmId())) do
|
||||
if ADSources.stationAccepts(entry.station, fillType) then
|
||||
local quote = ADPrices.getQuote(entry.station, fillType)
|
||||
if quote ~= nil and (best == nil or quote.percent > best.percent) then
|
||||
best = {percent = quote.percent, stationName = tostring(entry.marker.name)}
|
||||
end
|
||||
end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
-- "Sukker — 150.0k l — 78 % af top (Grain Elevator)"
|
||||
function SmartPickupFrame:describeProduct(adEnv, product)
|
||||
local price = self.i18n:getText("spu_price_none")
|
||||
local quote = self:getProductQuote(adEnv, product.fillType)
|
||||
if quote ~= nil then
|
||||
price = string.format(self.i18n:getText("spu_price_pct"), quote.percent, quote.stationName)
|
||||
end
|
||||
return string.format(self.i18n:getText("spu_product_row"), tostring(product.title), SmartPickupFrame.formatCompact(product.available or 0), price)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:getPolicyIndex(policy)
|
||||
for index, name in ipairs(SmartPickupFrame.POLICIES) do
|
||||
if name == policy then
|
||||
return index
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillSourceRows(adEnv, run)
|
||||
local chosen = {}
|
||||
for _, id in ipairs(run.sourceWayPointIds or {}) do
|
||||
chosen[id] = true
|
||||
end
|
||||
local entries = ADRunsController.getSourceMarkers(adEnv, g_currentMission:getFarmId())
|
||||
for _, entry in ipairs(entries) do
|
||||
local row = self:addRow(self.ingredientPrefab, {kind = "source", wayPointId = entry.marker.id})
|
||||
local kindText = self.i18n:getText("spu_source_kind_" .. tostring(entry.kind))
|
||||
row:getDescendantByName("title"):setText(string.format(" %s — %s (%s)", tostring(entry.marker.name), tostring(entry.name), kindText))
|
||||
row:getDescendantByName("option"):setState(chosen[entry.marker.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
end
|
||||
if #entries == 0 then
|
||||
self:addTextRow(self.i18n:getText("spu_noSources"))
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillProductRows(adEnv, run)
|
||||
self.productRows = {}
|
||||
local policyTexts = {}
|
||||
for _, policy in ipairs(SmartPickupFrame.POLICIES) do
|
||||
table.insert(policyTexts, self.i18n:getText("spu_policy_" .. policy))
|
||||
end
|
||||
local products = ADRunsController.getRunProducts(adEnv, run)
|
||||
for _, product in ipairs(products) do
|
||||
local row = self:addRow(self.productPrefab, {kind = "product", name = product.name})
|
||||
local option = row:getDescendantByName("option")
|
||||
option:setTexts(policyTexts)
|
||||
option:setState(self:getPolicyIndex(product.policy), false)
|
||||
local title = row:getDescendantByName("title")
|
||||
title:setText(self:describeProduct(adEnv, product))
|
||||
table.insert(self.productRows, {name = product.name, title = title, option = option})
|
||||
end
|
||||
if #products == 0 then
|
||||
self:addTextRow(#(run.sourceWayPointIds or {}) == 0 and self.i18n:getText("spu_noSources") or self.i18n:getText("spu_noProducts"))
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillOutboundStatusRows(adEnv, run)
|
||||
self.outboundStatusRows = {}
|
||||
local status = ADRunsController.getOutboundStatus(adEnv, run)
|
||||
for _, reason in ipairs(status.reasons or {}) do
|
||||
table.insert(self.outboundStatusRows, self:addTextRow(string.format(self.i18n:getText("spu_outbound_waiting"), reason)))
|
||||
end
|
||||
if status.warning ~= nil then
|
||||
table.insert(self.outboundStatusRows, self:addTextRow(string.format(self.i18n:getText("spu_outbound_warning"), status.warning)))
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:rebuildOutboundTab(adEnv, run)
|
||||
self.sellAtOption:setState(SmartPickupFrame.getStepIndex(self.sellAtSteps, run.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT), false)
|
||||
self.storeAboveOption:setState(SmartPickupFrame.getStepIndex(self.storeAboveSteps, run.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT), false)
|
||||
self.collectRoundOption:setState(run.collectRound and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
self:addTextRow(self.i18n:getText("spu_section_sources"))
|
||||
self:fillSourceRows(adEnv, run)
|
||||
self:addTextRow(self.i18n:getText("spu_section_products"))
|
||||
self:fillProductRows(adEnv, run)
|
||||
self:fillOutboundStatusRows(adEnv, run)
|
||||
end
|
||||
|
||||
-- Hvert sekund: produktrækkernes mængde/pris og statuslinjerne (antal linjer ændres kun ved rebuild).
|
||||
function SmartPickupFrame:refreshOutboundRows(adEnv, run)
|
||||
local byName = {}
|
||||
for _, product in ipairs(ADRunsController.getRunProducts(adEnv, run)) do
|
||||
byName[product.name] = product
|
||||
end
|
||||
for _, row in ipairs(self.productRows or {}) do
|
||||
if byName[row.name] ~= nil then
|
||||
row.title:setText(self:describeProduct(adEnv, byName[row.name]))
|
||||
end
|
||||
end
|
||||
local status = ADRunsController.getOutboundStatus(adEnv, run)
|
||||
local lines = {}
|
||||
for _, reason in ipairs(status.reasons or {}) do
|
||||
table.insert(lines, string.format(self.i18n:getText("spu_outbound_waiting"), reason))
|
||||
end
|
||||
if status.warning ~= nil then
|
||||
table.insert(lines, string.format(self.i18n:getText("spu_outbound_warning"), status.warning))
|
||||
end
|
||||
for index, row in ipairs(self.outboundStatusRows or {}) do
|
||||
row:setText(lines[index] or "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Oversigt: "SUGAR 78 % af top (kræver 90), lager 41 % · ⚠ …" eller "Sukker 150.0k l — 92 % af top (…)".
|
||||
function SmartPickupFrame:describeOutboundOverview(adEnv, run)
|
||||
local status = ADRunsController.getOutboundStatus(adEnv, run)
|
||||
local parts = {}
|
||||
for _, reason in ipairs(status.reasons or {}) do
|
||||
table.insert(parts, reason)
|
||||
end
|
||||
if #parts == 0 then
|
||||
for _, product in ipairs(status.products or {}) do
|
||||
if product.policy ~= "off" then
|
||||
table.insert(parts, self:describeProduct(adEnv, product))
|
||||
end
|
||||
end
|
||||
end
|
||||
if status.warning ~= nil then
|
||||
table.insert(parts, string.format(self.i18n:getText("spu_outbound_warning"), status.warning))
|
||||
end
|
||||
if #parts == 0 then
|
||||
return self.i18n:getText("spu_noSources")
|
||||
end
|
||||
return table.concat(parts, " · ")
|
||||
end
|
||||
|
||||
-- ---- callbacks ----
|
||||
|
||||
function SmartPickupFrame:onClickSource(state, element)
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
if run == nil or data == nil then return end
|
||||
ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, run.id, data.wayPointId)
|
||||
Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': sources=%s", tostring(run.name), table.concat(ADRuns.getRun(ADRunsController.store, run.id).sourceWayPointIds, " "))
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickProduct(state, element)
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
if run == nil or data == nil or data.kind ~= "product" then return end
|
||||
local policy = SmartPickupFrame.POLICIES[state] or "off"
|
||||
ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, run.id, data.name, policy)
|
||||
Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': %s=%s", tostring(run.name), tostring(data.name), policy)
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickSellAt(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {sellAtPercent = frame.sellAtSteps[state] or ADRuns.DEFAULT_SELL_AT_PERCENT})
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickStoreAbove(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {storeAbovePercent = frame.storeAboveSteps[state] or ADRuns.DEFAULT_STORE_ABOVE_PERCENT})
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickCollectRound(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {collectRound = state == SmartPickupFrame.STATE_ON})
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
-- Smart Pickup — fanen Forsyning
|
||||
-- Mål, varer (Auto/Vælg), behovsrække, ture og "kør hjem"-tærskel for et forsynings-run.
|
||||
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
||||
|
||||
function SmartPickupFrame:rebuildSupplyTab(adEnv, run)
|
||||
self.ingredientModeOption:setState(run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
self.loopsOption:setState(run.loops + 1, false)
|
||||
self.returnBelowOption:setState(self:getReturnBelowIndex(run), false)
|
||||
self:addTextRow(self.i18n:getText("spu_section_targets"))
|
||||
self:addTextRow(self.i18n:getText("spu_targets"))
|
||||
self:fillTargetRows(adEnv, run)
|
||||
self:fillIngredientRows(adEnv, run)
|
||||
self:fillNeedsRows(adEnv, run)
|
||||
end
|
||||
|
||||
-- Mål = afkrydsning (flere stalde/fabrikker pr. run). Ved hver tur vælges den der mangler mest.
|
||||
function SmartPickupFrame:fillTargetRows(adEnv, run)
|
||||
local chosen = {}
|
||||
for _, id in ipairs(ADRunsController.getRunTargets(run)) do
|
||||
chosen[id] = true
|
||||
end
|
||||
for _, marker in ipairs(ADRunsController.getTargetMarkers(adEnv, g_currentMission:getFarmId())) do
|
||||
local row = self:addRow(self.ingredientPrefab, {kind = "target", wayPointId = marker.id})
|
||||
row:getDescendantByName("title"):setText(" " .. tostring(marker.name))
|
||||
row:getDescendantByName("option"):setState(chosen[marker.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
end
|
||||
end
|
||||
|
||||
-- Behovsrække pr. mål ("Cow 1 Food: Forage 0/33.750 l · …"), opdateres hvert sekund.
|
||||
function SmartPickupFrame:fillNeedsRows(adEnv, run)
|
||||
self.needsRows = {}
|
||||
local targets = ADRunsController.getTargetsLevels(adEnv, run)
|
||||
if #targets == 0 then
|
||||
self:addTextRow(self.i18n:getText("spu_needs_noTarget"))
|
||||
return
|
||||
end
|
||||
for _, target in ipairs(targets) do
|
||||
table.insert(self.needsRows, {wayPointId = target.wayPointId, title = self:addTextRow(self:describeTargetNeeds(target))})
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:describeTargetNeeds(target)
|
||||
if #target.levels == 0 then
|
||||
return string.format("%s: %s", target.name, self.i18n:getText("spu_needs_none"))
|
||||
end
|
||||
return string.format(self.i18n:getText("spu_needs"), target.name .. ": " .. self:formatLevels(target.levels))
|
||||
end
|
||||
|
||||
function SmartPickupFrame:refreshNeedsRows(adEnv, run)
|
||||
local byId = {}
|
||||
for _, target in ipairs(ADRunsController.getTargetsLevels(adEnv, run)) do
|
||||
byId[target.wayPointId] = target
|
||||
end
|
||||
for _, row in ipairs(self.needsRows or {}) do
|
||||
if byId[row.wayPointId] ~= nil then
|
||||
row.title:setText(self:describeTargetNeeds(byId[row.wayPointId]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:fillIngredientRows(adEnv, run)
|
||||
if run.ingredientMode ~= "manual" then
|
||||
return
|
||||
end
|
||||
local chosen = {}
|
||||
for _, name in ipairs(run.ingredients) do
|
||||
chosen[name] = true
|
||||
end
|
||||
for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do
|
||||
local row = self:addRow(self.ingredientPrefab, {kind = "ingredient", name = fillType.name})
|
||||
row:getDescendantByName("title"):setText(tostring(fillType.title))
|
||||
row:getDescendantByName("option"):setState(chosen[fillType.name] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
end
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickTarget(state, element)
|
||||
self:safe(function(frame)
|
||||
local run, data = frame:getRun(), frame.rowData[element]
|
||||
if run == nil or data == nil then return end
|
||||
ADRunsController.store = ADRuns.toggleTarget(ADRunsController.store, run.id, data.wayPointId)
|
||||
Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': targets=%s", tostring(run.name), table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, run.id)), " "))
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickIngredientMode(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"})
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickLoops(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {loops = state - 1})
|
||||
end)
|
||||
end
|
||||
|
||||
function SmartPickupFrame:onClickReturnBelow(state)
|
||||
self:safe(function(frame)
|
||||
local run = frame:getRun()
|
||||
if run == nil then return end
|
||||
ADRunsController.updateRun(run.id, {returnBelowPercent = frame.returnBelowSteps[state] or ADRuns.DEFAULT_RETURN_BELOW_PERCENT})
|
||||
end)
|
||||
end
|
||||
@@ -43,7 +43,7 @@
|
||||
<text name="spu_deleteConfirm" text="Slet run '%s'? Dets kørende traktorer stoppes. Kan ikke fortrydes."/>
|
||||
<text name="spu_capacity" text="%s l"/>
|
||||
<text name="spu_tab_overview" text="Overblik"/>
|
||||
<text name="spu_tab_runs" text="Runs"/>
|
||||
<text name="spu_tab_runs" text="Forsyning"/>
|
||||
<text name="spu_targets" text="Mål (vælg én eller flere — den der mangler mest, får turen)"/>
|
||||
<text name="spu_returnBelow" text="Kør hjem fra Wait under"/>
|
||||
<text name="spu_returnBelow_tooltip" text="Ventende traktorer kører først hjem, når målet er faldet under denne andel (eller hele resten passer)."/>
|
||||
@@ -58,5 +58,40 @@
|
||||
<text name="spu_needs_noTarget" text="(vælg et mål for at se behov)"/>
|
||||
<text name="spu_needs_none" text="Målet tager ingen af run'ets varer"/>
|
||||
<text name="spu_canCarry" text="kan bære: %s"/>
|
||||
<text name="spu_tab_supply" text="Forsyning"/>
|
||||
<text name="spu_tab_outbound" text="Udkørsel"/>
|
||||
<text name="spu_section_run" text="— Run —"/>
|
||||
<text name="spu_section_targets" text="— Mål og varer —"/>
|
||||
<text name="spu_section_sources" text="— Kilder (fabrik, silo eller objektlager) —"/>
|
||||
<text name="spu_section_products" text="— Varer: Fra / Lager / Salg / Salg, ellers lager —"/>
|
||||
<text name="spu_section_rules" text="— Regler —"/>
|
||||
<text name="spu_section_wait" text="— Ventepunkter og ture —"/>
|
||||
<text name="spu_section_vehicles" text="— Traktorer —"/>
|
||||
<text name="spu_section_status" text="— Status —"/>
|
||||
<text name="spu_policy_off" text="Fra"/>
|
||||
<text name="spu_policy_store" text="Lager"/>
|
||||
<text name="spu_policy_sell" text="Salg"/>
|
||||
<text name="spu_policy_sellElseStore" text="Salg, ellers lager"/>
|
||||
<text name="spu_policy_tooltip" text="Lager: kør til en silo når der er et fuldt læs eller lageret er højt. Salg: sælg når prisen er tæt på årets top. Salg, ellers lager: sælg ved god pris, ellers til silo når lageret er højt."/>
|
||||
<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_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."/>
|
||||
<text name="spu_noSources" text="(vælg mindst én kilde)"/>
|
||||
<text name="spu_noProducts" text="Kilderne har ingen varer endnu"/>
|
||||
<text name="spu_reason_noSource" text="ingen genkendt kilde"/>
|
||||
<text name="spu_reason_noProducts" text="ingen vare valgt"/>
|
||||
<text name="spu_reason_needsAutoload" text="pallevarer kræver Universal Autoload på vognen"/>
|
||||
<text name="spu_product_row" text="%s — %s l — %s"/>
|
||||
<text name="spu_price_pct" text="%d %% af top (%s)"/>
|
||||
<text name="spu_price_none" text="intet salgssted"/>
|
||||
<text name="spu_outbound_waiting" text="venter: %s"/>
|
||||
<text name="spu_outbound_warning" text="⚠ %s"/>
|
||||
<text name="spu_activity_holding" text="holder ved %s (%s)"/>
|
||||
<text name="spu_source_kind_factory" text="fabrik"/>
|
||||
<text name="spu_source_kind_silo" text="silo"/>
|
||||
<text name="spu_source_kind_objectStorage" text="objektlager"/>
|
||||
</texts>
|
||||
</l10n>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<text name="spu_deleteConfirm" text="Delete run '%s'? Its running tractors will be stopped. This cannot be undone."/>
|
||||
<text name="spu_capacity" text="%s l"/>
|
||||
<text name="spu_tab_overview" text="Overview"/>
|
||||
<text name="spu_tab_runs" text="Runs"/>
|
||||
<text name="spu_tab_runs" text="Supply"/>
|
||||
<text name="spu_targets" text="Targets (pick one or more — the neediest gets the trip)"/>
|
||||
<text name="spu_returnBelow" text="Return from Wait below"/>
|
||||
<text name="spu_returnBelow_tooltip" text="Waiting tractors return only once the target has dropped below this share (or the whole rest fits)."/>
|
||||
@@ -58,5 +58,40 @@
|
||||
<text name="spu_needs_noTarget" text="(choose a target to see needs)"/>
|
||||
<text name="spu_needs_none" text="The target accepts none of the run's goods"/>
|
||||
<text name="spu_canCarry" text="can carry: %s"/>
|
||||
<text name="spu_tab_supply" text="Supply"/>
|
||||
<text name="spu_tab_outbound" text="Outbound"/>
|
||||
<text name="spu_section_run" text="— Run —"/>
|
||||
<text name="spu_section_targets" text="— Targets and goods —"/>
|
||||
<text name="spu_section_sources" text="— Sources (factory, silo or object storage) —"/>
|
||||
<text name="spu_section_products" text="— Goods: Off / Store / Sell / Sell, else store —"/>
|
||||
<text name="spu_section_rules" text="— Rules —"/>
|
||||
<text name="spu_section_wait" text="— Wait points and loops —"/>
|
||||
<text name="spu_section_vehicles" text="— Tractors —"/>
|
||||
<text name="spu_section_status" text="— Status —"/>
|
||||
<text name="spu_policy_off" text="Off"/>
|
||||
<text name="spu_policy_store" text="Store"/>
|
||||
<text name="spu_policy_sell" text="Sell"/>
|
||||
<text name="spu_policy_sellElseStore" text="Sell, else store"/>
|
||||
<text name="spu_policy_tooltip" text="Store: drive to a silo when there is a full load or the storage is high. Sell: sell when the price is near the yearly top. Sell, else store: sell at a good price, otherwise to a silo when storage is high."/>
|
||||
<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_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."/>
|
||||
<text name="spu_noSources" text="(choose at least one source)"/>
|
||||
<text name="spu_noProducts" text="The sources hold no goods yet"/>
|
||||
<text name="spu_reason_noSource" text="no recognised source"/>
|
||||
<text name="spu_reason_noProducts" text="no good selected"/>
|
||||
<text name="spu_reason_needsAutoload" text="pallet goods need Universal Autoload on the trailer"/>
|
||||
<text name="spu_product_row" text="%s — %s l — %s"/>
|
||||
<text name="spu_price_pct" text="%d %% of top (%s)"/>
|
||||
<text name="spu_price_none" text="no selling point"/>
|
||||
<text name="spu_outbound_waiting" text="waiting: %s"/>
|
||||
<text name="spu_outbound_warning" text="⚠ %s"/>
|
||||
<text name="spu_activity_holding" text="holding at %s (%s)"/>
|
||||
<text name="spu_source_kind_factory" text="factory"/>
|
||||
<text name="spu_source_kind_silo" text="silo"/>
|
||||
<text name="spu_source_kind_objectStorage" text="object storage"/>
|
||||
</texts>
|
||||
</l10n>
|
||||
|
||||
Reference in New Issue
Block a user