feat: adBuildings — gårdens bygninger med automatisk markør ved læsse-/aflæsningstrigger
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
-- AD Buildings
|
||||
-- Gårdens bygninger som brugeren vælger imellem (i stedet for markører): fabrik, silo, stald,
|
||||
-- objektlager og salgssted, hver med den AD-markør der står nærmest bygningens læsse- og
|
||||
-- aflæsningstrigger (≤ MAX_MARKER_DISTANCE; Wait-markører aldrig; siloers læssemarkør via det
|
||||
-- strenge opslag, så tætte rækker ikke blandes). Spil-adapter med 1 s cache.
|
||||
|
||||
ADBuildings = {}
|
||||
|
||||
ADBuildings.LOG_PREFIX = "ADSmartPickup: "
|
||||
ADBuildings.MAX_MARKER_DISTANCE = 40
|
||||
ADBuildings.CACHE_MS = 1000
|
||||
|
||||
local cache = {list = nil, farmId = nil, time = -math.huge}
|
||||
|
||||
local function now()
|
||||
if g_currentMission ~= nil and type(g_currentMission.time) == "number" then
|
||||
return g_currentMission.time
|
||||
end
|
||||
return os.clock() * 1000
|
||||
end
|
||||
|
||||
local function getNodePosition(node)
|
||||
if node == nil or (entityExists ~= nil and not entityExists(node)) then
|
||||
return nil
|
||||
end
|
||||
local x, _, z = getWorldTranslation(node)
|
||||
return x, z
|
||||
end
|
||||
|
||||
local function getName(placeable, fallback)
|
||||
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
|
||||
end
|
||||
return fallback
|
||||
end
|
||||
|
||||
function ADBuildings.getId(placeable)
|
||||
if placeable.uniqueId ~= nil then
|
||||
return tostring(placeable.uniqueId)
|
||||
end
|
||||
local x, z = getNodePosition(placeable.rootNode)
|
||||
if x == nil then
|
||||
return nil
|
||||
end
|
||||
return string.format("pos:%d:%d", math.floor(x + 0.5), math.floor(z + 0.5))
|
||||
end
|
||||
|
||||
local function getProductionPoint(placeable)
|
||||
return placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil
|
||||
end
|
||||
|
||||
local function isOwnedBy(placeable, farmId)
|
||||
if placeable.getOwnerFarmId ~= nil then
|
||||
local ok, owner = pcall(placeable.getOwnerFarmId, placeable)
|
||||
return ok and owner == farmId
|
||||
end
|
||||
return placeable.ownerFarmId == farmId
|
||||
end
|
||||
|
||||
local function getKind(placeable)
|
||||
if getProductionPoint(placeable) ~= nil then return "factory" end
|
||||
if placeable.spec_silo ~= nil then return "silo" end
|
||||
if placeable.getHusbandryFillLevel ~= nil then return "husbandry" end
|
||||
if placeable.spec_objectStorage ~= nil then return "objectStorage" end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Trigger-noder pr. bygning: læsse (loadTriggers.triggerNode, pallespawner, objektlager) og
|
||||
-- aflæsning (unloadTriggers exactFillRootNode/triggerNode/aiNode).
|
||||
local function collectNodes(placeable, loadStations, unloadStations)
|
||||
local loadNodes, unloadNodes = {}, {}
|
||||
for _, station in pairs(loadStations) do
|
||||
if station.owningPlaceable == placeable then
|
||||
for _, trigger in pairs(station.loadTriggers or {}) do
|
||||
if trigger.triggerNode ~= nil then table.insert(loadNodes, trigger.triggerNode) end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, station in pairs(unloadStations) do
|
||||
if station.owningPlaceable == placeable then
|
||||
for _, trigger in pairs(station.unloadTriggers or {}) do
|
||||
for _, field in ipairs({"exactFillRootNode", "triggerNode", "aiNode"}) do
|
||||
if trigger[field] ~= nil then table.insert(unloadNodes, trigger[field]) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local productionPoint = getProductionPoint(placeable)
|
||||
local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil
|
||||
if spawner ~= nil then
|
||||
for _, place in ipairs(spawner.spawnPlaces or {}) do
|
||||
if place.startNode ~= nil then table.insert(loadNodes, place.startNode) end
|
||||
end
|
||||
end
|
||||
if placeable.spec_objectStorage ~= nil and placeable.rootNode ~= nil then
|
||||
table.insert(loadNodes, placeable.rootNode)
|
||||
table.insert(unloadNodes, placeable.rootNode)
|
||||
end
|
||||
return loadNodes, unloadNodes
|
||||
end
|
||||
|
||||
-- Nærmeste ikke-Wait-markør til en af noderne, ≤ MAX_MARKER_DISTANCE.
|
||||
local function nearestMarker(adEnv, nodes)
|
||||
local best, bestDistance = nil, ADBuildings.MAX_MARKER_DISTANCE
|
||||
if #nodes == 0 then
|
||||
return nil
|
||||
end
|
||||
local positions = {}
|
||||
for _, node in ipairs(nodes) do
|
||||
local x, z = getNodePosition(node)
|
||||
if x ~= nil then table.insert(positions, {x = x, z = z}) end
|
||||
end
|
||||
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
|
||||
if not ADSupplyPlanner.isWaitMarker(marker) then
|
||||
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
|
||||
if wayPoint ~= nil then
|
||||
for _, position in ipairs(positions) do
|
||||
local dx, dz = wayPoint.x - position.x, wayPoint.z - position.z
|
||||
local distance = math.sqrt(dx * dx + dz * dz)
|
||||
if distance < bestDistance then
|
||||
best, bestDistance = marker, distance
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if best == nil then
|
||||
return nil
|
||||
end
|
||||
return {id = best.id, name = tostring(best.name), distance = math.floor(bestDistance + 0.5)}
|
||||
end
|
||||
|
||||
-- Silo: den markør hvis STRENGE opslag (trigger bag næsen) rammer siloens læssestation.
|
||||
local function siloLoadMarker(adEnv, farmId, placeable, loadStations)
|
||||
local own = {}
|
||||
for _, station in pairs(loadStations) do
|
||||
if station.owningPlaceable == placeable then own[station] = true end
|
||||
end
|
||||
local best = nil
|
||||
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
|
||||
if not ADSupplyPlanner.isWaitMarker(marker) then
|
||||
local station = ADSmartPickup.getLoadStationAtWayPointId(adEnv, marker.id, farmId)
|
||||
if station ~= nil and own[station] then
|
||||
best = {id = marker.id, name = tostring(marker.name), distance = 0}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
local function isSellPlaceable(placeable, unloadStations)
|
||||
for _, station in pairs(unloadStations) do
|
||||
if station.owningPlaceable == placeable and ADPrices.isSellingStation(station) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local ORDER = {factory = 1, husbandry = 2, objectStorage = 3, silo = 4, sell = 5}
|
||||
|
||||
local function build(adEnv, farmId)
|
||||
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
|
||||
local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil
|
||||
local loadStations = storage ~= nil and storage:getLoadingStations() or {}
|
||||
local unloadStations = storage ~= nil and storage:getUnloadingStations() or {}
|
||||
local result = {}
|
||||
for _, placeable in pairs(placeables) do
|
||||
local kind = getKind(placeable)
|
||||
local isSell = kind == nil and isSellPlaceable(placeable, unloadStations)
|
||||
if isSell then kind = "sell" end
|
||||
if kind ~= nil and (kind == "sell" or isOwnedBy(placeable, farmId)) then
|
||||
local id = ADBuildings.getId(placeable)
|
||||
if id ~= nil then
|
||||
local loadNodes, unloadNodes = collectNodes(placeable, loadStations, unloadStations)
|
||||
local loadMarker
|
||||
if kind == "silo" then
|
||||
loadMarker = siloLoadMarker(adEnv, farmId, placeable, loadStations)
|
||||
else
|
||||
loadMarker = nearestMarker(adEnv, loadNodes)
|
||||
end
|
||||
table.insert(result, {
|
||||
id = id, name = getName(placeable, id), kind = kind, placeable = placeable,
|
||||
loadMarker = loadMarker,
|
||||
unloadMarker = nearestMarker(adEnv, unloadNodes),
|
||||
canBeSource = kind ~= "sell",
|
||||
canBeTarget = kind == "husbandry" or kind == "factory",
|
||||
canSell = kind == "sell",
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(result, function(a, b)
|
||||
if ORDER[a.kind] ~= ORDER[b.kind] then return ORDER[a.kind] < ORDER[b.kind] end
|
||||
return a.name < b.name
|
||||
end)
|
||||
return result
|
||||
end
|
||||
|
||||
function ADBuildings.invalidate()
|
||||
cache.list, cache.time = nil, -math.huge
|
||||
end
|
||||
|
||||
function ADBuildings.list(adEnv, farmId)
|
||||
local time = now()
|
||||
if cache.list ~= nil and cache.farmId == farmId and (time - cache.time) < ADBuildings.CACHE_MS then
|
||||
return cache.list
|
||||
end
|
||||
cache.list, cache.farmId, cache.time = build(adEnv, farmId), farmId, time
|
||||
return cache.list
|
||||
end
|
||||
|
||||
function ADBuildings.find(adEnv, farmId, id)
|
||||
for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do
|
||||
if building.id == id then return building end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- role "load": bygningen hvis læssemarkør er wayPointId; "unload": aflæsningsmarkør.
|
||||
function ADBuildings.byMarker(adEnv, farmId, wayPointId, role)
|
||||
for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do
|
||||
local marker = role == "unload" and building.unloadMarker or building.loadMarker
|
||||
if marker ~= nil and marker.id == wayPointId then return building end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
Reference in New Issue
Block a user