-- AD Runs -- Rene funktioner (ingen spil-API) for "runs": navn, aflæsningsmål, varer, Wait-pulje, -- loop-tæller og tildelte traktorer. Alle funktioner returnerer en NY store — -- input røres aldrig, så GUI og controller kan holde referencer uden overraskelser. ADRuns = {} ADRuns.MAX_LOOPS = 99 -- "Kør hjem fra Wait når målet er under X %": 10..90, standard 50 (= den gamle 50 %-regel). ADRuns.DEFAULT_RETURN_BELOW_PERCENT = 50 ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT = 10, 90 ADRuns.XML_VERSION = 1 ADRuns.INGREDIENT_MODES = {auto = true, manual = true} local function copyList(list) local copy = {} for index, value in ipairs(list or {}) do copy[index] = value end return copy end local function copyRun(run) return { id = run.id, name = run.name, targetWayPointId = run.targetWayPointId, targetWayPointIds = copyList(run.targetWayPointIds), ingredientMode = run.ingredientMode, ingredients = copyList(run.ingredients), waitPoolGroup = run.waitPoolGroup, loops = run.loops, returnBelowPercent = run.returnBelowPercent, vehicleIds = copyList(run.vehicleIds), } end local function copyStore(store) local runs = {} for index, run in ipairs(store.runs) do runs[index] = copyRun(run) end return {nextId = store.nextId, runs = runs} end local function clampPercent(percent) local value = tonumber(percent) or ADRuns.DEFAULT_RETURN_BELOW_PERCENT return math.max(ADRuns.MIN_RETURN_BELOW_PERCENT, math.min(ADRuns.MAX_RETURN_BELOW_PERCENT, math.floor(value))) end local function clampLoops(loops) local value = tonumber(loops) or 0 return math.max(0, math.min(ADRuns.MAX_LOOPS, math.floor(value))) end function ADRuns.newStore() return {nextId = 1, runs = {}} end function ADRuns.createRun(store, name) local new = copyStore(store) local run = { id = new.nextId, name = name, targetWayPointId = nil, targetWayPointIds = {}, ingredientMode = "auto", ingredients = {}, waitPoolGroup = "", loops = 0, returnBelowPercent = ADRuns.DEFAULT_RETURN_BELOW_PERCENT, vehicleIds = {}, } new.nextId = new.nextId + 1 table.insert(new.runs, run) return new, run end function ADRuns.getRun(store, runId) for _, run in ipairs(store.runs) do if run.id == runId then return run end end return nil end function ADRuns.deleteRun(store, runId) local new = copyStore(store) for index, run in ipairs(new.runs) do if run.id == runId then table.remove(new.runs, index) break end end return new end -- changes: delmængde af {name, targetWayPointId, ingredientMode, ingredients, waitPoolGroup, loops}. function ADRuns.updateRun(store, runId, changes) local new = copyStore(store) local run = ADRuns.getRun(new, runId) if run == nil then return new end if changes.name ~= nil then run.name = changes.name end -- Mål: liste (flere stalde pr. run); targetWayPointId = første = "primært" (bagudkompatibelt). -- false = ryd målene (nil i changes betyder "rør ikke") if changes.targetWayPointIds ~= nil then run.targetWayPointIds = copyList(changes.targetWayPointIds) elseif changes.targetWayPointId == false then run.targetWayPointIds = {} elseif changes.targetWayPointId ~= nil then run.targetWayPointIds = {changes.targetWayPointId} end run.targetWayPointId = run.targetWayPointIds[1] if ADRuns.INGREDIENT_MODES[changes.ingredientMode] then run.ingredientMode = changes.ingredientMode end if changes.ingredients ~= nil then run.ingredients = copyList(changes.ingredients) end if changes.waitPoolGroup ~= nil then run.waitPoolGroup = changes.waitPoolGroup end if changes.loops ~= nil then run.loops = clampLoops(changes.loops) end if changes.returnBelowPercent ~= nil then run.returnBelowPercent = clampPercent(changes.returnBelowPercent) end return new end -- Tilføj/fjern et mål (afkrydsning i fanen). function ADRuns.toggleTarget(store, runId, wayPointId) local run = ADRuns.getRun(store, runId) if run == nil then return copyStore(store) end local targets = {} local found = false for _, id in ipairs(run.targetWayPointIds) do if id == wayPointId then found = true else table.insert(targets, id) end end if not found then table.insert(targets, wayPointId) end return ADRuns.updateRun(store, runId, {targetWayPointIds = targets}) end function ADRuns.findRunByVehicle(store, vehicleId) for _, run in ipairs(store.runs) do for _, id in ipairs(run.vehicleIds) do if id == vehicleId then return run end end end return nil end function ADRuns.unassignVehicle(store, vehicleId) local new = copyStore(store) for _, run in ipairs(new.runs) do for index, id in ipairs(run.vehicleIds) do if id == vehicleId then table.remove(run.vehicleIds, index) break end end end return new end -- En traktor er kun i ét run. Returnerer ny store + id på det run den blev flyttet FRA (nil = ingen). function ADRuns.assignVehicle(store, runId, vehicleId) local previous = ADRuns.findRunByVehicle(store, vehicleId) if previous ~= nil and previous.id == runId then return copyStore(store), nil end local new = ADRuns.unassignVehicle(store, vehicleId) local run = ADRuns.getRun(new, runId) if run == nil then return new, nil end table.insert(run.vehicleIds, vehicleId) return new, previous ~= nil and previous.id or nil end -- Varer gemmes som mellemrumsadskilte fillType-NAVNE ("STRAW FORAGE") — navne er -- stabile på tværs af mods/kort, fillType-indeks er det ikke. function ADRuns.serializeIngredients(list) return table.concat(list or {}, " ") end function ADRuns.parseIngredients(text) local list = {} for name in string.gmatch(tostring(text or ""), "%S+") do table.insert(list, name) end return list end -- Flade poster (kun strenge/tal) til XML-laget, i store'ens rækkefølge. function ADRuns.toRecords(store) local records = {} for index, run in ipairs(store.runs) do records[index] = { id = run.id, name = run.name, target = run.targetWayPointId or 0, targets = table.concat(run.targetWayPointIds, " "), ingredientMode = run.ingredientMode, ingredients = ADRuns.serializeIngredients(run.ingredients), waitPool = run.waitPoolGroup, loops = run.loops, returnBelow = run.returnBelowPercent, vehicleIds = copyList(run.vehicleIds), } end return records end -- Poster uden gyldigt id/navn eller med dublet-id springes over; nextId = højeste id + 1. function ADRuns.fromRecords(records) local store = ADRuns.newStore() for _, record in ipairs(records or {}) do local id = tonumber(record.id) local isValid = id ~= nil and id >= 1 and record.name ~= nil and record.name ~= "" if isValid and ADRuns.getRun(store, id) == nil then local target = tonumber(record.target) local targets = {} for token in string.gmatch(tostring(record.targets or ""), "%S+") do local wayPointId = tonumber(token) if wayPointId ~= nil and wayPointId > 0 then table.insert(targets, wayPointId) end end if #targets == 0 and target ~= nil and target > 0 then targets = {target} end table.insert(store.runs, { id = id, name = record.name, targetWayPointId = targets[1], targetWayPointIds = targets, ingredientMode = ADRuns.INGREDIENT_MODES[record.ingredientMode] and record.ingredientMode or "auto", ingredients = ADRuns.parseIngredients(record.ingredients), waitPoolGroup = record.waitPool or "", loops = clampLoops(record.loops), returnBelowPercent = clampPercent(record.returnBelow), vehicleIds = copyList(record.vehicleIds), }) store.nextId = math.max(store.nextId, id + 1) end end return store end