diff --git a/README.md b/README.md index dd959153..35160775 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # FS22_AutoDrive FS22 version of the AutoDrive mod -### Latest Release: 2.0.1.0 +### Latest Release: 2.0.1.2 ![GitHub all releases](https://img.shields.io/github/downloads/Stephan-S/FS22_AutoDrive/total?label=Downloads&style=plastic) [Latest Release](https://github.com/Stephan-S/FS22_AutoDrive/releases/latest) -Direct Download: https://github.com/Stephan-S/FS22_AutoDrive/releases/download/2.0.1.0/FS22_AutoDrive.zip +Direct Download: https://github.com/Stephan-S/FS22_AutoDrive/releases/download/2.0.1.2/FS22_AutoDrive.zip ## Discord Server: For help & support, feel free to join us on Discord: diff --git a/modDesc.xml b/modDesc.xml index e856e7a6..71756057 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -1,5 +1,5 @@ - + AutoDrive Team <en>AutoDrive</en> @@ -32,7 +32,7 @@ Différents modes d'utilisation ont été ajoutés depuis les premières version <![CDATA[Этот мод может быть использован для создания сети маршрутов для транспортных средств для автономного вождения. После настройки вы можете указать трактору, стоящему где-угодно рядом с сетью, проехать в любую точку, например, в магазин, поле №1 или в точку продажи.]]> </ru> </description> - <version>2.0.1.0</version> + <version>2.0.1.2</version> <multiplayer supported="true" /> <iconFilename>icon.dds</iconFilename> <extraSourceFiles> @@ -92,6 +92,7 @@ Différents modes d'utilisation ont été ajoutés depuis les premières version <action name="ADToggleHudExtension" category="VEHICLE" ignoreComboMask="false" /> <action name="ADToggleAutomaticUnloadTarget" category="VEHICLE" ignoreComboMask="false" /> <action name="ADToggleAutomaticPickupTarget" category="VEHICLE" ignoreComboMask="false" /> + <action name="ADToggleLoadByFillLevel" category="VEHICLE" ignoreComboMask="false" /> <action name="ADRepairVehicle" category="VEHICLE" ignoreComboMask="false" /> </actions> diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index d9e17fbc..e4415a40 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -1,5 +1,5 @@ AutoDrive = {} -AutoDrive.version = "2.0.1.0" +AutoDrive.version = "2.0.1.2" AutoDrive.directory = g_currentModDirectory @@ -11,7 +11,7 @@ AutoDrive.experimentalFeatures = {} AutoDrive.experimentalFeatures.redLinePosition = false -- AutoDrive.experimentalFeatures.telemetryOutput = false AutoDrive.experimentalFeatures.enableRoutesManagerOnDediServer = false -AutoDrive.experimentalFeatures.detectGrasField = true +AutoDrive.experimentalFeatures.detectSwath = true AutoDrive.experimentalFeatures.colorAssignmentMode = false AutoDrive.experimentalFeatures.UTurn = true AutoDrive.experimentalFeatures.FoldImplements = true @@ -289,7 +289,7 @@ function AutoDrive:refreshContextInputAIFrame() end function AutoDrive:drawBaseMission() - if AutoDrive.aiFrameOpen then + if AutoDrive.aiFrameOpen then AutoDrive:drawRouteOnMap() AutoDrive.drawNetworkOnMap() if AutoDrive.aiFrameVehicle ~= nil then @@ -355,20 +355,20 @@ function AutoDrive.drawRouteOnMap() if lastWp ~= nil and index >= currentWp then local startX, startY, _, _ = AutoDrive.getScreenPosFromWorldPos(lastWp.x, lastWp.z) local endX, endY, _, _ = AutoDrive.getScreenPosFromWorldPos(wp.x, wp.z) - + if startX and startY and endX and endY then dx2D = endX - startX; dy2D = ( endY - startY ) / g_screenAspectRatio; width = MathUtil.vector2Length(dx2D, dy2D); - + dx = wp.x - lastWp.x; dz = wp.z - lastWp.z; rotation = MathUtil.getYRotationFromDirection(dx, dz) - math.pi * 0.5; - + local lineThickness = 2 / g_screenHeight setOverlayColor( AutoDrive.courseOverlayId, 0.3, 0.5, 0.56, 1) setOverlayRotation( AutoDrive.courseOverlayId, rotation, 0, 0) - + renderOverlay( AutoDrive.courseOverlayId, startX, startY, width, lineThickness ) end setOverlayRotation( AutoDrive.courseOverlayId, 0, 0, 0 ) -- reset overlay rotation @@ -407,34 +407,34 @@ function AutoDrive.drawNetworkOnMap() local outNode = network[outNodeId] local startX, startY, _, _ = AutoDrive.getScreenPosFromWorldPos(node.x, node.z) local endX, endY, _, _ = AutoDrive.getScreenPosFromWorldPos(outNode.x, outNode.z) - + if startX and startY and endX and endY then dx2D = endX - startX; dy2D = ( endY - startY ) / g_screenAspectRatio; width = MathUtil.vector2Length(dx2D, dy2D); - + dx = outNode.x - node.x; dz = outNode.z - node.z; rotation = MathUtil.getYRotationFromDirection(dx, dz) - math.pi * 0.5; - + local lineThickness = 2 / g_screenHeight local r, g, b, a = unpack(AutoDrive.currentColors.ad_color_singleConnection) - + if isSubPrio(outNode) then r, g, b, a = unpack(AutoDrive.currentColors.ad_color_subPrioSingleConnection) end - + if ADGraphManager:isDualRoad(node, outNode) then r, g, b, a = unpack(AutoDrive.currentColors.ad_color_dualConnection) if isSubPrio(outNode) then r, g, b, a = unpack(AutoDrive.currentColors.ad_color_subPrioDualConnection) - end - elseif ADGraphManager:isReverseRoad(start, target) then + end + elseif ADGraphManager:isReverseRoad(start, target) then r, g, b, a = unpack(AutoDrive.currentColors.ad_color_reverseConnection) end setOverlayColor( AutoDrive.courseOverlayId, r, g, b, a) setOverlayRotation( AutoDrive.courseOverlayId, rotation, 0, 0) - + renderOverlay( AutoDrive.courseOverlayId, startX, startY, width, lineThickness ) end setOverlayRotation( AutoDrive.courseOverlayId, 0, 0, 0 ) -- reset overlay rotation @@ -448,7 +448,7 @@ function AutoDrive.getScreenPosFromWorldPos(worldX, worldZ) local objectX = (worldX + AutoDrive.aiFrame.ingameMapBase.worldCenterOffsetX) / AutoDrive.aiFrame.ingameMapBase.worldSizeX * 0.5 + 0.25 local objectZ = (worldZ + AutoDrive.aiFrame.ingameMapBase.worldCenterOffsetZ) / AutoDrive.aiFrame.ingameMapBase.worldSizeZ * 0.5 + 0.25 local x, y, _, _ = AutoDrive.aiFrame.ingameMapBase.layout:getMapObjectPosition(objectX, objectZ, 0, 0, 0, true) - + return x, y end @@ -557,7 +557,7 @@ end function AutoDrive:mouseEvent(posX, posY, isDown, isUp, button) local vehicle = AutoDrive.getADFocusVehicle() local mouseActiveForAutoDrive = (g_gui.currentGui == nil or AutoDrive.aiFrameOpen) and (g_inputBinding:getShowMouseCursor() == true) - + if not mouseActiveForAutoDrive then AutoDrive.lastButtonDown = nil return @@ -591,7 +591,7 @@ function AutoDrive:mouseEvent(posX, posY, isDown, isUp, button) end end -function AutoDrive:update(dt) +function AutoDrive:update(dt) if AutoDrive.scanDialogState == AutoDrive.SCAN_DIALOG_NONE and ADGraphManager:getWayPointsCount() == 0 then if g_server ~= nil and g_dedicatedServer == nil then -- open dialog @@ -657,7 +657,7 @@ function AutoDrive:update(dt) ADTriggerManager:update(dt) ADRoutesManager:update(dt) - -- AutoDrive.handleTelemetry(dt) + -- AutoDrive.handleTelemetry(dt) end function AutoDrive:draw() diff --git a/scripts/Events/HudInputEvent.lua b/scripts/Events/HudInputEvent.lua index e3df68ad..6e93ec1d 100644 --- a/scripts/Events/HudInputEvent.lua +++ b/scripts/Events/HudInputEvent.lua @@ -2,6 +2,8 @@ AutoDriveHudInputEventEvent = {} AutoDriveHudInputEventEvent.TYPE_FIRST_MARKER = 1 AutoDriveHudInputEventEvent.TYPE_SECOND_MARKER = 2 AutoDriveHudInputEventEvent.TYPE_FILLTYPE = 3 +AutoDriveHudInputEventEvent.TYPE_TOGGLE_FILLTYPE_SELECTION = 4 +AutoDriveHudInputEventEvent.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS = 5 AutoDriveHudInputEventEvent_mt = Class(AutoDriveHudInputEventEvent, Event) @@ -53,6 +55,14 @@ function AutoDriveHudInputEventEvent:run(connection) if self.eventType == self.TYPE_FILLTYPE then self.vehicle.ad.stateModule:setFillType(self.value) end + + if self.eventType == self.TYPE_TOGGLE_FILLTYPE_SELECTION then + self.vehicle.ad.stateModule:toggleFillTypeSelection(self.value) + end + + if self.eventType == self.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS then + self.vehicle.ad.stateModule:toggleAllFillTypeSelections(self.value) + end end end @@ -76,3 +86,17 @@ function AutoDriveHudInputEventEvent:sendFillTypeEvent(vehicle, fillTypeId) g_client:getServerConnection():sendEvent(AutoDriveHudInputEventEvent.new(vehicle, self.TYPE_FILLTYPE, fillTypeId)) end end + +function AutoDriveHudInputEventEvent:sendToggleFillTypeSelectionEvent(vehicle, fillTypeId) + if g_client ~= nil then + -- Client have to send to server + g_client:getServerConnection():sendEvent(AutoDriveHudInputEventEvent.new(vehicle, self.TYPE_TOGGLE_FILLTYPE_SELECTION, fillTypeId)) + end +end + +function AutoDriveHudInputEventEvent:sendToggleAllFillTypeSelectionsEvent(vehicle, fillTypeId) + if g_client ~= nil then + -- Client have to send to server + g_client:getServerConnection():sendEvent(AutoDriveHudInputEventEvent.new(vehicle, self.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS, fillTypeId)) + end +end diff --git a/scripts/ExternalInterface.lua b/scripts/ExternalInterface.lua index 3de1db5a..70ac2923 100644 --- a/scripts/ExternalInterface.lua +++ b/scripts/ExternalInterface.lua @@ -609,7 +609,16 @@ function AutoDrive:hasAL(object) if object == nil then return false end - return object.spec_aPalletAutoLoader ~= nil and object.spec_aPalletAutoLoader.loadArea ~= nil and object.spec_aPalletAutoLoader.loadArea["baseNode"] ~= nil + local ret = false + ret = ret or (object.spec_aPalletAutoLoader ~= nil and object.spec_aPalletAutoLoader.loadArea ~= nil and object.spec_aPalletAutoLoader.loadArea["baseNode"] ~= nil) + if (object.spec_universalAutoload ~= nil) then + local rootVehicle = object.getRootVehicle and object:getRootVehicle() + if rootVehicle and not rootVehicle.spec_locomotive then + -- use only bulk trailers of trains for now + ret = ret or object.spec_universalAutoload.isAutoloadEnabled + end + end + return ret end --[[ @@ -623,10 +632,20 @@ function AutoDrive:setALOn(object) end local spec = object.spec_aPalletAutoLoader if spec and object.SetLoadingState then - -- set loading state off + -- set loading state On AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALOn SetLoadingState 2") object:SetLoadingState(2) end + spec = object.spec_universalAutoload + if spec and object.ualStartLoad then + local rootVehicle = object.getRootVehicle and object:getRootVehicle() + if rootVehicle and rootVehicle.lastSpeedReal < 0.0005 then + -- loading not possible during movement + -- set loading state On + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALOn ualStartLoad") + object:ualStartLoad() + end + end end function AutoDrive:setALOff(object) @@ -640,6 +659,12 @@ function AutoDrive:setALOff(object) AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALOff SetLoadingState 1") object:SetLoadingState(1) end + local spec = object.spec_universalAutoload + if spec and object.ualStopLoad then + -- set loading state off + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALOff ualStopLoad") + object:ualStopLoad() + end end function AutoDrive.activateALTrailers(vehicle, trailers) @@ -684,32 +709,55 @@ function AutoDrive:unloadAL(object) end AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL start") local rootVehicle = object:getRootVehicle() - local unloadPositions = { - 3, - 1, - 4, - 2 - } - - local unloadPositionSetting = AutoDrive.getSetting("ALUnload", rootVehicle) - AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL unloadPositionSetting %s", tostring(unloadPositionSetting)) - if unloadPositionSetting ~= nil and unloadPositionSetting > 0 then - local unloadPosition = unloadPositions[unloadPositionSetting] - AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL unloadPosition %s", tostring(unloadPosition)) - if unloadPosition ~= nil then - -- should unload - AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL should unload") - if object.setAllTensionBeltsActive ~= nil then - object:setAllTensionBeltsActive(false, false) - end - local spec = object.spec_aPalletAutoLoader - if spec and object.SetTipside and object.unloadAll then - AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL SetTipside unloadPosition %s", tostring(unloadPosition)) - if unloadPosition > 0 then - object:SetTipside(unloadPosition) + -- spec_aPalletAutoLoader + local spec = object.spec_aPalletAutoLoader + if spec and object.SetTipside and object.unloadAll then + local unloadPositions = { + 3, + 1, + 4, + 2 + } + + local unloadPositionSetting = AutoDrive.getSetting("ALUnload", rootVehicle) + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_aPalletAutoLoader unloadPositionSetting %s", tostring(unloadPositionSetting)) + if unloadPositionSetting ~= nil and unloadPositionSetting > 0 then + local unloadPosition = unloadPositions[unloadPositionSetting] + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_aPalletAutoLoader unloadPosition %s", tostring(unloadPosition)) + if unloadPosition ~= nil then + -- should unload + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_aPalletAutoLoader should unload") + if object.setAllTensionBeltsActive ~= nil then + object:setAllTensionBeltsActive(false, false) end - -- set loading state off - object:unloadAll() + if object.SetTipside and object.unloadAll then + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_aPalletAutoLoader SetTipside unloadPosition %s", tostring(unloadPosition)) + if unloadPosition > 0 then + object:SetTipside(unloadPosition) + end + -- set loading state off + object:unloadAll() + end + end + end + end + -- spec_universalAutoload + local spec = object.spec_universalAutoload + if spec and object.ualUnload then + local unloadPositions = { + "center", + "left", + "behind", + "right" + } + local unloadPositionSetting = AutoDrive.getSetting("ALUnload", rootVehicle) + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_universalAutoload unloadPositionSetting %s", tostring(unloadPositionSetting)) + if unloadPositionSetting ~= nil and unloadPositionSetting > 0 then + local unloadPosition = unloadPositions[unloadPositionSetting] + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:unloadAL spec_universalAutoload unloadPosition %s", tostring(unloadPosition)) + if unloadPosition ~= nil and string.len(unloadPosition) > 0 then + object:ualSetUnloadPosition(unloadPosition) + object:ualUnload() end end end @@ -739,15 +787,20 @@ function AutoDrive:getALObjectFillLevels(object) -- used by getIsFillUnitEmpty, Logging.error("[AD] AutoDrive.getALObjectFillLevels rootVehicle == nil") return 0, 0, false, 0 end - AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALObjectFillLevels object.spec_aPalletAutoLoader %s ", tostring(object.spec_aPalletAutoLoader)) + AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALObjectFillLevels object.spec_aPalletAutoLoader %s object.spec_universalAutoload %s", tostring(object.spec_aPalletAutoLoader), tostring(object.spec_universalAutoload)) local fillCapacity = 0 local fillLevel = 0 local fillFreeCapacity = 0 - local spec = object.spec_aPalletAutoLoader - if spec and AutoDrive:hasAL(object) and object.getFillUnitCapacity and object.getFillUnitFillLevel and object.getFillUnitFreeCapacity then - fillCapacity = object:getFillUnitCapacity() - fillLevel = object:getFillUnitFillLevel() - fillFreeCapacity = object:getFillUnitFreeCapacity() + if AutoDrive:hasAL(object) then + if object.spec_aPalletAutoLoader and object.getFillUnitCapacity and object.getFillUnitFillLevel and object.getFillUnitFreeCapacity then + fillCapacity = object:getFillUnitCapacity() + fillLevel = object:getFillUnitFillLevel() + fillFreeCapacity = object:getFillUnitFreeCapacity() + elseif object.spec_universalAutoload and object.ualGetFillUnitCapacity and object.ualGetFillUnitFillLevel and object.ualGetFillUnitFreeCapacity then + fillCapacity = object:ualGetFillUnitCapacity() + fillLevel = object:ualGetFillUnitFillLevel() + fillFreeCapacity = object:ualGetFillUnitFreeCapacity() + end AutoDrive.debugPrint(object, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALObjectFillLevels fillCapacity %s fillLevel %s fillFreeCapacity %s", tostring(fillCapacity), tostring(fillLevel), tostring(fillFreeCapacity)) end local filledToUnload = AutoDrive.isUnloadFillLevelReached(rootVehicle, fillLevel, fillFreeCapacity, fillCapacity) @@ -760,6 +813,7 @@ function AutoDrive:getALFillTypes(object) -- used by PullDownList, getSupportedF end local fillTypes = {} + -- spec_aPalletAutoLoader local spec = object.spec_aPalletAutoLoader if spec and AutoDrive:hasAL(object) and object.GetAutoloadTypes then local autoLoadTypes = object:GetAutoloadTypes() @@ -771,6 +825,11 @@ function AutoDrive:getALFillTypes(object) -- used by PullDownList, getSupportedF end end end + -- spec_universalAutoload + local spec = object.spec_universalAutoload + if spec and AutoDrive:hasAL(object) then + AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALCurrentFillType spec_universalAutoload function not supported!") + end AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALFillTypes #fillTypes %s", tostring(#fillTypes)) return fillTypes end @@ -780,10 +839,16 @@ function AutoDrive:getALCurrentFillType(object) -- used by onEnterVehicle, onPos return nil end + -- spec_aPalletAutoLoader local spec = object.spec_aPalletAutoLoader if spec and AutoDrive:hasAL(object) and spec.currentautoLoadTypeIndex then return spec.currentautoLoadTypeIndex end + -- spec_universalAutoload + local spec = object.spec_universalAutoload + if spec and AutoDrive:hasAL(object) then + AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:getALCurrentFillType spec_universalAutoload function not supported!") + end return nil end @@ -796,13 +861,19 @@ function AutoDrive:setALFillType(vehicle, fillType) -- used by PullDownList if trailerCount > 0 then for i=1, trailerCount do local object = trailers[i] + -- spec_aPalletAutoLoader local spec = object.spec_aPalletAutoLoader if spec and AutoDrive:hasAL(object) and object.SetLoadingState and object.SetAutoloadType then - AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALFillType fillType %s", tostring(fillType)) + AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALFillType spec_aPalletAutoLoader fillType %s", tostring(fillType)) -- set loading state off object:SetLoadingState(1) object:SetAutoloadType(fillType) end + -- spec_universalAutoload + local spec = object.spec_universalAutoload + if spec and AutoDrive:hasAL(object) then + AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive:setALFillType spec_universalAutoload function not supported!") + end end end end diff --git a/scripts/Gui/ExperimentalFeaturesSettingsPage.lua b/scripts/Gui/ExperimentalFeaturesSettingsPage.lua index f55037e2..06a9c696 100644 --- a/scripts/Gui/ExperimentalFeaturesSettingsPage.lua +++ b/scripts/Gui/ExperimentalFeaturesSettingsPage.lua @@ -14,7 +14,6 @@ ADExperimentalFeaturesSettingsPage.CONTROLS = {"settingsContainer", "headerIcon" function ADExperimentalFeaturesSettingsPage:new(target) local element = TabbedMenuFrameElement.new(target, ADExperimentalFeaturesSettingsPage_mt) element.returnScreenName = "" - element.experimentalFeaturesElements = {} element:registerControls(ADExperimentalFeaturesSettingsPage.CONTROLS) return element end @@ -42,7 +41,6 @@ function ADExperimentalFeaturesSettingsPage:onCreate() cloned:setState(stateNumber) -- focusId has to be unique, but is copied by clone element, so generate and assign a new unique focusId cloned.focusId = "adFocus_" .. featureName - table.insert(self.experimentalFeaturesElements, cloned) end self.cloneElement:delete() end @@ -78,7 +76,7 @@ function ADExperimentalFeaturesSettingsPage:getMainElementPosition() end function ADExperimentalFeaturesSettingsPage:updateElementsState() - for _, element in pairs(self.experimentalFeaturesElements) do + for _, element in pairs(self.boxLayout.elements) do local stateNumber = 1 if AutoDrive.experimentalFeatures[element.name] then stateNumber = 2 diff --git a/scripts/Hud.lua b/scripts/Hud.lua index 88774940..5dd8620e 100644 --- a/scripts/Hud.lua +++ b/scripts/Hud.lua @@ -193,7 +193,8 @@ function AutoDriveHud:createHudAt(hudX, hudY) table.insert(self.hudElements, ADPullDownList:new(self.posX + 2 * self.gapWidth + self.buttonWidth, self.row3, self.iconWidth * 6 + self.gapWidth * 5, self.listItemHeight, ADPullDownList.TYPE_UNLOAD, 1)) - table.insert(self.hudElements, ADHudIcon:new(self.posX + self.gapWidth, self.row2, self.iconWidth, self.iconHeight, AutoDrive.directory .. "textures/fruit_overlay.dds", 1, "fruitOverlay")) + table.insert(self.hudElements, ADHudButton:new(self.posX + self.gapWidth, self.row2, self.iconWidth, self.iconHeight, "input_toggleLoadByFillLevel", nil, nil, nil, "input_ADToggleLoadByFillLevel", 1, true)) + --table.insert(self.hudElements, ADHudIcon:new(self.posX + self.gapWidth, self.row2, self.iconWidth, self.iconHeight, AutoDrive.directory .. "textures/fruit_overlay_1.dds", 1, "fruitOverlay")) table.insert( self.hudElements, diff --git a/scripts/Hud/HudButton.lua b/scripts/Hud/HudButton.lua index 65e339bd..43560a8d 100644 --- a/scripts/Hud/HudButton.lua +++ b/scripts/Hud/HudButton.lua @@ -228,6 +228,17 @@ function ADHudButton:getNewState(vehicle) end end + if self.primaryAction == "input_toggleLoadByFillLevel" then + self.isVisible = vehicle.ad.stateModule:getMode() == AutoDrive.MODE_LOAD or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_PICKUPANDDELIVER + if #vehicle.ad.stateModule:getSelectedFillTypes() <= 1 then + newState = 1 + elseif vehicle.ad.stateModule:getLoadByFillLevel() then + newState = 2 + else + newState = 3 + end + end + return newState end diff --git a/scripts/Hud/PullDownList.lua b/scripts/Hud/PullDownList.lua index a39670e9..f987a493 100644 --- a/scripts/Hud/PullDownList.lua +++ b/scripts/Hud/PullDownList.lua @@ -229,20 +229,28 @@ function ADPullDownList:onDraw(vehicle, uiScale) end end end + local isSelectedFillType = false + if self.type == ADPullDownList.TYPE_FILLTYPE then + isSelectedFillType = table.contains(vehicle.ad.stateModule:getSelectedFillTypes(), listEntry.returnValue) + end - if self.hovered == self.selected + (i - 1) and listEntry.isFolder == false then - setTextBold(false) - setTextColor(0, 0.871, 1, 1) - elseif self.hovered == self.selected + (i - 1) and listEntry.isFolder == true then -- folders mouse over - setTextBold(true) - setTextColor(0.296, 0.823, 1, 1) - else - if listEntry.isFolder == false then - setTextBold(false) - setTextColor(1, 1, 1, 1) + if self.hovered == self.selected + (i - 1) then + -- mouse hovering over selected item + if listEntry.isFolder or isSelectedFillType then + setTextBold(true) + setTextColor(0.296, 0.823, 1, 1) else + setTextBold(false) + setTextColor(0, 0.871, 1, 1) + end + else + -- other element + if listEntry.isFolder or isSelectedFillType then setTextBold(true) setTextColor(0.0, 0.569, 0.835, 1) + else + setTextBold(false) + setTextColor(1, 1, 1, 1) end end @@ -486,18 +494,19 @@ function ADPullDownList:createSelection_FillType() local vehicle = AutoDrive.getADFocusVehicle() if vehicle ~= nil then - local hasAL = false local trailers, _ = AutoDrive.getAllUnits(vehicle) - for _, trailer in ipairs(trailers) do - hasAL = hasAL or AutoDrive:hasAL(trailer) - end supportedFillTypes = {} - if hasAL then - -- AutoLoad - for _, trailer in ipairs(trailers) do - self.autoLoadFillTypes = AutoDrive:getALFillTypes(trailer) + for _, trailer in ipairs(trailers) do + if AutoDrive:hasAL(trailer) then + local alFillTypes = AutoDrive:getALFillTypes(trailer) + if alFillTypes ~= nil and #alFillTypes > 0 then + -- self.autoLoadFillTypes is either nil or it contains items. It is never empty. + self.autoLoadFillTypes = alFillTypes + break + end end - else + end + if self.autoLoadFillTypes == nil then supportedFillTypes = AutoDrive.getValidSupportedFillTypes(vehicle) end end @@ -508,7 +517,7 @@ function ADPullDownList:createSelection_FillType() local itemListIndex = 1 local lastIndexReached = false self.options[1][itemListIndex] = {displayName = "", returnValue = 0} - if self.autoLoadFillTypes ~= nil and #self.autoLoadFillTypes > 0 then + if self.autoLoadFillTypes ~= nil then -- AutoLoad for i = 1, #self.autoLoadFillTypes do self.options[1][itemListIndex] = {displayName = self.autoLoadFillTypes[i], returnValue = i} @@ -575,7 +584,7 @@ function ADPullDownList:getNewState_FillType(vehicle) local newState = self.state local newSelection = self.selected if self.state == ADPullDownList.STATE_COLLAPSED then - if self.autoLoadFillTypes ~= nil and #self.autoLoadFillTypes > 0 then + if self.autoLoadFillTypes ~= nil then -- AutoDrive.debugMsg(vehicle, "ADPullDownList:getNewState_FillType 0 self.text %s", tostring(self.text)) if vehicle.ad.stateModule:getFillType() <= #self.autoLoadFillTypes then self.text = self.autoLoadFillTypes[vehicle.ad.stateModule:getFillType()] @@ -612,7 +621,23 @@ function ADPullDownList:act(vehicle, posX, posY, isDown, isUp, button) local hitElement, hitIndex, hitIcon = self:getElementAt(vehicle, posX, posY) if button == 1 and isUp then -- left mouse button - if self.state == ADPullDownList.STATE_EXPANDED and self.type ~= ADPullDownList.TYPE_FILLTYPE and AutoDrive.isEditorModeEnabled() and AutoDrive.getSetting("useFolders") and self.dragged ~= nil and self.startedDraggingTimer > 200 then + if self.state == ADPullDownList.STATE_EXPANDED and self.type == ADPullDownList.TYPE_FILLTYPE and AutoDrive.leftCTRLmodifierKeyPressed then + -- left Ctrl click on open filltype list. Toggle item, except for auto-load + if self.autoLoadFillTypes == nil then + local value = self:getHoverEntryReturnValue(vehicle) + if value ~= nil then + AutoDriveHudInputEventEvent:sendToggleFillTypeSelectionEvent(vehicle, value) + end + end + elseif self.state == ADPullDownList.STATE_EXPANDED and self.type == ADPullDownList.TYPE_FILLTYPE and AutoDrive.leftALTmodifierKeyPressed then + -- left Alt click on open filltype list. Toggle item, except for auto-load + if self.autoLoadFillTypes == nil then + local value = self:getHoverEntryReturnValue(vehicle) + if value ~= nil then + AutoDriveHudInputEventEvent:sendToggleAllFillTypeSelectionsEvent(vehicle, value) + end + end + elseif self.state == ADPullDownList.STATE_EXPANDED and self.type ~= ADPullDownList.TYPE_FILLTYPE and AutoDrive.isEditorModeEnabled() and AutoDrive.getSetting("useFolders") and self.dragged ~= nil and self.startedDraggingTimer > 200 then -- drag element to hitElement if hitElement ~= nil then self:sortDraggedInGroup(self.draggedElement, hitElement) @@ -739,6 +764,16 @@ function ADPullDownList:expand(vehicle) end end +function ADPullDownList:getHoverEntryReturnValue(vehicle) + if self.hovered ~= nil then + local selectedEntry = self:getListElementByIndex(vehicle, self.hovered) + if selectedEntry ~= nil and selectedEntry.returnValue ~= nil and selectedEntry.isFolder == false then + return selectedEntry.returnValue + end + end + return nil +end + function ADPullDownList:collapse(vehicle, setItem) if self.state == ADPullDownList.STATE_EXPANDED then self.layer = self.layer - 1 @@ -760,7 +795,7 @@ function ADPullDownList:collapse(vehicle, setItem) elseif self.type == ADPullDownList.TYPE_FILLTYPE then -- AutoDrive.debugMsg(vehicle, "ADPullDownList:collapse self.hovered %s selectedEntry.returnValue %s", tostring(self.hovered), tostring(selectedEntry.returnValue)) AutoDriveHudInputEventEvent:sendFillTypeEvent(vehicle, selectedEntry.returnValue) - if self.autoLoadFillTypes ~= nil and #self.autoLoadFillTypes > 0 then + if self.autoLoadFillTypes ~= nil then -- AutoLoad AutoDrive:setALFillType(vehicle, selectedEntry.returnValue) end diff --git a/scripts/Manager/InputManager.lua b/scripts/Manager/InputManager.lua index bb346c58..8abec1a9 100644 --- a/scripts/Manager/InputManager.lua +++ b/scripts/Manager/InputManager.lua @@ -55,6 +55,7 @@ ADInputManager.actionsToInputs = { {"ADToggleHudExtension", "input_toggleHudExtension", true, false, true, 1}, {"ADToggleAutomaticUnloadTarget", "input_toggleAutomaticUnloadTarget", true, true}, {"ADToggleAutomaticPickupTarget", "input_toggleAutomaticPickupTarget", true, true}, + {"ADToggleLoadByFillLevel", "input_toggleLoadByFillLevel", true, true}, {"ADRepairVehicle", "input_repairVehicle", false, true} } @@ -490,6 +491,10 @@ function ADInputManager:input_toggleAutomaticPickupTarget(vehicle) vehicle.ad.stateModule:toggleAutomaticPickupTarget() end +function ADInputManager:input_toggleLoadByFillLevel(vehicle) + vehicle.ad.stateModule:toggleLoadByFillLevel() +end + function ADInputManager:input_devAction(vehicle) if AutoDrive.devAction ~= nil then AutoDrive.devAction(vehicle) diff --git a/scripts/Manager/UserDataManager.lua b/scripts/Manager/UserDataManager.lua index 283fc3de..d08665d8 100644 --- a/scripts/Manager/UserDataManager.lua +++ b/scripts/Manager/UserDataManager.lua @@ -28,6 +28,7 @@ end function ADUserDataManager:load() self.userSettingNames = self:getUserSettingNames() + self.isSinglePlayerOrHost = (not g_currentMission.missionDynamicInfo.isMultiplayer) or (g_currentMission.missionDynamicInfo.isMultiplayer and not g_currentMission.missionDynamicInfo.isClient) end function ADUserDataManager:loadFromXml() @@ -56,7 +57,7 @@ function ADUserDataManager:loadFromXml() uIndex = uIndex + 1 end - if not g_currentMission.missionDynamicInfo.isMultiplayer then + if self.isSinglePlayerOrHost then -- no client, use a single player user local uniqueId = ADUserDataManager.SinglePlayer if self.users[uniqueId] ~= nil then @@ -88,7 +89,7 @@ function ADUserDataManager:saveToXml() local file = g_currentMission.missionInfo.savegameDirectory .. "/AutoDriveUsersData.xml" local xmlFile = createXMLFile("AutoDriveUsersData_XML_temp", file, "AutoDriveUsersData") - if not g_currentMission.missionDynamicInfo.isMultiplayer then + if self.isSinglePlayerOrHost then -- no client, create a single player user ID local uniqueId = ADUserDataManager.SinglePlayer diff --git a/scripts/Modes/PickupAndDeliverMode.lua b/scripts/Modes/PickupAndDeliverMode.lua index f25381c4..5ad2ce87 100644 --- a/scripts/Modes/PickupAndDeliverMode.lua +++ b/scripts/Modes/PickupAndDeliverMode.lua @@ -22,7 +22,6 @@ end function PickupAndDeliverMode:reset() AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "PickupAndDeliverMode:reset set STATE_INIT") self.state = PickupAndDeliverMode.STATE_INIT - self.vehicle.ad.stateModule:setLoopsDone(0) self.activeTask = nil self.trailers, self.trailerCount = AutoDrive.getAllUnits(self.vehicle) self.vehicle.ad.trailerModule:reset() @@ -243,7 +242,7 @@ function PickupAndDeliverMode:getNextTask(forced) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "PickupAndDeliverMode:getNextTask self.state %s NO nextTask assigned !!!", tostring(self.state)) nextTask = StopAndDisableADTask:new(self.vehicle, ADTaskModule.DONT_PROPAGATE) self.state = PickupAndDeliverMode.STATE_FINISHED - AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_has_reached; %s", 5000, self.vehicle.ad.stateModule:getName(), self.vehicle.ad.stateModule:getFirstMarkerName()) + AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_has_reached; %s", 5000, self.vehicle.ad.stateModule:getName(), "???") end AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "PickupAndDeliverMode:getNextTask end loopsDone %s self.state %s", tostring(self.vehicle.ad.stateModule:getLoopsDone()), tostring(self.state)) diff --git a/scripts/Modules/PathFinderModule.lua b/scripts/Modules/PathFinderModule.lua index ca8c8ceb..87602e91 100644 --- a/scripts/Modules/PathFinderModule.lua +++ b/scripts/Modules/PathFinderModule.lua @@ -99,6 +99,8 @@ PathFinderModule.GRID_SIZE_FACTOR_SECOND_UNLOADER = 1.1 PathFinderModule.PP_MAX_EAGER_LOOKAHEAD_STEPS = 1 +PathFinderModule.MIN_FRUIT_VALUE = 50 +PathFinderModule.SLOPE_DETECTION_THRESHOLD = math.rad(20) --[[ from Giants Engine: AITurnStrategy.SLOPE_DETECTION_THRESHOLD = 0.5235987755983 @@ -356,9 +358,9 @@ function PathFinderModule:startPathPlanningTo(targetPoint, targetVector) self.goingToCombine = false self.startIsOnField = AutoDrive.checkIsOnField(vehicleWorldX, vehicleWorldY, vehicleWorldZ) and self.vehicle.ad.sensors.frontSensorField:pollInfo(true) - local endIsOnField = AutoDrive.checkIsOnField(targetX, vehicleWorldY, targetZ) + self.endIsOnField = AutoDrive.checkIsOnField(targetX, vehicleWorldY, targetZ) - self.restrictToField = AutoDrive.getSetting("restrictToField", self.vehicle) and self.startIsOnField and endIsOnField + self.restrictToField = AutoDrive.getSetting("restrictToField", self.vehicle) and self.startIsOnField and self.endIsOnField self.goingToPipe = false self.chasingVehicle = false self.isSecondChasingVehicle = false @@ -403,8 +405,9 @@ function PathFinderModule:startPathPlanningTo(targetPoint, targetVector) ) ) PathFinderModule.debugVehicleMsg(self.vehicle, - string.format("PFM startPathPlanningTo getSetting restrictToField %s", - tostring(self.restrictToField) + string.format("PFM startPathPlanningTo restrictToField %s endIsOnField %s", + tostring(self.restrictToField), + tostring(self.endIsOnField) ) ) PathFinderModule.debugVehicleMsg(self.vehicle, @@ -441,11 +444,11 @@ end function PathFinderModule:autoRestart() self.steps = 0 self.grid = {} + self.wayPoints = {} self.startCell.visited = false self.startCell.out = nil self.currentCell = nil - -- table.insert(self.grid, self.startCell) local gridKey = string.format("%d|%d|%d", self.startCell.x, self.startCell.z, self.startCell.direction) self.grid[gridKey] = self.startCell @@ -644,9 +647,6 @@ function PathFinderModule:update(dt) for i = 1, ADScheduler:getStepsPerFrame(), 1 do if self.currentCell == nil then - local minDistance = math.huge - local bestCell = nil - local bestSteps = math.huge self.currentCell = self:findClosestCell(self.grid, math.huge) @@ -844,7 +844,6 @@ function PathFinderModule:testNextCells(cell) else self:checkGridCell(location) end - -- table.insert(self.grid, location) gridKey = string.format("%d|%d|%d", location.x, location.z, location.direction) self.grid[gridKey] = location end @@ -864,8 +863,7 @@ function PathFinderModule:checkGridCell(cell) if cell.incoming.bordercells == 0 then -- if incoming cell is on field we check if the new is also on field - local isOnField = AutoDrive.checkIsOnField(worldPos.x, 0, worldPos.z) - if isOnField then + if cell.isOnField then -- still on field, so set the current cell counter to 0 cell.bordercells = 0 end @@ -888,8 +886,7 @@ function PathFinderModule:checkGridCell(cell) -- check the most probable restrictions on field first to prevent unneccessary checks if not cell.isRestricted and self.restrictToField and not (self.fallBackMode1 and self.fallBackMode2) then -- in fallBackMode1 we ignore the field restriction - local isOnField = AutoDrive.checkIsOnField(worldPos.x, 0, worldPos.z) - cell.isRestricted = cell.isRestricted or (not isOnField) + cell.isRestricted = cell.isRestricted or (not cell.isOnField) if cell.isRestricted then if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then @@ -920,7 +917,9 @@ function PathFinderModule:checkGridCell(cell) if not cell.isRestricted and cell.incoming ~= nil then -- check for up/down is to big or below water level local worldPosPrevious = self:gridLocationToWorldLocation(cell.incoming) - cell.hasCollision = cell.hasCollision or self:checkSlopeAngle(worldPos.x, worldPos.z, worldPosPrevious.x, worldPosPrevious.z) --> true if up/down is to big or below water level + local angelToSlope, angle = self:checkSlopeAngle(worldPos.x, worldPos.z, worldPosPrevious.x, worldPosPrevious.z) --> true if up/down or roll is to big or below water level + cell.angle = angle + cell.hasCollision = cell.hasCollision or angelToSlope end if not cell.isRestricted and not cell.hasCollision then @@ -1040,7 +1039,9 @@ function PathFinderModule:determineBlockedCells(cell) end function PathFinderModule:determineNextGridCells(cell) - cell.out = {} + if cell.out == nil then + cell.out = {} + end if cell.direction == self.PP_UP then cell.out[1] = {x = cell.x + 1, z = cell.z - 1} cell.out[1].direction = self.PP_UP_LEFT @@ -1121,10 +1122,10 @@ function PathFinderModule:checkForFruitInArea(cell, corners) self.fruitToCheck = nil end if self.fruitToCheck == nil then - for i = 1, #g_fruitTypeManager.fruitTypes do - if i ~= g_fruitTypeManager.nameToIndex["GRASS"] and i ~= g_fruitTypeManager.nameToIndex["DRYGRASS"] and i ~= g_fruitTypeManager.nameToIndex["MEADOW"] then - local fruitType = g_fruitTypeManager.fruitTypes[i].index - self:checkForFruitTypeInArea(cell, fruitType, corners) + for _, fruitType in pairs(g_fruitTypeManager:getFruitTypes()) do + if not (fruitType == g_fruitTypeManager:getFruitTypeByName("MEADOW")) then + local fruitTypeIndex = fruitType.index + self:checkForFruitTypeInArea(cell, fruitTypeIndex, corners) end --stop if cell is already restricted and/or fruit type is now known if cell.isRestricted ~= false or self.fruitToCheck ~= nil then @@ -1136,17 +1137,17 @@ function PathFinderModule:checkForFruitInArea(cell, corners) end end -function PathFinderModule:checkForFruitTypeInArea(cell, fruitType, corners) +function PathFinderModule:checkForFruitTypeInArea(cell, fruitTypeIndex, corners) local fruitValue = 0 - fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitType, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true, true) + fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitTypeIndex, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true, true) - if (self.fruitToCheck == nil or self.fruitToCheck < 1) and (fruitValue > 150) then - self.fruitToCheck = fruitType + if (self.fruitToCheck == nil or self.fruitToCheck < 1) and (fruitValue > PathFinderModule.MIN_FRUIT_VALUE) then + self.fruitToCheck = fruitTypeIndex end local wasRestricted = cell.isRestricted - cell.isRestricted = cell.isRestricted or (fruitValue > 150) + cell.isRestricted = cell.isRestricted or (fruitValue > PathFinderModule.MIN_FRUIT_VALUE) - cell.hasFruit = (fruitValue > 150) + cell.hasFruit = (fruitValue > PathFinderModule.MIN_FRUIT_VALUE) cell.fruitValue = fruitValue if cell.hasFruit then @@ -1206,7 +1207,7 @@ function PathFinderModule:drawDebugForPF() local baseY = shapeDefinition.y + 3 -- corners of shape - DebugUtil.drawOverlapBox(shapeDefinition.x, shapeDefinition.y + 3, shapeDefinition.z, 0, shapeDefinition.angleRad, 0, shapeDefinition.widthX, shapeDefinition.height, shapeDefinition.widthZ, color_red, color_green, color_blue) + -- DebugUtil.drawOverlapBox(shapeDefinition.x, shapeDefinition.y + 3, shapeDefinition.z, 0, shapeDefinition.angleRad, 0, shapeDefinition.widthX, shapeDefinition.height, shapeDefinition.widthZ, color_red, color_green, color_blue) for _, corner in pairs(corners) do local point_y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, corner.x, 1, corner.z) local pointUp_y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, corner.x, 1, corner.z) + 3 @@ -1215,7 +1216,7 @@ function PathFinderModule:drawDebugForPF() -- restriction, collision line up local pointCenter = self:gridLocationToWorldLocation(cell) - local pointCenterUp = pointCenter + local pointCenterUp = {x = pointCenter.x, z = pointCenter.z} pointCenter.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointCenter.x, 1, pointCenter.z) + 3 pointCenterUp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointCenterUp.x, 1, pointCenterUp.z) + 6 @@ -1233,6 +1234,36 @@ function PathFinderModule:drawDebugForPF() local cellText = tostring(cell.x) .. " " .. tostring(cell.z) -- Utils.renderTextAtWorldPosition(pointCenter.x, pointCenter.y, pointCenter.z, cellText, getCorrectTextSize(0.013), 0) +--[[ + if cell.isRestricted then + -- red + AutoDriveDM:addLineTask(pointCenter.x, pointCenter.y, pointCenter.z, pointCenterUp.x, pointCenterUp.y, pointCenterUp.z, 1, 0, 0) + else + if cell.isOnField then + -- blue + AutoDriveDM:addLineTask(pointCenter.x, pointCenter.y, pointCenter.z, pointCenterUp.x, pointCenterUp.y, pointCenterUp.z, 0, 0, 1) + else + -- green + AutoDriveDM:addLineTask(pointCenter.x, pointCenter.y, pointCenter.z, pointCenterUp.x, pointCenterUp.y, pointCenterUp.z, 0, 1, 0) + end + end + local cellIndex = string.format("%d , %d", cell.x, cell.z) + Utils.renderTextAtWorldPosition(pointCenter.x, pointCenter.y - 2, pointCenter.z, cellIndex, getCorrectTextSize(0.013), 0) + + if cell.angle then + local value = string.format("%.1f", math.deg(cell.angle)) + if (not cell.hasCollision) and (not cell.isRestricted) then + AutoDriveDM:addLineTask(pointA.x, pointA.y, pointA.z, pointB.x, pointB.y, pointB.z, 0, 1, 0) + AutoDriveDM:addLineTask(pointC.x, pointC.y, pointC.z, pointD.x, pointD.y, pointD.z, 0, 0, 1) + end + Utils.renderTextAtWorldPosition(pointCenter.x + (cell.x % 10), pointCenter.y - 1 + (cell.steps % 10 / 5) + (cell.z % 10 / 5), pointCenter.z, value, getCorrectTextSize(0.013), 0) + end + + if cell.incoming then + local cellIncommingIndex = string.format("%d -> %d , %d", cell.steps, cell.incoming.x, cell.incoming.z) + Utils.renderTextAtWorldPosition(pointCenter.x + (cell.x % 10), pointCenter.y - 0 + (cell.steps % 10 / 5) + (cell.z % 10 / 5), pointCenter.z, cellIncommingIndex, getCorrectTextSize(0.013), 0) + end +]] if cell.isRestricted == true then -- any restriction if cell.hasFruit == true then @@ -1720,22 +1751,8 @@ function PathFinderModule:smoothResultingPPPath_Refined() stepsThisFrame = stepsThisFrame + 1 local nodeAhead = self.wayPoints[self.smoothIndex + self.totalEagerSteps + 1] local nodeTwoAhead = self.wayPoints[self.smoothIndex + self.totalEagerSteps + 2] - - local angle = AutoDrive.angleBetween({x = nodeAhead.x - node.x, z = nodeAhead.z - node.z}, {x = nodeTwoAhead.x - nodeAhead.x, z = nodeTwoAhead.z - nodeAhead.z}) - angle = math.abs(angle) - if angle > 60 then - hasCollision = true - - if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then - PathFinderModule.debugVehicleMsg(self.vehicle, - string.format("PFM smoothResultingPPPath_Refined hasCollision %d", - 1 - ) - ) - end - end - if previousNode ~= nil then - angle = AutoDrive.angleBetween({x = node.x - previousNode.x, z = node.z - previousNode.z}, {x = nodeTwoAhead.x - node.x, z = nodeTwoAhead.z - node.z}) + if not hasCollision and nodeAhead and nodeTwoAhead then + local angle = AutoDrive.angleBetween({x = nodeAhead.x - node.x, z = nodeAhead.z - node.z}, {x = nodeTwoAhead.x - nodeAhead.x, z = nodeTwoAhead.z - nodeAhead.z}) angle = math.abs(angle) if angle > 60 then hasCollision = true @@ -1743,22 +1760,37 @@ function PathFinderModule:smoothResultingPPPath_Refined() if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then PathFinderModule.debugVehicleMsg(self.vehicle, string.format("PFM smoothResultingPPPath_Refined hasCollision %d", - 2 + 1 ) ) end end - angle = AutoDrive.angleBetween({x = node.x - previousNode.x, z = node.z - previousNode.z}, {x = nodeAhead.x - node.x, z = nodeAhead.z - node.z}) - angle = math.abs(angle) - if angle > 60 then - hasCollision = true + if previousNode ~= nil then + angle = AutoDrive.angleBetween({x = node.x - previousNode.x, z = node.z - previousNode.z}, {x = nodeTwoAhead.x - node.x, z = nodeTwoAhead.z - node.z}) + angle = math.abs(angle) + if angle > 60 then + hasCollision = true - if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then - PathFinderModule.debugVehicleMsg(self.vehicle, - string.format("PFM smoothResultingPPPath_Refined hasCollision %d", - 3 + if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then + PathFinderModule.debugVehicleMsg(self.vehicle, + string.format("PFM smoothResultingPPPath_Refined hasCollision %d", + 2 + ) ) - ) + end + end + angle = AutoDrive.angleBetween({x = node.x - previousNode.x, z = node.z - previousNode.z}, {x = nodeAhead.x - node.x, z = nodeAhead.z - node.z}) + angle = math.abs(angle) + if angle > 60 then + hasCollision = true + + if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then + PathFinderModule.debugVehicleMsg(self.vehicle, + string.format("PFM smoothResultingPPPath_Refined hasCollision %d", + 3 + ) + ) + end end end end @@ -1818,7 +1850,7 @@ function PathFinderModule:smoothResultingPPPath_Refined() length = MathUtil.vector3Length(worldPos.x - worldPosPrevious.x, worldPos.y - worldPosPrevious.y, worldPos.z - worldPosPrevious.z) local angleBetween = math.atan(math.abs(worldPos.y - worldPosPrevious.y) / length) - if (angleBetween) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then + if (angleBetween) > PathFinderModule.SLOPE_DETECTION_THRESHOLD then hasCollision = true if hasCollision then @@ -1846,14 +1878,14 @@ function PathFinderModule:smoothResultingPPPath_Refined() if self.goingToNetwork then -- check for all fruit types - for i = 1, #g_fruitTypeManager.fruitTypes do - if i ~= g_fruitTypeManager.nameToIndex["GRASS"] and i ~= g_fruitTypeManager.nameToIndex["DRYGRASS"] and i ~= g_fruitTypeManager.nameToIndex["MEADOW"] then - local fruitType = g_fruitTypeManager.fruitTypes[i].index + for _, fruitType in pairs(g_fruitTypeManager:getFruitTypes()) do + if not (fruitType == g_fruitTypeManager:getFruitTypeByName("MEADOW")) then + local fruitTypeIndex = fruitType.index local fruitValue = 0 if self.isSecondChasingVehicle then - fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitType, cornerWideX, cornerWideZ, cornerWide2X, cornerWide2Z, cornerWide4X, cornerWide4Z, true, true) + fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitTypeIndex, cornerWideX, cornerWideZ, cornerWide2X, cornerWide2Z, cornerWide4X, cornerWide4Z, true, true) else - fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitType, cornerX, cornerZ, corner2X, corner2Z, corner4X, corner4Z, true, true) + fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitTypeIndex, cornerX, cornerZ, corner2X, corner2Z, corner4X, corner4Z, true, true) end hasCollision = hasCollision or (fruitValue > 50) if hasCollision then @@ -1974,6 +2006,34 @@ function PathFinderModule:checkSlopeAngle(x1, z1, x2, z2) local angleBetween = math.atan(math.abs(terrain1 - terrain2) / length) local angleBetweenCenter = math.atan(math.abs(terrain3 - terrain2) / lengthMiddle) + local angleLeft = 0 + local angleRight = 0 + + if self.cos90 == nil then + -- speed up the calculation + self.cos90 = math.cos(math.rad(90)) + self.sin90 = math.sin(math.rad(90)) + self.cos270 = math.cos(math.rad(270)) + self.sin270 = math.sin(math.rad(270)) + end + + local rotX = vectorFromPrevious.x * self.cos90 - vectorFromPrevious.z * self.sin90 + local rotZ = vectorFromPrevious.x * self.sin90 + vectorFromPrevious.z * self.cos90 + local vectorLeft = {x = rotX, z = rotZ} + + local rotX = vectorFromPrevious.x * self.cos270 - vectorFromPrevious.z * self.sin270 + local rotZ = vectorFromPrevious.x * self.sin270 + vectorFromPrevious.z * self.cos270 + local vectorRight = {x = rotX, z = rotZ} + + local worldPosLeft = {x = x1 + vectorLeft.x / 2, z = z1 + vectorLeft.z / 2} + local worldPosRight = {x = x1 + vectorRight.x / 2, z = z1 + vectorRight.z / 2} + local terrainLeft = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, worldPosLeft.x, 0, worldPosLeft.z) + local terrainRight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, worldPosRight.x, 0, worldPosRight.z) + local lengthLeft = MathUtil.vector3Length(worldPosLeft.x - x1, terrainLeft - terrain1, worldPosLeft.z - z1) + local lengthRight = MathUtil.vector3Length(worldPosRight.x - x1, terrainRight - terrain1, worldPosRight.z - z1) + angleLeft = math.atan(math.abs(terrainLeft - terrain1) / lengthLeft) + angleRight = math.atan(math.abs(terrainRight - terrain1) / lengthRight) + local waterY = g_currentMission.environmentAreaSystem:getWaterYAtWorldPosition(worldPosMiddle.x, terrain3, worldPosMiddle.z) or -200 local belowGroundLevel = terrain1 < waterY - 0.5 or terrain2 < waterY - 0.5 or terrain3 < waterY - 0.5 @@ -1989,10 +2049,10 @@ function PathFinderModule:checkSlopeAngle(x1, z1, x2, z2) end end - if (angleBetween) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then + if (angleBetween) > PathFinderModule.SLOPE_DETECTION_THRESHOLD then if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then PathFinderModule.debugVehicleMsg(self.vehicle, - string.format("PFM checkSlopeAngle (angleBetween * 1.25) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD x,z %d %d", + string.format("PFM checkSlopeAngle (angleBetween * 1.25) > PathFinderModule.SLOPE_DETECTION_THRESHOLD x,z %d %d", math.floor(x1), math.floor(z1) ) @@ -2000,10 +2060,10 @@ function PathFinderModule:checkSlopeAngle(x1, z1, x2, z2) end end - if (angleBetweenCenter) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then + if (angleBetweenCenter) > PathFinderModule.SLOPE_DETECTION_THRESHOLD then if self.vehicle ~= nil and self.vehicle.ad ~= nil and self.vehicle.ad.debug ~= nil and AutoDrive.debugVehicleMsg ~= nil then PathFinderModule.debugVehicleMsg(self.vehicle, - string.format("PFM checkSlopeAngle (angleBetweenCenter * 1.25) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD x,z %d %d", + string.format("PFM checkSlopeAngle (angleBetweenCenter * 1.25) > PathFinderModule.SLOPE_DETECTION_THRESHOLD x,z %d %d", math.floor(x1), math.floor(z1) ) @@ -2011,10 +2071,12 @@ function PathFinderModule:checkSlopeAngle(x1, z1, x2, z2) end end - if belowGroundLevel or (angleBetween) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD or (angleBetweenCenter) > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then - return true + if belowGroundLevel or (angleBetween) > PathFinderModule.SLOPE_DETECTION_THRESHOLD or (angleBetweenCenter) > PathFinderModule.SLOPE_DETECTION_THRESHOLD + or (angleLeft > PathFinderModule.SLOPE_DETECTION_THRESHOLD or angleRight > PathFinderModule.SLOPE_DETECTION_THRESHOLD) + then + return true, angleBetween end - return false + return false, angleBetween end function PathFinderModule.debugVehicleMsg(vehicle, msg, ...) diff --git a/scripts/Modules/SpecialDrivingModule.lua b/scripts/Modules/SpecialDrivingModule.lua index 6e411a62..5a0082b5 100644 --- a/scripts/Modules/SpecialDrivingModule.lua +++ b/scripts/Modules/SpecialDrivingModule.lua @@ -332,10 +332,12 @@ function ADSpecialDrivingModule:getReverseNode() for _, implement in pairs(AutoDrive.getAllImplements(self.vehicle, true)) do -- Logging.info("[AD] ADSpecialDrivingModule:getReverseNode count %s ", tostring(count)) - + if implement.ad == nil then + implement.ad = {} + end if (implement ~= self.vehicle or reverseNode == nil) and - implement.spec_wheels ~= nil and - AutoDrive.isImplementAllowedForReverseDriving(self.vehicle, implement) -- whitelist of implements allowed as reverse node + implement.spec_wheels ~= nil + -- and AutoDrive.isImplementAllowedForReverseDriving(self.vehicle, implement) -- whitelist of implements allowed as reverse node then local implementX, implementY, implementZ = getWorldTranslation(implement.components[1].node) local _, _, diffZ = worldToLocal(self.vehicle.components[1].node, implementX, implementY, implementZ) @@ -344,11 +346,13 @@ function ADSpecialDrivingModule:getReverseNode() -- if diffZ < 0 and math.abs(diffZ) >= (self.vehicle.size.length / 2) then local hasSynchronizedWheels = false + local validWheel = false local centerX, centerZ = 0,0 local wheelCount = 0 for _, wheel in pairs(implement.spec_wheels.wheels) do - hasSynchronizedWheels = hasSynchronizedWheels or wheel.isSynchronized - if wheel.isSynchronized then + validWheel = (wheel.isSynchronized and wheel.hasGroundContact) + hasSynchronizedWheels = hasSynchronizedWheels or validWheel + if validWheel then wheelCount = wheelCount + 1 local posX, _, posZ = localToLocal(wheel.node, implement.components[1].node, wheel.positionX, wheel.positionY, wheel.positionZ) centerX = centerX + posX @@ -361,16 +365,30 @@ function ADSpecialDrivingModule:getReverseNode() centerX = centerX / wheelCount centerZ = centerZ / wheelCount - implement.spec_wheels.steeringCenterNode = createTransformGroup("steeringCenterNode") + if not implement.ad.reverseNode then + implement.ad.reverseNode = createTransformGroup("reverseNode") - link(implement.components[1].node, implement.spec_wheels.steeringCenterNode) + link(implement.components[1].node, implement.ad.reverseNode) + end if centerX ~= nil and centerZ ~= nil then - setTranslation(implement.spec_wheels.steeringCenterNode, centerX, 0, centerZ) + local vehX, _, vehZ = getWorldTranslation(self.vehicle.components[1].node) + local implX, _, implZ = getWorldTranslation(implement.components[1].node) + local trailerVecX, _, trailerVecZ = localDirectionToWorld(implement.components[1].node, 0, 0, 1) + local angleToVeh = AutoDrive.angleBetween({x = vehX - implX, z = vehZ - implZ}, {x = trailerVecX, z = trailerVecZ}) + setTranslation(implement.ad.reverseNode, centerX, 0, centerZ) + if angleToVeh > 60 then + -- setRotation(implement.spec_wheels.steeringCenterNode, 0, math.rad(90), 0) + setRotation(implement.ad.reverseNode, 0, math.rad(90), 0) + elseif angleToVeh < -60 then + -- setRotation(implement.spec_wheels.steeringCenterNode, 0, math.rad(-90), 0) + setRotation(implement.ad.reverseNode, 0, math.rad(-90), 0) + end end - end - reverseNode = implement.spec_wheels.steeringCenterNode - + else + implement.ad.reverseNode = implement.spec_wheels.steeringCenterNode + end + reverseNode = implement.ad.reverseNode self.reverseSolo = false self.vehicle.trailer = implement end diff --git a/scripts/Modules/StateModule.lua b/scripts/Modules/StateModule.lua index e9a93f35..2e6e66d9 100644 --- a/scripts/Modules/StateModule.lua +++ b/scripts/Modules/StateModule.lua @@ -30,6 +30,8 @@ function ADStateModule:reset() self.creationMode = ADStateModule.CREATE_OFF self.fillType = FillType.UNKNOWN + self.selectedFillTypes = {} + self.loadByFillLevel = true self.loopCounter = 0 self.loopsDone = 0 @@ -105,6 +107,18 @@ function ADStateModule:readFromXMLFile(xmlFile, key) self.fillType = fillType end + local selectedFillTypes = xmlFile:getValue(key .. "#selectedFillTypes") + if selectedFillTypes ~= nil then + self.selectedFillTypes = AutoDrive.stringToNumberList(selectedFillTypes) + else + self.selectedFillTypes = {self.fillType} + end + + local loadByFillLevel = xmlFile:getValue(key .. "#loadByFillLevel") + if loadByFillLevel ~= nil then + self.loadByFillLevel = loadByFillLevel + end + local loopCounter = xmlFile:getValue(key .. "#loopCounter") if loopCounter ~= nil then self.loopCounter = loopCounter @@ -165,6 +179,8 @@ function ADStateModule:saveToXMLFile(xmlFile, key) xmlFile:setValue(key .. "#secondMarker", self.secondMarker.markerIndex) end xmlFile:setValue(key .. "#fillType", self.fillType) + xmlFile:setValue(key .. "#selectedFillTypes", table.concat(self.selectedFillTypes, ',')) + xmlFile:setValue(key .. "#loadByFillLevel", self.loadByFillLevel) xmlFile:setValue(key .. "#loopCounter", self.loopCounter) xmlFile:setValue(key .. "#speedLimit", self.speedLimit) xmlFile:setValue(key .. "#fieldSpeedLimit", self.fieldSpeedLimit) @@ -183,6 +199,8 @@ function ADStateModule:writeStream(streamId) streamWriteUIntN(streamId, self:getSecondMarkerId() + 1, 17) streamWriteUIntN(streamId, self.creationMode, 3) streamWriteUIntN(streamId, self.fillType, 10) + AutoDrive.streamWriteUIntNList(streamId, self.selectedFillTypes, 10) + streamWriteBool(streamId, self.loadByFillLevel) streamWriteUIntN(streamId, self.loopCounter, 4) streamWriteUIntN(streamId, self.loopsDone, 4) streamWriteUIntN(streamId, self.speedLimit, 8) @@ -213,6 +231,8 @@ function ADStateModule:readStream(streamId) self.secondMarker = ADGraphManager:getMapMarkerById(streamReadUIntN(streamId, 17) - 1) self.creationMode = streamReadUIntN(streamId, 3) self.fillType = streamReadUIntN(streamId, 10) + self.selectedFillTypes = AutoDrive.streamReadUIntNList(streamId, 10) + self.loadByFillLevel = streamReadBool(streamId) self.loopCounter = streamReadUIntN(streamId, 4) self.loopsDone = streamReadUIntN(streamId, 4) self.speedLimit = streamReadUIntN(streamId, 8) @@ -245,6 +265,8 @@ function ADStateModule:writeUpdateStream(streamId) streamWriteUIntN(streamId, self:getSecondMarkerId() + 1, 17) streamWriteUIntN(streamId, self.creationMode, 3) streamWriteUIntN(streamId, self.fillType, 10) + AutoDrive.streamWriteUIntNList(streamId, self.selectedFillTypes, 10) + streamWriteBool(streamId, self.loadByFillLevel) streamWriteUIntN(streamId, self.loopCounter, 4) streamWriteUIntN(streamId, self.loopsDone, 4) streamWriteUIntN(streamId, self.speedLimit, 8) @@ -275,6 +297,8 @@ function ADStateModule:readUpdateStream(streamId) self.secondMarker = ADGraphManager:getMapMarkerById(streamReadUIntN(streamId, 17) - 1) self.creationMode = streamReadUIntN(streamId, 3) self.fillType = streamReadUIntN(streamId, 10) + self.selectedFillTypes = AutoDrive.streamReadUIntNList(streamId, 10) + self.loadByFillLevel = streamReadBool(streamId) self.loopCounter = streamReadUIntN(streamId, 4) self.loopsDone = streamReadUIntN(streamId, 4) self.speedLimit = streamReadUIntN(streamId, 8) @@ -810,13 +834,108 @@ function ADStateModule:getFillType() return self.fillType end +function ADStateModule:getSelectedFillTypes() + return self.selectedFillTypes +end + function ADStateModule:setFillType(fillType) if fillType > 0 and self.fillType ~= fillType then self.fillType = fillType + if not table.contains(self.selectedFillTypes, fillType) then + self.selectedFillTypes = {fillType} + end + self:raiseDirtyFlag() + end +end + +function ADStateModule:toggleFillTypeSelection(fillType) + if fillType > 0 then + if table.contains(self.selectedFillTypes, fillType) then + table.removeValue(self.selectedFillTypes, fillType) + if self.fillType == fillType and #self.selectedFillTypes > 0 then + -- the deselected filltype was the active filltype -> select the first remaining item + self.fillType = self.selectedFillTypes[1] + end + else + table.insert(self.selectedFillTypes, fillType) + end self:raiseDirtyFlag() end end +function ADStateModule:toggleAllFillTypeSelections(fillType) + if fillType > 0 then + local supportedFillTypes = AutoDrive.getSupportedFillTypesOfAllUnitsAlphabetically(self.vehicle) + if supportedFillTypes and #supportedFillTypes > 0 then + for _, selected in pairs(supportedFillTypes) do + if not table.contains(self.selectedFillTypes, selected) then + -- at least one supported fillType not yet selected. Select all + self.selectedFillTypes = supportedFillTypes + self:raiseDirtyFlag() + return + end + end + -- all fillTypes selected - clear selection and only select the given item + self.selectedFillTypes = {fillType} + self.fillType = fillType + self:raiseDirtyFlag() + end + end +end + + +function ADStateModule:toggleLoadByFillLevel() + self.loadByFillLevel = not self.loadByFillLevel + self:raiseDirtyFlag() +end + +function ADStateModule:setLoadByFillLevel(enabled) + self.loadByFillLevel = enabled + self:raiseDirtyFlag() +end + +function ADStateModule:getLoadByFillLevel() + return self.loadByFillLevel +end + + +function ADStateModule:selectPreferredFillTypeFromFillLevels(fillLevels) + if #self.selectedFillTypes == 0 then + return + end + + local fillLevelList = {} -- get a list of fill levels + for _, fillLevel in pairs(fillLevels) do + table.insert(fillLevelList, fillLevel) + end + table.sort(fillLevelList) -- sort it + local requiredFillLevel = fillLevelList[#fillLevelList] + local idx = table.indexOf(self.selectedFillTypes, self.fillType) -- starting point + local loopsLeft = #self.selectedFillTypes + local pickNextNonEmpty = requiredFillLevel == -1 or not self.loadByFillLevel + if idx == nil or requiredFillLevel == nil then + return + end + if pickNextNonEmpty then + -- infinite trigger (all fill levels are -1) or load-by-fill-level diabled: pick the next available filltype + idx = (idx % #self.selectedFillTypes) + 1 + end + while true do + local fillType = self.selectedFillTypes[idx] + if fillLevels[fillType] ~= nil and fillLevels[fillType] ~= 0 and (fillLevels[fillType] == requiredFillLevel or pickNextNonEmpty) then + -- found suitable filltype + self.fillType = fillType + break + end + + idx = (idx % #self.selectedFillTypes) + 1 + loopsLeft = loopsLeft - 1 + if loopsLeft <= 0 then + break + end + end +end + function ADStateModule:nextFillType() local supportedFillTypes = AutoDrive.getSupportedFillTypesOfAllUnitsAlphabetically(self.vehicle) if supportedFillTypes and #supportedFillTypes > 0 then diff --git a/scripts/Modules/TrailerModule.lua b/scripts/Modules/TrailerModule.lua index 4557032b..6989b03e 100644 --- a/scripts/Modules/TrailerModule.lua +++ b/scripts/Modules/TrailerModule.lua @@ -186,6 +186,10 @@ function ADTrailerModule:handleTrailerCovers() -- open trailer cover if trigger is reachable local isInRangeToLoadUnloadTarget = AutoDrive.isInRangeToLoadUnloadTarget(self.vehicle) AutoDrive.setTrailerCoverOpen(self.vehicle, self.trailers, isInRangeToLoadUnloadTarget) + if isInRangeToLoadUnloadTarget and self.hasAL then + -- open curtains for UAL + AutoDrive.openAllCurtains(self.trailers, true) -- open curtain at UAL trailers + end end function ADTrailerModule:updateStates() @@ -247,22 +251,20 @@ function ADTrailerModule:handleTrailerReversing(blockTrailers) trailer.ad.targetBlockedState = blockTrailers if trailer.ad.rotLimitBackup == nil then - trailer.ad.rotLimitBackup = {} - - if trailer.componentJoints[1].rotLimit == nil or - trailer.componentJoints[1].rotLimit[2] == nil then - trailer.ad.rotLimitBackup[1] = 0 - trailer.ad.rotLimitBackup[2] = 0 - else - trailer.ad.rotLimitBackup[1] = trailer.componentJoints[1].rotLimit[1] - trailer.ad.rotLimitBackup[2] = trailer.componentJoints[1].rotLimit[2] + for index, joint in pairs(trailer.componentJoints) do + if joint.rotLimit[1] == 0 and joint.rotLimit[2] ~= 0 and joint.rotLimit[3] == 0 then + trailer.ad.rotIndex = index + trailer.ad.rotLimitBackup = joint.rotLimit[2] + break + end end - else + end + if trailer.ad.rotIndex then if trailer.ad.lastBlockedState ~= trailer.ad.targetBlockedState then if trailer.ad.targetBlockedState then - trailer:setComponentJointRotLimit(trailer.componentJoints[1], 2, 0, 0) + trailer:setComponentJointRotLimit(trailer.componentJoints[trailer.ad.rotIndex], 2, 0, 0) else - trailer:setComponentJointRotLimit(trailer.componentJoints[1], 2, -trailer.ad.rotLimitBackup[2], trailer.ad.rotLimitBackup[2]) + trailer:setComponentJointRotLimit(trailer.componentJoints[trailer.ad.rotIndex], 2, -trailer.ad.rotLimitBackup, trailer.ad.rotLimitBackup) end trailer.ad.lastBlockedState = trailer.ad.targetBlockedState; end @@ -312,6 +314,8 @@ function ADTrailerModule:updateLoad(dt) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_TRAILERINFO, "ADTrailerModule:updateLoad Try loading at trigger now pair.fillUnitIndex %s", tostring(pair.fillUnitIndex)) fillFound = true -- start loading + + self.vehicle.ad.stateModule:selectPreferredFillTypeFromFillLevels(pair.fillLevels) self:tryLoadingAtTrigger(pair.trailer, pair.trigger, pair.fillUnitIndex) self.foundSuitableTrigger = true -- loading trigger was found return @@ -490,6 +494,9 @@ function ADTrailerModule:updateUnload(dt) if self.isUnloadingWithTrailer ~= nil and self.isUnloadingWithTrailer.setDischargeState then self.isUnloadingWithTrailer:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF) end + elseif fillUnitEmpty and self.unloadingToBunkerSilo then + self.unloadDelayTimer:timer(false) -- clear timer + self.unloadingToBunkerSilo = false elseif allTrailersClosed and self.isUnloadingWithTrailer ~= nil and self.isUnloadingWithTrailer.spec_pipe ~= nil then -- unload auger wagon to another trailer self.unloadDelayTimer:timer(false) -- clear timer diff --git a/scripts/Sensors/FieldSensor.lua b/scripts/Sensors/FieldSensor.lua index 1d573e65..912dd444 100644 --- a/scripts/Sensors/FieldSensor.lua +++ b/scripts/Sensors/FieldSensor.lua @@ -25,11 +25,7 @@ function ADFieldSensor:onUpdate(dt) local densityBits = getDensityAtWorldPos(groundTypeMapId, corner.x, y, corner.z) local densityType = bitAND(bitShiftRight(densityBits, groundTypeFirstChannel), 2^groundTypeNumChannels - 1) - if AutoDrive.experimentalFeatures.detectGrasField == true then - onField = onField and (densityType ~= 0) - else - onField = onField and (densityType ~= g_currentMission.grassValue and densityType ~= 0) - end + onField = onField and (densityType ~= 0) end self:setTriggered(onField) diff --git a/scripts/Sensors/FruitSensor.lua b/scripts/Sensors/FruitSensor.lua index 6a477a50..f0358f4b 100644 --- a/scripts/Sensors/FruitSensor.lua +++ b/scripts/Sensors/FruitSensor.lua @@ -4,7 +4,6 @@ function ADFruitSensor:new(vehicle, sensorParameters) local o = ADFruitSensor:create() o:init(vehicle, ADSensor.TYPE_FRUIT, sensorParameters) o.fruitType = 0 - o.foundFruitType = 0 if sensorParameters.fruitType ~= nil then o.fruitType = sensorParameters.fruitType @@ -16,12 +15,30 @@ end function ADFruitSensor:onUpdate(dt) local box = self:getBoxShape() local corners = self:getCorners(box) + self:setTriggerType(0) local foundFruit = false - if self.fruitType == nil or self.fruitType == 0 then - foundFruit, _ = self:checkForFruitInArea(corners) - else - foundFruit = self:checkForFruitTypeInArea(self.fruitType, corners) + if not foundFruit then + if self.fruitType == nil or self.fruitType == 0 then + foundFruit, _ = AutoDrive.checkForUnknownFruitInArea(corners) + else + foundFruit = AutoDrive.checkForFruitTypeInArea(corners, self.fruitType) + end + end + + if AutoDrive.experimentalFeatures.detectSwath then + local fillTypeName = "GRASS_WINDROW" + local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName) + if fillTypeIndex then + local fillLevel, _, _ = DensityMapHeightUtil.getFillLevelAtArea(fillTypeIndex, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z) + if (fillLevel and fillLevel > 0.1) then + local value = DensityMapHeightUtil.getValueAtArea(corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true) + if (value and value > 0.1) then + self:setTriggerType(ADSensor.TYPE_SWATH) + foundFruit = true + end + end + end end self:setTriggered(foundFruit) @@ -29,25 +46,20 @@ function ADFruitSensor:onUpdate(dt) self:onDrawDebug(box) end -function ADFruitSensor:checkForFruitInArea(corners) - for i = 1, #g_fruitTypeManager.fruitTypes do - if i ~= g_fruitTypeManager.nameToIndex["GRASS"] and i ~= g_fruitTypeManager.nameToIndex["DRYGRASS"] and i ~= g_fruitTypeManager.nameToIndex["MEADOW"] then - local fruitTypeToCheck = g_fruitTypeManager.fruitTypes[i].index - if self:checkForFruitTypeInArea(fruitTypeToCheck, corners) then - return true, fruitTypeToCheck +function AutoDrive.checkForUnknownFruitInArea(corners) + for _, fruitType in pairs(g_fruitTypeManager:getFruitTypes()) do + if not (fruitType == g_fruitTypeManager:getFruitTypeByName("MEADOW")) then + local fruitTypeIndex = fruitType.index + if AutoDrive.checkForFruitTypeInArea(corners, fruitTypeIndex) then + return true, fruitTypeIndex end end end return false end -function ADFruitSensor:checkForFruitTypeInArea(fruitType, corners) +function AutoDrive.checkForFruitTypeInArea(corners, fruitTypeIndex) local fruitValue = 0 - fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitType, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true, true) - + fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitTypeIndex, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true, true) return (fruitValue > 10) end - -function ADFruitSensor:setFruitType(newFruitType) - self.fruitType = newFruitType -end diff --git a/scripts/Sensors/VirtualSensors.lua b/scripts/Sensors/VirtualSensors.lua index 09859f40..ea5425cd 100644 --- a/scripts/Sensors/VirtualSensors.lua +++ b/scripts/Sensors/VirtualSensors.lua @@ -6,6 +6,7 @@ ADSensor.TYPE_COLLISION = 1 ADSensor.TYPE_FRUIT = 2 ADSensor.TYPE_TRIGGER = 3 ADSensor.TYPE_FIELDBORDER = 4 +ADSensor.TYPE_SWATH = 5 ADSensor.POS_FRONT = 1 ADSensor.POS_REAR = 2 @@ -154,6 +155,7 @@ function ADSensor:init(vehicle, sensorType, sensorParameters) self.sensorParameters = sensorParameters self.enabled = false self.triggered = false + self.triggerType = 0 self.initialized = false self.drawDebug = false self.executionDelay = 0 @@ -418,6 +420,9 @@ function ADSensor:onDrawDebug(box) AutoDriveDM:addLineTask(corners[1].x, corners[1].y, corners[1].z, corners[2].x, corners[2].y, corners[2].z, red, green, blue) -- right AutoDriveDM:addLineTask(corners[1].x, corners[1].y, corners[1].z, corners[3].x, corners[3].y, corners[3].z, red, green, blue) -- bottom if isTriggered and self.sensorType == ADSensor.TYPE_FRUIT then + if self.triggerType == ADSensor.TYPE_SWATH then + AutoDriveDM:addLineTask(corners[1].x, corners[1].y, corners[1].z, corners[4].x, corners[4].y, corners[4].z, red, green, blue) + end AutoDriveDM:addLineTask(corners[3].x, corners[3].y, corners[3].z, corners[2].x, corners[2].y, corners[2].z, 0, green, blue) end if isTriggered and self.sensorType == ADSensor.TYPE_FIELDBORDER then @@ -481,6 +486,14 @@ function ADSensor:setTriggered(triggered) end end +function ADSensor:setTriggerType(triggerType) + if triggerType ~= nil then + self.triggerType = triggerType + else + self.triggerType = 0 + end +end + function ADSensor:isTriggered() return self.triggered end diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua index 40237f24..29655236 100644 --- a/scripts/Specialization.lua +++ b/scripts/Specialization.lua @@ -136,6 +136,8 @@ function AutoDrive.initSpecialization() schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#firstMarker", "firstMarker") schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#secondMarker", "secondMarker") schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#fillType", "fillType") + schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).AutoDrive#selectedFillTypes", "selectedFillTypes") + schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?).AutoDrive#loadByFillLevel", "loadByFillLevel") schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#loopCounter", "loopCounter") schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#speedLimit", "speedLimit") schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).AutoDrive#fieldSpeedLimit", "fieldSpeedLimit") @@ -289,7 +291,7 @@ function AutoDrive:onPostLoad(savegame) link(self.components[1].node, self.ad.frontNode) setTranslation(self.ad.frontNode, 0, 0, self.size.length / 2 + self.size.lengthOffset + 0.75) self.ad.frontNodeGizmo = DebugGizmo.new() - self.ad.debug = RingQueue:new() + -- self.ad.debug = RingQueue:new() local x, y, z = getWorldTranslation(self.components[1].node) self.ad.lastDrawPosition = {x = x, z = z} @@ -1023,11 +1025,11 @@ function AutoDrive:startAutoDrive() if self.isServer then if not self.ad.stateModule:isActive() then self.ad.stateModule:setActive(true) + self.ad.stateModule:setLoopsDone(0) self.ad.isStoppingWithError = false self.ad.onRouteToPark = false - self.ad.foldStartTime = g_time - + AutoDrive.resetFoldState(self) AutoDrive.getAllVehicleDimensions(self, true) if self.spec_aiVehicle ~= nil then if self.getAINeedsTrafficCollisionBox ~= nil then @@ -1126,6 +1128,7 @@ function AutoDrive:stopAutoDrive() self.spec_aiVehicle.aiTrafficCollisionTranslation[2] = 0 end + self.ad.stateModule:setLoopsDone(0) self.ad.stateModule:setActive(false) self.ad.taskModule:abortAllTasks() diff --git a/scripts/Tasks/EmptyHarvesterTask.lua b/scripts/Tasks/EmptyHarvesterTask.lua index 249e9227..cb0089c7 100644 --- a/scripts/Tasks/EmptyHarvesterTask.lua +++ b/scripts/Tasks/EmptyHarvesterTask.lua @@ -65,11 +65,9 @@ function EmptyHarvesterTask:update(dt) self.vehicle.ad.specialDrivingModule:update(dt) end elseif self.state == EmptyHarvesterTask.STATE_DRIVING then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "EmptyHarvesterTask:update EmptyHarvesterTask.STATE_DRIVING") if self.vehicle.ad.drivePathModule:isTargetReached() then - --AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "EmptyHarvesterTask:update - next: EmptyHarvesterTask.STATE_UNLOADING 1") - self.state = EmptyHarvesterTask.STATE_UNLOADING - elseif (AutoDrive.getSetting("preCallLevel", self.combine) > 50 and self.combine.getDischargeState ~= nil and self.combine:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF) then - --AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "EmptyHarvesterTask:update - next: EmptyHarvesterTask.STATE_UNLOADING 2") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "EmptyHarvesterTask:update - next: EmptyHarvesterTask.STATE_UNLOADING") self.state = EmptyHarvesterTask.STATE_UNLOADING else self.vehicle.ad.drivePathModule:update(dt) diff --git a/scripts/Tasks/LoadAtDestinationTask.lua b/scripts/Tasks/LoadAtDestinationTask.lua index cb5cc9cc..6cbb1f27 100644 --- a/scripts/Tasks/LoadAtDestinationTask.lua +++ b/scripts/Tasks/LoadAtDestinationTask.lua @@ -11,6 +11,7 @@ function LoadAtDestinationTask:new(vehicle, destinationID) o.destinationID = destinationID o.trailers = nil o.waitForALLoadTimer = AutoDriveTON:new() + o.activatedUALLoading = false o.isReverseTriggerReached = false return o end @@ -82,6 +83,12 @@ function LoadAtDestinationTask:update(dt) local waitForALUnloadTime = AutoDrive.getSetting("ALUnloadWaitTime", self.vehicle) if self.vehicle.ad.trailerModule:getHasAL() then + -- UAL special handling - loading only possible if vehicle not moving -> self.lastSpeedReal > 0.0005 + -- assume no influence on aPalletAutoLoader + if self.vehicle.lastSpeedReal < 0.0005 and not self.activatedUALLoading then + self.activatedUALLoading = true + AutoDrive.activateALTrailers(self.vehicle, self.trailers) + end -- AutoLoad wait time if waitForALUnloadTime > 0 and not self.waitForALLoadTimer:timer(true, waitForALUnloadTime, dt) then AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "LoadAtDestinationTask:update wait time for AutoLoad...") @@ -105,7 +112,6 @@ function LoadAtDestinationTask:update(dt) self.loadRetryTimer:timer(false) -- clear timer AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "LoadAtDestinationTask:update try loading somehow") self.vehicle.ad.trailerModule:update(dt) - if not self.vehicle.ad.trailerModule:isActiveAtTrigger() then -- check fill levels only if not still filling something local _, _, isFull, _ = AutoDrive.getAllFillLevels(self.trailers) @@ -146,16 +152,22 @@ function LoadAtDestinationTask:continue() AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "LoadAtDestinationTask:continue -> trailerModule:stopLoading") self.vehicle.ad.trailerModule:stopLoading() AutoDrive.deactivateALTrailers(self.vehicle, self.trailers) + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers end function LoadAtDestinationTask:abort() AutoDrive.deactivateALTrailers(self.vehicle, self.trailers) + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers end function LoadAtDestinationTask:finished() AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "LoadAtDestinationTask:finished -> specialDrivingModule:releaseVehicle / setCurrentTaskFinished") self.vehicle.ad.specialDrivingModule:releaseVehicle() AutoDrive.deactivateALTrailers(self.vehicle, self.trailers) + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers self.vehicle.ad.taskModule:setCurrentTaskFinished() end diff --git a/scripts/Tasks/ParkTask.lua b/scripts/Tasks/ParkTask.lua index a9f79fd0..640d5272 100644 --- a/scripts/Tasks/ParkTask.lua +++ b/scripts/Tasks/ParkTask.lua @@ -87,7 +87,7 @@ function ParkTask:finished(propagate) if self.actualParkDestinationName ~= nil then AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.INFO, "$l10n_AD_Driver_of; %s $l10n_AD_has_reached; %s", 5000, self.vehicle.ad.stateModule:getName(), self.actualParkDestinationName) else - AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_has_reached; %s", 5000, self.vehicle.ad.stateModule:getName(), self.vehicle.ad.stateModule:getFirstMarkerName()) + AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.INFO, "$l10n_AD_Driver_of; %s $l10n_AD_has_reached; %s", 5000, self.vehicle.ad.stateModule:getName(), ADGraphManager:getMapMarkerByWayPointId(self.destinationID).name) end end diff --git a/scripts/Tasks/UnloadAtDestinationTask.lua b/scripts/Tasks/UnloadAtDestinationTask.lua index ae778b96..877e55b5 100644 --- a/scripts/Tasks/UnloadAtDestinationTask.lua +++ b/scripts/Tasks/UnloadAtDestinationTask.lua @@ -189,6 +189,8 @@ function UnloadAtDestinationTask:update(dt) end function UnloadAtDestinationTask:abort() + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers end function UnloadAtDestinationTask:continue() @@ -196,9 +198,13 @@ function UnloadAtDestinationTask:continue() self.vehicle.ad.trailerModule:stopUnloading() self.isContinued = true end + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers end function UnloadAtDestinationTask:finished() + AutoDrive.resetFoldState(self.vehicle) + AutoDrive.closeAllCurtains(self.trailers, true) -- close curtain at UAL trailers self.vehicle.ad.taskModule:setCurrentTaskFinished() end diff --git a/scripts/Utils/AutoDriveUtilFuncs.lua b/scripts/Utils/AutoDriveUtilFuncs.lua index cc2300b7..1bfb47cb 100644 --- a/scripts/Utils/AutoDriveUtilFuncs.lua +++ b/scripts/Utils/AutoDriveUtilFuncs.lua @@ -330,6 +330,7 @@ function AutoDrive.foldAllImplements(vehicle) local implements = AutoDrive.getAllImplements(vehicle, true) local spec AutoDrive.setAugerPipeOpen(implements, false) -- close all pipes first + AutoDrive.closeAllCurtains(implements, true) -- close curtain at UAL trailers for _, implement in pairs(implements) do spec = implement.spec_baleLoader if spec and spec.doStateChange then @@ -354,7 +355,6 @@ function AutoDrive.foldAllImplements(vehicle) end end end - AutoDrive.closeCurtain(implement) -- combine handle ladder separate when enter or leave combine AutoDrive.foldLadder(implement) end @@ -403,6 +403,12 @@ function AutoDrive.getAllImplementsFolded(vehicle) return ret end +function AutoDrive.resetFoldState(vehicle) + if vehicle and vehicle.ad then + vehicle.ad.foldStartTime = g_time + end +end + function AutoDrive.foldLadder(vehicle) local spec = vehicle.spec_combine if spec and not AutoDrive.isLadderFolded(vehicle) then @@ -435,28 +441,76 @@ function AutoDrive.isLadderFolded(vehicle) end function AutoDrive.closeCurtain(vehicle) + local leftDone, rightDone = false, false local spec = vehicle.spec_trailer if spec and not AutoDrive.isCurtainClosed(vehicle) then - local spec = vehicle.spec_trailer - if spec then - local tipSide = spec.tipSides[spec.preferedTipSideIndex] - if not vehicle:getIsAnimationPlaying(tipSide.animation.name) then - vehicle:playAnimation(tipSide.animation.name, tipSide.animation.closeSpeedScale, vehicle:getAnimationTime(tipSide.animation.name), true) + for _, tipSide in pairs(spec.tipSides) do + if tipSide and tipSide.manualTipToggle and tipSide.animation and tipSide.animation.name then + if vehicle.getIsAnimationPlaying and vehicle.playAnimation then + if not vehicle:getIsAnimationPlaying(tipSide.animation.name) then + vehicle:playAnimation(tipSide.animation.name, tipSide.animation.closeSpeedScale, vehicle:getAnimationTime(tipSide.animation.name), true) + end + end + end + end + end +end + +function AutoDrive.closeAllCurtains(trailers, onlyUAL) + if trailers and #trailers > 0 then + for _, trailer in ipairs(trailers) do + -- if (not onlyUAL) or (onlyUAL and AutoDrive:hasAL(trailer)) then + AutoDrive.closeCurtain(trailer) + -- end + end + end +end + +function AutoDrive.openCurtain(vehicle) + local leftDone, rightDone = false, false + local spec = vehicle.spec_trailer + if spec and AutoDrive.isCurtainClosed(vehicle) then + for _, tipSide in pairs(spec.tipSides) do + if tipSide and tipSide.manualTipToggle and tipSide.animation and tipSide.animation.name then + if vehicle.getIsAnimationPlaying and vehicle.playAnimation then + if not leftDone and string.find(tipSide.animation.name, "Left") then + leftDone = true + if not vehicle:getIsAnimationPlaying(tipSide.animation.name) then + vehicle:playAnimation(tipSide.animation.name, tipSide.animation.speedScale, vehicle:getAnimationTime(tipSide.animation.name), true) + end + end + if not rightDone and string.find(tipSide.animation.name, "Right") then + rightDone = true + if not vehicle:getIsAnimationPlaying(tipSide.animation.name) then + vehicle:playAnimation(tipSide.animation.name, tipSide.animation.speedScale, vehicle:getAnimationTime(tipSide.animation.name), true) + end + end + end + end + end + end +end + +function AutoDrive.openAllCurtains(trailers, onlyUAL) + if trailers and #trailers > 0 then + for _, trailer in ipairs(trailers) do + if (not onlyUAL) or (onlyUAL and AutoDrive:hasAL(trailer)) then + AutoDrive.openCurtain(trailer) end end end end function AutoDrive.isCurtainClosed(vehicle) + local leftDone, rightDone = false, false local ret = true local spec = vehicle.spec_trailer if spec then - local tipSide = spec.tipSides[spec.preferedTipSideIndex] - if tipSide and tipSide.manualTipToggle then - if tipSide.animation and tipSide.animation.closeSpeedScale then - if vehicle.getAnimationDuration and vehicle:getAnimationDuration(tipSide.animation.name) > 1 then + for _, tipSide in pairs(spec.tipSides) do + if tipSide and tipSide.manualTipToggle and tipSide.animation and tipSide.animation.name then + if tipSide.animation.closeSpeedScale ~= 0 and vehicle.getAnimationDuration and vehicle:getAnimationDuration(tipSide.animation.name) > 1 then local animationTime = vehicle:getAnimationTime(tipSide.animation.name) - ret = animationTime <= 0.01 + ret = ret and animationTime <= 0.01 end end end @@ -464,6 +518,18 @@ function AutoDrive.isCurtainClosed(vehicle) return ret end +function AutoDrive.getAllCurtainsClosed(trailers, onlyUAL) + local ret = true + if trailers and #trailers > 0 then + for _, trailer in ipairs(trailers) do + if (not onlyUAL) or (onlyUAL and AutoDrive:hasAL(trailer)) then + ret = ret and AutoDrive.isCurtainClosed(trailer) + end + end + end + return ret +end + function AutoDrive.isVehicleFolded(vehicle) local spec = vehicle.spec_foldable if spec ~= nil and #spec.foldingParts > 0 then @@ -640,7 +706,7 @@ function AutoDrive.getSupportedFillTypesOfAllUnitsAlphabetically(vehicle) if dischargeableUnit.object and dischargeableUnit.object.getFillUnitSupportedFillTypes ~= nil then if dischargeableUnit.fillUnitIndex and dischargeableUnit.fillUnitIndex > 0 then for fillType, supported in pairs(dischargeableUnit.object:getFillUnitSupportedFillTypes(dischargeableUnit.fillUnitIndex)) do - if supported then + if supported and not table.contains(supportedFillTypes, fillType) then table.insert(supportedFillTypes, fillType) end end diff --git a/scripts/Utils/CombineUtil.lua b/scripts/Utils/CombineUtil.lua index 84aa96cb..98132549 100644 --- a/scripts/Utils/CombineUtil.lua +++ b/scripts/Utils/CombineUtil.lua @@ -350,21 +350,3 @@ function AutoDrive.getCornersForAreaRelativeToVehicle(vehicle, xOffset, zOffset, return corners end - -function AutoDrive.checkForUnknownFruitInArea(corners) - for i = 1, #g_fruitTypeManager.fruitTypes do - if i ~= g_fruitTypeManager.nameToIndex["GRASS"] and i ~= g_fruitTypeManager.nameToIndex["DRYGRASS"] and i ~= g_fruitTypeManager.nameToIndex["MEADOW"] then - local fruitTypeToCheck = g_fruitTypeManager.fruitTypes[i].index - if AutoDrive.checkForFruitTypeInArea(corners, fruitTypeToCheck) then - return true, fruitTypeToCheck - end - end - end - return false, nil -end - -function AutoDrive.checkForFruitTypeInArea(corners, fruitType) - local fruitValue = 0 - fruitValue, _, _, _ = FSDensityMapUtil.getFruitArea(fruitType, corners[1].x, corners[1].z, corners[2].x, corners[2].z, corners[3].x, corners[3].z, true, true) - return (fruitValue > 10) -end \ No newline at end of file diff --git a/scripts/Utils/TrailerUtil.lua b/scripts/Utils/TrailerUtil.lua index 21883960..ffa29aa9 100644 --- a/scripts/Utils/TrailerUtil.lua +++ b/scripts/Utils/TrailerUtil.lua @@ -151,6 +151,10 @@ function AutoDrive.getObjectFillLevels(object) updateFillLevels(fillUnitIndex) elseif object.spec_baleLoader and object.spec_baleLoader.fillUnitIndex and object.spec_baleLoader.fillUnitIndex > 0 and object.spec_baleLoader.fillUnitIndex == fillUnitIndex then updateFillLevels(fillUnitIndex) + elseif spec_dischargeable and spec_dischargeable.dischargeNodes and #spec_dischargeable.dischargeNodes == 0 then + if object.getFillUnitCapacity and object:getFillUnitCapacity(fillUnitIndex) > 0 then + updateFillLevels(fillUnitIndex) + end end end end @@ -260,9 +264,8 @@ function AutoDrive.fillTypesMatch(vehicle, fillTrigger, workTool, allowedFillTyp else if vehicle.ad.stateModule:getFillType() == nil or vehicle.ad.stateModule:getFillType() == FillType.UNKNOWN then return false - else - table.insert(fillTypesToCheck, vehicle.ad.stateModule:getFillType()) end + fillTypesToCheck = vehicle.ad.stateModule:getSelectedFillTypes() end -- go through the single fillUnits and check: @@ -722,7 +725,7 @@ function AutoDrive.getTriggerAndTrailerPairs(vehicle, dt) if distance <= maxTriggerDistance then AutoDrive.debugPrint(trailer, AutoDrive.DC_TRAILERINFO, "AutoDrive.getTriggerAndTrailerPairs distance %s", tostring(distance)) vehicle.ad.debugTrigger = trigger - local allowedFillTypes = {vehicle.ad.stateModule:getFillType()} + local allowedFillTypes = vehicle.ad.stateModule:getSelectedFillTypes() -- seeds, fertilizer, liquidfertilizer should always be loaded if in trigger available if #fillUnits > 1 then @@ -747,6 +750,8 @@ function AutoDrive.getTriggerAndTrailerPairs(vehicle, dt) for i = 1, #fillUnits do local hasFill = trigger.hasInfiniteCapacity local isFillAllowed = false + local availableFillLevels = {} + hasRequiredFillType = AutoDrive.fillTypesMatch(vehicle, trigger, trailer, allowedFillTypes, i) local isNotFilled = trailer:getFillUnitFreeCapacity(i) > 0.1 @@ -754,8 +759,15 @@ function AutoDrive.getTriggerAndTrailerPairs(vehicle, dt) for _, allowedFillType in pairs(allowedFillTypes) do if trailer:getFillUnitSupportsFillType(i, allowedFillType) and trailer:getFillUnitAllowsFillType(i, allowedFillType) then - isFillAllowed = isFillAllowed or (fillLevels[allowedFillType] ~= nil) - hasFill = hasFill or (fillLevels[allowedFillType] ~= nil and fillLevels[allowedFillType] > 0) + if fillLevels[allowedFillType] ~= nil then + isFillAllowed = true + if trigger.hasInfiniteCapacity then + availableFillLevels[allowedFillType] = -1 + elseif fillLevels[allowedFillType] ~= nil and fillLevels[allowedFillType] > 0 then + hasFill = true + availableFillLevels[allowedFillType] = fillLevels[allowedFillType] + end + end end end AutoDrive.debugPrint(trailer, AutoDrive.DC_TRAILERINFO, "AutoDrive.getTriggerAndTrailerPairs isFillAllowed %s hasFill %s", tostring(isFillAllowed), tostring(hasFill)) @@ -776,7 +788,7 @@ function AutoDrive.getTriggerAndTrailerPairs(vehicle, dt) if timerDone and hasRequiredFillType and isNotFilled and isFillAllowed then AutoDrive.debugPrint(trailer, AutoDrive.DC_TRAILERINFO, "AutoDrive.getTriggerAndTrailerPairs timerDone %s trigger %s fillUnitIndex %s", tostring(timerDone), tostring(trigger), tostring(i)) - local pair = {trailer = trailer, trigger = trigger, fillUnitIndex = i, hasFill = hasFill} + local pair = {trailer = trailer, trigger = trigger, fillUnitIndex = i, hasFill = hasFill, fillLevels = availableFillLevels} table.insert(trailerTriggerPairs, pair) end end diff --git a/scripts/Utils/UtilFuncs.lua b/scripts/Utils/UtilFuncs.lua index 4d23f9f7..7dec6be3 100644 --- a/scripts/Utils/UtilFuncs.lua +++ b/scripts/Utils/UtilFuncs.lua @@ -104,6 +104,24 @@ function AutoDrive.streamWriteStringOrEmpty(streamId, string) streamWriteString(streamId, string) end +function AutoDrive.streamReadUIntNList(streamId, numberOfBits) + list = {} + local len = streamReadUIntN(streamId, numberOfBits) + for i = 1, len do + local v = streamReadUIntN(streamId, numberOfBits) + table.insert(list, v) + end + return list +end + +function AutoDrive.streamWriteUIntNList(streamId, list, numberOfBits) + list = list or {} + streamWriteUIntN(streamId, #list, numberOfBits) + for _, v in pairs(list) do + streamWriteUIntN(streamId, v, numberOfBits) + end +end + function AutoDrive.boxesIntersect(a, b) local polygons = {a, b} local minA, maxA, minB, maxB @@ -1172,4 +1190,16 @@ function AutoDrive.playSample(sample, volume, forced) if (AutoDrive.getSetting("playSounds") and sample~= nil) or forced then playSample(sample, 1, volume, 0, 0, 0) end -end \ No newline at end of file +end + +function AutoDrive.stringToNumberList(text, sep) + sep = sep or ',' + local list = {} + for _, v in pairs(text:split(sep)) do + local num = tonumber(v) + if num ~= nil then + table.insert(list, num) + end + end + return list +end diff --git a/textures/fruit_overlay.dds b/textures/input_toggleLoadByFillLevel_1.dds similarity index 100% rename from textures/fruit_overlay.dds rename to textures/input_toggleLoadByFillLevel_1.dds diff --git a/textures/input_toggleLoadByFillLevel_2.dds b/textures/input_toggleLoadByFillLevel_2.dds new file mode 100644 index 00000000..9082a288 Binary files /dev/null and b/textures/input_toggleLoadByFillLevel_2.dds differ diff --git a/textures/input_toggleLoadByFillLevel_3.dds b/textures/input_toggleLoadByFillLevel_3.dds new file mode 100644 index 00000000..0c8cf2ea Binary files /dev/null and b/textures/input_toggleLoadByFillLevel_3.dds differ diff --git a/translations/translation_br.xml b/translations/translation_br.xml index 56fead6f..a2d8b58b 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: Ação Developer" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Alternar para coleta automática" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Alternar para descarga automática" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Evitar plantações" /> <text name="hud_startCp" text="AD: Iniciar CP / AIVE no destino" /> <text name="AD_parkVehicle_noPosSet" text="Nenhum destino de estacionamento atribuído." /> diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 90f5f316..49aedfef 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: 开发人员操作" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: 切换自动拾取目标" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: 切换自动卸载目标" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: 规避作物" /> <text name="hud_startCp" text="AD: 在目的地开始 CP / AIVE" /> <text name="AD_parkVehicle_noPosSet" text="未指定停车目的地。" /> diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 003d9c06..1a0a6f82 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Vývojářské Akce" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Přepnout cíl automatického vyzvednutí" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Přepnout cíl automatického vyložení" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Nevjíždět do porostu" /> <text name="hud_startCp" text="AD: Start CP / AIVE v místě určení" /> <text name="AD_parkVehicle_noPosSet" text="Vozidlo nemá parkoviště. Vyberte cíl a stiskněte pravého tlačítka myši." /> diff --git a/translations/translation_de.xml b/translations/translation_de.xml index f993f30a..e0e8fb46 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -62,6 +62,7 @@ <text name="input_AD_devAction" text="AD: Entwickler Aktion" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Umschalten automatisches Ladeziel" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Umschalten automatisches Entladeziel" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Fruchtumfahrung" /> <text name="hud_startCp" text="AD: Starte CP / AIVE Fahrer am Zielpunkt" /> <text name="AD_parkVehicle_noPosSet" text="Kein Parkplatz zugewiesen." /> diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 2a63f564..8a83806e 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: Developer Action" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Toggle automatic pickup target" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Toggle automatic unload target" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Avoid fruit" /> <text name="hud_startCp" text="AD: Start CP / AIVE at destination" /> <text name="AD_parkVehicle_noPosSet" text="No parking destination assigned." /> diff --git a/translations/translation_es.xml b/translations/translation_es.xml index b91625b8..d6a1abf0 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Funciones DEV" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Cambiar a carga automática en destino" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Cambiar a descarga automática en destino" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Evitar destruir cosechas" /> <text name="hud_startCp" text="AD: Iniciar CP/AIVE en destino" /> <text name="AD_parkVehicle_noPosSet" text="No se ha asignado ningún destino de estacionamiento. Asigna el destino seleccionado actualmente haciendo clic derecho." /> diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index 4200e2a0..cefdfe27 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: Action du développeur" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Choix automatique du point de chargement" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Choix automatique du point de déchargement" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Éviter les cultures" /> <text name="hud_startCp" text="AD: Démarrer CP/AIVE à la destination" /> <text name="AD_parkVehicle_noPosSet" text="Pas de parking attribué. Veuillez sélectionner la (Destination actuelle) et corriger avec le clic droit de souris sur le 'P' HUD." /> diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 8323b550..252c5266 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Developer Action" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Toggle automatic pickup target" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Toggle automatic unload target" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Termény kikerülése" /> <text name="hud_startCp" text="AD: Start CP / AIVE at destination" /> <text name="AD_parkVehicle_noPosSet" text="Járműnek nincs parkolója. Jobb egérgombbal rendeljen hozzá parkolási helyet." /> diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 01400665..ddf47954 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Azione dello sviluppatore" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Attiva/Disattiva il target di ritiro automatico" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Attiva/Disattiva obiettivo di scaricamento automatico" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Evita raccolto" /> <text name="hud_startCp" text="AD: Avvia CP/AIVE a destinazione" /> <text name="AD_parkVehicle_noPosSet" text="Nessuna destinazione di parcheggio assegnata. Si prega di assegnare l'obiettivo attualmente selezionato con il tasto destro del mouse." /> diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 5f5f1bf9..92e28d27 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: Ontwikkelaars actie" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Automatische laad bestemming aan/uit" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Automatische los bestemming aan/uit" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Fruit vermijden" /> <text name="hud_startCp" text="AD: Start CP/AIVE op bestemming" /> <text name="AD_parkVehicle_noPosSet" text="Geen parkeerbestemming toegewezen. Wijzig het momenteel geselecteerde bestemming toe met de rechtermuisknop." /> diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index 773e023f..d1a2dcaf 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -57,6 +57,7 @@ <text name="input_AD_open_tipOfTheDay" text="AD: Otwórz podpowiedź dnia" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Wybierz automatycznie cel ładowania" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Wybierz automatycznie cel rozładowania" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Unikaj upraw" /> <text name="hud_startCp" text="AD: Uruchom CP / AIVE po dotarciu do celu" /> <text name="AD_parkVehicle_noPosSet" text="Nie przydzielono parkingu." /> diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 76245044..bafd3295 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Developer Action" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Toggle automatic pickup target" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Toggle automatic unload target" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Evitar cultura" /> <text name="hud_startCp" text="AD: Iniciar o CP / AIVE no destino" /> <text name="AD_parkVehicle_noPosSet" text="Nenhum destino de estacionamento atribuído. Atribua o destino atualmente selecionado com o botão direito do mouse." /> diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 27982cba..c2bc7626 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -56,6 +56,7 @@ <text name="input_AD_devAction" text="AD: Действия разработчика" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Переключать автоматически цели подбора" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Переключать автоматически цели разгрузки" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Избегать посевов" /> <text name="hud_startCp" text="AD: Запустить CP/AIVE в пункте назначения" /> <text name="AD_parkVehicle_noPosSet" text="Место для парковки не назначено. Чтобы назначить выбранную локацию, как место парковки, нажмите правой кнопкой мыши на этой же кнопке." /> diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 3804cb34..4737e928 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -55,6 +55,7 @@ <text name="input_AD_devAction" text="AD: Developer Action" /> <text name="input_ADToggleAutomaticPickupTarget" text="AD: Toggle automatic pickup target" /> <text name="input_ADToggleAutomaticUnloadTarget" text="AD: Toggle automatic unload target" /> + <text name="input_ADToggleLoadByFillLevel" text="AD: Toggle loading by fill level" /> <text name="hud_avoidFruit" text="AD: Meyvelerden kaçının" /> <text name="hud_startCp" text="AD: CP / AIVE'ı varış noktasında başlatın" /> <text name="AD_parkVehicle_noPosSet" text="Park yeri atanmadı. Lütfen halihazırda seçili olan hedefi sağ fare tuşu ile atayın." />