diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua
index dcd6c80..0cbb5f7 100644
--- a/FS25_ADSmartPickup/adSmartPickup.lua
+++ b/FS25_ADSmartPickup/adSmartPickup.lua
@@ -10,6 +10,11 @@
ADSmartPickup = {}
+ADSmartPickup.dir = g_currentModDirectory
+if ADSmartPickup.dir ~= nil and source ~= nil then
+ source(ADSmartPickup.dir .. "gui/SmartPickupFrame.lua")
+end
+
-- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et
-- vogntog bagved. Siloer tættere end dette regnes som "markørens silo".
ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE = 40
@@ -671,6 +676,76 @@ local function restoreStoppedVehicles()
end
end
+-- Indsætter fanen i ESC-menuen foran Statistik/Priser (mønster fra FS25_FarmOperationsDashboard).
+local function insertInGameMenuPage(frame, pageName, iconFile, uvs, position, predicate)
+ local inGameMenu = g_gui ~= nil and g_gui.screenControllers ~= nil and g_gui.screenControllers[InGameMenu] or nil
+ if inGameMenu == nil or inGameMenu.pagingElement == nil then
+ return false
+ end
+ local insertAt = position
+ for i = 1, #inGameMenu.pagingElement.elements do
+ local child = inGameMenu.pagingElement.elements[i]
+ if child == inGameMenu.pageStatistics or child == inGameMenu.pagePrices then
+ insertAt = i
+ break
+ end
+ end
+ if inGameMenu.controlIDs ~= nil then
+ inGameMenu.controlIDs[pageName] = nil
+ end
+ inGameMenu[pageName] = frame
+ inGameMenu.pagingElement:addElement(frame)
+ if inGameMenu.exposeControlsAsFields ~= nil then
+ inGameMenu:exposeControlsAsFields(pageName)
+ end
+ for _, list in ipairs({inGameMenu.pagingElement.elements, inGameMenu.pageFrames}) do
+ for i = 1, #list do
+ if list[i] == frame then
+ table.remove(list, i)
+ table.insert(list, insertAt, frame)
+ break
+ end
+ end
+ end
+ for i = 1, #inGameMenu.pagingElement.pages do
+ if inGameMenu.pagingElement.pages[i].element == frame then
+ local page = table.remove(inGameMenu.pagingElement.pages, i)
+ table.insert(inGameMenu.pagingElement.pages, insertAt, page)
+ break
+ end
+ end
+ inGameMenu.pagingElement:updateAbsolutePosition()
+ inGameMenu.pagingElement:updatePageMapping()
+ inGameMenu:registerPage(frame, position, predicate)
+ inGameMenu:addPageTab(frame, iconFile, GuiUtils.getUVs(uvs))
+ inGameMenu:rebuildTabList()
+ return true
+end
+
+local function loadGui()
+ if g_gui == nil or SmartPickupFrame == nil or ADSmartPickup.dir == nil then
+ return
+ end
+ g_gui:loadProfiles(ADSmartPickup.dir .. "gui/guiProfiles.xml")
+ local frame = SmartPickupFrame.new(g_i18n, getAutoDriveEnv)
+ g_gui:loadGui(ADSmartPickup.dir .. "gui/SmartPickupFrame.xml", "smartPickupFrame", frame, true)
+ local iconFile = Utils.getFilename("icon.dds", ADSmartPickup.dir)
+ if not insertInGameMenuPage(frame, "smartPickupFrame", iconFile, {0, 0, 512, 512}, 2, function() return g_currentMission ~= nil end) then
+ Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke indsætte fanen i ESC-menuen")
+ return
+ end
+ frame:initialize()
+ ADSmartPickup.guiFrame = frame
+ Logging.info(ADSmartPickup.LOG_PREFIX .. "ESC-fane 'Smart Pickup' registreret")
+end
+
+function ADSmartPickup:loadMap()
+ local ok, err = pcall(loadGui)
+ if not ok then
+ Logging.warning(ADSmartPickup.LOG_PREFIX .. "GUI kunne ikke indlæses (resten af moden virker): %s", tostring(err))
+ end
+end
+
function ADSmartPickup:update(dt)
if not isHooked then
local adEnv = getAutoDriveEnv()
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
new file mode 100644
index 0000000..b44168d
--- /dev/null
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
@@ -0,0 +1,407 @@
+-- Smart Pickup — ESC-fane
+-- Viser og redigerer runs og starter/stopper dem. Al logik ligger i ADRunsController;
+-- her oversættes kun mellem GUI-elementer og run-felter. Alt kører i pcall så en GUI-fejl
+-- aldrig tager spillet med sig.
+
+SmartPickupFrame = {}
+SmartPickupFrame._mt = Class(SmartPickupFrame, TabbedMenuFrameElement)
+
+SmartPickupFrame.REFRESH_INTERVAL_MS = 1000
+SmartPickupFrame.LOG_PREFIX = "ADSmartPickup: "
+SmartPickupFrame.STATE_OFF, SmartPickupFrame.STATE_ON = 1, 2
+
+function SmartPickupFrame.new(i18n, getAdEnv)
+ local self = SmartPickupFrame:superClass().new(nil, SmartPickupFrame._mt)
+ self.name = "SmartPickupFrame"
+ self.i18n = i18n
+ self.getAdEnv = getAdEnv
+ self.selectedRunId = nil
+ self.runIds, self.targetIds, self.poolGroups = {}, {}, {}
+ self.rowData = {} -- element -> {kind = "ingredient"|"vehicle", ...}
+ self.clonedRows = {}
+ self.refreshTimer = 0
+ self.statusOverride = nil
+ self.backButtonInfo = {inputAction = InputAction.MENU_BACK}
+ self.startButtonInfo = {inputAction = InputAction.MENU_ACTIVATE, text = i18n:getText("spu_button_start"), callback = function() self:onClickStartStop() end}
+ self.newButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_button_new"), callback = function() self:onClickNew() end}
+ self.renameButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_rename"), callback = function() self:onClickRename() end}
+ self.deleteButtonInfo = {inputAction = InputAction.MENU_CANCEL, text = i18n:getText("spu_button_delete"), callback = function() self:onClickDelete() end}
+ self:setMenuButtonInfo({self.backButtonInfo, self.startButtonInfo, self.newButtonInfo, self.renameButtonInfo, self.deleteButtonInfo})
+ return self
+end
+
+function SmartPickupFrame:delete()
+ SmartPickupFrame:superClass().delete(self)
+end
+
+function SmartPickupFrame:copyAttributes(src)
+ SmartPickupFrame:superClass().copyAttributes(self, src)
+ self.i18n, self.getAdEnv = src.i18n, src.getAdEnv
+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", "poolPrefab"}) do
+ local prefab = self[name]
+ if prefab ~= nil then
+ prefab:unlinkElement()
+ FocusManager:removeElement(prefab)
+ end
+ end
+ self.ingredientModeOption:setTexts({self.i18n:getText("spu_mode_auto"), self.i18n:getText("spu_mode_manual")})
+ local loopTexts = {self.i18n:getText("spu_loops_infinite")}
+ for loops = 1, ADRuns.MAX_LOOPS do
+ table.insert(loopTexts, tostring(loops))
+ end
+ self.loopsOption:setTexts(loopTexts)
+end
+
+function SmartPickupFrame:onFrameOpen()
+ SmartPickupFrame:superClass().onFrameOpen(self)
+ self.statusOverride = nil
+ self:safe(self.rebuild)
+ FocusManager:setFocus(self.runOption)
+end
+
+function SmartPickupFrame:onFrameClose()
+ SmartPickupFrame:superClass().onFrameClose(self)
+end
+
+-- Live-opdatering af status/traktor-rækker mens siden er åben (uden at genopbygge rækkerne).
+function SmartPickupFrame:update(dt)
+ SmartPickupFrame:superClass().update(self, dt)
+ self.refreshTimer = self.refreshTimer + dt
+ if self.refreshTimer >= SmartPickupFrame.REFRESH_INTERVAL_MS then
+ self.refreshTimer = 0
+ self:safe(self.refreshLive)
+ end
+end
+
+function SmartPickupFrame:safe(fn, ...)
+ local ok, err = pcall(fn, self, ...)
+ if not ok then
+ Logging.warning(SmartPickupFrame.LOG_PREFIX .. "GUI-fejl: %s", tostring(err))
+ self.statusOverride = string.format(self.i18n:getText("spu_status_error"), tostring(err))
+ if self.statusText ~= nil then
+ self.statusText:setText(self.statusOverride)
+ end
+ end
+end
+
+function SmartPickupFrame:getRun()
+ return self.selectedRunId ~= nil and ADRuns.getRun(ADRunsController.store, self.selectedRunId) or nil
+end
+
+-- ---- opbygning ----
+
+function SmartPickupFrame:clearRows()
+ for _, row in ipairs(self.clonedRows) do
+ row:delete()
+ end
+ self.clonedRows, self.rowData = {}, {}
+end
+
+function SmartPickupFrame:addRow(prefab, parent, data)
+ local row = prefab:clone(parent)
+ FocusManager:loadElementFromCustomValues(row)
+ table.insert(self.clonedRows, row)
+ local option = row:getDescendantByName("option")
+ if option ~= nil then
+ option:setTexts({self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")})
+ self.rowData[option] = data
+ end
+ return row
+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
+ end
+ self:clearRows()
+ self:fillRunOption(adEnv)
+ local run = self:getRun()
+ local hasRun = run ~= nil and adEnv ~= nil
+ for _, element in ipairs({self.targetOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption}) do
+ element:setDisabled(not hasRun)
+ end
+ if hasRun then
+ self:fillTargetOption(adEnv, run)
+ self.ingredientModeOption:setState(run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, true)
+ self:fillIngredientRows(adEnv, run)
+ self:fillWaitPoolOption(adEnv, run)
+ self.loopsOption:setState(run.loops + 1, true)
+ self:fillVehicleRows(adEnv, run)
+ end
+ self:fillPoolRows(adEnv)
+ for _, box in ipairs({self.ingredientBox, self.vehicleBox, self.poolBox, self.boxLayout}) do
+ box:invalidateLayout()
+ end
+ self:refreshLive()
+end
+
+function SmartPickupFrame:fillRunOption(adEnv)
+ local texts, index = {}, 1
+ self.runIds = {}
+ for position, run in ipairs(ADRunsController.store.runs) 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)
+ if run.id == self.selectedRunId then
+ index = position
+ end
+ end
+ if #texts == 0 then
+ texts = {self.i18n:getText("spu_noRuns")}
+ end
+ self.runOption:setTexts(texts)
+ self.runOption:setState(index, true)
+ self.runOption:setDisabled(#self.runIds == 0)
+end
+
+function SmartPickupFrame:fillTargetOption(adEnv, run)
+ local texts, index = {self.i18n:getText("spu_noTarget")}, 1
+ self.targetIds = {false}
+ for _, marker in ipairs(ADRunsController.getTargetMarkers(adEnv, g_currentMission:getFarmId())) do
+ table.insert(texts, tostring(marker.name))
+ table.insert(self.targetIds, marker.id)
+ if marker.id == run.targetWayPointId then
+ index = #texts
+ end
+ end
+ self.targetOption:setTexts(texts)
+ self.targetOption:setState(index, true)
+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, self.ingredientBox, {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, true)
+ end
+end
+
+function SmartPickupFrame:fillWaitPoolOption(adEnv, run)
+ local texts, index = {self.i18n:getText("spu_waitPool_all")}, 1
+ self.poolGroups = {""}
+ for _, pool in ipairs(ADRunsController.getWaitPools(adEnv)) do
+ table.insert(texts, string.format("%s (%d)", pool.group, pool.count))
+ table.insert(self.poolGroups, pool.group)
+ if pool.group == run.waitPoolGroup then
+ index = #texts
+ end
+ end
+ self.waitPoolOption:setTexts(texts)
+ self.waitPoolOption:setState(index, true)
+end
+
+function SmartPickupFrame:fillVehicleRows(adEnv, run)
+ local inRun = {}
+ for _, id in ipairs(run.vehicleIds) do
+ inRun[id] = true
+ end
+ for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
+ local id = ADRunsController.getVehicleId(vehicle)
+ local row = self:addRow(self.vehiclePrefab, self.vehicleBox, {kind = "vehicle", vehicle = vehicle, row = nil})
+ self.rowData[row:getDescendantByName("option")].row = row
+ row:getDescendantByName("option"):setState(inRun[id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, true)
+ row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, vehicle, inRun[id]))
+ end
+end
+
+-- "Fastrac 2135 4WS (#1) — 1.000.000 l — ✔" / "… — ✖ ingen vogn"; reservationsnote fra Start bevares.
+function SmartPickupFrame:describeVehicle(adEnv, run, vehicle, isInRun)
+ local label = ADRunsController.getVehicleLabel(adEnv, vehicle)
+ if not isInRun then
+ return label
+ end
+ local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
+ local capacity = validation.capacity ~= nil and string.format(self.i18n:getText("spu_capacity"), g_i18n:formatNumber(validation.capacity, 0)) or nil
+ local parts = {label}
+ if capacity ~= nil then
+ table.insert(parts, capacity)
+ end
+ if validation.ok then
+ table.insert(parts, "✔")
+ else
+ table.insert(parts, "✖ " .. self.i18n:getText("spu_reason_" .. tostring(validation.reason)))
+ end
+ return table.concat(parts, " — ")
+end
+
+function SmartPickupFrame:fillPoolRows(adEnv)
+ if adEnv == nil then
+ return
+ end
+ local occupied = {}
+ for _, claim in ipairs(ADUnloadWait.getClaims()) do
+ occupied[claim.marker.group] = occupied[claim.marker.group] or {}
+ table.insert(occupied[claim.marker.group], ADRunsController.getVehicleLabel(adEnv, claim.vehicle))
+ end
+ for _, pool in ipairs(ADRunsController.getWaitPools(adEnv)) do
+ local names = occupied[pool.group] or {}
+ local suffix = #names > 0 and (" (" .. table.concat(names, ", ") .. ")") or ""
+ local row = self:addRow(self.poolPrefab, self.poolBox, {kind = "pool"})
+ row:getDescendantByName("title"):setText(string.format(self.i18n:getText("spu_waitPoolRow"), pool.group, pool.count, #names, suffix))
+ end
+end
+
+-- Status + Start/Stop-knappens tekst, kaldes hvert sekund.
+function SmartPickupFrame:refreshLive()
+ local adEnv = self.getAdEnv()
+ local run = self:getRun()
+ if adEnv == nil then
+ self.statusText:setText(self.i18n:getText("spu_status_noAd"))
+ return
+ end
+ local isRunning = false
+ if run ~= nil then
+ 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)
+ or string.format(self.i18n:getText("spu_status_stopped"), status.total)
+ self.statusText:setText(self.statusOverride or text)
+ for option, data in pairs(self.rowData) do
+ if data.kind == "vehicle" and data.row ~= nil then
+ data.row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, data.vehicle, option:getState() == SmartPickupFrame.STATE_ON))
+ end
+ end
+ else
+ self.statusText:setText(self.statusOverride or self.i18n:getText("spu_noRuns"))
+ end
+ self.startButtonInfo.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start")
+ self.startButtonInfo.disabled = run == nil
+ self.renameButtonInfo.disabled = run == nil
+ self.deleteButtonInfo.disabled = run == nil
+ self:setMenuButtonInfoDirty()
+end
+
+-- ---- callbacks fra XML (state = 1-baseret indeks; element = det klikkede element) ----
+
+function SmartPickupFrame:onClickRun(state)
+ self.selectedRunId = self.runIds[state]
+ self.statusOverride = nil
+ self:safe(self.rebuild)
+end
+
+function SmartPickupFrame:onClickTarget(state)
+ local run = self:getRun()
+ if run == nil then return end
+ -- targetIds[1] er false = "(vælg mål)"; modellen tolker false som "ryd målet"
+ ADRunsController.updateRun(run.id, {targetWayPointId = self.targetIds[state] or false})
+ self:safe(self.rebuild)
+end
+
+function SmartPickupFrame:onClickIngredientMode(state)
+ local run = self:getRun()
+ if run == nil then return end
+ ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"})
+ self:safe(self.rebuild)
+end
+
+function SmartPickupFrame:onClickIngredient(state, element)
+ local run, data = self:getRun(), self.rowData[element]
+ if run == nil or data == nil then return end
+ local names = {}
+ for _, name in ipairs(run.ingredients) do
+ if name ~= data.name then table.insert(names, name) end
+ end
+ if state == SmartPickupFrame.STATE_ON then
+ table.insert(names, data.name)
+ end
+ ADRunsController.updateRun(run.id, {ingredients = names})
+end
+
+function SmartPickupFrame:onClickWaitPool(state)
+ local run = self:getRun()
+ if run == nil then return end
+ ADRunsController.updateRun(run.id, {waitPoolGroup = self.poolGroups[state] or ""})
+end
+
+function SmartPickupFrame:onClickLoops(state)
+ local run = self:getRun()
+ if run == nil then return end
+ ADRunsController.updateRun(run.id, {loops = state - 1})
+end
+
+function SmartPickupFrame:onClickVehicle(state, element)
+ local run, data = self:getRun(), self.rowData[element]
+ if run == nil or data == nil then return end
+ if state == SmartPickupFrame.STATE_ON then
+ local previousRunId = ADRunsController.assignVehicle(run.id, data.vehicle)
+ if previousRunId ~= nil then
+ local previous = ADRuns.getRun(ADRunsController.store, previousRunId)
+ self.statusOverride = string.format(self.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId))
+ end
+ else
+ ADRunsController.unassignVehicle(data.vehicle)
+ end
+ self:safe(self.refreshLive)
+end
+
+-- ---- knapper i bundlinjen ----
+
+function SmartPickupFrame:onClickStartStop()
+ self:safe(function(frame)
+ local adEnv, run = frame.getAdEnv(), frame:getRun()
+ if adEnv == nil or run == nil then return end
+ if ADRunsController.getRunStatus(adEnv, run).running > 0 then
+ ADRunsController.stopRun(adEnv, run.id)
+ frame.statusOverride = nil
+ else
+ local started, skipped = 0, 0
+ for _, result in ipairs(ADRunsController.startRun(adEnv, run.id)) do
+ if result.ok then started = started + 1 else skipped = skipped + 1 end
+ end
+ frame.statusOverride = string.format(frame.i18n:getText("spu_status_started"), started, skipped)
+ end
+ ADRunsController.save()
+ frame:rebuild()
+ end)
+end
+
+function SmartPickupFrame:onClickNew()
+ self:safe(function(frame)
+ local run = ADRunsController.createRun(string.format(frame.i18n:getText("spu_newRunName"), ADRunsController.store.nextId - 1))
+ frame.selectedRunId = run.id
+ frame.statusOverride = nil
+ frame:rebuild()
+ end)
+end
+
+function SmartPickupFrame:onClickRename()
+ local run = self:getRun()
+ if run == nil then return end
+ TextInputDialog.show(SmartPickupFrame.onRenameResult, self, run.name, self.i18n:getText("spu_renameTitle"), nil, 30, g_i18n:getText("button_ok"))
+end
+
+function SmartPickupFrame:onRenameResult(value, clickOk)
+ local run = self:getRun()
+ if clickOk and run ~= nil and value ~= nil and value:match("%S") then
+ ADRunsController.updateRun(run.id, {name = value})
+ self:safe(self.rebuild)
+ end
+end
+
+function SmartPickupFrame:onClickDelete()
+ local run = self:getRun()
+ if run == nil then return end
+ YesNoDialog.show(SmartPickupFrame.onDeleteResult, self, string.format(self.i18n:getText("spu_deleteConfirm"), run.name), self.i18n:getText("spu_button_delete"))
+end
+
+function SmartPickupFrame:onDeleteResult(yes)
+ local run = self:getRun()
+ if yes and run ~= nil then
+ ADRunsController.deleteRun(run.id)
+ self.selectedRunId = nil
+ self:safe(self.rebuild)
+ end
+end
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml
new file mode 100644
index 0000000..76d1c52
--- /dev/null
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/gui/guiProfiles.xml b/FS25_ADSmartPickup/gui/guiProfiles.xml
new file mode 100644
index 0000000..e75ec5c
--- /dev/null
+++ b/FS25_ADSmartPickup/gui/guiProfiles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
new file mode 100644
index 0000000..16d65fe
--- /dev/null
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
new file mode 100644
index 0000000..ea4064e
--- /dev/null
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index e23508f..29b968b 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -10,6 +10,7 @@
icon.dds
+