From ed82ff11d96c9eda645249af52172a68952a4d1b Mon Sep 17 00:00:00 2001 From: RedMonster-HUN <54554640+RedMonster-HUN@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:18:00 +0200 Subject: [PATCH 01/51] Update translation_hu.xml --- translations/translation_hu.xml | 63 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 252c5266..40169266 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -10,7 +10,7 @@ - + @@ -40,15 +40,15 @@ - - + + - + - - - + + + @@ -57,7 +57,7 @@ - + @@ -69,8 +69,8 @@ - - + + @@ -89,6 +89,7 @@ + @@ -188,8 +189,8 @@ - - + + @@ -214,33 +215,33 @@ - - - + + + - - + + - + - - - - - - - + + + + + + + - - - + + + @@ -251,7 +252,7 @@ - + @@ -313,7 +314,7 @@ - + @@ -328,7 +329,7 @@ - + From 7570e02c91b39d4eee284a69e0b811da5e686167 Mon Sep 17 00:00:00 2001 From: Efratror Date: Tue, 12 Sep 2023 21:57:14 +0200 Subject: [PATCH 02/51] Fix restartMySavegame console command Switched bool and string around --- scripts/AutoDrive.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index bd0b10a4..c5a2e3af 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -146,7 +146,7 @@ end function AutoDrive:restartMySavegame() if g_server then - restartApplication(" -autoStartSavegameId 1", true) + restartApplication(true, " -autoStartSavegameId 1") end end From 565f907d7e8cea0703976cf5fca19db5a1a4388e Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:43:05 +0200 Subject: [PATCH 03/51] implement #991 --- scripts/AutoDrive.lua | 5 +++-- scripts/Gui/EnterDestinationFilterGUI.lua | 5 +++++ scripts/Gui/EnterDriverNameGUI.lua | 5 +++++ scripts/Gui/EnterGroupNameGUI.lua | 5 +++++ scripts/Gui/EnterTargetNameGUI.lua | 5 +++++ scripts/Gui/RoutesManagerGUI.lua | 5 +++++ translations/translation_br.xml | 1 + translations/translation_cs.xml | 1 + translations/translation_cz.xml | 1 + translations/translation_de.xml | 1 + translations/translation_en.xml | 1 + translations/translation_es.xml | 1 + translations/translation_fr.xml | 1 + translations/translation_hu.xml | 1 + translations/translation_it.xml | 1 + translations/translation_nl.xml | 1 + translations/translation_pl.xml | 1 + translations/translation_pt.xml | 1 + translations/translation_ru.xml | 1 + translations/translation_tr.xml | 1 + 20 files changed, 42 insertions(+), 2 deletions(-) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index bd0b10a4..9d5c27ca 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -1,5 +1,5 @@ AutoDrive = {} -AutoDrive.version = "2.0.1.4" +AutoDrive.version = "2.0.1.5-RC" AutoDrive.directory = g_currentModDirectory @@ -108,7 +108,8 @@ AutoDrive.colors = { ad_color_closestLine = {1, 0, 0, 1}, ad_color_editorHeightLine = {1, 1, 1, 1}, ad_color_previewOk = {0.3, 0.9, 0, 1}, - ad_color_previewNotOk = {1, 0.1, 0, 1} + ad_color_previewNotOk = {1, 0.1, 0, 1}, + ad_color_textInputBackground = {0.0227, 0.5346, 0.8519, 1} -- Giants original } AutoDrive.currentColors = {} -- this will hold the current colors, derived from default colors above, overwritten by local settings diff --git a/scripts/Gui/EnterDestinationFilterGUI.lua b/scripts/Gui/EnterDestinationFilterGUI.lua index eabb27b8..aa4f187a 100644 --- a/scripts/Gui/EnterDestinationFilterGUI.lua +++ b/scripts/Gui/EnterDestinationFilterGUI.lua @@ -22,6 +22,11 @@ function ADEnterDestinationFilterGui:onOpen() ADEnterDestinationFilterGui:superClass().onOpen(self) self.textInputElement.blockTime = 0 self.textInputElement:onFocusActivate() + if self.textInputElement.overlay and self.textInputElement.overlay.colorFocused then + if AutoDrive.currentColors and AutoDrive.currentColors.ad_color_textInputBackground then + self.textInputElement.overlay.colorFocused = AutoDrive.currentColors.ad_color_textInputBackground + end + end if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle.ad ~= nil then self.textInputElement:setText(g_currentMission.controlledVehicle.ad.destinationFilterText) end diff --git a/scripts/Gui/EnterDriverNameGUI.lua b/scripts/Gui/EnterDriverNameGUI.lua index 0b50b154..5144981a 100644 --- a/scripts/Gui/EnterDriverNameGUI.lua +++ b/scripts/Gui/EnterDriverNameGUI.lua @@ -22,6 +22,11 @@ function ADEnterDriverNameGui:onOpen() ADEnterDriverNameGui:superClass().onOpen(self) self.textInputElement.blockTime = 0 self.textInputElement:onFocusActivate() + if self.textInputElement.overlay and self.textInputElement.overlay.colorFocused then + if AutoDrive.currentColors and AutoDrive.currentColors.ad_color_textInputBackground then + self.textInputElement.overlay.colorFocused = AutoDrive.currentColors.ad_color_textInputBackground + end + end if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle.ad ~= nil then self.textInputElement:setText(g_currentMission.controlledVehicle.ad.stateModule:getName()) end diff --git a/scripts/Gui/EnterGroupNameGUI.lua b/scripts/Gui/EnterGroupNameGUI.lua index b6eca8d3..bdc4c926 100644 --- a/scripts/Gui/EnterGroupNameGUI.lua +++ b/scripts/Gui/EnterGroupNameGUI.lua @@ -22,6 +22,11 @@ function ADEnterGroupNameGui:onOpen() ADEnterGroupNameGui:superClass().onOpen(self) self.textInputElement.blockTime = 0 self.textInputElement:onFocusActivate() + if self.textInputElement.overlay and self.textInputElement.overlay.colorFocused then + if AutoDrive.currentColors and AutoDrive.currentColors.ad_color_textInputBackground then + self.textInputElement.overlay.colorFocused = AutoDrive.currentColors.ad_color_textInputBackground + end + end self.textInputElement:setText("") end diff --git a/scripts/Gui/EnterTargetNameGUI.lua b/scripts/Gui/EnterTargetNameGUI.lua index 44b0e5a8..c115da1b 100644 --- a/scripts/Gui/EnterTargetNameGUI.lua +++ b/scripts/Gui/EnterTargetNameGUI.lua @@ -24,6 +24,11 @@ function ADEnterTargetNameGui:onOpen() ADEnterTargetNameGui:superClass().onOpen(self) self.textInputElement.blockTime = 0 self.textInputElement:onFocusActivate() + if self.textInputElement.overlay and self.textInputElement.overlay.colorFocused then + if AutoDrive.currentColors and AutoDrive.currentColors.ad_color_textInputBackground then + self.textInputElement.overlay.colorFocused = AutoDrive.currentColors.ad_color_textInputBackground + end + end self.editName = nil self.editId = nil self.edit = false diff --git a/scripts/Gui/RoutesManagerGUI.lua b/scripts/Gui/RoutesManagerGUI.lua index bfda3289..7bd6b6ef 100644 --- a/scripts/Gui/RoutesManagerGUI.lua +++ b/scripts/Gui/RoutesManagerGUI.lua @@ -17,6 +17,11 @@ function ADRoutesManagerGui:onCreate() end function ADRoutesManagerGui:onOpen() + if self.textInputElement.overlay and self.textInputElement.overlay.colorFocused then + if AutoDrive.currentColors and AutoDrive.currentColors.ad_color_textInputBackground then + self.textInputElement.overlay.colorFocused = AutoDrive.currentColors.ad_color_textInputBackground + end + end self:refreshItems() ADRoutesManagerGui:superClass().onOpen(self) end diff --git a/translations/translation_br.xml b/translations/translation_br.xml index a2d8b58b..1565ea63 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -302,6 +302,7 @@ + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 49aedfef..61f1862d 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -305,6 +305,7 @@ + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 1a0a6f82..7fffe228 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -303,6 +303,7 @@ + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index e0e8fb46..9c366ca7 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -309,6 +309,7 @@ + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 8a83806e..54b5d85e 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -302,6 +302,7 @@ + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index be8910e0..8be5b564 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -301,6 +301,7 @@ + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index cefdfe27..eb974d8a 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -305,6 +305,7 @@ + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 252c5266..d96674cb 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -297,6 +297,7 @@ + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 33e4749c..8be8436f 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -304,6 +304,7 @@ + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 92e28d27..df647977 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -305,6 +305,7 @@ + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index d1a2dcaf..fc159348 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -304,6 +304,7 @@ + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index bafd3295..365b8a43 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -303,6 +303,7 @@ + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index c2bc7626..93944588 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -302,6 +302,7 @@ + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 4737e928..452adeb9 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -301,6 +301,7 @@ + From 4d20270fa70f1762d3bf81b3c5688de8c432a101 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sat, 23 Sep 2023 17:44:48 +0200 Subject: [PATCH 04/51] fix #980 still need to reenter/change vehicle to take effect --- scripts/Specialization.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua index 936445f8..00aaeb24 100644 --- a/scripts/Specialization.lua +++ b/scripts/Specialization.lua @@ -98,10 +98,9 @@ function AutoDrive:onRegisterActionEvents(_, isOnActiveVehicle) if registerEvents then -- attach our actions local _, eventName - local toggleButton = false local showF1Help = AutoDrive.getSetting("showHelp") for _, action in pairs(ADInputManager.actionsToInputs) do - _, eventName = InputBinding.registerActionEvent(g_inputBinding, action[1], self, ADInputManager.onActionCall, toggleButton, true, false, true) + _, eventName = InputBinding.registerActionEvent(g_inputBinding, action[1], self, ADInputManager.onActionCall, false, true, false, true) if action[5] then g_inputBinding:setActionEventTextVisibility(eventName, action[5] and showF1Help) if showF1Help then @@ -110,6 +109,8 @@ function AutoDrive:onRegisterActionEvents(_, isOnActiveVehicle) g_inputBinding:setActionEventTextPriority(eventName, action[6]) end end + else + g_inputBinding:setActionEventTextVisibility(eventName, false) end end end From 422b4ee620147dab0ac9dc7a1e76976e3d0c9210 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Tue, 26 Sep 2023 20:13:11 +0200 Subject: [PATCH 05/51] fix #995 --- scripts/Utils/CollisionDetectionUtils.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/Utils/CollisionDetectionUtils.lua b/scripts/Utils/CollisionDetectionUtils.lua index bfa4d1fe..8e1df41b 100644 --- a/scripts/Utils/CollisionDetectionUtils.lua +++ b/scripts/Utils/CollisionDetectionUtils.lua @@ -16,8 +16,9 @@ function AutoDrive.checkForVehiclesInBox(boundingBox, excludedVehicles) end end end - if otherVehicle.spec_conveyorBelt and otherVehicle.spec_motorized and otherVehicle.getIsMotorStarted and otherVehicle:getIsMotorStarted() then - -- ignore operating conveyor belts + if (otherVehicle.spec_conveyorBelt and otherVehicle.spec_motorized and otherVehicle.getIsMotorStarted and otherVehicle:getIsMotorStarted()) -- ignore operating conveyor belts + or (otherVehicle.trainSystem ~= nil) -- ignore train vehicles + then isExcluded = true end if (not isExcluded) and otherVehicle ~= nil and otherVehicle.components ~= nil and otherVehicle.size.width ~= nil and otherVehicle.size.length ~= nil and otherVehicle.rootNode ~= nil then From b0158d1c2fa2c172e9499799d85b5030ab4b08e8 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Wed, 27 Sep 2023 19:33:49 +1300 Subject: [PATCH 06/51] Issue #990 - select current filltype when adding first entry to multi-selection - ensure a valid index when looping over filltypes --- scripts/Modules/StateModule.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/Modules/StateModule.lua b/scripts/Modules/StateModule.lua index 2e6e66d9..eb31bd29 100644 --- a/scripts/Modules/StateModule.lua +++ b/scripts/Modules/StateModule.lua @@ -858,6 +858,10 @@ function ADStateModule:toggleFillTypeSelection(fillType) end else table.insert(self.selectedFillTypes, fillType) + if not table.contains(self.selectedFillTypes, self.fillType) then + -- selectedFillTypes was empty, select the new fillType + self.fillType = fillType + end end self:raiseDirtyFlag() end @@ -910,7 +914,7 @@ function ADStateModule:selectPreferredFillTypeFromFillLevels(fillLevels) end table.sort(fillLevelList) -- sort it local requiredFillLevel = fillLevelList[#fillLevelList] - local idx = table.indexOf(self.selectedFillTypes, self.fillType) -- starting point + local idx = table.indexOf(self.selectedFillTypes, self.fillType) or 0 -- starting point local loopsLeft = #self.selectedFillTypes local pickNextNonEmpty = requiredFillLevel == -1 or not self.loadByFillLevel if idx == nil or requiredFillLevel == nil then From 90307f576ae70a257d62c2294209b42983a6a596 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:29:47 +0200 Subject: [PATCH 07/51] further implement #991 increased options to scale lines more --- scripts/Settings.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Settings.lua b/scripts/Settings.lua index 1fcab523..469b5568 100644 --- a/scripts/Settings.lua +++ b/scripts/Settings.lua @@ -1024,8 +1024,8 @@ AutoDrive.settings.useHazardLightReverse = { } AutoDrive.settings.scaleLines = { - values = {0.5, 1, 2, 3, 4, 5, 6, 10}, - texts = {"50%", "100%", "200%", "300%", "400%", "500%", "600%", "1000%"}, + values = {0.5, 1, 2, 3, 4, 5, 6, 10, 20, 30, 50, 100}, + texts = {"50%", "100%", "200%", "300%", "400%", "500%", "600%", "1000%", "2000%", "3000%", "5000%", "10000%"}, default = 2, current = 2, text = "gui_ad_scaleLines", From 70e94cf5c4abb9bfbf40d912f9ff1211d215d4ea Mon Sep 17 00:00:00 2001 From: Gonimy-Vetrom Date: Thu, 28 Sep 2023 21:48:18 +0300 Subject: [PATCH 08/51] Update translation_ru.xml --- translations/translation_ru.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index c2bc7626..0ce95f1d 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -54,9 +54,9 @@ - + - + From 25cb3fece99b5a7a4c4f3f4b0c240f6cdbf041cc Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sat, 30 Sep 2023 17:27:52 +1300 Subject: [PATCH 09/51] Issue #996: increase max number of loops from 9 to 99, allow changing via mouse-wheel, use L-Shift to change quicker. --- register.lua | 1 + scripts/Events/HudInputEvent.lua | 17 +++ scripts/Hud.lua | 5 +- scripts/Hud/HudButton.lua | 11 +- scripts/Hud/HudCounterButton.lua | 108 ++++++++++++++++++ scripts/Manager/InputManager.lua | 4 +- scripts/Modules/StateModule.lua | 30 +++-- textures/input_incLoopCounter_10.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_11.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_12.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_13.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_14.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_15.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_16.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_17.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_18.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_19.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_2.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_3.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_4.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_5.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_6.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_7.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_8.dds | Bin 22000 -> 0 bytes textures/input_incLoopCounter_9.dds | Bin 22000 -> 0 bytes textures/loop_counter_active.dds | Bin 0 -> 16512 bytes textures/loop_counter_inactive.dds | Bin 0 -> 16512 bytes ...LoopCounter_1.dds => loop_counter_inf.dds} | Bin 28 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 scripts/Hud/HudCounterButton.lua delete mode 100644 textures/input_incLoopCounter_10.dds delete mode 100644 textures/input_incLoopCounter_11.dds delete mode 100644 textures/input_incLoopCounter_12.dds delete mode 100644 textures/input_incLoopCounter_13.dds delete mode 100644 textures/input_incLoopCounter_14.dds delete mode 100644 textures/input_incLoopCounter_15.dds delete mode 100644 textures/input_incLoopCounter_16.dds delete mode 100644 textures/input_incLoopCounter_17.dds delete mode 100644 textures/input_incLoopCounter_18.dds delete mode 100644 textures/input_incLoopCounter_19.dds delete mode 100644 textures/input_incLoopCounter_2.dds delete mode 100644 textures/input_incLoopCounter_3.dds delete mode 100644 textures/input_incLoopCounter_4.dds delete mode 100644 textures/input_incLoopCounter_5.dds delete mode 100644 textures/input_incLoopCounter_6.dds delete mode 100644 textures/input_incLoopCounter_7.dds delete mode 100644 textures/input_incLoopCounter_8.dds delete mode 100644 textures/input_incLoopCounter_9.dds create mode 100644 textures/loop_counter_active.dds create mode 100644 textures/loop_counter_inactive.dds rename textures/{input_incLoopCounter_1.dds => loop_counter_inf.dds} (100%) diff --git a/register.lua b/register.lua index 1c3162f8..3ea29f85 100644 --- a/register.lua +++ b/register.lua @@ -22,6 +22,7 @@ source(Utils.getFilename("scripts/TelemetryExport.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Hud/GenericHudElement.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Hud/HudButton.lua", g_currentModDirectory)) +source(Utils.getFilename("scripts/Hud/HudCounterButton.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Hud/HudSettingsButton.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Hud/HudIcon.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Hud/HudSpeedmeter.lua", g_currentModDirectory)) diff --git a/scripts/Events/HudInputEvent.lua b/scripts/Events/HudInputEvent.lua index 6e93ec1d..94c7eec7 100644 --- a/scripts/Events/HudInputEvent.lua +++ b/scripts/Events/HudInputEvent.lua @@ -4,6 +4,10 @@ AutoDriveHudInputEventEvent.TYPE_SECOND_MARKER = 2 AutoDriveHudInputEventEvent.TYPE_FILLTYPE = 3 AutoDriveHudInputEventEvent.TYPE_TOGGLE_FILLTYPE_SELECTION = 4 AutoDriveHudInputEventEvent.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS = 5 +AutoDriveHudInputEventEvent.CHANGE_LOOP_COUNTER = 6 + + + AutoDriveHudInputEventEvent_mt = Class(AutoDriveHudInputEventEvent, Event) @@ -63,6 +67,11 @@ function AutoDriveHudInputEventEvent:run(connection) if self.eventType == self.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS then self.vehicle.ad.stateModule:toggleAllFillTypeSelections(self.value) end + + if self.eventType == self.CHANGE_LOOP_COUNTER then + local increment, fast, wheel = ADHudCounterButton.int_to_flags(self.value) + self.vehicle.ad.stateModule:changeLoopCounter(increment, fast, wheel) + end end end @@ -100,3 +109,11 @@ function AutoDriveHudInputEventEvent:sendToggleAllFillTypeSelectionsEvent(vehicl g_client:getServerConnection():sendEvent(AutoDriveHudInputEventEvent.new(vehicle, self.TYPE_TOGGLE_ALL_FILLTYPE_SELECTIONS, fillTypeId)) end end + +function AutoDriveHudInputEventEvent:sendChangeLoopCounterEvent(vehicle, increment, fast, wheel) + if g_client ~= nil then + -- Client have to send to server + local value = ADHudCounterButton.flags_to_int(increment, fast, wheel) + g_client:getServerConnection():sendEvent(AutoDriveHudInputEventEvent.new(vehicle, self.CHANGE_LOOP_COUNTER, value)) + end +end diff --git a/scripts/Hud.lua b/scripts/Hud.lua index 5dd8620e..aad46513 100644 --- a/scripts/Hud.lua +++ b/scripts/Hud.lua @@ -216,7 +216,10 @@ function AutoDriveHud:createHudAt(hudX, hudY) self:AddButton("input_continue", nil, nil, nil, "input_AD_continue", 1, true) self:AddButton("input_parkVehicle", "input_setParkDestination", nil, nil, "input_ADParkVehicle", 1, true) if vehicle == nil or vehicle.ad.stateModule:getMode() ~= AutoDrive.MODE_BGA then - self:AddButton("input_incLoopCounter", "input_decLoopCounter", nil, nil, "input_ADIncLoopCounter", 1, true) + local loopX = self.posX + (self.cols - 2 + self.buttonCollOffset) * self.borderX + (self.cols - 3 + self.buttonCollOffset) * self.buttonWidth + local loopY = self.posY + (1) * self.borderY + (0) * self.buttonHeight + table.insert(self.hudElements, ADHudCounterButton:new(loopX, loopY, self.buttonWidth, self.buttonHeight, "loop_counter")) + self.buttonCounter = self.buttonCounter + 1 else self:AddButton("input_bunkerUnloadType", nil, nil, nil, "input_ADbunkerUnloadType", 1, true) end diff --git a/scripts/Hud/HudButton.lua b/scripts/Hud/HudButton.lua index 43560a8d..c305e120 100644 --- a/scripts/Hud/HudButton.lua +++ b/scripts/Hud/HudButton.lua @@ -29,7 +29,7 @@ function ADHudButton:readImages() path = "input_toggleAutomaticUnloadTarget" end - while counter <= 19 do + while counter <= 6 do images[counter] = AutoDrive.directory .. "textures/" .. path .. "_" .. counter .. ".dds" counter = counter + 1 end @@ -141,15 +141,6 @@ function ADHudButton:getNewState(vehicle) self.isVisible = AutoDrive.isEditorModeEnabled() end - if self.primaryAction == "input_incLoopCounter" then - newState = math.max(0, vehicle.ad.stateModule:getLoopCounter() - vehicle.ad.stateModule:getLoopsDone()) + 1 - if vehicle.ad.stateModule:isActive() and vehicle.ad.stateModule:getMode() == AutoDrive.MODE_PICKUPANDDELIVER then - if newState > 1 then - newState = newState + 9 - end - end - end - if self.primaryAction == "input_parkVehicle" then local actualParkDestination = vehicle.ad.stateModule:getParkDestinationAtJobFinished() diff --git a/scripts/Hud/HudCounterButton.lua b/scripts/Hud/HudCounterButton.lua new file mode 100644 index 00000000..4dd35177 --- /dev/null +++ b/scripts/Hud/HudCounterButton.lua @@ -0,0 +1,108 @@ +ADHudCounterButton = ADInheritsFrom(ADGenericHudElement) + +ADHudCounterButton.STATE_INFINITE = 1 +ADHudCounterButton.STATE_ACTIVE = 2 +ADHudCounterButton.STATE_INACTIVE = 3 + +function ADHudCounterButton:new(posX, posY, width, height, mode) + local o = ADHudCounterButton:create() + o:init(posX, posY, width, height) + o.state = 1 + o.counter = 1 + o.mode = mode + + o.images = { + [ADHudCounterButton.STATE_INFINITE] = AutoDrive.directory .. "textures/" .. mode .. "_inf.dds", + [ADHudCounterButton.STATE_ACTIVE] = AutoDrive.directory .. "textures/" .. mode .. "_active.dds", + [ADHudCounterButton.STATE_INACTIVE] = AutoDrive.directory .. "textures/" .. mode .. "_inactive.dds", + } + + o.layer = 5 + o.ov = Overlay.new(o.images[o.state], o.position.x, o.position.y, o.size.width, o.size.height) + return o +end + +function ADHudCounterButton:updateState(vehicle) + local newState, newCounter = self:getNewState(vehicle) + self.ov:setImage(self.images[newState]) + self.state = newState + self.counter = newCounter +end + +function ADHudCounterButton:getNewState(vehicle) + local newState = self.state + local newCounter = self.counter + if self.mode == "loop_counter" then + if vehicle.ad.stateModule:getLoopCounter() == 0 then + newState = ADHudCounterButton.STATE_INFINITE + newCounter = -1 + else + newCounter = math.max(0, vehicle.ad.stateModule:getLoopCounter() - vehicle.ad.stateModule:getLoopsDone()) + if vehicle.ad.stateModule:isActive() and vehicle.ad.stateModule:getMode() == AutoDrive.MODE_PICKUPANDDELIVER then + newState = ADHudCounterButton.STATE_ACTIVE + else + newState = ADHudCounterButton.STATE_INACTIVE + end + end + end + return newState, newCounter + +end + +function ADHudCounterButton:onDraw(vehicle, uiScale) + self:updateState(vehicle) + self.ov:render() + + if self.state ~= ADHudCounterButton.STATE_INFINITE then + local adFontSize = AutoDrive.FONT_SCALE * uiScale + if self.state == ADHudCounterButton.STATE_ACTIVE then + setTextColor(0.16, 0.76, 0.93, 1) + else + setTextColor(1, 1, 1, 1) + end + setTextAlignment(RenderText.ALIGN_CENTER) + local text = string.format("%d", self.counter) + local posX = self.position.x + (self.size.width / 2) + local posY = self.position.y + AutoDrive.Hud.gapHeight + renderText(posX, posY, adFontSize, text) + end +end + + +-- Helper functions to wrap the 3 boolean flags into an int and back. +ADHudCounterButton.FLAG_INCREMENT = 1 +ADHudCounterButton.FLAG_FAST = 2 +ADHudCounterButton.FLAG_WHEEL = 4 + + +function ADHudCounterButton.flags_to_int(increment, fast, wheel) + return (increment and ADHudCounterButton.FLAG_INCREMENT or 0) + + (fast and ADHudCounterButton.FLAG_FAST or 0) + + (wheel and ADHudCounterButton.FLAG_WHEEL or 0) +end + +function ADHudCounterButton.int_to_flags(value) + return bitAND(value, ADHudCounterButton.FLAG_INCREMENT) > 0, bitAND(value, ADHudCounterButton.FLAG_FAST) > 0, bitAND(value, ADHudCounterButton.FLAG_WHEEL) > 0 +end + + +function ADHudCounterButton:act(vehicle, posX, posY, isDown, isUp, button) + if not isUp or button < 1 or button > 5 then + return false + end + + local increment = (button == 1) or (button == 4) + local wheel = (button == 4) or (button == 5) + local fast = AutoDrive.leftLSHIFTmodifierKeyPressed + + if wheel then + AutoDrive.mouseWheelActive = true + end + + if self.mode == "loop_counter" then + AutoDriveHudInputEventEvent:sendChangeLoopCounterEvent(vehicle, increment, fast, wheel) + return true + end + + return false +end diff --git a/scripts/Manager/InputManager.lua b/scripts/Manager/InputManager.lua index 8abec1a9..ed0c0d34 100644 --- a/scripts/Manager/InputManager.lua +++ b/scripts/Manager/InputManager.lua @@ -296,11 +296,11 @@ function ADInputManager:input_start_stop(vehicle, farmId) end function ADInputManager:input_incLoopCounter(vehicle) - vehicle.ad.stateModule:increaseLoopCounter() + vehicle.ad.stateModule:changeLoopCounter(true) end function ADInputManager:input_decLoopCounter(vehicle) - vehicle.ad.stateModule:decreaseLoopCounter() + vehicle.ad.stateModule:changeLoopCounter(false) end function ADInputManager:input_setParkDestination(vehicle) diff --git a/scripts/Modules/StateModule.lua b/scripts/Modules/StateModule.lua index 2e6e66d9..f12cb2ea 100644 --- a/scripts/Modules/StateModule.lua +++ b/scripts/Modules/StateModule.lua @@ -697,17 +697,29 @@ function ADStateModule:getLoopCounter() return self.loopCounter end -function ADStateModule:increaseLoopCounter() - self.loopCounter = (self.loopCounter + 1) % 10 - self:raiseDirtyFlag() -end - -function ADStateModule:decreaseLoopCounter() - if self.loopCounter > 0 then - self.loopCounter = self.loopCounter - 1 +function ADStateModule:changeLoopCounter(increment, fast, wheel) + local newCounter = self.loopCounter + local delta = fast and 10 or 1 + if increment then + newCounter = newCounter + delta + if newCounter >= 100 then + if fast or wheel then + newCounter = 99 + else + newCounter = newCounter % 100 + end + end else - self.loopCounter = 9 + newCounter = newCounter - delta + if newCounter < 0 then + if fast or wheel then + newCounter = 0 + else + newCounter = (newCounter + 100) % 100 + end + end end + self.loopCounter = newCounter self:raiseDirtyFlag() end diff --git a/textures/input_incLoopCounter_10.dds b/textures/input_incLoopCounter_10.dds deleted file mode 100644 index 6005b465dfcc10ebb70e8d6412d994b1e72b3fb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_11.dds b/textures/input_incLoopCounter_11.dds deleted file mode 100644 index cc4e9a28efad4aa0bb4cb426e741bb861d147d42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 YbX~TQt)F}DA@f@ztvy~#-uHt37m$R)lK=n! diff --git a/textures/input_incLoopCounter_12.dds b/textures/input_incLoopCounter_12.dds deleted file mode 100644 index d217ead9e28f2aa7c53886f31fe86a0d0c64503c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 z zAM95&)=+JqMuNOFd-h0`(2w*(<=^-E7>K`GSpQ*on!$w78S4G^YS@o;V8AP*4PBwU z@iy5X!%(2rDy@GRtIMj}mjVA++R_h+p%`z;Bb@H$OsN9+gYb*YLVZa1#RLkylA2U_ z9{xmVP2MWe6^iRT+_?kvIeS02zp3F|KAGQ%sC=dpWm4s?NEi>X9~Kd4+QE7`EBi|k zzT+P%{&!r@+82_K==Vo_XeFiI2Kh>`7sJzRL_QyladSrMIYj?81_T7)s}BH*#6B2C z!*HAN-g*|_Ud0{>TR%+YO-U^0b?R#0pcTr^%{QOV`d4@2>GS-X-~ayc{nYc1hJ4K$ z^>)Aq>iKOZ`tSe!d0v0G2*%U0jt<7q z-$C{#@z`unV0Z*NugsLrE%xK#5&}PeJh7KtDERK9w(LsCxe%G_c!SrfV8Ljnq?+`u5(4)Wk%$7zFS7C; z9$h#(9hL%L8`y6!j3)Ui99cy8|2GK_uZ?kl0I>WICJf^l!2g^do?rfn@qZNTLlbKs z-kND}BD9a<_1Eu$|EAdsG^yVu^1LUC{62;+y^AXN_e40~?BPo+iMQ^rH+#s!-Q?E#Z}W=`%-0w?z$`w#9%$R;;ps%T4AtDIIa~Ls2RY z8a5gH9ciS%b_0*tJ6klVfFl0ST#KI-k4X8y#P7@ne{`W8^a;fs-lp0$t_*&!=&|-K zY`n(yV_P8~ z_C58WzR%}_@c9)SzR8cUNVn)8ng8ME&%^IYV7zpV3eIHtbKA)lCc&H7i!6Urk5^x| zMFHGoec`x8fBDjU5I%J7;eqEBf_%%=WniyK|BFeFMd~8ONPmPqg`>xBUf$OWv~1~9 z(EmvCJ$-_&$oEr7zccwgC(f_1sID-qu5gV5zq9ZI@dv8jC&u*bc$#CH(K=yNK2Egt zQKvUVzPAYaxfUjO@~FV!G7;`xL?}S0uF~;8Sq@jn=ic^XZ4(UO?G(3_{<4s dHxv08(->y$<#M^RW|8$Rl5IP_?Klh<{SSDK@R9%k diff --git a/textures/input_incLoopCounter_13.dds b/textures/input_incLoopCounter_13.dds deleted file mode 100644 index 249a135f3c03ec4675553c0709ab18c5a96351b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_15.dds b/textures/input_incLoopCounter_15.dds deleted file mode 100644 index 9a652d9ede9fb87b6b52842c7269ed157479f385..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 J9mj!0{|}AW)BgYf diff --git a/textures/input_incLoopCounter_17.dds b/textures/input_incLoopCounter_17.dds deleted file mode 100644 index 2be38e4af88a6e4a54b36034232781559d5fcc98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_18.dds b/textures/input_incLoopCounter_18.dds deleted file mode 100644 index be2a581d16fa3486b1efa4694eff23702271ba50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 zcmeHP30PBCzCT&8S;U)wh^f*Nn@CXuxTGo-NVQr+YoX#bX{8~p^XOuAqSg{BO9mZV zTdhDvPy`uUS4OFJf$_|3{5dPw&Jdf2|Y1vGPlBQ{V00pC=B7sLC z7b7-bjRX?n50`gtr9s)k#RhLW)co(KABy~WL+0Q!m!CHL=MWwYbKuzq2cn2gm~pnpf& zVHvDDu1|hu{71lgnraFI?fBY8gsg_&fqb{8G!H3u9t(AJBxxFCHJO!5o*trDz!kT2 zCuuyI_VmgfGS-*Rf;^{fWAPu`zgnEjML`eA#WY=t@SgU+Fk_JadGua{+mL}JCGudN z46*oNxAvol{_-In=%IG-i>2O=aeRL?J^aM@OT2gw?ZI1b5Y-e_0^XgCM?)P(ou)miB0WEh7P!qbB^uhM#9O|d z#9||qQ9nzP7s^Jsqes5)e0KipME>{S88s^?Y#toF<34`xkN5vN_9*t2?%mrfA3Qz! z;$pC{($M>C&phx z)f5$`1_J-^CbSqcbgrqyemf_aRK(yNfIpN(kR2#?ALc1IfOp@1MO8EUzLV?@8W*%H zYCbb|z{vDyNSD%wY_|@dhQGNR|N61wFmaD<_y+vth)_`24H-ggR`31#=%X9XBNXgW zG$`O*g%=_2i?#csgTj2oR)bD+aG~+6y?SLzvXm=kWtBMaid2_?!CNk|DScVE(@T#Q z_q1m6srUYU_%@N-bCtzlufhidJ^_Don`G%uA2EEQnX#WJKi$bCV;3`Dl+CiZQ$L3> z|CalmK^zmDu)zN4;X`-0>%)#Ml*RhYI4GLcD)de(to zsYr<{bMT9tpkHiCIljsYhU;MehZZq-_s~xt-oJ;NS_R5HN(Px$EtDS4ITdfzdRQUi95eCsMb0Lk)}Xhp8`}0^^n09+a`P;SBFbzYO7z1VVlPawAU6B< z_QD@9j@6JXTS-GQ~Ay4%i!+NlAg&Dp8U9 zSln+&eF5;^`}g6U1$dhTN{bupO{-qpDA87RxV{NlwpuQ(cU37Krb^;s;z+!tG}f#T zvi&GSPyFPgIwG*7)Y6=fL1berN;5hTN*DjrkWi6dK>QJnF2jgl3NQ!`WhXq_%oY2} zm3h6-$JpWqb610}Aa)7jsVwdlJ5*K^Iu&yMz3APmBHvlq#EYt`s*+GoY?s9n{Qr9+ zhr{~W@t;fq_-k{97rRSi4RBmc7%%pR*ZxI5;y^fXH%o-zWpSrFOX28UU+?~2nvqRK zIylj-%jJ?&b#--9{l<(h?E0!H_4Bu;r>9<6We>h=NU1Kg6Z=S$lyL(aPy`AR#Q{Hy z5zEJhpCbHS2K=2&_?!OAD1!G@S=pkQ5?59D+qt1@3sc6sh8&NbDRCH#I-Gi?RS=+p z`N+iqa^G9|?Hr*7Zm`8YH~~JWa9F&eeQ#V$XX7kM;(Xfn7}XLJlYTXX#G?Rj;XvhY z6qTBzU7M%1yWg5_G#U&}dNQ+LAV0(bw)%S9iJ1Muc!YiTo;`bd_lwYj4UR7-wycVV zS7~gMC<+bY{j@!=X4)Z_YpEG=IUK+8N8T_C0;$H@M0sQ&5!{0g6!HF!P z+9T5B5`cf+G%D?rkmXs?WPji0`zjuxCg&8^Y?BqkSZ8#Y-WuBAD977mZZpVNO}A;q zu;E{I;CE#!_U)3|s>UM2$P-0dQ&Xt%X=|is!QjMk6{(v2dO@+1*ynjle3=zN5Vp8; zR};IlcYk~75ic+eIwqo~7Qy%R2JO7p0t?Eb+Y(@R*GSD#iZUrR=c=rmbHEP#%wdnQ zdHq>7*kg=8ds}<#NvdYEu_6!r$(N@aj7HrRO{&;`@=^-qiNt2h3jljVga(oMbvCj< zVB#MA(+Kg<>gsCzWdtz47k?hWukhl({TIXkLBKz;X7l?*{_FJ)Yh&2trJAMr`EO0< zigg)jM*KDKs}JG-&`8AQ+^YW<6>u(DhwQfFnUeX5TEi;86$hiYQwLH%Un5nRUGzkv z-&`9G5c!JOrLGWS|Kd4AFm7Fr(0Td=QO&mM6wQz`b-SdF8yT9*N@8!)Lqwo&bs9PV z@03IkoGtD(QaS&-_GXX$@jfs);*BGCBa_N-?lH&2nDn#F$78wTwyG-JFj;*SAG+YD zjs*GNl2QN7sLbc_E508r0r@+o+Eb?ke=X$C8@@>I@z-Qze%;h!@BH@CkHEOA`V7oh z9cglOB;^gSNKLtXil4hKSpx15jaPy^8WzE_WAJ~Ja^G9%@oU7@Bi@5Vz9N%J_4$3f zH$5TYwPCV=WY<@A=e=J`g<<0dKyTSYE#!V)+9{7>0Ph|preBSjyM>bE@*+}dq zt;rFY`F!Uq!o{Hznndg}tod+?dPyb3SJaVUPj_ItBaZmvO4gRo34haaT(Bpe;7$CC zAH@4SJdBa=kRJfR@l5Mr*V!2{&o(DZv|w-Uk3CUTnJNh+_TeXv8%CUYrHM5(82o#= zBu~!g;M`j9*SK*1nUX}k33bMHCv-mVtA6?j^L*`xQ`OIt_)lnLlL(b!`fL=$Hx^g$ zc@wzel8($gyd3<=ykM}0;rli@2cH)QlNlAe=PY3C&HnarBH|rG)KsFcKprK&A}T41 zyp|9$65^v~5J)0wc8|SLZ%?^BdhQyy8I{&BkV?|Z>tzi=L`MzTN7mst@;waf?s z`v&9Vv0#4Op5KW)V(ib}eCAIZ{|}&Qa+;Xu4YvS$ARXmwO=&OeBJvT;H^=ocI~;Ql zZMGBtMM-Ot@M}O1d7^|O;vcgWHi+lJLHr@N89v-I zQ-B{pz~clMgZ*%$Dv)*{UTlKk|Gbncuwc&ky8H#NzU8fM2r2 z1m~}fH9b@Z^LuTdTw2-mbb2}Fb*I(N>QzERk(Q}}_Af#=BYg)59 zk}a%qNvX@(UFwq|c04{!SAv8Q)Vry^(psz^X%p!+xA@Bhm1AL#t=2YNnSQwaG(u>VaC{T~Br3iBub#}JU$QYK!n zsYqYGQ^jf^`CnSe$Chs&f^CI-;HSJDpqYE)xj|ut3c^qLl9kclL1B|*@npR6JQ1^(xcuVXjZ3OF11xUn!JK{Pm>l z2@-z+`x5-yUmV~5^1lE0F30~VQEE;gyrzM_#^i^V9LN^zA^1UjSSj!GFZ=L22>z3m zJyO9JJ-*BFpAGhzW^Mk*q<#YMZ^e(5 z8K}v5D+>w!0eKpTpTa@%frUh!vT&u9Z321jfp{^nFS@cYV=gZBU4QKX$mi1ZJiMY} zMZ(SqR;9v6Y|B&j#b5gH-w5#+lVBXl7n-bk!@gm=%OcwnVhI16%}T{kYW62GCjLL= z2n+I=zwy79e~4#Gkhs(PA%%EjT1_F;1uR*pcQ_pTADk>sft1t5xe)J5gL;8nPA%)( zjVT@lSIqG>)0px92|Y0RKo)j-^}R=Na)bfqhuevkKydyUIioNB3kHzy2?~=#I6-g3 z36MN&n-LQ<8sh&Nnx0KLK8(KhVRVu6rAVbharf1i-sSxNR8M_Dx-&Eq^uMkh>I+gP zpA*zil1nriTL9kwt%~5-1^Su*dW$@7AZS}b|9?vA2XsvS6=kzMiWf-uh!6C_#BT__ zsNg^F3uif3Oac|TADw8hZXF8sLZ#I8dWa7wpk64DQWO0T@&8Fhzv=%w&+m|5*$VXq zT#{cgL4Co>VNhQ%ekrBgkt{VP>U21BPb+I!Jd4;NS&9KL5wpXZ_Unz+J}wfPXd@UIc#?9GUlB z5m8q}{;kWCrHOFhPXZ0BKoKiCOfKfbaSOj2;yKEAs+HL9TCFzTcwgFd{GM>J=+$J2 zaG!^^MlB6o9!?J-@p+>$BNKl^@^A5AF9`e~|Hi)Mez66srk-9EMd}eqJz6+rV22n+ z0sjw9@P*?}>$^LCzq<^7sE3985~-gUX$SKSE%$Sa5>oREryO}hs?#MJ@yh{v)-I8- z3-mo73Mg*DtK7=g1K)ZSK750f`1f(s@yS7;ABg>@LwHT9dx$@&Cz6Y80Z>2cA=-o3 z0}YBjB>(VFGU5h%6b6t7NOf&=0o8GJM$E=XxzbbD%~hi*!@-cWvoOE1MSQ-HbI0+z z)AHah!@vK2J`DDMK}}JUg{hx#m|CRX`j9fH=gBAaA~~|9pRSbdC zed#2O<4HbY8~FD<`GiN1jN}gpzK}n#HQiVHcalG7UDPBZ`2#(r{j8(=8sr~H{-8{7 zFV*tjA>i-Xaoa{HKm7IM-Cus(<@n!@p8~1Sp8VsaB5yun2a`|0_~P88y$DU{mrsbG zvR@=307Lx>U53#8l-A>=ucuagR>c*NAp2{6K}ev0 z-xpx~>4D$B7mpu*eK2AN-+VqS{=4fRpnQ&j{)hK#@}U0#>iZzUfXAe|NqsTYBTF@; zf8^e_RqfWn`l#q+sku(8PwwgGBK;k|NIx<(8T^>_KhEsW)77O=m4l{N(SHyd$?KYat@X>1N2e$Bfdv!I?9_CrthBQ2|u>3?9a zz4RH}zboY>=nqVmq8mwc=P>A}JU0k!`8-*&o2J>Fe$Y=jLa6$vZk9ygq8)!?>O(o# zi=n;~$jcVM%&#XO_e1@Y16;of?$@N=0{Fsu0bm?Zu`*$2Jd4z$=;|R~@OKH2Jwnwd zDB+%xk`g9J?EUZXv^LK{3>)zz1KPPyqQ!F7%^8{2L0c8c99s zt6A{Zh-4IlYFZ>5BfLPNDW49BL0gQU?lZxatWXGZ$+a)wWrHQKWje< z^;PpUIP;+#HiTq?!e)x0UXl;~7#xEh>;n55;;sf9lO1mVywF&wTqA{edJ;~P>b>=v z$xc=`wvpB>=(KkLai7CBqSGx4S#f6?MD zazZ;{X`QGgoTLOveVfayRASM6(BDxct3L6D#9Xgy!IJp0=#1jSDJ#~5Hn=Z6kHb^1 z2u8=V5+Q$?4?S%-lk5-gL5tf5>c6b|^Hj@3sP79Y0)OL>+;^Y%_b>=OGrxzyno6lR zt%iQ9MyR)ka2V8&NknXEE70Q`QXe>=-i;FhFX$H%)xrV!sh<9T)FVuOa)9gK2mZFP zW;5j5-jHgw1_Ry?`^^=xiphN096r9+vC3r?s9-(PO{cWhGT_Ii#X>cqKU50wRR6Ur zQ!Yoq{B{A}0lv7N6$}ejk$PViKn?!bs;Fg+he>^2JTzF~iR}wdaz{hI<*ra0t>!o= zsK5EM?_zSEy^q#(LHxfm>~GMol`J*rXeE9Z=n;-$m>(Yq2?rpmt$_5$&6L!hZYT%; z{A91cNcxYc0j^&O^^Au>U)rJGQf~!*^b2SeEY15kvH57IkFq`y|Cu~Jn^o*=355^7 z*!2XX-z@HO^L+RZe1RDPzPan*lO9VD_gHpZ4CyDe8XC<1DOz!5LCxmF(BCPgp&$-F z2N!V(e?z}G;w!OB+jl=Lxu1)XHzjcYeUPv);qh=&>o|t~kSe~uifZ|k_@|BTT~a0V zBmG$Z?|=Px$iFo)_QT)=g9)Ki)XvsQsQW*U8GPg>CeFUXefpPt!8oUGo5k8 z4IkAV4NbCjY7dGsu68YXoHf!8_5Gz9`T&vNX6gW;7tMsnOmNtesS;-h%!lX?v+%qAnuYnLi2m~*DE_xx&*&H8FaB_T z6MnxW_prJE^u=4#8GR9y+CBvMy;2GNy@5Wy*gOxSu3 zpa}A7$r1?9`{20j4N=+Dx-J-lJdMZ8?iaD|SHb$hULg6KU=ZG_RZ|<3Gec`@Pj3bO zJt&()_WMj7>4zEUd~W&vPrQEk{eZ2Ey#VzIkgw9?DSJc-WqPPDkV_{Md*Lzy4Tq+9 z6geY+eGK?46el4S#G@bggEuzzhvts+99QAaS^UY$Lyi7~-kv1#pY#{~`tkogRgB+*}t{X_bD?m&IX zPfY({uk^%cEp8PoECu4T4UE26Bh}q7fqwAU9|C!9cOR3u=x!Jj{Xpb*i&SF^N3*Gx zZ@l_i9jO2Tc?t zZ9awZx_gmB+u-4WzFwXcWpS??g*ZNeRKM@vcFbMODM5mgKK;r2zW;q4@N25s4E>S3 z3E(bl#Q{^lCNtW$O~7t&ZEwYqh+_hir%Unnn0OVkVz3_y%)}o9`(Y)>*U{}0*8+m+ z&f~H5>F7Z?e>TLynMC>z*}e~DQ{Z(6!|Q*0J{BFwW=lovVyG{jG-%KueD822weD|C zL1nuorw;w&TO46sSJjy(+Ln1t`ns^ z$%Iv=309v(E3wZVGah-wkN4$v#p&(PH!`i6P2^d>^h7oG_EVO%KGw+xB^`63PBTW{c@yXOeQvne8P=9_&icxc>z5xX-T?9qibkS`Q_ zaEt1_(Z!G-nbBG>TExp96`DIL6ytPnzgWNLj|Bbg<-gbVso*|)r~s&U&xQ58HJ#oK z@@aV6yOsB=RGswB@soAof584V3qs;K$GXb-0)xD`a}UMdMC>uruL(bYgCZ$oPyf;2 z1h7}p?e@PvzmEZVvRYmIJB*jEhWbktUa(qMBGH3?`G#cSbG~cwDDJctP#ImyouQ9> zQ_w1A?ERDQ{AZou4&0iLy$$~TpFf9EZ{(^sHIe=s6-)rn$_g9Dd5z_fw@4Q*`-YFl za-T7=1rc3}W1)}y=Zsbf^>~)oUU8c_KV+};e?AwO&nLV5COyI?eOLbQzCYg0DbJ?SxB>QvV0iSz>WPiT5{(lPBZWBuKV~gAe zCYBhryN?Lf(>J_Z1ZrbwXH^&xMw@MtpUmUZpqvP53JK(^uhs6CdTXSnM+2 xr<2}%&i`SH+m$9OpENEx?DTb6+*R4g`r#H2o6Vj*o9u6{Y|D`?M_{_>e*g(&(E$Je diff --git a/textures/input_incLoopCounter_19.dds b/textures/input_incLoopCounter_19.dds deleted file mode 100644 index 179e5063b726da03e4dea0bc39d293e45f331cd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_3.dds b/textures/input_incLoopCounter_3.dds deleted file mode 100644 index bb19514814e59737fb6833333cfed3f134c2e110..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_4.dds b/textures/input_incLoopCounter_4.dds deleted file mode 100644 index b504e94e63941a88fc26995762052c05df08f64c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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

ACeh)iUF3Huwa z*Xvn-KK{o3F#i6L(;qtqJpuP&{k`ebiBI7VE8M?UCeSzU$)itmzmK49hu?qDn23pj zzq$QzH27cWFG4&P(=raWaKN4bP02%W|3aWuG#>P?5B#1+H0#gDzt|u8^RE5V@OO1W ze@2Muutf=Vg1-NRitKBAN{6`F%6g z(eeMuE&+ehV8eJ}2uy%JuN`Z0k2hg^>hMP&jkr<&>@cRkBJ;yE(0}d_K+wpS(+?9r zg8DEno(Sm^!{h?euMX-J;XEV6Q;`KT`;XFjLOj95ybb5;BBTBr{Z8yp2?~a?!U5It z{09y2d^ntE4ZlJE@Ov{o>g$SGZ(Td6^i5#DJ#apXrWcR8%enUBloEd$VW|k*E*c5` zC#-+GrkE9OLw^jPKMv17&8GCUzc=XLRo_AU)?++$#=)GN`!xa%UuBR7ouVB$QYRXZ z>!H#7AM8aR`(FV3_-1#1ZdTG0mFeRNt|$B+k%&&!2Aj0_`vq>k3l^$xY_DAV_X6ST z$5fy^fcO;vf5J-_fcy!|N2d8_`6;rtcF`aB4^~&e?@QqQ4!FO|KQ9sKSwMroKwQ7y zi}nKHBTN5?{ek!mFOapYtvH%?@0KN;{BNUU6%AK@-;bm{Zu6mOpKa&>nof9(d~iCS zp~_}Kl?;Brs#6qp0EYS`|cS6?c_6&UN4kS zFm>L%;(%U=T1)Q8!1Ze! zyS7bKniBxyW0dgo04-y!uXlFt4%qp5J`*3Wq^yic^T;<3JAU-P56jvN^KCR=C>mFkIlm>-4DqMWknACyC&o49hyB vscF&^^EW&-=#Pa9rwqHCo|cxOJi86;*Z%3BjKTcp;>EoGsbCf0dJ^(KQPf4G diff --git a/textures/input_incLoopCounter_5.dds b/textures/input_incLoopCounter_5.dds deleted file mode 100644 index 63ccfdc80edc30536e74324661ce88a4b77656e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 U$0CRiEL#S`Kt8ktOmBq#AB!wV?f?J) diff --git a/textures/input_incLoopCounter_6.dds b/textures/input_incLoopCounter_6.dds deleted file mode 100644 index dae46c14e44eafe5e975e4923b4cc5a06b003612..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 diff --git a/textures/input_incLoopCounter_7.dds b/textures/input_incLoopCounter_7.dds deleted file mode 100644 index e9eaeb498f549b7d85459a3cc4cfe70cbfc1d78e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 YUrHlho5J#+k3S9q@9{Iy|BTmv0}$*E{Qv*} diff --git a/textures/input_incLoopCounter_9.dds b/textures/input_incLoopCounter_9.dds deleted file mode 100644 index 9215a421f3e15f30b0a31c4e59a14b27e2fc465f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22000 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 EY*W$4yOPMZ&W>Or*c@LjlevyW~{h1WH zZa1eAJ9hlp#jWu3AI1|h;O7ApNgeF>&fj#E6xO4HNz{nW_g3QV{26^dG?-nN#H!)y zh!e@M&ScIWYw?EjHJ~B(ELI>io!Z&!^>4ybdiYlVW>VUm8~!&S6ixBFlRVFtw$$-B z$v@jpufK!!WJ6>0{qMMapW%PUpKo-&-)lPn}_iI_4nUn{O_TsyO*D1{6EJ3WBh;j>T68@ z{l4pK4F56wAp-yGe)C<|KV$v>_nn_(_>bW~TK~a+0l&dl@S7SMv;T$T0pR}(-~R=K CEOb2p literal 0 HcmV?d00001 diff --git a/textures/loop_counter_inactive.dds b/textures/loop_counter_inactive.dds new file mode 100644 index 0000000000000000000000000000000000000000..862c73eedba08a8c3cdd8341f1848bd969ec011b GIT binary patch literal 16512 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 Date: Sun, 8 Oct 2023 17:21:27 +1300 Subject: [PATCH 10/51] #962 #858 #204 - Optionally, set a vehicle specific maximum trigger distance --- gui/vehicleSettingsPage.xml | 8 ++++++++ scripts/Modules/DrivePathModule.lua | 4 ++-- scripts/Modules/TrailerModule.lua | 4 ++-- scripts/Settings.lua | 23 +++++++++++++++++++++++ scripts/Utils/TrailerUtil.lua | 6 +++--- translations/translation_br.xml | 1 + translations/translation_cs.xml | 1 + translations/translation_cz.xml | 1 + translations/translation_de.xml | 1 + translations/translation_en.xml | 1 + translations/translation_es.xml | 1 + translations/translation_fr.xml | 1 + translations/translation_hu.xml | 1 + translations/translation_it.xml | 1 + translations/translation_nl.xml | 1 + translations/translation_pl.xml | 1 + translations/translation_pt.xml | 1 + translations/translation_ru.xml | 1 + translations/translation_tr.xml | 1 + 19 files changed, 52 insertions(+), 7 deletions(-) diff --git a/gui/vehicleSettingsPage.xml b/gui/vehicleSettingsPage.xml index 75bf7088..64f1bb08 100644 --- a/gui/vehicleSettingsPage.xml +++ b/gui/vehicleSettingsPage.xml @@ -163,6 +163,14 @@ + + + + + + + + diff --git a/scripts/Modules/DrivePathModule.lua b/scripts/Modules/DrivePathModule.lua index ef47a3a3..de5cd0b2 100644 --- a/scripts/Modules/DrivePathModule.lua +++ b/scripts/Modules/DrivePathModule.lua @@ -269,12 +269,12 @@ function ADDrivePathModule:followWaypoints(dt) self.speedLimit = math.min(self.vehicle.ad.trailerModule:getBunkerSiloSpeed(), self.speedLimit) maxSpeedDiff = 1 else - if self.distanceToTarget < (AutoDrive.MAX_BUNKERSILO_LENGTH + AutoDrive.getSetting("maxTriggerDistance")) and AutoDrive.isVehicleInBunkerSiloArea(self.vehicle) then + if self.distanceToTarget < (AutoDrive.MAX_BUNKERSILO_LENGTH + AutoDrive.getMaxTriggerDistance(self.vehicle)) and AutoDrive.isVehicleInBunkerSiloArea(self.vehicle) then -- vehicle enters drive through bunker silo self.speedLimit = math.min(12, self.speedLimit) maxSpeedDiff = 3 else - local isInRangeToLoadUnloadTarget = AutoDrive.isInRangeToLoadUnloadTarget(self.vehicle) and self.distanceToTarget <= AutoDrive.getSetting("maxTriggerDistance") + local isInRangeToLoadUnloadTarget = AutoDrive.isInRangeToLoadUnloadTarget(self.vehicle) and self.distanceToTarget <= AutoDrive.getMaxTriggerDistance(self.vehicle) if isInRangeToLoadUnloadTarget == true then self.speedLimit = math.min(5, self.speedLimit) end diff --git a/scripts/Modules/TrailerModule.lua b/scripts/Modules/TrailerModule.lua index a1c2f9a1..88512eec 100644 --- a/scripts/Modules/TrailerModule.lua +++ b/scripts/Modules/TrailerModule.lua @@ -448,7 +448,7 @@ end function ADTrailerModule:updateUnload(dt) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_TRAILERINFO, "ADTrailerModule:updateUnload ") - AutoDrive.setAugerPipeOpen(self.trailers, AutoDrive.getDistanceToUnloadPosition(self.vehicle) <= AutoDrive.getSetting("maxTriggerDistance")) + AutoDrive.setAugerPipeOpen(self.trailers, AutoDrive.getDistanceToUnloadPosition(self.vehicle) <= AutoDrive.getMaxTriggerDistance(self.vehicle)) if not self.isUnloading and not (self.lastUnloadRotateTrigger) then AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_TRAILERINFO, "ADTrailerModule:updateUnload not self.isUnloading") @@ -604,7 +604,7 @@ function ADTrailerModule:lookForPossibleUnloadTrigger(trailer) -- local distanceToTarget = AutoDrive.getDistanceToUnloadPosition(self.vehicle) local distanceToTarget = AutoDrive.getDistanceToUnloadPosition(trailer) - if distanceToTarget < AutoDrive.getSetting("maxTriggerDistance") then + if distanceToTarget < AutoDrive.getMaxTriggerDistance(self.vehicle) then -- silo trigger - found by CanDischargeToObject, no need to loop through all triggers if trailer.getCanDischargeToObject and trailer.getCurrentDischargeNode and trailer.getDischargeState then if trailer:getCanDischargeToObject(trailer:getCurrentDischargeNode()) then diff --git a/scripts/Settings.lua b/scripts/Settings.lua index 1fcab523..89af02c2 100644 --- a/scripts/Settings.lua +++ b/scripts/Settings.lua @@ -740,6 +740,18 @@ AutoDrive.settings.maxTriggerDistance = { isVehicleSpecific = false } +AutoDrive.settings.maxTriggerDistanceVehicle = { + values = {0, 10, 25, 50, 100, 200}, + texts = {"gui_ad_useGlobalSetting", "10 m", "25 m", "50 m", "100 m", "200 m"}, + default = 0, + current = 0, + text = "gui_ad_maxTriggerDistance", + tooltip = "gui_ad_maxTriggerDistance_tooltip", + translate = true, + isVehicleSpecific = true, +} + + AutoDrive.settings.useBeaconLights = { values = {false, true}, texts = {"gui_ad_no", "gui_ad_yes"}, @@ -1162,3 +1174,14 @@ function AutoDrive.readVehicleSettingsFromXML(vehicle, xmlFile, key) end end end + +function AutoDrive.getMaxTriggerDistance(vehicle) + -- the max-trigger-distance can be set globally and per-vehicle. + -- a per-vehicle setting of 0 means "use global value" + -- NB: this might not be the best place for this function + local distance = AutoDrive.getSetting("maxTriggerDistanceVehicle", vehicle) + if distance == 0 then + distance = AutoDrive.getSetting("maxTriggerDistance") + end + return distance +end diff --git a/scripts/Utils/TrailerUtil.lua b/scripts/Utils/TrailerUtil.lua index 10909a94..0a8ca95a 100644 --- a/scripts/Utils/TrailerUtil.lua +++ b/scripts/Utils/TrailerUtil.lua @@ -713,7 +713,7 @@ function AutoDrive.getTriggerAndTrailerPairs(vehicle, dt) local trailerTriggerPairs = {} -- local trailers, _ = AutoDrive.getTrailersOf(vehicle, false) local trailers, _ = AutoDrive.getAllUnits(vehicle) - local maxTriggerDistance = AutoDrive.getSetting("maxTriggerDistance") + local maxTriggerDistance = AutoDrive.getMaxTriggerDistance(vehicle) for _, trailer in ipairs(trailers) do if trailer.getFillUnits ~= nil then local fillUnits = trailer:getFillUnits() @@ -918,9 +918,9 @@ function AutoDrive.isInRangeToLoadUnloadTarget(vehicle) else ret = ( - ((rootVehicle.ad.stateModule:getCurrentMode():shouldLoadOnTrigger() == true) and AutoDrive.getDistanceToTargetPosition(vehicle) <= AutoDrive.getSetting("maxTriggerDistance")) + ((rootVehicle.ad.stateModule:getCurrentMode():shouldLoadOnTrigger() == true) and AutoDrive.getDistanceToTargetPosition(vehicle) <= AutoDrive.getMaxTriggerDistance(vehicle)) or - ((rootVehicle.ad.stateModule:getCurrentMode():shouldUnloadAtTrigger() == true) and AutoDrive.getDistanceToUnloadPosition(vehicle) <= AutoDrive.getSetting("maxTriggerDistance")) + ((rootVehicle.ad.stateModule:getCurrentMode():shouldUnloadAtTrigger() == true) and AutoDrive.getDistanceToUnloadPosition(vehicle) <= AutoDrive.getMaxTriggerDistance(vehicle)) ) end end diff --git a/translations/translation_br.xml b/translations/translation_br.xml index a2d8b58b..e970c1dc 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 49aedfef..be0eae25 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 1a0a6f82..e539d04f 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index e0e8fb46..c7dd25d0 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -172,6 +172,7 @@ + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 8a83806e..d3a3789f 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index be8910e0..c3eeb46d 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -164,6 +164,7 @@ + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index cefdfe27..fe67c569 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 252c5266..29852a9f 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -164,6 +164,7 @@ + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 33e4749c..dae3838c 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 92e28d27..3685f0a8 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -168,6 +168,7 @@ + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index d1a2dcaf..9fd2ea65 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -167,6 +167,7 @@ + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index bafd3295..45dac9b9 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index c2bc7626..ca56a75d 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -166,6 +166,7 @@ + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 4737e928..59934895 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -164,6 +164,7 @@ + From 4a204979d580e6a4cf54ae86d0dec1d97c3413af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20M=C3=A9ndez?= <46683179+KCHARRO@users.noreply.github.com> Date: Thu, 12 Oct 2023 22:27:53 +0200 Subject: [PATCH 11/51] Update translation_es.xml --- translations/translation_es.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_es.xml b/translations/translation_es.xml index 8be5b564..69ebf8e5 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -301,7 +301,7 @@ - + From e1b5e1cfac3b1c0bd1e6c575b968ba0926e2ce63 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 15 Oct 2023 18:29:09 +1300 Subject: [PATCH 12/51] Issue #996 - Use 7 bits for loop counter stream --- scripts/Modules/StateModule.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/Modules/StateModule.lua b/scripts/Modules/StateModule.lua index f12cb2ea..94600e11 100644 --- a/scripts/Modules/StateModule.lua +++ b/scripts/Modules/StateModule.lua @@ -201,8 +201,8 @@ function ADStateModule:writeStream(streamId) 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.loopCounter, 7) + streamWriteUIntN(streamId, self.loopsDone, 7) streamWriteUIntN(streamId, self.speedLimit, 8) streamWriteUIntN(streamId, self.fieldSpeedLimit, 8) streamWriteUIntN(streamId, self.parkDestination + 1, 17) @@ -233,8 +233,8 @@ function ADStateModule:readStream(streamId) 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.loopCounter = streamReadUIntN(streamId, 7) + self.loopsDone = streamReadUIntN(streamId, 7) self.speedLimit = streamReadUIntN(streamId, 8) self.fieldSpeedLimit = streamReadUIntN(streamId, 8) self.parkDestination = streamReadUIntN(streamId, 17) - 1 @@ -267,8 +267,8 @@ function ADStateModule:writeUpdateStream(streamId) 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.loopCounter, 7) + streamWriteUIntN(streamId, self.loopsDone, 7) streamWriteUIntN(streamId, self.speedLimit, 8) streamWriteUIntN(streamId, self.fieldSpeedLimit, 8) streamWriteUIntN(streamId, self.parkDestination + 1, 17) @@ -299,8 +299,8 @@ function ADStateModule:readUpdateStream(streamId) 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.loopCounter = streamReadUIntN(streamId, 7) + self.loopsDone = streamReadUIntN(streamId, 7) self.speedLimit = streamReadUIntN(streamId, 8) self.fieldSpeedLimit = streamReadUIntN(streamId, 8) self.parkDestination = streamReadUIntN(streamId, 17) - 1 From bb588184d1c924b498bda0bda7ba34495e0b7b31 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:07:27 +0200 Subject: [PATCH 13/51] Update modDesc.xml --- modDesc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modDesc.xml b/modDesc.xml index 61d08954..99ca7b0f 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -32,7 +32,7 @@ Différents modes d'utilisation ont été ajoutés depuis les premières version - 2.0.1.4 + 2.0.1.5-RC icon.dds From b8dff90d29005db91dd4634b800b4fee459658c3 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Fri, 27 Oct 2023 21:02:42 +0200 Subject: [PATCH 14/51] fix #998 - if AD not startet by player we have to force the delivery target change if rotate is active --- scripts/AutoDrive.lua | 5 +++++ scripts/Modes/CombineUnloaderMode.lua | 2 +- scripts/Modes/DriveToMode.lua | 2 +- scripts/Modes/LoadMode.lua | 2 +- scripts/Modes/PickupAndDeliverMode.lua | 5 +++-- scripts/Modes/UnloadAtMode.lua | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index 9d5c27ca..5984eeb6 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -92,6 +92,11 @@ AutoDrive.FLAG_TRAFFIC_SYSTEM_CONNECTION = 4 -- add this to measured size of vehicles AutoDrive.DIMENSION_ADDITION = 0.2 +-- AD invoked by which type of user +AutoDrive.USER_PLAYER = 1 +AutoDrive.USER_GIANTS = 2 +AutoDrive.USER_CP = 3 + AutoDrive.colors = { ad_color_singleConnection = {0, 1, 0, 1}, ad_color_dualConnection = {0, 0, 1, 1}, diff --git a/scripts/Modes/CombineUnloaderMode.lua b/scripts/Modes/CombineUnloaderMode.lua index fca772bd..6ec5919d 100644 --- a/scripts/Modes/CombineUnloaderMode.lua +++ b/scripts/Modes/CombineUnloaderMode.lua @@ -41,7 +41,7 @@ function CombineUnloaderMode:reset() AutoDrive.getAllDischargeableUnits(self.vehicle, true) -- force initialisation end -function CombineUnloaderMode:start() +function CombineUnloaderMode:start(user) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:start start self.state %s", tostring(self.state)) if not self.vehicle.ad.stateModule:isActive() then self.vehicle:startAutoDrive() diff --git a/scripts/Modes/DriveToMode.lua b/scripts/Modes/DriveToMode.lua index 22928cd7..7d8ddb21 100644 --- a/scripts/Modes/DriveToMode.lua +++ b/scripts/Modes/DriveToMode.lua @@ -13,7 +13,7 @@ function DriveToMode:reset() self.vehicle.ad.trailerModule:reset() end -function DriveToMode:start() +function DriveToMode:start(user) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "DriveToMode:start self.vehicle.ad.onRouteToRefuel %s", tostring(self.vehicle.ad.onRouteToRefuel)) if not self.vehicle.ad.stateModule:isActive() then self.vehicle:startAutoDrive() diff --git a/scripts/Modes/LoadMode.lua b/scripts/Modes/LoadMode.lua index 763dc6fe..e9f39a8b 100644 --- a/scripts/Modes/LoadMode.lua +++ b/scripts/Modes/LoadMode.lua @@ -21,7 +21,7 @@ function LoadMode:reset() self.vehicle.ad.trailerModule:reset() end -function LoadMode:start() +function LoadMode:start(user) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "LoadMode:start start self.state %s", tostring(self.state)) if not self.vehicle.ad.stateModule:isActive() then self.vehicle:startAutoDrive() diff --git a/scripts/Modes/PickupAndDeliverMode.lua b/scripts/Modes/PickupAndDeliverMode.lua index 5ad2ce87..89632306 100644 --- a/scripts/Modes/PickupAndDeliverMode.lua +++ b/scripts/Modes/PickupAndDeliverMode.lua @@ -27,7 +27,7 @@ function PickupAndDeliverMode:reset() self.vehicle.ad.trailerModule:reset() end -function PickupAndDeliverMode:start() +function PickupAndDeliverMode:start(user) AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "PickupAndDeliverMode:start start self.state %s", tostring(self.state)) if not self.vehicle.ad.stateModule:isActive() then @@ -39,7 +39,8 @@ function PickupAndDeliverMode:start() end self:reset() - self.activeTask = self:getNextTask() + local forced = not (user == AutoDrive.USER_PLAYER) -- force target delivery change if not started by player, i.e. CP + self.activeTask = self:getNextTask(forced) if self.activeTask ~= nil then self.vehicle.ad.taskModule:addTask(self.activeTask) end diff --git a/scripts/Modes/UnloadAtMode.lua b/scripts/Modes/UnloadAtMode.lua index e4972ff5..c70547f3 100644 --- a/scripts/Modes/UnloadAtMode.lua +++ b/scripts/Modes/UnloadAtMode.lua @@ -18,7 +18,7 @@ function UnloadAtMode:reset() self.vehicle.ad.trailerModule:reset() end -function UnloadAtMode:start() +function UnloadAtMode:start(user) if not self.vehicle.ad.stateModule:isActive() then self.vehicle:startAutoDrive() end From a906273988f28a5cb18f999e2cfe78cdf4d4b5fd Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sat, 28 Oct 2023 00:54:25 +0200 Subject: [PATCH 15/51] fix #998 --- scripts/Manager/InputManager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Manager/InputManager.lua b/scripts/Manager/InputManager.lua index 8abec1a9..3ce92a78 100644 --- a/scripts/Manager/InputManager.lua +++ b/scripts/Manager/InputManager.lua @@ -269,7 +269,7 @@ function ADInputManager:input_start_stop(vehicle, farmId) vehicle.ad.stateModule:setActualFarmId(farmId) -- input_start_stop end - vehicle.ad.stateModule:getCurrentMode():start() + vehicle.ad.stateModule:getCurrentMode():start(AutoDrive.USER_PLAYER) if AutoDrive.rightSHIFTmodifierKeyPressed then for _, otherVehicle in pairs(g_currentMission.vehicles) do @@ -278,7 +278,7 @@ function ADInputManager:input_start_stop(vehicle, farmId) if otherVehicle.ad.stateModule.activeBeforeSave then -- g_currentMission:requestToEnterVehicle(otherVehicle) - otherVehicle.ad.stateModule:getCurrentMode():start() + otherVehicle.ad.stateModule:getCurrentMode():start(AutoDrive.USER_PLAYER) end if otherVehicle.ad.stateModule.AIVEActiveBeforeSave and otherVehicle.acParameters ~= nil then -- g_currentMission:requestToEnterVehicle(otherVehicle) From d8bcfa0664ade941d94bf98780ede42cf7c5ea6c Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Mon, 30 Oct 2023 15:27:08 +1300 Subject: [PATCH 16/51] #990 - make the current filltype visible even if nothing is selected --- scripts/Hud/PullDownList.lua | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/Hud/PullDownList.lua b/scripts/Hud/PullDownList.lua index f987a493..7bcbbb7a 100644 --- a/scripts/Hud/PullDownList.lua +++ b/scripts/Hud/PullDownList.lua @@ -229,27 +229,29 @@ function ADPullDownList:onDraw(vehicle, uiScale) end end end - local isSelectedFillType = false + + local makeBold = listEntry.isFolder + local makeBlue = listEntry.isFolder + if self.type == ADPullDownList.TYPE_FILLTYPE then - isSelectedFillType = table.contains(vehicle.ad.stateModule:getSelectedFillTypes(), listEntry.returnValue) + makeBold = table.contains(vehicle.ad.stateModule:getSelectedFillTypes(), listEntry.returnValue) + makeBlue = makeBold or vehicle.ad.stateModule:getFillType() == listEntry.returnValue end + setTextBold(makeBold) + if self.hovered == self.selected + (i - 1) then -- mouse hovering over selected item - if listEntry.isFolder or isSelectedFillType then - setTextBold(true) + if makeBlue then 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) + if makeBlue then setTextColor(0.0, 0.569, 0.835, 1) else - setTextBold(false) setTextColor(1, 1, 1, 1) end end From a3e55c07066fe541d0fa8c5d7260920b23b5131f Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Mon, 30 Oct 2023 17:11:47 +1300 Subject: [PATCH 17/51] #587 and maybe #221 - only apply positive offset compensation - the collision-height determines the bottom of the hit-box --- scripts/Sensors/CollSensor.lua | 2 +- scripts/Sensors/VirtualSensors.lua | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts/Sensors/CollSensor.lua b/scripts/Sensors/CollSensor.lua index dff2403e..bee46f1b 100644 --- a/scripts/Sensors/CollSensor.lua +++ b/scripts/Sensors/CollSensor.lua @@ -226,7 +226,7 @@ function ADCollSensor:onUpdate(dt) self:setTriggered(self.hit) self.newHit = false - local offsetCompensation = -math.tan(box.rx) * box.size[3] + local offsetCompensation = math.max(-math.tan(box.rx) * box.size[3], 0) box.y = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, box.x, 300, box.z), box.y) + offsetCompensation self.collisionHits = overlapBox(box.x, box.y, box.z, box.rx, box.ry, 0, box.size[1], box.size[2], box.size[3], "collisionTestCallback", self, self.mask, true, true, true) --AIVehicleUtil.COLLISION_MASK --16783599 diff --git a/scripts/Sensors/VirtualSensors.lua b/scripts/Sensors/VirtualSensors.lua index 1f2cddef..8f4abb33 100644 --- a/scripts/Sensors/VirtualSensors.lua +++ b/scripts/Sensors/VirtualSensors.lua @@ -287,6 +287,8 @@ function ADSensor:getBoxShape() end local boxYPos = 2 + local boxHeight = 0.75 + if self.position == ADSensor.POS_FRONT_LEFT or self.position == ADSensor.POS_FRONT_RIGHT then boxYPos = 2.25 end @@ -299,33 +301,33 @@ function ADSensor:getBoxShape() box.size = {} box.center = {} box.size[1] = self.width * 0.5 - box.size[2] = 0.75 -- fixed height for now + box.size[2] = boxHeight * 0.5 box.size[3] = lookAheadDistance * 0.5 box.offset[1] = self.location.x - box.offset[2] = boxYPos -- fixed y pos for now + box.offset[2] = boxYPos box.offset[3] = self.location.z - box.center[1] = box.offset[1] + vecZ.x * box.size[3] -- + vecX.x * box.size[1] - box.center[2] = boxYPos -- fixed y pos for now - box.center[3] = box.offset[3] + vecZ.z * box.size[3] -- + vecX.z * box.size[1] + box.center[1] = box.offset[1] + vecZ.x * box.size[3] + box.center[2] = box.offset[2] + box.size[2] + box.center[3] = box.offset[3] + vecZ.z * box.size[3] box.topLeft = {} box.topLeft[1] = box.center[1] - vecX.x * box.size[1] + vecZ.x * box.size[3] - box.topLeft[2] = boxYPos + box.topLeft[2] = box.center[2] box.topLeft[3] = box.center[3] - vecX.z * box.size[1] + vecZ.z * box.size[3] box.topRight = {} box.topRight[1] = box.center[1] + vecX.x * box.size[1] + vecZ.x * box.size[3] - box.topRight[2] = boxYPos + box.topRight[2] = box.center[2] box.topRight[3] = box.center[3] + vecX.z * box.size[1] + vecZ.z * box.size[3] box.downRight = {} box.downRight[1] = box.center[1] + vecX.x * box.size[1] - vecZ.x * box.size[3] - box.downRight[2] = boxYPos + box.downRight[2] = box.center[2] box.downRight[3] = box.center[3] + vecX.z * box.size[1] - vecZ.z * box.size[3] box.downLeft = {} box.downLeft[1] = box.center[1] - vecX.x * box.size[1] - vecZ.x * box.size[3] - box.downLeft[2] = boxYPos + box.downLeft[2] = box.center[2] box.downLeft[3] = box.center[3] - vecX.z * box.size[1] - vecZ.z * box.size[3] if self.sideFactor == -1 then From bd7332c72bdb09884fcf406d4ebb713d378d1bed Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:34:20 +0100 Subject: [PATCH 18/51] prevent lua error if selling attachments, the vehicle chain is not updated complete in 1 frame, cause lua error --- scripts/Utils/CollisionDetectionUtils.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Utils/CollisionDetectionUtils.lua b/scripts/Utils/CollisionDetectionUtils.lua index 8e1df41b..6117d34c 100644 --- a/scripts/Utils/CollisionDetectionUtils.lua +++ b/scripts/Utils/CollisionDetectionUtils.lua @@ -198,6 +198,10 @@ function ADDimensionSensor:getRealVehicleDimensions() local maxWidthLeft, maxWidthRight, maxLengthFront, maxLengthBack = 0,0,0,0 + if not entityExists(self.vehicle.components[1].node) then + -- if selling attachments, the vehicle chain is not updated complete in 1 frame, so need this + return 0, 0 + end local rx, ry, rz = getWorldRotation(self.vehicle.components[1].node) local function leftright(dimStart, dimEnd) From 6045ee733600b5c8651714a14a27523ace9d7d78 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 5 Nov 2023 15:01:51 +1300 Subject: [PATCH 19/51] #962 - Update translation --- translations/translation_de.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index c7dd25d0..f4a9c221 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -172,7 +172,7 @@ - + From fa647558f56b2f7d6a0ad1977ae33bb5d9420292 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 12 Nov 2023 21:16:47 +1300 Subject: [PATCH 20/51] #211 - use a shorter minimum distance for object collision check and longer distance for vehicle collision check --- scripts/Modules/CollisionDetectionModule.lua | 7 +++--- scripts/Sensors/VirtualSensors.lua | 23 +++++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scripts/Modules/CollisionDetectionModule.lua b/scripts/Modules/CollisionDetectionModule.lua index dda4dd1d..002838ae 100644 --- a/scripts/Modules/CollisionDetectionModule.lua +++ b/scripts/Modules/CollisionDetectionModule.lua @@ -24,12 +24,10 @@ function ADCollisionDetectionModule:update(dt) end function ADCollisionDetectionModule:detectObstacle() - local box = self.vehicle.ad.sensors.frontSensorDynamic:getBoxShape() - if AutoDrive.getSetting("enableTrafficDetection") >= 1 then - if self.vehicle.ad.sensors.frontSensorDynamic:pollInfo() then + if self.vehicle.ad.sensors.frontSensorDynamicShort:pollInfo() then local frontSensorDynamicInBunkerArea = false - local sensorLocation = self.vehicle.ad.sensors.frontSensorDynamic:getLocationByPosition() + local sensorLocation = self.vehicle.ad.sensors.frontSensorDynamicShort:getLocationByPosition() local vehX, vehY, vehZ = getWorldTranslation(self.vehicle.components[1].node) local worldOffsetX, worldOffsetY, worldOffsetZ = localDirectionToWorld(self.vehicle.components[1].node, sensorLocation.x, 0, sensorLocation.z) for _, trigger in pairs(ADTriggerManager.getUnloadTriggers()) do @@ -52,6 +50,7 @@ function ADCollisionDetectionModule:detectObstacle() if ((g_updateLoopIndex + self.vehicle.id) % AutoDrive.PERF_FRAMES == 0) then local excludedList = self.vehicle.ad.taskModule:getActiveTask():getExcludedVehiclesForCollisionCheck() + local box = self.vehicle.ad.sensors.frontSensorDynamicLong:getBoxShape() local boundingBox = {} boundingBox[1] = box.topLeft boundingBox[2] = box.topRight diff --git a/scripts/Sensors/VirtualSensors.lua b/scripts/Sensors/VirtualSensors.lua index 8f4abb33..a689b4c6 100644 --- a/scripts/Sensors/VirtualSensors.lua +++ b/scripts/Sensors/VirtualSensors.lua @@ -44,12 +44,17 @@ function ADSensor:addSensorsToVehicle(vehicle) vehicle.ad.sensors = {} local sensorParameters = {} sensorParameters.dynamicLength = true + sensorParameters.minDynamicLength = 0.2 sensorParameters.position = ADSensor.POS_FRONT sensorParameters.width = vehicle.size.width * 0.75 - local frontSensorDynamic = ADCollSensor:new(vehicle, sensorParameters) - --frontSensorDynamic.drawDebug = true --test - --frontSensorDynamic.enabled = true --test - vehicle.ad.sensors["frontSensorDynamic"] = frontSensorDynamic + local frontSensorDynamicShort = ADCollSensor:new(vehicle, sensorParameters) + --frontSensorDynamicShort.drawDebug = true --test + --frontSensorDynamicShort.enabled = true --test + vehicle.ad.sensors["frontSensorDynamicShort"] = frontSensorDynamicShort + + sensorParameters.minDynamicLength = 2 + local frontSensorDynamicLong = ADCollSensor:new(vehicle, sensorParameters) + vehicle.ad.sensors["frontSensorDynamicLong"] = frontSensorDynamicLong sensorParameters.dynamicLength = false sensorParameters.width = vehicle.size.width * 0.65 @@ -83,6 +88,7 @@ function ADSensor:addSensorsToVehicle(vehicle) sensorParameters = {} sensorParameters.dynamicLength = true + sensorParameters.minDynamicLength = 0.3 sensorParameters.width = vehicle.size.width * 0.75 sensorParameters.position = ADSensor.POS_REAR local rearSensor = ADCollSensor:new(vehicle, sensorParameters) @@ -168,6 +174,7 @@ function ADSensor:loadBaseParameters() local vehicle = self.vehicle if vehicle ~= nil and vehicle.size.length ~= nil and vehicle.size.width ~= nil then self.dynamicLength = true + self.minDynamicLength = 2 self.dynamicRotation = true self.length = vehicle.size.length self.width = vehicle.size.width * ADSensor.WIDTH_FACTOR @@ -188,6 +195,9 @@ function ADSensor:loadDynamicParameters(sensorParameters) if sensorParameters.dynamicLength ~= nil then self.dynamicLength = sensorParameters.dynamicLength == true end + if sensorParameters.minDynamicLength ~= nil then + self.minDynamicLength = sensorParameters.minDynamicLength + end if sensorParameters.dynamicRotation ~= nil then self.dynamicRotation = sensorParameters.dynamicRotation == true end @@ -270,10 +280,7 @@ function ADSensor:getBoxShape() self.location = self:getLocationByPosition() local lookAheadDistance = self.length if self.dynamicLength then - lookAheadDistance = MathUtil.clamp(vehicle.lastSpeedReal * 3600 / 40, 0.13, 1) * 15.5 - if self.position == ADSensor.POS_REAR then - lookAheadDistance = MathUtil.clamp(vehicle.lastSpeedReal * 3600 / 40, 0.02, 1) * 15.5 - end + lookAheadDistance = MathUtil.clamp(vehicle.lastSpeedReal * 3600 * 15.5 / 40, self.minDynamicLength, 16) end local vecZ = {x = 0, z = 1} From 023ebe60057f4ea158892358a2f10c6fceb29d18 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Fri, 17 Nov 2023 22:47:10 +1300 Subject: [PATCH 21/51] #94 - consider all incoming/outgoing waypoint connections when drawing splines. --- scripts/Utils/UtilFuncs.lua | 103 ++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/scripts/Utils/UtilFuncs.lua b/scripts/Utils/UtilFuncs.lua index 7dec6be3..0c547870 100644 --- a/scripts/Utils/UtilFuncs.lua +++ b/scripts/Utils/UtilFuncs.lua @@ -504,24 +504,66 @@ function AutoDrive:createSplineInterpolationBetween(startNode, endNode) return end - -- TODO: if we have more then one inbound or outbound connections, get the avg. vector - if #startNode.incoming >= 1 and #endNode.out >= 1 and AutoDrive.splineInterpolationUserCurvature >= 0.5 then - local p0 = nil - for _, px in pairs(startNode.incoming) do - p0 = ADGraphManager:getWayPointById(px) - --self.splineInterpolation.p0 = p0 - break + if AutoDrive.splineInterpolationUserCurvature >= 0.5 then + local p0, p3 = AutoDrive:getSplineControlPoints(startNode, endNode) + if p0 ~= nil and p3 ~= nil then + return AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) end - local p3 = nil - for _, px in pairs(endNode.out) do - p3 = ADGraphManager:getWayPointById(px) - --self.splineInterpolation.p3 = p3 - break - end - return AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) - else -- fallback to straight line connection behaviour - return end + -- fallback to straight line connection behaviour +end + +function AutoDrive:getSplineControlPoints(startNode, endNode) + -- Returns control points for the given startNode and endNode + if #startNode.incoming + #startNode.out == 0 then + return nil, nil + end + if #endNode.incoming + #endNode.out == 0 then + return nil, nil + end + + local function getWaypointDirection(node, waypoint_id) + -- returns unit vector from start/end node to a waypoint + local wp = ADGraphManager:getWayPointById(waypoint_id) + local wp_vec = ADVectorUtils.subtract2D(node, wp) + return ADVectorUtils.unitVector2D(wp_vec) + end + + local function getAllConnectionVectors(node) + -- returns unit-vectors for all connections of a node + local connections = {} + for _, px in pairs(node.incoming) do + table.insert(connections, getWaypointDirection(node, px)) + end + for _, px in pairs(node.out) do + table.insert(connections, getWaypointDirection(node, px)) + end + return connections + end + + -- cache candidates + local p0v_candidates = getAllConnectionVectors(startNode) + local p3v_candidates = getAllConnectionVectors(endNode) + local best_p0v, best_p3v, best_dist = nil, nil, nil + + -- iterate over all pairs of p0/p3 candidates + for _, p0v in pairs(p0v_candidates) do + for _, p3v in pairs(p3v_candidates) do + local intersects, dist_p0, dist_p3 = MathUtil.getLineLineIntersection2D(startNode.x, startNode.z, p0v.x, p0v.z, endNode.x, endNode.z, p3v.x, p3v.z) + -- p0/p3 are pointing away from the desired intersection, the returned distance has to be negative + if intersects and dist_p0 < 0 and dist_p3 < 0 then + local dist = -(dist_p0 + dist_p3) + if best_dist == nil or best_dist > dist then + -- keep the pair that results in the shortest distance + best_p0v, best_p3v, best_dist = p0v, p3v, dist + end + end + end + end + if best_dist ~= nil then + return ADVectorUtils.add2D(startNode, best_p0v), ADVectorUtils.add2D(endNode, best_p3v) + end + return nil, nil end function AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) @@ -550,21 +592,19 @@ function AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) -- distance from start to end, divided by two to give it more roundness... local dStartEnd = ADVectorUtils.distance2D(startNode, endNode) / self.splineInterpolation.curvature - -- we need to normalize the length of p0-start and end-p3, otherwise their length will influence the curve - -- get vector from p0->start - local vp0Start = ADVectorUtils.subtract2D(p0, startNode) - -- calculate unit vector of vp0Start - vp0Start = ADVectorUtils.unitVector2D(vp0Start) + -- we need to normalize the length of start-p0 and end-p3, otherwise their length will influence the curve + -- get vector from start->p0 + local vStartp0 = ADVectorUtils.subtract2D(startNode, p0) + -- calculate unit vector of vStartp0 + vStartp0 = ADVectorUtils.unitVector2D(vStartp0) -- scale it like start->end - vp0Start = ADVectorUtils.scaleVector2D(vp0Start, dStartEnd) - -- invert it - vp0Start = ADVectorUtils.invert2D(vp0Start) + vStartp0 = ADVectorUtils.scaleVector2D(vStartp0, dStartEnd) -- add it to the start Vector so that we get new p0 - p0 = ADVectorUtils.add2D(startNode, vp0Start) + p0 = ADVectorUtils.add2D(startNode, vStartp0) -- make sure p0 has a y value p0.y = startNode.y - -- same for end->p3, except that we do not need to invert it, but just add it to the endNode + -- same for end->p3 local vEndp3 = ADVectorUtils.subtract2D(endNode, p3) vEndp3 = ADVectorUtils.unitVector2D(vEndp3) vEndp3 = ADVectorUtils.scaleVector2D(vEndp3, dStartEnd) @@ -573,12 +613,10 @@ function AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) local prevWP = startNode local secondLastWp = nil - local prevV = ADVectorUtils.subtract2D(p0, startNode) -- we're calculting a VERY smooth curve and whenever the new point on the curve has a good distance to the last one create a new waypoint -- but make sure that the last point also has a good distance to the endNode for i = 1, 200 do local px = AutoDrive:CatmullRomInterpolate(i, p0, startNode, endNode, p3, 200) - local newV = ADVectorUtils.subtract2D(prevWP, px) local distPrev = ADVectorUtils.distance2D(prevWP, px) local distEnd = ADVectorUtils.distance2D(px, endNode) @@ -604,7 +642,6 @@ function AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) secondLastWp = prevWP prevWP = px - prevV = newV end end table.insert(self.splineInterpolation.waypoints, endNode) @@ -693,9 +730,7 @@ end --- @return table Vector function ADVectorUtils.scaleVector2D(vector, scale) scale = scale or 1.0 - vector.x = ( vector.x * scale ) or 0 - vector.z = ( vector.z * scale ) or 0 - return vector + return { x = ( vector.x * scale ) or 0, z = ( vector.z * scale ) or 0 } end --- Returns the distance between zwo vectors using their x and z coordinates. @@ -718,9 +753,7 @@ end --- @param vector table with x and z property --- @return table Vector inverted function ADVectorUtils.invert2D(vector) - vector.x = vector.x * -1 - vector.z = vector.z * -1 - return vector + return {x = vector.x * -1, z = vector.z * -1} end --- Adds x and z values of two given vectors and returns a new vector with x and z properties. From c9b1108baed06d30a5aa371fb136f0feafe5d8cc Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sat, 18 Nov 2023 23:23:55 +0100 Subject: [PATCH 22/51] addition to #94 inverse logic to catch another issue connection --- scripts/Manager/GraphManager.lua | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index d2768cc8..2098377b 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -967,29 +967,25 @@ function ADGraphManager:getNetworkErrors() wp.errorMapping = {} if #wp.incoming > 0 then - for outIndex, outId in ipairs(wp.out) do - wp.errorMapping[outId] = true -- assume no path - end - for inIndex, inId in ipairs(wp.incoming) do local inPoint = network[inId] for outIndex, outId in ipairs(wp.out) do if inId ~= outId then local outPoint = network[outId] local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) - if angle <= 90 then - wp.errorMapping[outId] = false + if angle > 90 then + wp.errorMapping[outId] = inId else local isReverseStart = not table.contains(outPoint.incoming, wp.id) local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - wp.errorMapping[outId] = false + if not (isReverseStart or isReverseEnd) then + wp.errorMapping[outId] = inId end end end - if inId == outId and #wp.incoming == 1 then + if not (inId == outId and #wp.incoming == 1) then -- only 1 dual connection - wp.errorMapping[outId] = false + wp.errorMapping[outId] = inId end end end @@ -1281,7 +1277,6 @@ end function ADGraphManager:checkForOtherErrors(wp) local ret = false - if wp == nil then return true end @@ -1290,7 +1285,7 @@ function ADGraphManager:checkForOtherErrors(wp) local network = self:getWayPoints() for outIndex, outId in ipairs(wp.out) do - ret = ret or wp.errorMapping[outId] + ret = ret or (wp.errorMapping[outId] ~= nil) end return ret end From 02b91c8250a3489a6562f0939fa244b0f12f30a9 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 19 Nov 2023 11:58:03 +1300 Subject: [PATCH 23/51] $94-ish: generate 9_x network errors if there are any bad angles --- scripts/Manager/GraphManager.lua | 42 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index d2768cc8..a36565e8 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -963,36 +963,37 @@ end -- here also additional checks may be implemented function ADGraphManager:getNetworkErrors() local network = self:getWayPoints() - for id, wp in ipairs(network) do + for _, wp in ipairs(network) do wp.errorMapping = {} if #wp.incoming > 0 then - for outIndex, outId in ipairs(wp.out) do - wp.errorMapping[outId] = true -- assume no path - end - - for inIndex, inId in ipairs(wp.incoming) do + for _, inId in ipairs(wp.incoming) do local inPoint = network[inId] - for outIndex, outId in ipairs(wp.out) do + for _, outId in ipairs(wp.out) do + local error = false if inId ~= outId then local outPoint = network[outId] local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) - if angle <= 90 then - wp.errorMapping[outId] = false - else + if angle > 90 then local isReverseStart = not table.contains(outPoint.incoming, wp.id) local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - wp.errorMapping[outId] = false + if not isReverseStart and not isReverseEnd then + error = true end end end if inId == outId and #wp.incoming == 1 then -- only 1 dual connection - wp.errorMapping[outId] = false + error = false end + wp.errorMapping[outId] = wp.errorMapping[outId] or error end end + for _, outId in ipairs(wp.out) do + if wp.errorMapping[outId] == nil then + wp.errorMapping[outId] = true + end + end end end end @@ -1177,7 +1178,7 @@ function ADGraphManager:createDebugMarkers(updateMap) -- possible other errors if not wp.foundError then - local wrongAngle, count1, count2 = self:checkForOtherErrors(wp) + local wrongAngle = self:checkForOtherErrors(wp) if wrongAngle then local debugMapMarkerName = "9_" .. tostring(count9) @@ -1280,19 +1281,16 @@ end function ADGraphManager:checkForOtherErrors(wp) - local ret = false - if wp == nil then return true end --- TODO - - local network = self:getWayPoints() - for outIndex, outId in ipairs(wp.out) do - ret = ret or wp.errorMapping[outId] + for _, outId in ipairs(wp.out) do + if wp.errorMapping[outId] then + return true + end end - return ret + return false end function ADGraphManager:toggleWayPointAsSubPrio(wayPointId) From 1138bc02e14eb3ba28c3e7c2b5f6b9ad72225f0c Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 19 Nov 2023 15:03:17 +1300 Subject: [PATCH 24/51] #94 - create low-priority spline if start or end is low-priority, create dual connections if L-Ctrl is held down --- scripts/AutoDrive.lua | 5 ++- .../Graph/CreateSplineConnectionEvent.lua | 15 +++++--- .../Events/Graph/ToggleConnectionEvent.lua | 13 ++++--- scripts/Hud.lua | 26 +++++++------ scripts/Manager/GraphManager.lua | 28 ++++++++------ scripts/Manager/InputManager.lua | 4 +- scripts/Modules/RecordingModule.lua | 10 +---- scripts/Specialization.lua | 38 +++++++++++-------- scripts/Utils/TrafficSplineUtils.lua | 13 +++---- translations/translation_en.xml | 9 +++-- 10 files changed, 90 insertions(+), 71 deletions(-) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index bd0b10a4..7d8dad8b 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -107,7 +107,10 @@ AutoDrive.colors = { ad_color_currentConnection = {1, 1, 1, 1}, ad_color_closestLine = {1, 0, 0, 1}, ad_color_editorHeightLine = {1, 1, 1, 1}, - ad_color_previewOk = {0.3, 0.9, 0, 1}, + ad_color_previewSingleConnection = {0.3, 0.9, 0, 1}, + ad_color_previewDualConnection = {0, 0, 0.9, 1}, + ad_color_previewSubPrioSingleConnection = {0.9, 0.4, 0.1, 1}, + ad_color_previewSubPrioDualConnection = {0.3, 0.15, 0, 1}, ad_color_previewNotOk = {1, 0.1, 0, 1} } diff --git a/scripts/Events/Graph/CreateSplineConnectionEvent.lua b/scripts/Events/Graph/CreateSplineConnectionEvent.lua index b2d2297a..bc095350 100644 --- a/scripts/Events/Graph/CreateSplineConnectionEvent.lua +++ b/scripts/Events/Graph/CreateSplineConnectionEvent.lua @@ -8,15 +8,16 @@ function CreateSplineConnectionEvent.emptyNew() return self end -function CreateSplineConnectionEvent.new(start, waypoints, target) +function CreateSplineConnectionEvent.new(start, waypoints, target, dualConnection) local self = CreateSplineConnectionEvent.emptyNew() self.start = start self.waypoints = waypoints self.target = target + self.dualConnection = dualConnection return self end -function CreateSplineConnectionEvent:writeStream(streamId, connection) +function CreateSplineConnectionEvent:writeStream(streamId, connection) local paramsXZ = g_currentMission.vehicleXZPosCompressionParams local paramsY = g_currentMission.vehicleYPosCompressionParams @@ -30,6 +31,7 @@ function CreateSplineConnectionEvent:writeStream(streamId, connection) end streamWriteInt32(streamId, self.target) + streamWriteBool(streamId, self.dualConnection) end function CreateSplineConnectionEvent:readStream(streamId, connection) @@ -47,21 +49,22 @@ function CreateSplineConnectionEvent:readStream(streamId, connection) end self.target = streamReadInt32(streamId) + self.dualConnection = streamReadBool(streamId) self:run(connection) end function CreateSplineConnectionEvent:run(connection) if g_server ~= nil and connection:getIsServer() == false then -- If the event is coming from a client, server have only to broadcast - CreateSplineConnectionEvent.sendEvent(self.start, self.waypoints, self.target) + CreateSplineConnectionEvent.sendEvent(self.start, self.waypoints, self.target, self.dualConnection) else -- If the event is coming from the server, both clients and server have to create the way point - ADGraphManager:createSplineConnection(self.start, self.waypoints, self.target, false) + ADGraphManager:createSplineConnection(self.start, self.waypoints, self.target, self.dualConnection, false) end end -function CreateSplineConnectionEvent.sendEvent(start, waypoints, target) - local event = CreateSplineConnectionEvent.new(start, waypoints, target) +function CreateSplineConnectionEvent.sendEvent(start, waypoints, target, dualConnection) + local event = CreateSplineConnectionEvent.new(start, waypoints, target, dualConnection) if g_server ~= nil then -- Server have to broadcast to all clients and himself g_server:broadcastEvent(event, true) diff --git a/scripts/Events/Graph/ToggleConnectionEvent.lua b/scripts/Events/Graph/ToggleConnectionEvent.lua index 323d51e7..7193beb1 100644 --- a/scripts/Events/Graph/ToggleConnectionEvent.lua +++ b/scripts/Events/Graph/ToggleConnectionEvent.lua @@ -8,11 +8,12 @@ function AutoDriveToggleConnectionEvent.emptyNew() return self end -function AutoDriveToggleConnectionEvent.new(startNode, endNode, reverseDirection) +function AutoDriveToggleConnectionEvent.new(startNode, endNode, reverseDirection, dualConnection) local self = AutoDriveToggleConnectionEvent.emptyNew() self.startNode = startNode self.endNode = endNode self.reverseDirection = reverseDirection + self.dualConnection = dualConnection return self end @@ -20,26 +21,28 @@ function AutoDriveToggleConnectionEvent:writeStream(streamId, connection) streamWriteUIntN(streamId, self.startNode.id, 20) streamWriteUIntN(streamId, self.endNode.id, 20) streamWriteBool(streamId, self.reverseDirection) + streamWriteBool(streamId, self.dualConnection) end function AutoDriveToggleConnectionEvent:readStream(streamId, connection) self.startNode = ADGraphManager:getWayPointById(streamReadUIntN(streamId, 20)) self.endNode = ADGraphManager:getWayPointById(streamReadUIntN(streamId, 20)) self.reverseDirection = streamReadBool(streamId) + self.dualConnection = streamReadBool(streamId) self:run(connection) end function AutoDriveToggleConnectionEvent:run(connection) if g_server ~= nil and connection:getIsServer() == false then -- If the event is coming from a client, server have only to broadcast - AutoDriveToggleConnectionEvent.sendEvent(self.startNode, self.endNode, self.reverseDirection) + AutoDriveToggleConnectionEvent.sendEvent(self.startNode, self.endNode, self.reverseDirection, self.dualConnection) else - ADGraphManager:toggleConnectionBetween(self.startNode, self.endNode, self.reverseDirection, false) + ADGraphManager:toggleConnectionBetween(self.startNode, self.endNode, self.reverseDirection, self.dualConnection, false) end end -function AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection) - local event = AutoDriveToggleConnectionEvent.new(startNode, endNode, reverseDirection) +function AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection, dualConnection) + local event = AutoDriveToggleConnectionEvent.new(startNode, endNode, reverseDirection, dualConnection) if g_server ~= nil then -- Server have to broadcast to all clients and himself g_server:broadcastEvent(event, true) diff --git a/scripts/Hud.lua b/scripts/Hud.lua index aad46513..6cf9c328 100644 --- a/scripts/Hud.lua +++ b/scripts/Hud.lua @@ -505,13 +505,16 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) -- waypoint at mouse position if button == 1 and isUp and not AutoDrive.leftLSHIFTmodifierKeyPressed - and not AutoDrive.leftCTRLmodifierKeyPressed - and not AutoDrive.leftALTmodifierKeyPressed - --and AutoDrive.rightSHIFTmodifierKeyPressed -- see below !!! + -- and not AutoDrive.leftCTRLmodifierKeyPressed -- dual connection + and not AutoDrive.leftALTmodifierKeyPressed + -- and AutoDrive.rightSHIFTmodifierKeyPressed -- reverse connection then -- left mouse button to select point / connect to already selected point if vehicle.ad.selectedNodeId ~= nil then if vehicle.ad.selectedNodeId ~= vehicle.ad.hoveredNodeId then + local reverseDirection = AutoDrive.rightSHIFTmodifierKeyPressed + local dualConnection = AutoDrive.leftCTRLmodifierKeyPressed + if not table.contains(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId).out, vehicle.ad.hoveredNodeId) then -- connect selected point with hovered point @@ -528,20 +531,16 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) end end - ADGraphManager:createSplineConnection(vehicle.ad.selectedNodeId, waypoints, vehicle.ad.hoveredNodeId) + ADGraphManager:createSplineConnection(vehicle.ad.selectedNodeId, waypoints, vehicle.ad.hoveredNodeId, dualConnection) else AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 1 vehicle.ad.selectedNodeId %d vehicle.ad.hoveredNodeId %d", vehicle.ad.selectedNodeId, vehicle.ad.hoveredNodeId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), AutoDrive.rightSHIFTmodifierKeyPressed) - if AutoDrive.leftLSHIFTmodifierKeyPressed then - AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleWayPointAsSubPrio 1 selectedNodeId %d", vehicle.ad.selectedNodeId) - ADGraphManager:toggleWayPointAsSubPrio(vehicle.ad.selectedNodeId) - end + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, dualConnection) end AutoDrive.splineInterpolationUserCurvature = nil else AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 1 vehicle.ad.selectedNodeId %d vehicle.ad.hoveredNodeId %d", vehicle.ad.selectedNodeId, vehicle.ad.hoveredNodeId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), AutoDrive.rightSHIFTmodifierKeyPressed) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, dualConnection) end end @@ -633,6 +632,9 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) and not AutoDrive.leftALTmodifierKeyPressed -- and not AutoDrive.rightSHIFTmodifierKeyPressed -- see below !!! then + local reverseDirection = AutoDrive.rightSHIFTmodifierKeyPressed + local subPrio = AutoDrive.leftLSHIFTmodifierKeyPressed and not reverseDirection + --For rough depth assertion, we use the closest nodes location as this is roughly in the screen's center local closest = vehicle:getClosestWayPoint() closest = ADGraphManager:getWayPointById(closest) @@ -681,7 +683,7 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) -- auto connect only working in single player properly ! local createdId = ADGraphManager:getWayPointsCount() - if AutoDrive.leftLSHIFTmodifierKeyPressed and not AutoDrive.rightSHIFTmodifierKeyPressed then + if subPrio then ADGraphManager:toggleWayPointAsSubPrio(createdId) AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleWayPointAsSubPrio 3 createdId %d", createdId) end @@ -689,7 +691,7 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) if vehicle.ad.newcreated ~= nil and vehicle.ad.selectedNodeId == vehicle.ad.newcreated then -- connect only if previous created point is selected and newcreated ~= nil AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 2 vehicle.ad.selectedNodeId %d to %d", vehicle.ad.selectedNodeId, createdId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(createdId), AutoDrive.rightSHIFTmodifierKeyPressed) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(createdId), reverseDirection) end vehicle.ad.newcreated = createdId vehicle.ad.selectedNodeId = vehicle.ad.newcreated diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index a36565e8..f3b581a1 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -534,13 +534,13 @@ function ADGraphManager:removeMapMarkerByWayPoint(wayPointId, sendEvent) end end -function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, sendEvent) +function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, dualConnection, sendEvent) if startNode == nil or endNode == nil then return end if sendEvent == nil or sendEvent == true then -- Propagating connection toggling all over the network - AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection) + AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection, dualConnection) else if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then table.removeValue(startNode.out, endNode.id) @@ -549,6 +549,10 @@ function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirec table.insert(startNode.out, endNode.id) if not reverseDirection then table.insert(endNode.incoming, startNode.id) + if dualConnection then + table.insert(endNode.out, startNode.id) + table.insert(startNode.incoming, endNode.id) + end end end @@ -687,10 +691,7 @@ function ADGraphManager:recordWayPoint(x, y, z, connectPrevious, dual, isReverse local newWp = self:createNode(newId, x, y, z, {}, {}, flags) self:setWayPoint(newWp) if connectPrevious then - self:toggleConnectionBetween(previous, newWp, isReverse, false) - if dual then - self:toggleConnectionBetween(newWp, previous, isReverse, false) - end + self:toggleConnectionBetween(previous, newWp, isReverse, dual, false) end self:markChanges() @@ -1599,26 +1600,31 @@ function ADGraphManager:removeNodesWithFlag(flagToRemove) end end -function ADGraphManager:createSplineConnection(start, waypoints, target, sendEvent) +function ADGraphManager:createSplineConnection(start, waypoints, target, dualConnection, sendEvent) if sendEvent == nil or sendEvent == true then -- Propagating connection toggling all over the network - CreateSplineConnectionEvent.sendEvent(start, waypoints, target) + CreateSplineConnectionEvent.sendEvent(start, waypoints, target, dualConnection) else local lastId = start local lastHeight = ADGraphManager:getWayPointById(start).y - for wpId, wp in pairs(waypoints) do + local subPrio = self:getIsPointSubPrio(start) or self:getIsPointSubPrio(target) + + for _, wp in pairs(waypoints) do if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc wp.y = lastHeight end self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) local createdId = self:getWayPointsCount() - self:toggleConnectionBetween(ADGraphManager:getWayPointById(lastId), ADGraphManager:getWayPointById(createdId), false, false) + if subPrio then + ADGraphManager:toggleWayPointAsSubPrio(createdId) + end + self:toggleConnectionBetween(ADGraphManager:getWayPointById(lastId), ADGraphManager:getWayPointById(createdId), false, dualConnection, false) lastId = createdId lastHeight = wp.y end local wp = self:getWayPointById(lastId) - self:toggleConnectionBetween(wp, self:getWayPointById(target), false, false) + self:toggleConnectionBetween(wp, self:getWayPointById(target), false, dualConnection, false) end end diff --git a/scripts/Manager/InputManager.lua b/scripts/Manager/InputManager.lua index ed0c0d34..30fd37df 100644 --- a/scripts/Manager/InputManager.lua +++ b/scripts/Manager/InputManager.lua @@ -191,7 +191,7 @@ function ADInputManager:input_toggleConnection(vehicle) local closestWayPoint, _ = vehicle:getClosestWayPoint() if ADGraphManager:getWayPointById(closestWayPoint) ~= nil then if vehicle.ad.stateModule:getSelectedNeighbourPoint() ~= nil then - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(closestWayPoint), vehicle.ad.stateModule:getSelectedNeighbourPoint(), false) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(closestWayPoint), vehicle.ad.stateModule:getSelectedNeighbourPoint(), false, false) end end end @@ -202,7 +202,7 @@ function ADInputManager:input_toggleConnectionInverted(vehicle) local closestWayPoint, _ = vehicle:getClosestWayPoint() if ADGraphManager:getWayPointById(closestWayPoint) ~= nil then if vehicle.ad.stateModule:getSelectedNeighbourPoint() ~= nil then - ADGraphManager:toggleConnectionBetween(vehicle.ad.stateModule:getSelectedNeighbourPoint(), ADGraphManager:getWayPointById(closestWayPoint), false) + ADGraphManager:toggleConnectionBetween(vehicle.ad.stateModule:getSelectedNeighbourPoint(), ADGraphManager:getWayPointById(closestWayPoint), false, false) end end end diff --git a/scripts/Modules/RecordingModule.lua b/scripts/Modules/RecordingModule.lua index 8a9f97dc..674f23a4 100644 --- a/scripts/Modules/RecordingModule.lua +++ b/scripts/Modules/RecordingModule.lua @@ -46,10 +46,7 @@ function ADRecordingModule:start(dual, subPrio) if AutoDrive.getSetting("autoConnectStart") then if startNode ~= nil then if ADGraphManager:getDistanceBetweenNodes(startNodeId, self.lastWp.id) < 12 then - ADGraphManager:toggleConnectionBetween(startNode, self.lastWp, self.drivingReverse) - if self.isDual then - ADGraphManager:toggleConnectionBetween(self.lastWp, startNode, self.drivingReverse) - end + ADGraphManager:toggleConnectionBetween(startNode, self.lastWp, self.drivingReverse, self.isDual) end end end @@ -63,10 +60,7 @@ function ADRecordingModule:stop() local targetId = ADGraphManager:findMatchingWayPointForVehicle(self.vehicle) local targetNode = ADGraphManager:getWayPointById(targetId) if targetNode ~= nil then - ADGraphManager:toggleConnectionBetween(self.lastWp, targetNode, false) - if self.isDual then - ADGraphManager:toggleConnectionBetween(targetNode, self.lastWp, false) - end + ADGraphManager:toggleConnectionBetween(self.lastWp, targetNode, false, self.isDual) end end end diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua index 936445f8..7213d278 100644 --- a/scripts/Specialization.lua +++ b/scripts/Specialization.lua @@ -637,37 +637,45 @@ function AutoDrive.drawTripod(node, offset) end function AutoDrive:onDrawPreviews() - --if AutoDrive:checkForCollisionOnSpline() then local lastHeight = AutoDrive.splineInterpolation.startNode.y local lastWp = AutoDrive.splineInterpolation.startNode + local targetWp = AutoDrive.splineInterpolation.endNode local arrowPosition = ADDrawingManager.arrows.position.middle local collisionFree = AutoDrive:checkForCollisionOnSpline() + local color + local isSubPrio = ADGraphManager:getIsPointSubPrio(lastWp.id) or ADGraphManager:getIsPointSubPrio(targetWp.id) + local isDual = AutoDrive.leftCTRLmodifierKeyPressed -- leftCtrl: make dual connection + if not collisionFree then + color = AutoDrive.currentColors.ad_color_previewNotOk + elseif isDual and isSubPrio then + color = AutoDrive.currentColors.ad_color_previewSubPrioDualConnection + elseif isDual then + color = AutoDrive.currentColors.ad_color_previewDualConnection + elseif isSubPrio then + color = AutoDrive.currentColors.ad_color_previewSubPrioSingleConnection + else + color = AutoDrive.currentColors.ad_color_previewSingleConnection + end + for wpId, wp in pairs(AutoDrive.splineInterpolation.waypoints) do if wpId ~= 1 and wpId < (#AutoDrive.splineInterpolation.waypoints - 1) then if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc wp.y = lastHeight end - if collisionFree then - ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, unpack(AutoDrive.currentColors.ad_color_previewOk)) - ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, arrowPosition, unpack(AutoDrive.currentColors.ad_color_previewOk)) - else - ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, unpack(AutoDrive.currentColors.ad_color_previewNotOk)) - ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, arrowPosition, unpack(AutoDrive.currentColors.ad_color_previewNotOk)) + ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, unpack(color)) + if not isDual then + ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, wp.x, wp.y, wp.z, 1, arrowPosition, unpack(color)) end - lastWp = {x = wp.x, y = wp.y, z = wp.z} lastHeight = wp.y end end - local targetWp = AutoDrive.splineInterpolation.endNode - if collisionFree then - ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, unpack(AutoDrive.currentColors.ad_color_previewOk)) - ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, arrowPosition, unpack(AutoDrive.currentColors.ad_color_previewOk)) - else - ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, unpack(AutoDrive.currentColors.ad_color_previewNotOk)) - ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, arrowPosition, unpack(AutoDrive.currentColors.ad_color_previewNotOk)) + + ADDrawingManager:addLineTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, unpack(color)) + if not isDual then + ADDrawingManager:addArrowTask(lastWp.x, lastWp.y, lastWp.z, targetWp.x, targetWp.y, targetWp.z, 1, arrowPosition, unpack(color)) end end diff --git a/scripts/Utils/TrafficSplineUtils.lua b/scripts/Utils/TrafficSplineUtils.lua index 6a886fe9..aa6ca24d 100644 --- a/scripts/Utils/TrafficSplineUtils.lua +++ b/scripts/Utils/TrafficSplineUtils.lua @@ -169,10 +169,7 @@ function AutoDrive:createWaypointsForSpline(startNodes, endNodes, usedSplines, s if targetId >= 0 then local wpId = ADGraphManager:getWayPointsCount() local wp = ADGraphManager:getWayPointById(wpId) - ADGraphManager:toggleConnectionBetween(wp, ADGraphManager:getWayPointById(targetId)) - if isDualRoad then - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(targetId), wp) - end + ADGraphManager:toggleConnectionBetween(wp, ADGraphManager:getWayPointById(targetId), false, isDualRoad) else if posX < mapSize and posZ < mapSize then ADGraphManager:recordWayPoint(posX, posY, posZ, true, isDualRoad, false, 0, AutoDrive.FLAG_TRAFFIC_SYSTEM) @@ -289,13 +286,13 @@ function AutoDrive:createJunctions(startNodes, endNodes, maxAngle, maxDist) end local wp = ADGraphManager:getWayPointById(lastId) - ADGraphManager:toggleConnectionBetween(wp, startNode, false) + ADGraphManager:toggleConnectionBetween(wp, startNode, false, false) end else --print("AutoDrive:createJunctions - Fallback to toggle connections") - ADGraphManager:toggleConnectionBetween(endNode, startNode, false) + ADGraphManager:toggleConnectionBetween(endNode, startNode, false, false) end - --ADGraphManager:toggleConnectionBetween(endNode, startNode, false) + --ADGraphManager:toggleConnectionBetween(endNode, startNode, false, false) end end end @@ -312,7 +309,7 @@ function AutoDrive:checkForCollisionOnSpline() local mask = AutoDrive.collisionMaskSplines for wpId, wp in pairs(self.splineInterpolation.waypoints) do - if wpId > 1 and wpId < (#self.splineInterpolation.waypoints - 1) then + if wpId > 1 then local wpLast = self.splineInterpolation.waypoints[wpId - 1] local deltaX, deltaY, deltaZ = wp.x - wpLast.x, wp.y - wpLast.y, wp.z - wpLast.z local centerX, centerY, centerZ = wpLast.x + deltaX/2, wpLast.y + deltaY/2, wpLast.z + deltaZ/2 diff --git a/translations/translation_en.xml b/translations/translation_en.xml index d3a3789f..506ead98 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -290,7 +290,7 @@ - + @@ -301,8 +301,11 @@ - - + + + + + From 7213a2b7c194204a3115d653d3c3c08b8b025713 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sun, 19 Nov 2023 14:49:11 +0100 Subject: [PATCH 25/51] debug network enhancement just another improvement+fix, see #94 --- scripts/Manager/GraphManager.lua | 41 +++++++++++++++++--------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 2098377b..3b70fc31 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -975,18 +975,19 @@ function ADGraphManager:getNetworkErrors() local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) if angle > 90 then wp.errorMapping[outId] = inId - else local isReverseStart = not table.contains(outPoint.incoming, wp.id) local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if not (isReverseStart or isReverseEnd) then - wp.errorMapping[outId] = inId + if isReverseStart or isReverseEnd then + wp.errorMapping[outId] = nil + end + if ADGraphManager:isDualRoad(wp, outPoint) then + wp.errorMapping[outId] = nil + end + if ADGraphManager:isDualRoad(wp, inPoint) then + wp.errorMapping[outId] = nil end end end - if not (inId == outId and #wp.incoming == 1) then - -- only 1 dual connection - wp.errorMapping[outId] = inId - end end end end @@ -1079,20 +1080,22 @@ function ADGraphManager:createDebugMarkers(updateMap) -- mark wayPoint without outgoing connection if #wp.out == 0 then if wp ~= nil then - local debugMapMarkerName = "1_" .. tostring(count1) + if not ADGraphManager:getMapMarkerByWayPointId(wp.id) then + local debugMapMarkerName = "1_" .. tostring(count1) - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) - wp.foundError = true - count1 = count1 + 1 - mapMarkerCounter = mapMarkerCounter + 1 + wp.foundError = true + count1 = count1 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end end end From 0af7d07d489d30f3fcd216b224ea6e92e0732145 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Mon, 20 Nov 2023 19:04:37 +1300 Subject: [PATCH 26/51] #94 - Update translations --- translations/translation_br.xml | 7 +++++-- translations/translation_cs.xml | 7 +++++-- translations/translation_cz.xml | 7 +++++-- translations/translation_de.xml | 7 +++++-- translations/translation_es.xml | 7 +++++-- translations/translation_fr.xml | 7 +++++-- translations/translation_hu.xml | 7 +++++-- translations/translation_it.xml | 7 +++++-- translations/translation_nl.xml | 7 +++++-- translations/translation_pl.xml | 7 +++++-- translations/translation_pt.xml | 7 +++++-- translations/translation_ru.xml | 7 +++++-- translations/translation_tr.xml | 7 +++++-- 13 files changed, 65 insertions(+), 26 deletions(-) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index e970c1dc..5277614e 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -301,8 +301,11 @@ - - + + + + + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index be0eae25..e9c2c4c5 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -304,8 +304,11 @@ - - + + + + + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index e539d04f..316f91fd 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -302,8 +302,11 @@ - - + + + + + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index f4a9c221..fce07ff3 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -308,8 +308,11 @@ - - + + + + + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index c3eeb46d..0063b21a 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -300,8 +300,11 @@ - - + + + + + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index fe67c569..f407df39 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -304,8 +304,11 @@ - - + + + + + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 29852a9f..78f66f5f 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -296,8 +296,11 @@ - - + + + + + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index dae3838c..9f697d5f 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -303,8 +303,11 @@ - - + + + + + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 3685f0a8..a8f4745d 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -304,8 +304,11 @@ - - + + + + + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index 9fd2ea65..bc36af30 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -303,8 +303,11 @@ - - + + + + + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 45dac9b9..a3e0eec0 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -302,8 +302,11 @@ - - + + + + + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index ca56a75d..2b2d913c 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -301,8 +301,11 @@ - - + + + + + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 59934895..f9534b35 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -300,8 +300,11 @@ - - + + + + + From 6cb1cc0d09c6a859b27858ac7563737e1cb3df57 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Mon, 20 Nov 2023 20:18:41 +1300 Subject: [PATCH 27/51] #94 - Use LCtrl+LAlt for dual connections --- scripts/Hud.lua | 28 ++++++++++++++-------------- scripts/Specialization.lua | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/Hud.lua b/scripts/Hud.lua index 6cf9c328..c1d03d95 100644 --- a/scripts/Hud.lua +++ b/scripts/Hud.lua @@ -503,17 +503,14 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) end -- waypoint at mouse position - if button == 1 and isUp - and not AutoDrive.leftLSHIFTmodifierKeyPressed - -- and not AutoDrive.leftCTRLmodifierKeyPressed -- dual connection - and not AutoDrive.leftALTmodifierKeyPressed - -- and AutoDrive.rightSHIFTmodifierKeyPressed -- reverse connection + local connectOneWay = not AutoDrive.leftLSHIFTmodifierKeyPressed and not AutoDrive.leftCTRLmodifierKeyPressed and not AutoDrive.leftALTmodifierKeyPressed + local connectDual = not AutoDrive.leftLSHIFTmodifierKeyPressed and AutoDrive.leftCTRLmodifierKeyPressed and AutoDrive.leftALTmodifierKeyPressed + if button == 1 and isUp and (connectOneWay or connectDual) then -- left mouse button to select point / connect to already selected point if vehicle.ad.selectedNodeId ~= nil then if vehicle.ad.selectedNodeId ~= vehicle.ad.hoveredNodeId then local reverseDirection = AutoDrive.rightSHIFTmodifierKeyPressed - local dualConnection = AutoDrive.leftCTRLmodifierKeyPressed if not table.contains(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId).out, vehicle.ad.hoveredNodeId) then -- connect selected point with hovered point @@ -531,16 +528,16 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) end end - ADGraphManager:createSplineConnection(vehicle.ad.selectedNodeId, waypoints, vehicle.ad.hoveredNodeId, dualConnection) + ADGraphManager:createSplineConnection(vehicle.ad.selectedNodeId, waypoints, vehicle.ad.hoveredNodeId, connectDual) else AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 1 vehicle.ad.selectedNodeId %d vehicle.ad.hoveredNodeId %d", vehicle.ad.selectedNodeId, vehicle.ad.hoveredNodeId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, dualConnection) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, connectDual) end AutoDrive.splineInterpolationUserCurvature = nil else AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 1 vehicle.ad.selectedNodeId %d vehicle.ad.hoveredNodeId %d", vehicle.ad.selectedNodeId, vehicle.ad.hoveredNodeId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, dualConnection) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(vehicle.ad.hoveredNodeId), reverseDirection, connectDual) end end @@ -611,7 +608,8 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent auto connection 1 selectedNodeId %d", vehicle.ad.selectedNodeId) end - if button == 1 and isUp + -- if LSHIFT is pressed, selecting a waypoint will toggle its priority + if button == 1 and isUp and AutoDrive.leftLSHIFTmodifierKeyPressed and not AutoDrive.leftCTRLmodifierKeyPressed and not AutoDrive.leftALTmodifierKeyPressed @@ -627,13 +625,15 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) --If no node is hovered / moved - create new node if vehicle.ad.nodeToMoveId == nil and vehicle.ad.hoveredNodeId == nil then if button == 1 and isUp - -- and not AutoDrive.leftLSHIFTmodifierKeyPressed -- see below !!! + -- and not AutoDrive.leftLSHIFTmodifierKeyPressed -- sub-priority and AutoDrive.leftCTRLmodifierKeyPressed - and not AutoDrive.leftALTmodifierKeyPressed - -- and not AutoDrive.rightSHIFTmodifierKeyPressed -- see below !!! + -- and not AutoDrive.leftALTmodifierKeyPressed -- dual connection + -- and not AutoDrive.rightSHIFTmodifierKeyPressed -- reverse then local reverseDirection = AutoDrive.rightSHIFTmodifierKeyPressed local subPrio = AutoDrive.leftLSHIFTmodifierKeyPressed and not reverseDirection + local dualConnection = AutoDrive.leftALTmodifierKeyPressed and not reverseDirection + --For rough depth assertion, we use the closest nodes location as this is roughly in the screen's center local closest = vehicle:getClosestWayPoint() @@ -691,7 +691,7 @@ function AutoDriveHud:mouseEvent(vehicle, posX, posY, isDown, isUp, button) if vehicle.ad.newcreated ~= nil and vehicle.ad.selectedNodeId == vehicle.ad.newcreated then -- connect only if previous created point is selected and newcreated ~= nil AutoDriveHud.debugMsg(vehicle, "AutoDriveHud:mouseEvent toggleConnectionBetween 2 vehicle.ad.selectedNodeId %d to %d", vehicle.ad.selectedNodeId, createdId) - ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(createdId), reverseDirection) + ADGraphManager:toggleConnectionBetween(ADGraphManager:getWayPointById(vehicle.ad.selectedNodeId), ADGraphManager:getWayPointById(createdId), reverseDirection, dualConnection) end vehicle.ad.newcreated = createdId vehicle.ad.selectedNodeId = vehicle.ad.newcreated diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua index 7213d278..8c752a23 100644 --- a/scripts/Specialization.lua +++ b/scripts/Specialization.lua @@ -644,7 +644,7 @@ function AutoDrive:onDrawPreviews() local collisionFree = AutoDrive:checkForCollisionOnSpline() local color local isSubPrio = ADGraphManager:getIsPointSubPrio(lastWp.id) or ADGraphManager:getIsPointSubPrio(targetWp.id) - local isDual = AutoDrive.leftCTRLmodifierKeyPressed -- leftCtrl: make dual connection + local isDual = AutoDrive.leftCTRLmodifierKeyPressed and AutoDrive.leftALTmodifierKeyPressed if not collisionFree then color = AutoDrive.currentColors.ad_color_previewNotOk elseif isDual and isSubPrio then From d25e7474903ef9c7c55c4d31058acaa79000eb02 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sat, 2 Dec 2023 20:57:15 +1300 Subject: [PATCH 28/51] #94 - Try a different (simpler) approach. --- scripts/Utils/UtilFuncs.lua | 58 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/scripts/Utils/UtilFuncs.lua b/scripts/Utils/UtilFuncs.lua index 0c547870..f39d5143 100644 --- a/scripts/Utils/UtilFuncs.lua +++ b/scripts/Utils/UtilFuncs.lua @@ -522,48 +522,40 @@ function AutoDrive:getSplineControlPoints(startNode, endNode) return nil, nil end - local function getWaypointDirection(node, waypoint_id) + local function getWaypointDirection(node, waypointId) -- returns unit vector from start/end node to a waypoint - local wp = ADGraphManager:getWayPointById(waypoint_id) - local wp_vec = ADVectorUtils.subtract2D(node, wp) - return ADVectorUtils.unitVector2D(wp_vec) + local wp = ADGraphManager:getWayPointById(waypointId) + local wpVec = ADVectorUtils.subtract2D(node, wp) + return ADVectorUtils.unitVector2D(wpVec) end - local function getAllConnectionVectors(node) - -- returns unit-vectors for all connections of a node - local connections = {} + local function getControlPoint(node, otherNode) + -- returns the node that forms the straightest connection (closest to 180 degrees) + local bestAngle, bestDirection = nil, nil + local startEndVec = ADVectorUtils.subtract2D(node, otherNode) + for _, px in pairs(node.incoming) do - table.insert(connections, getWaypointDirection(node, px)) + local dir = getWaypointDirection(node, px) + local angle = math.abs(180 - math.abs(AutoDrive.angleBetween(startEndVec, dir))) + if bestAngle == nil or bestAngle > angle then + bestAngle = angle + bestDirection = dir + end end for _, px in pairs(node.out) do - table.insert(connections, getWaypointDirection(node, px)) - end - return connections - end - - -- cache candidates - local p0v_candidates = getAllConnectionVectors(startNode) - local p3v_candidates = getAllConnectionVectors(endNode) - local best_p0v, best_p3v, best_dist = nil, nil, nil - - -- iterate over all pairs of p0/p3 candidates - for _, p0v in pairs(p0v_candidates) do - for _, p3v in pairs(p3v_candidates) do - local intersects, dist_p0, dist_p3 = MathUtil.getLineLineIntersection2D(startNode.x, startNode.z, p0v.x, p0v.z, endNode.x, endNode.z, p3v.x, p3v.z) - -- p0/p3 are pointing away from the desired intersection, the returned distance has to be negative - if intersects and dist_p0 < 0 and dist_p3 < 0 then - local dist = -(dist_p0 + dist_p3) - if best_dist == nil or best_dist > dist then - -- keep the pair that results in the shortest distance - best_p0v, best_p3v, best_dist = p0v, p3v, dist - end + local dir = getWaypointDirection(node, px) + local angle = math.abs(180 - math.abs(AutoDrive.angleBetween(startEndVec, dir))) + if bestAngle == nil or bestAngle > angle then + bestAngle = angle + bestDirection = dir end end + if bestDirection == nil then + return nil + end + return ADVectorUtils.add2D(node, bestDirection) end - if best_dist ~= nil then - return ADVectorUtils.add2D(startNode, best_p0v), ADVectorUtils.add2D(endNode, best_p3v) - end - return nil, nil + return getControlPoint(startNode, endNode), getControlPoint(endNode, startNode) end function AutoDrive:createSplineWithControlPoints(startNode, p0, endNode, p3) From 4d0ae1902c07808b00744b21608da82fd434ca96 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sat, 9 Dec 2023 15:44:49 +0100 Subject: [PATCH 29/51] fix #1034 working with player, Giants helper not working with CP at the moment -> need update from CP --- scripts/Modes/CombineUnloaderMode.lua | 82 +++++++++++++++++++-------- scripts/Utils/CombineUtil.lua | 16 +++++- 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/scripts/Modes/CombineUnloaderMode.lua b/scripts/Modes/CombineUnloaderMode.lua index 6ec5919d..f1f3490a 100644 --- a/scripts/Modes/CombineUnloaderMode.lua +++ b/scripts/Modes/CombineUnloaderMode.lua @@ -1,17 +1,17 @@ CombineUnloaderMode = ADInheritsFrom(AbstractMode) -CombineUnloaderMode.STATE_INIT = 1 -CombineUnloaderMode.STATE_WAIT_TO_BE_CALLED = 2 -CombineUnloaderMode.STATE_DRIVE_TO_COMBINE = 3 -CombineUnloaderMode.STATE_DRIVE_TO_PIPE = 4 -CombineUnloaderMode.STATE_LEAVE_CROP = 5 -CombineUnloaderMode.STATE_DRIVE_TO_START = 6 -CombineUnloaderMode.STATE_DRIVE_TO_UNLOAD = 7 -CombineUnloaderMode.STATE_FOLLOW_COMBINE = 8 -CombineUnloaderMode.STATE_ACTIVE_UNLOAD_COMBINE = 9 -CombineUnloaderMode.STATE_FOLLOW_CURRENT_UNLOADER = 10 -CombineUnloaderMode.STATE_EXIT_FIELD = 11 -CombineUnloaderMode.STATE_REVERSE_FROM_BAD_LOCATION = 12 +CombineUnloaderMode.STATE_INIT = {} +CombineUnloaderMode.STATE_WAIT_TO_BE_CALLED = {} +CombineUnloaderMode.STATE_DRIVE_TO_COMBINE = {} +CombineUnloaderMode.STATE_DRIVE_TO_PIPE = {} +CombineUnloaderMode.STATE_LEAVE_CROP = {} +CombineUnloaderMode.STATE_DRIVE_TO_START = {} +CombineUnloaderMode.STATE_DRIVE_TO_UNLOAD = {} +CombineUnloaderMode.STATE_FOLLOW_COMBINE = {} +CombineUnloaderMode.STATE_ACTIVE_UNLOAD_COMBINE = {} +CombineUnloaderMode.STATE_FOLLOW_CURRENT_UNLOADER = {} +CombineUnloaderMode.STATE_EXIT_FIELD = {} +CombineUnloaderMode.STATE_REVERSE_FROM_BAD_LOCATION = {} CombineUnloaderMode.MAX_COMBINE_FILLLEVEL_CHASING = 101 CombineUnloaderMode.STATIC_X_OFFSET_FROM_HEADER = 0 @@ -23,6 +23,7 @@ function CombineUnloaderMode:new(vehicle) o.trailerCount = 0 o.tractorTrainLength = 0 CombineUnloaderMode.reset(o) + CombineUnloaderMode.setStateNames(o) return o end @@ -42,7 +43,7 @@ function CombineUnloaderMode:reset() end function CombineUnloaderMode:start(user) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:start start self.state %s", tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:start start self.state %s", tostring(self:getStateName())) if not self.vehicle.ad.stateModule:isActive() then self.vehicle:startAutoDrive() end @@ -57,7 +58,7 @@ function CombineUnloaderMode:start(user) if self.activeTask ~= nil then self.vehicle.ad.taskModule:addTask(self.activeTask) end - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:start end self.state %s", tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:start end self.state %s", tostring(self:getStateName())) end function CombineUnloaderMode:monitorTasks(dt) @@ -140,7 +141,7 @@ function CombineUnloaderMode:handleFinishedTask() if self.activeTask ~= nil then self.vehicle.ad.taskModule:addTask(self.activeTask) end - AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:handleFinishedTask self.state %s", tostring(self.state)) + AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:handleFinishedTask self.state %s", tostring(self:getStateName())) end function CombineUnloaderMode:stop() @@ -165,7 +166,7 @@ function CombineUnloaderMode:continue() AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:continue self.STATE_DRIVE_TO_UNLOAD") self.activeTask:continue() else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:continue self.state" .. tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:continue self.state" .. tostring(self:getStateName())) self.vehicle.ad.taskModule:abortCurrentTask() if AutoDrive.checkIsOnField(x, y, z) and distanceToStart > 30 then @@ -193,7 +194,7 @@ function CombineUnloaderMode:continue() end function CombineUnloaderMode:getNextTask() - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask start self.state %s", tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask start self.state %s", tostring(self:getStateName())) local nextTask local x, y, z = getWorldTranslation(self.vehicle.components[1].node) @@ -223,7 +224,7 @@ function CombineUnloaderMode:getNextTask() self:setToWaitForCall() end end - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask - STATE_INIT end self.state %s", tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask - STATE_INIT end self.state %s", tostring(self:getStateName())) elseif self.state == self.STATE_DRIVE_TO_COMBINE then AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask - STATE_DRIVE_TO_COMBINE") -- we finished the precall to combine route @@ -278,7 +279,7 @@ function CombineUnloaderMode:getNextTask() self.state = self.STATE_DRIVE_TO_UNLOAD end - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask end self.state %s", tostring(self.state)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getNextTask end self.state %s", tostring(self:getStateName())) return nextTask end @@ -290,7 +291,7 @@ function CombineUnloaderMode:setToWaitForCall(keepCombine) if self.combine ~= nil and self.combine.ad ~= nil and (keepCombine == nil or keepCombine ~= true) then self.combine = nil end - AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:setToWaitForCall end self.state %s self.combine %s", tostring(self.state), tostring(self.combine)) + AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:setToWaitForCall end self.state %s self.combine %s", tostring(self:getStateName()), tostring(self.combine)) end function CombineUnloaderMode:assignToHarvester(harvester) @@ -306,7 +307,9 @@ function CombineUnloaderMode:assignToHarvester(harvester) local cpIsCalling = AutoDrive:getIsCPWaitingForUnload(harvester) - if (self.combine.spec_combine == nil or not AutoDrive.getIsBufferCombine(self.combine)) and (self.combine.ad.noMovementTimer.elapsedTime > 500 or cleftCapacity < 0.1 or cpIsCalling or cFillRatio > 0.945) then + if (self.combine.spec_combine == nil or not AutoDrive.getIsBufferCombine(self.combine)) + and (self.combine.ad.noMovementTimer.elapsedTime > 500 or cleftCapacity < 0.1 or cpIsCalling or cFillRatio > 0.945) + then -- default unloading - no movement self.state = self.STATE_DRIVE_TO_PIPE self.vehicle.ad.taskModule:addTask(EmptyHarvesterTask:new(self.vehicle, self.combine)) @@ -500,7 +503,7 @@ function CombineUnloaderMode:getSideChaseOffsetX() if AutoDrive.isSugarcaneHarvester(self.combine) then -- check for SugarcaneHarvester has to be first, as it also is IsBufferCombine! sideChaseTermX = AutoDrive.getPipeLength(self.combine) - elseif AutoDrive.getIsBufferCombine(self.combine) then + elseif AutoDrive.getIsAutoAimingChopper(self.combine) then sideChaseTermX = sideChaseTermPipeIn + CombineUnloaderMode.STATIC_X_OFFSET_FROM_HEADER elseif (self.combine.ad ~= nil and self.combine.ad.storedPipeLength ~= nil) or AutoDrive.isPipeOut(self.combine) then -- If the pipe is extended, though, target it regardless @@ -527,7 +530,7 @@ function CombineUnloaderMode:getSideChaseOffsetX_new() if AutoDrive.isSugarcaneHarvester(self.combine) then -- check for SugarcaneHarvester has to be first, as it also is IsBufferCombine! sideChaseTermX = AutoDrive.getPipeLength(self.combine) - elseif AutoDrive.getIsBufferCombine(self.combine) then + elseif AutoDrive.getIsAutoAimingChopper(self.combine) then sideChaseTermX = sideChaseTermPipeIn + CombineUnloaderMode.STATIC_X_OFFSET_FROM_HEADER elseif (self.combine.ad ~= nil and self.combine.ad.storedPipeLength ~= nil) or AutoDrive.isPipeOut(self.combine) then -- If the pipe is extended, though, target it regardless @@ -645,7 +648,7 @@ end function CombineUnloaderMode:getRearChaseOffsetX(leftBlocked, rightBlocked) local rearChaseOffset = (self.combine.size.width / 2 + self.vehicle.size.width / 2) + 1 - if AutoDrive.getIsBufferCombine(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then + if AutoDrive.getIsAutoAimingChopper(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then return 0 elseif rightBlocked and leftBlocked then return 0 @@ -659,7 +662,7 @@ end function CombineUnloaderMode:getRearChaseOffsetZ() local followDistance = AutoDrive.getSetting("followDistance", self.vehicle) local rearChaseOffset = -followDistance - (self.combine.size.length / 2) - if AutoDrive.getIsBufferCombine(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then + if AutoDrive.getIsAutoAimingChopper(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then rearChaseOffset = -followDistance - (self.combine.size.length / 2) else -- math.sqrt(2) ensures the trailer could straighten if it was turned 90 degrees, and it makes this point further @@ -716,6 +719,7 @@ function CombineUnloaderMode:getPipeChasePosition(planningPhase) if AutoDrive.getIsBufferCombine(self.combine) then -- chopper --AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getPipeChasePosition=IsBufferCombine") + local leftChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, sideChaseTermX + self:getPipeSlopeCorrection(), sideChaseTermZ - 2) local rightChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, -(sideChaseTermX + self:getPipeSlopeCorrection()), sideChaseTermZ - 2) local rearChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, 0, rearChaseTermZ) @@ -731,9 +735,14 @@ function CombineUnloaderMode:getPipeChasePosition(planningPhase) chaseNode = rightChasePos sideIndex = AutoDrive.CHASEPOS_RIGHT end + -- following order is important! if AutoDrive:getIsCPActive(self.combine) and AutoDrive.combineIsTurning(self.combine) then chaseNode = rearChasePos sideIndex = AutoDrive.CHASEPOS_REAR + elseif not AutoDrive.getIsAutoAimingChopper(self.combine) then + -- chopper with fixed unloading + local sideOffsetZ = self:getDynamicSideChaseOffsetZ_fromDischargeNode(planningPhase) + chaseNode = self:getPipeChaseWayPoint(0, sideOffsetZ) elseif (not leftBlocked) and ((self:isUnloaderOnCorrectSide(AutoDrive.CHASEPOS_LEFT) and angleToLeftChaseSide < angleToRearChaseSide) or planningPhase) then chaseNode = leftChasePos sideIndex = AutoDrive.CHASEPOS_LEFT @@ -745,6 +754,7 @@ function CombineUnloaderMode:getPipeChasePosition(planningPhase) sideIndex = AutoDrive.CHASEPOS_REAR end else + -- harveser with bunker --AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getPipeChasePosition:IsNormalCombine") local rearChaseTermX = self:getRearChaseOffsetX(leftBlocked, rightBlocked) @@ -839,6 +849,28 @@ function CombineUnloaderMode:unregisterFollowingUnloader() self.followingUnloader = nil end +function CombineUnloaderMode:setStateNames() + if self.statesToNames == nil then + self.statesToNames = {} + for name, id in pairs(CombineUnloaderMode) do + if string.sub(name, 1, 6) == "STATE_" then + self.statesToNames[id] = name + end + end + end +end + +function CombineUnloaderMode:getStateName(state) + local requestedState = state + if requestedState == nil then + requestedState = self.state + end + if requestedState == nil then + Logging.error("[AD] CombineUnloaderMode: Could not find name for state ->%s<- !", tostring(requestedState)) + end + return self.statesToNames[requestedState] or "" +end + function CombineUnloaderMode.debugMsg(vehicle, debugText, ...) if CombineUnloaderMode.debug == true then AutoDrive.debugMsg(vehicle, debugText, ...) diff --git a/scripts/Utils/CombineUtil.lua b/scripts/Utils/CombineUtil.lua index 98132549..52ba2b77 100644 --- a/scripts/Utils/CombineUtil.lua +++ b/scripts/Utils/CombineUtil.lua @@ -5,7 +5,17 @@ AutoDrive.CHASEPOS_FRONT = 4 AutoDrive.CHASEPOS_UNKNOWN = 0 function AutoDrive.getIsBufferCombine(vehicle) - return vehicle ~= nil and vehicle.spec_combine ~= nil and vehicle.spec_combine.isBufferCombine == true + return vehicle ~= nil + and vehicle.spec_combine ~= nil + and vehicle.spec_combine.isBufferCombine == true +end + +function AutoDrive.getIsAutoAimingChopper(vehicle) + return vehicle ~= nil + and vehicle.spec_combine ~= nil + and vehicle.spec_combine.isBufferCombine == true + and vehicle.spec_pipe ~= nil + and vehicle.spec_pipe.numAutoAimingStates > 0 end function AutoDrive.getDischargeNode(combine) @@ -114,7 +124,7 @@ function AutoDrive.getPipeSide(combine) local dischargeNode = AutoDrive.getDischargeNode(combine) local dischargeX, dichargeY, dischargeZ = getWorldTranslation(dischargeNode) local diffX, _, _ = worldToLocal(combineNode, dischargeX, dichargeY, dischargeZ) - if combine.ad ~= nil and AutoDrive.isPipeOut(combine) and not AutoDrive.getIsBufferCombine(combine) then + if combine.ad ~= nil and AutoDrive.isPipeOut(combine) and not AutoDrive.getIsAutoAimingChopper(combine) then combine.ad.storedPipeSide = AutoDrive.sign(diffX) end return AutoDrive.sign(diffX) @@ -131,7 +141,7 @@ function AutoDrive.getPipeLength(combine) 0, pipeRootZ - dischargeZ) --AutoDrive.debugPrint(combine, AutoDrive.DC_COMBINEINFO, "AutoDrive.getPipeLength - " .. length) - if AutoDrive.isPipeOut(combine) and not AutoDrive.getIsBufferCombine(combine) then + if AutoDrive.isPipeOut(combine) and not AutoDrive.getIsAutoAimingChopper(combine) then local combineNode = AutoDrive.getPipeRoot(combine) local dischargeX, dichargeY, dischargeZ = getWorldTranslation(AutoDrive.getDischargeNode(combine)) diffX, _, _ = worldToLocal(combineNode, dischargeX, dichargeY, dischargeZ) From ec891921002869585b156288b1632423e1ae1282 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Mon, 11 Dec 2023 00:58:50 +0100 Subject: [PATCH 30/51] Update GraphManager.lua Version von Axel --- scripts/Manager/GraphManager.lua | 3118 +++++++++++++++--------------- 1 file changed, 1567 insertions(+), 1551 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index d7fb0d2e..124fd4ab 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -1,3 +1,4 @@ + ADGraphManager = {} ADGraphManager.debugGroupName = "AD_Debug" @@ -6,1834 +7,1849 @@ ADGraphManager.MIN_START_DISTANCE = 8 ADGraphManager.MAX_POINTS_IN_SECTION = 100000 function ADGraphManager:load() - self.wayPoints = {} - self.mapMarkers = {} - self.groups = {} - self.groups["All"] = 1 - self.changes = false - self.preparedWayPoints = false + self.wayPoints = {} + self.mapMarkers = {} + self.groups = {} + self.groups["All"] = 1 + self.changes = false + self.preparedWayPoints = false end function ADGraphManager:markChanges() - self.changes = true - self.preparedWayPoints = false + self.changes = true + self.preparedWayPoints = false end function ADGraphManager:resetChanges() - self.changes = false + self.changes = false end function ADGraphManager:hasChanges() - return self.changes + return self.changes end function ADGraphManager:areWayPointsPrepared() - return self.preparedWayPoints + return self.preparedWayPoints end -- Calling functions expect a linear, continuous array function ADGraphManager:getWayPoints() - return self.wayPoints + return self.wayPoints end function ADGraphManager:getWayPointById(wayPointId) - return self.wayPoints[wayPointId] + return self.wayPoints[wayPointId] end function ADGraphManager:resetWayPoints() - self.wayPoints = {} - self:markChanges() + self.wayPoints = {} + self:markChanges() end function ADGraphManager:setWayPoints(wayPoints) - self.wayPoints = wayPoints - self:markChanges() + self.wayPoints = wayPoints + self:markChanges() end function ADGraphManager:getWayPointsCount() - return #self.wayPoints + return #self.wayPoints end function ADGraphManager:setWayPoint(newPoint) - self.wayPoints[newPoint.id] = newPoint - self:markChanges() + self.wayPoints[newPoint.id] = newPoint + self:markChanges() end function ADGraphManager:getMapMarkers() - return self.mapMarkers + return self.mapMarkers end function ADGraphManager:getMapMarkerById(mapMarkerId) - return self.mapMarkers[mapMarkerId] + return self.mapMarkers[mapMarkerId] end function ADGraphManager:getMapMarkerByWayPointId(wayPointId) - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == wayPointId then - return mapMarker - end - end - return nil + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == wayPointId then + return mapMarker + end + end + return nil end function ADGraphManager:getMapMarkerByName(mapMarkerName) - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.name == mapMarkerName then - return mapMarker - end - end - return nil + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.name == mapMarkerName then + return mapMarker + end + end + return nil end function ADGraphManager:getMapMarkersInGroup(groupName) - local markersInGroup = {} + local markersInGroup = {} - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.group == groupName then - table.insert(markersInGroup, mapMarker) - end - end + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.group == groupName then + table.insert(markersInGroup, mapMarker) + end + end - local sort_func = function(a, b) - a = tostring(a.name):lower() - b = tostring(b.name):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end + local sort_func = function(a, b) + a = tostring(a.name):lower() + b = tostring(b.name):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end - table.sort(markersInGroup, sort_func) + table.sort(markersInGroup, sort_func) - return markersInGroup + return markersInGroup end function ADGraphManager:resetMapMarkers() - self.mapMarkers = {} + self.mapMarkers = {} end function ADGraphManager:setMapMarkers(mapMarkers) - self.mapMarkers = mapMarkers - -- create debug markers, debug markers are not saved, so no need to delete them or update map hotspots required - -- notifyDestinationListeners is called from caller function -> argument is false - self:createDebugMarkers(false) + self.mapMarkers = mapMarkers + -- create debug markers, debug markers are not saved, so no need to delete them or update map hotspots required + -- notifyDestinationListeners is called from caller function -> argument is false + self:createDebugMarkers(false) end function ADGraphManager:setMapMarker(mapMarker) - self.mapMarkers[mapMarker.markerIndex] = mapMarker + self.mapMarkers[mapMarker.markerIndex] = mapMarker end function ADGraphManager:getPathTo(vehicle, waypointId, startPoint) - local wp = {} - - local x, _, z = getWorldTranslation(vehicle.components[1].node) - local wp_target = self.wayPoints[waypointId] - - if wp_target ~= nil then - local distanceToTarget = MathUtil.vector2Length(x - wp_target.x, z - wp_target.z) - if distanceToTarget < ADGraphManager.MIN_START_DISTANCE then - table.insert(wp, wp_target) - return wp - end - end - - local closestWaypoint = self:findMatchingWayPointForVehicle(vehicle) - if startPoint ~= nil and startPoint.id ~= nil then - -- consider id to avoid using Pathfinder wayPoints - local distanceToStartPoint = MathUtil.vector2Length(x - startPoint.x, z - startPoint.z) - if distanceToStartPoint < 5 then - closestWaypoint = startPoint.id - end - end - if closestWaypoint ~= nil then - local outCandidates = self:getBestOutPoints(vehicle, closestWaypoint) - wp = self:pathFromTo(closestWaypoint, waypointId, outCandidates) - end - - return wp + local wp = {} + + local x, _, z = getWorldTranslation(vehicle.components[1].node) + local wp_target = self.wayPoints[waypointId] + + if wp_target ~= nil then + local distanceToTarget = MathUtil.vector2Length(x - wp_target.x, z - wp_target.z) + if distanceToTarget < ADGraphManager.MIN_START_DISTANCE then + table.insert(wp, wp_target) + return wp + end + end + + local closestWaypoint = self:findMatchingWayPointForVehicle(vehicle) + if startPoint ~= nil and startPoint.id ~= nil then + -- consider id to avoid using Pathfinder wayPoints + local distanceToStartPoint = MathUtil.vector2Length(x - startPoint.x, z - startPoint.z) + if distanceToStartPoint < 5 then + closestWaypoint = startPoint.id + end + end + if closestWaypoint ~= nil then + local outCandidates = self:getBestOutPoints(vehicle, closestWaypoint) + wp = self:pathFromTo(closestWaypoint, waypointId, outCandidates) + end + + return wp end function ADGraphManager:pathFromTo(startWaypointId, targetWaypointId, preferredNeighbors) - local wp = {} - if - startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and targetWaypointId ~= nil and - self.wayPoints[targetWaypointId] ~= nil - then - if startWaypointId == targetWaypointId then - table.insert(wp, self.wayPoints[targetWaypointId]) - else - if preferredNeighbors == nil then - preferredNeighbors = {} - end - wp = ADPathCalculator:GetPath(startWaypointId, targetWaypointId, preferredNeighbors) - end - end - return wp + local wp = {} + if + startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and targetWaypointId ~= nil and + self.wayPoints[targetWaypointId] ~= nil + then + if startWaypointId == targetWaypointId then + table.insert(wp, self.wayPoints[targetWaypointId]) + else + if preferredNeighbors == nil then + preferredNeighbors = {} + end + wp = ADPathCalculator:GetPath(startWaypointId, targetWaypointId, preferredNeighbors) + end + end + return wp end function ADGraphManager:pathFromToMarker(startWaypointId, markerId) - local wp = {} - if - startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and self.mapMarkers[markerId] ~= nil and - self.mapMarkers[markerId].id ~= nil - then - local targetId = self.mapMarkers[markerId].id - if targetId == startWaypointId then - table.insert(wp, 1, self.wayPoints[targetId]) - return wp - else - wp = ADPathCalculator:GetPath(startWaypointId, targetId, {}) - end - end - return wp + local wp = {} + if + startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and self.mapMarkers[markerId] ~= nil and + self.mapMarkers[markerId].id ~= nil + then + local targetId = self.mapMarkers[markerId].id + if targetId == startWaypointId then + table.insert(wp, 1, self.wayPoints[targetId]) + return wp + else + wp = ADPathCalculator:GetPath(startWaypointId, targetId, {}) + end + end + return wp end function ADGraphManager:FastShortestPath(start, markerName, markerId) - local wp = {} - local start_id = start - local target_id = 0 + local wp = {} + local start_id = start + local target_id = 0 - if start_id == nil or start_id == 0 then - return wp - end + if start_id == nil or start_id == 0 then + return wp + end - for i in pairs(self.mapMarkers) do - if self.mapMarkers[i].name == markerName then - target_id = self.mapMarkers[i].id - break - end - end + for i in pairs(self.mapMarkers) do + if self.mapMarkers[i].name == markerName then + target_id = self.mapMarkers[i].id + break + end + end - if target_id == 0 then - return wp - end + if target_id == 0 then + return wp + end - if target_id == start_id then - table.insert(wp, 1, self.wayPoints[target_id]) - return wp - end + if target_id == start_id then + table.insert(wp, 1, self.wayPoints[target_id]) + return wp + end - wp = ADPathCalculator:GetPath(start_id, target_id, {}) - return wp + wp = ADPathCalculator:GetPath(start_id, target_id, {}) + return wp end function ADGraphManager:getDistanceFromNetwork(vehicle) - local _, distance = vehicle:getClosestWayPoint() - return distance + local _, distance = vehicle:getClosestWayPoint() + return distance end function ADGraphManager:checkYPositionIntegrity() - for _, wp in pairs(self.wayPoints) do - if wp.y == -1 then - wp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wp.x, 1, wp.z) - end - end + for _, wp in pairs(self.wayPoints) do + if wp.y == -1 then + wp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wp.x, 1, wp.z) + end + end end function ADGraphManager:removeWayPoint(wayPointId, sendEvent) - if wayPointId ~= nil and wayPointId >= 0 and self.wayPoints[wayPointId] ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating way point deletion all over the network - AutoDriveDeleteWayPointEvent.sendEvent(wayPointId) - else - -- Deleting map marker if there is one on this waypoint, 'sendEvent' must be false because the event propagation has already happened - self:removeMapMarkerByWayPoint(wayPointId, false) - - local wayPoint = self.wayPoints[wayPointId] - - -- Removing incoming node reference on all out nodes - for _, id in pairs(wayPoint.out) do - if self.wayPoints[id] ~= nil and self.wayPoints[id].incoming ~= nil and wayPoint.id ~= nil then - local incomingId = table.indexOf(self.wayPoints[id].incoming, wayPoint.id) - if incomingId ~= nil then - table.remove(self.wayPoints[id].incoming, incomingId) - end - end - end - - -- Removing out node reference on all incoming nodes - for _, id in pairs(wayPoint.incoming) do - if self.wayPoints[id] ~= nil and self.wayPoints[id].out ~= nil and wayPoint.id ~= nil then - local outId = table.indexOf(self.wayPoints[id].out, wayPoint.id) - if outId ~= nil then - table.remove(self.wayPoints[id].out, outId) - end - end - end - - if #wayPoint.incoming == 0 then - -- This is a reverse node, so we can't rely on the incoming table - for _, wp in pairs(self.wayPoints) do - if wp.out ~= nil and wayPoint.id ~= nil then - if table.contains(wp.out, wayPoint.id) then - table.removeValue(wp.out, wayPoint.id) - end - end - end - end - - -- Removing waypoint from waypoints array and invalidate it by setting id to -1 - local wp = table.remove(self.wayPoints, wayPoint.id) - if wp ~= nil then - wp.id = -1 - end - - -- Adjusting ids for all succesive nodes :( - for _, wp in pairs(self.wayPoints) do - if wp.id > wayPointId then - wp.id = wp.id - 1 - end - for i, outId in pairs(wp.out) do - if outId > wayPointId then - wp.out[i] = outId - 1 - end - end - for i, incomingId in pairs(wp.incoming) do - if incomingId > wayPointId then - wp.incoming[i] = incomingId - 1 - end - end - end - - -- Adjusting way point id in markers - for _, marker in pairs(self.mapMarkers) do - if marker.id > wayPointId then - marker.id = marker.id - 1 - end - end - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if wayPointId ~= nil and wayPointId >= 0 and self.wayPoints[wayPointId] ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating way point deletion all over the network + AutoDriveDeleteWayPointEvent.sendEvent(wayPointId) + else + -- Deleting map marker if there is one on this waypoint, 'sendEvent' must be false because the event propagation has already happened + self:removeMapMarkerByWayPoint(wayPointId, false) + + local wayPoint = self.wayPoints[wayPointId] + + -- Removing incoming node reference on all out nodes + for _, id in pairs(wayPoint.out) do + if self.wayPoints[id] ~= nil and self.wayPoints[id].incoming ~= nil and wayPoint.id ~= nil then + local incomingId = table.indexOf(self.wayPoints[id].incoming, wayPoint.id) + if incomingId ~= nil then + table.remove(self.wayPoints[id].incoming, incomingId) + end + end + end + + -- Removing out node reference on all incoming nodes + for _, id in pairs(wayPoint.incoming) do + if self.wayPoints[id] ~= nil and self.wayPoints[id].out ~= nil and wayPoint.id ~= nil then + local outId = table.indexOf(self.wayPoints[id].out, wayPoint.id) + if outId ~= nil then + table.remove(self.wayPoints[id].out, outId) + end + end + end + + if #wayPoint.incoming == 0 then + -- This is a reverse node, so we can't rely on the incoming table + for _, wp in pairs(self.wayPoints) do + if wp.out ~= nil and wayPoint.id ~= nil then + if table.contains(wp.out, wayPoint.id) then + table.removeValue(wp.out, wayPoint.id) + end + end + end + end + + -- Removing waypoint from waypoints array and invalidate it by setting id to -1 + local wp = table.remove(self.wayPoints, wayPoint.id) + if wp ~= nil then + wp.id = -1 + end + + -- Adjusting ids for all succesive nodes :( + for _, wp in pairs(self.wayPoints) do + if wp.id > wayPointId then + wp.id = wp.id - 1 + end + for i, outId in pairs(wp.out) do + if outId > wayPointId then + wp.out[i] = outId - 1 + end + end + for i, incomingId in pairs(wp.incoming) do + if incomingId > wayPointId then + wp.incoming[i] = incomingId - 1 + end + end + end + + -- Adjusting way point id in markers + for _, marker in pairs(self.mapMarkers) do + if marker.id > wayPointId then + marker.id = marker.id - 1 + end + end + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:renameMapMarker(newName, markerId, sendEvent) - if newName:len() >= 1 and markerId >= 0 then - local mapMarker = self:getMapMarkerById(markerId) - if mapMarker == nil or mapMarker.isADDebug == true then - -- do not allow rename debug marker - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating marker rename all over the network - AutoDriveRenameMapMarkerEvent.sendEvent(newName, markerId) - else - -- Saving old map marker name - local oldName = self.mapMarkers[markerId].name - -- Renaming map marker - self.mapMarkers[markerId].name = newName - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if newName:len() >= 1 and markerId >= 0 then + local mapMarker = self:getMapMarkerById(markerId) + if mapMarker == nil or mapMarker.isADDebug == true then + -- do not allow rename debug marker + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating marker rename all over the network + AutoDriveRenameMapMarkerEvent.sendEvent(newName, markerId) + else + -- Saving old map marker name + local oldName = self.mapMarkers[markerId].name + -- Renaming map marker + self.mapMarkers[markerId].name = newName + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:createMapMarkerOnClosest(vehicle, markerName, sendEvent) - if vehicle ~= nil and markerName:len() >= 1 then - -- Finding closest waypoint - local closest, _ = vehicle:getClosestWayPoint() - if closest ~= nil and closest ~= -1 and self.wayPoints[closest] ~= nil then - self:createMapMarker(closest, markerName, sendEvent) - end - end + if vehicle ~= nil and markerName:len() >= 1 then + -- Finding closest waypoint + local closest, _ = vehicle:getClosestWayPoint() + if closest ~= nil and closest ~= -1 and self.wayPoints[closest] ~= nil then + self:createMapMarker(closest, markerName, sendEvent) + end + end end function ADGraphManager:createMapMarker(markerId, markerName, sendEvent) - if markerId ~= nil and markerId >= 0 and markerName:len() >= 1 then - if sendEvent == nil or sendEvent == true then - -- Propagating marker creation all over the network - AutoDriveCreateMapMarkerEvent.sendEvent(markerId, markerName) - else - -- Creating the new map marker - self.mapMarkers[#self.mapMarkers + 1] = { - id = markerId, - markerIndex = (#self.mapMarkers + 1), - name = markerName, - group = "All" - } - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if markerId ~= nil and markerId >= 0 and markerName:len() >= 1 then + if sendEvent == nil or sendEvent == true then + -- Propagating marker creation all over the network + AutoDriveCreateMapMarkerEvent.sendEvent(markerId, markerName) + else + -- Creating the new map marker + self.mapMarkers[#self.mapMarkers + 1] = { + id = markerId, + markerIndex = (#self.mapMarkers + 1), + name = markerName, + group = "All" + } + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:addGroup(groupName, sendEvent) - if groupName:len() >= 1 and self.groups[groupName] == nil then - if sendEvent == nil or sendEvent == true then - -- Propagating group creation all over the network - AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_ADD) - else - self.groups[groupName] = table.count(self.groups) + 1 - for _, vehicle in pairs(g_currentMission.vehicles) do - if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then - if vehicle.ad.groups[groupName] == nil then - vehicle.ad.groups[groupName] = false - end - end - end - -- Resetting HUD - if AutoDrive.Hud ~= nil then - AutoDrive.Hud.lastUIScale = 0 - end - self:markChanges() - end - end + if groupName:len() >= 1 and self.groups[groupName] == nil then + if sendEvent == nil or sendEvent == true then + -- Propagating group creation all over the network + AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_ADD) + else + self.groups[groupName] = table.count(self.groups) + 1 + for _, vehicle in pairs(g_currentMission.vehicles) do + if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then + if vehicle.ad.groups[groupName] == nil then + vehicle.ad.groups[groupName] = false + end + end + end + -- Resetting HUD + if AutoDrive.Hud ~= nil then + AutoDrive.Hud.lastUIScale = 0 + end + self:markChanges() + end + end end function ADGraphManager:removeGroup(groupName, sendEvent) - if self.groups[groupName] ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating group creation all over the network - AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_REMOVE) - else - local groupId = self.groups[groupName] - -- Removing group from the groups list - self.groups[groupName] = nil - -- Removing group from the vehicles groups list - for _, vehicle in pairs(g_currentMission.vehicles) do - if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then - if vehicle.ad.groups[groupName] ~= nil then - vehicle.ad.groups[groupName] = nil - end - end - end - -- Moving all markers in the deleted group to default group - for markerID, mapMarker in pairs(self:getMapMarkers()) do - if mapMarker.group == groupName then - mapMarker.group = "All" - end - end - -- Resetting other goups id - for gName, gId in pairs(self.groups) do - if groupId <= gId then - self.groups[gName] = gId - 1 - end - end - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - self:markChanges() - end - end + if self.groups[groupName] ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating group creation all over the network + AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_REMOVE) + else + local groupId = self.groups[groupName] + -- Removing group from the groups list + self.groups[groupName] = nil + -- Removing group from the vehicles groups list + for _, vehicle in pairs(g_currentMission.vehicles) do + if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then + if vehicle.ad.groups[groupName] ~= nil then + vehicle.ad.groups[groupName] = nil + end + end + end + -- Moving all markers in the deleted group to default group + for markerID, mapMarker in pairs(self:getMapMarkers()) do + if mapMarker.group == groupName then + mapMarker.group = "All" + end + end + -- Resetting other goups id + for gName, gId in pairs(self.groups) do + if groupId <= gId then + self.groups[gName] = gId - 1 + end + end + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + self:markChanges() + end + end end function ADGraphManager:changeMapMarkerGroup(groupName, markerId, sendEvent) - if - groupName:len() >= 1 and self.groups[groupName] ~= nil and markerId >= 0 and - groupName ~= ADGraphManager.debugGroupName - then - if sendEvent == nil or sendEvent == true then - -- Propagating marker group change all over the network - AutoDriveChangeMapMarkerGroupEvent.sendEvent(groupName, markerId) - else - -- Changing the group name of the marker - self.mapMarkers[markerId].group = groupName - self:markChanges() - end - end + if + groupName:len() >= 1 and self.groups[groupName] ~= nil and markerId >= 0 and + groupName ~= ADGraphManager.debugGroupName + then + if sendEvent == nil or sendEvent == true then + -- Propagating marker group change all over the network + AutoDriveChangeMapMarkerGroupEvent.sendEvent(groupName, markerId) + else + -- Changing the group name of the marker + self.mapMarkers[markerId].group = groupName + self:markChanges() + end + end end function ADGraphManager:getGroups() - return self.groups + return self.groups end function ADGraphManager:setGroups(groups, updateVehicles) - self.groups = groups - if updateVehicles then - for _, vehicle in pairs(g_currentMission.vehicles) do - if vehicle.ad ~= nil then - if vehicle.ad.groups == nil then - vehicle.ad.groups = {} - end - local newGroups = {} - for groupName, _ in pairs(ADGraphManager:getGroups()) do - newGroups[groupName] = vehicle.ad.groups[groupName] or false - end - vehicle.ad.groups = newGroups - end - end - end + self.groups = groups + if updateVehicles then + for _, vehicle in pairs(g_currentMission.vehicles) do + if vehicle.ad ~= nil then + if vehicle.ad.groups == nil then + vehicle.ad.groups = {} + end + local newGroups = {} + for groupName, _ in pairs(ADGraphManager:getGroups()) do + newGroups[groupName] = vehicle.ad.groups[groupName] or false + end + vehicle.ad.groups = newGroups + end + end + end end function ADGraphManager:getGroupByName(groupName) - return self.groups[groupName] + return self.groups[groupName] end function ADGraphManager:removeMapMarker(markerId, sendEvent) - if markerId ~= nil and markerId >= 0 then - if sendEvent == nil or sendEvent == true then - -- Propagating marker deletion all over the network - AutoDriveDeleteMapMarkerEvent.sendEvent(markerId) - else - if self.mapMarkers[markerId] ~= nil then - table.remove(self.mapMarkers, markerId) - --Readjust stored markerIndex values to point to corrected ID - for markerID, marker in pairs(self.mapMarkers) do - marker.markerIndex = markerID - end - - if g_server ~= nil then - -- Removing references to it on all vehicles - for _, vehicle in pairs(g_currentMission.vehicles) do - if - vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil and - vehicle.ad.stateModule.getParkDestinationAtJobFinished ~= nil - then - local parkDestinationAtJobFinished = - vehicle.ad.stateModule:getParkDestinationAtJobFinished() - if parkDestinationAtJobFinished ~= nil and parkDestinationAtJobFinished >= markerId then - if parkDestinationAtJobFinished == markerId then - vehicle.ad.stateModule:setParkDestinationAtJobFinished(-1) - else - vehicle.ad.stateModule:setParkDestinationAtJobFinished( - math.max(parkDestinationAtJobFinished - 1, 1) - ) - end - end - end - end - -- handle all vehicles and tools park destination - for _, vehicle in pairs(g_currentMission.vehicles) do - if - vehicle.advd ~= nil and vehicle.advd.getParkDestination ~= nil and - vehicle.advd.setParkDestination ~= nil - then - local parkDestination = vehicle.advd:getParkDestination(vehicle) - if parkDestination ~= nil and parkDestination >= markerId then - if parkDestination == markerId then - vehicle.advd:setParkDestination(vehicle, -1) - else - vehicle.advd:setParkDestination(vehicle, math.max(parkDestination - 1, 1)) - end - end - end - end - end - -- remove deleted marker from vehicle destinations - ADGraphManager:checkResetVehicleDestinations(markerId) - end - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if markerId ~= nil and markerId >= 0 then + if sendEvent == nil or sendEvent == true then + -- Propagating marker deletion all over the network + AutoDriveDeleteMapMarkerEvent.sendEvent(markerId) + else + if self.mapMarkers[markerId] ~= nil then + table.remove(self.mapMarkers, markerId) + --Readjust stored markerIndex values to point to corrected ID + for markerID, marker in pairs(self.mapMarkers) do + marker.markerIndex = markerID + end + + if g_server ~= nil then + -- Removing references to it on all vehicles + for _, vehicle in pairs(g_currentMission.vehicles) do + if + vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil and + vehicle.ad.stateModule.getParkDestinationAtJobFinished ~= nil + then + local parkDestinationAtJobFinished = + vehicle.ad.stateModule:getParkDestinationAtJobFinished() + if parkDestinationAtJobFinished ~= nil and parkDestinationAtJobFinished >= markerId then + if parkDestinationAtJobFinished == markerId then + vehicle.ad.stateModule:setParkDestinationAtJobFinished(-1) + else + vehicle.ad.stateModule:setParkDestinationAtJobFinished( + math.max(parkDestinationAtJobFinished - 1, 1) + ) + end + end + end + end + -- handle all vehicles and tools park destination + for _, vehicle in pairs(g_currentMission.vehicles) do + if + vehicle.advd ~= nil and vehicle.advd.getParkDestination ~= nil and + vehicle.advd.setParkDestination ~= nil + then + local parkDestination = vehicle.advd:getParkDestination(vehicle) + if parkDestination ~= nil and parkDestination >= markerId then + if parkDestination == markerId then + vehicle.advd:setParkDestination(vehicle, -1) + else + vehicle.advd:setParkDestination(vehicle, math.max(parkDestination - 1, 1)) + end + end + end + end + end + -- remove deleted marker from vehicle destinations + ADGraphManager:checkResetVehicleDestinations(markerId) + end + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:removeMapMarkerByWayPoint(wayPointId, sendEvent) - if wayPointId ~= nil and wayPointId >= 0 then - -- Finding the map waypoint where the marker should be - local wayPoint = self.wayPoints[wayPointId] - if wayPoint ~= nil then - for markerId, marker in pairs(self.mapMarkers) do - -- Checking if the waypoint id matches the marker id - if marker.id == wayPoint.id then - self:removeMapMarker(markerId, sendEvent) - break - end - end - end - end -end - -function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, dualConnection, sendEvent) - if startNode == nil or endNode == nil then - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network - AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection, dualConnection) - else - if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then - table.removeValue(startNode.out, endNode.id) - table.removeValue(endNode.incoming, startNode.id) - else - table.insert(startNode.out, endNode.id) - if not reverseDirection then - table.insert(endNode.incoming, startNode.id) - if dualConnection then - table.insert(endNode.out, startNode.id) - table.insert(startNode.incoming, endNode.id) - end - end - end - - self:markChanges() - end + if wayPointId ~= nil and wayPointId >= 0 then + -- Finding the map waypoint where the marker should be + local wayPoint = self.wayPoints[wayPointId] + if wayPoint ~= nil then + for markerId, marker in pairs(self.mapMarkers) do + -- Checking if the waypoint id matches the marker id + if marker.id == wayPoint.id then + self:removeMapMarker(markerId, sendEvent) + break + end + end + end + end +end + +function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, sendEvent) + if startNode == nil or endNode == nil then + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network + AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection) + else + if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then + table.removeValue(startNode.out, endNode.id) + table.removeValue(endNode.incoming, startNode.id) + else + table.insert(startNode.out, endNode.id) + if not reverseDirection then + table.insert(endNode.incoming, startNode.id) + + + + + end + end + + self:markChanges() + end end --[[ -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 -reverse connection 4 +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 +reverse connection 4 ]] function ADGraphManager:setConnectionBetween(startNode, endNode, direction, sendEvent) - if startNode == nil or endNode == nil or direction < 1 or direction > 4 then - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network - AutoDriveSetConnectionEvent.sendEvent(startNode, endNode, direction) - else - -- remove all connections between the 2 nodes - if table.contains(startNode.out, endNode.id) then - table.removeValue(startNode.out, endNode.id) - end - if table.contains(startNode.incoming, endNode.id) then - table.removeValue(startNode.incoming, endNode.id) - end - if table.contains(endNode.out, startNode.id) then - table.removeValue(endNode.out, startNode.id) - end - if table.contains(endNode.incoming, startNode.id) then - table.removeValue(endNode.incoming, startNode.id) - end - if direction == 1 then - -- forward - table.insert(startNode.out, endNode.id) - table.insert(endNode.incoming, startNode.id) - elseif direction == 2 then - -- backward - table.insert(startNode.incoming, endNode.id) - table.insert(endNode.out, startNode.id) - elseif direction == 3 then - -- dual - table.insert(startNode.out, endNode.id) - table.insert(endNode.incoming, startNode.id) - table.insert(startNode.incoming, endNode.id) - table.insert(endNode.out, startNode.id) - elseif direction == 4 then - -- reverse - table.insert(startNode.out, endNode.id) - end - self:markChanges() - end + if startNode == nil or endNode == nil or direction < 1 or direction > 4 then + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network + AutoDriveSetConnectionEvent.sendEvent(startNode, endNode, direction) + else + -- remove all connections between the 2 nodes + if table.contains(startNode.out, endNode.id) then + table.removeValue(startNode.out, endNode.id) + end + if table.contains(startNode.incoming, endNode.id) then + table.removeValue(startNode.incoming, endNode.id) + end + if table.contains(endNode.out, startNode.id) then + table.removeValue(endNode.out, startNode.id) + end + if table.contains(endNode.incoming, startNode.id) then + table.removeValue(endNode.incoming, startNode.id) + end + if direction == 1 then + -- forward + table.insert(startNode.out, endNode.id) + table.insert(endNode.incoming, startNode.id) + elseif direction == 2 then + -- backward + table.insert(startNode.incoming, endNode.id) + table.insert(endNode.out, startNode.id) + elseif direction == 3 then + -- dual + table.insert(startNode.out, endNode.id) + table.insert(endNode.incoming, startNode.id) + table.insert(startNode.incoming, endNode.id) + table.insert(endNode.out, startNode.id) + elseif direction == 4 then + -- reverse + table.insert(startNode.out, endNode.id) + end + self:markChanges() + end end function ADGraphManager:createWayPoint(x, y, z, sendEvent) - if sendEvent == nil or sendEvent == true then - -- Propagating waypoint creation all over the network - AutoDriveCreateWayPointEvent.sendEvent(x, y, z) - else - local prevId = self:getWayPointsCount() - local newId = prevId + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, 0) - self:setWayPoint(newWp) - self:markChanges() + if sendEvent == nil or sendEvent == true then + -- Propagating waypoint creation all over the network + AutoDriveCreateWayPointEvent.sendEvent(x, y, z) + else + local prevId = self:getWayPointsCount() + local newId = prevId + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, 0) + self:setWayPoint(newWp) + self:markChanges() - return newWp - end + return newWp + end end function ADGraphManager:createWayPointColored(x, y, z, colors) - local prevId = self:getWayPointsCount() - local newId = prevId + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, 0, colors) - self:setWayPoint(newWp) - self:markChanges() + local prevId = self:getWayPointsCount() + local newId = prevId + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, 0, colors) + self:setWayPoint(newWp) + self:markChanges() - return newWp + return newWp end function ADGraphManager:changeWayPointPosition(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) + end end function ADGraphManager:moveWayPoint(wayPointId, x, y, z, flags, sendEvent) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating waypoint moving all over the network - AutoDriveMoveWayPointEvent.sendEvent(wayPointId, x, y, z, flags) - else - wayPoint.x = x - wayPoint.y = y - wayPoint.z = z - wayPoint.flags = flags - self:markChanges() - end - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating waypoint moving all over the network + AutoDriveMoveWayPointEvent.sendEvent(wayPointId, x, y, z, flags) + else + wayPoint.x = x + wayPoint.y = y + wayPoint.z = z + wayPoint.flags = flags + self:markChanges() + end + end end function ADGraphManager:recordWayPoint(x, y, z, connectPrevious, dual, isReverse, previousId, flags, sendEvent) - previousId = previousId or 0 - local previous - if connectPrevious then - if previousId == nil or previousId == 0 then - previousId = self:getWayPointsCount() - end - previous = self:getWayPointById(previousId) - end - if g_server ~= nil then - if sendEvent ~= false then - -- Propagating waypoint recording to clients - AutoDriveRecordWayPointEvent.sendEvent(x, y, z, connectPrevious, dual, isReverse, previousId, flags) - end - else - if sendEvent ~= false then - Logging.warning("ADGraphManager:recordWayPoint() must be called only on the server.") - return - end - end - - -- play sound only on client with enabled editor mode or RecordWhileNotInVehicle - if g_client ~= nil then - local vehicle = g_currentMission.controlledVehicle - local forced = AutoDrive.experimentalFeatures.RecordWhileNotInVehicle - if (vehicle ~= nil and vehicle.ad ~= nil and AutoDrive.isInExtendedEditorMode()) or forced then - AutoDrive.playSample(AutoDrive.recordWaypointSample, 0.25, forced) - end - end - - local newId = self:getWayPointsCount() + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, flags) - self:setWayPoint(newWp) - if connectPrevious then - self:toggleConnectionBetween(previous, newWp, isReverse, dual, false) - end - - self:markChanges() - return newWp + previousId = previousId or 0 + local previous + if connectPrevious then + if previousId == nil or previousId == 0 then + previousId = self:getWayPointsCount() + end + previous = self:getWayPointById(previousId) + end + if g_server ~= nil then + if sendEvent ~= false then + -- Propagating waypoint recording to clients + AutoDriveRecordWayPointEvent.sendEvent(x, y, z, connectPrevious, dual, isReverse, previousId, flags) + end + else + if sendEvent ~= false then + Logging.warning("ADGraphManager:recordWayPoint() must be called only on the server.") + return + end + end + + -- play sound only on client with enabled editor mode or RecordWhileNotInVehicle + if g_client ~= nil then + local vehicle = g_currentMission.controlledVehicle + local forced = AutoDrive.experimentalFeatures.RecordWhileNotInVehicle + if (vehicle ~= nil and vehicle.ad ~= nil and AutoDrive.isInExtendedEditorMode()) or forced then + AutoDrive.playSample(AutoDrive.recordWaypointSample, 0.25, forced) + end + end + + local newId = self:getWayPointsCount() + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, flags) + self:setWayPoint(newWp) + if connectPrevious then + self:toggleConnectionBetween(previous, newWp, isReverse, false) + if dual then + self:toggleConnectionBetween(newWp, previous, isReverse, false) + end + end + + self:markChanges() + return newWp end function ADGraphManager:isDualRoad(start, target) - if - start == nil or target == nil or start.incoming == nil or target.incoming == nil or start.id == nil or - target.id == nil - then - return false - end - if table.contains(start.incoming, target.id) and table.contains(target.incoming, start.id) then - return true - end - return false + if + start == nil or target == nil or start.incoming == nil or target.incoming == nil or start.id == nil or + target.id == nil + then + return false + end + if table.contains(start.incoming, target.id) and table.contains(target.incoming, start.id) then + return true + end + return false end function ADGraphManager:isReverseRoad(start, target) - if start == nil or target == nil or start.out == nil or start.id == nil or target.id == nil then - return false - end - return (not table.contains(target.incoming, start.id) and table.contains(start.out, target.id)) + if start == nil or target == nil or start.out == nil or start.id == nil or target.id == nil then + return false + end + return (not table.contains(target.incoming, start.id) and table.contains(start.out, target.id)) end function ADGraphManager:getDistanceBetweenNodes(start, target) - local euclidianDistance = - MathUtil.vector2Length( - self.wayPoints[start].x - self.wayPoints[target].x, - self.wayPoints[start].z - self.wayPoints[target].z - ) + local euclidianDistance = + MathUtil.vector2Length( + self.wayPoints[start].x - self.wayPoints[target].x, + self.wayPoints[start].z - self.wayPoints[target].z + ) - local distance = euclidianDistance + local distance = euclidianDistance - if AutoDrive.getSetting("mapMarkerDetour") > 0 then - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == start then - distance = distance + AutoDrive.getSetting("mapMarkerDetour") - break - end - end - end + if AutoDrive.getSetting("mapMarkerDetour") > 0 then + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == start then + distance = distance + AutoDrive.getSetting("mapMarkerDetour") + break + end + end + end - if self:getIsPointSubPrio(self.wayPoints[target].id) then - distance = distance * ADGraphManager.SUB_PRIO_FACTOR - end + if self:getIsPointSubPrio(self.wayPoints[target].id) then + distance = distance * ADGraphManager.SUB_PRIO_FACTOR + end - return distance + return distance end function ADGraphManager:getDriveTimeBetweenNodes(start, target, past, maxDrivingSpeed, arrivalTime) - --changed setToUse to defined 3 point for angle calculation - local wp_ahead = self.wayPoints[target] - local wp_current = self.wayPoints[start] - - if wp_ahead == nil or wp_current == nil then - return 0 - end - - local angle = 0 - - if past ~= nil then - local wp_ref = self.wayPoints[past] - if wp_ref ~= nil then - angle = - math.abs( - AutoDrive.angleBetween( - {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, - {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} - ) - ) - end - end - - local driveTime = 0 - local drivingSpeed = 50 - - if angle < 3 then - drivingSpeed = 50 - elseif angle < 5 then - drivingSpeed = 38 - elseif angle < 8 then - drivingSpeed = 27 - elseif angle < 12 then - drivingSpeed = 20 - elseif angle < 15 then - drivingSpeed = 13 - elseif angle < 20 then - drivingSpeed = 10 - elseif angle < 30 then - drivingSpeed = 7 - else - drivingSpeed = 4 - end - - if maxDrivingSpeed ~= nil then - drivingSpeed = math.min(drivingSpeed, maxDrivingSpeed) - end - - local drivingDistance = MathUtil.vector2Length(wp_ahead.x - wp_current.x, wp_ahead.z - wp_current.z) - - driveTime = (drivingDistance) / (drivingSpeed * (1000 / 3600)) - - --avoid map marker - - if not arrivalTime == true then --only for djikstra, for live travel timer we ignore it - if AutoDrive.getSetting("mapMarkerDetour") > 0 then - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == start then - driveTime = driveTime + (AutoDrive.getSetting("mapMarkerDetour") / (20 / 3.6)) - break - end - end - end - end - - return driveTime, angle + --changed setToUse to defined 3 point for angle calculation + local wp_ahead = self.wayPoints[target] + local wp_current = self.wayPoints[start] + + if wp_ahead == nil or wp_current == nil then + return 0 + end + + local angle = 0 + + if past ~= nil then + local wp_ref = self.wayPoints[past] + if wp_ref ~= nil then + angle = + math.abs( + AutoDrive.angleBetween( + {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, + {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} + ) + ) + end + end + + local driveTime = 0 + local drivingSpeed = 50 + + if angle < 3 then + drivingSpeed = 50 + elseif angle < 5 then + drivingSpeed = 38 + elseif angle < 8 then + drivingSpeed = 27 + elseif angle < 12 then + drivingSpeed = 20 + elseif angle < 15 then + drivingSpeed = 13 + elseif angle < 20 then + drivingSpeed = 10 + elseif angle < 30 then + drivingSpeed = 7 + else + drivingSpeed = 4 + end + + if maxDrivingSpeed ~= nil then + drivingSpeed = math.min(drivingSpeed, maxDrivingSpeed) + end + + local drivingDistance = MathUtil.vector2Length(wp_ahead.x - wp_current.x, wp_ahead.z - wp_current.z) + + driveTime = (drivingDistance) / (drivingSpeed * (1000 / 3600)) + + --avoid map marker + + if not arrivalTime == true then --only for djikstra, for live travel timer we ignore it + if AutoDrive.getSetting("mapMarkerDetour") > 0 then + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == start then + driveTime = driveTime + (AutoDrive.getSetting("mapMarkerDetour") / (20 / 3.6)) + break + end + end + end + end + + return driveTime, angle end function ADGraphManager:getDriveTimeForWaypoints(wps, currentWaypoint, maxDrivingSpeed) - local totalTime = 0 - - if - wps ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil and wps[currentWaypoint] ~= nil and - wps[currentWaypoint - 1] == nil - then - totalTime = - totalTime + - self:getDriveTimeBetweenNodes( - wps[currentWaypoint].id, - wps[currentWaypoint + 1].id, - nil, - maxDrivingSpeed, - true - ) --first segment, only 2 points, no angle - currentWaypoint = currentWaypoint + 1 - end - while wps ~= nil and wps[currentWaypoint - 1] ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil do - if wps[currentWaypoint] ~= nil then - totalTime = - totalTime + - self:getDriveTimeBetweenNodes( - wps[currentWaypoint].id, - wps[currentWaypoint + 1].id, - wps[currentWaypoint - 1].id, - maxDrivingSpeed, - true - ) --continuous segments, 3 points for angle - end - currentWaypoint = currentWaypoint + 1 - end - return totalTime * 1.15 + local totalTime = 0 + + if + wps ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil and wps[currentWaypoint] ~= nil and + wps[currentWaypoint - 1] == nil + then + totalTime = + totalTime + + self:getDriveTimeBetweenNodes( + wps[currentWaypoint].id, + wps[currentWaypoint + 1].id, + nil, + maxDrivingSpeed, + true + ) --first segment, only 2 points, no angle + currentWaypoint = currentWaypoint + 1 + end + while wps ~= nil and wps[currentWaypoint - 1] ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil do + if wps[currentWaypoint] ~= nil then + totalTime = + totalTime + + self:getDriveTimeBetweenNodes( + wps[currentWaypoint].id, + wps[currentWaypoint + 1].id, + wps[currentWaypoint - 1].id, + maxDrivingSpeed, + true + ) --continuous segments, 3 points for angle + end + currentWaypoint = currentWaypoint + 1 + end + return totalTime * 1.15 end function ADGraphManager:getHighestConsecutiveIndex() - local toCheckFor = 0 - local consecutive = true - while consecutive == true do - toCheckFor = toCheckFor + 1 - consecutive = false - if self.wayPoints[toCheckFor] ~= nil then - if self.wayPoints[toCheckFor].id == toCheckFor then - consecutive = true - end - end - end + local toCheckFor = 0 + local consecutive = true + while consecutive == true do + toCheckFor = toCheckFor + 1 + consecutive = false + if self.wayPoints[toCheckFor] ~= nil then + if self.wayPoints[toCheckFor].id == toCheckFor then + consecutive = true + end + end + end - return (toCheckFor - 1) + return (toCheckFor - 1) end function ADGraphManager:findMatchingWayPointForVehicle(vehicle) - local startNode = vehicle.ad.frontNode - --returns waypoint closest to vehicle position and with the most suited heading - local x1, _, z1 = getWorldTranslation(startNode) - local rx, _, rz = localDirectionToWorld(startNode, 0, 0, 1) - local vehicleVector = {x = rx, z = rz} - local point = {x = x1, z = z1} + local startNode = vehicle.ad.frontNode + --returns waypoint closest to vehicle position and with the most suited heading + local x1, _, z1 = getWorldTranslation(startNode) + local rx, _, rz = localDirectionToWorld(startNode, 0, 0, 1) + local vehicleVector = {x = rx, z = rz} + local point = {x = x1, z = z1} - local bestPoint, distance = self:findMatchingWayPoint(point, vehicleVector, vehicle:getWayPointIdsInRange(1, 20)) + local bestPoint, distance = self:findMatchingWayPoint(point, vehicleVector, vehicle:getWayPointIdsInRange(1, 20)) - if bestPoint == -1 then - return vehicle:getClosestNotReversedWayPoint() - end + if bestPoint == -1 then + return vehicle:getClosestNotReversedWayPoint() + end - return bestPoint, distance + return bestPoint, distance end function ADGraphManager:findMatchingWayPoint(point, direction, candidates) - candidates = candidates or {} - - local closest = -1 - local distance = -1 - local lastAngleToPoint = -1 - local lastAngleToVehicle = -1 - for _, id in pairs(candidates) do - local toCheck = self.wayPoints[id] - local nextP = nil - local outIndex = 1 - if toCheck.out ~= nil then - if toCheck.out[outIndex] ~= nil then - nextP = self.wayPoints[toCheck.out[outIndex]] - end - - while nextP ~= nil do - local vecToNextPoint = {x = nextP.x - toCheck.x, z = nextP.z - toCheck.z} - local vecToVehicle = {x = toCheck.x - point.x, z = toCheck.z - point.z} - local angleToNextPoint = AutoDrive.angleBetween(direction, vecToNextPoint) - local angleToVehicle = AutoDrive.angleBetween(direction, vecToVehicle) - local dis = MathUtil.vector2Length(toCheck.x - point.x, toCheck.z - point.z) - if - closest == -1 and (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) and - #toCheck.incoming > 0 - then - closest = toCheck.id - distance = dis - lastAngleToPoint = angleToNextPoint - lastAngleToVehicle = angleToVehicle - else - if - #toCheck.incoming > 0 and - (math.abs(angleToNextPoint) + math.abs(angleToVehicle)) < - (math.abs(lastAngleToPoint) + math.abs(lastAngleToVehicle)) and - (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) - then - closest = toCheck.id - distance = dis - lastAngleToPoint = angleToNextPoint - lastAngleToVehicle = angleToVehicle - end - end - - outIndex = outIndex + 1 - if toCheck.out[outIndex] ~= nil then - nextP = self.wayPoints[toCheck.out[outIndex]] - else - nextP = nil - end - end - end - end - - return closest, distance + candidates = candidates or {} + + local closest = -1 + local distance = -1 + local lastAngleToPoint = -1 + local lastAngleToVehicle = -1 + for _, id in pairs(candidates) do + local toCheck = self.wayPoints[id] + local nextP = nil + local outIndex = 1 + if toCheck.out ~= nil then + if toCheck.out[outIndex] ~= nil then + nextP = self.wayPoints[toCheck.out[outIndex]] + end + + while nextP ~= nil do + local vecToNextPoint = {x = nextP.x - toCheck.x, z = nextP.z - toCheck.z} + local vecToVehicle = {x = toCheck.x - point.x, z = toCheck.z - point.z} + local angleToNextPoint = AutoDrive.angleBetween(direction, vecToNextPoint) + local angleToVehicle = AutoDrive.angleBetween(direction, vecToVehicle) + local dis = MathUtil.vector2Length(toCheck.x - point.x, toCheck.z - point.z) + if + closest == -1 and (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) and + #toCheck.incoming > 0 + then + closest = toCheck.id + distance = dis + lastAngleToPoint = angleToNextPoint + lastAngleToVehicle = angleToVehicle + else + if + #toCheck.incoming > 0 and + (math.abs(angleToNextPoint) + math.abs(angleToVehicle)) < + (math.abs(lastAngleToPoint) + math.abs(lastAngleToVehicle)) and + (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) + then + closest = toCheck.id + distance = dis + lastAngleToPoint = angleToNextPoint + lastAngleToVehicle = angleToVehicle + end + end + + outIndex = outIndex + 1 + if toCheck.out[outIndex] ~= nil then + nextP = self.wayPoints[toCheck.out[outIndex]] + else + nextP = nil + end + end + end + end + + return closest, distance end function ADGraphManager:getWayPointsInRange(point, rangeMin, rangeMax) - local inRange = {} + local inRange = {} - for _, wp in pairs(self.wayPoints) do - local dis = MathUtil.vector2Length(wp.x - point.x, wp.z - point.z) - if dis < rangeMax and dis > rangeMin then - table.insert(inRange, wp.id) - end - end + for _, wp in pairs(self.wayPoints) do + local dis = MathUtil.vector2Length(wp.x - point.x, wp.z - point.z) + if dis < rangeMax and dis > rangeMin then + table.insert(inRange, wp.id) + end + end - return inRange + return inRange end function ADGraphManager:createNode(id, x, y, z, out, incoming, flags, colors) - return { - id = id, - x = x, - y = y, - z = z, - out = out, - incoming = incoming, - flags = flags, - colors = colors - } + return { + id = id, + x = x, + y = y, + z = z, + out = out, + incoming = incoming, + flags = flags, + colors = colors + } end function ADGraphManager:prepareWayPoints() - local network = self:getWayPoints() - for id, wp in ipairs(network) do - wp.transitMapping = {} - wp.inverseTransitMapping = {} - if #wp.incoming > 0 then --and #wp.out > 0 - for outIndex, outId in ipairs(wp.out) do - wp.inverseTransitMapping[outId] = {} - end - - for inIndex, inId in ipairs(wp.incoming) do - local inPoint = network[inId] - wp.transitMapping[inId] = {} - for outIndex, outId in ipairs(wp.out) do - local outPoint = network[outId] - local angle = - math.abs( - AutoDrive.angleBetween( - {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, - {x = wp.x - inPoint.x, z = wp.z - inPoint.z} - ) - ) - --print("prep4: " .. outId .. " angle: " .. angle) - - if angle <= 90 then - table.insert(wp.transitMapping[inId], outId) - table.insert(wp.inverseTransitMapping[outId], inId) - else - --Also for reverse routes - but only checked on demand, if angle check fails - local isReverseStart = not table.contains(outPoint.incoming, wp.id) - local isReverseEnd = - table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - table.insert(wp.transitMapping[inId], outId) - table.insert(wp.inverseTransitMapping[outId], inId) - end - end - end - end - end - end - self.preparedWayPoints = true + local network = self:getWayPoints() + for id, wp in ipairs(network) do + wp.transitMapping = {} + wp.inverseTransitMapping = {} + if #wp.incoming > 0 then --and #wp.out > 0 + for outIndex, outId in ipairs(wp.out) do + wp.inverseTransitMapping[outId] = {} + end + + for inIndex, inId in ipairs(wp.incoming) do + local inPoint = network[inId] + wp.transitMapping[inId] = {} + for outIndex, outId in ipairs(wp.out) do + local outPoint = network[outId] + local angle = + math.abs( + AutoDrive.angleBetween( + {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, + {x = wp.x - inPoint.x, z = wp.z - inPoint.z} + ) + ) + + --print("prep4: " .. outId .. " angle: " .. angle) + + if angle <= 90 then + table.insert(wp.transitMapping[inId], outId) + table.insert(wp.inverseTransitMapping[outId], inId) + else + --Also for reverse routes - but only checked on demand, if angle check fails + local isReverseStart = not table.contains(outPoint.incoming, wp.id) + local isReverseEnd = + table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) + if isReverseStart or isReverseEnd then + table.insert(wp.transitMapping[inId], outId) + table.insert(wp.inverseTransitMapping[outId], inId) + end + end + end + end + end + end + self.preparedWayPoints = true end -- this looks simlar to prepareWayPoints, but is separated to not disturb this calculation -- here also additional checks may be implemented function ADGraphManager:getNetworkErrors() - local network = self:getWayPoints() - for _, wp in ipairs(network) do - wp.errorMapping = {} - - if #wp.incoming > 0 then - for _, inId in ipairs(wp.incoming) do - -- for inIndex, inId in ipairs(wp.incoming) do - local inPoint = network[inId] - for _, outId in ipairs(wp.out) do - local error = false - if inId ~= outId then - local outPoint = network[outId] - local angle = - math.abs( - AutoDrive.angleBetween( - {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, - {x = wp.x - inPoint.x, z = wp.z - inPoint.z} - ) - ) - if angle > 90 then - local isReverseStart = not table.contains(outPoint.incoming, wp.id) - local isReverseEnd = - table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if not isReverseStart and not isReverseEnd then - error = true - end - end - end - if inId == outId and #wp.incoming == 1 then - -- only 1 dual connection - error = false - end - wp.errorMapping[outId] = wp.errorMapping[outId] or error - end - end - for _, outId in ipairs(wp.out) do - if wp.errorMapping[outId] == nil then - wp.errorMapping[outId] = true - end - end - end - end + local network = self:getWayPoints() + for id, wp in ipairs(network) do + wp.errorMapping = {} + + if #wp.incoming > 0 then + + for inIndex, inId in ipairs(wp.incoming) do + local inPoint = network[inId] + for outIndex, outId in ipairs(wp.out) do + + if inId ~= outId then + local outPoint = network[outId] + local angle = + math.abs( + AutoDrive.angleBetween( + {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, + {x = wp.x - inPoint.x, z = wp.z - inPoint.z} + ) + ) + if angle > 90 then + wp.errorMapping[outId] = inId + local isReverseStart = not table.contains(outPoint.incoming, wp.id) + local isReverseEnd = + table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) + if isReverseStart or isReverseEnd then + wp.errorMapping[outId] = nil + end + if ADGraphManager:isDualRoad(wp, outPoint) then + wp.errorMapping[outId] = nil + end + if ADGraphManager:isDualRoad(wp, inPoint) then + wp.errorMapping[outId] = nil + end + end + end + + + + + + end + end + + + + + + end + end end function ADGraphManager:checkResetVehicleDestinations(destination) - if destination == nil or destination < 1 then - return - end - -- remove deleted marker in vehicle destinations - for _, vehicle in pairs(g_currentMission.vehicles) do - if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil then - if destination == vehicle.ad.stateModule:getFirstMarkerId() then - local firstMarker = ADGraphManager:getMapMarkerById(1) - if firstMarker ~= nil then - vehicle.ad.stateModule:setFirstMarker(1) - end - end - if destination == vehicle.ad.stateModule:getSecondMarkerId() then - local secondMarker = ADGraphManager:getMapMarkerById(1) - if secondMarker ~= nil then - vehicle.ad.stateModule:setSecondMarker(1) - end - end - end - end + if destination == nil or destination < 1 then + return + end + -- remove deleted marker in vehicle destinations + for _, vehicle in pairs(g_currentMission.vehicles) do + if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil then + if destination == vehicle.ad.stateModule:getFirstMarkerId() then + local firstMarker = ADGraphManager:getMapMarkerById(1) + if firstMarker ~= nil then + vehicle.ad.stateModule:setFirstMarker(1) + end + end + if destination == vehicle.ad.stateModule:getSecondMarkerId() then + local secondMarker = ADGraphManager:getMapMarkerById(1) + if secondMarker ~= nil then + vehicle.ad.stateModule:setSecondMarker(1) + end + end + end + end end -- this function is used to remove the debug markers function ADGraphManager:removeDebugMarkers() - local foundDebugMarker = false - local index = #self:getMapMarkers() - while index >= 1 do - local mapMarker = self:getMapMarkerById(index) - if mapMarker ~= nil and mapMarker.isADDebug == true then - -- remove debug marker from vehicle destinations - ADGraphManager:checkResetVehicleDestinations(mapMarker.markerIndex) - table.remove(self.mapMarkers, mapMarker.markerIndex) - foundDebugMarker = true - end - index = index - 1 - end - if self:getGroupByName(ADGraphManager.debugGroupName) ~= nil then - -- sendEvent should be false as function is initiated on server and all clients via debug setting - self:removeGroup(ADGraphManager.debugGroupName, false) - end - --Readjust stored markerIndex values to point to corrected ID - if foundDebugMarker == true then - if #self:getMapMarkers() > 0 then - for markerID, marker in pairs(self.mapMarkers) do - marker.markerIndex = markerID - end - end - end + local foundDebugMarker = false + local index = #self:getMapMarkers() + while index >= 1 do + local mapMarker = self:getMapMarkerById(index) + if mapMarker ~= nil and mapMarker.isADDebug == true then + -- remove debug marker from vehicle destinations + ADGraphManager:checkResetVehicleDestinations(mapMarker.markerIndex) + table.remove(self.mapMarkers, mapMarker.markerIndex) + foundDebugMarker = true + end + index = index - 1 + end + if self:getGroupByName(ADGraphManager.debugGroupName) ~= nil then + -- sendEvent should be false as function is initiated on server and all clients via debug setting + self:removeGroup(ADGraphManager.debugGroupName, false) + end + --Readjust stored markerIndex values to point to corrected ID + if foundDebugMarker == true then + if #self:getMapMarkers() > 0 then + for markerID, marker in pairs(self.mapMarkers) do + marker.markerIndex = markerID + end + end + end end -- create debug markers for waypoints issues function ADGraphManager:createDebugMarkers(updateMap) - local overallnumberWP = self:getWayPointsCount() - if overallnumberWP < 3 then - return - end - ADGraphManager:getNetworkErrors() - local network = self:getWayPoints() - - local shouldUpdateMap = updateMap - if shouldUpdateMap == nil then - shouldUpdateMap = true - end - - if shouldUpdateMap == true then - self:removeDebugMarkers() - end - - if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_ROADNETWORKINFO) then - -- create markers for open ends - if self:getGroupByName(ADGraphManager.debugGroupName) == nil then - -- sendEvent should be false as function is initiated on server and all clients via debug setting - self:addGroup(ADGraphManager.debugGroupName, false) - end - local count1 = 1 - local count2 = 1 - local count3 = 1 - local count4 = 1 - local count9 = 1 - local mapMarkerCounter = #self:getMapMarkers() + 1 - for i, wp in pairs(network) do - wp.foundError = false - -- mark wayPoint without outgoing connection - if #wp.out == 0 then - if wp ~= nil then - if not ADGraphManager:getMapMarkerByWayPointId(wp.id) then - local debugMapMarkerName = "1_" .. tostring(count1) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count1 = count1 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- mark reverse wayPoint with less angle to be reverse -> wrong connection in network - if not wp.foundError and wp.incoming ~= nil then - for _, wp_in in pairs(wp.incoming) do - if wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - -- if self:isReverseStart(wp,self:getWayPointById(wp_1)) then - local isWrongReverseStart = - self:checkForWrongReverseStart( - self:getWayPointById(wp_in), - wp, - self:getWayPointById(wp_out) - ) - - if isWrongReverseStart then - local debugMapMarkerName = "2_" .. tostring(count2) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count2 = count2 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - end - end - - -- mark wayPoint without incoming connection - if not wp.foundError and wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - local missingIncoming = self:checkForMissingIncoming(wp, self:getWayPointById(wp_out)) - if missingIncoming then - local debugMapMarkerName = "3_" .. tostring(count3) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count3 = count3 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- mark dual wayPoint with death end - if not wp.foundError and wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - local missingIncoming = self:checkForMissingDualConnection(wp) - if missingIncoming then - local debugMapMarkerName = "4_" .. tostring(count4) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count4 = count4 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- possible other errors - if not wp.foundError then - local wrongAngle = self:checkForOtherErrors(wp) - if wrongAngle then - local debugMapMarkerName = "9_" .. tostring(count9) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count9 = count9 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - end - if shouldUpdateMap == true then - AutoDrive:notifyDestinationListeners() - end + local overallnumberWP = self:getWayPointsCount() + if overallnumberWP < 3 then + return + end + ADGraphManager:getNetworkErrors() + local network = self:getWayPoints() + + local shouldUpdateMap = updateMap + if shouldUpdateMap == nil then + shouldUpdateMap = true + end + + if shouldUpdateMap == true then + self:removeDebugMarkers() + end + + if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_ROADNETWORKINFO) then + -- create markers for open ends + if self:getGroupByName(ADGraphManager.debugGroupName) == nil then + -- sendEvent should be false as function is initiated on server and all clients via debug setting + self:addGroup(ADGraphManager.debugGroupName, false) + end + local count1 = 1 + local count2 = 1 + local count3 = 1 + local count4 = 1 + local count9 = 1 + local mapMarkerCounter = #self:getMapMarkers() + 1 + for i, wp in pairs(network) do + wp.foundError = false + -- mark wayPoint without outgoing connection + if #wp.out == 0 then + if wp ~= nil then + if not ADGraphManager:getMapMarkerByWayPointId(wp.id) then + local debugMapMarkerName = "1_" .. tostring(count1) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count1 = count1 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- mark reverse wayPoint with less angle to be reverse -> wrong connection in network + if not wp.foundError and wp.incoming ~= nil then + for _, wp_in in pairs(wp.incoming) do + if wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + -- if self:isReverseStart(wp,self:getWayPointById(wp_1)) then + local isWrongReverseStart = + self:checkForWrongReverseStart( + self:getWayPointById(wp_in), + wp, + self:getWayPointById(wp_out) + ) + + if isWrongReverseStart then + local debugMapMarkerName = "2_" .. tostring(count2) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count2 = count2 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + end + end + + -- mark wayPoint without incoming connection + if not wp.foundError and wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + local missingIncoming = self:checkForMissingIncoming(wp, self:getWayPointById(wp_out)) + if missingIncoming then + local debugMapMarkerName = "3_" .. tostring(count3) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count3 = count3 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- mark dual wayPoint with death end + if not wp.foundError and wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + local missingIncoming = self:checkForMissingDualConnection(wp) + if missingIncoming then + local debugMapMarkerName = "4_" .. tostring(count4) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count4 = count4 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- possible other errors + if not wp.foundError then + local wrongAngle, count1, count2 = self:checkForOtherErrors(wp) + if wrongAngle then + local debugMapMarkerName = "9_" .. tostring(count9) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count9 = count9 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + end + if shouldUpdateMap == true then + AutoDrive:notifyDestinationListeners() + end end function ADGraphManager:checkForWrongReverseStart(wp_ref, wp_current, wp_ahead) - local reverseStart = false + local reverseStart = false - if wp_ref == nil or wp_current == nil or wp_ahead == nil then - return reverseStart - end + if wp_ref == nil or wp_current == nil or wp_ahead == nil then + return reverseStart + end - local isReverseStart = wp_ahead.incoming ~= nil and (not table.contains(wp_ahead.incoming, wp_current.id)) - isReverseStart = - isReverseStart and not (wp_current.incoming ~= nil and (not table.contains(wp_current.incoming, wp_ref.id))) + local isReverseStart = wp_ahead.incoming ~= nil and (not table.contains(wp_ahead.incoming, wp_current.id)) + isReverseStart = + isReverseStart and not (wp_current.incoming ~= nil and (not table.contains(wp_current.incoming, wp_ref.id))) - local angle = - AutoDrive.angleBetween( - {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, - {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} - ) + local angle = + AutoDrive.angleBetween( + {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, + {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} + ) - angle = math.abs(angle) - if angle <= 90 and isReverseStart then - reverseStart = true - end + angle = math.abs(angle) + if angle <= 90 and isReverseStart then + reverseStart = true + end - return reverseStart + return reverseStart end function ADGraphManager:checkForMissingIncoming(wp_current) - local ret = false - - if wp_current == nil then - return ret - end - local reverseFound = false - if wp_current.incoming ~= nil and #wp_current.incoming == 0 then - -- search for a possible reverse connection - for _, wp in pairs(self.wayPoints) do - if wp.out ~= nil and wp_current.id ~= nil then - if table.contains(wp.out, wp_current.id) then - reverseFound = true - break - end - end - end - if not reverseFound then - -- the waypoint has no incoming connection - ret = true - end - end - return ret + local ret = false + + if wp_current == nil then + return ret + end + local reverseFound = false + if wp_current.incoming ~= nil and #wp_current.incoming == 0 then + -- search for a possible reverse connection + for _, wp in pairs(self.wayPoints) do + if wp.out ~= nil and wp_current.id ~= nil then + if table.contains(wp.out, wp_current.id) then + reverseFound = true + break + end + end + end + if not reverseFound then + -- the waypoint has no incoming connection + ret = true + end + end + return ret end function ADGraphManager:checkForMissingDualConnection(wp_current) - local ret = false - - if wp_current == nil then - return ret - end - - if wp_current.incoming ~= nil and wp_current.out ~= nil and #wp_current.incoming == 1 and #wp_current.out == 1 then - if wp_current.incoming[1] ~= nil and wp_current.out[1] ~= nil and wp_current.incoming[1] == wp_current.out[1] then - local mapMarker = ADGraphManager:getMapMarkerByWayPointId(wp_current.id) - -- only dual waypoint without Marker is an error - if mapMarker == nil then - -- dual waypoint - ret = true - end - - -- search for a possible reverse connection - for _, wp in pairs(self.wayPoints) do - if self:isReverseRoad(wp, wp_current) then - -- reverse connection to wayPoint is OK - no death end - ret = false - break - end - end - end - end - return ret + local ret = false + + if wp_current == nil then + return ret + end + + if wp_current.incoming ~= nil and wp_current.out ~= nil and #wp_current.incoming == 1 and #wp_current.out == 1 then + if wp_current.incoming[1] ~= nil and wp_current.out[1] ~= nil and wp_current.incoming[1] == wp_current.out[1] then + local mapMarker = ADGraphManager:getMapMarkerByWayPointId(wp_current.id) + -- only dual waypoint without Marker is an error + if mapMarker == nil then + -- dual waypoint + ret = true + end + + -- search for a possible reverse connection + for _, wp in pairs(self.wayPoints) do + if self:isReverseRoad(wp, wp_current) then + -- reverse connection to wayPoint is OK - no death end + ret = false + break + end + end + end + end + return ret end function ADGraphManager:checkForOtherErrors(wp) - if wp == nil then - return true - end + local ret = false + if wp == nil then + return true + end + -- TODO - for _, outId in ipairs(wp.out) do - if wp.errorMapping[outId] then - return true - end - end - return false + local network = self:getWayPoints() + + for outIndex, outId in ipairs(wp.out) do + ret = ret or (wp.errorMapping[outId] ~= nil) + + + end + return ret end function ADGraphManager:toggleWayPointAsSubPrio(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - if self:getIsPointSubPrio(wayPointId) then - wayPoint.flags = wayPoint.flags - AutoDrive.FLAG_SUBPRIO - else - wayPoint.flags = wayPoint.flags + AutoDrive.FLAG_SUBPRIO - end - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + if self:getIsPointSubPrio(wayPointId) then + wayPoint.flags = wayPoint.flags - AutoDrive.FLAG_SUBPRIO + else + wayPoint.flags = wayPoint.flags + AutoDrive.FLAG_SUBPRIO + end + end - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) - self:markChanges() + self:markChanges() end function ADGraphManager:getIsPointSubPrio(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) + local wayPoint = self:getWayPointById(wayPointId) - return wayPoint ~= nil and bitAND(wayPoint.flags, AutoDrive.FLAG_SUBPRIO) > 0 + return wayPoint ~= nil and bitAND(wayPoint.flags, AutoDrive.FLAG_SUBPRIO) > 0 end function ADGraphManager:setWayPointFlags(wayPointId, flags) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil and flags ~= nil then - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, flags) + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil and flags ~= nil then + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, flags) - self:markChanges() - end + self:markChanges() + end end function ADGraphManager:getBestOutPoints(vehicle, nodeId) - local neighbors = {} + local neighbors = {} - local x, y, z = getWorldTranslation(vehicle.components[1].node) - local toCheck = self.wayPoints[nodeId] - local baseDistance = MathUtil.vector2Length(toCheck.x - x, toCheck.z - z) + local x, y, z = getWorldTranslation(vehicle.components[1].node) + local toCheck = self.wayPoints[nodeId] + local baseDistance = MathUtil.vector2Length(toCheck.x - x, toCheck.z - z) - if toCheck.out ~= nil then - for _, outId in pairs(toCheck.out) do - local out = self.wayPoints[outId] - local _, _, offsetZ = worldToLocal(vehicle.components[1].node, out.x, y, out.z) - if out ~= nil and baseDistance < MathUtil.vector2Length(out.x - x, out.z - z) and offsetZ > 0 then - table.insert(neighbors, out.id) - end - end - end + if toCheck.out ~= nil then + for _, outId in pairs(toCheck.out) do + local out = self.wayPoints[outId] + local _, _, offsetZ = worldToLocal(vehicle.components[1].node, out.x, y, out.z) + if out ~= nil and baseDistance < MathUtil.vector2Length(out.x - x, out.z - z) and offsetZ > 0 then + table.insert(neighbors, out.id) + end + end + end - return neighbors + return neighbors end --[[ TODO: at the moment only supported for directions: -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 -reverse connection not tested !!! +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 +reverse connection not tested !!! ]] function ADGraphManager:getIsWayPointInSection(wayPointId, direction) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint == nil then - return false - end - - local connectedIds = {} - for _, incomingId in pairs(wayPoint.incoming) do - if not table.contains(connectedIds, incomingId) then - table.insert(connectedIds, incomingId) - end - end - for _, outId in pairs(wayPoint.out) do - if not table.contains(connectedIds, outId) then - table.insert(connectedIds, outId) - end - end - if #connectedIds == 2 and not (direction == 4) then - -- single or dual connection - -- check for reverse wayPoint -> if found as connected wayPoint the current is reported as not in section - -- this is used to treat this wayPoint as last in a section - local foundReverse = false - for _, connectedId in pairs(connectedIds) do - connectedWayPoint = self:getWayPointById(connectedId) - foundReverse = foundReverse or ADGraphManager:isReverseRoad(wayPoint, connectedWayPoint) - end - return not foundReverse - elseif #connectedIds == 1 and direction == 4 then - -- single end -> used for reverse section TODO: to be checked - return true - else - return false - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint == nil then + return false + end + + local connectedIds = {} + for _, incomingId in pairs(wayPoint.incoming) do + if not table.contains(connectedIds, incomingId) then + table.insert(connectedIds, incomingId) + end + end + for _, outId in pairs(wayPoint.out) do + if not table.contains(connectedIds, outId) then + table.insert(connectedIds, outId) + end + end + if #connectedIds == 2 and not (direction == 4) then + -- single or dual connection + -- check for reverse wayPoint -> if found as connected wayPoint the current is reported as not in section + -- this is used to treat this wayPoint as last in a section + local foundReverse = false + for _, connectedId in pairs(connectedIds) do + connectedWayPoint = self:getWayPointById(connectedId) + foundReverse = foundReverse or ADGraphManager:isReverseRoad(wayPoint, connectedWayPoint) + end + return not foundReverse + elseif #connectedIds == 1 and direction == 4 then + -- single end -> used for reverse section TODO: to be checked + return true + else + return false + end end --[[ -no junction return 0 -single connection ahead return 1 -single connection backward (not reverse) return 2 -dual connection return 3 -reverse connection return 4 +no junction return 0 +single connection ahead return 1 +single connection backward (not reverse) return 2 +dual connection return 3 +reverse connection return 4 ]] function ADGraphManager:getIsWayPointJunction(startId, targetId) - if startId <= 0 or targetId <= 0 then - return 0 - end - - local wayPointStart = self:getWayPointById(startId) - local wayPointTarget = self:getWayPointById(targetId) - if wayPointTarget == nil or wayPointStart == nil then - return 0 - end - - local startConnectedIds = {} - local startConnectedIdsIncoming = {} - local startConnectedIdsOut = {} - local targetConnectedIds = {} - local targetConnectedIdsIncoming = {} - local targetConnectedIdsOut = {} - - local function addConnections(input, connections) - if connections ~= nil and #input > 0 then - for _, connectedId in pairs(input) do - if not table.contains(connections, connectedId) then - table.insert(connections, connectedId) - end - end - end - end - - addConnections(wayPointStart.incoming, startConnectedIds) - addConnections(wayPointStart.out, startConnectedIds) - addConnections(wayPointStart.incoming, startConnectedIdsIncoming) - addConnections(wayPointStart.out, startConnectedIdsOut) - - addConnections(wayPointTarget.incoming, targetConnectedIds) - addConnections(wayPointTarget.out, targetConnectedIds) - addConnections(wayPointTarget.incoming, targetConnectedIdsIncoming) - addConnections(wayPointTarget.out, targetConnectedIdsOut) - - if - not (table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId)) and - table.contains(startConnectedIdsOut, targetId) and - table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- one way ahead - return 1 - elseif - table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and - not (table.contains(startConnectedIdsOut, targetId) and table.contains(targetConnectedIdsIncoming, startId)) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- one way backward - return 2 - elseif - table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and - table.contains(startConnectedIdsOut, targetId) and - table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- two way - return 3 - elseif - table.contains(startConnectedIdsOut, targetId) and not table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) - then - -- reverse - return 4 - else - return 0 - end + if startId <= 0 or targetId <= 0 then + return 0 + end + + local wayPointStart = self:getWayPointById(startId) + local wayPointTarget = self:getWayPointById(targetId) + if wayPointTarget == nil or wayPointStart == nil then + return 0 + end + + local startConnectedIds = {} + local startConnectedIdsIncoming = {} + local startConnectedIdsOut = {} + local targetConnectedIds = {} + local targetConnectedIdsIncoming = {} + local targetConnectedIdsOut = {} + + local function addConnections(input, connections) + if connections ~= nil and #input > 0 then + for _, connectedId in pairs(input) do + if not table.contains(connections, connectedId) then + table.insert(connections, connectedId) + end + end + end + end + + addConnections(wayPointStart.incoming, startConnectedIds) + addConnections(wayPointStart.out, startConnectedIds) + addConnections(wayPointStart.incoming, startConnectedIdsIncoming) + addConnections(wayPointStart.out, startConnectedIdsOut) + + addConnections(wayPointTarget.incoming, targetConnectedIds) + addConnections(wayPointTarget.out, targetConnectedIds) + addConnections(wayPointTarget.incoming, targetConnectedIdsIncoming) + addConnections(wayPointTarget.out, targetConnectedIdsOut) + + if + not (table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId)) and + table.contains(startConnectedIdsOut, targetId) and + table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- one way ahead + return 1 + elseif + table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and + not (table.contains(startConnectedIdsOut, targetId) and table.contains(targetConnectedIdsIncoming, startId)) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- one way backward + return 2 + elseif + table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and + table.contains(startConnectedIdsOut, targetId) and + table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- two way + return 3 + elseif + table.contains(startConnectedIdsOut, targetId) and not table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) + then + -- reverse + return 4 + else + return 0 + end end --[[ at the moment only supported for directions: -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 ]] function ADGraphManager:getWayPointsInSection(startId, targetId, direction) - local previousId = startId - local nextId = targetId - local sectionWayPoints = {} - - if direction < 1 or direction > 3 then - return sectionWayPoints - end - - if not table.contains(sectionWayPoints, previousId) then - -- add the first wayPoint - table.insert(sectionWayPoints, previousId) - end - local count = 1 - while count < ADGraphManager.MAX_POINTS_IN_SECTION and self:getIsWayPointInSection(nextId, direction) do - count = count + 1 - table.insert(sectionWayPoints, nextId) - local nextwayPoint = self:getWayPointById(nextId) - - if nextwayPoint.incoming[1] ~= nil and nextwayPoint.incoming[1] ~= previousId then - previousId = nextId - nextId = nextwayPoint.incoming[1] - elseif nextwayPoint.incoming[2] ~= nil and nextwayPoint.incoming[2] ~= previousId then - previousId = nextId - nextId = nextwayPoint.incoming[2] - elseif nextwayPoint.out[1] ~= nil and nextwayPoint.out[1] ~= previousId then - previousId = nextId - nextId = nextwayPoint.out[1] - elseif nextwayPoint.out[2] ~= nil and nextwayPoint.out[2] ~= previousId then - previousId = nextId - nextId = nextwayPoint.out[2] - else - break - end - end - if not table.contains(sectionWayPoints, nextId) then - -- add the last wayPoint - table.insert(sectionWayPoints, nextId) - end - return sectionWayPoints + local previousId = startId + local nextId = targetId + local sectionWayPoints = {} + + if direction < 1 or direction > 3 then + return sectionWayPoints + end + + if not table.contains(sectionWayPoints, previousId) then + -- add the first wayPoint + table.insert(sectionWayPoints, previousId) + end + local count = 1 + while count < ADGraphManager.MAX_POINTS_IN_SECTION and self:getIsWayPointInSection(nextId, direction) do + count = count + 1 + table.insert(sectionWayPoints, nextId) + local nextwayPoint = self:getWayPointById(nextId) + + if nextwayPoint.incoming[1] ~= nil and nextwayPoint.incoming[1] ~= previousId then + previousId = nextId + nextId = nextwayPoint.incoming[1] + elseif nextwayPoint.incoming[2] ~= nil and nextwayPoint.incoming[2] ~= previousId then + previousId = nextId + nextId = nextwayPoint.incoming[2] + elseif nextwayPoint.out[1] ~= nil and nextwayPoint.out[1] ~= previousId then + previousId = nextId + nextId = nextwayPoint.out[1] + elseif nextwayPoint.out[2] ~= nil and nextwayPoint.out[2] ~= previousId then + previousId = nextId + nextId = nextwayPoint.out[2] + else + break + end + end + if not table.contains(sectionWayPoints, nextId) then + -- add the last wayPoint + table.insert(sectionWayPoints, nextId) + end + return sectionWayPoints end function ADGraphManager:setConnectionBetweenWayPointsInSection(vehicle, direction) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - for i = 1, #vehicle.ad.sectionWayPoints - 1 do - ADGraphManager:setConnectionBetween( - ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i]), - ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i + 1]), - direction - ) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + for i = 1, #vehicle.ad.sectionWayPoints - 1 do + ADGraphManager:setConnectionBetween( + ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i]), + ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i + 1]), + direction + ) + end + end end function ADGraphManager:setWayPointsFlagsInSection(vehicle, flags) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - for i = 2, #vehicle.ad.sectionWayPoints - 1 do - -- do not set start and end wayPoint as these are the connections to other lines - ADGraphManager:setWayPointFlags(vehicle.ad.sectionWayPoints[i], flags) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + for i = 2, #vehicle.ad.sectionWayPoints - 1 do + -- do not set start and end wayPoint as these are the connections to other lines + ADGraphManager:setWayPointFlags(vehicle.ad.sectionWayPoints[i], flags) + end + end end function ADGraphManager:deleteWayPointsInSection(vehicle) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - local pointsToDelete = {} - for i = 2, #vehicle.ad.sectionWayPoints - 1 do - table.insert(pointsToDelete, vehicle.ad.sectionWayPoints[i]) - end - - -- delete last wayPoint if not connected to a junction - local lastWayPointID = vehicle.ad.sectionWayPoints[#vehicle.ad.sectionWayPoints] - local lastWayPoint = self:getWayPointById(lastWayPointID) - local connectedIds = {} - for _, incomingId in pairs(lastWayPoint.incoming) do - if not table.contains(connectedIds, incomingId) then - table.insert(connectedIds, incomingId) - end - end - for _, outId in pairs(lastWayPoint.out) do - if not table.contains(connectedIds, outId) then - table.insert(connectedIds, outId) - end - end - if #connectedIds == 1 then - table.insert(pointsToDelete, lastWayPointID) - end - - -- sort the wayPoints to delete in descant order to ensure correct linkage deletion - local sort_func = function(a, b) - return a > b - end - table.sort(pointsToDelete, sort_func) - for i = 1, #pointsToDelete do - ADGraphManager:removeWayPoint(pointsToDelete[i]) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + local pointsToDelete = {} + for i = 2, #vehicle.ad.sectionWayPoints - 1 do + table.insert(pointsToDelete, vehicle.ad.sectionWayPoints[i]) + end + + -- delete last wayPoint if not connected to a junction + local lastWayPointID = vehicle.ad.sectionWayPoints[#vehicle.ad.sectionWayPoints] + local lastWayPoint = self:getWayPointById(lastWayPointID) + local connectedIds = {} + for _, incomingId in pairs(lastWayPoint.incoming) do + if not table.contains(connectedIds, incomingId) then + table.insert(connectedIds, incomingId) + end + end + for _, outId in pairs(lastWayPoint.out) do + if not table.contains(connectedIds, outId) then + table.insert(connectedIds, outId) + end + end + if #connectedIds == 1 then + table.insert(pointsToDelete, lastWayPointID) + end + + -- sort the wayPoints to delete in descant order to ensure correct linkage deletion + local sort_func = function(a, b) + return a > b + end + table.sort(pointsToDelete, sort_func) + for i = 1, #pointsToDelete do + ADGraphManager:removeWayPoint(pointsToDelete[i]) + end + end end function ADGraphManager:deleteColorSelectionWayPoints() - -- delete the color selection wayPoints in descant order to ensure correct linkage deletion - local actualID = self:getWayPointsCount() - local count = 1 - while count < (10000000) and actualID > 1 do - count = count + 1 - local wp = ADGraphManager:getWayPointById(actualID) - if wp.colors ~= nil then - ADGraphManager:removeWayPoint(actualID, false) - actualID = self:getWayPointsCount() -- removed a wayPoint so check from highest again - else - actualID = actualID - 1 - end - end + -- delete the color selection wayPoints in descant order to ensure correct linkage deletion + local actualID = self:getWayPointsCount() + local count = 1 + while count < (10000000) and actualID > 1 do + count = count + 1 + local wp = ADGraphManager:getWayPointById(actualID) + if wp.colors ~= nil then + ADGraphManager:removeWayPoint(actualID, false) + actualID = self:getWayPointsCount() -- removed a wayPoint so check from highest again + else + actualID = actualID - 1 + end + end end function ADGraphManager:removeNodesWithFlag(flagToRemove) - local network = self:getWayPoints() - local pointsToDelete = {} - for i, wp in pairs(network) do - if bitAND(wp.flags, flagToRemove) > 0 then - table.insert(pointsToDelete, wp.id) - end - end - - -- sort the wayPoints to delete in descant order to ensure correct linkage deletion - local sort_func = function(a, b) - return a > b - end - table.sort(pointsToDelete, sort_func) - - for i = 1, #pointsToDelete do - ADGraphManager:removeWayPoint(pointsToDelete[i]) - end -end - -function ADGraphManager:createSplineConnection(start, waypoints, target, dualConnection, sendEvent) - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network - CreateSplineConnectionEvent.sendEvent(start, waypoints, target, dualConnection) - else - local lastId = start - local lastHeight = ADGraphManager:getWayPointById(start).y - local subPrio = self:getIsPointSubPrio(start) or self:getIsPointSubPrio(target) - - for _, wp in pairs(waypoints) do - if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc - wp.y = lastHeight - end - self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) - local createdId = self:getWayPointsCount() - if subPrio then - ADGraphManager:toggleWayPointAsSubPrio(createdId) - end - self:toggleConnectionBetween( - ADGraphManager:getWayPointById(lastId), - ADGraphManager:getWayPointById(createdId), - false, - dualConnection, - false - ) - lastId = createdId - lastHeight = wp.y - end - - local wp = self:getWayPointById(lastId) - self:toggleConnectionBetween(wp, self:getWayPointById(target), false, dualConnection, false) - end + local network = self:getWayPoints() + local pointsToDelete = {} + for i, wp in pairs(network) do + if bitAND(wp.flags, flagToRemove) > 0 then + table.insert(pointsToDelete, wp.id) + end + end + + -- sort the wayPoints to delete in descant order to ensure correct linkage deletion + local sort_func = function(a, b) + return a > b + end + table.sort(pointsToDelete, sort_func) + + for i = 1, #pointsToDelete do + ADGraphManager:removeWayPoint(pointsToDelete[i]) + end +end + +function ADGraphManager:createSplineConnection(start, waypoints, target, sendEvent) + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network + CreateSplineConnectionEvent.sendEvent(start, waypoints, target) + else + local lastId = start + local lastHeight = ADGraphManager:getWayPointById(start).y + + + for wpId, wp in pairs(waypoints) do + if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc + wp.y = lastHeight + end + self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) + local createdId = self:getWayPointsCount() + + + + self:toggleConnectionBetween( + ADGraphManager:getWayPointById(lastId), + ADGraphManager:getWayPointById(createdId), + false, + + false + ) + lastId = createdId + lastHeight = wp.y + end + + local wp = self:getWayPointById(lastId) + self:toggleConnectionBetween(wp, self:getWayPointById(target), false, false) + end end function ADGraphManager:getNextTargetAlphabetically(markerId) - local sortedMarkers = self:getSortedMarkers() - for i, markerIdIter in ipairs(sortedMarkers) do - if markerIdIter == markerId then - if i < #sortedMarkers then - return sortedMarkers[i + 1] - else - return sortedMarkers[1] - end - end - end - return markerId + local sortedMarkers = self:getSortedMarkers() + for i, markerIdIter in ipairs(sortedMarkers) do + if markerIdIter == markerId then + if i < #sortedMarkers then + return sortedMarkers[i + 1] + else + return sortedMarkers[1] + end + end + end + return markerId end function ADGraphManager:getPreviousTargetAlphabetically(markerId) - local sortedMarkers = self:getSortedMarkers() - for i, markerIdIter in ipairs(sortedMarkers) do - if markerIdIter == markerId then - if i > 1 then - return sortedMarkers[i - 1] - else - return sortedMarkers[#sortedMarkers] - end - end - end - return markerId + local sortedMarkers = self:getSortedMarkers() + for i, markerIdIter in ipairs(sortedMarkers) do + if markerIdIter == markerId then + if i > 1 then + return sortedMarkers[i - 1] + else + return sortedMarkers[#sortedMarkers] + end + end + end + return markerId end function ADGraphManager:getSortedMarkers() - local useFolders = AutoDrive.getSetting("useFolders") - - local groups = {} - local groupsToSortedIndex = {} - if useFolders then - groups, groupsToSortedIndex = self:sortGroups(groups) - end - - if #groups == 0 then - groups[1] = {} - groupsToSortedIndex[1] = "All" - end - - for markerID, marker in pairs(self:getMapMarkers()) do - if useFolders then - table.insert(groups[groupsToSortedIndex[marker.group]], {displayName = marker.name, returnValue = markerID}) - else - table.insert(groups[1], {displayName = marker.name, returnValue = markerID}) - end - end - - local sort_func = function(a, b) - a = tostring(a.displayName):lower() - b = tostring(b.displayName):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end - - for groupId, _ in pairs(groups) do - table.sort(groups[groupId], sort_func) - end - - local allInOneTable = {} - for groupId, entries in pairs(groups) do - for _, marker in pairs(entries) do - allInOneTable[#allInOneTable + 1] = marker.returnValue - end - end - - return allInOneTable + local useFolders = AutoDrive.getSetting("useFolders") + + local groups = {} + local groupsToSortedIndex = {} + if useFolders then + groups, groupsToSortedIndex = self:sortGroups(groups) + end + + if #groups == 0 then + groups[1] = {} + groupsToSortedIndex[1] = "All" + end + + for markerID, marker in pairs(self:getMapMarkers()) do + if useFolders then + table.insert(groups[groupsToSortedIndex[marker.group]], {displayName = marker.name, returnValue = markerID}) + else + table.insert(groups[1], {displayName = marker.name, returnValue = markerID}) + end + end + + local sort_func = function(a, b) + a = tostring(a.displayName):lower() + b = tostring(b.displayName):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end + + for groupId, _ in pairs(groups) do + table.sort(groups[groupId], sort_func) + end + + local allInOneTable = {} + for groupId, entries in pairs(groups) do + for _, marker in pairs(entries) do + allInOneTable[#allInOneTable + 1] = marker.returnValue + end + end + + return allInOneTable end function ADGraphManager:sortGroups(groups) - groups = {} - groups[1] = {} - - local sort_func = function(a, b) - a = tostring(a):lower() - b = tostring(b):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end - - local groupTable = {} - for groupName, groupID in pairs(ADGraphManager:getGroups()) do - table.insert(groupTable, groupName) - end - - table.sort(groupTable, sort_func) - local groupsToSortedIndex = {} - groupsToSortedIndex[1] = "All" - for i = 1, #groupTable do - groups[#groups + 1] = {} - groupsToSortedIndex[groupTable[i]] = i + 1 - end - - return groups, groupsToSortedIndex -end \ No newline at end of file + groups = {} + groups[1] = {} + + local sort_func = function(a, b) + a = tostring(a):lower() + b = tostring(b):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end + + local groupTable = {} + for groupName, groupID in pairs(ADGraphManager:getGroups()) do + table.insert(groupTable, groupName) + end + + table.sort(groupTable, sort_func) + local groupsToSortedIndex = {} + groupsToSortedIndex[1] = "All" + for i = 1, #groupTable do + groups[#groups + 1] = {} + groupsToSortedIndex[groupTable[i]] = i + 1 + end + + return groups, groupsToSortedIndex +end From ff6dcabeab2ddcc36df5488cd4e57af95f2c55d7 Mon Sep 17 00:00:00 2001 From: Admilson Afonso Date: Tue, 12 Dec 2023 09:15:18 -0300 Subject: [PATCH 31/51] Update translation_br.xml --- translations/translation_br.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index a2d8b58b..3fbf9372 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -56,7 +56,7 @@ - + From 43997d6a1c4374807331bb8896b65615cb2f3982 Mon Sep 17 00:00:00 2001 From: MeRida2104 <96819380+MeRida2104@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:56:05 +0100 Subject: [PATCH 32/51] Update translation_de.xml Tippfehler behoben --- translations/translation_de.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index e0e8fb46..129cd895 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -212,7 +212,7 @@ - + From b5a3ed649ea143bdcafff4552019d7138c1e19a7 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:25:22 +0100 Subject: [PATCH 33/51] added support for CP #1034, rework combine types - Premium Expansion combines handling improvements - support Premium Expansion combines operated by CP >= V7.4.0.1 - internal: changed type queries for combine types to evaluation during loading / attaching --- scripts/Events/HudInputEvent.lua | 2 +- scripts/Gui/Settings.lua | 2 +- scripts/Hud/HudHarvesterInfo.lua | 4 +- scripts/Manager/HarvestManager.lua | 16 +- scripts/Manager/InputManager.lua | 10 +- scripts/Modes/CombineUnloaderMode.lua | 41 +++-- scripts/Modules/PathFinderModule.lua | 30 ++-- scripts/Modules/StateModule.lua | 4 +- scripts/Specialization.lua | 22 +-- scripts/Tasks/CatchCombinePipeTask.lua | 20 ++- scripts/Tasks/EmptyHarvesterTask.lua | 2 +- scripts/Tasks/FollowCombineTask.lua | 203 ++++++++++++++-------- scripts/Tasks/HandleHarvesterTurnTask.lua | 2 +- scripts/Utils/AutoDriveUtilFuncs.lua | 2 +- scripts/Utils/CombineUtil.lua | 61 ++++--- scripts/Utils/UtilFuncs.lua | 2 +- 16 files changed, 257 insertions(+), 166 deletions(-) diff --git a/scripts/Events/HudInputEvent.lua b/scripts/Events/HudInputEvent.lua index 6e93ec1d..a90ff872 100644 --- a/scripts/Events/HudInputEvent.lua +++ b/scripts/Events/HudInputEvent.lua @@ -40,7 +40,7 @@ function AutoDriveHudInputEventEvent:run(connection) if self.eventType == self.TYPE_FIRST_MARKER then local currentFirstMarker = self.vehicle.ad.stateModule:getFirstMarkerId() if currentFirstMarker > 0 and currentFirstMarker ~= self.value then - if not (self.vehicle.spec_combine or AutoDrive.getIsBufferCombine(self.vehicle) or self.vehicle.ad.isCombine ~= nil) then + if not self.vehicle.ad.hasCombine then -- not stop / change CP for harvesters AutoDrive:StopCP(self.vehicle) end diff --git a/scripts/Gui/Settings.lua b/scripts/Gui/Settings.lua index f9116e15..baaaea69 100644 --- a/scripts/Gui/Settings.lua +++ b/scripts/Gui/Settings.lua @@ -87,7 +87,7 @@ function ADSettings:setupPages() end local combineEnabled = function() - if vehicleEnabled() and g_currentMission.controlledVehicle.ad.isCombine then + if vehicleEnabled() and (g_currentMission.controlledVehicle.ad and g_currentMission.controlledVehicle.ad.hasCombine) then return true end return false diff --git a/scripts/Hud/HudHarvesterInfo.lua b/scripts/Hud/HudHarvesterInfo.lua index b78bdf8e..776cba6d 100644 --- a/scripts/Hud/HudHarvesterInfo.lua +++ b/scripts/Hud/HudHarvesterInfo.lua @@ -10,14 +10,14 @@ function HudHarvesterInfo:new(posX, posY, width, height) end function HudHarvesterInfo:onDraw(vehicle, uiScale) - if (vehicle.ad.isCombine and (vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DELIVERTO or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DRIVETO)) or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_UNLOAD then + if ((vehicle.ad.hasCombine) and (vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DELIVERTO or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DRIVETO)) or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_UNLOAD then if AutoDrive.pullDownListExpanded == 0 or AutoDrive.Hud.targetPullDownList.direction == ADPullDownList.EXPANDED_UP then local text = "" setTextColor(1, 1, 1, 1) local harvesterPairingOk = vehicle.ad.stateModule:getHarvesterPairingOk() - if vehicle.ad.isCombine then + if vehicle.ad.isRegisterdHarvester then if not harvesterPairingOk then setTextColor(1, 0, 0, 1) text = g_i18n:getText("gui_ad_noUnloaderAvailable") diff --git a/scripts/Manager/HarvestManager.lua b/scripts/Manager/HarvestManager.lua index c9dc9fa3..f5249885 100644 --- a/scripts/Manager/HarvestManager.lua +++ b/scripts/Manager/HarvestManager.lua @@ -16,7 +16,8 @@ function ADHarvestManager:registerHarvester(harvester) if not table.contains(self.idleHarvesters, harvester) and not table.contains(self.harvesters, harvester) then AutoDrive.debugPrint(harvester, AutoDrive.DC_COMBINEINFO, "ADHarvestManager:registerHarvester - inserted") if harvester ~= nil and harvester.ad ~= nil then - harvester.ad.isCombine = true + local rootVehicle = harvester:getRootVehicle() + rootVehicle.ad.isRegisterdHarvester = true end if g_server ~= nil then table.insert(self.idleHarvesters, harvester) @@ -27,7 +28,8 @@ end function ADHarvestManager:unregisterHarvester(harvester) AutoDrive.debugPrint(harvester, AutoDrive.DC_COMBINEINFO, "ADHarvestManager:unregisterHarvester") if harvester ~= nil and harvester.ad ~= nil then - harvester.ad.isCombine = false + local rootVehicle = harvester:getRootVehicle() + rootVehicle.ad.isRegisterdHarvester = false end if g_server ~= nil then if table.contains(self.idleHarvesters, harvester) then @@ -97,7 +99,7 @@ function ADHarvestManager:update(dt) if vehicle.isTrailedHarvester then vehicle = vehicle.trailingVehicle end - if (vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive()) or (AutoDrive:getIsEntered(vehicle) and AutoDrive.isPipeOut(vehicle)) then + if (vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive()) or (AutoDrive:getIsEntered(vehicle:getRootVehicle()) and AutoDrive.isPipeOut(vehicle)) then table.insert(self.harvesters, idleHarvester) table.removeValue(self.idleHarvesters, idleHarvester) end @@ -107,7 +109,7 @@ function ADHarvestManager:update(dt) if vehicle.isTrailedHarvester then vehicle = vehicle.trailingVehicle end - if not ((vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive()) or (AutoDrive:getIsEntered(vehicle) and AutoDrive.isPipeOut(vehicle))) then + if not ((vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive()) or (AutoDrive:getIsEntered(vehicle:getRootVehicle()) and AutoDrive.isPipeOut(vehicle))) then table.insert(self.idleHarvesters, harvester) table.removeValue(self.harvesters, harvester) @@ -229,7 +231,7 @@ function ADHarvestManager.doesHarvesterNeedUnloading(harvester, ignorePipe) end function ADHarvestManager.isHarvesterActive(harvester) - if AutoDrive.getIsBufferCombine(harvester) then + if harvester.ad.isChopper then return true else local fillLevel, fillCapacity, _, fillFreeCapacity = AutoDrive.getObjectFillLevels(harvester) @@ -240,7 +242,7 @@ function ADHarvestManager.isHarvesterActive(harvester) -- Only chase the rear on low fill levels of the combine. This should prevent getting into unneccessarily tight spots for the final approach to the pipe. -- Also for small fields, there is often no purpose in chasing so far behind the combine as it will already start a turn soon local allowedToChase = true - if not AutoDrive.isSugarcaneHarvester(harvester) then + if not harvester.ad.isSugarcaneHarvester then local chasingRear = false local pipeSide = AutoDrive.getPipeSide(harvester) if pipeSide == AutoDrive.CHASEPOS_LEFT then @@ -258,7 +260,7 @@ function ADHarvestManager.isHarvesterActive(harvester) end end - local manuallyControlled = AutoDrive:getIsEntered(harvester) and (not (harvester.getIsAIActive ~= nil and harvester:getIsAIActive())) + local manuallyControlled = AutoDrive:getIsEntered(harvester:getRootVehicle()) and (not (harvester.getIsAIActive ~= nil and harvester:getIsAIActive())) if manuallyControlled then return AutoDrive.isPipeOut(harvester) diff --git a/scripts/Manager/InputManager.lua b/scripts/Manager/InputManager.lua index 3ce92a78..4bd730e3 100644 --- a/scripts/Manager/InputManager.lua +++ b/scripts/Manager/InputManager.lua @@ -388,7 +388,7 @@ function ADInputManager:input_nextTarget(vehicle) local currentTarget = vehicle.ad.stateModule:getFirstMarkerId() local nextTarget = ADGraphManager:getNextTargetAlphabetically(currentTarget) vehicle.ad.stateModule:setFirstMarker(nextTarget) - if not (vehicle.spec_combine or AutoDrive.getIsBufferCombine(vehicle) or vehicle.ad.isCombine ~= nil) then + if not vehicle.ad.hasCombine then -- not stop / change CP for harvesters AutoDrive:StopCP(vehicle) end @@ -400,7 +400,7 @@ function ADInputManager:input_previousTarget(vehicle) local currentTarget = vehicle.ad.stateModule:getFirstMarkerId() local previousTarget = ADGraphManager:getPreviousTargetAlphabetically(currentTarget) vehicle.ad.stateModule:setFirstMarker(previousTarget) - if not (vehicle.spec_combine or AutoDrive.getIsBufferCombine(vehicle) or vehicle.ad.isCombine ~= nil) then + if not vehicle.ad.hasCombine then -- not stop / change CP for harvesters AutoDrive:StopCP(vehicle) end @@ -436,9 +436,9 @@ function ADInputManager:input_continue(vehicle) end function ADInputManager:input_callDriver(vehicle) - if vehicle.spec_pipe ~= nil and vehicle.spec_enterable ~= nil then + if vehicle.spec_combine ~= nil and vehicle.spec_pipe ~= nil and vehicle.spec_enterable ~= nil then ADHarvestManager:assignUnloaderToHarvester(vehicle) - elseif vehicle.ad.isCombine and vehicle.ad.attachableCombine ~= nil then + elseif vehicle.ad.attachableCombine ~= nil then ADHarvestManager:assignUnloaderToHarvester(vehicle.ad.attachableCombine) end end @@ -464,7 +464,7 @@ function ADInputManager:input_swapTargets(vehicle) local currentFirstMarker = vehicle.ad.stateModule:getFirstMarkerId() vehicle.ad.stateModule:setFirstMarker(vehicle.ad.stateModule:getSecondMarkerId()) vehicle.ad.stateModule:setSecondMarker(currentFirstMarker) - if not (vehicle.spec_combine or AutoDrive.getIsBufferCombine(vehicle) or vehicle.ad.isCombine ~= nil) then + if not vehicle.ad.hasCombine then -- not stop / change CP for harvesters AutoDrive:StopCP(vehicle) end diff --git a/scripts/Modes/CombineUnloaderMode.lua b/scripts/Modes/CombineUnloaderMode.lua index f1f3490a..d123a1a2 100644 --- a/scripts/Modes/CombineUnloaderMode.lua +++ b/scripts/Modes/CombineUnloaderMode.lua @@ -141,7 +141,7 @@ function CombineUnloaderMode:handleFinishedTask() if self.activeTask ~= nil then self.vehicle.ad.taskModule:addTask(self.activeTask) end - AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:handleFinishedTask self.state %s", tostring(self:getStateName())) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:handleFinishedTask self.state %s", tostring(self:getStateName())) end function CombineUnloaderMode:stop() @@ -291,7 +291,7 @@ function CombineUnloaderMode:setToWaitForCall(keepCombine) if self.combine ~= nil and self.combine.ad ~= nil and (keepCombine == nil or keepCombine ~= true) then self.combine = nil end - AutoDrive.debugPrint(vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:setToWaitForCall end self.state %s self.combine %s", tostring(self:getStateName()), tostring(self.combine)) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:setToWaitForCall end self.state %s self.combine %s", tostring(self:getStateName()), tostring(self.combine)) end function CombineUnloaderMode:assignToHarvester(harvester) @@ -304,10 +304,9 @@ function CombineUnloaderMode:assignToHarvester(harvester) local cfillLevel, cfillCapacity, _, cleftCapacity = AutoDrive.getObjectFillLevels(self.combine) local cFillRatio = cfillCapacity > 0 and cfillLevel / cfillCapacity or 0 - local cpIsCalling = AutoDrive:getIsCPWaitingForUnload(harvester) - if (self.combine.spec_combine == nil or not AutoDrive.getIsBufferCombine(self.combine)) + if (self.combine.ad.isHarvester) and (self.combine.ad.noMovementTimer.elapsedTime > 500 or cleftCapacity < 0.1 or cpIsCalling or cFillRatio > 0.945) then -- default unloading - no movement @@ -369,7 +368,7 @@ function CombineUnloaderMode:getTaskAfterUnload(filledToUnload) end else -- Should we park in the field? - if AutoDrive.getIsBufferCombine(self.combine) or (AutoDrive.getSetting("parkInField", self.vehicle) or (self.lastTask ~= nil and self.lastTask.stayOnField)) then + if self.combine.ad.isChopper or (AutoDrive.getSetting("parkInField", self.vehicle) or (self.lastTask ~= nil and self.lastTask.stayOnField)) then -- If we are in fruit, we should clear it if AutoDrive.isVehicleOrTrailerInCrop(self.vehicle, true) then AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getTaskAfterUnload ClearCropTask...") @@ -500,10 +499,10 @@ function CombineUnloaderMode:getSideChaseOffsetX() -- So, choose the max between the two to avoid a collison local sideChaseTermX = math.max(sideChaseTermPipeIn, sideChaseTermPipeOut) - if AutoDrive.isSugarcaneHarvester(self.combine) then + if self.combine.ad.isSugarcaneHarvester then -- check for SugarcaneHarvester has to be first, as it also is IsBufferCombine! sideChaseTermX = AutoDrive.getPipeLength(self.combine) - elseif AutoDrive.getIsAutoAimingChopper(self.combine) then + elseif self.combine.ad.isAutoAimingChopper then sideChaseTermX = sideChaseTermPipeIn + CombineUnloaderMode.STATIC_X_OFFSET_FROM_HEADER elseif (self.combine.ad ~= nil and self.combine.ad.storedPipeLength ~= nil) or AutoDrive.isPipeOut(self.combine) then -- If the pipe is extended, though, target it regardless @@ -527,10 +526,10 @@ function CombineUnloaderMode:getSideChaseOffsetX_new() -- So, choose the max between the two to avoid a collison local sideChaseTermX = 0 - if AutoDrive.isSugarcaneHarvester(self.combine) then + if self.combine.ad.isSugarcaneHarvester then -- check for SugarcaneHarvester has to be first, as it also is IsBufferCombine! sideChaseTermX = AutoDrive.getPipeLength(self.combine) - elseif AutoDrive.getIsAutoAimingChopper(self.combine) then + elseif self.combine.ad.isAutoAimingChopper then sideChaseTermX = sideChaseTermPipeIn + CombineUnloaderMode.STATIC_X_OFFSET_FROM_HEADER elseif (self.combine.ad ~= nil and self.combine.ad.storedPipeLength ~= nil) or AutoDrive.isPipeOut(self.combine) then -- If the pipe is extended, though, target it regardless @@ -648,7 +647,7 @@ end function CombineUnloaderMode:getRearChaseOffsetX(leftBlocked, rightBlocked) local rearChaseOffset = (self.combine.size.width / 2 + self.vehicle.size.width / 2) + 1 - if AutoDrive.getIsAutoAimingChopper(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then + if self.combine.ad.isAutoAimingChopper and not self.combine.ad.isSugarcaneHarvester then return 0 elseif rightBlocked and leftBlocked then return 0 @@ -662,14 +661,14 @@ end function CombineUnloaderMode:getRearChaseOffsetZ() local followDistance = AutoDrive.getSetting("followDistance", self.vehicle) local rearChaseOffset = -followDistance - (self.combine.size.length / 2) - if AutoDrive.getIsAutoAimingChopper(self.combine) and not AutoDrive.isSugarcaneHarvester(self.combine) then + if self.combine.ad.isAutoAimingChopper and not self.combine.ad.isSugarcaneHarvester then rearChaseOffset = -followDistance - (self.combine.size.length / 2) else -- math.sqrt(2) ensures the trailer could straighten if it was turned 90 degrees, and it makes this point further -- back than the pathfinder (straightening) target in PathFinderModule:startPathPlanningToPipe -- math.sqrt(2) gives the hypotenuse of an isosceles right trangle with side length equal to the length -- of the trailer - if AutoDrive.isSugarcaneHarvester(self.combine) then + if self.combine.ad.isSugarcaneHarvester then rearChaseOffset = -self.combine.size.length / 2 - self.tractorTrainLength * math.sqrt(2) else if self.combine.lastSpeedReal > 0.002 and self.combine.ad.sensors.frontSensorFruit:pollInfo() then @@ -713,13 +712,12 @@ function CombineUnloaderMode:getPipeChasePosition(planningPhase) self.targetFillUnit, self.targetFillNode = AutoDrive.getNextFreeDischargeableUnit(self.vehicle) local sideChaseTermX = self:getSideChaseOffsetX() - local sideChaseTermZ = self:getSideChaseOffsetZ(AutoDrive.dynamicChaseDistance or not AutoDrive.getIsBufferCombine(self.combine)) + local sideChaseTermZ = self:getSideChaseOffsetZ(AutoDrive.dynamicChaseDistance or self.combine.ad.isHarvester) local rearChaseTermZ = self:getRearChaseOffsetZ() - if AutoDrive.getIsBufferCombine(self.combine) then - -- chopper + if self.combine.ad.isChopper then + -- any chopper --AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getPipeChasePosition=IsBufferCombine") - local leftChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, sideChaseTermX + self:getPipeSlopeCorrection(), sideChaseTermZ - 2) local rightChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, -(sideChaseTermX + self:getPipeSlopeCorrection()), sideChaseTermZ - 2) local rearChasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, 0, rearChaseTermZ) @@ -736,20 +734,21 @@ function CombineUnloaderMode:getPipeChasePosition(planningPhase) sideIndex = AutoDrive.CHASEPOS_RIGHT end -- following order is important! - if AutoDrive:getIsCPActive(self.combine) and AutoDrive.combineIsTurning(self.combine) then - chaseNode = rearChasePos - sideIndex = AutoDrive.CHASEPOS_REAR - elseif not AutoDrive.getIsAutoAimingChopper(self.combine) then + if self.combine.ad.isFixedPipeChopper then -- chopper with fixed unloading local sideOffsetZ = self:getDynamicSideChaseOffsetZ_fromDischargeNode(planningPhase) chaseNode = self:getPipeChaseWayPoint(0, sideOffsetZ) + sideIndex = self.pipeSide + elseif AutoDrive:getIsCPActive(self.combine) and AutoDrive.combineIsTurning(self.combine) then + chaseNode = rearChasePos + sideIndex = AutoDrive.CHASEPOS_REAR elseif (not leftBlocked) and ((self:isUnloaderOnCorrectSide(AutoDrive.CHASEPOS_LEFT) and angleToLeftChaseSide < angleToRearChaseSide) or planningPhase) then chaseNode = leftChasePos sideIndex = AutoDrive.CHASEPOS_LEFT elseif (not rightBlocked) and (self:isUnloaderOnCorrectSide(AutoDrive.CHASEPOS_RIGHT) or planningPhase) then chaseNode = rightChasePos sideIndex = AutoDrive.CHASEPOS_RIGHT - elseif not AutoDrive.isSugarcaneHarvester(self.combine) then + elseif not self.combine.ad.isSugarcaneHarvester then chaseNode = rearChasePos sideIndex = AutoDrive.CHASEPOS_REAR end diff --git a/scripts/Modules/PathFinderModule.lua b/scripts/Modules/PathFinderModule.lua index 56bab793..232fd50e 100644 --- a/scripts/Modules/PathFinderModule.lua +++ b/scripts/Modules/PathFinderModule.lua @@ -217,14 +217,26 @@ function PathFinderModule:startPathPlanningToPipe(combine, chasing) --else -- self:startPathPlanningTo(target, combineVector) --end - if combine.spec_combine ~= nil and AutoDrive.getIsBufferCombine(combine) then - local pathFinderTarget = {x = pipeChasePos.x - (self.PATHFINDER_TARGET_DISTANCE) * rx, y = worldY, z = pipeChasePos.z - (self.PATHFINDER_TARGET_DISTANCE) * rz} - --local appendTarget = {x = pipeChasePos.x - (lengthOffset/2) * rx, y = worldY, z = pipeChasePos.z - (lengthOffset/2) * rz} + if combine.ad.isAutoAimingChopper then + -- local pathFinderTarget = {x = pipeChasePos.x - (self.PATHFINDER_TARGET_DISTANCE) * rx, y = worldY, z = pipeChasePos.z - (self.PATHFINDER_TARGET_DISTANCE) * rz} + local pathFinderTarget = {x = pipeChasePos.x, y = worldY, z = pipeChasePos.z} + self:startPathPlanningTo(pathFinderTarget, combineVector) + + elseif combine.ad.isFixedPipeChopper then + local pathFinderTarget = {x = pipeChasePos.x, y = worldY, z = pipeChasePos.z} + -- only append target points / try to straighten the driver/trailer combination if we are driving up to the pipe not the rear end + if pipeChaseSide ~= AutoDrive.CHASEPOS_REAR then + pathFinderTarget = {x = pipeChasePos.x - (combine.size.length) * rx, y = worldY, z = pipeChasePos.z - (combine.size.length) * rz} + end + local appendedNode = {x = pipeChasePos.x - (combine.size.length / 2 * rx), y = worldY, z = pipeChasePos.z - (combine.size.length / 2 * rz)} self:startPathPlanningTo(pathFinderTarget, combineVector) - --table.insert(self.appendWayPoints, appendTarget) + if pipeChaseSide ~= AutoDrive.CHASEPOS_REAR then + table.insert(self.appendWayPoints, appendedNode) + end else + -- combine.ad.isHarvester local pathFinderTarget = {x = pipeChasePos.x, y = worldY, z = pipeChasePos.z} -- only append target points / try to straighten the driver/trailer combination if we are driving up to the pipe not the rear end if pipeChaseSide ~= AutoDrive.CHASEPOS_REAR then @@ -240,10 +252,10 @@ function PathFinderModule:startPathPlanningToPipe(combine, chasing) end end - if combine.spec_combine ~= nil then + if combine.spec_combine ~= nil and combine.ad.isHarvester then if combine.spec_combine.fillUnitIndex ~= nil and combine.spec_combine.fillUnitIndex ~= 0 then local fillType = g_fruitTypeManager:getFruitTypeIndexByFillTypeIndex(combine:getFillUnitFillType(combine.spec_combine.fillUnitIndex)) - if fillType ~= nil and (not AutoDrive.getIsBufferCombine(combine)) then + if fillType ~= nil then self.fruitToCheck = fillType local fruitType = g_fruitTypeManager:getFruitTypeByIndex(fillType) @@ -1176,11 +1188,9 @@ function PathFinderModule:drawDebugForPF() pointTarget.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointTarget.x, 1, pointTarget.z) + 3 pointTargetUp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointTargetUp.x, 1, pointTargetUp.z) + 20 AutoDriveDM:addLineTask(pointTarget.x, pointTarget.y, pointTarget.z, pointTargetUp.x, pointTargetUp.y, pointTargetUp.z, 1, 0, 0, 1) - local pointStart = self:gridLocationToWorldLocation(self.startCell) - local pointStartUp = self:gridLocationToWorldLocation(self.startCell) + local pointStart = {x = self.startX, z = self.startZ} pointStart.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointStart.x, 1, pointStart.z) + 3 - pointStartUp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, pointStartUp.x, 1, pointStartUp.z) + 6 - AutoDriveDM:addLineTask(pointTarget.x, pointTarget.y, pointTarget.z, pointTargetUp.x, pointTargetUp.y, pointTargetUp.z, 1, 0, 0, 1) + AutoDriveDM:addLineTask(pointStart.x, pointStart.y, pointStart.z, pointStart.x, pointStart.y + 20, pointStart.z, 1, 0, 1, 0) local color_red = 0.1 local color_green = 0.1 diff --git a/scripts/Modules/StateModule.lua b/scripts/Modules/StateModule.lua index 2e6e66d9..e160ca75 100644 --- a/scripts/Modules/StateModule.lua +++ b/scripts/Modules/StateModule.lua @@ -350,8 +350,8 @@ function ADStateModule:update(dt) end if g_server ~= nil then - if self.vehicle.ad.isCombine or (self:getMode() == AutoDrive.MODE_UNLOAD and self.active) then - if self.vehicle.ad.isCombine then + if self.vehicle.ad.isRegisterdHarvester or (self:getMode() == AutoDrive.MODE_UNLOAD and self.active) then + if self.vehicle.ad.isRegisterdHarvester then if ADHarvestManager:hasHarvesterPotentialUnloaders(self.vehicle) ~= self.harversterPairingOk then self:setHarvesterPairingOk(not self.harversterPairingOk) end diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua index 00aaeb24..2fafd3f7 100644 --- a/scripts/Specialization.lua +++ b/scripts/Specialization.lua @@ -263,6 +263,7 @@ function AutoDrive:onPostLoad(savegame) end end + -- sugarcane harvester need special consideration if self.spec_pipe ~= nil and self.spec_enterable ~= nil and self.spec_combine ~= nil then if self.typeName == "combineCutterFruitPreparer" then local _, vehicleFillCapacity, _, _ = AutoDrive.getObjectFillLevels(self) @@ -270,7 +271,9 @@ function AutoDrive:onPostLoad(savegame) end end - if self.spec_pipe ~= nil and self.spec_enterable ~= nil and self.spec_combine ~= nil then + -- harvester types + local isValidHarvester = AutoDrive.setCombineType(self) + if isValidHarvester then ADHarvestManager:registerHarvester(self) end @@ -675,17 +678,17 @@ end function AutoDrive:onPostAttachImplement(attachable, inputJointDescIndex, jointDescIndex) if attachable["spec_FS19_addon_strawHarvest.strawHarvestPelletizer"] ~= nil then attachable.isPremos = true - -- attachable.getIsBufferCombine = function() - -- return false - -- end end if (attachable.spec_pipe ~= nil and attachable.spec_combine ~= nil) or attachable.isPremos then + attachable.ad = self.ad -- takeover i.e. sensors from trailing vehicle attachable.isTrailedHarvester = true attachable.trailingVehicle = self - ADHarvestManager:registerHarvester(attachable) - self.ad.isCombine = true + -- harvester types self.ad.attachableCombine = attachable - attachable.ad = self.ad + local isValidHarvester = AutoDrive.setCombineType(attachable) + if isValidHarvester then + ADHarvestManager:registerHarvester(attachable) + end end AutoDrive.setValidSupportedFillType(self) @@ -699,14 +702,11 @@ function AutoDrive:onPreDetachImplement(implement) local attachable = implement.object if attachable.isTrailedHarvester and attachable.trailingVehicle == self then attachable.ad = nil - self.ad.isCombine = false self.ad.attachableCombine = nil ADHarvestManager:unregisterHarvester(attachable) attachable.isTrailedHarvester = false attachable.trailingVehicle = nil - if attachable.isPremos then - -- attachable.getIsBufferCombine = nil - end + self.ad.isRegisterdHarvester = nil end if self.ad ~= nil then self.ad.frontToolWidth = nil diff --git a/scripts/Tasks/CatchCombinePipeTask.lua b/scripts/Tasks/CatchCombinePipeTask.lua index 6b3a0689..e25d9fae 100644 --- a/scripts/Tasks/CatchCombinePipeTask.lua +++ b/scripts/Tasks/CatchCombinePipeTask.lua @@ -37,7 +37,8 @@ function CatchCombinePipeTask:setUp() local angleToCombineHeading = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getAngleToCombineHeading() local angleToCombine = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getAngleToCombine() - if angleToCombineHeading < 35 and angleToCombine < 90 and AutoDrive.getDistanceBetween(self.vehicle, self.combine) < 60 then + if angleToCombineHeading < 35 and angleToCombine < 90 and AutoDrive.getDistanceBetween(self.vehicle, self.combine) < 60 + and not self.combine.ad.isFixedPipeChopper then self:finished() end self.trailers, _ = AutoDrive.getAllUnits(self.vehicle) @@ -73,6 +74,7 @@ function CatchCombinePipeTask:update(dt) self.vehicle.ad.specialDrivingModule:update(dt) end elseif self.state == CatchCombinePipeTask.STATE_DELAY_PATHPLANNING then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_DELAY_PATHPLANNING") if self.waitForCheckTimer:timer(true, 1000, dt) then if self:startNewPathFinding() then self.vehicle.ad.pathFinderModule:addDelayTimer(6000) @@ -97,6 +99,7 @@ function CatchCombinePipeTask:update(dt) -- or AutoDrive.getDistanceBetween(self.vehicle, self.combine) < self.MIN_COMBINE_DISTANCE then -- got stuck or to close to combine -> reverse + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_DRIVING stuck") self.stuckTimer:timer(false) local x, y, z = getWorldTranslation(self.vehicle.components[1].node) self.reverseStartLocation = {x = x, y = y, z = z} @@ -112,10 +115,12 @@ function CatchCombinePipeTask:update(dt) if self.vehicle.ad.drivePathModule:isTargetReached() then -- check if we have actually reached the target or not -- accept current location if we are in a good position to start chasing: distance and angle are important here + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_DRIVING TargetReached") local angleToCombine = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getAngleToCombineHeading() local isCorrectSide = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide() if angleToCombine < 35 and AutoDrive.getDistanceBetween(self.vehicle, self.combine) < 80 and isCorrectSide then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_DRIVING -> STATE_FINISHED") self.state = CatchCombinePipeTask.STATE_FINISHED return else @@ -137,6 +142,7 @@ function CatchCombinePipeTask:update(dt) self.vehicle.ad.specialDrivingModule:driveReverse(dt, 15, 1, self.vehicle.ad.trailerModule:canBeHandledInReverse()) end elseif self.state == CatchCombinePipeTask.STATE_WAIT_BEFORE_FINISH then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_WAIT_BEFORE_FINISH") self.waitTimer:timer(true, self.MAX_REVERSE_TIME, dt) if self.waitTimer:done() then self.waitTimer:timer(false) @@ -147,6 +153,7 @@ function CatchCombinePipeTask:update(dt) self.vehicle.ad.specialDrivingModule:update(dt) end elseif self.state == CatchCombinePipeTask.STATE_FINISHED then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:update - STATE_FINISHED") self:finished() return end @@ -161,6 +168,7 @@ function CatchCombinePipeTask:finished(propagate) end function CatchCombinePipeTask:startNewPathFinding() + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:startNewPathFinding()") local pipeChasePos, pipeChaseSide = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getPipeChasePosition(true) local x, _, z = getWorldTranslation(self.combine.components[1].node) local targetFieldId = g_farmlandManager:getFarmlandIdAtWorldPosition(pipeChasePos.x, pipeChasePos.z) @@ -181,7 +189,7 @@ function CatchCombinePipeTask:startNewPathFinding() self.newPathFindingCounter = self.newPathFindingCounter + 1 -- used to prevent deadlock - if AutoDrive.getIsBufferCombine(self.combine) or (pipeChaseSide ~= AutoDrive.CHASEPOS_REAR or (targetFieldId == combineFieldId and cFillRatio <= 0.85)) then + if self.combine.ad.isChopper or (pipeChaseSide ~= AutoDrive.CHASEPOS_REAR or (targetFieldId == combineFieldId and cFillRatio <= 0.85)) then -- if self.combine:getIsBufferCombine() or (pipeChaseSide ~= AutoDrive.CHASEPOS_REAR and targetFieldId == combineFieldId and cFillRatio <= 0.85) then -- is chopper or chase not rear and harvester on correct field and filled < 85% - i.e. combine pipe not in fruit AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CatchCombinePipeTask:startNewPathFinding() - chase pos looks good - calculate path to it...") @@ -197,6 +205,14 @@ function CatchCombinePipeTask:startNewPathFinding() return false end +function CatchCombinePipeTask:getExcludedVehiclesForCollisionCheck() + local excludedVehicles = {} + if self.state == CatchCombinePipeTask.STATE_DRIVING then + table.insert(excludedVehicles, self.combine:getRootVehicle()) + end + return excludedVehicles +end + function CatchCombinePipeTask:getI18nInfo() local text = "$l10n_AD_task_catch_up_with_combine;" if self.state == CatchCombinePipeTask.STATE_PATHPLANNING then diff --git a/scripts/Tasks/EmptyHarvesterTask.lua b/scripts/Tasks/EmptyHarvesterTask.lua index cb0089c7..9dcd7531 100644 --- a/scripts/Tasks/EmptyHarvesterTask.lua +++ b/scripts/Tasks/EmptyHarvesterTask.lua @@ -183,7 +183,7 @@ end function EmptyHarvesterTask:getExcludedVehiclesForCollisionCheck() local excludedVehicles = {} if self.state == EmptyHarvesterTask.STATE_DRIVING then - table.insert(excludedVehicles, self.combine) + table.insert(excludedVehicles, self.combine:getRootVehicle()) end return excludedVehicles end diff --git a/scripts/Tasks/FollowCombineTask.lua b/scripts/Tasks/FollowCombineTask.lua index 09fa22ab..9c4f5bce 100644 --- a/scripts/Tasks/FollowCombineTask.lua +++ b/scripts/Tasks/FollowCombineTask.lua @@ -29,6 +29,8 @@ function FollowCombineTask:new(vehicle, combine) o.angleWrongTimer = AutoDriveTON:new() o.waitForTurnTimer = AutoDriveTON:new() o.stuckTimer = AutoDriveTON:new() + o.dischargeTimer = AutoDriveTON:new() + o.fillingTimer = AutoDriveTON:new() o.lastChaseSide = -10 o.waitForPassByTimer = AutoDriveTON:new() o.chaseTimer = AutoDriveTON:new() @@ -44,7 +46,7 @@ function FollowCombineTask:new(vehicle, combine) end function FollowCombineTask:setUp() - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "Setting up FollowCombineTask") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask setUp") self.lastChaseSide = self.chaseSide self.trailers, _ = AutoDrive.getAllUnits(self.vehicle) self.activeUnloading = AutoDrive.getSetting("activeUnloading", self.combine) @@ -63,20 +65,21 @@ function FollowCombineTask:update(dt) self.chaseTimer:timer(true, 4000, dt) self.stuckTimer:timer(self.vehicle.lastSpeedReal <= 0.0002, self.MAX_STUCK_TIME, dt) - if AutoDrive.getIsBufferCombine(self.combine) then + if self.combine.ad.isChopper then if self.filled and self.chaseSide ~= nil and self.chaseSide ~= AutoDrive.CHASEPOS_REAR then --skip reversing - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "I am filled and driving on the side - skip reversing and finish now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - filled chopper") self.state = FollowCombineTask.STATE_FINISHED -- finish immediate return elseif self.filledToUnload and self.chaseSide ~= nil and self.chaseSide == AutoDrive.CHASEPOS_REAR then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - filledToUnload chopper") local x, y, z = getWorldTranslation(self.vehicle.components[1].node) self.reverseStartLocation = {x = x, y = y, z = z} self.state = FollowCombineTask.STATE_REVERSING -- reverse to get room from harvester return end - elseif self.filled or ( not AutoDrive.getIsBufferCombine(self.combine) and self.combineFillPercent <= 0.1 and (not self.activeUnloading)) then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "I am filled - reversing now") + elseif self.filled or (self.combine.ad.isHarvester and self.combineFillPercent <= 0.1 and (not self.activeUnloading)) then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - filled harvester") self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH -- unload after some time to let harvester drive away return end @@ -88,10 +91,11 @@ function FollowCombineTask:update(dt) FollowCombineTask.debugMsg(self.vehicle, "FollowCombineTask:update STATE_CHASING stuckTimer:done -> STATE_REVERSING") end self.stuckTimer:timer(false) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "got stuck - reversing now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - stuck -> stuckTimer:%s angleWrongTimer:%s" + , tostring(self.stuckTimer:done()), tostring(self.angleWrongTimer.elapsedTime > 15000)) local x, y, z = getWorldTranslation(self.vehicle.components[1].node) self.reverseStartLocation = {x = x, y = y, z = z} - if AutoDrive.getIsBufferCombine(self.combine) then + if self.combine.ad.isChopper then self.state = FollowCombineTask.STATE_REVERSING_FROM_CHOPPER else self.state = FollowCombineTask.STATE_REVERSING -- reverse to get room from harvester @@ -100,15 +104,16 @@ function FollowCombineTask:update(dt) end if not self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "I am not on the correct side - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - not UnloaderOnCorrectSide -> finished") self:finished() return end - if (not AutoDrive.getIsBufferCombine(self.combine)) and self.combineFillPercent > 90 + if self.combine.ad.isHarvester and self.combineFillPercent > 90 and AutoDrive.getDistanceBetween(self.vehicle, self.combine) < self.MIN_COMBINE_DISTANCE -- if to close -> reverse then -- Stop chasing and wait for a normal unload call while standing + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - to close to harvester -> reverse") local x, y, z = getWorldTranslation(self.vehicle.components[1].node) self.reverseStartLocation = {x = x, y = y, z = z} self.state = FollowCombineTask.STATE_REVERSING -- reverse to get room from harvester @@ -117,7 +122,7 @@ function FollowCombineTask:update(dt) if (not self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide(self.chaseSide)) and (not AutoDrive.combineIsTurning(self.combine)) then if self.lastChaseSide ~= CombineUnloaderMode.CHASEPOS_REAR then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "switching chase side from side to elsewhere - let's wait for passby next") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - switching chase side from side to elsewhere - let's wait for passby next") self.state = FollowCombineTask.STATE_WAIT_FOR_PASS_BY end end @@ -125,7 +130,7 @@ function FollowCombineTask:update(dt) if AutoDrive.combineIsTurning(self.combine) then -- harvester turns --print("Waiting for turn now - 1- t:" .. tostring(AutoDrive.combineIsTurning(self.combine)) .. " anglewrongtimer: " .. tostring(self.angleWrongTimer.elapsedTime > 10000)) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "Detected combine turning: " .. tostring(AutoDrive.combineIsTurning(self.combine)) .. " - waiting for turn to be finished next") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_CHASING - combineIsTurning") self.state = FollowCombineTask.STATE_WAIT_FOR_TURN return elseif ((self.combine.lastSpeedReal * self.combine.movingDirection) <= -0.00005) then @@ -134,49 +139,74 @@ function FollowCombineTask:update(dt) self:followChasePoint(dt) end elseif self.state == FollowCombineTask.STATE_WAIT_FOR_TURN then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_WAIT_FOR_TURN") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN") self.waitForTurnTimer:timer(true, self.MAX_TURN_TIME, dt) if self.waitForTurnTimer:done() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "combine turn took to long - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combine turn took to long - set finished now") self.waitForTurnTimer:timer(false) self.state = FollowCombineTask.STATE_FINISHED return end if AutoDrive.combineIsTurning(self.combine) then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update combineIsTurning -> ") - if not AutoDrive.getIsBufferCombine(self.combine) and (self.distanceToCombine < ((self.vehicle.size.length + self.combine.size.length) / 2 + 10)) then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combineIsTurning") + if self.combine.ad.isHarvester and (self.distanceToCombine < ((self.vehicle.size.length + self.combine.size.length) / 2 + 10)) then -- harvester -- if combine drive reverse to turn -> reverse to keep distance self:reverse(dt) - elseif AutoDrive.getIsBufferCombine(self.combine) and AutoDrive:getIsCPActive(self.combine) then + elseif self.combine.ad.isChopper and AutoDrive:getIsCPActive(self.combine) then -- CP chopper turn - -- local isdrivingReverse = ((self.combine.lastSpeedReal * self.combine.movingDirection) <= -0.00005) - local isdrivingReverse = ((self.combine.lastSpeedReal * self.combine.movingDirection) <= -0.00051) - local combineIsDriving = (self.combine.lastSpeedReal > 0.001) - self.stuckTimer:timer(self.vehicle.lastSpeedReal <= 0.0002, 15000, dt) - - if isdrivingReverse then - self:reverse(dt) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update -> self:reverse") - elseif self.stuckTimer:done() or (not combineIsDriving and (self:getAngleToCobine() > 45)) then - -- if stuck with harvester - try reverse - self.stuckTimer:timer(false) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update stuck / getAngleToCobine() > 45 -> STATE_REVERSING_FROM_CHOPPER combineIsDriving %s", tostring(combineIsDriving)) - local x, y, z = getWorldTranslation(self.vehicle.components[1].node) - self.reverseStartLocation = {x = x, y = y, z = z} - self.state = FollowCombineTask.STATE_REVERSING_FROM_CHOPPER -- reverse to get room from harvester - return - elseif combineIsDriving then - self.vehicle.ad.specialDrivingModule:stopVehicle() - self.vehicle.ad.specialDrivingModule:update(dt) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update combineIsDriving -> stopVehicle") + if self.combine.ad.isAutoAimingChopper then + local isdrivingReverse = ((self.combine.lastSpeedReal * self.combine.movingDirection) <= -0.00051) + local combineIsDriving = (self.combine.lastSpeedReal > 0.001) + self.stuckTimer:timer(self.vehicle.lastSpeedReal <= 0.0002, 15000, dt) + if isdrivingReverse then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN -> self:reverse") + self:reverse(dt) + elseif self.stuckTimer:done() or (not combineIsDriving and (self:getAngleToCobine() > 45)) then + -- if stuck with harvester - try reverse + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - stuck / getAngleToCobine() > 45 -> STATE_REVERSING_FROM_CHOPPER combineIsDriving %s", tostring(combineIsDriving)) + self.stuckTimer:timer(false) + local x, y, z = getWorldTranslation(self.vehicle.components[1].node) + self.reverseStartLocation = {x = x, y = y, z = z} + self.state = FollowCombineTask.STATE_REVERSING_FROM_CHOPPER -- reverse to get room from harvester + return + elseif combineIsDriving then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combineIsDriving -> stopVehicle") + self.vehicle.ad.specialDrivingModule:stopVehicle() + self.vehicle.ad.specialDrivingModule:update(dt) + else + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN -> followChasePoint self.chaseSide %s", tostring(self.chaseSide)) + self:followChasePoint(dt) + end else - self:followChasePoint(dt) - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update -> followChasePoint self.chaseSide %s", tostring(self.chaseSide)) + -- isFixedPipeChopper + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - noMovementTimer %d", self.combine.ad.noMovementTimer.elapsedTime) + local dischargeState = self.combine:getDischargeState() + self.fillingTimer:timer(not self.combine.spec_combine.isFilling, 100, dt) + if self.fillingTimer:done() and self.combine.ad.noMovementTimer.elapsedTime < 5000 then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - fillingTimer:done") + -- harvested to end of row + AutoDrive:holdCPCombine(self.combine) + self.vehicle.ad.specialDrivingModule:stopVehicle() + self.vehicle.ad.specialDrivingModule:update(dt) + else + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN -> followChasePoint no AutoAimingChopper") + self:followChasePoint(dt) + end + self.dischargeTimer:timer(dischargeState ~= Dischargeable.DISCHARGE_STATE_OBJECT , 500, dt) + if self.dischargeTimer:done() and self.fillingTimer:done() and self.combine.ad.noMovementTimer.elapsedTime < 5000 then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - dischargeTimer:done") + self.fillingTimer:timer(false) + self.dischargeTimer:timer(false) + local x, y, z = getWorldTranslation(self.vehicle.components[1].node) + self.reverseStartLocation = {x = x, y = y, z = z} + self.state = FollowCombineTask.STATE_REVERSING_FROM_CHOPPER -- reverse to get room from harvester + return + end end else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update -> stopVehicle") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN -> stopVehicle") -- stop while combine is turning self.vehicle.ad.specialDrivingModule:stopVehicle() self.vehicle.ad.specialDrivingModule:update(dt) @@ -189,34 +219,34 @@ function FollowCombineTask:update(dt) ( self.combine.ad.sensors.frontSensorFruit:pollInfo() and ( - AutoDrive.getIsBufferCombine(self.combine) -- chopper + self.combine.ad.isChopper -- chopper or self.combine.ad.driveForwardTimer.elapsedTime > 8000 -- Harvester moves ) ) or self.waitForTurnTimer.elapsedTime > 15000 -- turn longer than 15 sec ) then if (self.angleToCombineHeading + self.angleToCombine) < 180 and self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide(self.chaseSide) then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "combine turn finished - Heading looks good - start chasing again") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combine turn finished - Heading looks good - start chasing again") self.waitForTurnTimer:timer(false) self.chaseTimer:timer(false) self.state = FollowCombineTask.STATE_CHASING return - elseif self.angleToCombineHeading > 150 and self.angleToCombineHeading < 210 and self.distanceToCombine < 80 and AutoDrive.experimentalFeatures.UTurn == true and not AutoDrive.getIsBufferCombine(self.combine) then + elseif self.angleToCombineHeading > 150 and self.angleToCombineHeading < 210 and self.distanceToCombine < 80 and AutoDrive.experimentalFeatures.UTurn == true and self.combine.ad.isHarvester then -- Instead of directly trying a long way around to get behind the harvester, let's wait for him to pass us by and then U-turn - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "combine turn finished - Heading inverted - wait for passby, then U-turn") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combine turn finished - Heading inverted - wait for passby, then U-turn") self.state = FollowCombineTask.STATE_WAIT_FOR_COMBINE_TO_PASS_BY self.waitForTurnTimer:timer(false) self.chaseTimer:timer(false) self.waitForPassByTimer:timer(false) else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "combine turn finished - Heading looks bad - stop to be able to start pathfinder") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_TURN - combine turn finished - Heading looks bad - stop to be able to start pathfinder") self.stayOnField = true self.state = FollowCombineTask.STATE_FINISHED return end end elseif self.state == FollowCombineTask.STATE_WAIT_FOR_PASS_BY then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_WAIT_FOR_PASS_BY") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_PASS_BY") self.waitForPassByTimer:timer(true, 2200, dt) self.vehicle.ad.specialDrivingModule:stopVehicle() self.vehicle.ad.specialDrivingModule:update(dt) @@ -224,24 +254,24 @@ function FollowCombineTask:update(dt) self.waitForPassByTimer:timer(false) self.chaseTimer:timer(false) if (self.angleToCombineHeading + self.angleToCombine) < 180 and self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "passby timer elapsed - heading looks good - chasing again") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_PASS_BY - passby timer elapsed - heading looks good - chasing again") self.state = FollowCombineTask.STATE_CHASING return else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "passby timer elapsed - heading looks bad - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_PASS_BY - passby timer elapsed - heading looks bad - set finished now") self.stayOnField = true self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH return end end elseif self.state == FollowCombineTask.STATE_REVERSING then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_REVERSING") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING") local x, y, z = getWorldTranslation(self.vehicle.components[1].node) local distanceToReverseStart = MathUtil.vector2Length(x - self.reverseStartLocation.x, z - self.reverseStartLocation.z) self.reverseTimer:timer(true, self.MAX_REVERSE_TIME, dt) local doneReversing = distanceToReverseStart > self.MAX_REVERSE_DISTANCE or (not self.startedChasing) if doneReversing or self.reverseTimer:done() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "done reversing - set finished") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING - done reversing - set finished") self.reverseTimer:timer(false) self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH return @@ -249,25 +279,41 @@ function FollowCombineTask:update(dt) self:reverse(dt) end elseif self.state == FollowCombineTask.STATE_REVERSING_FROM_CHOPPER then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_REVERSING_FROM_CHOPPER") - local x, y, z = getWorldTranslation(self.vehicle.components[1].node) - local distanceToReverseStart = MathUtil.vector2Length(x - self.reverseStartLocation.x, z - self.reverseStartLocation.z) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING_FROM_CHOPPER") + local cx, _, cz = getWorldTranslation(self.combine.components[1].node) + local vx, _, vz = getWorldTranslation(self.vehicle.components[1].node) + local distanceToReverseStart = MathUtil.vector2Length(vx - self.reverseStartLocation.x, vz - self.reverseStartLocation.z) + local distanceToCombine = MathUtil.vector2Length(cx - vx, cz - vz) self.reverseTimer:timer(true, self.MAX_REVERSE_TIME, dt) - local doneReversing = distanceToReverseStart > self.MAX_REVERSE_DISTANCE + local doneReversing = distanceToReverseStart > self.MAX_REVERSE_DISTANCE or distanceToCombine > self.MAX_REVERSE_DISTANCE if doneReversing or self.reverseTimer:done() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "done reversing - set finished") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING_FROM_CHOPPER - done reversing - set finished") self.reverseTimer:timer(false) - self.state = FollowCombineTask.STATE_FINISHED + if self.combine.ad.isFixedPipeChopper and AutoDrive:getIsCPActive(self.combine) then + -- wait for CP to finish a turn maneuver before invoke pathfinder + self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH + else + self.state = FollowCombineTask.STATE_FINISHED + end return else - if self:getAngleToCobine() > 30 then + if self.combine.ad.isFixedPipeChopper then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING_FROM_CHOPPER - not AutoAimingChopper -> holdCPCombine") AutoDrive:holdCPCombine(self.combine) + else + if self:getAngleToCobine() > 30 then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_REVERSING_FROM_CHOPPER - AngleToCobine > 30 -> holdCPCombine") + AutoDrive:holdCPCombine(self.combine) + end end self:reverse(dt) end elseif self.state == FollowCombineTask.STATE_WAIT_BEFORE_FINISH then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_WAIT_BEFORE_FINISH") - self.waitTimer:timer(true, self.WAIT_BEFORE_FINISH_TIME, dt) + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_BEFORE_FINISH") + -- wait for CP to finish a turn maneuver before invoke pathfinder + -- TODO: check if following is useful! + local combineIsDriving = self.combine.ad.isFixedPipeChopper and AutoDrive:getIsCPActive(self.combine) and (self.combine.lastSpeedReal > 0.001) + self.waitTimer:timer(not combineIsDriving, self.WAIT_BEFORE_FINISH_TIME, dt) if self.waitTimer:done() then self.waitTimer:timer(false) self.state = FollowCombineTask.STATE_FINISHED @@ -277,12 +323,12 @@ function FollowCombineTask:update(dt) self.vehicle.ad.specialDrivingModule:update(dt) end elseif self.state == FollowCombineTask.STATE_WAIT_FOR_COMBINE_TO_PASS_BY then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_WAIT_FOR_COMBINE_TO_PASS_BY") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_COMBINE_TO_PASS_BY") self.waitForPassByTimer:timer(true, 15000, dt) self.vehicle.ad.specialDrivingModule:stopVehicle() self.vehicle.ad.specialDrivingModule:update(dt) if self.waitForPassByTimer:done() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "passby timer elapsed - heading looks bad - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_COMBINE_TO_PASS_BY - passby timer elapsed - heading looks bad - set finished now") self.stayOnField = true self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH return @@ -290,7 +336,7 @@ function FollowCombineTask:update(dt) local cx, cy, cz = getWorldTranslation(self.combine.components[1].node) local _, _, offsetZ = worldToLocal(self.vehicle.components[1].node, cx, cy, cz) if offsetZ <= -10 then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "combine passed us. Calculate U-turn now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_WAIT_FOR_COMBINE_TO_PASS_BY - combine passed us. Calculate U-turn now") self.state = FollowCombineTask.STATE_GENERATE_UTURN_PATH local cx, cy, cz = getWorldTranslation(self.combine.components[1].node) local offsetX, _, _ = worldToLocal(self.vehicle.components[1].node, cx, cy, cz) @@ -299,30 +345,30 @@ function FollowCombineTask:update(dt) end end elseif self.state == FollowCombineTask.STATE_GENERATE_UTURN_PATH then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_GENERATE_UTURN_PATH") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_GENERATE_UTURN_PATH") if self.vehicle.ad.uTurn ~= nil and self.vehicle.ad.uTurn.inProgress then self.vehicle:generateUTurn(true) elseif self.vehicle.ad.uTurn ~= nil and not self.vehicle.ad.uTurn.inProgress then if self.vehicle.ad.uTurn.colliFound or self.vehicle.ad.uTurn.points == nil or #self.vehicle.ad.uTurn.points < 5 then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "U-Turn generation failed due to collision - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_GENERATE_UTURN_PATH - U-Turn generation failed due to collision - set finished now") self.stayOnField = true self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "U-Turn generation finished - passing points to drivePathModule now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_GENERATE_UTURN_PATH - U-Turn generation finished - passing points to drivePathModule now") self.vehicle.ad.drivePathModule:setWayPoints(self.vehicle.ad.uTurn.points) self.state = FollowCombineTask.STATE_DRIVE_UTURN_PATH end end elseif self.state == FollowCombineTask.STATE_DRIVE_UTURN_PATH then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_DRIVE_UTURN_PATH") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_DRIVE_UTURN_PATH") if self.vehicle.ad.drivePathModule:isTargetReached() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "U-Turn finished") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_DRIVE_UTURN_PATH - U-Turn finished") if (self.angleToCombineHeading + self.angleToCombine) < 180 and self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:isUnloaderOnCorrectSide() then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "passby timer elapsed - heading looks good - chasing again") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_DRIVE_UTURN_PATH - passby timer elapsed - heading looks good - chasing again") self.state = FollowCombineTask.STATE_CHASING return else - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "passby timer elapsed - heading looks bad - set finished now") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_DRIVE_UTURN_PATH - passby timer elapsed - heading looks bad - set finished now") self.stayOnField = true self.state = FollowCombineTask.STATE_WAIT_BEFORE_FINISH return @@ -331,7 +377,7 @@ function FollowCombineTask:update(dt) self.vehicle.ad.drivePathModule:update(dt) end elseif self.state == FollowCombineTask.STATE_FINISHED then - AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update FollowCombineTask.STATE_FINISHED") + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:update STATE_FINISHED") self:finished() return end @@ -387,6 +433,7 @@ end function FollowCombineTask:followChasePoint(dt) if self:shouldWaitForChasePos(dt) then + AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "FollowCombineTask:followChasePoint getAngleToChasePos %.0f -> stopVehicle", self:getAngleToChasePos()) self.vehicle.ad.specialDrivingModule:stopVehicle() self.vehicle.ad.specialDrivingModule:update(dt) else @@ -423,6 +470,18 @@ function FollowCombineTask:isCaughtCurrentChaseSide() return caught end + +function FollowCombineTask:getAngleToCombineHeading() + if self.vehicle == nil or self.combine == nil then + return math.huge + end + + local combineRx, _, combineRz = localDirectionToWorld(self.combine:getAIDirectionNode(), 0, 0, 1) + local rx, _, rz = localDirectionToWorld(self.vehicle.components[1].node, 0, 0, 1) + + return math.abs(AutoDrive.angleBetween({x = rx, z = rz}, {x = combineRx, z = combineRz})) +end + function FollowCombineTask:getAngleToChasePos() local worldX, _, worldZ = getWorldTranslation(self.vehicle.components[1].node) local rx, _, rz = localDirectionToWorld(self.vehicle.components[1].node, 0, 0, 1) @@ -453,8 +512,8 @@ end function FollowCombineTask:getExcludedVehiclesForCollisionCheck() local excludedVehicles = {} - if self.state == FollowCombineTask.STATE_CHASING then - table.insert(excludedVehicles, self.combine) + if self.state == FollowCombineTask.STATE_CHASING or self.state == FollowCombineTask.STATE_WAIT_FOR_TURN then + table.insert(excludedVehicles, self.combine:getRootVehicle()) end return excludedVehicles end @@ -474,7 +533,7 @@ function FollowCombineTask:getI18nInfo() elseif self.chaseSide == AutoDrive.CHASEPOS_RIGHT then text = text .. " - " .. "$l10n_AD_task_chase_side_right;" end - elseif self.state == FollowCombineTask.STATE_WAIT_FOR_TURN then + elseif self.state == FollowCombineTask.STATE_REVERSING_FROM_CHOPPER then text = text .. " - " .. "$l10n_AD_task_wait_for_combine_turn;" elseif self.state == FollowCombineTask.STATE_REVERSING then text = text .. " - " .. "$l10n_AD_task_reversing_from_combine;" diff --git a/scripts/Tasks/HandleHarvesterTurnTask.lua b/scripts/Tasks/HandleHarvesterTurnTask.lua index a96926e7..dab933e5 100644 --- a/scripts/Tasks/HandleHarvesterTurnTask.lua +++ b/scripts/Tasks/HandleHarvesterTurnTask.lua @@ -313,7 +313,7 @@ function HandleHarvesterTurnTask:generateReverseToCombineSection() local nodes = {} local sideChaseTermX = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getSideChaseOffsetX() - local sideChaseTermZ = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getSideChaseOffsetZ(AutoDrive.dynamicChaseDistance or not AutoDrive.getIsBufferCombine(self.combine)) + local sideChaseTermZ = self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getSideChaseOffsetZ(AutoDrive.dynamicChaseDistance or not self.combine.ad.isChopper) if not AutoDrive.combineIsTurning(self.combine) then local chasePos = AutoDrive.createWayPointRelativeToVehicle(self.combine, -(sideChaseTermX + self.vehicle.ad.modes[AutoDrive.MODE_UNLOAD]:getPipeSlopeCorrection()), sideChaseTermZ - 2) diff --git a/scripts/Utils/AutoDriveUtilFuncs.lua b/scripts/Utils/AutoDriveUtilFuncs.lua index 1bfb47cb..3d6134f2 100644 --- a/scripts/Utils/AutoDriveUtilFuncs.lua +++ b/scripts/Utils/AutoDriveUtilFuncs.lua @@ -160,7 +160,7 @@ function AutoDrive.combineIsTurning(combine) local fieldLengthInFront = AutoDrive.getLengthOfFieldInFront(combine, true, 50, 5) local fieldLengthBehind = math.abs(AutoDrive.getLengthOfFieldInFront(combine, false, 50, -5)) - if (fieldLengthInFront <= 20 or fieldLengthBehind <= 20) and combine.ad.noMovementTimer.elapsedTime < 5000 and not AutoDrive.getIsBufferCombine(combine) then + if (fieldLengthInFront <= 20 or fieldLengthBehind <= 20) and combine.ad.noMovementTimer.elapsedTime < 5000 and not combine.ad.isChopper then combineIsTurning = true end diff --git a/scripts/Utils/CombineUtil.lua b/scripts/Utils/CombineUtil.lua index 52ba2b77..30fbe521 100644 --- a/scripts/Utils/CombineUtil.lua +++ b/scripts/Utils/CombineUtil.lua @@ -4,20 +4,40 @@ AutoDrive.CHASEPOS_REAR = 3 AutoDrive.CHASEPOS_FRONT = 4 AutoDrive.CHASEPOS_UNKNOWN = 0 -function AutoDrive.getIsBufferCombine(vehicle) - return vehicle ~= nil - and vehicle.spec_combine ~= nil - and vehicle.spec_combine.isBufferCombine == true -end +-- harvester types +function AutoDrive.setCombineType(vehicle) + if vehicle == nil then + return false + end + if vehicle.ad == nil then + vehicle.ad = {} + end + local rootVehicle = vehicle:getRootVehicle() + local rootVehicleName = rootVehicle and rootVehicle:getName() or "no rootVehicle!!!" + vehicle.ad.hasCombine = false -- vehicle is any combine or has attached one + vehicle.ad.isAutoAimingChopper = false -- chopper with flexible self targeting pipe + vehicle.ad.isFixedPipeChopper = false -- chopper with fixed pipe + vehicle.ad.isChopper = false -- chopper, without bunker + vehicle.ad.isHarvester = false -- harvester with bunker + + if (vehicle.spec_combine ~= nil and vehicle.spec_pipe ~= nil and vehicle.spec_combine.isBufferCombine == true and vehicle.spec_pipe.numAutoAimingStates > 0) then + vehicle.ad.isAutoAimingChopper = true + end + if (vehicle.spec_combine ~= nil and vehicle.spec_pipe ~= nil and vehicle.spec_combine.isBufferCombine == true and vehicle.spec_pipe.numAutoAimingStates == 0) then + vehicle.ad.isFixedPipeChopper = true + end + if (vehicle.spec_combine ~= nil and vehicle.spec_pipe ~= nil and vehicle.spec_combine.isBufferCombine == true) then + vehicle.ad.isChopper = true + end + if (vehicle.spec_combine ~= nil and vehicle.spec_pipe ~= nil and not vehicle.spec_combine.isBufferCombine) then + vehicle.ad.isHarvester = true + end -function AutoDrive.getIsAutoAimingChopper(vehicle) - return vehicle ~= nil - and vehicle.spec_combine ~= nil - and vehicle.spec_combine.isBufferCombine == true - and vehicle.spec_pipe ~= nil - and vehicle.spec_pipe.numAutoAimingStates > 0 + rootVehicle.ad.hasCombine = vehicle.ad.isAutoAimingChopper or vehicle.ad.isFixedPipeChopper or vehicle.ad.isChopper or vehicle.ad.isHarvester + return rootVehicle.ad.hasCombine end + function AutoDrive.getDischargeNode(combine) local dischargeNode = nil if combine.spec_dischargeable ~= nil then @@ -124,7 +144,7 @@ function AutoDrive.getPipeSide(combine) local dischargeNode = AutoDrive.getDischargeNode(combine) local dischargeX, dichargeY, dischargeZ = getWorldTranslation(dischargeNode) local diffX, _, _ = worldToLocal(combineNode, dischargeX, dichargeY, dischargeZ) - if combine.ad ~= nil and AutoDrive.isPipeOut(combine) and not AutoDrive.getIsAutoAimingChopper(combine) then + if combine.ad ~= nil and (combine.ad.isFixedPipeChopper or combine.ad.isHarvester) and AutoDrive.isPipeOut(combine) then combine.ad.storedPipeSide = AutoDrive.sign(diffX) end return AutoDrive.sign(diffX) @@ -141,7 +161,7 @@ function AutoDrive.getPipeLength(combine) 0, pipeRootZ - dischargeZ) --AutoDrive.debugPrint(combine, AutoDrive.DC_COMBINEINFO, "AutoDrive.getPipeLength - " .. length) - if AutoDrive.isPipeOut(combine) and not AutoDrive.getIsAutoAimingChopper(combine) then + if (combine.ad.isFixedPipeChopper or combine.ad.isHarvester) and AutoDrive.isPipeOut(combine) then local combineNode = AutoDrive.getPipeRoot(combine) local dischargeX, dichargeY, dischargeZ = getWorldTranslation(AutoDrive.getDischargeNode(combine)) diffX, _, _ = worldToLocal(combineNode, dischargeX, dichargeY, dischargeZ) @@ -188,21 +208,6 @@ function AutoDrive.isPipeOut(combine) return false end -function AutoDrive.isSugarcaneHarvester(combine) - -- see Specialisation - return combine and combine.ad and combine.ad.isSugarcaneHarvester -end - -function AutoDrive.isSugarcaneHarvester_old(combine) - local isSugarCaneHarvester = combine.typeName == "combineCutterFruitPreparer" - for _, implement in pairs(AutoDrive.getAllImplements(combine)) do - if implement ~= combine then - isSugarCaneHarvester = false - end - end - return isSugarCaneHarvester -end - function AutoDrive.getAIMarkerWidth(object) local retWidth = 0 if object ~= nil and object.getAIMarkers ~= nil then diff --git a/scripts/Utils/UtilFuncs.lua b/scripts/Utils/UtilFuncs.lua index 7dec6be3..c48a8b2c 100644 --- a/scripts/Utils/UtilFuncs.lua +++ b/scripts/Utils/UtilFuncs.lua @@ -763,7 +763,7 @@ end function AutoDrive.debugMsg(vehicle, debugText, ...) local printText = "[AD] " .. tostring(g_updateLoopIndex) .. " " if vehicle ~= nil then - if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil then + if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil and vehicle == vehicle:getRootVehicle() then printText = printText .. vehicle.ad.stateModule:getName() .. ": " elseif vehicle.getName ~= nil then printText = printText .. vehicle:getName() .. ": " From 8c6d28b19a8935278bebd770259c58062d16f1a4 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:28:32 +0100 Subject: [PATCH 34/51] FollowCombineTask add missing state definition --- scripts/Tasks/FollowCombineTask.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/Tasks/FollowCombineTask.lua b/scripts/Tasks/FollowCombineTask.lua index 9c4f5bce..f21799a7 100644 --- a/scripts/Tasks/FollowCombineTask.lua +++ b/scripts/Tasks/FollowCombineTask.lua @@ -4,6 +4,7 @@ FollowCombineTask.debug = false FollowCombineTask.STATE_CHASING = 1 FollowCombineTask.STATE_WAIT_FOR_TURN = 2 FollowCombineTask.STATE_REVERSING = 3 +FollowCombineTask.STATE_REVERSING_FROM_CHOPPER = 12 FollowCombineTask.STATE_WAIT_FOR_PASS_BY = 4 FollowCombineTask.STATE_CIRCLING_PATHPLANNING = 5 FollowCombineTask.STATE_CIRCLING = 6 From f67bf34b69484568b0f196dc2538a46afcc580de Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:04:34 +0100 Subject: [PATCH 35/51] small fix --- scripts/Modes/CombineUnloaderMode.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Modes/CombineUnloaderMode.lua b/scripts/Modes/CombineUnloaderMode.lua index d123a1a2..e90529bb 100644 --- a/scripts/Modes/CombineUnloaderMode.lua +++ b/scripts/Modes/CombineUnloaderMode.lua @@ -368,7 +368,7 @@ function CombineUnloaderMode:getTaskAfterUnload(filledToUnload) end else -- Should we park in the field? - if self.combine.ad.isChopper or (AutoDrive.getSetting("parkInField", self.vehicle) or (self.lastTask ~= nil and self.lastTask.stayOnField)) then + if (self.combine and self.combine.ad.isChopper) or (AutoDrive.getSetting("parkInField", self.vehicle) or (self.lastTask ~= nil and self.lastTask.stayOnField)) then -- If we are in fruit, we should clear it if AutoDrive.isVehicleOrTrailerInCrop(self.vehicle, true) then AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_COMBINEINFO, "CombineUnloaderMode:getTaskAfterUnload ClearCropTask...") From cf541f92c8ae4246dbf7e19f3ffad68de40d32a9 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Sun, 31 Dec 2023 15:49:50 +1300 Subject: [PATCH 36/51] Fix GraphManager. Bad merge? --- scripts/Manager/GraphManager.lua | 35 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 124fd4ab..2c384b29 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -558,13 +558,13 @@ function ADGraphManager:removeMapMarkerByWayPoint(wayPointId, sendEvent) end end -function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, sendEvent) +function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, dualConnection, sendEvent) if startNode == nil or endNode == nil then return end if sendEvent == nil or sendEvent == true then -- Propagating connection toggling all over the network - AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection) + AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection, dualConnection) else if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then table.removeValue(startNode.out, endNode.id) @@ -573,10 +573,10 @@ function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirec table.insert(startNode.out, endNode.id) if not reverseDirection then table.insert(endNode.incoming, startNode.id) - - - - + if dualConnection then + table.insert(endNode.out, startNode.id) + table.insert(startNode.incoming, endNode.id) + end end end @@ -715,10 +715,7 @@ function ADGraphManager:recordWayPoint(x, y, z, connectPrevious, dual, isReverse local newWp = self:createNode(newId, x, y, z, {}, {}, flags) self:setWayPoint(newWp) if connectPrevious then - self:toggleConnectionBetween(previous, newWp, isReverse, false) - if dual then - self:toggleConnectionBetween(newWp, previous, isReverse, false) - end + self:toggleConnectionBetween(previous, newWp, isReverse, dual, false) end self:markChanges() @@ -1712,29 +1709,29 @@ function ADGraphManager:removeNodesWithFlag(flagToRemove) end end -function ADGraphManager:createSplineConnection(start, waypoints, target, sendEvent) +function ADGraphManager:createSplineConnection(start, waypoints, target, dualConnection, sendEvent) if sendEvent == nil or sendEvent == true then -- Propagating connection toggling all over the network - CreateSplineConnectionEvent.sendEvent(start, waypoints, target) + CreateSplineConnectionEvent.sendEvent(start, waypoints, target, dualConnection) else local lastId = start local lastHeight = ADGraphManager:getWayPointById(start).y + local subPrio = self:getIsPointSubPrio(start) or self:getIsPointSubPrio(target) - - for wpId, wp in pairs(waypoints) do + for _, wp in pairs(waypoints) do if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc wp.y = lastHeight end self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) local createdId = self:getWayPointsCount() - - - + if subPrio then + ADGraphManager:toggleWayPointAsSubPrio(createdId) + end self:toggleConnectionBetween( ADGraphManager:getWayPointById(lastId), ADGraphManager:getWayPointById(createdId), false, - + dualConnection, false ) lastId = createdId @@ -1742,7 +1739,7 @@ function ADGraphManager:createSplineConnection(start, waypoints, target, sendEve end local wp = self:getWayPointById(lastId) - self:toggleConnectionBetween(wp, self:getWayPointById(target), false, false) + self:toggleConnectionBetween(wp, self:getWayPointById(target), false, dualConnection, false) end end From 0c081aca0d8f82b1fedea201abb8df9c5e24b0d4 Mon Sep 17 00:00:00 2001 From: Thomas Matern Date: Tue, 2 Jan 2024 20:50:56 +1300 Subject: [PATCH 37/51] #94 - Fix network error check --- scripts/Manager/GraphManager.lua | 45 ++++++++++++++------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 2c384b29..df28649e 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -1036,15 +1036,15 @@ end -- here also additional checks may be implemented function ADGraphManager:getNetworkErrors() local network = self:getWayPoints() - for id, wp in ipairs(network) do + for _, wp in ipairs(network) do wp.errorMapping = {} if #wp.incoming > 0 then - for inIndex, inId in ipairs(wp.incoming) do + for _, inId in ipairs(wp.incoming) do local inPoint = network[inId] - for outIndex, outId in ipairs(wp.out) do - + local hasGoodAngle = false + for _, outId in ipairs(wp.out) do if inId ~= outId then local outPoint = network[outId] local angle = @@ -1055,33 +1055,31 @@ function ADGraphManager:getNetworkErrors() ) ) if angle > 90 then - wp.errorMapping[outId] = inId + local isBadAngle = true local isReverseStart = not table.contains(outPoint.incoming, wp.id) local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) if isReverseStart or isReverseEnd then - wp.errorMapping[outId] = nil + isBadAngle = false end if ADGraphManager:isDualRoad(wp, outPoint) then - wp.errorMapping[outId] = nil + isBadAngle = false end if ADGraphManager:isDualRoad(wp, inPoint) then - wp.errorMapping[outId] = nil + isBadAngle = false + end + if isBadAngle then + wp.errorMapping[inId] = outId end + else + hasGoodAngle = true end end - - - - - + end + if hasGoodAngle then + wp.errorMapping[inId] = nil end end - - - - - end end end @@ -1383,13 +1381,10 @@ function ADGraphManager:checkForOtherErrors(wp) return true end -- TODO + -- local network = self:getWayPoints() - local network = self:getWayPoints() - - for outIndex, outId in ipairs(wp.out) do - ret = ret or (wp.errorMapping[outId] ~= nil) - - + for _, inId in ipairs(wp.incoming) do + ret = ret or (wp.errorMapping[inId] ~= nil) end return ret end @@ -1474,7 +1469,7 @@ function ADGraphManager:getIsWayPointInSection(wayPointId, direction) -- this is used to treat this wayPoint as last in a section local foundReverse = false for _, connectedId in pairs(connectedIds) do - connectedWayPoint = self:getWayPointById(connectedId) + local connectedWayPoint = self:getWayPointById(connectedId) foundReverse = foundReverse or ADGraphManager:isReverseRoad(wayPoint, connectedWayPoint) end return not foundReverse From 09a8c76b21f7673c5ddd59bbce894c59635d8ebe Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:50:59 +0100 Subject: [PATCH 38/51] fix rare on route traffic detection in special rare cases a leading vehicle may observe the following vehicle as traffic on the route and stops, cause stop of both vehicles --- scripts/Modules/CollisionDetectionModule.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Modules/CollisionDetectionModule.lua b/scripts/Modules/CollisionDetectionModule.lua index dda4dd1d..4e553200 100644 --- a/scripts/Modules/CollisionDetectionModule.lua +++ b/scripts/Modules/CollisionDetectionModule.lua @@ -96,7 +96,7 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() idToCheck = idToCheck + 1 end - if #dualRoutePoints > 0 then + if #dualRoutePoints > 1 then for _, other in pairs(g_currentMission.vehicles) do if other ~= self.vehicle and other.ad ~= nil and other.ad.stateModule ~= nil and other.ad.stateModule:isActive() and other.ad.drivePathModule:isOnRoadNetwork() then local onSameRoute = false From 26fd7029038bfc7fbcc7f4c3f1455b255cde18e6 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sun, 7 Jan 2024 11:48:30 +0100 Subject: [PATCH 39/51] fix #1061 --- scripts/Utils/TrailerUtil.lua | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts/Utils/TrailerUtil.lua b/scripts/Utils/TrailerUtil.lua index 10909a94..d0d9c976 100644 --- a/scripts/Utils/TrailerUtil.lua +++ b/scripts/Utils/TrailerUtil.lua @@ -620,17 +620,19 @@ function AutoDrive.findGrainBackDoorTipSideIndex(vehicle, trailer) local tipSide = spec.tipSides[i] trailer:setCurrentDischargeNodeIndex(tipSide.dischargeNodeIndex) local currentDischargeNode = trailer:getCurrentDischargeNode() - local tx, ty, tz = getWorldTranslation(currentDischargeNode.node) - local _, _, diffZ = worldToLocal(trailer.components[1].node, tx, ty, tz + 50) - -- get the 2 most back doors - if diffZ < backDistance1 and currentDischargeNode and currentDischargeNode.effects and table.count(currentDischargeNode.effects) > 0 then - backDistance1 = diffZ - dischargeSpeed1 = currentDischargeNode.emptySpeed - tipSideIndex1 = i - elseif diffZ < backDistance2 and currentDischargeNode and currentDischargeNode.effects and table.count(currentDischargeNode.effects) > 0 then - backDistance2 = diffZ - dischargeSpeed2 = currentDischargeNode.emptySpeed - tipSideIndex2 = i + if currentDischargeNode then + local tx, ty, tz = getWorldTranslation(currentDischargeNode.node) + local _, _, diffZ = worldToLocal(trailer.components[1].node, tx, ty, tz + 50) + -- get the 2 most back doors + if diffZ < backDistance1 and currentDischargeNode and currentDischargeNode.effects and table.count(currentDischargeNode.effects) > 0 then + backDistance1 = diffZ + dischargeSpeed1 = currentDischargeNode.emptySpeed + tipSideIndex1 = i + elseif diffZ < backDistance2 and currentDischargeNode and currentDischargeNode.effects and table.count(currentDischargeNode.effects) > 0 then + backDistance2 = diffZ + dischargeSpeed2 = currentDischargeNode.emptySpeed + tipSideIndex2 = i + end end end local foundTwoBackDoors = math.abs(backDistance2 - backDistance1) < 1 From 2c5d0f9efe0beb7a555cca43b64854d75356c469 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:33:26 +0100 Subject: [PATCH 40/51] improve route traffic detection better approach to avoid block follow vehicles as traffic --- scripts/Modules/CollisionDetectionModule.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/Modules/CollisionDetectionModule.lua b/scripts/Modules/CollisionDetectionModule.lua index 4e553200..f0e8ba24 100644 --- a/scripts/Modules/CollisionDetectionModule.lua +++ b/scripts/Modules/CollisionDetectionModule.lua @@ -70,6 +70,7 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() self.trafficVehicle = nil local idToCheck = 3 local alreadyOnDualRoute = false + local lastId = nil if wayPoints[currentWayPoint - 1] ~= nil and wayPoints[currentWayPoint] ~= nil then alreadyOnDualRoute = ADGraphManager:isDualRoad(wayPoints[currentWayPoint - 1], wayPoints[currentWayPoint]) end @@ -86,6 +87,7 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() local testDual = ADGraphManager:isDualRoad(startNode, targetNode) if testDual == true then table.insert(dualRoutePoints, startNode.id) + lastId = targetNode.id dualRoute = true else dualRoute = false @@ -96,7 +98,7 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() idToCheck = idToCheck + 1 end - if #dualRoutePoints > 1 then + if #dualRoutePoints > 0 then for _, other in pairs(g_currentMission.vehicles) do if other ~= self.vehicle and other.ad ~= nil and other.ad.stateModule ~= nil and other.ad.stateModule:isActive() and other.ad.drivePathModule:isOnRoadNetwork() then local onSameRoute = false @@ -110,6 +112,13 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() if point == otherWayPoints[otherCurrentWayPoint + i].id then onSameRoute = true --check if going in same direction + if #dualRoutePoints == 1 then + if lastId ~= nil and otherWayPoints[otherCurrentWayPoint + i + 1] ~= nil then + if lastId == otherWayPoints[otherCurrentWayPoint + i + 1].id then + sameDirection = true + end + end + end if dualRoutePoints[_ + 1] ~= nil and otherWayPoints[otherCurrentWayPoint + i + 1] ~= nil then if dualRoutePoints[_ + 1] == otherWayPoints[otherCurrentWayPoint + i + 1].id then sameDirection = true From 7d160239e042faee15074fe914812443459a1f69 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:48:16 +0100 Subject: [PATCH 41/51] improve route traffic detection consider only the first found dual section to catch vehicles with same direction correct --- scripts/Modules/CollisionDetectionModule.lua | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/Modules/CollisionDetectionModule.lua b/scripts/Modules/CollisionDetectionModule.lua index f0e8ba24..96205e88 100644 --- a/scripts/Modules/CollisionDetectionModule.lua +++ b/scripts/Modules/CollisionDetectionModule.lua @@ -70,30 +70,36 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() self.trafficVehicle = nil local idToCheck = 3 local alreadyOnDualRoute = false - local lastId = nil + local lastDualId = nil if wayPoints[currentWayPoint - 1] ~= nil and wayPoints[currentWayPoint] ~= nil then alreadyOnDualRoute = ADGraphManager:isDualRoad(wayPoints[currentWayPoint - 1], wayPoints[currentWayPoint]) end if wayPoints[currentWayPoint + idToCheck] ~= nil and wayPoints[currentWayPoint + idToCheck + 1] ~= nil and not alreadyOnDualRoute then - local dualRoute = ADGraphManager:isDualRoad(wayPoints[currentWayPoint + idToCheck], wayPoints[currentWayPoint + idToCheck + 1]) local dualRoutePoints = {} idToCheck = 0 - while (dualRoute == true) or (idToCheck < 8) do + local foundDualRoute = false + local continueSearch = true + while (continueSearch == true) do local startNode = wayPoints[currentWayPoint + idToCheck] local targetNode = wayPoints[currentWayPoint + idToCheck + 1] if (startNode ~= nil) and (targetNode ~= nil) then local testDual = ADGraphManager:isDualRoad(startNode, targetNode) if testDual == true then table.insert(dualRoutePoints, startNode.id) - lastId = targetNode.id - dualRoute = true + lastDualId = targetNode.id + continueSearch = true + foundDualRoute = true + elseif foundDualRoute == true then + -- dual section ended - exit + continueSearch = false else - dualRoute = false + -- check min 7 WP ahead even if not dual + continueSearch = idToCheck < 8 end else - dualRoute = false + continueSearch = false end idToCheck = idToCheck + 1 end @@ -113,8 +119,8 @@ function ADCollisionDetectionModule:detectAdTrafficOnRoute() onSameRoute = true --check if going in same direction if #dualRoutePoints == 1 then - if lastId ~= nil and otherWayPoints[otherCurrentWayPoint + i + 1] ~= nil then - if lastId == otherWayPoints[otherCurrentWayPoint + i + 1].id then + if lastDualId ~= nil and otherWayPoints[otherCurrentWayPoint + i + 1] ~= nil then + if lastDualId == otherWayPoints[otherCurrentWayPoint + i + 1].id then sameDirection = true end end From 0eb2af1a9f935cd02e1fb929415e83204c3814f7 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:28:45 +0100 Subject: [PATCH 42/51] introduce Bunker Silo Manager - sequence unloading vehicles with destination(s) in the same bunker silo area - range to keep following vehicle(s) waiting selectable in global settings, default 50m - not influence CP silo compacter --- gui/settingsPage.xml | 8 +++ register.lua | 1 + scripts/AutoDrive.lua | 2 + scripts/Manager/BunkerSiloManager.lua | 81 +++++++++++++++++++++++ scripts/Settings.lua | 11 +++ scripts/Tasks/UnloadAtDestinationTask.lua | 1 + translations/translation_br.xml | 2 + translations/translation_cs.xml | 2 + translations/translation_cz.xml | 2 + translations/translation_de.xml | 2 + translations/translation_en.xml | 2 + translations/translation_es.xml | 2 + translations/translation_fr.xml | 2 + translations/translation_hu.xml | 2 + translations/translation_it.xml | 2 + translations/translation_nl.xml | 2 + translations/translation_pl.xml | 2 + translations/translation_pt.xml | 2 + translations/translation_ru.xml | 2 + translations/translation_tr.xml | 2 + 20 files changed, 132 insertions(+) create mode 100644 scripts/Manager/BunkerSiloManager.lua diff --git a/gui/settingsPage.xml b/gui/settingsPage.xml index deacf582..9a8122fe 100644 --- a/gui/settingsPage.xml +++ b/gui/settingsPage.xml @@ -100,6 +100,14 @@ + + + + + + + + diff --git a/register.lua b/register.lua index 1c3162f8..1faedaac 100644 --- a/register.lua +++ b/register.lua @@ -79,6 +79,7 @@ source(Utils.getFilename("scripts/Manager/UserDataManager.lua", g_currentModDire source(Utils.getFilename("scripts/Manager/MultipleTargetsManager.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Manager/ThirdPartyModsManager.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Manager/Scheduler.lua", g_currentModDirectory)) +source(Utils.getFilename("scripts/Manager/BunkerSiloManager.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Tasks/AbstractTask.lua", g_currentModDirectory)) source(Utils.getFilename("scripts/Tasks/DriveToDestinationTask.lua", g_currentModDirectory)) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index 5984eeb6..c4982f2c 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -258,6 +258,7 @@ function AutoDrive:loadMap(name) ADMultipleTargetsManager:load() -- AutoDrive.initTelemetry() + ADBunkerSiloManager:load() InGameMenuAIFrame.onFrameOpen = Utils.appendedFunction(InGameMenuAIFrame.onFrameOpen, AutoDrive.onAIFrameOpen) InGameMenuAIFrame.onFrameClose = Utils.appendedFunction(InGameMenuAIFrame.onFrameClose, AutoDrive.onAIFrameClose) @@ -657,6 +658,7 @@ function AutoDrive:update(dt) if g_server ~= nil then ADHarvestManager:update(dt) ADScheduler:update(dt) + ADBunkerSiloManager:update(dt) end ADMessagesManager:update(dt) diff --git a/scripts/Manager/BunkerSiloManager.lua b/scripts/Manager/BunkerSiloManager.lua new file mode 100644 index 00000000..4f1eccf4 --- /dev/null +++ b/scripts/Manager/BunkerSiloManager.lua @@ -0,0 +1,81 @@ +ADBunkerSiloManager = {} + +ADBunkerSiloManager.UPDATE_TIME = 1000 + +function ADBunkerSiloManager:load() + self.bunkerSilos = {} + self.lastUpdateTime = 0 +end + +function ADBunkerSiloManager:update(dt) + + if g_time < self.lastUpdateTime + ADBunkerSiloManager.UPDATE_TIME then + return + end + self.lastUpdateTime = g_time + local bsmRange = AutoDrive.getSetting("BSMRange") or 0 + if bsmRange == 0 then + return + end + local network = ADGraphManager:getWayPoints() + + self.bunkerSilos = {} + for _, bunkerSilo in pairs(ADTriggerManager.getUnloadTriggers()) do + if bunkerSilo and bunkerSilo.bunkerSiloArea then + bunkerSilo.adVehicles = {} + table.insert(self.bunkerSilos, bunkerSilo) + end + end + + for _, bunkerSilo in pairs(self.bunkerSilos) do + local minDistance = math.huge + for _, vehicle in pairs(g_currentMission.vehicles) do + if vehicle and vehicle.ad and vehicle.ad.stateModule and vehicle.ad.stateModule:isActive() then + local currentTask = vehicle.ad.taskModule:getActiveTask() + local isUnloading = currentTask and currentTask.taskType and vehicle.ad.taskModule:getActiveTask().taskType == "UnloadAtDestinationTask" + if isUnloading then + local destination = vehicle.ad.stateModule:getCurrentDestination() + if destination then + local wp = network[destination.id] + if wp then + local targetInBunker = MathUtil.isPointInParallelogram(wp.x, wp.z, bunkerSilo.bunkerSiloArea.sx, bunkerSilo.bunkerSiloArea.sz, + bunkerSilo.bunkerSiloArea.dwx, bunkerSilo.bunkerSiloArea.dwz, bunkerSilo.bunkerSiloArea.dhx, bunkerSilo.bunkerSiloArea.dhz) + if targetInBunker then + table.insert(bunkerSilo.adVehicles, vehicle) + local vehicleX, _, vehicleZ = getWorldTranslation(vehicle.components[1].node) + local triggerX, _, triggerZ = ADTriggerManager.getTriggerPos(bunkerSilo) + if triggerX ~= nil then + local distance = MathUtil.vector2Length(triggerX - vehicleX, triggerZ - vehicleZ) + if minDistance > distance then + minDistance = distance + bunkerSilo.adClosestVehicle = vehicle + end + end + end + end + end + end + end + end + end + + for _, bunkerSilo in pairs(self.bunkerSilos) do + for _, vehicle in pairs(bunkerSilo.adVehicles) do + local vehicleX, _, vehicleZ = getWorldTranslation(vehicle.components[1].node) + local triggerX, _, triggerZ = ADTriggerManager.getTriggerPos(bunkerSilo) + if triggerX ~= nil then + local distance = MathUtil.vector2Length(triggerX - vehicleX, triggerZ - vehicleZ) + if distance < bsmRange then + if bunkerSilo.adClosestVehicle == vehicle + -- or bunkerSilo.adClosestVehicle == nil + or AutoDrive.isVehicleInBunkerSiloArea(vehicle) then + -- IMPORTANT: DO NOT SET setUnPaused to avoid crash with CP silo compacter !!! + -- vehicle.ad.drivePathModule:setUnPaused() + else + vehicle.ad.drivePathModule:setPaused() + end + end + end + end + end +end diff --git a/scripts/Settings.lua b/scripts/Settings.lua index 469b5568..47531500 100644 --- a/scripts/Settings.lua +++ b/scripts/Settings.lua @@ -1047,6 +1047,17 @@ AutoDrive.settings.remainingDriveTimeInterval = { isUserSpecific = false } +AutoDrive.settings.BSMRange = { + values = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 400, 500}, + texts = { "gui_ad_off", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "120", "140", "160", "180", "200", "250", "300", "400", "500"}, + default = 6, + current = 6, + text = "gui_ad_BSMRange", + tooltip = "gui_ad_BSMRange_tooltip", + translate = true, + isVehicleSpecific = false +} + function AutoDrive.getSetting(settingName, vehicle) if AutoDrive.settings[settingName] ~= nil then local setting = AutoDrive.settings[settingName] diff --git a/scripts/Tasks/UnloadAtDestinationTask.lua b/scripts/Tasks/UnloadAtDestinationTask.lua index 877e55b5..72695a4b 100644 --- a/scripts/Tasks/UnloadAtDestinationTask.lua +++ b/scripts/Tasks/UnloadAtDestinationTask.lua @@ -8,6 +8,7 @@ UnloadAtDestinationTask.BALE_UNLOAD_DISTANCE = 5 function UnloadAtDestinationTask:new(vehicle, destinationID) local o = UnloadAtDestinationTask:create() + o.taskType = "UnloadAtDestinationTask" o.vehicle = vehicle o.destinationID = destinationID o.isContinued = false diff --git a/translations/translation_br.xml b/translations/translation_br.xml index 1565ea63..77fba0d0 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 61f1862d..de77803e 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 7fffe228..05540d6d 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -108,6 +108,8 @@ + + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 9c366ca7..844105da 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -116,6 +116,8 @@ + + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 54b5d85e..f4086bb4 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index 8be5b564..0171d6e5 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -108,6 +108,8 @@ + + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index eb974d8a..e7debe44 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index d96674cb..c258f083 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -108,6 +108,8 @@ + + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 8be8436f..2e253e1b 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -108,6 +108,8 @@ + + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index df647977..728d9e3a 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index fc159348..9c5eec44 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -111,6 +111,8 @@ + + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 365b8a43..7255347e 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -108,6 +108,8 @@ + + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 93944588..78f71217 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -110,6 +110,8 @@ + + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 452adeb9..76ed3a33 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -108,6 +108,8 @@ + + From 5528946549f6f90dfd88c26ad1f98c7535355ebf Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Mon, 15 Jan 2024 18:43:02 +0100 Subject: [PATCH 43/51] BSM: consider only distance, not task --- scripts/Manager/BunkerSiloManager.lua | 55 +++++++++++++++------------ 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/scripts/Manager/BunkerSiloManager.lua b/scripts/Manager/BunkerSiloManager.lua index 4f1eccf4..12bfc0d7 100644 --- a/scripts/Manager/BunkerSiloManager.lua +++ b/scripts/Manager/BunkerSiloManager.lua @@ -17,7 +17,6 @@ function ADBunkerSiloManager:update(dt) if bsmRange == 0 then return end - local network = ADGraphManager:getWayPoints() self.bunkerSilos = {} for _, bunkerSilo in pairs(ADTriggerManager.getUnloadTriggers()) do @@ -29,29 +28,18 @@ function ADBunkerSiloManager:update(dt) for _, bunkerSilo in pairs(self.bunkerSilos) do local minDistance = math.huge + bunkerSilo.adClosestVehicle = nil for _, vehicle in pairs(g_currentMission.vehicles) do if vehicle and vehicle.ad and vehicle.ad.stateModule and vehicle.ad.stateModule:isActive() then - local currentTask = vehicle.ad.taskModule:getActiveTask() - local isUnloading = currentTask and currentTask.taskType and vehicle.ad.taskModule:getActiveTask().taskType == "UnloadAtDestinationTask" - if isUnloading then - local destination = vehicle.ad.stateModule:getCurrentDestination() - if destination then - local wp = network[destination.id] - if wp then - local targetInBunker = MathUtil.isPointInParallelogram(wp.x, wp.z, bunkerSilo.bunkerSiloArea.sx, bunkerSilo.bunkerSiloArea.sz, - bunkerSilo.bunkerSiloArea.dwx, bunkerSilo.bunkerSiloArea.dwz, bunkerSilo.bunkerSiloArea.dhx, bunkerSilo.bunkerSiloArea.dhz) - if targetInBunker then - table.insert(bunkerSilo.adVehicles, vehicle) - local vehicleX, _, vehicleZ = getWorldTranslation(vehicle.components[1].node) - local triggerX, _, triggerZ = ADTriggerManager.getTriggerPos(bunkerSilo) - if triggerX ~= nil then - local distance = MathUtil.vector2Length(triggerX - vehicleX, triggerZ - vehicleZ) - if minDistance > distance then - minDistance = distance - bunkerSilo.adClosestVehicle = vehicle - end - end - end + if self:isDestinationInBunkerSilo(vehicle, bunkerSilo) then + table.insert(bunkerSilo.adVehicles, vehicle) + local vehicleX, _, vehicleZ = getWorldTranslation(vehicle.components[1].node) + local triggerX, _, triggerZ = ADTriggerManager.getTriggerPos(bunkerSilo) + if triggerX ~= nil then + local distance = MathUtil.vector2Length(triggerX - vehicleX, triggerZ - vehicleZ) + if minDistance > distance then + minDistance = distance + bunkerSilo.adClosestVehicle = vehicle end end end @@ -66,9 +54,7 @@ function ADBunkerSiloManager:update(dt) if triggerX ~= nil then local distance = MathUtil.vector2Length(triggerX - vehicleX, triggerZ - vehicleZ) if distance < bsmRange then - if bunkerSilo.adClosestVehicle == vehicle - -- or bunkerSilo.adClosestVehicle == nil - or AutoDrive.isVehicleInBunkerSiloArea(vehicle) then + if AutoDrive.isVehicleInBunkerSiloArea(vehicle) or bunkerSilo.adClosestVehicle == vehicle then -- IMPORTANT: DO NOT SET setUnPaused to avoid crash with CP silo compacter !!! -- vehicle.ad.drivePathModule:setUnPaused() else @@ -79,3 +65,22 @@ function ADBunkerSiloManager:update(dt) end end end + +function ADBunkerSiloManager:isDestinationInBunkerSilo(vehicle, bunkerSilo) + local network = ADGraphManager:getWayPoints() + local destination = nil + local destinationInBunkerSilo = false + if vehicle.ad.stateModule:getMode() == AutoDrive.MODE_PICKUPANDDELIVER or vehicle.ad.stateModule:getMode() == AutoDrive.MODE_UNLOAD then + destination = vehicle.ad.stateModule:getSecondWayPoint() + elseif vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DELIVERTO then + destination = vehicle.ad.stateModule:getFirstWayPoint() + end + if destination and destination > 0 then + local wp = network[destination] + if wp then + destinationInBunkerSilo = MathUtil.isPointInParallelogram(wp.x, wp.z, bunkerSilo.bunkerSiloArea.sx, bunkerSilo.bunkerSiloArea.sz, + bunkerSilo.bunkerSiloArea.dwx, bunkerSilo.bunkerSiloArea.dwz, bunkerSilo.bunkerSiloArea.dhx, bunkerSilo.bunkerSiloArea.dhz) + end + end + return destinationInBunkerSilo +end From 4d7296cfb2bb8f557e13c6753dbcae2acdfc7678 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sat, 27 Jan 2024 22:58:24 +0100 Subject: [PATCH 44/51] decrease max turn angle in route network to <= 80deg --- scripts/Manager/GraphManager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 3b70fc31..a8ca653f 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -940,7 +940,7 @@ function ADGraphManager:prepareWayPoints() local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) --print("prep4: " .. outId .. " angle: " .. angle) - if angle <= 90 then + if angle <= 80 then table.insert(wp.transitMapping[inId], outId) table.insert(wp.inverseTransitMapping[outId], inId) else From e081d8867199143d89034e3b1fb36d82feff3fd2 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:05:07 +0100 Subject: [PATCH 45/51] debug max turn angle in route network --- scripts/Manager/GraphManager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index a8ca653f..2abc11eb 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -973,7 +973,7 @@ function ADGraphManager:getNetworkErrors() if inId ~= outId then local outPoint = network[outId] local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) - if angle > 90 then + if angle > 80 then wp.errorMapping[outId] = inId local isReverseStart = not table.contains(outPoint.incoming, wp.id) local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) From a577a270fe89393c20f7d6c00f44ebdef2e101bc Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Mon, 12 Feb 2024 18:59:02 +0100 Subject: [PATCH 46/51] move waypoints outside map boundaries singleplayer, enter vehicle, activate edit mode, locate vehicle where waypoints are some meters away, activate debug road network, invoke developer action waypoints outside map boundaries will be moved to vehicle position --- scripts/Utils/DevFuncs.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/Utils/DevFuncs.lua b/scripts/Utils/DevFuncs.lua index d73313b9..2128524a 100644 --- a/scripts/Utils/DevFuncs.lua +++ b/scripts/Utils/DevFuncs.lua @@ -47,6 +47,16 @@ function AutoDrive.devAction(vehicle) else Logging.info("[AD] AutoDrive.devAction vehicle %s", tostring(vehicle)) end + + if vehicle ~= nil and g_currentMission.mapWidth and AutoDrive.getDebugChannelIsSet(AutoDrive.DC_ROADNETWORKINFO) and AutoDrive.isInExtendedEditorMode() then + local x, y, z = getWorldTranslation(vehicle.rootNode) + + for _, wp in pairs(ADGraphManager:getWayPoints()) do + if math.abs(wp.x) >= g_currentMission.mapWidth/2 or math.abs(wp.z) >= g_currentMission.mapHeight/2 then + ADGraphManager:moveWayPoint(wp.id, x, y, z, wp.flags) + end + end + end AutoDrive.devPrintDebugQueue(vehicle) end From 80b44733dca78eab450fca1e51b07f2ff925cf44 Mon Sep 17 00:00:00 2001 From: Axel32019 <52026009+Axel32019@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:17:52 +0100 Subject: [PATCH 47/51] Revert "debug max turn angle in route network" --- scripts/Manager/GraphManager.lua | 67 ++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 030cc285..35d148df 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -1036,35 +1036,52 @@ end -- here also additional checks may be implemented function ADGraphManager:getNetworkErrors() local network = self:getWayPoints() - for id, wp in ipairs(network) do + for _, wp in ipairs(network) do wp.errorMapping = {} if #wp.incoming > 0 then - for inIndex, inId in ipairs(wp.incoming) do + + for _, inId in ipairs(wp.incoming) do local inPoint = network[inId] - for outIndex, outId in ipairs(wp.out) do - if inId ~= outId then - local outPoint = network[outId] - local angle = math.abs(AutoDrive.angleBetween({x = outPoint.x - wp.x, z = outPoint.z - wp.z}, {x = wp.x - inPoint.x, z = wp.z - inPoint.z})) - if angle > 80 then - wp.errorMapping[outId] = inId - local isReverseStart = not table.contains(outPoint.incoming, wp.id) - local isReverseEnd = table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - wp.errorMapping[outId] = nil - end - if ADGraphManager:isDualRoad(wp, outPoint) then - wp.errorMapping[outId] = nil - end - if ADGraphManager:isDualRoad(wp, inPoint) then - wp.errorMapping[outId] = nil - end - end - end - end - end - end - end + local hasGoodAngle = false + for _, outId in ipairs(wp.out) do + if inId ~= outId then + local outPoint = network[outId] + local angle = + math.abs( + AutoDrive.angleBetween( + {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, + {x = wp.x - inPoint.x, z = wp.z - inPoint.z} + ) + ) + if angle > 90 then + local isBadAngle = true + local isReverseStart = not table.contains(outPoint.incoming, wp.id) + local isReverseEnd = + table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) + if isReverseStart or isReverseEnd then + isBadAngle = false + end + if ADGraphManager:isDualRoad(wp, outPoint) then + isBadAngle = false + end + if ADGraphManager:isDualRoad(wp, inPoint) then + isBadAngle = false + end + if isBadAngle then + wp.errorMapping[inId] = outId + end + else + hasGoodAngle = true + end + end + end + if hasGoodAngle then + wp.errorMapping[inId] = nil + end + end + end + end end function ADGraphManager:checkResetVehicleDestinations(destination) From f94b7b58cd9bf7d3c32086e2ff038938473c8ae8 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:50:16 +0100 Subject: [PATCH 48/51] Update GraphManager.lua debug max turn angle in route network --- scripts/Manager/GraphManager.lua | 3081 +++++++++++++++--------------- 1 file changed, 1544 insertions(+), 1537 deletions(-) diff --git a/scripts/Manager/GraphManager.lua b/scripts/Manager/GraphManager.lua index 35d148df..7f761fb2 100644 --- a/scripts/Manager/GraphManager.lua +++ b/scripts/Manager/GraphManager.lua @@ -1,4 +1,3 @@ - ADGraphManager = {} ADGraphManager.debugGroupName = "AD_Debug" @@ -7,1841 +6,1849 @@ ADGraphManager.MIN_START_DISTANCE = 8 ADGraphManager.MAX_POINTS_IN_SECTION = 100000 function ADGraphManager:load() - self.wayPoints = {} - self.mapMarkers = {} - self.groups = {} - self.groups["All"] = 1 - self.changes = false - self.preparedWayPoints = false + self.wayPoints = {} + self.mapMarkers = {} + self.groups = {} + self.groups["All"] = 1 + self.changes = false + self.preparedWayPoints = false end function ADGraphManager:markChanges() - self.changes = true - self.preparedWayPoints = false + self.changes = true + self.preparedWayPoints = false end function ADGraphManager:resetChanges() - self.changes = false + self.changes = false end function ADGraphManager:hasChanges() - return self.changes + return self.changes end function ADGraphManager:areWayPointsPrepared() - return self.preparedWayPoints + return self.preparedWayPoints end -- Calling functions expect a linear, continuous array function ADGraphManager:getWayPoints() - return self.wayPoints + return self.wayPoints end function ADGraphManager:getWayPointById(wayPointId) - return self.wayPoints[wayPointId] + return self.wayPoints[wayPointId] end function ADGraphManager:resetWayPoints() - self.wayPoints = {} - self:markChanges() + self.wayPoints = {} + self:markChanges() end function ADGraphManager:setWayPoints(wayPoints) - self.wayPoints = wayPoints - self:markChanges() + self.wayPoints = wayPoints + self:markChanges() end function ADGraphManager:getWayPointsCount() - return #self.wayPoints + return #self.wayPoints end function ADGraphManager:setWayPoint(newPoint) - self.wayPoints[newPoint.id] = newPoint - self:markChanges() + self.wayPoints[newPoint.id] = newPoint + self:markChanges() end function ADGraphManager:getMapMarkers() - return self.mapMarkers + return self.mapMarkers end function ADGraphManager:getMapMarkerById(mapMarkerId) - return self.mapMarkers[mapMarkerId] + return self.mapMarkers[mapMarkerId] end function ADGraphManager:getMapMarkerByWayPointId(wayPointId) - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == wayPointId then - return mapMarker - end - end - return nil + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == wayPointId then + return mapMarker + end + end + return nil end function ADGraphManager:getMapMarkerByName(mapMarkerName) - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.name == mapMarkerName then - return mapMarker - end - end - return nil + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.name == mapMarkerName then + return mapMarker + end + end + return nil end function ADGraphManager:getMapMarkersInGroup(groupName) - local markersInGroup = {} + local markersInGroup = {} - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.group == groupName then - table.insert(markersInGroup, mapMarker) - end - end + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.group == groupName then + table.insert(markersInGroup, mapMarker) + end + end - local sort_func = function(a, b) - a = tostring(a.name):lower() - b = tostring(b.name):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end + local sort_func = function(a, b) + a = tostring(a.name):lower() + b = tostring(b.name):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end - table.sort(markersInGroup, sort_func) + table.sort(markersInGroup, sort_func) - return markersInGroup + return markersInGroup end function ADGraphManager:resetMapMarkers() - self.mapMarkers = {} + self.mapMarkers = {} end function ADGraphManager:setMapMarkers(mapMarkers) - self.mapMarkers = mapMarkers - -- create debug markers, debug markers are not saved, so no need to delete them or update map hotspots required - -- notifyDestinationListeners is called from caller function -> argument is false - self:createDebugMarkers(false) + self.mapMarkers = mapMarkers + -- create debug markers, debug markers are not saved, so no need to delete them or update map hotspots required + -- notifyDestinationListeners is called from caller function -> argument is false + self:createDebugMarkers(false) end function ADGraphManager:setMapMarker(mapMarker) - self.mapMarkers[mapMarker.markerIndex] = mapMarker + self.mapMarkers[mapMarker.markerIndex] = mapMarker end function ADGraphManager:getPathTo(vehicle, waypointId, startPoint) - local wp = {} - - local x, _, z = getWorldTranslation(vehicle.components[1].node) - local wp_target = self.wayPoints[waypointId] - - if wp_target ~= nil then - local distanceToTarget = MathUtil.vector2Length(x - wp_target.x, z - wp_target.z) - if distanceToTarget < ADGraphManager.MIN_START_DISTANCE then - table.insert(wp, wp_target) - return wp - end - end - - local closestWaypoint = self:findMatchingWayPointForVehicle(vehicle) - if startPoint ~= nil and startPoint.id ~= nil then - -- consider id to avoid using Pathfinder wayPoints - local distanceToStartPoint = MathUtil.vector2Length(x - startPoint.x, z - startPoint.z) - if distanceToStartPoint < 5 then - closestWaypoint = startPoint.id - end - end - if closestWaypoint ~= nil then - local outCandidates = self:getBestOutPoints(vehicle, closestWaypoint) - wp = self:pathFromTo(closestWaypoint, waypointId, outCandidates) - end - - return wp + local wp = {} + + local x, _, z = getWorldTranslation(vehicle.components[1].node) + local wp_target = self.wayPoints[waypointId] + + if wp_target ~= nil then + local distanceToTarget = MathUtil.vector2Length(x - wp_target.x, z - wp_target.z) + if distanceToTarget < ADGraphManager.MIN_START_DISTANCE then + table.insert(wp, wp_target) + return wp + end + end + + local closestWaypoint = self:findMatchingWayPointForVehicle(vehicle) + if startPoint ~= nil and startPoint.id ~= nil then + -- consider id to avoid using Pathfinder wayPoints + local distanceToStartPoint = MathUtil.vector2Length(x - startPoint.x, z - startPoint.z) + if distanceToStartPoint < 5 then + closestWaypoint = startPoint.id + end + end + if closestWaypoint ~= nil then + local outCandidates = self:getBestOutPoints(vehicle, closestWaypoint) + wp = self:pathFromTo(closestWaypoint, waypointId, outCandidates) + end + + return wp end function ADGraphManager:pathFromTo(startWaypointId, targetWaypointId, preferredNeighbors) - local wp = {} - if - startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and targetWaypointId ~= nil and - self.wayPoints[targetWaypointId] ~= nil - then - if startWaypointId == targetWaypointId then - table.insert(wp, self.wayPoints[targetWaypointId]) - else - if preferredNeighbors == nil then - preferredNeighbors = {} - end - wp = ADPathCalculator:GetPath(startWaypointId, targetWaypointId, preferredNeighbors) - end - end - return wp + local wp = {} + + if + startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and targetWaypointId ~= nil and + self.wayPoints[targetWaypointId] ~= nil + then + if startWaypointId == targetWaypointId then + table.insert(wp, self.wayPoints[targetWaypointId]) + else + if preferredNeighbors == nil then + preferredNeighbors = {} + end + wp = ADPathCalculator:GetPath(startWaypointId, targetWaypointId, preferredNeighbors) + end + end + return wp end function ADGraphManager:pathFromToMarker(startWaypointId, markerId) - local wp = {} - if - startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and self.mapMarkers[markerId] ~= nil and - self.mapMarkers[markerId].id ~= nil - then - local targetId = self.mapMarkers[markerId].id - if targetId == startWaypointId then - table.insert(wp, 1, self.wayPoints[targetId]) - return wp - else - wp = ADPathCalculator:GetPath(startWaypointId, targetId, {}) - end - end - return wp + local wp = {} + + if + startWaypointId ~= nil and self.wayPoints[startWaypointId] ~= nil and self.mapMarkers[markerId] ~= nil and + self.mapMarkers[markerId].id ~= nil + then + local targetId = self.mapMarkers[markerId].id + if targetId == startWaypointId then + table.insert(wp, 1, self.wayPoints[targetId]) + return wp + else + wp = ADPathCalculator:GetPath(startWaypointId, targetId, {}) + end + end + return wp end function ADGraphManager:FastShortestPath(start, markerName, markerId) - local wp = {} - local start_id = start - local target_id = 0 + local wp = {} + local start_id = start + local target_id = 0 - if start_id == nil or start_id == 0 then - return wp - end + if start_id == nil or start_id == 0 then + return wp + end - for i in pairs(self.mapMarkers) do - if self.mapMarkers[i].name == markerName then - target_id = self.mapMarkers[i].id - break - end - end + for i in pairs(self.mapMarkers) do + if self.mapMarkers[i].name == markerName then + target_id = self.mapMarkers[i].id + break + end + end - if target_id == 0 then - return wp - end + if target_id == 0 then + return wp + end - if target_id == start_id then - table.insert(wp, 1, self.wayPoints[target_id]) - return wp - end + if target_id == start_id then + table.insert(wp, 1, self.wayPoints[target_id]) + return wp + end - wp = ADPathCalculator:GetPath(start_id, target_id, {}) - return wp + wp = ADPathCalculator:GetPath(start_id, target_id, {}) + return wp end function ADGraphManager:getDistanceFromNetwork(vehicle) - local _, distance = vehicle:getClosestWayPoint() - return distance + local _, distance = vehicle:getClosestWayPoint() + return distance end function ADGraphManager:checkYPositionIntegrity() - for _, wp in pairs(self.wayPoints) do - if wp.y == -1 then - wp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wp.x, 1, wp.z) - end - end + for _, wp in pairs(self.wayPoints) do + if wp.y == -1 then + wp.y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wp.x, 1, wp.z) + end + end end function ADGraphManager:removeWayPoint(wayPointId, sendEvent) - if wayPointId ~= nil and wayPointId >= 0 and self.wayPoints[wayPointId] ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating way point deletion all over the network - AutoDriveDeleteWayPointEvent.sendEvent(wayPointId) - else - -- Deleting map marker if there is one on this waypoint, 'sendEvent' must be false because the event propagation has already happened - self:removeMapMarkerByWayPoint(wayPointId, false) - - local wayPoint = self.wayPoints[wayPointId] - - -- Removing incoming node reference on all out nodes - for _, id in pairs(wayPoint.out) do - if self.wayPoints[id] ~= nil and self.wayPoints[id].incoming ~= nil and wayPoint.id ~= nil then - local incomingId = table.indexOf(self.wayPoints[id].incoming, wayPoint.id) - if incomingId ~= nil then - table.remove(self.wayPoints[id].incoming, incomingId) - end - end - end - - -- Removing out node reference on all incoming nodes - for _, id in pairs(wayPoint.incoming) do - if self.wayPoints[id] ~= nil and self.wayPoints[id].out ~= nil and wayPoint.id ~= nil then - local outId = table.indexOf(self.wayPoints[id].out, wayPoint.id) - if outId ~= nil then - table.remove(self.wayPoints[id].out, outId) - end - end - end - - if #wayPoint.incoming == 0 then - -- This is a reverse node, so we can't rely on the incoming table - for _, wp in pairs(self.wayPoints) do - if wp.out ~= nil and wayPoint.id ~= nil then - if table.contains(wp.out, wayPoint.id) then - table.removeValue(wp.out, wayPoint.id) - end - end - end - end - - -- Removing waypoint from waypoints array and invalidate it by setting id to -1 - local wp = table.remove(self.wayPoints, wayPoint.id) - if wp ~= nil then - wp.id = -1 - end - - -- Adjusting ids for all succesive nodes :( - for _, wp in pairs(self.wayPoints) do - if wp.id > wayPointId then - wp.id = wp.id - 1 - end - for i, outId in pairs(wp.out) do - if outId > wayPointId then - wp.out[i] = outId - 1 - end - end - for i, incomingId in pairs(wp.incoming) do - if incomingId > wayPointId then - wp.incoming[i] = incomingId - 1 - end - end - end - - -- Adjusting way point id in markers - for _, marker in pairs(self.mapMarkers) do - if marker.id > wayPointId then - marker.id = marker.id - 1 - end - end - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if wayPointId ~= nil and wayPointId >= 0 and self.wayPoints[wayPointId] ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating way point deletion all over the network + AutoDriveDeleteWayPointEvent.sendEvent(wayPointId) + else + -- Deleting map marker if there is one on this waypoint, 'sendEvent' must be false because the event propagation has already happened + self:removeMapMarkerByWayPoint(wayPointId, false) + + local wayPoint = self.wayPoints[wayPointId] + + -- Removing incoming node reference on all out nodes + for _, id in pairs(wayPoint.out) do + if self.wayPoints[id] ~= nil and self.wayPoints[id].incoming ~= nil and wayPoint.id ~= nil then + local incomingId = table.indexOf(self.wayPoints[id].incoming, wayPoint.id) + if incomingId ~= nil then + table.remove(self.wayPoints[id].incoming, incomingId) + end + end + end + + -- Removing out node reference on all incoming nodes + for _, id in pairs(wayPoint.incoming) do + if self.wayPoints[id] ~= nil and self.wayPoints[id].out ~= nil and wayPoint.id ~= nil then + local outId = table.indexOf(self.wayPoints[id].out, wayPoint.id) + if outId ~= nil then + table.remove(self.wayPoints[id].out, outId) + end + end + end + + if #wayPoint.incoming == 0 then + -- This is a reverse node, so we can't rely on the incoming table + for _, wp in pairs(self.wayPoints) do + if wp.out ~= nil and wayPoint.id ~= nil then + if table.contains(wp.out, wayPoint.id) then + table.removeValue(wp.out, wayPoint.id) + end + end + end + end + + -- Removing waypoint from waypoints array and invalidate it by setting id to -1 + local wp = table.remove(self.wayPoints, wayPoint.id) + if wp ~= nil then + wp.id = -1 + end + + -- Adjusting ids for all succesive nodes :( + for _, wp in pairs(self.wayPoints) do + if wp.id > wayPointId then + wp.id = wp.id - 1 + end + for i, outId in pairs(wp.out) do + if outId > wayPointId then + wp.out[i] = outId - 1 + end + end + for i, incomingId in pairs(wp.incoming) do + if incomingId > wayPointId then + wp.incoming[i] = incomingId - 1 + end + end + end + + -- Adjusting way point id in markers + for _, marker in pairs(self.mapMarkers) do + if marker.id > wayPointId then + marker.id = marker.id - 1 + end + end + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:renameMapMarker(newName, markerId, sendEvent) - if newName:len() >= 1 and markerId >= 0 then - local mapMarker = self:getMapMarkerById(markerId) - if mapMarker == nil or mapMarker.isADDebug == true then - -- do not allow rename debug marker - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating marker rename all over the network - AutoDriveRenameMapMarkerEvent.sendEvent(newName, markerId) - else - -- Saving old map marker name - local oldName = self.mapMarkers[markerId].name - -- Renaming map marker - self.mapMarkers[markerId].name = newName - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if newName:len() >= 1 and markerId >= 0 then + local mapMarker = self:getMapMarkerById(markerId) + if mapMarker == nil or mapMarker.isADDebug == true then + -- do not allow rename debug marker + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating marker rename all over the network + AutoDriveRenameMapMarkerEvent.sendEvent(newName, markerId) + else + -- Saving old map marker name + local oldName = self.mapMarkers[markerId].name + -- Renaming map marker + self.mapMarkers[markerId].name = newName + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:createMapMarkerOnClosest(vehicle, markerName, sendEvent) - if vehicle ~= nil and markerName:len() >= 1 then - -- Finding closest waypoint - local closest, _ = vehicle:getClosestWayPoint() - if closest ~= nil and closest ~= -1 and self.wayPoints[closest] ~= nil then - self:createMapMarker(closest, markerName, sendEvent) - end - end + if vehicle ~= nil and markerName:len() >= 1 then + -- Finding closest waypoint + local closest, _ = vehicle:getClosestWayPoint() + if closest ~= nil and closest ~= -1 and self.wayPoints[closest] ~= nil then + self:createMapMarker(closest, markerName, sendEvent) + end + end end function ADGraphManager:createMapMarker(markerId, markerName, sendEvent) - if markerId ~= nil and markerId >= 0 and markerName:len() >= 1 then - if sendEvent == nil or sendEvent == true then - -- Propagating marker creation all over the network - AutoDriveCreateMapMarkerEvent.sendEvent(markerId, markerName) - else - -- Creating the new map marker - self.mapMarkers[#self.mapMarkers + 1] = { - id = markerId, - markerIndex = (#self.mapMarkers + 1), - name = markerName, - group = "All" - } - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if markerId ~= nil and markerId >= 0 and markerName:len() >= 1 then + if sendEvent == nil or sendEvent == true then + -- Propagating marker creation all over the network + AutoDriveCreateMapMarkerEvent.sendEvent(markerId, markerName) + else + -- Creating the new map marker + self.mapMarkers[#self.mapMarkers + 1] = { + id = markerId, + markerIndex = (#self.mapMarkers + 1), + name = markerName, + group = "All" + } + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:addGroup(groupName, sendEvent) - if groupName:len() >= 1 and self.groups[groupName] == nil then - if sendEvent == nil or sendEvent == true then - -- Propagating group creation all over the network - AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_ADD) - else - self.groups[groupName] = table.count(self.groups) + 1 - for _, vehicle in pairs(g_currentMission.vehicles) do - if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then - if vehicle.ad.groups[groupName] == nil then - vehicle.ad.groups[groupName] = false - end - end - end - -- Resetting HUD - if AutoDrive.Hud ~= nil then - AutoDrive.Hud.lastUIScale = 0 - end - self:markChanges() - end - end + if groupName:len() >= 1 and self.groups[groupName] == nil then + if sendEvent == nil or sendEvent == true then + -- Propagating group creation all over the network + AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_ADD) + else + self.groups[groupName] = table.count(self.groups) + 1 + for _, vehicle in pairs(g_currentMission.vehicles) do + if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then + if vehicle.ad.groups[groupName] == nil then + vehicle.ad.groups[groupName] = false + end + end + end + -- Resetting HUD + if AutoDrive.Hud ~= nil then + AutoDrive.Hud.lastUIScale = 0 + end + self:markChanges() + end + end end function ADGraphManager:removeGroup(groupName, sendEvent) - if self.groups[groupName] ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating group creation all over the network - AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_REMOVE) - else - local groupId = self.groups[groupName] - -- Removing group from the groups list - self.groups[groupName] = nil - -- Removing group from the vehicles groups list - for _, vehicle in pairs(g_currentMission.vehicles) do - if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then - if vehicle.ad.groups[groupName] ~= nil then - vehicle.ad.groups[groupName] = nil - end - end - end - -- Moving all markers in the deleted group to default group - for markerID, mapMarker in pairs(self:getMapMarkers()) do - if mapMarker.group == groupName then - mapMarker.group = "All" - end - end - -- Resetting other goups id - for gName, gId in pairs(self.groups) do - if groupId <= gId then - self.groups[gName] = gId - 1 - end - end - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - self:markChanges() - end - end + if self.groups[groupName] ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating group creation all over the network + AutoDriveGroupsEvent.sendEvent(groupName, AutoDriveGroupsEvent.TYPE_REMOVE) + else + local groupId = self.groups[groupName] + -- Removing group from the groups list + self.groups[groupName] = nil + -- Removing group from the vehicles groups list + for _, vehicle in pairs(g_currentMission.vehicles) do + if (vehicle.ad ~= nil and vehicle.ad.groups ~= nil) then + if vehicle.ad.groups[groupName] ~= nil then + vehicle.ad.groups[groupName] = nil + end + end + end + -- Moving all markers in the deleted group to default group + for markerID, mapMarker in pairs(self:getMapMarkers()) do + if mapMarker.group == groupName then + mapMarker.group = "All" + end + end + -- Resetting other goups id + for gName, gId in pairs(self.groups) do + if groupId <= gId then + self.groups[gName] = gId - 1 + end + end + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + self:markChanges() + end + end end function ADGraphManager:changeMapMarkerGroup(groupName, markerId, sendEvent) - if - groupName:len() >= 1 and self.groups[groupName] ~= nil and markerId >= 0 and - groupName ~= ADGraphManager.debugGroupName - then - if sendEvent == nil or sendEvent == true then - -- Propagating marker group change all over the network - AutoDriveChangeMapMarkerGroupEvent.sendEvent(groupName, markerId) - else - -- Changing the group name of the marker - self.mapMarkers[markerId].group = groupName - self:markChanges() - end - end + if + groupName:len() >= 1 and self.groups[groupName] ~= nil and markerId >= 0 and + groupName ~= ADGraphManager.debugGroupName + then + if sendEvent == nil or sendEvent == true then + -- Propagating marker group change all over the network + AutoDriveChangeMapMarkerGroupEvent.sendEvent(groupName, markerId) + else + -- Changing the group name of the marker + self.mapMarkers[markerId].group = groupName + self:markChanges() + end + end end function ADGraphManager:getGroups() - return self.groups + return self.groups end function ADGraphManager:setGroups(groups, updateVehicles) - self.groups = groups - if updateVehicles then - for _, vehicle in pairs(g_currentMission.vehicles) do - if vehicle.ad ~= nil then - if vehicle.ad.groups == nil then - vehicle.ad.groups = {} - end - local newGroups = {} - for groupName, _ in pairs(ADGraphManager:getGroups()) do - newGroups[groupName] = vehicle.ad.groups[groupName] or false - end - vehicle.ad.groups = newGroups - end - end - end + self.groups = groups + if updateVehicles then + for _, vehicle in pairs(g_currentMission.vehicles) do + if vehicle.ad ~= nil then + if vehicle.ad.groups == nil then + vehicle.ad.groups = {} + end + local newGroups = {} + for groupName, _ in pairs(ADGraphManager:getGroups()) do + newGroups[groupName] = vehicle.ad.groups[groupName] or false + end + vehicle.ad.groups = newGroups + end + end + end end function ADGraphManager:getGroupByName(groupName) - return self.groups[groupName] + return self.groups[groupName] end function ADGraphManager:removeMapMarker(markerId, sendEvent) - if markerId ~= nil and markerId >= 0 then - if sendEvent == nil or sendEvent == true then - -- Propagating marker deletion all over the network - AutoDriveDeleteMapMarkerEvent.sendEvent(markerId) - else - if self.mapMarkers[markerId] ~= nil then - table.remove(self.mapMarkers, markerId) - --Readjust stored markerIndex values to point to corrected ID - for markerID, marker in pairs(self.mapMarkers) do - marker.markerIndex = markerID - end - - if g_server ~= nil then - -- Removing references to it on all vehicles - for _, vehicle in pairs(g_currentMission.vehicles) do - if - vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil and - vehicle.ad.stateModule.getParkDestinationAtJobFinished ~= nil - then - local parkDestinationAtJobFinished = - vehicle.ad.stateModule:getParkDestinationAtJobFinished() - if parkDestinationAtJobFinished ~= nil and parkDestinationAtJobFinished >= markerId then - if parkDestinationAtJobFinished == markerId then - vehicle.ad.stateModule:setParkDestinationAtJobFinished(-1) - else - vehicle.ad.stateModule:setParkDestinationAtJobFinished( - math.max(parkDestinationAtJobFinished - 1, 1) - ) - end - end - end - end - -- handle all vehicles and tools park destination - for _, vehicle in pairs(g_currentMission.vehicles) do - if - vehicle.advd ~= nil and vehicle.advd.getParkDestination ~= nil and - vehicle.advd.setParkDestination ~= nil - then - local parkDestination = vehicle.advd:getParkDestination(vehicle) - if parkDestination ~= nil and parkDestination >= markerId then - if parkDestination == markerId then - vehicle.advd:setParkDestination(vehicle, -1) - else - vehicle.advd:setParkDestination(vehicle, math.max(parkDestination - 1, 1)) - end - end - end - end - end - -- remove deleted marker from vehicle destinations - ADGraphManager:checkResetVehicleDestinations(markerId) - end - - -- Calling external interop listeners - AutoDrive:notifyDestinationListeners() - - -- Resetting HUD - AutoDrive.Hud.lastUIScale = 0 - - self:markChanges() - end - end + if markerId ~= nil and markerId >= 0 then + if sendEvent == nil or sendEvent == true then + -- Propagating marker deletion all over the network + AutoDriveDeleteMapMarkerEvent.sendEvent(markerId) + else + if self.mapMarkers[markerId] ~= nil then + table.remove(self.mapMarkers, markerId) + --Readjust stored markerIndex values to point to corrected ID + for markerID, marker in pairs(self.mapMarkers) do + marker.markerIndex = markerID + end + + if g_server ~= nil then + -- Removing references to it on all vehicles + for _, vehicle in pairs(g_currentMission.vehicles) do + if + vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil and + vehicle.ad.stateModule.getParkDestinationAtJobFinished ~= nil + then + local parkDestinationAtJobFinished = + vehicle.ad.stateModule:getParkDestinationAtJobFinished() + if parkDestinationAtJobFinished ~= nil and parkDestinationAtJobFinished >= markerId then + if parkDestinationAtJobFinished == markerId then + vehicle.ad.stateModule:setParkDestinationAtJobFinished(-1) + else + vehicle.ad.stateModule:setParkDestinationAtJobFinished( + math.max(parkDestinationAtJobFinished - 1, 1) + ) + end + end + end + end + -- handle all vehicles and tools park destination + for _, vehicle in pairs(g_currentMission.vehicles) do + if + vehicle.advd ~= nil and vehicle.advd.getParkDestination ~= nil and + vehicle.advd.setParkDestination ~= nil + then + local parkDestination = vehicle.advd:getParkDestination(vehicle) + if parkDestination ~= nil and parkDestination >= markerId then + if parkDestination == markerId then + vehicle.advd:setParkDestination(vehicle, -1) + else + vehicle.advd:setParkDestination(vehicle, math.max(parkDestination - 1, 1)) + end + end + end + end + end + -- remove deleted marker from vehicle destinations + ADGraphManager:checkResetVehicleDestinations(markerId) + end + + -- Calling external interop listeners + AutoDrive:notifyDestinationListeners() + + -- Resetting HUD + AutoDrive.Hud.lastUIScale = 0 + + self:markChanges() + end + end end function ADGraphManager:removeMapMarkerByWayPoint(wayPointId, sendEvent) - if wayPointId ~= nil and wayPointId >= 0 then - -- Finding the map waypoint where the marker should be - local wayPoint = self.wayPoints[wayPointId] - if wayPoint ~= nil then - for markerId, marker in pairs(self.mapMarkers) do - -- Checking if the waypoint id matches the marker id - if marker.id == wayPoint.id then - self:removeMapMarker(markerId, sendEvent) - break - end - end - end - end + if wayPointId ~= nil and wayPointId >= 0 then + -- Finding the map waypoint where the marker should be + local wayPoint = self.wayPoints[wayPointId] + if wayPoint ~= nil then + for markerId, marker in pairs(self.mapMarkers) do + -- Checking if the waypoint id matches the marker id + if marker.id == wayPoint.id then + self:removeMapMarker(markerId, sendEvent) + break + end + end + end + end end function ADGraphManager:toggleConnectionBetween(startNode, endNode, reverseDirection, dualConnection, sendEvent) - if startNode == nil or endNode == nil then - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network + if startNode == nil or endNode == nil then + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network AutoDriveToggleConnectionEvent.sendEvent(startNode, endNode, reverseDirection, dualConnection) - else - if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then - table.removeValue(startNode.out, endNode.id) - table.removeValue(endNode.incoming, startNode.id) - else - table.insert(startNode.out, endNode.id) - if not reverseDirection then - table.insert(endNode.incoming, startNode.id) + else + if table.contains(startNode.out, endNode.id) or table.contains(endNode.incoming, startNode.id) then + table.removeValue(startNode.out, endNode.id) + table.removeValue(endNode.incoming, startNode.id) + else + table.insert(startNode.out, endNode.id) + if not reverseDirection then + table.insert(endNode.incoming, startNode.id) if dualConnection then table.insert(endNode.out, startNode.id) table.insert(startNode.incoming, endNode.id) end - end - end + end + end - self:markChanges() - end + self:markChanges() + end end --[[ -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 -reverse connection 4 +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 +reverse connection 4 ]] function ADGraphManager:setConnectionBetween(startNode, endNode, direction, sendEvent) - if startNode == nil or endNode == nil or direction < 1 or direction > 4 then - return - end - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network - AutoDriveSetConnectionEvent.sendEvent(startNode, endNode, direction) - else - -- remove all connections between the 2 nodes - if table.contains(startNode.out, endNode.id) then - table.removeValue(startNode.out, endNode.id) - end - if table.contains(startNode.incoming, endNode.id) then - table.removeValue(startNode.incoming, endNode.id) - end - if table.contains(endNode.out, startNode.id) then - table.removeValue(endNode.out, startNode.id) - end - if table.contains(endNode.incoming, startNode.id) then - table.removeValue(endNode.incoming, startNode.id) - end - if direction == 1 then - -- forward - table.insert(startNode.out, endNode.id) - table.insert(endNode.incoming, startNode.id) - elseif direction == 2 then - -- backward - table.insert(startNode.incoming, endNode.id) - table.insert(endNode.out, startNode.id) - elseif direction == 3 then - -- dual - table.insert(startNode.out, endNode.id) - table.insert(endNode.incoming, startNode.id) - table.insert(startNode.incoming, endNode.id) - table.insert(endNode.out, startNode.id) - elseif direction == 4 then - -- reverse - table.insert(startNode.out, endNode.id) - end - self:markChanges() - end + if startNode == nil or endNode == nil or direction < 1 or direction > 4 then + return + end + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network + AutoDriveSetConnectionEvent.sendEvent(startNode, endNode, direction) + else + -- remove all connections between the 2 nodes + if table.contains(startNode.out, endNode.id) then + table.removeValue(startNode.out, endNode.id) + end + if table.contains(startNode.incoming, endNode.id) then + table.removeValue(startNode.incoming, endNode.id) + end + if table.contains(endNode.out, startNode.id) then + table.removeValue(endNode.out, startNode.id) + end + if table.contains(endNode.incoming, startNode.id) then + table.removeValue(endNode.incoming, startNode.id) + end + if direction == 1 then + -- forward + table.insert(startNode.out, endNode.id) + table.insert(endNode.incoming, startNode.id) + elseif direction == 2 then + -- backward + table.insert(startNode.incoming, endNode.id) + table.insert(endNode.out, startNode.id) + elseif direction == 3 then + -- dual + table.insert(startNode.out, endNode.id) + table.insert(endNode.incoming, startNode.id) + table.insert(startNode.incoming, endNode.id) + table.insert(endNode.out, startNode.id) + elseif direction == 4 then + -- reverse + table.insert(startNode.out, endNode.id) + end + self:markChanges() + end end function ADGraphManager:createWayPoint(x, y, z, sendEvent) - if sendEvent == nil or sendEvent == true then - -- Propagating waypoint creation all over the network - AutoDriveCreateWayPointEvent.sendEvent(x, y, z) - else - local prevId = self:getWayPointsCount() - local newId = prevId + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, 0) - self:setWayPoint(newWp) - self:markChanges() + if sendEvent == nil or sendEvent == true then + -- Propagating waypoint creation all over the network + AutoDriveCreateWayPointEvent.sendEvent(x, y, z) + else + local prevId = self:getWayPointsCount() + local newId = prevId + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, 0) + self:setWayPoint(newWp) + self:markChanges() - return newWp - end + return newWp + end end function ADGraphManager:createWayPointColored(x, y, z, colors) - local prevId = self:getWayPointsCount() - local newId = prevId + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, 0, colors) - self:setWayPoint(newWp) - self:markChanges() + local prevId = self:getWayPointsCount() + local newId = prevId + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, 0, colors) + self:setWayPoint(newWp) + self:markChanges() - return newWp + return newWp end function ADGraphManager:changeWayPointPosition(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) + end end function ADGraphManager:moveWayPoint(wayPointId, x, y, z, flags, sendEvent) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - if sendEvent == nil or sendEvent == true then - -- Propagating waypoint moving all over the network - AutoDriveMoveWayPointEvent.sendEvent(wayPointId, x, y, z, flags) - else - wayPoint.x = x - wayPoint.y = y - wayPoint.z = z - wayPoint.flags = flags - self:markChanges() - end - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + if sendEvent == nil or sendEvent == true then + -- Propagating waypoint moving all over the network + AutoDriveMoveWayPointEvent.sendEvent(wayPointId, x, y, z, flags) + else + wayPoint.x = x + wayPoint.y = y + wayPoint.z = z + wayPoint.flags = flags + self:markChanges() + end + end end function ADGraphManager:recordWayPoint(x, y, z, connectPrevious, dual, isReverse, previousId, flags, sendEvent) - previousId = previousId or 0 - local previous - if connectPrevious then - if previousId == nil or previousId == 0 then - previousId = self:getWayPointsCount() - end - previous = self:getWayPointById(previousId) - end - if g_server ~= nil then - if sendEvent ~= false then - -- Propagating waypoint recording to clients - AutoDriveRecordWayPointEvent.sendEvent(x, y, z, connectPrevious, dual, isReverse, previousId, flags) - end - else - if sendEvent ~= false then - Logging.warning("ADGraphManager:recordWayPoint() must be called only on the server.") - return - end - end - - -- play sound only on client with enabled editor mode or RecordWhileNotInVehicle - if g_client ~= nil then - local vehicle = g_currentMission.controlledVehicle - local forced = AutoDrive.experimentalFeatures.RecordWhileNotInVehicle - if (vehicle ~= nil and vehicle.ad ~= nil and AutoDrive.isInExtendedEditorMode()) or forced then - AutoDrive.playSample(AutoDrive.recordWaypointSample, 0.25, forced) - end - end - - local newId = self:getWayPointsCount() + 1 - local newWp = self:createNode(newId, x, y, z, {}, {}, flags) - self:setWayPoint(newWp) - if connectPrevious then + previousId = previousId or 0 + local previous + if connectPrevious then + if previousId == nil or previousId == 0 then + previousId = self:getWayPointsCount() + end + previous = self:getWayPointById(previousId) + end + if g_server ~= nil then + if sendEvent ~= false then + -- Propagating waypoint recording to clients + AutoDriveRecordWayPointEvent.sendEvent(x, y, z, connectPrevious, dual, isReverse, previousId, flags) + end + else + if sendEvent ~= false then + Logging.warning("ADGraphManager:recordWayPoint() must be called only on the server.") + return + end + end + + -- play sound only on client with enabled editor mode or RecordWhileNotInVehicle + if g_client ~= nil then + local vehicle = g_currentMission.controlledVehicle + local forced = AutoDrive.experimentalFeatures.RecordWhileNotInVehicle + if (vehicle ~= nil and vehicle.ad ~= nil and AutoDrive.isInExtendedEditorMode()) or forced then + AutoDrive.playSample(AutoDrive.recordWaypointSample, 0.25, forced) + end + end + + local newId = self:getWayPointsCount() + 1 + local newWp = self:createNode(newId, x, y, z, {}, {}, flags) + self:setWayPoint(newWp) + if connectPrevious then self:toggleConnectionBetween(previous, newWp, isReverse, dual, false) - end + end - self:markChanges() - return newWp + self:markChanges() + return newWp end function ADGraphManager:isDualRoad(start, target) - if - start == nil or target == nil or start.incoming == nil or target.incoming == nil or start.id == nil or - target.id == nil - then - return false - end - if table.contains(start.incoming, target.id) and table.contains(target.incoming, start.id) then - return true - end - return false + if + start == nil or target == nil or start.incoming == nil or target.incoming == nil or start.id == nil or + target.id == nil + then + return false + end + if table.contains(start.incoming, target.id) and table.contains(target.incoming, start.id) then + return true + end + return false end function ADGraphManager:isReverseRoad(start, target) - if start == nil or target == nil or start.out == nil or start.id == nil or target.id == nil then - return false - end - return (not table.contains(target.incoming, start.id) and table.contains(start.out, target.id)) + if start == nil or target == nil or start.out == nil or start.id == nil or target.id == nil then + return false + end + return (not table.contains(target.incoming, start.id) and table.contains(start.out, target.id)) end function ADGraphManager:getDistanceBetweenNodes(start, target) - local euclidianDistance = - MathUtil.vector2Length( - self.wayPoints[start].x - self.wayPoints[target].x, - self.wayPoints[start].z - self.wayPoints[target].z - ) - - local distance = euclidianDistance - - if AutoDrive.getSetting("mapMarkerDetour") > 0 then - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == start then - distance = distance + AutoDrive.getSetting("mapMarkerDetour") - break - end - end - end + local euclidianDistance = + MathUtil.vector2Length( + self.wayPoints[start].x - self.wayPoints[target].x, + self.wayPoints[start].z - self.wayPoints[target].z + ) + + local distance = euclidianDistance + + if AutoDrive.getSetting("mapMarkerDetour") > 0 then + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == start then + distance = distance + AutoDrive.getSetting("mapMarkerDetour") + break + end + end + end - if self:getIsPointSubPrio(self.wayPoints[target].id) then - distance = distance * ADGraphManager.SUB_PRIO_FACTOR - end + if self:getIsPointSubPrio(self.wayPoints[target].id) then + distance = distance * ADGraphManager.SUB_PRIO_FACTOR + end - return distance + return distance end function ADGraphManager:getDriveTimeBetweenNodes(start, target, past, maxDrivingSpeed, arrivalTime) - --changed setToUse to defined 3 point for angle calculation - local wp_ahead = self.wayPoints[target] - local wp_current = self.wayPoints[start] - - if wp_ahead == nil or wp_current == nil then - return 0 - end - - local angle = 0 - - if past ~= nil then - local wp_ref = self.wayPoints[past] - if wp_ref ~= nil then - angle = - math.abs( - AutoDrive.angleBetween( - {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, - {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} - ) - ) - end - end - - local driveTime = 0 - local drivingSpeed = 50 - - if angle < 3 then - drivingSpeed = 50 - elseif angle < 5 then - drivingSpeed = 38 - elseif angle < 8 then - drivingSpeed = 27 - elseif angle < 12 then - drivingSpeed = 20 - elseif angle < 15 then - drivingSpeed = 13 - elseif angle < 20 then - drivingSpeed = 10 - elseif angle < 30 then - drivingSpeed = 7 - else - drivingSpeed = 4 - end - - if maxDrivingSpeed ~= nil then - drivingSpeed = math.min(drivingSpeed, maxDrivingSpeed) - end - - local drivingDistance = MathUtil.vector2Length(wp_ahead.x - wp_current.x, wp_ahead.z - wp_current.z) - - driveTime = (drivingDistance) / (drivingSpeed * (1000 / 3600)) - - --avoid map marker - - if not arrivalTime == true then --only for djikstra, for live travel timer we ignore it - if AutoDrive.getSetting("mapMarkerDetour") > 0 then - for _, mapMarker in pairs(self.mapMarkers) do - if mapMarker.id == start then - driveTime = driveTime + (AutoDrive.getSetting("mapMarkerDetour") / (20 / 3.6)) - break - end - end - end - end - - return driveTime, angle + --changed setToUse to defined 3 point for angle calculation + local wp_ahead = self.wayPoints[target] + local wp_current = self.wayPoints[start] + + if wp_ahead == nil or wp_current == nil then + return 0 + end + + local angle = 0 + + if past ~= nil then + local wp_ref = self.wayPoints[past] + if wp_ref ~= nil then + angle = + math.abs( + AutoDrive.angleBetween( + {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, + {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} + ) + ) + end + end + + local driveTime = 0 + local drivingSpeed = 50 + + if angle < 3 then + drivingSpeed = 50 + elseif angle < 5 then + drivingSpeed = 38 + elseif angle < 8 then + drivingSpeed = 27 + elseif angle < 12 then + drivingSpeed = 20 + elseif angle < 15 then + drivingSpeed = 13 + elseif angle < 20 then + drivingSpeed = 10 + elseif angle < 30 then + drivingSpeed = 7 + else + drivingSpeed = 4 + end + + if maxDrivingSpeed ~= nil then + drivingSpeed = math.min(drivingSpeed, maxDrivingSpeed) + end + + local drivingDistance = MathUtil.vector2Length(wp_ahead.x - wp_current.x, wp_ahead.z - wp_current.z) + + driveTime = (drivingDistance) / (drivingSpeed * (1000 / 3600)) + + --avoid map marker + + if not arrivalTime == true then --only for djikstra, for live travel timer we ignore it + if AutoDrive.getSetting("mapMarkerDetour") > 0 then + for _, mapMarker in pairs(self.mapMarkers) do + if mapMarker.id == start then + driveTime = driveTime + (AutoDrive.getSetting("mapMarkerDetour") / (20 / 3.6)) + break + end + end + end + end + + return driveTime, angle end function ADGraphManager:getDriveTimeForWaypoints(wps, currentWaypoint, maxDrivingSpeed) - local totalTime = 0 - - if - wps ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil and wps[currentWaypoint] ~= nil and - wps[currentWaypoint - 1] == nil - then - totalTime = - totalTime + - self:getDriveTimeBetweenNodes( - wps[currentWaypoint].id, - wps[currentWaypoint + 1].id, - nil, - maxDrivingSpeed, - true - ) --first segment, only 2 points, no angle - currentWaypoint = currentWaypoint + 1 - end - while wps ~= nil and wps[currentWaypoint - 1] ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil do - if wps[currentWaypoint] ~= nil then - totalTime = - totalTime + - self:getDriveTimeBetweenNodes( - wps[currentWaypoint].id, - wps[currentWaypoint + 1].id, - wps[currentWaypoint - 1].id, - maxDrivingSpeed, - true - ) --continuous segments, 3 points for angle - end - currentWaypoint = currentWaypoint + 1 - end - return totalTime * 1.15 + local totalTime = 0 + + if + wps ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil and wps[currentWaypoint] ~= nil and + wps[currentWaypoint - 1] == nil + then + totalTime = + totalTime + + self:getDriveTimeBetweenNodes( + wps[currentWaypoint].id, + wps[currentWaypoint + 1].id, + nil, + maxDrivingSpeed, + true + ) --first segment, only 2 points, no angle + + currentWaypoint = currentWaypoint + 1 + end + while wps ~= nil and wps[currentWaypoint - 1] ~= nil and currentWaypoint ~= nil and wps[currentWaypoint + 1] ~= nil do + if wps[currentWaypoint] ~= nil then + totalTime = + totalTime + + self:getDriveTimeBetweenNodes( + wps[currentWaypoint].id, + wps[currentWaypoint + 1].id, + wps[currentWaypoint - 1].id, + maxDrivingSpeed, + true + ) --continuous segments, 3 points for angle + end + currentWaypoint = currentWaypoint + 1 + end + return totalTime * 1.15 end function ADGraphManager:getHighestConsecutiveIndex() - local toCheckFor = 0 - local consecutive = true - while consecutive == true do - toCheckFor = toCheckFor + 1 - consecutive = false - if self.wayPoints[toCheckFor] ~= nil then - if self.wayPoints[toCheckFor].id == toCheckFor then - consecutive = true - end - end - end + local toCheckFor = 0 + local consecutive = true + while consecutive == true do + toCheckFor = toCheckFor + 1 + consecutive = false + if self.wayPoints[toCheckFor] ~= nil then + if self.wayPoints[toCheckFor].id == toCheckFor then + consecutive = true + end + end + end - return (toCheckFor - 1) + return (toCheckFor - 1) end function ADGraphManager:findMatchingWayPointForVehicle(vehicle) - local startNode = vehicle.ad.frontNode - --returns waypoint closest to vehicle position and with the most suited heading - local x1, _, z1 = getWorldTranslation(startNode) - local rx, _, rz = localDirectionToWorld(startNode, 0, 0, 1) - local vehicleVector = {x = rx, z = rz} - local point = {x = x1, z = z1} + local startNode = vehicle.ad.frontNode + --returns waypoint closest to vehicle position and with the most suited heading + local x1, _, z1 = getWorldTranslation(startNode) + local rx, _, rz = localDirectionToWorld(startNode, 0, 0, 1) + local vehicleVector = {x = rx, z = rz} + local point = {x = x1, z = z1} - local bestPoint, distance = self:findMatchingWayPoint(point, vehicleVector, vehicle:getWayPointIdsInRange(1, 20)) + local bestPoint, distance = self:findMatchingWayPoint(point, vehicleVector, vehicle:getWayPointIdsInRange(1, 20)) - if bestPoint == -1 then - return vehicle:getClosestNotReversedWayPoint() - end + if bestPoint == -1 then + return vehicle:getClosestNotReversedWayPoint() + end - return bestPoint, distance + return bestPoint, distance end function ADGraphManager:findMatchingWayPoint(point, direction, candidates) - candidates = candidates or {} - - local closest = -1 - local distance = -1 - local lastAngleToPoint = -1 - local lastAngleToVehicle = -1 - for _, id in pairs(candidates) do - local toCheck = self.wayPoints[id] - local nextP = nil - local outIndex = 1 - if toCheck.out ~= nil then - if toCheck.out[outIndex] ~= nil then - nextP = self.wayPoints[toCheck.out[outIndex]] - end - - while nextP ~= nil do - local vecToNextPoint = {x = nextP.x - toCheck.x, z = nextP.z - toCheck.z} - local vecToVehicle = {x = toCheck.x - point.x, z = toCheck.z - point.z} - local angleToNextPoint = AutoDrive.angleBetween(direction, vecToNextPoint) - local angleToVehicle = AutoDrive.angleBetween(direction, vecToVehicle) - local dis = MathUtil.vector2Length(toCheck.x - point.x, toCheck.z - point.z) - if - closest == -1 and (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) and - #toCheck.incoming > 0 - then - closest = toCheck.id - distance = dis - lastAngleToPoint = angleToNextPoint - lastAngleToVehicle = angleToVehicle - else - if - #toCheck.incoming > 0 and - (math.abs(angleToNextPoint) + math.abs(angleToVehicle)) < - (math.abs(lastAngleToPoint) + math.abs(lastAngleToVehicle)) and - (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) - then - closest = toCheck.id - distance = dis - lastAngleToPoint = angleToNextPoint - lastAngleToVehicle = angleToVehicle - end - end - - outIndex = outIndex + 1 - if toCheck.out[outIndex] ~= nil then - nextP = self.wayPoints[toCheck.out[outIndex]] - else - nextP = nil - end - end - end - end - - return closest, distance + candidates = candidates or {} + + local closest = -1 + local distance = -1 + local lastAngleToPoint = -1 + local lastAngleToVehicle = -1 + for _, id in pairs(candidates) do + local toCheck = self.wayPoints[id] + local nextP = nil + local outIndex = 1 + if toCheck.out ~= nil then + if toCheck.out[outIndex] ~= nil then + nextP = self.wayPoints[toCheck.out[outIndex]] + end + + while nextP ~= nil do + local vecToNextPoint = {x = nextP.x - toCheck.x, z = nextP.z - toCheck.z} + local vecToVehicle = {x = toCheck.x - point.x, z = toCheck.z - point.z} + local angleToNextPoint = AutoDrive.angleBetween(direction, vecToNextPoint) + local angleToVehicle = AutoDrive.angleBetween(direction, vecToVehicle) + local dis = MathUtil.vector2Length(toCheck.x - point.x, toCheck.z - point.z) + + if + closest == -1 and (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) and + #toCheck.incoming > 0 + then + closest = toCheck.id + distance = dis + lastAngleToPoint = angleToNextPoint + lastAngleToVehicle = angleToVehicle + else + if + #toCheck.incoming > 0 and + (math.abs(angleToNextPoint) + math.abs(angleToVehicle)) < + (math.abs(lastAngleToPoint) + math.abs(lastAngleToVehicle)) and + (math.abs(angleToNextPoint) < 60 and math.abs(angleToVehicle) < 30) + then + closest = toCheck.id + distance = dis + lastAngleToPoint = angleToNextPoint + lastAngleToVehicle = angleToVehicle + end + end + + outIndex = outIndex + 1 + if toCheck.out[outIndex] ~= nil then + nextP = self.wayPoints[toCheck.out[outIndex]] + else + nextP = nil + end + end + end + end + + return closest, distance end function ADGraphManager:getWayPointsInRange(point, rangeMin, rangeMax) - local inRange = {} + local inRange = {} - for _, wp in pairs(self.wayPoints) do - local dis = MathUtil.vector2Length(wp.x - point.x, wp.z - point.z) - if dis < rangeMax and dis > rangeMin then - table.insert(inRange, wp.id) - end - end + for _, wp in pairs(self.wayPoints) do + local dis = MathUtil.vector2Length(wp.x - point.x, wp.z - point.z) + if dis < rangeMax and dis > rangeMin then + table.insert(inRange, wp.id) + end + end - return inRange + return inRange end function ADGraphManager:createNode(id, x, y, z, out, incoming, flags, colors) - return { - id = id, - x = x, - y = y, - z = z, - out = out, - incoming = incoming, - flags = flags, - colors = colors - } + return { + id = id, + x = x, + y = y, + z = z, + out = out, + incoming = incoming, + flags = flags, + colors = colors + } end function ADGraphManager:prepareWayPoints() - local network = self:getWayPoints() - for id, wp in ipairs(network) do - wp.transitMapping = {} - wp.inverseTransitMapping = {} - if #wp.incoming > 0 then --and #wp.out > 0 - for outIndex, outId in ipairs(wp.out) do - wp.inverseTransitMapping[outId] = {} - end - - for inIndex, inId in ipairs(wp.incoming) do - local inPoint = network[inId] - wp.transitMapping[inId] = {} - for outIndex, outId in ipairs(wp.out) do - local outPoint = network[outId] - local angle = - math.abs( - AutoDrive.angleBetween( - {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, - {x = wp.x - inPoint.x, z = wp.z - inPoint.z} - ) - ) - - --print("prep4: " .. outId .. " angle: " .. angle) - - if angle <= 80 then - table.insert(wp.transitMapping[inId], outId) - table.insert(wp.inverseTransitMapping[outId], inId) - else - --Also for reverse routes - but only checked on demand, if angle check fails - local isReverseStart = not table.contains(outPoint.incoming, wp.id) - local isReverseEnd = - table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - table.insert(wp.transitMapping[inId], outId) - table.insert(wp.inverseTransitMapping[outId], inId) - end - end - end - end - end - end - self.preparedWayPoints = true + local network = self:getWayPoints() + for id, wp in ipairs(network) do + wp.transitMapping = {} + wp.inverseTransitMapping = {} + if #wp.incoming > 0 then --and #wp.out > 0 + for outIndex, outId in ipairs(wp.out) do + wp.inverseTransitMapping[outId] = {} + end + + for inIndex, inId in ipairs(wp.incoming) do + local inPoint = network[inId] + wp.transitMapping[inId] = {} + for outIndex, outId in ipairs(wp.out) do + local outPoint = network[outId] + local angle = + math.abs( + AutoDrive.angleBetween( + {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, + {x = wp.x - inPoint.x, z = wp.z - inPoint.z} + ) + ) + + --print("prep4: " .. outId .. " angle: " .. angle) + + if angle <= 80 then + table.insert(wp.transitMapping[inId], outId) + table.insert(wp.inverseTransitMapping[outId], inId) + else + --Also for reverse routes - but only checked on demand, if angle check fails + local isReverseStart = not table.contains(outPoint.incoming, wp.id) + + local isReverseEnd = + table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) + if isReverseStart or isReverseEnd then + table.insert(wp.transitMapping[inId], outId) + table.insert(wp.inverseTransitMapping[outId], inId) + end + end + end + end + end + end + self.preparedWayPoints = true end -- this looks simlar to prepareWayPoints, but is separated to not disturb this calculation -- here also additional checks may be implemented function ADGraphManager:getNetworkErrors() - local network = self:getWayPoints() - for _, wp in ipairs(network) do - wp.errorMapping = {} - - if #wp.incoming > 0 then - - for _, inId in ipairs(wp.incoming) do - local inPoint = network[inId] - local hasGoodAngle = false - for _, outId in ipairs(wp.out) do - if inId ~= outId then - local outPoint = network[outId] - local angle = - math.abs( - AutoDrive.angleBetween( - {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, - {x = wp.x - inPoint.x, z = wp.z - inPoint.z} - ) - ) - if angle > 90 then - local isBadAngle = true - local isReverseStart = not table.contains(outPoint.incoming, wp.id) - local isReverseEnd = - table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) - if isReverseStart or isReverseEnd then - isBadAngle = false - end - if ADGraphManager:isDualRoad(wp, outPoint) then - isBadAngle = false - end - if ADGraphManager:isDualRoad(wp, inPoint) then - isBadAngle = false - end - if isBadAngle then - wp.errorMapping[inId] = outId - end - else - hasGoodAngle = true - end - end - end - if hasGoodAngle then - wp.errorMapping[inId] = nil - end - end - end - end + local network = self:getWayPoints() + for _, wp in ipairs(network) do + wp.errorMapping = {} + + if #wp.incoming > 0 then + for _, inId in ipairs(wp.incoming) do + local inPoint = network[inId] + local hasGoodAngle = false + for _, outId in ipairs(wp.out) do + if inId ~= outId then + local outPoint = network[outId] + local angle = + math.abs( + AutoDrive.angleBetween( + {x = outPoint.x - wp.x, z = outPoint.z - wp.z}, + {x = wp.x - inPoint.x, z = wp.z - inPoint.z} + ) + ) + if angle > 80 then + local isBadAngle = true + local isReverseStart = not table.contains(outPoint.incoming, wp.id) + + local isReverseEnd = + table.contains(outPoint.incoming, wp.id) and not table.contains(wp.incoming, inPoint.id) + if isReverseStart or isReverseEnd then + isBadAngle = false + end + if ADGraphManager:isDualRoad(wp, outPoint) then + isBadAngle = false + end + if ADGraphManager:isDualRoad(wp, inPoint) then + isBadAngle = false + end + if isBadAngle then + wp.errorMapping[inId] = outId + end + else + hasGoodAngle = true + end + end + end + if hasGoodAngle then + wp.errorMapping[inId] = nil + end + end + end + end end function ADGraphManager:checkResetVehicleDestinations(destination) - if destination == nil or destination < 1 then - return - end - -- remove deleted marker in vehicle destinations - for _, vehicle in pairs(g_currentMission.vehicles) do - if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil then - if destination == vehicle.ad.stateModule:getFirstMarkerId() then - local firstMarker = ADGraphManager:getMapMarkerById(1) - if firstMarker ~= nil then - vehicle.ad.stateModule:setFirstMarker(1) - end - end - if destination == vehicle.ad.stateModule:getSecondMarkerId() then - local secondMarker = ADGraphManager:getMapMarkerById(1) - if secondMarker ~= nil then - vehicle.ad.stateModule:setSecondMarker(1) - end - end - end - end + if destination == nil or destination < 1 then + return + end + -- remove deleted marker in vehicle destinations + for _, vehicle in pairs(g_currentMission.vehicles) do + if vehicle.ad ~= nil and vehicle.ad.stateModule ~= nil then + if destination == vehicle.ad.stateModule:getFirstMarkerId() then + local firstMarker = ADGraphManager:getMapMarkerById(1) + if firstMarker ~= nil then + vehicle.ad.stateModule:setFirstMarker(1) + end + end + if destination == vehicle.ad.stateModule:getSecondMarkerId() then + local secondMarker = ADGraphManager:getMapMarkerById(1) + if secondMarker ~= nil then + vehicle.ad.stateModule:setSecondMarker(1) + end + end + end + end end -- this function is used to remove the debug markers function ADGraphManager:removeDebugMarkers() - local foundDebugMarker = false - local index = #self:getMapMarkers() - while index >= 1 do - local mapMarker = self:getMapMarkerById(index) - if mapMarker ~= nil and mapMarker.isADDebug == true then - -- remove debug marker from vehicle destinations - ADGraphManager:checkResetVehicleDestinations(mapMarker.markerIndex) - table.remove(self.mapMarkers, mapMarker.markerIndex) - foundDebugMarker = true - end - index = index - 1 - end - if self:getGroupByName(ADGraphManager.debugGroupName) ~= nil then - -- sendEvent should be false as function is initiated on server and all clients via debug setting - self:removeGroup(ADGraphManager.debugGroupName, false) - end - --Readjust stored markerIndex values to point to corrected ID - if foundDebugMarker == true then - if #self:getMapMarkers() > 0 then - for markerID, marker in pairs(self.mapMarkers) do - marker.markerIndex = markerID - end - end - end + local foundDebugMarker = false + local index = #self:getMapMarkers() + while index >= 1 do + local mapMarker = self:getMapMarkerById(index) + if mapMarker ~= nil and mapMarker.isADDebug == true then + -- remove debug marker from vehicle destinations + ADGraphManager:checkResetVehicleDestinations(mapMarker.markerIndex) + table.remove(self.mapMarkers, mapMarker.markerIndex) + foundDebugMarker = true + end + index = index - 1 + end + if self:getGroupByName(ADGraphManager.debugGroupName) ~= nil then + -- sendEvent should be false as function is initiated on server and all clients via debug setting + self:removeGroup(ADGraphManager.debugGroupName, false) + end + --Readjust stored markerIndex values to point to corrected ID + if foundDebugMarker == true then + if #self:getMapMarkers() > 0 then + for markerID, marker in pairs(self.mapMarkers) do + marker.markerIndex = markerID + end + end + end end -- create debug markers for waypoints issues function ADGraphManager:createDebugMarkers(updateMap) - local overallnumberWP = self:getWayPointsCount() - if overallnumberWP < 3 then - return - end - ADGraphManager:getNetworkErrors() - local network = self:getWayPoints() - - local shouldUpdateMap = updateMap - if shouldUpdateMap == nil then - shouldUpdateMap = true - end - - if shouldUpdateMap == true then - self:removeDebugMarkers() - end - - if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_ROADNETWORKINFO) then - -- create markers for open ends - if self:getGroupByName(ADGraphManager.debugGroupName) == nil then - -- sendEvent should be false as function is initiated on server and all clients via debug setting - self:addGroup(ADGraphManager.debugGroupName, false) - end - local count1 = 1 - local count2 = 1 - local count3 = 1 - local count4 = 1 - local count9 = 1 - local mapMarkerCounter = #self:getMapMarkers() + 1 - for i, wp in pairs(network) do - wp.foundError = false - -- mark wayPoint without outgoing connection - if #wp.out == 0 then - if wp ~= nil then - if not ADGraphManager:getMapMarkerByWayPointId(wp.id) then - local debugMapMarkerName = "1_" .. tostring(count1) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count1 = count1 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- mark reverse wayPoint with less angle to be reverse -> wrong connection in network - if not wp.foundError and wp.incoming ~= nil then - for _, wp_in in pairs(wp.incoming) do - if wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - -- if self:isReverseStart(wp,self:getWayPointById(wp_1)) then - local isWrongReverseStart = - self:checkForWrongReverseStart( - self:getWayPointById(wp_in), - wp, - self:getWayPointById(wp_out) - ) - - if isWrongReverseStart then - local debugMapMarkerName = "2_" .. tostring(count2) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count2 = count2 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - end - end - - -- mark wayPoint without incoming connection - if not wp.foundError and wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - local missingIncoming = self:checkForMissingIncoming(wp, self:getWayPointById(wp_out)) - if missingIncoming then - local debugMapMarkerName = "3_" .. tostring(count3) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count3 = count3 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- mark dual wayPoint with death end - if not wp.foundError and wp.out ~= nil then - for _, wp_out in pairs(wp.out) do - local missingIncoming = self:checkForMissingDualConnection(wp) - if missingIncoming then - local debugMapMarkerName = "4_" .. tostring(count4) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count4 = count4 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - - -- possible other errors - if not wp.foundError then - local wrongAngle, count1, count2 = self:checkForOtherErrors(wp) - if wrongAngle then - local debugMapMarkerName = "9_" .. tostring(count9) - - -- create the mapMarker - local mapMarker = {} - mapMarker.name = debugMapMarkerName - mapMarker.group = ADGraphManager.debugGroupName - mapMarker.markerIndex = mapMarkerCounter - mapMarker.id = wp.id - mapMarker.isADDebug = true - self:setMapMarker(mapMarker) - - wp.foundError = true - count9 = count9 + 1 - mapMarkerCounter = mapMarkerCounter + 1 - end - end - end - end - if shouldUpdateMap == true then - AutoDrive:notifyDestinationListeners() - end + local overallnumberWP = self:getWayPointsCount() + if overallnumberWP < 3 then + return + end + ADGraphManager:getNetworkErrors() + local network = self:getWayPoints() + + local shouldUpdateMap = updateMap + if shouldUpdateMap == nil then + shouldUpdateMap = true + end + + if shouldUpdateMap == true then + self:removeDebugMarkers() + end + + if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_ROADNETWORKINFO) then + -- create markers for open ends + if self:getGroupByName(ADGraphManager.debugGroupName) == nil then + -- sendEvent should be false as function is initiated on server and all clients via debug setting + self:addGroup(ADGraphManager.debugGroupName, false) + end + local count1 = 1 + local count2 = 1 + local count3 = 1 + local count4 = 1 + local count9 = 1 + local mapMarkerCounter = #self:getMapMarkers() + 1 + for i, wp in pairs(network) do + wp.foundError = false + -- mark wayPoint without outgoing connection + if #wp.out == 0 then + if wp ~= nil then + if not ADGraphManager:getMapMarkerByWayPointId(wp.id) then + local debugMapMarkerName = "1_" .. tostring(count1) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count1 = count1 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- mark reverse wayPoint with less angle to be reverse -> wrong connection in network + if not wp.foundError and wp.incoming ~= nil then + for _, wp_in in pairs(wp.incoming) do + if wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + -- if self:isReverseStart(wp,self:getWayPointById(wp_1)) then + local isWrongReverseStart = + self:checkForWrongReverseStart( + self:getWayPointById(wp_in), + wp, + self:getWayPointById(wp_out) + ) + + if isWrongReverseStart then + local debugMapMarkerName = "2_" .. tostring(count2) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count2 = count2 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + end + end + + -- mark wayPoint without incoming connection + if not wp.foundError and wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + local missingIncoming = self:checkForMissingIncoming(wp, self:getWayPointById(wp_out)) + if missingIncoming then + local debugMapMarkerName = "3_" .. tostring(count3) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count3 = count3 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- mark dual wayPoint with death end + if not wp.foundError and wp.out ~= nil then + for _, wp_out in pairs(wp.out) do + local missingIncoming = self:checkForMissingDualConnection(wp) + if missingIncoming then + local debugMapMarkerName = "4_" .. tostring(count4) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count4 = count4 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + + -- possible other errors + if not wp.foundError then + local wrongAngle, count1, count2 = self:checkForOtherErrors(wp) + if wrongAngle then + local debugMapMarkerName = "9_" .. tostring(count9) + + -- create the mapMarker + local mapMarker = {} + mapMarker.name = debugMapMarkerName + mapMarker.group = ADGraphManager.debugGroupName + mapMarker.markerIndex = mapMarkerCounter + mapMarker.id = wp.id + mapMarker.isADDebug = true + self:setMapMarker(mapMarker) + + wp.foundError = true + count9 = count9 + 1 + mapMarkerCounter = mapMarkerCounter + 1 + end + end + end + end + if shouldUpdateMap == true then + AutoDrive:notifyDestinationListeners() + end end function ADGraphManager:checkForWrongReverseStart(wp_ref, wp_current, wp_ahead) - local reverseStart = false + local reverseStart = false + + if wp_ref == nil or wp_current == nil or wp_ahead == nil then + return reverseStart + end - if wp_ref == nil or wp_current == nil or wp_ahead == nil then - return reverseStart - end + local isReverseStart = wp_ahead.incoming ~= nil and (not table.contains(wp_ahead.incoming, wp_current.id)) - local isReverseStart = wp_ahead.incoming ~= nil and (not table.contains(wp_ahead.incoming, wp_current.id)) - isReverseStart = - isReverseStart and not (wp_current.incoming ~= nil and (not table.contains(wp_current.incoming, wp_ref.id))) + isReverseStart = + isReverseStart and not (wp_current.incoming ~= nil and (not table.contains(wp_current.incoming, wp_ref.id))) - local angle = - AutoDrive.angleBetween( - {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, - {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} - ) + local angle = + AutoDrive.angleBetween( + {x = wp_ahead.x - wp_current.x, z = wp_ahead.z - wp_current.z}, + {x = wp_current.x - wp_ref.x, z = wp_current.z - wp_ref.z} + ) - angle = math.abs(angle) - if angle <= 90 and isReverseStart then - reverseStart = true - end + angle = math.abs(angle) + if angle <= 90 and isReverseStart then + reverseStart = true + end - return reverseStart + return reverseStart end function ADGraphManager:checkForMissingIncoming(wp_current) - local ret = false - - if wp_current == nil then - return ret - end - local reverseFound = false - if wp_current.incoming ~= nil and #wp_current.incoming == 0 then - -- search for a possible reverse connection - for _, wp in pairs(self.wayPoints) do - if wp.out ~= nil and wp_current.id ~= nil then - if table.contains(wp.out, wp_current.id) then - reverseFound = true - break - end - end - end - if not reverseFound then - -- the waypoint has no incoming connection - ret = true - end - end - return ret + local ret = false + + if wp_current == nil then + return ret + end + local reverseFound = false + if wp_current.incoming ~= nil and #wp_current.incoming == 0 then + -- search for a possible reverse connection + for _, wp in pairs(self.wayPoints) do + if wp.out ~= nil and wp_current.id ~= nil then + if table.contains(wp.out, wp_current.id) then + reverseFound = true + break + end + end + end + if not reverseFound then + -- the waypoint has no incoming connection + ret = true + end + end + return ret end function ADGraphManager:checkForMissingDualConnection(wp_current) - local ret = false - - if wp_current == nil then - return ret - end - - if wp_current.incoming ~= nil and wp_current.out ~= nil and #wp_current.incoming == 1 and #wp_current.out == 1 then - if wp_current.incoming[1] ~= nil and wp_current.out[1] ~= nil and wp_current.incoming[1] == wp_current.out[1] then - local mapMarker = ADGraphManager:getMapMarkerByWayPointId(wp_current.id) - -- only dual waypoint without Marker is an error - if mapMarker == nil then - -- dual waypoint - ret = true - end - - -- search for a possible reverse connection - for _, wp in pairs(self.wayPoints) do - if self:isReverseRoad(wp, wp_current) then - -- reverse connection to wayPoint is OK - no death end - ret = false - break - end - end - end - end - return ret + local ret = false + + if wp_current == nil then + return ret + end + + if wp_current.incoming ~= nil and wp_current.out ~= nil and #wp_current.incoming == 1 and #wp_current.out == 1 then + if wp_current.incoming[1] ~= nil and wp_current.out[1] ~= nil and wp_current.incoming[1] == wp_current.out[1] then + local mapMarker = ADGraphManager:getMapMarkerByWayPointId(wp_current.id) + -- only dual waypoint without Marker is an error + if mapMarker == nil then + -- dual waypoint + ret = true + end + + -- search for a possible reverse connection + for _, wp in pairs(self.wayPoints) do + if self:isReverseRoad(wp, wp_current) then + -- reverse connection to wayPoint is OK - no death end + ret = false + break + end + end + end + end + return ret end function ADGraphManager:checkForOtherErrors(wp) - local ret = false - if wp == nil then - return true - end - -- TODO - -- local network = self:getWayPoints() + local ret = false + if wp == nil then + return true + end + -- TODO + + -- local network = self:getWayPoints() - for _, inId in ipairs(wp.incoming) do - ret = ret or (wp.errorMapping[inId] ~= nil) - end - return ret + for _, inId in ipairs(wp.incoming) do + ret = ret or (wp.errorMapping[inId] ~= nil) + end + return ret end function ADGraphManager:toggleWayPointAsSubPrio(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil then - if self:getIsPointSubPrio(wayPointId) then - wayPoint.flags = wayPoint.flags - AutoDrive.FLAG_SUBPRIO - else - wayPoint.flags = wayPoint.flags + AutoDrive.FLAG_SUBPRIO - end - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil then + if self:getIsPointSubPrio(wayPointId) then + wayPoint.flags = wayPoint.flags - AutoDrive.FLAG_SUBPRIO + else + wayPoint.flags = wayPoint.flags + AutoDrive.FLAG_SUBPRIO + end + end - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, wayPoint.flags) - self:markChanges() + self:markChanges() end function ADGraphManager:getIsPointSubPrio(wayPointId) - local wayPoint = self:getWayPointById(wayPointId) + local wayPoint = self:getWayPointById(wayPointId) - return wayPoint ~= nil and bitAND(wayPoint.flags, AutoDrive.FLAG_SUBPRIO) > 0 + return wayPoint ~= nil and bitAND(wayPoint.flags, AutoDrive.FLAG_SUBPRIO) > 0 end function ADGraphManager:setWayPointFlags(wayPointId, flags) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint ~= nil and flags ~= nil then - self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, flags) + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint ~= nil and flags ~= nil then + self:moveWayPoint(wayPointId, wayPoint.x, wayPoint.y, wayPoint.z, flags) - self:markChanges() - end + self:markChanges() + end end function ADGraphManager:getBestOutPoints(vehicle, nodeId) - local neighbors = {} - - local x, y, z = getWorldTranslation(vehicle.components[1].node) - local toCheck = self.wayPoints[nodeId] - local baseDistance = MathUtil.vector2Length(toCheck.x - x, toCheck.z - z) - - if toCheck.out ~= nil then - for _, outId in pairs(toCheck.out) do - local out = self.wayPoints[outId] - local _, _, offsetZ = worldToLocal(vehicle.components[1].node, out.x, y, out.z) - if out ~= nil and baseDistance < MathUtil.vector2Length(out.x - x, out.z - z) and offsetZ > 0 then - table.insert(neighbors, out.id) - end - end - end + local neighbors = {} + + local x, y, z = getWorldTranslation(vehicle.components[1].node) + local toCheck = self.wayPoints[nodeId] + local baseDistance = MathUtil.vector2Length(toCheck.x - x, toCheck.z - z) + + if toCheck.out ~= nil then + for _, outId in pairs(toCheck.out) do + local out = self.wayPoints[outId] + local _, _, offsetZ = worldToLocal(vehicle.components[1].node, out.x, y, out.z) + if out ~= nil and baseDistance < MathUtil.vector2Length(out.x - x, out.z - z) and offsetZ > 0 then + table.insert(neighbors, out.id) + end + end + end - return neighbors + return neighbors end --[[ TODO: at the moment only supported for directions: -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 -reverse connection not tested !!! +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 +reverse connection not tested !!! ]] function ADGraphManager:getIsWayPointInSection(wayPointId, direction) - local wayPoint = self:getWayPointById(wayPointId) - if wayPoint == nil then - return false - end - - local connectedIds = {} - for _, incomingId in pairs(wayPoint.incoming) do - if not table.contains(connectedIds, incomingId) then - table.insert(connectedIds, incomingId) - end - end - for _, outId in pairs(wayPoint.out) do - if not table.contains(connectedIds, outId) then - table.insert(connectedIds, outId) - end - end - if #connectedIds == 2 and not (direction == 4) then - -- single or dual connection - -- check for reverse wayPoint -> if found as connected wayPoint the current is reported as not in section - -- this is used to treat this wayPoint as last in a section - local foundReverse = false - for _, connectedId in pairs(connectedIds) do - local connectedWayPoint = self:getWayPointById(connectedId) - foundReverse = foundReverse or ADGraphManager:isReverseRoad(wayPoint, connectedWayPoint) - end - return not foundReverse - elseif #connectedIds == 1 and direction == 4 then - -- single end -> used for reverse section TODO: to be checked - return true - else - return false - end + local wayPoint = self:getWayPointById(wayPointId) + if wayPoint == nil then + return false + end + + local connectedIds = {} + for _, incomingId in pairs(wayPoint.incoming) do + if not table.contains(connectedIds, incomingId) then + table.insert(connectedIds, incomingId) + end + end + for _, outId in pairs(wayPoint.out) do + if not table.contains(connectedIds, outId) then + table.insert(connectedIds, outId) + end + end + if #connectedIds == 2 and not (direction == 4) then + -- single or dual connection + -- check for reverse wayPoint -> if found as connected wayPoint the current is reported as not in section + -- this is used to treat this wayPoint as last in a section + local foundReverse = false + for _, connectedId in pairs(connectedIds) do + local connectedWayPoint = self:getWayPointById(connectedId) + foundReverse = foundReverse or ADGraphManager:isReverseRoad(wayPoint, connectedWayPoint) + end + return not foundReverse + elseif #connectedIds == 1 and direction == 4 then + -- single end -> used for reverse section TODO: to be checked + return true + else + return false + end end --[[ -no junction return 0 -single connection ahead return 1 -single connection backward (not reverse) return 2 -dual connection return 3 -reverse connection return 4 +no junction return 0 +single connection ahead return 1 +single connection backward (not reverse) return 2 +dual connection return 3 +reverse connection return 4 ]] function ADGraphManager:getIsWayPointJunction(startId, targetId) - if startId <= 0 or targetId <= 0 then - return 0 - end - - local wayPointStart = self:getWayPointById(startId) - local wayPointTarget = self:getWayPointById(targetId) - if wayPointTarget == nil or wayPointStart == nil then - return 0 - end - - local startConnectedIds = {} - local startConnectedIdsIncoming = {} - local startConnectedIdsOut = {} - local targetConnectedIds = {} - local targetConnectedIdsIncoming = {} - local targetConnectedIdsOut = {} - - local function addConnections(input, connections) - if connections ~= nil and #input > 0 then - for _, connectedId in pairs(input) do - if not table.contains(connections, connectedId) then - table.insert(connections, connectedId) - end - end - end - end - - addConnections(wayPointStart.incoming, startConnectedIds) - addConnections(wayPointStart.out, startConnectedIds) - addConnections(wayPointStart.incoming, startConnectedIdsIncoming) - addConnections(wayPointStart.out, startConnectedIdsOut) - - addConnections(wayPointTarget.incoming, targetConnectedIds) - addConnections(wayPointTarget.out, targetConnectedIds) - addConnections(wayPointTarget.incoming, targetConnectedIdsIncoming) - addConnections(wayPointTarget.out, targetConnectedIdsOut) - - if - not (table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId)) and - table.contains(startConnectedIdsOut, targetId) and - table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- one way ahead - return 1 - elseif - table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and - not (table.contains(startConnectedIdsOut, targetId) and table.contains(targetConnectedIdsIncoming, startId)) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- one way backward - return 2 - elseif - table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and - table.contains(startConnectedIdsOut, targetId) and - table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) and - (#targetConnectedIds == 2) - then - -- two way - return 3 - elseif - table.contains(startConnectedIdsOut, targetId) and not table.contains(targetConnectedIdsIncoming, startId) and - (#startConnectedIds >= 3) - then - -- reverse - return 4 - else - return 0 - end + if startId <= 0 or targetId <= 0 then + return 0 + end + + local wayPointStart = self:getWayPointById(startId) + local wayPointTarget = self:getWayPointById(targetId) + if wayPointTarget == nil or wayPointStart == nil then + return 0 + end + + local startConnectedIds = {} + local startConnectedIdsIncoming = {} + local startConnectedIdsOut = {} + local targetConnectedIds = {} + local targetConnectedIdsIncoming = {} + local targetConnectedIdsOut = {} + + local function addConnections(input, connections) + if connections ~= nil and #input > 0 then + for _, connectedId in pairs(input) do + if not table.contains(connections, connectedId) then + table.insert(connections, connectedId) + end + end + end + end + + addConnections(wayPointStart.incoming, startConnectedIds) + addConnections(wayPointStart.out, startConnectedIds) + addConnections(wayPointStart.incoming, startConnectedIdsIncoming) + addConnections(wayPointStart.out, startConnectedIdsOut) + + addConnections(wayPointTarget.incoming, targetConnectedIds) + addConnections(wayPointTarget.out, targetConnectedIds) + addConnections(wayPointTarget.incoming, targetConnectedIdsIncoming) + addConnections(wayPointTarget.out, targetConnectedIdsOut) + + if + not (table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId)) and + table.contains(startConnectedIdsOut, targetId) and + table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- one way ahead + return 1 + elseif + table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and + not (table.contains(startConnectedIdsOut, targetId) and table.contains(targetConnectedIdsIncoming, startId)) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- one way backward + return 2 + elseif + table.contains(startConnectedIdsIncoming, targetId) and table.contains(targetConnectedIdsOut, startId) and + table.contains(startConnectedIdsOut, targetId) and + table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) and + (#targetConnectedIds == 2) + then + -- two way + return 3 + elseif + table.contains(startConnectedIdsOut, targetId) and not table.contains(targetConnectedIdsIncoming, startId) and + (#startConnectedIds >= 3) + then + -- reverse + return 4 + else + return 0 + end end --[[ at the moment only supported for directions: -single connection ahead 1 -single connection backward (not reverse) 2 -dual connection 3 +single connection ahead 1 +single connection backward (not reverse) 2 +dual connection 3 ]] function ADGraphManager:getWayPointsInSection(startId, targetId, direction) - local previousId = startId - local nextId = targetId - local sectionWayPoints = {} - - if direction < 1 or direction > 3 then - return sectionWayPoints - end - - if not table.contains(sectionWayPoints, previousId) then - -- add the first wayPoint - table.insert(sectionWayPoints, previousId) - end - local count = 1 - while count < ADGraphManager.MAX_POINTS_IN_SECTION and self:getIsWayPointInSection(nextId, direction) do - count = count + 1 - table.insert(sectionWayPoints, nextId) - local nextwayPoint = self:getWayPointById(nextId) - - if nextwayPoint.incoming[1] ~= nil and nextwayPoint.incoming[1] ~= previousId then - previousId = nextId - nextId = nextwayPoint.incoming[1] - elseif nextwayPoint.incoming[2] ~= nil and nextwayPoint.incoming[2] ~= previousId then - previousId = nextId - nextId = nextwayPoint.incoming[2] - elseif nextwayPoint.out[1] ~= nil and nextwayPoint.out[1] ~= previousId then - previousId = nextId - nextId = nextwayPoint.out[1] - elseif nextwayPoint.out[2] ~= nil and nextwayPoint.out[2] ~= previousId then - previousId = nextId - nextId = nextwayPoint.out[2] - else - break - end - end - if not table.contains(sectionWayPoints, nextId) then - -- add the last wayPoint - table.insert(sectionWayPoints, nextId) - end - return sectionWayPoints + local previousId = startId + local nextId = targetId + local sectionWayPoints = {} + + if direction < 1 or direction > 3 then + return sectionWayPoints + end + + if not table.contains(sectionWayPoints, previousId) then + -- add the first wayPoint + table.insert(sectionWayPoints, previousId) + end + local count = 1 + while count < ADGraphManager.MAX_POINTS_IN_SECTION and self:getIsWayPointInSection(nextId, direction) do + count = count + 1 + table.insert(sectionWayPoints, nextId) + local nextwayPoint = self:getWayPointById(nextId) + + if nextwayPoint.incoming[1] ~= nil and nextwayPoint.incoming[1] ~= previousId then + previousId = nextId + nextId = nextwayPoint.incoming[1] + elseif nextwayPoint.incoming[2] ~= nil and nextwayPoint.incoming[2] ~= previousId then + previousId = nextId + nextId = nextwayPoint.incoming[2] + elseif nextwayPoint.out[1] ~= nil and nextwayPoint.out[1] ~= previousId then + previousId = nextId + nextId = nextwayPoint.out[1] + elseif nextwayPoint.out[2] ~= nil and nextwayPoint.out[2] ~= previousId then + previousId = nextId + nextId = nextwayPoint.out[2] + else + break + end + end + if not table.contains(sectionWayPoints, nextId) then + -- add the last wayPoint + table.insert(sectionWayPoints, nextId) + end + return sectionWayPoints end function ADGraphManager:setConnectionBetweenWayPointsInSection(vehicle, direction) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - for i = 1, #vehicle.ad.sectionWayPoints - 1 do - ADGraphManager:setConnectionBetween( - ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i]), - ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i + 1]), - direction - ) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + for i = 1, #vehicle.ad.sectionWayPoints - 1 do + ADGraphManager:setConnectionBetween( + ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i]), + ADGraphManager:getWayPointById(vehicle.ad.sectionWayPoints[i + 1]), + direction + ) + end + end end function ADGraphManager:setWayPointsFlagsInSection(vehicle, flags) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - for i = 2, #vehicle.ad.sectionWayPoints - 1 do - -- do not set start and end wayPoint as these are the connections to other lines - ADGraphManager:setWayPointFlags(vehicle.ad.sectionWayPoints[i], flags) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + for i = 2, #vehicle.ad.sectionWayPoints - 1 do + -- do not set start and end wayPoint as these are the connections to other lines + ADGraphManager:setWayPointFlags(vehicle.ad.sectionWayPoints[i], flags) + end + end end function ADGraphManager:deleteWayPointsInSection(vehicle) - if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then - local pointsToDelete = {} - for i = 2, #vehicle.ad.sectionWayPoints - 1 do - table.insert(pointsToDelete, vehicle.ad.sectionWayPoints[i]) - end - - -- delete last wayPoint if not connected to a junction - local lastWayPointID = vehicle.ad.sectionWayPoints[#vehicle.ad.sectionWayPoints] - local lastWayPoint = self:getWayPointById(lastWayPointID) - local connectedIds = {} - for _, incomingId in pairs(lastWayPoint.incoming) do - if not table.contains(connectedIds, incomingId) then - table.insert(connectedIds, incomingId) - end - end - for _, outId in pairs(lastWayPoint.out) do - if not table.contains(connectedIds, outId) then - table.insert(connectedIds, outId) - end - end - if #connectedIds == 1 then - table.insert(pointsToDelete, lastWayPointID) - end - - -- sort the wayPoints to delete in descant order to ensure correct linkage deletion - local sort_func = function(a, b) - return a > b - end - table.sort(pointsToDelete, sort_func) - for i = 1, #pointsToDelete do - ADGraphManager:removeWayPoint(pointsToDelete[i]) - end - end + if vehicle.ad.sectionWayPoints ~= nil and #vehicle.ad.sectionWayPoints > 2 then + local pointsToDelete = {} + for i = 2, #vehicle.ad.sectionWayPoints - 1 do + table.insert(pointsToDelete, vehicle.ad.sectionWayPoints[i]) + end + + -- delete last wayPoint if not connected to a junction + local lastWayPointID = vehicle.ad.sectionWayPoints[#vehicle.ad.sectionWayPoints] + local lastWayPoint = self:getWayPointById(lastWayPointID) + local connectedIds = {} + for _, incomingId in pairs(lastWayPoint.incoming) do + if not table.contains(connectedIds, incomingId) then + table.insert(connectedIds, incomingId) + end + end + for _, outId in pairs(lastWayPoint.out) do + if not table.contains(connectedIds, outId) then + table.insert(connectedIds, outId) + end + end + if #connectedIds == 1 then + table.insert(pointsToDelete, lastWayPointID) + end + + -- sort the wayPoints to delete in descant order to ensure correct linkage deletion + local sort_func = function(a, b) + return a > b + end + table.sort(pointsToDelete, sort_func) + for i = 1, #pointsToDelete do + ADGraphManager:removeWayPoint(pointsToDelete[i]) + end + end end function ADGraphManager:deleteColorSelectionWayPoints() - -- delete the color selection wayPoints in descant order to ensure correct linkage deletion - local actualID = self:getWayPointsCount() - local count = 1 - while count < (10000000) and actualID > 1 do - count = count + 1 - local wp = ADGraphManager:getWayPointById(actualID) - if wp.colors ~= nil then - ADGraphManager:removeWayPoint(actualID, false) - actualID = self:getWayPointsCount() -- removed a wayPoint so check from highest again - else - actualID = actualID - 1 - end - end + -- delete the color selection wayPoints in descant order to ensure correct linkage deletion + local actualID = self:getWayPointsCount() + local count = 1 + while count < (10000000) and actualID > 1 do + count = count + 1 + local wp = ADGraphManager:getWayPointById(actualID) + if wp.colors ~= nil then + ADGraphManager:removeWayPoint(actualID, false) + actualID = self:getWayPointsCount() -- removed a wayPoint so check from highest again + else + actualID = actualID - 1 + end + end end function ADGraphManager:removeNodesWithFlag(flagToRemove) - local network = self:getWayPoints() - local pointsToDelete = {} - for i, wp in pairs(network) do - if bitAND(wp.flags, flagToRemove) > 0 then - table.insert(pointsToDelete, wp.id) - end - end + local network = self:getWayPoints() + local pointsToDelete = {} + for i, wp in pairs(network) do + if bitAND(wp.flags, flagToRemove) > 0 then + table.insert(pointsToDelete, wp.id) + end + end - -- sort the wayPoints to delete in descant order to ensure correct linkage deletion - local sort_func = function(a, b) - return a > b - end - table.sort(pointsToDelete, sort_func) + -- sort the wayPoints to delete in descant order to ensure correct linkage deletion + local sort_func = function(a, b) + return a > b + end + table.sort(pointsToDelete, sort_func) - for i = 1, #pointsToDelete do - ADGraphManager:removeWayPoint(pointsToDelete[i]) - end + for i = 1, #pointsToDelete do + ADGraphManager:removeWayPoint(pointsToDelete[i]) + end end function ADGraphManager:createSplineConnection(start, waypoints, target, dualConnection, sendEvent) - if sendEvent == nil or sendEvent == true then - -- Propagating connection toggling all over the network + if sendEvent == nil or sendEvent == true then + -- Propagating connection toggling all over the network CreateSplineConnectionEvent.sendEvent(start, waypoints, target, dualConnection) - else - local lastId = start - local lastHeight = ADGraphManager:getWayPointById(start).y + else + local lastId = start + local lastHeight = ADGraphManager:getWayPointById(start).y local subPrio = self:getIsPointSubPrio(start) or self:getIsPointSubPrio(target) - + for _, wp in pairs(waypoints) do - if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc - wp.y = lastHeight - end - self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) - local createdId = self:getWayPointsCount() + if math.abs(wp.y - lastHeight) > 1 then -- prevent point dropping into the ground in case of bridges etc + wp.y = lastHeight + end + self:createWayPoint(wp.x, wp.y, wp.z, sendEvent) + local createdId = self:getWayPointsCount() if subPrio then ADGraphManager:toggleWayPointAsSubPrio(createdId) end - self:toggleConnectionBetween( - ADGraphManager:getWayPointById(lastId), - ADGraphManager:getWayPointById(createdId), - false, + + self:toggleConnectionBetween( + ADGraphManager:getWayPointById(lastId), + ADGraphManager:getWayPointById(createdId), + false, dualConnection, - false - ) - lastId = createdId - lastHeight = wp.y - end + false + ) + lastId = createdId + lastHeight = wp.y + end - local wp = self:getWayPointById(lastId) + local wp = self:getWayPointById(lastId) self:toggleConnectionBetween(wp, self:getWayPointById(target), false, dualConnection, false) - end + end end function ADGraphManager:getNextTargetAlphabetically(markerId) - local sortedMarkers = self:getSortedMarkers() - for i, markerIdIter in ipairs(sortedMarkers) do - if markerIdIter == markerId then - if i < #sortedMarkers then - return sortedMarkers[i + 1] - else - return sortedMarkers[1] - end - end - end - return markerId + local sortedMarkers = self:getSortedMarkers() + for i, markerIdIter in ipairs(sortedMarkers) do + if markerIdIter == markerId then + if i < #sortedMarkers then + return sortedMarkers[i + 1] + else + return sortedMarkers[1] + end + end + end + return markerId end function ADGraphManager:getPreviousTargetAlphabetically(markerId) - local sortedMarkers = self:getSortedMarkers() - for i, markerIdIter in ipairs(sortedMarkers) do - if markerIdIter == markerId then - if i > 1 then - return sortedMarkers[i - 1] - else - return sortedMarkers[#sortedMarkers] - end - end - end - return markerId + local sortedMarkers = self:getSortedMarkers() + for i, markerIdIter in ipairs(sortedMarkers) do + if markerIdIter == markerId then + if i > 1 then + return sortedMarkers[i - 1] + else + return sortedMarkers[#sortedMarkers] + end + end + end + return markerId end function ADGraphManager:getSortedMarkers() - local useFolders = AutoDrive.getSetting("useFolders") - - local groups = {} - local groupsToSortedIndex = {} - if useFolders then - groups, groupsToSortedIndex = self:sortGroups(groups) - end - - if #groups == 0 then - groups[1] = {} - groupsToSortedIndex[1] = "All" - end - - for markerID, marker in pairs(self:getMapMarkers()) do - if useFolders then - table.insert(groups[groupsToSortedIndex[marker.group]], {displayName = marker.name, returnValue = markerID}) - else - table.insert(groups[1], {displayName = marker.name, returnValue = markerID}) - end - end - - local sort_func = function(a, b) - a = tostring(a.displayName):lower() - b = tostring(b.displayName):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end - - for groupId, _ in pairs(groups) do - table.sort(groups[groupId], sort_func) - end - - local allInOneTable = {} - for groupId, entries in pairs(groups) do - for _, marker in pairs(entries) do - allInOneTable[#allInOneTable + 1] = marker.returnValue - end - end - - return allInOneTable + local useFolders = AutoDrive.getSetting("useFolders") + + local groups = {} + local groupsToSortedIndex = {} + if useFolders then + groups, groupsToSortedIndex = self:sortGroups(groups) + end + + if #groups == 0 then + groups[1] = {} + groupsToSortedIndex[1] = "All" + end + + for markerID, marker in pairs(self:getMapMarkers()) do + if useFolders then + table.insert(groups[groupsToSortedIndex[marker.group]], {displayName = marker.name, returnValue = markerID}) + else + table.insert(groups[1], {displayName = marker.name, returnValue = markerID}) + end + end + + local sort_func = function(a, b) + a = tostring(a.displayName):lower() + b = tostring(b.displayName):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end + + for groupId, _ in pairs(groups) do + table.sort(groups[groupId], sort_func) + end + + local allInOneTable = {} + for groupId, entries in pairs(groups) do + for _, marker in pairs(entries) do + allInOneTable[#allInOneTable + 1] = marker.returnValue + end + end + + return allInOneTable end function ADGraphManager:sortGroups(groups) - groups = {} - groups[1] = {} - - local sort_func = function(a, b) - a = tostring(a):lower() - b = tostring(b):lower() - local patt = "^(.-)%s*(%d+)$" - local _, _, col1, num1 = a:find(patt) - local _, _, col2, num2 = b:find(patt) - if (col1 and col2) and col1 == col2 then - return tonumber(num1) < tonumber(num2) - end - return a < b - end - - local groupTable = {} - for groupName, groupID in pairs(ADGraphManager:getGroups()) do - table.insert(groupTable, groupName) - end - - table.sort(groupTable, sort_func) - local groupsToSortedIndex = {} - groupsToSortedIndex[1] = "All" - for i = 1, #groupTable do - groups[#groups + 1] = {} - groupsToSortedIndex[groupTable[i]] = i + 1 - end - - return groups, groupsToSortedIndex + groups = {} + groups[1] = {} + + local sort_func = function(a, b) + a = tostring(a):lower() + b = tostring(b):lower() + local patt = "^(.-)%s*(%d+)$" + local _, _, col1, num1 = a:find(patt) + local _, _, col2, num2 = b:find(patt) + if (col1 and col2) and col1 == col2 then + return tonumber(num1) < tonumber(num2) + end + return a < b + end + + local groupTable = {} + for groupName, groupID in pairs(ADGraphManager:getGroups()) do + table.insert(groupTable, groupName) + end + + table.sort(groupTable, sort_func) + local groupsToSortedIndex = {} + groupsToSortedIndex[1] = "All" + for i = 1, #groupTable do + groups[#groups + 1] = {} + groupsToSortedIndex[groupTable[i]] = i + 1 + end + + return groups, groupsToSortedIndex end From e3c9f2911cdbeda2c01b6f73cd0a3422bc883317 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Tue, 27 Feb 2024 07:46:29 +0100 Subject: [PATCH 49/51] Update README.md --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9db02ebe..d066aa1c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # FS22_AutoDrive FS22 version of the AutoDrive mod -### Latest Release: 2.0.1.4 +### Latest Release: 2.0.1.6 ![GitHub all releases](https://img.shields.io/github/downloads/Stephan-S/FS22_AutoDrive/total?label=Downloads&style=plastic) Direct Download: https://github.com/Stephan-S/FS22_AutoDrive/releases/latest/download/FS22_AutoDrive.zip @@ -18,13 +18,12 @@ And to all who do donate: Thank you very much :-) #### Stephan (Founder): https://www.paypal.me/StephanSchlosser -#### Axel (Modder) - #### Iwan1803 (Community Manager, Supporter & Tester): -https://www.tipeeestream.com/iwan1803/tip +https://iwan1803.de/urls/tip -#### Willi (Supporter & Tester) +#### Axel & Tommo (Modder) +#### Willi (Supporter & Tester) ## Course Editor The course editor is now maintained by @KillBait and can be found here: From cf46b92f8eb797aae5e290ce0868b5edb1d9e3e1 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Tue, 27 Feb 2024 07:46:58 +0100 Subject: [PATCH 50/51] Update AutoDrive.lua --- scripts/AutoDrive.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/AutoDrive.lua b/scripts/AutoDrive.lua index 9e1cfb0a..76cfe98d 100644 --- a/scripts/AutoDrive.lua +++ b/scripts/AutoDrive.lua @@ -1,5 +1,5 @@ AutoDrive = {} -AutoDrive.version = "2.0.1.5-RC" +AutoDrive.version = "2.0.1.6" AutoDrive.directory = g_currentModDirectory From fe7706c797c38ebfe04bef487fa63c2406307894 Mon Sep 17 00:00:00 2001 From: Iwan1803 <47528836+Iwan1803@users.noreply.github.com> Date: Tue, 27 Feb 2024 07:48:15 +0100 Subject: [PATCH 51/51] Update modDesc.xml --- modDesc.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modDesc.xml b/modDesc.xml index 99ca7b0f..968ce961 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.5-RC</version> + <version>2.0.1.6</version> <multiplayer supported="true" /> <iconFilename>icon.dds</iconFilename> <extraSourceFiles>