From 9a284c875e4b0f1c46f0b52bed47dc803dc2c288 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Sun, 27 Sep 2026 15:05:14 +0200 Subject: [PATCH] =?UTF-8?q?feat(navne):=20egne=20bygningsnavne=20fra=20ADS?= =?UTF-8?q?martPickup=5Fnames.xml=20bruges=20overalt=20(FS25=20kan=20ikke?= =?UTF-8?q?=20omd=C3=B8be=20bygninger);=20marksilo:=20siloer=20i=20samme?= =?UTF-8?q?=20klynge=20som=20et=20g=C3=A5rdlager=20er=20g=C3=A5rdens=20(v1?= =?UTF-8?q?.39.0.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adBuildings.lua | 2 ++ FS25_ADSmartPickup/adFieldSilos.lua | 30 ++++++++++++++++- FS25_ADSmartPickup/adNames.lua | 45 +++++++++++++++++++++++++ FS25_ADSmartPickup/adRunsController.lua | 1 + FS25_ADSmartPickup/adSmartPickup.lua | 2 +- FS25_ADSmartPickup/adSources.lua | 2 ++ FS25_ADSmartPickup/modDesc.xml | 3 +- tests/test_adFieldSilos.lua | 12 +++++++ tests/test_adNames.lua | 20 +++++++++++ 9 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 FS25_ADSmartPickup/adNames.lua create mode 100644 tests/test_adNames.lua diff --git a/FS25_ADSmartPickup/adBuildings.lua b/FS25_ADSmartPickup/adBuildings.lua index cea6177..f7aada6 100644 --- a/FS25_ADSmartPickup/adBuildings.lua +++ b/FS25_ADSmartPickup/adBuildings.lua @@ -31,6 +31,8 @@ local function getNodePosition(node) end local function getName(placeable, fallback) + local own = ADNames ~= nil and ADNames.forPlaceable(placeable) or nil + if own ~= nil then return own end if placeable ~= nil and placeable.getName ~= nil then local ok, name = pcall(placeable.getName, placeable) if ok and name ~= nil then return tostring(name) end diff --git a/FS25_ADSmartPickup/adFieldSilos.lua b/FS25_ADSmartPickup/adFieldSilos.lua index 48c318f..397d88d 100644 --- a/FS25_ADSmartPickup/adFieldSilos.lua +++ b/FS25_ADSmartPickup/adFieldSilos.lua @@ -14,6 +14,8 @@ ADFieldSilos.CARGO_MIN_LITERS = 100 -- rest under dette tæller ikke som last -- Silo så tæt på et gårdlager er en del af gården (Farma-siloer deler lager inden for 50 m): ikke marksilo. -- (in-game 27/9: sukkerroer blev kørt fra én Farma-silo til nabosiloen i samme klynge) ADFieldSilos.FARM_RADIUS = 100 +-- Siloer højst så langt fra en nabo hører til samme klynge (Farma-rækker: 15 m mellem siloer, 18 m mellem rækker) +ADFieldSilos.CLUSTER_LINK = 60 ADFieldSilos.MOVING_SPEED = 0.00028 -- m/ms, samme grænse som AutoDrive ADFieldSilos.FILE_NAME = "ADSmartPickup_fieldsilos.xml" ADFieldSilos.ROOT = "ADSmartPickupFieldSilos" @@ -113,6 +115,26 @@ local function runBuildingIds() return ids end +-- Alle siloer i samme klynge som et gårdlager (kæde af naboer ≤ CLUSTER_LINK): gårdens, aldrig marksiloer. +-- (in-game 27/9: gårdens Farma-klynge er ~300 m lang; siloer over 100 m fra et valgt lager blev marksiloer) +local function farmCluster(allSilos, storeIds) + local farm, queue = {}, {} + for _, silo in ipairs(allSilos) do + if storeIds[silo.id] then farm[silo.id] = true; table.insert(queue, silo) end + end + local link2 = ADFieldSilos.CLUSTER_LINK ^ 2 + while #queue > 0 do + local current = table.remove(queue) + for _, silo in ipairs(allSilos) do + if not farm[silo.id] and (silo.x - current.x) ^ 2 + (silo.z - current.z) ^ 2 <= link2 then + farm[silo.id] = true + table.insert(queue, silo) + end + end + end + return farm +end + local function nearStore(stores, x, z) for _, store in ipairs(stores) do if (store.x - x) ^ 2 + (store.z - z) ^ 2 <= ADFieldSilos.FARM_RADIUS ^ 2 then return true end @@ -123,9 +145,14 @@ end -- Gårdlagre fra opsætningen + marksiloer (alle markers marksiloer, uden dubletter og uden gårdlagre). function ADFieldSilos.collectBuildings(adEnv) local fid = farmId() - local stores, byId = {}, {} + local stores, byId, allSilos, storeIds = {}, {}, {}, {} for _, building in ipairs(ADBuildings.list(adEnv, fid)) do byId[building.id] = building + if building.kind == "silo" then + local sx, sz = position(building.placeable ~= nil and building.placeable.rootNode or nil) + table.insert(allSilos, {id = building.id, x = sx, z = sz}) + if ADFieldSiloConfig.hasStore(ADFieldSilos.config, building.id) then storeIds[building.id] = true end + end if building.kind == "silo" and ADFieldSiloConfig.hasStore(ADFieldSilos.config, building.id) then local x, z = position(building.placeable ~= nil and building.placeable.rootNode or nil) table.insert(stores, {id = building.id, name = building.name, markerId = firstMarker(building, "unload"), @@ -133,6 +160,7 @@ function ADFieldSilos.collectBuildings(adEnv) end end local silos, seen = {}, runBuildingIds() + for id in pairs(farmCluster(allSilos, storeIds)) do seen[id] = true end for _, info in ipairs(ADFields.list()) do for _, building in ipairs(ADFields.fieldSilos(adEnv, fid, info)) do local x, z = position(building.placeable ~= nil and building.placeable.rootNode or nil) diff --git a/FS25_ADSmartPickup/adNames.lua b/FS25_ADSmartPickup/adNames.lua new file mode 100644 index 0000000..a5856da --- /dev/null +++ b/FS25_ADSmartPickup/adNames.lua @@ -0,0 +1,45 @@ +-- AD Names +-- Egne bygningsnavne (FS25 kan ikke omdøbe bygninger): ADSmartPickup_names.xml i savegame-mappen, +-- . +-- Moden læser filen og skriver den aldrig — spillerens/Claudes navne overlever. Bruges af alle navneopslag. + +ADNames = {} +ADNames.FILE_NAME = "ADSmartPickup_names.xml" +ADNames.ROOT = "ADSmartPickupNames" +ADNames.byId = nil + +-- records = {{id, name}} -> {[id] = name}; tomme/ugyldige springes over +function ADNames.fromRecords(records) + local byId = {} + for _, record in ipairs(records or {}) do + local id, name = record.id, record.name + if type(id) == "string" and id ~= "" and type(name) == "string" and name:match("%S") then byId[id] = name end + end + return byId +end + +function ADNames.load() + ADNames.byId = {} + local path = ADRunsStorage ~= nil and ADRunsStorage.getPath() or nil + if path == nil then return end + path = path:gsub(ADRunsStorage.FILE_NAME .. "$", ADNames.FILE_NAME) + if not fileExists(path) then return end + local xmlFile = XMLFile.load("ADSmartPickupNames", path) + if xmlFile == nil then return end + local records = {} + xmlFile:iterate(ADNames.ROOT .. ".building", function(_, key) + table.insert(records, {id = xmlFile:getString(key .. "#id"), name = xmlFile:getString(key .. "#name")}) + end) + xmlFile:delete() + ADNames.byId = ADNames.fromRecords(records) + local count = 0 + for _ in pairs(ADNames.byId) do count = count + 1 end + Logging.info("ADSmartPickup: %d egne bygningsnavne indlæst", count) +end + +-- Eget navn for bygningen (placeable), ellers nil. +function ADNames.forPlaceable(placeable) + if placeable == nil or placeable.uniqueId == nil then return nil end + if ADNames.byId == nil then pcall(ADNames.load) end + return (ADNames.byId or {})[tostring(placeable.uniqueId)] +end diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua index 2c54f54..657a578 100644 --- a/FS25_ADSmartPickup/adRunsController.lua +++ b/FS25_ADSmartPickup/adRunsController.lua @@ -1333,6 +1333,7 @@ function ADRunsController.load(adEnv) return end ADRunsController.store = store + if ADNames ~= nil then pcall(ADNames.load) end pcall(ADRunsController.migrateBuildings, adEnv) if ADFieldSilos ~= nil then pcall(ADFieldSilos.load) end local vehiclesById = getFarmVehiclesById(adEnv) diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 5719a7f..87fdf36 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -471,7 +471,7 @@ end local factoryFallbackLogged = setmetatable({}, {__mode = "k"}) local function logFactoryFallbackOnce(driver, station) local placeable = station.owningPlaceable - local name = placeable ~= nil and placeable.getName ~= nil and placeable:getName() or "fabrik" + local name = (ADNames ~= nil and ADNames.forPlaceable(placeable)) or (placeable ~= nil and placeable.getName ~= nil and placeable:getName()) or "fabrik" local message = string.format("'%s': %s har ingen ønskede produktioner (tændte eller auto-slukkede pga. mangel) med varer vognen kan bære; bruger dit varevalg", tostring(ADRuns.vehicleLabel(driver)), tostring(name)) if factoryFallbackLogged[driver] ~= message then factoryFallbackLogged[driver] = message diff --git a/FS25_ADSmartPickup/adSources.lua b/FS25_ADSmartPickup/adSources.lua index ff23d7a..96c25fa 100644 --- a/FS25_ADSmartPickup/adSources.lua +++ b/FS25_ADSmartPickup/adSources.lua @@ -39,6 +39,8 @@ local function getNodePosition(node) end local function getPlaceableName(placeable, fallback) + local own = ADNames ~= nil and ADNames.forPlaceable(placeable) or nil + if own ~= nil then return own end if placeable ~= nil and placeable.getName ~= nil then local ok, name = pcall(placeable.getName, placeable) if ok and name ~= nil then diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 09b3879..003c6f5 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.38.7.0 + 1.39.0.0 <en>AD Smart Pickup</en> @@ -13,6 +13,7 @@ + diff --git a/tests/test_adFieldSilos.lua b/tests/test_adFieldSilos.lua index 5d64ccf..43c6b23 100644 --- a/tests/test_adFieldSilos.lua +++ b/tests/test_adFieldSilos.lua @@ -125,6 +125,18 @@ do check("gårdens nabosilo (40 m fra gårdlager) er ikke marksilo", table.concat(names, ","), "fs") ADFields.fieldSilos = function() return {fieldSilo, farm} end + -- in-game 27/9: gårdens Farma-klynge er 300 m lang — siloer over 100 m fra et valgt gårdlager blev marksiloer. + -- En silo i samme klynge (≤ 60 m til en nabo) som et gårdlager er gårdens. + local c1 = {id = "c1", name = "Klynge 1", kind = "silo", placeable = {rootNode = {x = 50, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} + local c2 = {id = "c2", name = "Klynge 2", kind = "silo", placeable = {rootNode = {x = 105, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} + ADBuildings.list = function() return {fieldSilo, farm, c1, c2} end + ADFields.fieldSilos = function() return {fieldSilo, farm, c1, c2} end + local clustered = {} + for _, silo in ipairs(S.collectBuildings(adEnv)) do table.insert(clustered, silo.id) end + check("gårdens klynge (105 m via nabo) er ikke marksilo", table.concat(clustered, ","), "fs") + ADBuildings.list = function() return {fieldSilo, farm} end + ADFields.fieldSilos = function() return {fieldSilo, farm} end + -- M5: rest under 100 l tæller ikke som last residue = 50 local vehicles = S.collectVehicles(adEnv) diff --git a/tests/test_adNames.lua b/tests/test_adNames.lua new file mode 100644 index 0000000..ca4bd6e --- /dev/null +++ b/tests/test_adNames.lua @@ -0,0 +1,20 @@ +-- Kør: luajit tests/test_adNames.lua (fra repo-roden) +dofile("FS25_ADSmartPickup/adNames.lua") +local failures = 0 +local function check(name, actual, expected) + if actual == expected then print("OK " .. name) else + failures = failures + 1 + print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) + end +end +local byId = ADNames.fromRecords({{id = "placeableA", name = "Gårdsilo 10"}, {id = "", name = "x"}, {id = "placeableB", name = " "}, {id = "placeableC"}}) +check("navn indlæst", byId.placeableA, "Gårdsilo 10") +check("tomt id springes over", byId[""], nil) +check("blankt navn springes over", byId.placeableB, nil) +check("manglende navn springes over", byId.placeableC, nil) +ADNames.byId = byId +check("opslag på bygning", ADNames.forPlaceable({uniqueId = "placeableA"}), "Gårdsilo 10") +check("bygning uden eget navn", ADNames.forPlaceable({uniqueId = "placeableZ"}), nil) +check("uden uniqueId", ADNames.forPlaceable({}), nil) +print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) +os.exit(failures == 0 and 0 or 1)