feat: spuSellBlock slår salgssteder fra/til pr. savegame

This commit is contained in:
2026-09-30 23:35:01 +02:00
parent e229a27624
commit bb287a8154
7 changed files with 169 additions and 4 deletions
+1
View File
@@ -8,6 +8,7 @@ står i [docs/BRUGERMANUAL.md](docs/BRUGERMANUAL.md) (dansk) og [docs/USER_MANUA
## 1.62 — Kæder slås selv til
- **1.62.7** Nyt: konsolkommandoen `spuSellBlock` slår salgssteder fra og til (pr. savegame). Et salgssted, der er slået fra, bruges hverken til udkørsel eller i priserne. Til stationer, hvor aflæsningen går i stå, fx "Market Price Liquid Fill Station".
- **1.62.6** Pålæsningsdiagnosen logger kun, når en trigger med varen er inden for 20 m (køretøjer der holder ved Wait-punkter eller marker gav falske meldinger).
- Manualerne understreger nu, at parkering og hentning af redskaber kun virker ved at bakke: ruten til `P <navn>` og `Connect P <navn>` skal slutte som en bakrute.
- **1.62.5** Diagnose: står AutoDrive 30 s ved et LOAD-mål uden at fylde, skriver loggen én linje med markør, vare og for hver vogn den nærmeste AD-læssetrigger (afstand, har varen, står vognen i triggerens boks). Ingen indgreb.
+2
View File
@@ -1478,6 +1478,7 @@ function ADRunsController.save()
if ADFieldSilos ~= nil then pcall(ADFieldSilos.save) end
-- kædens tilstand (tændte mellemled, pausede kæder, afviste forslag) — samme gem-regel som navnefilen
if ADChain ~= nil and ADChain.save ~= nil then pcall(ADChain.save) end
if ADSellBlock ~= nil and ADSellBlock.save ~= nil then pcall(ADSellBlock.save) end
-- navnefilen skal skrives under spillets gem: FS25 genskriver savegame-mappen, og filer ingen skriver
-- under gemningen forsvinder (in-game 27/9: ADSmartPickup_names.xml blev slettet ved første gem)
if ADNames ~= nil and ADNames.byId ~= nil then pcall(ADNames.save) end
@@ -1498,6 +1499,7 @@ function ADRunsController.load(adEnv)
pcall(ADRunsController.migrateBuildings, adEnv)
if ADFieldSilos ~= nil then pcall(ADFieldSilos.load) end
if ADChain ~= nil and ADChain.load ~= nil then pcall(ADChain.load) end
if ADSellBlock ~= nil and ADSellBlock.load ~= nil then pcall(ADSellBlock.load) end
if ADCpResume ~= nil then pcall(ADCpResume.load) end
local vehiclesById = getFarmVehiclesById(adEnv)
for _, run in ipairs(store.runs) do
+154
View File
@@ -0,0 +1,154 @@
-- AD Sell Block
-- Salgssteder spilleren har slået fra (in-game 30/9: "Market Price Liquid Fill Station" fra FarmFillStations gik i stå
-- ved aflæsning af gylle). Konsol: spuSellBlock viser salgsstederne; spuSellBlock <del af navn> slår dem fra/til.
-- Udkørsel (ADSources.findSellMarkers) og priser (ADSources.getMarketQuote) springer udelukkede over. Gemmes pr.
-- savegame i ADSmartPickup_sellblock.xml (skrives fra ADRunsController.save, som de andre filer).
ADSellBlock = {}
ADSellBlock.FILE_NAME = "ADSmartPickup_sellblock.xml"
ADSellBlock.ROOT = "sellBlock"
ADSellBlock.blocked = {} -- {[stationId] = visningsnavn}
-- ---- ren logik ----
-- stations = {{id, names = {...}}}, query = del af et navn (små/store bogstaver ligegyldige) -> matchende stationer
function ADSellBlock.match(stations, query)
local wanted = string.lower(tostring(query or ""))
local hits = {}
if wanted == "" then return hits end
for _, station in ipairs(stations or {}) do
for _, name in ipairs(station.names or {}) do
if string.find(string.lower(tostring(name)), wanted, 1, true) ~= nil then
table.insert(hits, station)
break
end
end
end
return hits
end
-- Ny tabel: hver station skiftes (fra -> til, til -> fra). Første navn huskes til visning.
function ADSellBlock.toggle(blocked, stations)
local result = {}
for id, name in pairs(blocked or {}) do result[id] = name end
for _, station in ipairs(stations or {}) do
if result[station.id] ~= nil then
result[station.id] = nil
else
result[station.id] = tostring((station.names or {})[1] or station.id)
end
end
return result
end
-- ---- spil ----
local function stationId(station)
local placeable = station ~= nil and station.owningPlaceable or nil
if placeable ~= nil and placeable.uniqueId ~= nil then return tostring(placeable.uniqueId) end
return nil
end
function ADSellBlock.isBlocked(station)
local id = stationId(station)
return id ~= nil and ADSellBlock.blocked[id] ~= nil
end
local function stationNames(station)
local names = {}
local placeable = station.owningPlaceable
if placeable ~= nil and placeable.getName ~= nil then
local ok, name = pcall(placeable.getName, placeable)
if ok and name ~= nil then table.insert(names, name) end
end
if station.getName ~= nil then
local ok, name = pcall(station.getName, station)
if ok and name ~= nil then table.insert(names, name) end
end
return names
end
-- Alle salgssteder med AD-markør -> {{id, names}} (markørens navn først: det spilleren ser i AutoDrive).
local function sellStations(adEnv)
local list, byId = {}, {}
local farmId = g_currentMission:getFarmId()
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local station = ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId)
local id = station ~= nil and ADPrices.isSellingStation(station) and stationId(station) or nil
if id ~= nil then
if byId[id] == nil then
byId[id] = {id = id, names = {}}
table.insert(list, byId[id])
for _, name in ipairs(stationNames(station)) do table.insert(byId[id].names, name) end
end
table.insert(byId[id].names, 1, marker.name)
end
end
table.sort(list, function(a, b) return tostring(a.names[1]) < tostring(b.names[1]) end)
return list
end
function ADSellBlock:consoleSellBlock(...)
local query = table.concat({...}, " ")
local adEnv = FS25_AutoDrive
if adEnv == nil or adEnv.ADGraphManager == nil then return "AutoDrive ikke fundet" end
local stations = sellStations(adEnv)
if query == "" then
local lines = {"Salgssteder (spuSellBlock <del af navn> slår fra/til):"}
for _, station in ipairs(stations) do
table.insert(lines, string.format(" %s %s", ADSellBlock.blocked[station.id] ~= nil and "[FRA]" or "[til]", table.concat(station.names, " / ")))
end
return table.concat(lines, "\n")
end
local hits = ADSellBlock.match(stations, query)
if #hits == 0 then return string.format("Intet salgssted matcher '%s' (spuSellBlock uden navn viser listen)", query) end
ADSellBlock.blocked = ADSellBlock.toggle(ADSellBlock.blocked, hits)
local lines = {}
for _, station in ipairs(hits) do
local text = string.format("%s: %s", station.names[1], ADSellBlock.blocked[station.id] ~= nil and "slået FRA som salgssted" or "bruges igen")
table.insert(lines, text)
Logging.info("ADSmartPickup: salgssted " .. text)
end
if ADRunsController ~= nil and ADRunsController.save ~= nil then pcall(ADRunsController.save) end
return table.concat(lines, "\n")
end
local function getPath()
local path = ADRunsStorage ~= nil and ADRunsStorage.getPath() or nil
if path == nil then return nil end
return (path:gsub(ADRunsStorage.FILE_NAME .. "$", ADSellBlock.FILE_NAME))
end
function ADSellBlock.save()
local path = getPath()
if path == nil then return false end
local xmlFile = XMLFile.create("ADSmartPickupSellBlock", path, ADSellBlock.ROOT)
if xmlFile == nil then return false end
local index = 0
for id, name in pairs(ADSellBlock.blocked) do
local key = string.format("%s.station(%d)", ADSellBlock.ROOT, index)
xmlFile:setString(key .. "#id", id)
xmlFile:setString(key .. "#name", name)
index = index + 1
end
xmlFile:save()
xmlFile:delete()
return true
end
function ADSellBlock.load()
ADSellBlock.blocked = {}
local path = getPath()
if path == nil or not fileExists(path) then return end
local xmlFile = XMLFile.load("ADSmartPickupSellBlock", path)
if xmlFile == nil then return end
xmlFile:iterate(ADSellBlock.ROOT .. ".station", function(_, key)
local id = xmlFile:getString(key .. "#id")
if id ~= nil then ADSellBlock.blocked[id] = xmlFile:getString(key .. "#name") or id end
end)
xmlFile:delete()
end
if addConsoleCommand ~= nil then
addConsoleCommand("spuSellBlock", "Salgssteder: uden navn = liste; spuSellBlock <del af navn> slår fra/til", "consoleSellBlock", ADSellBlock)
end
+8 -3
View File
@@ -644,7 +644,12 @@ function ADSources.describeAccepted(station)
return table.concat(names, ", ")
end
-- Markører hvis aflæsningsstation er et rigtigt salgssted: { {marker, station} }.
-- Salgssted spilleren har slået fra (spuSellBlock, ADSellBlock).
function ADSources.isSellBlocked(station)
return ADSellBlock ~= nil and ADSellBlock.isBlocked ~= nil and ADSellBlock.isBlocked(station) == true
end
-- Markører hvis aflæsningsstation er et rigtigt salgssted (og ikke slået fra): { {marker, station} }.
function ADSources.getSellMarkers(adEnv, farmId)
return cacheGet("sellMarkers", tostring(farmId), 10000, function() return ADSources.findSellMarkers(adEnv, farmId) end)
end
@@ -653,7 +658,7 @@ function ADSources.findSellMarkers(adEnv, farmId)
local result = {}
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local station = ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId)
if station ~= nil and ADPrices.isSellingStation(station) then
if station ~= nil and ADPrices.isSellingStation(station) and not ADSources.isSellBlocked(station) then
table.insert(result, {marker = marker, station = station})
end
end
@@ -1002,7 +1007,7 @@ function ADSources.getMarketQuote(fillType)
local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil
local best = nil
for _, station in pairs(storage ~= nil and storage:getUnloadingStations() or {}) do
if ADPrices.isSellingStation(station) and ADSources.stationAccepts(station, fillType) then
if ADPrices.isSellingStation(station) and not ADSources.isSellBlocked(station) and ADSources.stationAccepts(station, fillType) then
local quote = ADPrices.getQuote(station, fillType)
if quote ~= nil and (best == nil or quote.price > best.price) then
best = {price = quote.price, top = quote.top}
+2 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.62.6.0</version>
<version>1.62.7.0</version>
<title>
<en>AD Smart Pickup</en>
</title>
@@ -33,6 +33,7 @@
<sourceFile filename="adOutbound.lua"/>
<sourceFile filename="adTipSide.lua"/>
<sourceFile filename="adLoadDiag.lua"/>
<sourceFile filename="adSellBlock.lua"/>
<sourceFile filename="adUnloadWait.lua"/>
<sourceFile filename="adFieldModel.lua"/>
<sourceFile filename="adFieldFleet.lua"/>
+1
View File
@@ -1206,6 +1206,7 @@ FS25 kan ikke selv omdøbe bygninger, så moden har sine egne navne. De gemmes i
| `spuNameFleet` | Giver alle traktorer og lastbiler uden for flows butiksnavnet (*Fendt 942 1*, *Fendt 942 2* …) og omdøber deres egen parkering til `P <navn>`, hvis ingen anden deler den. Overskriver egne navne på de køretøjer. Gem spillet bagefter. Pas på: en eksisterende `Connect <gammelt navn>`-markør passer ikke længere |
| `spuConnect` | Kobler det nærmeste redskab (inden for ca. 3 m) på den maskine, du sidder i |
| `spuDetach` | Kobler redskaberne bag den maskine, du sidder i, af |
| `spuSellBlock [del af navn]` | Uden navn: viser alle salgssteder, og hvilke der er slået fra. Med navn: slår de salgssteder fra/til, hvis navn indeholder teksten (fx `spuSellBlock market price`). Et salgssted, der er slået fra, bruges hverken til udkørsel eller i priserne. Gemmes pr. savegame. Til salgssteder, hvor aflæsningen går i stå |
| `spuTrip A > B > C` | Kører en tur over flere markører: Connect-markører kobler på, Afkobl eller redskabets egen parkering kobler af, og ved andre markører holdes der 10 sekunder. Mest til test |
---
+1
View File
@@ -1244,6 +1244,7 @@ Open the game's console to use them:
| `spuNameFleet` | Gives all tractors and trucks outside flows their shop name (*Fendt 942 1*, *Fendt 942 2* …) and renames their own parking spot to `P <name>` if nobody else shares it. Overwrites custom names on those vehicles. Save the game afterwards. Note: an existing `Connect <old name>` marker no longer matches |
| `spuConnect` | Hitches the nearest implement (within about 3 m) to the machine you are sitting in |
| `spuDetach` | Detaches the implements behind the machine you are sitting in |
| `spuSellBlock [part of name]` | Without a name: lists all selling points and which ones are switched off. With a name: switches off/on the selling points whose name contains the text (e.g. `spuSellBlock market price`). A switched-off selling point is used neither for outbound trips nor in prices. Saved per savegame. For selling points where unloading keeps stalling |
| `spuTrip A > B > C` | Drives a trip over several markers: Connect markers hitch, Afkobl or the implement's own parking detach, and at other markers it stops for 10 seconds. Mostly for testing |
---