diff --git a/FS25_ADSmartPickup/adSources.lua b/FS25_ADSmartPickup/adSources.lua index 7cbdbee..5ba6a2a 100644 --- a/FS25_ADSmartPickup/adSources.lua +++ b/FS25_ADSmartPickup/adSources.lua @@ -632,10 +632,29 @@ local function addRate(rates, fillType, field, liters) rates[fillType][field] = rates[fillType][field] + liters end +-- Statistik: kun produktioner der kører NU. "Ønsket" (tændt eller mangler input) bruges til reserve og +-- forsyning, men ville her tælle stillestående fabrikker med fuld hastighed — fx fermenteringssiloer +-- med 200.000 cyklusser i timen gav hundreder af millioner liter pr. døgn. +local function isProductionRunning(productionPoint, production) + local status = ProductionPoint ~= nil and ProductionPoint.PROD_STATUS ~= nil and ProductionPoint.PROD_STATUS.RUNNING or nil + if status ~= nil and production.status ~= nil then + return production.status == status + end + return isProductionWanted(productionPoint, production) +end + +-- Spillets rater gælder pr. måned (én 24-timers dag pr. måned); faktisk pr. døgn = rate × timeAdjustment +-- (1 / dage pr. periode). +local function getTimeAdjustment() + local environment = g_currentMission ~= nil and g_currentMission.environment or nil + local value = environment ~= nil and tonumber(environment.timeAdjustment) or nil + return value ~= nil and value > 0 and value or 1 +end + local function addFactoryRates(rates, placeable) local productionPoint = getProductionPoint(placeable) for _, production in ipairs(productionPoint ~= nil and productionPoint.productions or {}) do - if isProductionWanted(productionPoint, production) then + if isProductionRunning(productionPoint, production) then local perDay = (production.cyclesPerMinute or ((production.cyclesPerHour or 0) / 60)) * 1440 for _, input in ipairs(production.inputs or {}) do addRate(rates, input.type, "consumption", (input.amount or 0) * perDay) end for _, output in ipairs(production.outputs or {}) do addRate(rates, output.type, "production", (output.amount or 0) * perDay) end @@ -732,6 +751,10 @@ function ADSources.getFarmRates(adEnv, farmId) end end end + local adjustment = getTimeAdjustment() + for _, rate in pairs(rates) do + rate.consumption, rate.production = rate.consumption * adjustment, rate.production * adjustment + end return rates end diff --git a/FS25_ADSmartPickup/adStorageStats.lua b/FS25_ADSmartPickup/adStorageStats.lua index c7e6d63..1308981 100644 --- a/FS25_ADSmartPickup/adStorageStats.lua +++ b/FS25_ADSmartPickup/adStorageStats.lua @@ -90,3 +90,12 @@ function ADStorageStats.rateText(consumption, production, format) if (production or 0) > 0 then table.insert(parts, "+" .. format(production)) end return #parts > 0 and table.concat(parts, " ") or "-" end + +-- Bygninger af én type (fanerne Fabrikker/Siloer/Stalde/Palleskure); rækkefølgen bevares. +function ADStorageStats.filterKind(inventory, kind) + local result = {} + for _, entry in ipairs(inventory or {}) do + if entry.kind == kind then table.insert(result, entry) end + end + return result +end diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 870fa85..84d009c 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -10,8 +10,14 @@ SmartPickupFrame.REFRESH_INTERVAL_MS = 1000 SmartPickupFrame.STATUS_OVERRIDE_TICKS = 6 SmartPickupFrame.LOG_PREFIX = "ADSmartPickup: " SmartPickupFrame.STATE_OFF, SmartPickupFrame.STATE_ON = 1, 2 -SmartPickupFrame.TAB_FLOWS, SmartPickupFrame.TAB_STORAGE = 1, 2 -SmartPickupFrame.TAB_NAMES = {"spu_tab_flows", "spu_tab_storage"} +SmartPickupFrame.TAB_FLOWS = 1 +-- Lagerfaner: én pr. bygningstype (indeks -> kind i ADSources.getFarmInventory) +SmartPickupFrame.TAB_NAMES = {"spu_tab_flows", "spu_tab_factories", "spu_tab_silos", "spu_tab_barns", "spu_tab_sheds"} +SmartPickupFrame.TAB_KINDS = {[2] = "factory", [3] = "silo", [4] = "husbandry", [5] = "objectStorage"} + +function SmartPickupFrame.getTabKind(tab) + return SmartPickupFrame.TAB_KINDS[tab] +end SmartPickupFrame.POLICIES = {"off", "store", "sell", "sellElseStore"} -- Afvisningsgrunde der handler om VOGNEN (ikke run'ets opsætning): traktoren kan ikke vælges. SmartPickupFrame.VEHICLE_BLOCKING_REASONS = {noTrailer = true, cannotCarry = true, needsAutoload = true} @@ -349,7 +355,7 @@ function SmartPickupFrame:rebuild() self:clearRows() local run = self:getRun() self:setStaticRowsVisible(self.currentTab, run) - if self.currentTab == SmartPickupFrame.TAB_STORAGE then + if SmartPickupFrame.getTabKind(self.currentTab) ~= nil then if adEnv ~= nil then self:fillStorageRows(adEnv) end diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml index beac163..7b95632 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml @@ -74,20 +74,19 @@ - - - - - - - + + + + + + + - + - - - - + + + diff --git a/FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua b/FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua index 860facf..52304fa 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua @@ -3,7 +3,7 @@ -- Kun bygninger med indhold vises; teksterne opdateres hvert sekund uden genopbygning. -- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua). -SmartPickupFrame.GRID_CELLS = 3 +SmartPickupFrame.GRID_CELLS = 2 -- Én celle i gitteret: "*Ensilage 1.7M/20.0M" eller "Kurve 12 paller" (* = fabrikkens output). function SmartPickupFrame:describeInventoryItem(item) @@ -16,14 +16,13 @@ function SmartPickupFrame:describeInventoryItem(item) return string.format("%s%s %s", item.isOutput and "*" or "", tostring(item.title), amount) end --- Gitterlinjer for en bygning: {{navn, celle, celle, celle}, {"", celle, ...}, ...}. +-- Gitterlinjer for en bygning: {{navn, celle, celle}, {"", celle, ...}, ...} (fanen giver typen). function SmartPickupFrame:inventoryLines(entry) local cells = {} for _, item in ipairs(entry.entries) do table.insert(cells, self:describeInventoryItem(item)) end - local kindText = self.i18n:getText("spu_source_kind_" .. tostring(entry.kind)) local lines = {} for index, chunk in ipairs(ADStorageStats.chunk(cells, SmartPickupFrame.GRID_CELLS)) do - local line = {index == 1 and string.format("%s (%s)", tostring(entry.name), kindText) or ""} + local line = {index == 1 and tostring(entry.name) or ""} for cell = 1, SmartPickupFrame.GRID_CELLS do line[cell + 1] = chunk[cell] or "" end table.insert(lines, line) end @@ -94,7 +93,7 @@ function SmartPickupFrame:statCells(stat) local i18n = self.i18n local fc = SmartPickupFrame.formatCompact local here = string.format("%s/%s", fc(stat.level), fc(stat.capacity)) - if stat.percent ~= nil then here = here .. string.format(" %d %%", stat.percent) end + if stat.percent ~= nil then here = here .. string.format(" %d %%", stat.percent) end local now, top = i18n:getText("spu_stat_noSell"), "" if stat.valueNow ~= nil then now, top = SmartPickupFrame.formatMoney(stat.valueNow), SmartPickupFrame.formatMoney(stat.valueTop) @@ -119,12 +118,13 @@ function SmartPickupFrame:storageLabels(inventory) return labels end -function SmartPickupFrame:fillStorageDetail(adEnv, inventory) +function SmartPickupFrame:fillStorageDetail(adEnv, inventory, fullInventory) self.storageDetailRows = {} if #inventory == 0 then return end - self.storageIndex = math.max(1, math.min(self.storageIndex or 1, #inventory)) + self.storageIndexByTab = self.storageIndexByTab or {} + self.storageIndex = math.max(1, math.min(self.storageIndexByTab[self.currentTab] or 1, #inventory)) local row = self:addRow(self.productPrefab, {kind = "storageBuilding"}) local option = row:getDescendantByName("option") option:setTexts(self:storageLabels(inventory)) @@ -136,7 +136,7 @@ function SmartPickupFrame:fillStorageDetail(adEnv, inventory) local tooltipText = self.i18n:getText("spu_storage_select_tooltip") if tooltip ~= nil then tooltip:setText(tooltipText) end option.toolTipText = tooltipText - local stats = self:computeBuildingStats(adEnv, entry, inventory) + local stats = self:computeBuildingStats(adEnv, entry, fullInventory) self:addCellRow(self.statPrefab, self:statHeader()) for _, stat in ipairs(stats) do table.insert(self.storageDetailRows, self:addCellRow(self.statPrefab, self:statCells(stat))) @@ -144,7 +144,7 @@ function SmartPickupFrame:fillStorageDetail(adEnv, inventory) table.insert(self.storageDetailRows, self:addCellRow(self.statPrefab, self:statTotalCells(stats))) end -function SmartPickupFrame:refreshStorageDetail(adEnv, inventory) +function SmartPickupFrame:refreshStorageDetail(adEnv, inventory, fullInventory) local entry = nil for _, candidate in ipairs(inventory) do if candidate.placeable == self.storageDetailPlaceable then entry = candidate end @@ -152,7 +152,7 @@ function SmartPickupFrame:refreshStorageDetail(adEnv, inventory) if entry == nil or #(self.storageDetailRows or {}) ~= #entry.entries + 1 then return end - local stats = self:computeBuildingStats(adEnv, entry, inventory) + local stats = self:computeBuildingStats(adEnv, entry, fullInventory) for index, stat in ipairs(stats) do SmartPickupFrame.setCells(self.storageDetailRows[index], self:statCells(stat)) end @@ -160,15 +160,17 @@ function SmartPickupFrame:refreshStorageDetail(adEnv, inventory) end function SmartPickupFrame:onClickStorageBuilding(state) - self.storageIndex = state + self.storageIndexByTab = self.storageIndexByTab or {} + self.storageIndexByTab[self.currentTab] = state self:requestRebuild() end function SmartPickupFrame:fillStorageRows(adEnv) self.storageRows = {} - local inventory = self:getInventory(adEnv) - self:fillStorageDetail(adEnv, inventory) - self:addTextRow(self.i18n:getText("spu_section_storage")) + local fullInventory = self:getInventory(adEnv) + local inventory = ADStorageStats.filterKind(fullInventory, SmartPickupFrame.getTabKind(self.currentTab)) + self:fillStorageDetail(adEnv, inventory, fullInventory) + self:addTextRow(self.i18n:getText("spu_section_storage_" .. tostring(SmartPickupFrame.getTabKind(self.currentTab)))) for _, entry in ipairs(inventory) do local rows = {} for _, line in ipairs(self:inventoryLines(entry)) do @@ -182,15 +184,17 @@ function SmartPickupFrame:fillStorageRows(adEnv) end function SmartPickupFrame:refreshStorageRows(adEnv) - if self.currentTab ~= SmartPickupFrame.TAB_STORAGE or #(self.storageRows or {}) == 0 then + local kind = SmartPickupFrame.getTabKind(self.currentTab) + if kind == nil or #(self.storageRows or {}) == 0 then return end - local inventory = self:getInventory(adEnv) + local fullInventory = self:getInventory(adEnv) + local inventory = ADStorageStats.filterKind(fullInventory, kind) local byPlaceable = {} for _, entry in ipairs(inventory) do byPlaceable[entry.placeable] = entry end - pcall(self.refreshStorageDetail, self, adEnv, inventory) + pcall(self.refreshStorageDetail, self, adEnv, inventory, fullInventory) for _, row in ipairs(self.storageRows) do local entry = byPlaceable[row.placeable] local lines = entry ~= nil and self:inventoryLines(entry) or nil diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index e3b7fa8..17ec1a5 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -134,6 +134,14 @@ + + + + + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index ddf4c9f..6999a11 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -134,6 +134,14 @@ + + + + + + + + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 51ea5c2..4019c86 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.15.2.0 + 1.16.0.0 <en>AD Smart Pickup</en> diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index 0865930..e8af2f1 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — brugermanual -Version 1.15.2 · Farming Simulator 25 · tillæg til AutoDrive +Version 1.16.0 · Farming Simulator 25 · tillæg til AutoDrive *English version: [USER_MANUAL.md](USER_MANUAL.md)* @@ -27,7 +27,7 @@ Moden kan to ting: 6. [Udkørsel: tøm, lagr og sælg](#6-udkørsel-tøm-lagr-og-sælg) 7. [Traktorer og vogne](#7-traktorer-og-vogne) 8. [Start, stop og gemning](#8-start-stop-og-gemning) -9. [Fanen Lagre: beholdning og statistik](#9-fanen-lagre-beholdning-og-statistik) +9. [Lagerfanerne: beholdning og statistik](#9-lagerfanerne-beholdning-og-statistik) 10. [Opskrifter](#10-opskrifter) 11. [Fejlfinding](#11-fejlfinding) 12. [Ordliste](#12-ordliste) @@ -113,12 +113,15 @@ som er 25 m som standard. Står siloerne tættere, kan nabosiloen få læsset. S ## 4. Menuen Smart Pickup -Tryk **ESC** og vælg fanen **Smart Pickup**. Fanen har to underfaner: +Tryk **ESC** og vælg fanen **Smart Pickup**. Fanen har fem underfaner: | Fane | Indhold | |---|---| | **Flows** | Tavlen med alle flows, redigering af det valgte flow og Wait-puljerne | -| **Lagre** | Gårdens beholdning pr. bygning med værdi og statistik | +| **Fabrikker** | Beholdning og statistik for gårdens fabrikker | +| **Siloer** | Beholdning og statistik for gårdens siloer | +| **Stalde** | Beholdning og statistik for gårdens stalde | +| **Palleskure** | Beholdning og statistik for gårdens objektlagre | ### Flow-tavlen @@ -393,9 +396,10 @@ Flows fra ældre versioner indlæses automatisk. Gamle markørvalg oversættes t --- -## 9. Fanen Lagre: beholdning og statistik +## 9. Lagerfanerne: beholdning og statistik -Fanen **Lagre** viser gårdens beholdning og statistik pr. bygning. Tallene opdateres hvert sekund. +Fanerne **Fabrikker**, **Siloer**, **Stalde** og **Palleskure** viser hver én bygningstype. De er +bygget ens. Tallene opdateres hvert sekund, og hver fane husker, hvilken bygning du sidst valgte. ### Statistik for en bygning @@ -413,21 +417,22 @@ kolonneoverskrifter øverst: | **I bygningen** | Mængde og kapacitet i den valgte bygning samt fyldningsgrad | | **Værdi nu** | Værdien ved den bedste pris lige nu, blandt alle salgssteder på kortet | | **Ved toppris** | Værdien, hvis du sælger ved årets højeste pris | -| **Gården** | Gårdens samlede beholdning af varen i alle bygninger | +| **Gården** | Gårdens samlede beholdning af varen i alle bygninger, også dem på de andre faner | | **Rækker til** | Hvor mange døgn gårdens beholdning holder ved det nuværende forbrug | | **Forbrug/prod. døgn** | Forbrug pr. døgn med minus og produktion pr. døgn med plus | Nederst står **Bygningen i alt** med bygningens samlede værdi nu og ved toppris. -### Gårdens beholdning +### Oversigt over fanens bygninger -Under statistikken står alle bygninger med indhold som et gitter. Bygningens navn står til venstre, -og varerne står med op til tre pr. række. Fabrikkens outputs er markeret med `*`, og palleskure +Under statistikken står fanens bygninger med indhold som et gitter. Bygningens navn står til venstre, +og varerne står med op til to pr. række. Fabrikkens outputs er markeret med `*`, og palleskure viser antal paller. **Hvad tælles med** -- **Fabrikker:** ønskede produktioners forbrug og produktion. +- **Fabrikker:** forbrug og produktion for de produktioner, der kører lige nu. Fabrikker, der står + stille eller mangler input, tæller ikke med. - **Stalde, forbrug:** foder, halm og vand. Vand tælles ikke, når stalden har automatisk vand. Foderforbruget fordeles på de fodertyper, der ligger i truget. - **Stalde, produktion:** hvert dyrs produktion efter alder gange antal dyr. Det dækker mælk, diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 7850cf3..427d450 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — user manual -Version 1.15.2 · Farming Simulator 25 · add-on for AutoDrive +Version 1.16.0 · Farming Simulator 25 · add-on for AutoDrive *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* @@ -27,7 +27,7 @@ The mod does two things: 6. [Delivery: empty, store and sell](#6-delivery-empty-store-and-sell) 7. [Tractors and trailers](#7-tractors-and-trailers) 8. [Start, stop and saving](#8-start-stop-and-saving) -9. [The Storage tab: inventory and statistics](#9-the-storage-tab-inventory-and-statistics) +9. [The storage tabs: inventory and statistics](#9-the-storage-tabs-inventory-and-statistics) 10. [Recipes](#10-recipes) 11. [Troubleshooting](#11-troubleshooting) 12. [Glossary](#12-glossary) @@ -115,12 +115,15 @@ storage target with a neighbouring trigger. ## 4. The Smart Pickup menu -Press **ESC** and open the **Smart Pickup** tab. It has two sub-tabs: +Press **ESC** and open the **Smart Pickup** tab. It has five sub-tabs: | Tab | Contents | |---|---| | **Flows** | The board with all flows, editing of the selected flow and the Wait pools | -| **Storage** | The farm's inventory per building with value and statistics | +| **Factories** | Inventory and statistics for the farm's factories | +| **Silos** | Inventory and statistics for the farm's silos | +| **Barns** | Inventory and statistics for the farm's barns | +| **Pallet sheds** | Inventory and statistics for the farm's object storages | ### The flow board @@ -397,10 +400,10 @@ Flows from older versions load automatically. Old marker choices are translated --- -## 9. The Storage tab: inventory and statistics +## 9. The storage tabs: inventory and statistics -The **Storage** tab shows the farm's inventory and statistics per building. Figures update every -second. +The **Factories**, **Silos**, **Barns** and **Pallet sheds** tabs each show one type of building. +They work the same way. Figures update every second, and each tab remembers the building you picked last. ### Statistics for a building @@ -418,21 +421,22 @@ one row per good: | **In building** | Amount and capacity in the selected building, plus fill level | | **Value now** | The value at the best price right now, across all selling points on the map | | **At top price** | The value if you sell at the year's highest price | -| **Farm** | The farm's total amount of the good across all buildings | +| **Farm** | The farm's total amount of the good across all buildings, including those on the other tabs | | **Lasts** | How many days the farm's stock lasts at the current use | | **Use/prod. per day** | Daily use with a minus sign and daily production with a plus sign | At the bottom, **Building total** shows the building's total value now and at top price. -### Farm inventory +### Overview of the tab's buildings -Below the statistics, all buildings with contents are shown as a grid. The building name is on the -left, with up to three goods per row. Factory outputs are marked with `*`, and pallet sheds show the +Below the statistics, the tab's buildings with contents are shown as a grid. The building name is on +the left, with up to two goods per row. Factory outputs are marked with `*`, and pallet sheds show the number of pallets. **What is counted** -- **Factories:** use and production of the wanted productions. +- **Factories:** use and production of the productions that are running right now. Factories that + are idle or missing inputs are not counted. - **Barns, use:** feed, straw and water. Water is not counted when the barn has automatic water. Feed use is split across the feed types in the trough. - **Barns, production:** each animal's output by age times the number of animals. This covers milk, diff --git a/tests/test_adSources.lua b/tests/test_adSources.lua index 1a72779..79553d6 100644 --- a/tests/test_adSources.lua +++ b/tests/test_adSources.lua @@ -443,5 +443,19 @@ check("S19b æg 100 høns × 30", rates3[EGG] and rates3[EGG].production, 3000) check("S19b gødning via navn", rates3[MANURE_FT] and rates3[MANURE_FT].production, 1000) check("S19b mælk uændret", rates3[MILK].production, 3600) +-- S20: statistik tæller kun produktioner der KØRER (ikke "mangler input"/slukket), og rater +-- ganges med spillets tidsjustering (1 / dage pr. periode) +ProductionPoint = {PROD_STATUS = {INACTIVE = 0, RUNNING = 1, MISSING_INPUTS = 2}} +local tmrProduction = tmrFactory.spec_productionPoint.productionPoint.productions[1] +tmrProduction.status = ProductionPoint.PROD_STATUS.MISSING_INPUTS +local idle = ADSources.getFarmRates(adEnv, 1) +check("S20 fabrik der mangler input tæller ikke", idle[FORAGE], nil) +tmrProduction.status = ProductionPoint.PROD_STATUS.RUNNING +g_currentMission.environment = {timeAdjustment = 0.5} +local running = ADSources.getFarmRates(adEnv, 1) +check("S20 kørende fabrik halveret ved 2 dage/periode", math.floor(running[FORAGE].production + 0.5), 576000) +check("S20 stald halveret", running[STRAW].consumption, 6000) +g_currentMission.environment, ProductionPoint = nil, nil + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adStorageStats.lua b/tests/test_adStorageStats.lua index 45d6810..8071b2d 100644 --- a/tests/test_adStorageStats.lua +++ b/tests/test_adStorageStats.lua @@ -56,5 +56,12 @@ check("ST7 kun prod", S.rateText(0, 3600, fmt), "+3.6k") check("ST7 kun forbrug", S.rateText(2000, 0, fmt), "-2.0k") check("ST7 intet", S.rateText(0, 0, fmt), "-") +-- ST8: fane pr. bygningstype — filtrér inventar på kind, rækkefølgen bevares +local inv = {{kind = "silo", name = "A"}, {kind = "factory", name = "B"}, {kind = "silo", name = "C"}} +local silos = S.filterKind(inv, "silo") +check("ST8 to siloer", #silos, 2) +check("ST8 rækkefølge", silos[2].name, "C") +check("ST8 ingen stalde", #S.filterKind(inv, "husbandry"), 0) + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)