From 080f69b873807ffdb5a3d4ce5e47f2b3fd4dcc11 Mon Sep 17 00:00:00 2001 From: RedMonster-HUN <54554640+RedMonster-HUN@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:12:25 +0200 Subject: [PATCH 001/107] Update translation_hu.xml (#2773) Translation corrected up to line 437 --- translations/translation_hu.xml | 134 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 9564c521c..b21bfd0a1 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -18,27 +18,27 @@ - + - - - - - + + + + + - - + + - - + + @@ -58,49 +58,49 @@ - + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - + + + - - + + @@ -110,9 +110,9 @@ - - - + + + @@ -141,8 +141,8 @@ - - + + @@ -157,12 +157,12 @@ - - + + - - + + @@ -175,8 +175,8 @@ - - + + @@ -188,8 +188,8 @@ - - + + @@ -202,8 +202,8 @@ - - + + @@ -299,12 +299,12 @@ - - - - - - + + + + + + @@ -365,7 +365,7 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü - + @@ -412,13 +412,13 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü - + - - - + + + @@ -426,15 +426,15 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü - + - - + + From 514d8aecd5b3860e9b0a1e56ea791dbfd597229f Mon Sep 17 00:00:00 2001 From: SniperKittenCZ <61847408+SniperKittenCZ@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:36:00 +0200 Subject: [PATCH 002/107] Update translation_cz.xml --- translations/translation_cz.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 7ee9aa392..865c96a0c 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -321,7 +321,7 @@ - + From d9c9ac3e7bceec76a2b2f3d1e85d7e9bc6b8a490 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Tue, 19 Sep 2023 13:54:14 -0400 Subject: [PATCH 003/107] fix: pathfinder reverse turn Always extend the path before changing from reverse to forward to allow the vehicle to align better with the forward leg. #2777 --- scripts/ai/turns/AITurn.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai/turns/AITurn.lua b/scripts/ai/turns/AITurn.lua index 2118efaee..012c5b62b 100644 --- a/scripts/ai/turns/AITurn.lua +++ b/scripts/ai/turns/AITurn.lua @@ -730,8 +730,8 @@ function CourseTurn:onPathfindingDone(path) self.turnCourse:setUseTightTurnOffsetForLastWaypoints(15) local endingTurnLength = self.turnContext:appendEndingTurnCourse(self.turnCourse, nil, true) local x = AIUtil.getDirectionNodeToReverserNodeOffset(self.vehicle) - self:debug('Extending course at direction switch for reversing: %.1f m', -x ) - self.turnCourse:adjustForReversing(math.max(0, -x)) + self:debug('Extending course at direction switch for reversing to %.1f m (or at least 1m)', -x ) + self.turnCourse:adjustForReversing(math.max(1, -x)) TurnManeuver.setLowerImplements(self.turnCourse, endingTurnLength, true) else self:debug('No path found in %d ms, falling back to normal turn course generator', g_currentMission.time - (self.pathfindingStartedAt or 0)) From 9b3bae4149c2b415c5423ab6bee9d418fab88119 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 5 Jul 2023 21:03:18 +0200 Subject: [PATCH 004/107] WIP --- Courseplay.lua | 3 +- config/MasterTranslations.xml | 14 +- config/jobParameters/JobParameterSetup.xml | 2 + .../SiloLoaderJobParameterSetup.xml | 16 +- modDesc.xml | 29 + scripts/Course.lua | 1 + scripts/CpSettingsUtil.lua | 4 +- .../ai/AIDriveStrategyShovelSiloLoader.lua | 506 ++++++++++++++++++ scripts/ai/ImplementUtil.lua | 63 ++- scripts/ai/controllers/ShovelController.lua | 60 ++- scripts/ai/jobs/CpAIJob.lua | 2 +- scripts/ai/jobs/CpAIJobSiloLoader.lua | 44 +- scripts/ai/jobs/CpJobParameters.lua | 45 +- .../parameters/CpAIParameterPositionAngle.lua | 3 +- scripts/gui/CpAIFrameExtended.lua | 52 +- scripts/gui/CpGamePadHudScreen.lua | 4 +- scripts/gui/hud/CpBaseHud.lua | 7 +- scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua | 8 +- scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 8 + scripts/pathfinder/PathfinderUtil.lua | 33 +- scripts/silo/BunkerSiloWrapper.lua | 40 +- scripts/specializations/CpAIBaleFinder.lua | 4 +- .../specializations/CpAIBunkerSiloWorker.lua | 11 +- .../specializations/CpAICombineUnloader.lua | 4 +- scripts/specializations/CpAIFieldWorker.lua | 4 +- .../specializations/CpAISiloLoaderWorker.lua | 33 +- scripts/specializations/CpAIWorker.lua | 2 +- scripts/specializations/CpGamePadHud.lua | 3 +- scripts/specializations/CpShovelPositions.lua | 422 +++++++++++++++ scripts/trigger/TriggerManager.lua | 44 ++ scripts/trigger/TriggerWrapper.lua | 20 + 31 files changed, 1448 insertions(+), 43 deletions(-) create mode 100644 scripts/ai/AIDriveStrategyShovelSiloLoader.lua create mode 100644 scripts/specializations/CpShovelPositions.lua create mode 100644 scripts/trigger/TriggerManager.lua create mode 100644 scripts/trigger/TriggerWrapper.lua diff --git a/Courseplay.lua b/Courseplay.lua index 737948e89..f4ff27aea 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -339,11 +339,12 @@ function Courseplay.register(typeManager) CpAIFieldWorker.register(typeManager, typeName, typeEntry.specializations) CpAIBaleFinder.register(typeManager, typeName, typeEntry.specializations) CpAICombineUnloader.register(typeManager, typeName, typeEntry.specializations) - CpAIBunkerSiloWorker.register(typeManager, typeName, typeEntry.specializations) CpAISiloLoaderWorker.register(typeManager, typeName, typeEntry.specializations) + CpAIBunkerSiloWorker.register(typeManager, typeName, typeEntry.specializations) CpGamePadHud.register(typeManager, typeName,typeEntry.specializations) CpHud.register(typeManager, typeName, typeEntry.specializations) CpInfoTexts.register(typeManager, typeName, typeEntry.specializations) + CpShovelPositions.register(typeManager, typeName, typeEntry.specializations) end end TypeManager.finalizeTypes = Utils.prependedFunction(TypeManager.finalizeTypes, Courseplay.register) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 919c581e4..ed8a652c8 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -79,6 +79,10 @@ + + + + @@ -300,7 +304,15 @@ - + + + + + + + + + diff --git a/config/jobParameters/JobParameterSetup.xml b/config/jobParameters/JobParameterSetup.xml index fa003f020..4d7c917dc 100644 --- a/config/jobParameters/JobParameterSetup.xml +++ b/config/jobParameters/JobParameterSetup.xml @@ -19,12 +19,14 @@ 2 3 4 + 5 nearest first last bunkerSilo + siloLoader diff --git a/config/jobParameters/SiloLoaderJobParameterSetup.xml b/config/jobParameters/SiloLoaderJobParameterSetup.xml index 6ec615b9c..68040f16b 100644 --- a/config/jobParameters/SiloLoaderJobParameterSetup.xml +++ b/config/jobParameters/SiloLoaderJobParameterSetup.xml @@ -10,6 +10,20 @@ - + + + + + + 1 + 2 + + + trailer + unloadTrigger + + + + diff --git a/modDesc.xml b/modDesc.xml index 6d7920e3a..b57225da1 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -251,6 +251,8 @@ Changelog 7.1.0.0: + + @@ -318,6 +320,32 @@ Changelog 7.1.0.0: + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -405,6 +433,7 @@ Changelog 7.1.0.0: + diff --git a/scripts/Course.lua b/scripts/Course.lua index 5b6e579b7..58c09b489 100644 --- a/scripts/Course.lua +++ b/scripts/Course.lua @@ -910,6 +910,7 @@ function Course:appendWaypoints(waypoints) end --- Append another course to the course +---@param other Course function Course:append(other) self:appendWaypoints(other.waypoints) end diff --git a/scripts/CpSettingsUtil.lua b/scripts/CpSettingsUtil.lua index 24d9639bc..945cf28e9 100644 --- a/scripts/CpSettingsUtil.lua +++ b/scripts/CpSettingsUtil.lua @@ -70,7 +70,7 @@ function CpSettingsUtil.init() schema:register(XMLValueType.STRING, "Settings#autoUpdateGui", "Gui gets updated automatically") local key = "Settings.SettingSubTitle(?)" - schema:register(XMLValueType.STRING, key .."#title", "Setting sub title", nil, true) + schema:register(XMLValueType.STRING, key .."#title", "Setting sub title", nil) schema:register(XMLValueType.BOOL, key .."#prefix", "Setting sub title is a prefix", true) schema:register(XMLValueType.STRING, key.."#isDisabled", "Callback function, if the settings is disabled.") -- optional @@ -157,7 +157,7 @@ function CpSettingsUtil.loadSettingsFromSetup(class, filePath) class.pageTitle = setupKey .. "title" end xmlFile:iterate("Settings.SettingSubTitle", function (i, masterKey) - local subTitle = xmlFile:getValue(masterKey.."#title") + local subTitle = xmlFile:getValue(masterKey.."#title", "...") --- This flag can by used to simplify the translation text. local pre = xmlFile:getValue(masterKey.."#prefix", true) if pre then diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua new file mode 100644 index 000000000..11f8019a2 --- /dev/null +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -0,0 +1,506 @@ +--[[ +This file is part of Courseplay (https://github.com/Courseplay/courseplay) +Copyright (C) 2022 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +---@class AIDriveStrategyShovelSiloLoader : AIDriveStrategyCourse +---@field shovelController ShovelController +AIDriveStrategyShovelSiloLoader = {} +local AIDriveStrategyShovelSiloLoader_mt = Class(AIDriveStrategyShovelSiloLoader, AIDriveStrategyCourse) + +---------------------------------------------------------------- +--- State properties +---------------------------------------------------------------- +--[[ + shovelPosition : number (1-4) + shovelMovingSpeed : number|nil speed while the shovel/ front loader is moving +]] + + +---------------------------------------------------------------- +--- States +---------------------------------------------------------------- + +AIDriveStrategyShovelSiloLoader.myStates = { + DRIVING_ALIGNMENT_COURSE = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_INTO_SILO = {shovelPosition = ShovelController.POSITIONS.LOADING, shovelMovingSpeed = 0}, + DRIVING_OUT_OF_SILO = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + WAITING_FOR_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_TO_UNLOAD_POSITION = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_TO_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_TO_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, + UNLOADING = {shovelPosition = ShovelController.POSITIONS.UNLOADING, shovelMovingSpeed = 0}, +} + +AIDriveStrategyShovelSiloLoader.safeSpaceToTrailer = 5 +AIDriveStrategyShovelSiloLoader.maxValidTrailerDistance = 30 + +function AIDriveStrategyShovelSiloLoader.new(customMt) + if customMt == nil then + customMt = AIDriveStrategyShovelSiloLoader_mt + end + local self = AIDriveStrategyCourse.new(customMt) + AIDriveStrategyCourse.initStates(self, AIDriveStrategyShovelSiloLoader.myStates) + self.state = self.states.INITIAL + return self +end + +function AIDriveStrategyShovelSiloLoader:delete() + AIDriveStrategyShovelSiloLoader:superClass().delete(self) + if self.siloController then + self.siloController:delete() + self.siloController = nil + end + CpUtil.destroyNode(self.heapNode) + CpUtil.destroyNode(self.unloadNode) + CpUtil.destroyNode(self.unloadPositionNode) +end + +function AIDriveStrategyShovelSiloLoader:getGeneratedCourse(jobParameters) + return nil +end + +function AIDriveStrategyShovelSiloLoader:setSiloAndHeap(bunkerSilo, heapSilo) + self.bunkerSilo = bunkerSilo + self.heapSilo = heapSilo +end + +function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) + + -- to always have a valid course (for the traffic conflict detector mainly) + self.course = Course.createStraightForwardCourse(self.vehicle, 25) + self:startCourse(self.course, 1) + + self.jobParameters = jobParameters + local position = jobParameters.unloadPosition + self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", position.x, position.z, position.angle ) + + if self.bunkerSilo ~= nil then + self:debug("Bunker silo was found.") + self.silo = self.bunkerSilo + else + self:debug("Heap was found.") + self.silo = self.heapSilo + end + + self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) +end + +----------------------------------------------------------------------------------------------------------------------- +--- Implement handling +----------------------------------------------------------------------------------------------------------------------- +function AIDriveStrategyShovelSiloLoader:initializeImplementControllers(vehicle) + self:addImplementController(vehicle, MotorController, Motorized, {}, nil) + self:addImplementController(vehicle, WearableController, Wearable, {}, nil) + + self.shovelImplement, self.shovelController = self:addImplementController(vehicle, ShovelController, Shovel, {}, nil) + +end + +--- Fuel save only allowed when no trailer is there to unload into. +function AIDriveStrategyShovelSiloLoader:isFuelSaveAllowed() + return self.state == self.states.WAITING_FOR_TRAILER +end + +----------------------------------------------------------------------------------------------------------------------- +--- Static parameters (won't change while driving) +----------------------------------------------------------------------------------------------------------------------- +function AIDriveStrategyShovelSiloLoader:setAllStaticParameters() + self.reverser = AIReverseDriver(self.vehicle, self.ppc) + self.proximityController = ProximityController(self.vehicle, self:getWorkWidth()) + self.proximityController:registerIgnoreObjectCallback(self, self.ignoreProximityObject) + self:setFrontAndBackMarkers() + + self.siloEndProximitySensor = SingleForwardLookingProximitySensorPack(self.vehicle, self.shovelController:getShovelNode(), 5, 1) + + self.heapNode = CpUtil.createNode("heapNode", 0, 0, 0, nil) + self.unloadNode = CpUtil.createNode("unloadNode", 0, 0, 0, nil) +end + +----------------------------------------------------------------------------------------------------------------------- +--- Event listeners +----------------------------------------------------------------------------------------------------------------------- +function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) + if course:isLastWaypointIx(ix) then + if self.state == self.states.DRIVING_ALIGNMENT_COURSE then + local course = self:getRememberedCourseAndIx() + self:startCourse(course, 1) + self.state = self.states.DRIVING_INTO_SILO + elseif self.state == self.states.DRIVING_INTO_SILO then + self:startDrivingOutOfSilo() + elseif self.state == self.states.DRIVING_OUT_OF_SILO then + self:startPathfindingToUnloadPosition() + elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then + self.state = self.states.WAITING_FOR_TRAILER + elseif self.state == self.states.DRIVING_TO_TRAILER then + local course = Course.createFromNodeToNode(self.vehicle, self.vehicle:getAIDirectionNode(), self.unloadNode, + 0, 0, 0, 3, false) + self:startCourse(course, 1) + + self.state = self.states.DRIVING_TO_UNLOAD + elseif self.state == self.states.DRIVING_TO_UNLOAD then + self.state = self.states.UNLOADING + + --self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new()) + end + end +end + +--- this the part doing the actual work on the field after/before all +--- implements are started/lowered etc. +function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) + self:updateLowFrequencyImplementControllers() + + local moveForwards = not self.ppc:isReversing() + local gx, gz + + ---------------------------------------------------------------- + if not moveForwards then + local maxSpeed + gx, gz, maxSpeed = self:getReverseDriveData() + self:setMaxSpeed(maxSpeed) + else + gx, _, gz = self.ppc:getGoalPointPosition() + end + if self.state == self.states.INITIAL then + if self.silo:getTotalFillLevel() <=0 then + self:debug("Stopping the driver, as the silo is empty.") + self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new()) + return + end + if self.shovelController:isFull() then + self:startPathfindingToUnloadPosition() + else + self:startDrivingToSilo() + end + self:setMaxSpeed(0) + elseif self.state == self.states.DRIVING_ALIGNMENT_COURSE then + self:setMaxSpeed(self.settings.fieldSpeed:getValue()) + elseif self.state == self.states.WAITING_FOR_PATHFINDER then + self:setMaxSpeed(0) + elseif self.state == self.states.DRIVING_INTO_SILO then + self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) + + local _, _, closestObject = self.siloEndProximitySensor:getClosestObjectDistanceAndRootVehicle() + local isEndReached, maxSpeed = self.siloController:isEndReached(self.shovelController:getShovelNode(), 0) + if self.silo:isTheSameSilo(closestObject) or isEndReached then + self:debug("End wall detected or bunker silo end is reached.") + self:startDrivingOutOfSilo() + end + if self.shovelController:isFull() then + self:debug("Shovel is full, starting to drive out of the silo.") + self:startDrivingOutOfSilo() + end + elseif self.state == self.states.DRIVING_OUT_OF_SILO then + self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) + elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then + self:setMaxSpeed(self.settings.fieldSpeed:getValue()) + elseif self.state == self.states.WAITING_FOR_TRAILER then + self:setMaxSpeed(0) + self:searchForTrailerToUnloadInto() + elseif self.state == self.states.DRIVING_TO_TRAILER then + self:setMaxSpeed(self.settings.fieldSpeed:getValue()) + elseif self.state == self.states.DRIVING_TO_UNLOAD then + self:setMaxSpeed(self.settings.reverseSpeed:getValue()) + if self.shovelController:isShovelOverTrailer(self.targetTrailer.trailer) then + self.state = self.states.UNLOADING + end + elseif self.state == self.states.UNLOADING then + self:setMaxSpeed(0) + if self:hasFinishedUnloading() then + self:startDrivingToSilo() + end + end + if self.state.properties.shovelPosition then + if not self.frozen and self.shovelController:moveShovelToPosition(self.state.properties.shovelPosition) then + if self.state.properties.shovelMovingSpeed ~= nil then + self:setMaxSpeed(self.state.properties.shovelMovingSpeed) + end + end + end + + self:limitSpeed() + return gx, gz, moveForwards, self.maxSpeed, 100 +end + +function AIDriveStrategyShovelSiloLoader:update(dt) + AIDriveStrategyCourse.update(self) + self:updateImplementControllers(dt) + if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then + if self.course:isTemporary() then + self.course:draw() + elseif self.ppc:getCourse():isTemporary() then + self.ppc:getCourse():draw() + end + if self.silo then + self.silo:drawDebug() + end + if self.heapSilo then + CpUtil.drawDebugNode(self.heapNode, false, 3) + end + if self.targetTrailer then + CpUtil.drawDebugNode(self.unloadNode, false, 3) + CpUtil.drawDebugNode(self.targetTrailer.exactFillRootNode, false, 3, "ExactFillRootNode") + end + CpUtil.drawDebugNode(self.unloadPositionNode, false, 3) + end +end + +--- Ignores the bunker silo for the proximity sensors. +function AIDriveStrategyShovelSiloLoader:ignoreProximityObject(object, vehicle) + if self.silo:isTheSameSilo(object) then + return true + end + --- This ignores the terrain. + if object == nil then + return true + end +end + +function AIDriveStrategyShovelSiloLoader:getProximitySensorWidth() + -- a bit less as size.width always has plenty of buffer + return self.vehicle.size.width - 0.5 +end + +---------------------------------------------------------------- +--- Pathfinding +---------------------------------------------------------------- + +--- Find an alignment path to the heap course. +---@param course table heap course +function AIDriveStrategyShovelSiloLoader:startPathfindingToStart(course) + if not self.pathfinder or not self.pathfinder:isActive() then + self.state = self.states.WAITING_FOR_PATHFINDER + self:rememberCourse(course, 1) + + self.pathfindingStartedAt = g_currentMission.time + local done, path + local fm = self:getFrontAndBackMarkers() + self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToWaypoint( + self.vehicle, course, 1, 0, -(fm + 4), + true, nil) + if done then + return self:onPathfindingDoneToStart(path) + else + self:setPathfindingDoneCallback(self, self.onPathfindingDoneToStart) + end + else + self:debug('Pathfinder already active') + end + return true +end + +function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToStart(path) + if path and #path > 2 then + self:debug("Found alignment path to the course for the heap.") + local alignmentCourse = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + self:startCourse(alignmentCourse, 1) + self.state = self.states.DRIVING_ALIGNMENT_COURSE + else + local course = self:getRememberedCourseAndIx() + self:debug("No alignment path found!") + self:startCourse(course, 1) + self.state = self.states.DRIVING_INTO_SILO + end +end + + +function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() + self:debugSparse("Searching for an trailer nearby.") + local function getClosestTrailerAndDistance() + local closestDistance = math.huge + local closestTrailer = nil + for i, vehicle in pairs(g_currentMission.vehicles) do + if SpecializationUtil.hasSpecialization(Trailer, vehicle.specializations) and AIUtil.isStopped(vehicle.rootVehicle) then + local dist = calcDistanceFrom(vehicle.rootNode, self.unloadPositionNode) + if dist < closestDistance then + closestDistance = dist + closestTrailer = vehicle + end + end + end + return closestTrailer, closestDistance + end + local trailer, dist = getClosestTrailerAndDistance() + if not trailer then + return + end + if dist > 20 then + return + end + self:debug("Found a trailer %s within distance %.2f", CpUtil.getName(trailer), dist) + local canLoad, fillUnitIndex, fillType, exactFillRootNode = + ImplementUtil.getCanLoadTo(trailer, self.shovelImplement) + if canLoad and exactFillRootNode ~= nil then + self.targetTrailer = { + fillUnitIndex = fillUnitIndex, + fillType = fillType, + exactFillRootNode = exactFillRootNode, + trailer = trailer + } + -- self:debug("Found valid trailer %s attached to %s with a distance of %.2fm to the silo front center for fill type %s in fill unit %d.", + -- CpUtil.getName(vehicle), CpUtil.getName(vehicle.rootVehicle), + -- self.maxValidTrailerDistance, g_fillTypeManager:getFillTypeTitleByIndex(fillType), fillUnitIndex) + self:startPathfindingToTrailer(trailer, exactFillRootNode) + end +end + +function AIDriveStrategyShovelSiloLoader:startPathfindingToUnloadPosition() + if not self.pathfinder or not self.pathfinder:isActive() then + self.state = self.states.WAITING_FOR_PATHFINDER + + + local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) + self.pathfindingStartedAt = g_currentMission.time + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( + self.vehicle, self.unloadPositionNode, + 0, -2*spaceToTrailer, true, + nil, {}, nil, + 0, nil, true + ) + if done then + return self:onPathfindingDoneToUnloadPosition(path, goalNodeInvalid) + else + self:setPathfindingDoneCallback(self, self.onPathfindingDoneToUnloadPosition) + end + else + self:debug('Pathfinder already active') + end + return true +end + +function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, goalNodeInvalid) + if path and #path > 2 then + self:debug("Found path to unloading station.") + local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + self:startCourse(course, 1) + self.state = self.states.DRIVING_TO_UNLOAD_POSITION + else + self:debug("Failed to drive close to unload position.") + self.state = self.states.WAITING_FOR_TRAILER + end +end + +--- Find an alignment path to the heap course. +function AIDriveStrategyShovelSiloLoader:startPathfindingToTrailer(trailer, exactFillRootNode) + if not self.pathfinder or not self.pathfinder:isActive() then + self.state = self.states.WAITING_FOR_PATHFINDER + local dx, _, _ = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) + + local loadingLeftSide = dx > 0 + + local x, y, z = localToLocal(exactFillRootNode, trailer.rootNode, 0, 0, 0) + + local gx, gy, gz = localToWorld(trailer.rootNode, x, y, z) + local dirX, dirZ = localDirectionToWorld(trailer.rootNode, loadingLeftSide and 1 or -1, 0, 0) + local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) + setTranslation(self.unloadNode, gx, gy, gz) + setRotation(self.unloadNode, 0, yRot, 0) + + local spaceToTrailer = math.max(self.turningRadius, self.safeSpaceToTrailer) + + self.pathfindingStartedAt = g_currentMission.time + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( + self.vehicle, self.unloadNode, + 0, -spaceToTrailer, true, + nil, {}, nil, + 0, nil, true + ) + if done then + return self:onPathfindingDoneToTrailer(path, goalNodeInvalid) + else + self:setPathfindingDoneCallback(self, self.onPathfindingDoneToTrailer) + end + else + self:debug('Pathfinder already active') + end + return true +end + +function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToTrailer(path, goalNodeInvalid) + if path and #path > 2 then + self:debug("Found alignment path to trailer.") + local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + self:startCourse(course, 1) + self.state = self.states.DRIVING_TO_TRAILER + else + self:debug("Failed to find valid path to trailer!") + self.state = self.sates.WAITING_FOR_TRAILER + end +end + +---------------------------------------------------------------- +--- Silo work +---------------------------------------------------------------- + +function AIDriveStrategyShovelSiloLoader:startDrivingToSilo() + local startPos, endPos = self.siloController:getTarget(self:getWorkWidth()) + local x, z = unpack(startPos) + local dx, dz = unpack(endPos) + + local siloCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, 0, 3, 3, false) + + + local distance = siloCourse:getDistanceBetweenVehicleAndWaypoint(self.vehicle, 1) + + if distance > 2 * self.turningRadius then + self:debug("Start driving to silo with pathfinder.") + self:startPathfindingToStart(siloCourse) + else + self:debug("Start driving into the silo.") + self:startCourse(siloCourse, 1) + self.state = self.states.DRIVING_INTO_SILO + end +end + +function AIDriveStrategyShovelSiloLoader:startDrivingOutOfSilo() + local startPos, endPos = self.siloController:getLastTarget() + local x, z = unpack(endPos) + local dx, dz = unpack(startPos) + + local reverseCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, 0, 6, 3, true) + local ix = reverseCourse:getNextRevWaypointIxFromVehiclePosition(1, self.vehicle:getAIDirectionNode(), 10) + if ix == 1 then + ix = reverseCourse:getNumberOfWaypoints() + end + self:startCourse(reverseCourse, ix) + self.state = self.states.DRIVING_OUT_OF_SILO +end + +function AIDriveStrategyShovelSiloLoader:getWorkWidth() + return self.settings.bunkerSiloWorkWidth:getValue() +end + +---------------------------------------------------------------- +--- Unloading +---------------------------------------------------------------- +function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() + if self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then + self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) + return true + end + if self.shovelController:isEmpty() then + self:debug("Finished unloading, as the shovel is empty.") + return true + end + + return false +end + diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 8536cd752..c7cf60699 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -407,6 +407,9 @@ function ImplementUtil.getLevelerNode(object) return object.spec_leveler and object.spec_leveler.nodes and object.spec_leveler.nodes[1] and object.spec_leveler.nodes[1] end +--- Visually displays the bale collector offset +---@param vehicle table +---@param offset number function ImplementUtil.showBaleCollectorOffset(vehicle, offset) local implement = AIUtil.getImplementWithSpecialization(vehicle, BaleLoader) if not implement then @@ -417,4 +420,62 @@ function ImplementUtil.showBaleCollectorOffset(vehicle, offset) local dx, dy, dz = localToWorld(vehicle:getAIDirectionNode(), -offset, 3, 2) DebugUtil.drawDebugLine(x, y, z, dx, dy, dz, 1, 0, 0) end -end \ No newline at end of file +end + +--- Checks if loading from an implement to another is possible. +---@param loadTargetImplement table +---@param implementToLoadFrom table +---@param dischargeNode table|nil optional otherwise the current selected node is used. +---@param suppressLog boolean|nil +---@return boolean is loading possible? +---@return number|nil target implement fill unit ix to load into. +---@return number|nil fill type to load +---@return number|nil target exact fill root node +function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, dischargeNode, suppressLog) + + local function debug(str, ...) + if not suppressLog then + CpUtil.debugVehicle(CpDebug.DBG_SILO, implementToLoadFrom.rootVehicle, + str, ...) + end + end + + if dischargeNode == nil then + dischargeNode = implementToLoadFrom:getCurrentDischargeNode() + end + if dischargeNode == nil then + debug("No valid discharge node found!") + return false, nil, nil, nil + end + + local fillType = implementToLoadFrom:getDischargeFillType(dischargeNode) + + if fillType == nil or fillType == FillType.UNKNOWN then + debug("No valid fill type to load!") + return false, nil, nil, nil + end + + local validTarget, targetFillUnitIndex, exactFillRootNode + for fillUnitIndex, fillUnit in pairs(loadTargetImplement:getFillUnits()) do + if loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, fillType) then + if loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, fillType) then + if loadTargetImplement.getFillUnitFreeCapacity == nil or loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, fillType, implementToLoadFrom:getActiveFarm()) > 0 then + if loadTargetImplement.getIsFillAllowedFromFarm == nil or loadTargetImplement:getIsFillAllowedFromFarm(implementToLoadFrom:getActiveFarm()) then + validTarget, targetFillUnitIndex, exactFillRootNode = true, fillUnitIndex, loadTargetImplement:getFillUnitExactFillRootNode(fillUnitIndex) + else + debug("Fill unit(%d) filling to target farm %s from %s not allowed!", fillUnitIndex, loadTargetImplement:getOwnerFarmId(), implementToLoadFrom:getActiveFarm()) + end + else + debug("Fill unit(%d) is full with fill type %s!", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) + end + else + debug("Fill unit(%d) doesn't allow fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) + end + else + debug("Fill unit(%d) doesn't support fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) + end + end + + return validTarget, targetFillUnitIndex, fillType, exactFillRootNode +end + diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 7a840dd6e..adc7efd12 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -1,6 +1,14 @@ ---@class ShovelController : ImplementController ShovelController = CpObject(ImplementController) +ShovelController.POSITIONS = { + DEACTIVATED = 0, + LOADING = 1, + TRANSPORT = 2, + PRE_UNLOADING = 3, + UNLOADING = 4, +} + function ShovelController:init(vehicle, implement) ImplementController.init(self, vehicle, implement) self.shovelSpec = self.implement.spec_shovel @@ -16,11 +24,11 @@ function ShovelController:getShovelNode() end function ShovelController:isFull() - return self:getFillLevelPercentage() >= 0.98 + return self:getFillLevelPercentage() >= 99 end function ShovelController:isEmpty() - return self:getFillLevelPercentage() <= 0.01 + return self:getFillLevelPercentage() <= 1 end function ShovelController:getFillLevelPercentage() @@ -40,6 +48,48 @@ function ShovelController:getShovelFillType() return self.shovelSpec.loadingFillType end -function ShovelController:isReadyToLoad() - return self:getShovelFillType() == FillType.UNKNOWN and self:getFillLevelPercentage() < 0.5 -end \ No newline at end of file +function ShovelController:getDischargeFillType() + return self.implement:getDischargeFillType(self:getDischargeNode()) +end + +function ShovelController:getDischargeNode() + return self.implement:getCurrentDischargeNode() +end + +--- Is the shovel node over the trailer? +---@param trailer table +---@param margin number|nil +---@return boolean +function ShovelController:isShovelOverTrailer(trailer, margin) + local node = self:getShovelNode() + local x, y, z = localToLocal(trailer.rootNode, node, 0, 0, 0) + margin = margin or 0 + return z < margin +end + +function ShovelController:moveShovelToLoadingPosition() + return self:moveShovelToPosition(self.POSITIONS.LOADING) +end + +function ShovelController:moveShovelToTransportPosition() + return self:moveShovelToPosition(self.POSITIONS.TRANSPORT) +end + +function ShovelController:moveShovelToPreUnloadPosition() + return self:moveShovelToPosition(self.POSITIONS.PRE_UNLOADING) +end + +function ShovelController:moveShovelToUnloadPosition() + return self:moveShovelToPosition(self.POSITIONS.UNLOADING) +end + +function ShovelController:onFinished() + self.implement:cpResetShovelState() +end + +---@param pos number shovel position 1-4 +---@return boolean reached? +function ShovelController:moveShovelToPosition(pos) + self.implement:cpSetShovelState(pos) + return self.implement:areCpShovelPositionsDirty() +end diff --git a/scripts/ai/jobs/CpAIJob.lua b/scripts/ai/jobs/CpAIJob.lua index 9d36dfb35..4212b4ace 100644 --- a/scripts/ai/jobs/CpAIJob.lua +++ b/scripts/ai/jobs/CpAIJob.lua @@ -364,8 +364,8 @@ function CpAIJob.registerJob(AIJobTypeManager) AIJobTypeManager:registerJobType(CpAIJobBaleFinder.name, CpAIJobBaleFinder.jobName, CpAIJobBaleFinder) AIJobTypeManager:registerJobType(CpAIJobFieldWork.name, CpAIJobFieldWork.jobName, CpAIJobFieldWork) AIJobTypeManager:registerJobType(CpAIJobCombineUnloader.name, CpAIJobCombineUnloader.jobName, CpAIJobCombineUnloader) - AIJobTypeManager:registerJobType(CpAIJobBunkerSilo.name, CpAIJobBunkerSilo.jobName, CpAIJobBunkerSilo) AIJobTypeManager:registerJobType(CpAIJobSiloLoader.name, CpAIJobSiloLoader.jobName, CpAIJobSiloLoader) + AIJobTypeManager:registerJobType(CpAIJobBunkerSilo.name, CpAIJobBunkerSilo.jobName, CpAIJobBunkerSilo) end diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 60499cae4..d3640814c 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -36,6 +36,7 @@ function CpAIJobSiloLoader:setupJobParameters() CpAIJobSiloLoader:superClass().setupJobParameters(self) self:setupCpJobParameters(CpSiloLoaderJobParameters(self)) self.cpJobParameters.loadPosition:setSnappingAngle(math.pi/8) -- AI menu snapping angle of 22.5 degree. + self.cpJobParameters.unloadPosition:setSnappingAngle(math.pi/8) -- AI menu snapping angle of 22.5 degree. end function CpAIJobSiloLoader:getIsAvailableForVehicle(vehicle) @@ -68,6 +69,14 @@ function CpAIJobSiloLoader:applyCurrentState(vehicle, mission, farmId, isDirectS self.cpJobParameters.loadPosition:setPosition(x, z) self.cpJobParameters.loadPosition:setAngle(angle) end + + local x, z = self.cpJobParameters.unloadPosition:getPosition() + + -- no unload position use the vehicle's current position + if x == nil or z == nil then + x, _, z = getWorldTranslation(vehicle.rootNode) + self.cpJobParameters.unloadPosition:setPosition(x, z) + end end @@ -121,6 +130,14 @@ function CpAIJobSiloLoader:validate(farmId) return false, g_i18n:getText("CP_error_no_heap_found") end + if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then + --- Validate the trigger setup + local found, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) + + return false, g_i18n:getText("CP_error_no_unload_trigger_found") + end + + return isValid, errorMessage end @@ -147,7 +164,32 @@ function CpAIJobSiloLoader:getBunkerSiloOrHeap(loadPosition, node) return found, nil, heapSilo end +--- Gets the unload trigger at the unload position. +---@param unloadPosition CpAIParameterPositionAngle +---@return boolean +---@return table|nil +function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) + local x, z = unloadPosition:getPosition() + local angle = unloadPosition:getAngle() + if x == nil or angle == nil then + return false + end + return false +end + function CpAIJobSiloLoader:drawSilos(map) self.heapPlot:draw(map) g_bunkerSiloManager:drawSilos(map, self.bunkerSilo) -end \ No newline at end of file +end + + +--- Gets the giants unload station. +function CpAIJobSiloLoader:getUnloadingStations() + local unloadingStations = {} + for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do + if g_currentMission.accessHandler:canPlayerAccess(unloadingStation) and unloadingStation:isa(UnloadingStation) then + table.insert(unloadingStations, unloadingStation) + end + end + return unloadingStations +end diff --git a/scripts/ai/jobs/CpJobParameters.lua b/scripts/ai/jobs/CpJobParameters.lua index 58e4bf271..9fec909c1 100644 --- a/scripts/ai/jobs/CpJobParameters.lua +++ b/scripts/ai/jobs/CpJobParameters.lua @@ -127,6 +127,16 @@ function CpJobParameters:isBunkerSiloHudModeDisabled() return self:isAIMenuJob() end +function CpJobParameters:isSiloLoadingHudModeDisabled() + local vehicle = self.job:getVehicle() + if vehicle then + if not vehicle:getCanStartCpSiloLoaderWorker() then + return true + end + end + return self:isAIMenuJob() +end + --- Callback raised by a setting and executed as an vehicle event. ---@param callbackStr string event to be raised ---@param setting AIParameterSettingList setting that raised the callback. @@ -137,7 +147,6 @@ function CpJobParameters:raiseCallback(callbackStr, setting, ...) end end - function CpJobParameters:__tostring() return tostring(self.settings) end @@ -311,6 +320,9 @@ end --- AI parameters for the bunker silo job. ---@class CpSiloLoaderJobParameters : CpJobParameters +---@field unloadAt AIParameterSettingList +---@field UNLOAD_TRAILER number +---@field UNLOAD_TRIGGER number CpSiloLoaderJobParameters = CpObject(CpJobParameters) function CpSiloLoaderJobParameters:init(job) @@ -326,3 +338,34 @@ end function CpSiloLoaderJobParameters.getSettings(vehicle) return vehicle.spec_cpAISiloLoaderWorker.cpJob:getCpJobParameters() end + +function CpSiloLoaderJobParameters:isShovelSiloLoadDisabled() + local vehicle = self.job:getVehicle() + if vehicle then + return AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) + end + return false +end + +function CpSiloLoaderJobParameters:isUnloadPositionDisabled() + return self:isShovelSiloLoadDisabled() or self.unloadAt == CpSiloLoaderJobParameters.UNLOAD_TRAILER +end + +function CpSiloLoaderJobParameters:isUnloadStationDisabled() + return true +end + +function CpSiloLoaderJobParameters:generateUnloadingStations(setting, oldIx) + local unloadingStationIds = {} + local texts = {} + table.insert(unloadingStationIds, -1) + table.insert(texts, "---") + if self.job then + for i, unloadingStation in ipairs(self.job:getUnloadingStations()) do + local id = NetworkUtil.getObjectId(unloadingStation) + table.insert(unloadingStationIds, id) + table.insert(texts, unloadingStation:getName() or "") + end + end + return unloadingStationIds, texts, oldIx +end \ No newline at end of file diff --git a/scripts/ai/parameters/CpAIParameterPositionAngle.lua b/scripts/ai/parameters/CpAIParameterPositionAngle.lua index 46ccc8b02..dbdac4b0a 100644 --- a/scripts/ai/parameters/CpAIParameterPositionAngle.lua +++ b/scripts/ai/parameters/CpAIParameterPositionAngle.lua @@ -121,7 +121,8 @@ CpAIParameterPositionAngle = CpObject(CpAIParameterPosition) CpAIParameterPositionAngle.POSITION_TYPES = { DRIVE_TO = 0, --- with angle FIELD_OR_SILO = 1, --- without angle - UNLOAD = 2 --- with angle + UNLOAD = 2, --- with angle + LOAD = 3 --- with angle } ---@param data table ---@param vehicle table diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 99f2d296d..b7d393fec 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -122,6 +122,8 @@ function CpInGameMenuAIFrameExtended:onAIFrameLoadMapFinished() self.unloadAiTargetMapHotspot = AITargetHotspot.new() + self.loadAiTargetMapHotspot = AITargetHotspot.new() + self.ingameMap.onClickHotspotCallback = Utils.appendedFunction(self.ingameMap.onClickHotspotCallback, CpInGameMenuAIFrameExtended.onClickHotspot) @@ -321,6 +323,7 @@ end function CpInGameMenuAIFrameExtended:setMapSelectionItem(hotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) if hotspot ~= nil then local vehicle = InGameMenuMapUtil.getHotspotVehicle(hotspot) @@ -353,6 +356,10 @@ function CpInGameMenuAIFrameExtended:setMapSelectionItem(hotspot) if param:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) end + elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then + if param:applyToMapHotspot(self.loadAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) + end end end end @@ -401,6 +408,7 @@ function CpInGameMenuAIFrameExtended:onAIFrameClose() self.lastHotspot = self.currentHotspot g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) CpInGameMenuAIFrameExtended.unbindCourseGeneratorSettings(self) g_currentMission.inGameMenu:updatePages() end @@ -473,9 +481,36 @@ function CpInGameMenuAIFrameExtended:delete() self.unloadAiTargetMapHotspot = nil end + if self.loadAiTargetMapHotspot ~= nil then + self.loadAiTargetMapHotspot:delete() + + self.loadAiTargetMapHotspot = nil + end end InGameMenuAIFrame.delete = Utils.appendedFunction(InGameMenuAIFrame.delete,CpInGameMenuAIFrameExtended.delete) + +function CpInGameMenuAIFrameExtended:onClickPositionParameter(superFunc, element, ...) + local parameter = element.aiParameter + if parameter:getCanBeChanged() then + --- Checks if the position setting is not disabled + superFunc(self, element, ...) + end +end +InGameMenuAIFrame.onClickPositionParameter = Utils.overwrittenFunction( + InGameMenuAIFrame.onClickPositionParameter, CpInGameMenuAIFrameExtended.onClickPositionParameter) + +function CpInGameMenuAIFrameExtended:onClickPositionRotationParameter(superFunc, element, ...) + local parameter = element.aiParameter + if parameter:getCanBeChanged() then + --- Checks if the position setting is not disabled + superFunc(self, element, ...) + end +end +InGameMenuAIFrame.onClickPositionRotationParameter = Utils.overwrittenFunction( + InGameMenuAIFrame.onClickPositionRotationParameter, CpInGameMenuAIFrameExtended.onClickPositionRotationParameter) + + --- Ugly hack to swap the main AI hotspot with the field position hotspot, --- as only the main hotspot can be moved by the player. function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, callback, ...) @@ -492,6 +527,11 @@ function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, self.aiTargetMapHotspot = self.unloadAiTargetMapHotspot self.unloadAiTargetMapHotspot = mapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD + elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then + local mapHotspot = self.aiTargetMapHotspot + self.aiTargetMapHotspot = self.loadAiTargetMapHotspot + self.loadAiTargetMapHotspot = mapHotspot + self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.LOAD end end callback = Utils.appendedFunction(callback,function (finished, x, z) @@ -500,8 +540,11 @@ function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, superFunc(self, parameter, callback, ...) end -InGameMenuAIFrame.startPickPosition = Utils.overwrittenFunction(InGameMenuAIFrame.startPickPosition,CpInGameMenuAIFrameExtended.startPickingPosition) -InGameMenuAIFrame.startPickPositionAndRotation = Utils.overwrittenFunction(InGameMenuAIFrame.startPickPositionAndRotation,CpInGameMenuAIFrameExtended.startPickingPosition) +InGameMenuAIFrame.startPickPosition = Utils.overwrittenFunction(InGameMenuAIFrame.startPickPosition, + CpInGameMenuAIFrameExtended.startPickingPosition) + +InGameMenuAIFrame.startPickPositionAndRotation = Utils.overwrittenFunction(InGameMenuAIFrame.startPickPositionAndRotation, + CpInGameMenuAIFrameExtended.startPickingPosition) function CpInGameMenuAIFrameExtended:resetHotspots() if self.currentPickingMapHotspotType == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then @@ -528,6 +571,7 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) for _, element in ipairs(self.currentJobElements) do local parameter = element.aiParameter local parameterType = parameter:getType() @@ -550,6 +594,10 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) if parameter:applyToMapHotspot(self.unloadAiTargetMapHotspot) then g_currentMission:addMapHotspot(self.unloadAiTargetMapHotspot) end + elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then + if parameter:applyToMapHotspot(self.loadAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) + end end elseif parameterType == AIParameterType.POSITION or parameterType == AIParameterType.POSITION_ANGLE then element:setText(parameter:getString()) diff --git a/scripts/gui/CpGamePadHudScreen.lua b/scripts/gui/CpGamePadHudScreen.lua index 276724dfe..f5950b3e9 100644 --- a/scripts/gui/CpGamePadHudScreen.lua +++ b/scripts/gui/CpGamePadHudScreen.lua @@ -260,7 +260,9 @@ end function CpGamePadHudSiloLoaderScreen:update(dt, ...) CpGamePadHudSiloLoaderScreen:superClass().update(self, dt, ...) - + if not self.vehicle:getCanStartCpSiloLoaderWorker() or self.vehicle:getCpStartingPointSetting():getValue() ~= CpJobParameters.START_AT_SILO_LOADING then + self.vehicle:reopenCpGamePadHud() + end end function CpGamePadHudSiloLoaderScreen:drawWorkWidth() diff --git a/scripts/gui/hud/CpBaseHud.lua b/scripts/gui/hud/CpBaseHud.lua index 9e3df27a4..e4383ef88 100644 --- a/scripts/gui/hud/CpBaseHud.lua +++ b/scripts/gui/hud/CpBaseHud.lua @@ -425,9 +425,12 @@ function CpBaseHud:getActiveHudPage(vehicle) elseif vehicle:getCanStartCpBaleFinder() and not vehicle:hasCpCourse() then return self.baleFinderLayout elseif vehicle:getCanStartCpBunkerSiloWorker() and vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO - or AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) then + or (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) + and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel)) then return self.bunkerSiloWorkerLayout - elseif vehicle:getCanStartCpSiloLoaderWorker() then + elseif vehicle:getCanStartCpSiloLoaderWorker() and + (AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) or + vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING) then return self.siloLoaderWorkerLayout else return self.fieldworkLayout diff --git a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua index b61ee59c4..dc5489420 100644 --- a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua +++ b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua @@ -101,5 +101,11 @@ function CpBunkerSiloWorkerHudPageElement:updateContent(vehicle, status) end function CpBunkerSiloWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) - return AIUtil.hasAIImplementWithSpecialization(vehicle, Leveler) + return AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) + and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel) + end + +function CpBunkerSiloWorkerHudPageElement:getStartingPointBtnText(vehicle) + return vehicle:getCpStartingPointSetting():getString() +end \ No newline at end of file diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index d6af9cdbd..e8e7aaec6 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -145,4 +145,12 @@ function CpSiloLoaderWorkerHudPageElement:arePositionEqual(parameters, otherPara return false end return true +end + +function CpSiloLoaderWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) + return AIUtil.hasAIImplementWithSpecialization(vehicle, ConveyorBelt) +end + +function CpSiloLoaderWorkerHudPageElement:getStartingPointBtnText(vehicle) + return vehicle:getCpStartingPointSetting():getString() end \ No newline at end of file diff --git a/scripts/pathfinder/PathfinderUtil.lua b/scripts/pathfinder/PathfinderUtil.lua index 29a5c062d..4565bfa01 100644 --- a/scripts/pathfinder/PathfinderUtil.lua +++ b/scripts/pathfinder/PathfinderUtil.lua @@ -663,8 +663,21 @@ function PathfinderUtil.initializeTrailerHeading(start, vehicleData) end end ----@param start State3D +---@param vehicle table ---@param goal State3D +---@param allowReverse boolean +---@param fieldNum number|nil +---@param vehiclesToIgnore table|nil +---@param objectsToIgnore table|nil +---@param maxFruitPercent number|nil +---@param offFieldPenalty number|nil +---@param areaToAvoid PathfinderUtil.Area|nil +---@param mustBeAccurate boolean|nil +---@param areaToIgnoreFruit PathfinderUtil.Area|nil +---@return PathfinderInterface pathfinder +---@return boolean done finished pathfinding? +---@return table|nil path that was found? +---@return boolean|nil goalNodeInvalid function PathfinderUtil.startPathfindingFromVehicleToGoal(vehicle, goal, allowReverse, fieldNum, vehiclesToIgnore, objectsToIgnore, @@ -789,6 +802,10 @@ end --- in front of the vehicle when it stops working on that row before the turn starts. Negative values mean the --- vehicle is towing the implements and is past the end of the row when the implement reaches the end of the row. ---@param turnOnField boolean is turn on field allowed? +---@return PathfinderInterface pathfinder +---@return boolean done finished pathfinding? +---@return table|nil path that was found? +---@return boolean|nil goalNodeInvalid function PathfinderUtil.findPathForTurn(vehicle, startOffset, goalReferenceNode, goalOffset, turnRadius, allowReverse, courseWithHeadland, workingWidth, backMarkerDistance, turnOnField) local x, z, yRot = PathfinderUtil.getNodePositionAndDirection(vehicle:getAIDirectionNode(), 0, startOffset or 0) @@ -839,6 +856,8 @@ end ---@param zOffset number offset in meters relative to the goal node (forward positive, backward negative) --- Together with the goalReferenceNode defines the goal ---@param turnRadius number vehicle turning radius +---@return table|nil path +---@return number length function PathfinderUtil.findAnalyticPath(solver, vehicleDirectionNode, startOffset, goalReferenceNode, xOffset, zOffset, turnRadius) local x, z, yRot = PathfinderUtil.getNodePositionAndDirection(vehicleDirectionNode, 0, startOffset or 0) @@ -902,6 +921,10 @@ end ---@param offFieldPenalty number penalty to apply to nodes off the field ---@param areaToAvoid PathfinderUtil.NodeArea nodes in this area will be penalized so the path will most likely avoid it ---@param areaToIgnoreFruit PathfinderUtil.Area area to ignore fruit +---@return PathfinderInterface pathfinder +---@return boolean done finished pathfinding? +---@return table|nil path that was found? +---@return boolean|nil goalNodeInvalid function PathfinderUtil.startPathfindingFromVehicleToWaypoint(vehicle, course, goalWaypointIx, xOffset, zOffset, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, @@ -927,6 +950,10 @@ end ---@param offFieldPenalty number|nil penalty to apply to nodes off the field ---@param areaToAvoid PathfinderUtil.NodeArea|nil nodes in this area will be penalized so the path will most likely avoid it ---@param mustBeAccurate boolean|nil must be accurately find the goal position/angle (optional) +---@return PathfinderInterface pathfinder +---@return boolean done finished pathfinding? +---@return table|nil path that was found? +---@return boolean|nil goalNodeInvalid function PathfinderUtil.startPathfindingFromVehicleToNode(vehicle, goalNode, xOffset, zOffset, allowReverse, fieldNum, vehiclesToIgnore, maxFruitPercent, @@ -948,6 +975,10 @@ end ---@param fieldNum number if other than 0 or nil the pathfinding is restricted to the given field and its vicinity ---@param vehiclesToIgnore table[] list of vehicles to ignore for the collision detection (optional) ---@param maxFruitPercent number maximum percentage of fruit present before a node is marked as invalid (optional) +---@return PathfinderInterface pathfinder +---@return boolean done finished pathfinding? +---@return table|nil path that was found? +---@return boolean|nil goalNodeInvalid function PathfinderUtil.startAStarPathfindingFromVehicleToNode(vehicle, goalNode, xOffset, zOffset, fieldNum, vehiclesToIgnore, maxFruitPercent) diff --git a/scripts/silo/BunkerSiloWrapper.lua b/scripts/silo/BunkerSiloWrapper.lua index 6c6062985..7ef3fc4f0 100644 --- a/scripts/silo/BunkerSiloWrapper.lua +++ b/scripts/silo/BunkerSiloWrapper.lua @@ -65,68 +65,98 @@ function CpSilo:init(sx, sz, wx, wz, hx, hz) end +---@return number sx +---@return number sz function CpSilo:getStartPosition() return self.sx, self.sz end +---@return number wx +---@return number wz function CpSilo:getWidthPosition() return self.wx, self.wz end +---@return number hx +---@return number hz function CpSilo:getHeightPosition() return self.hx, self.hz end +---@return number width function CpSilo:getWidth() return self.width end +---@return number length function CpSilo:getLength() return self.length end +---@return number dirX +---@return number dirZ function CpSilo:getLengthDirection() return self.dirXLength, self.dirZLength end +---@return number dirX +---@return number dirZ function CpSilo:getWidthDirection() return self.dirXWidth, self.dirZWidth end +---@return number cx +---@return number cz function CpSilo:getCenter() local cx, cz = self:getFrontCenter() return cx + self.dirXLength * self.length/2, cz + self.dirZLength * self.length/2 end +---@return number fcx +---@return number fcz function CpSilo:getFrontCenter() local width = self:getWidth() return self.sx + self.dirXWidth * width/2, self.sz + self.dirZWidth * width/2 end +---@return number bcx +---@return number bcz function CpSilo:getBackCenter() local length = self:getLength() local fcx, fcz = self:getFrontCenter() return fcx + self.dirXLength * length/2, fcz + self.dirZLength * length/2 end ---- Is the point directly in the silo area. +--- Is the point directly in the silo area? +---@param x number +---@param z number +---@return boolean function CpSilo:isPointInSilo(x, z) return self:isPointInArea(x, z, self.area) end +---@param node number +---@return boolean function CpSilo:isNodeInSilo(node) local x, _, z = getWorldTranslation(node) return self:isPointInArea(x, z, self.area) end +---@param vehicle table +---@return boolean function CpSilo:isVehicleInSilo(vehicle) return self:isNodeInSilo(vehicle.rootNode) end +---@param x number +---@param z number +---@param area table +---@return boolean function CpSilo:isPointInArea(x, z, area) return CpMathUtil.isPointInPolygon(area, x, z) end +---@return table area function CpSilo:getArea() return self.area end @@ -388,6 +418,14 @@ function CpBunkerSilo:getNode() return self.silo.interactionTriggerNode end +function CpBunkerSilo:getFillType() + return self.silo.outputFillType +end + +function CpBunkerSilo:getTotalFillLevel() + return self.silo.fillLevel +end + function CpBunkerSilo:delete() for _, controller in pairs(self.controllers) do controller:setBunkerSiloInvalid() diff --git a/scripts/specializations/CpAIBaleFinder.lua b/scripts/specializations/CpAIBaleFinder.lua index 535e1197e..d0ba354c3 100644 --- a/scripts/specializations/CpAIBaleFinder.lua +++ b/scripts/specializations/CpAIBaleFinder.lua @@ -135,9 +135,9 @@ function CpAIBaleFinder:getCanStartCp(superFunc) end --- Only use the bale finder, if the cp field work job is not possible. -function CpAIBaleFinder:getCpStartableJob(superFunc) +function CpAIBaleFinder:getCpStartableJob(superFunc, ...) local spec = self.spec_cpAIBaleFinder - return superFunc(self) or self:getCanStartCpBaleFinder() and spec.cpJob + return superFunc(self, ...) or self:getCanStartCpBaleFinder() and spec.cpJob end function CpAIBaleFinder:getCpStartText(superFunc) diff --git a/scripts/specializations/CpAIBunkerSiloWorker.lua b/scripts/specializations/CpAIBunkerSiloWorker.lua index 6b73042db..2a6828256 100644 --- a/scripts/specializations/CpAIBunkerSiloWorker.lua +++ b/scripts/specializations/CpAIBunkerSiloWorker.lua @@ -98,8 +98,11 @@ function CpAIBunkerSiloWorker:getCanStartCpBunkerSiloWorker() return not self:getCanStartCpFieldWork() and not self:getCanStartCpBaleFinder() and not self:getCanStartCpCombineUnloader() - and not self:getCanStartCpSiloLoaderWorker() - and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, Leveler, nil)) + + and (not self:hasCpCourse() or + AIUtil.hasChildVehicleWithSpecialization(self, Leveler)) + and not (AIUtil.hasChildVehicleWithSpecialization(self, Shovel) + and AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt)) end function CpAIBunkerSiloWorker:getCanStartCp(superFunc) @@ -109,10 +112,10 @@ end function CpAIBunkerSiloWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAIBunkerSiloWorker local job = self:getCanStartCpBunkerSiloWorker() and spec.cpJob - if isStartedByHud and not AIUtil.hasChildVehicleWithSpecialization(self, Leveler) then + if isStartedByHud then job = self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO and job end - return superFunc(self) or job + return superFunc(self, isStartedByHud) or job end function CpAIBunkerSiloWorker:getCpStartText(superFunc) diff --git a/scripts/specializations/CpAICombineUnloader.lua b/scripts/specializations/CpAICombineUnloader.lua index ced1a7148..1a1d20bfe 100644 --- a/scripts/specializations/CpAICombineUnloader.lua +++ b/scripts/specializations/CpAICombineUnloader.lua @@ -215,9 +215,9 @@ function CpAICombineUnloader:getCanStartCp(superFunc) return superFunc(self) or self:getCanStartCpCombineUnloader() and not self:getIsCpCourseRecorderActive() end -function CpAICombineUnloader:getCpStartableJob(superFunc) +function CpAICombineUnloader:getCpStartableJob(superFunc, ...) local spec = self.spec_cpAICombineUnloader - return superFunc(self) or self:getCanStartCpCombineUnloader() and spec.cpJob + return superFunc(self, ...) or self:getCanStartCpCombineUnloader() and spec.cpJob end function CpAICombineUnloader:getCpStartText(superFunc) diff --git a/scripts/specializations/CpAIFieldWorker.lua b/scripts/specializations/CpAIFieldWorker.lua index 4fd0569f7..c16ef3294 100644 --- a/scripts/specializations/CpAIFieldWorker.lua +++ b/scripts/specializations/CpAIFieldWorker.lua @@ -299,9 +299,9 @@ function CpAIFieldWorker:getCanStartCp(superFunc) end --- Gets the field work job for the hud or start action event. -function CpAIFieldWorker:getCpStartableJob(superFunc) +function CpAIFieldWorker:getCpStartableJob(superFunc, ...) local spec = self.spec_cpAIFieldWorker - return self:getCanStartCpFieldWork() and self:hasCpCourse() and spec.cpJob or superFunc(self) + return self:getCanStartCpFieldWork() and self:hasCpCourse() and spec.cpJob or superFunc(self, ...) end function CpAIFieldWorker:getCpStartText(superFunc) diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index 972bf1174..0b8771950 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -1,6 +1,8 @@ local modName = CpAISiloLoaderWorker and CpAISiloLoaderWorker.MOD_NAME -- for reload +--- Specialization for the silo loader job +--- Used for shovel loader and big silo loader, like the Ropa NarwaRo. ---@class CpAISiloLoaderWorker CpAISiloLoaderWorker = {} @@ -92,11 +94,12 @@ function CpAISiloLoaderWorker:onUpdate(dt) local spec = self.spec_cpAISiloLoaderWorker end ---- Is the bunker silo allowed? function CpAISiloLoaderWorker:getCanStartCpSiloLoaderWorker() - return not self:getCanStartCpFieldWork() and not self:getCanStartCpBaleFinder() and not self:hasCpCourse() - and not self:getCanStartCpCombineUnloader() and AIUtil.hasChildVehicleWithSpecialization(self, Shovel) - and AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) + return not self:getCanStartCpFieldWork() + and not self:getCanStartCpBaleFinder() + and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt)) + and not self:getCanStartCpCombineUnloader() + and AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end function CpAISiloLoaderWorker:getCanStartCp(superFunc) @@ -105,7 +108,16 @@ end function CpAISiloLoaderWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAISiloLoaderWorker - return superFunc(self) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob + if AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) then + return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob + elseif isStartedByHud then + if self:getCanStartCpSiloLoaderWorker() + and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING then + return superFunc(self, isStartedByHud) or spec.cpJob + end + + end + return superFunc(self, isStartedByHud) end function CpAISiloLoaderWorker:getCpStartText(superFunc) @@ -168,11 +180,16 @@ end function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap) if self.isServer then - local strategy = AIDriveStrategySiloLoader.new() - -- this also starts the strategy + local strategy + if AIUtil.hasAIImplementWithSpecialization(self, ConveyorBelt) then + CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting a silo loader strategy.") + strategy = AIDriveStrategySiloLoader.new() + else + CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting a shovel silo loader strategy.") + strategy = AIDriveStrategyShovelSiloLoader.new() + end strategy:setSiloAndHeap(bunkerSilo, heap) strategy:setAIVehicle(self, jobParameters) - CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting silo worker job.") self:startCpWithStrategy(strategy) end end diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 2034afe03..a600e569d 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -265,7 +265,7 @@ function CpAIWorker:changeCourseVisibility() end function CpAIWorker:startStopCpActionEvent() - self:cpStartStopDriver() + self:cpStartStopDriver(true) end --- Directly starts a cp job or stops a currently active job. diff --git a/scripts/specializations/CpGamePadHud.lua b/scripts/specializations/CpGamePadHud.lua index 1afa7e2ec..9837bdb2e 100644 --- a/scripts/specializations/CpGamePadHud.lua +++ b/scripts/specializations/CpGamePadHud.lua @@ -197,7 +197,8 @@ function CpGamePadHud:actionEventOpenCloseDisplay() elseif self:getCanStartCpBunkerSiloWorker() and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO or AIUtil.hasChildVehicleWithSpecialization(self, Leveler) then page = CpGamePadHud.BUNKER_SILO_PAGE - elseif self:getCanStartCpSiloLoaderWorker() then + elseif self:getCanStartCpSiloLoaderWorker() and ( AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) + or self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING) then page = CpGamePadHud.SILO_LOADER_PAGE else page = CpGamePadHud.FIELDWORK_PAGE diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua new file mode 100644 index 000000000..2873e8eac --- /dev/null +++ b/scripts/specializations/CpShovelPositions.lua @@ -0,0 +1,422 @@ +--[[ + This specialization is used to control the shovel position into 4 stages: + - Loading position 0.2m above the ground. + - Transport position + - Pre unloading position + - Unloading position + + TODO: + - Fine tuning + - Testing from different front loaders ... + - Add Telescopic handlers support. +]]-- + +---@class CpShovelPositions +CpShovelPositions = { + DEACTIVATED = 0, + LOADING = 1, + TRANSPORT = 2, + PRE_UNLOAD = 3, + UNLOADING = 4, + NUM_STATES = 4, + LOADING_POSITION = { + ARM_LIMITS = { + 0, + 0.1 + }, + SHOVEL_LIMITS = { + 88, + 92 + }, + }, + TRANSPORT_POSITION = { + ARM_LIMITS = { + 0.1, + 0.20 + }, + SHOVEL_LIMITS = { + 53, + 57 + }, + }, + PRE_UNLOAD_POSITION = { + ARM_LIMITS = { + 3, + 4 + }, + SHOVEL_LIMITS = { + 43, + 47 + }, + }, + UNLOADING_POSITION = { + ARM_LIMITS = { + 4, + 5 + }, + }, + DEBUG = false +} +CpShovelPositions.MOD_NAME = g_currentModName +CpShovelPositions.NAME = ".cpShovelPositions" +CpShovelPositions.SPEC_NAME = CpShovelPositions.MOD_NAME .. CpShovelPositions.NAME +CpShovelPositions.KEY = "." .. CpShovelPositions.SPEC_NAME + +function CpShovelPositions.initSpecialization() + local schema = Vehicle.xmlSchemaSavegame + if CpShovelPositions.DEBUG then + g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelState', 'cpSetShovelState', 'consoleCommandSetShovelState', CpShovelPositions) + end +end + +function CpShovelPositions.prerequisitesPresent(specializations) + return SpecializationUtil.hasSpecialization(Shovel, specializations) and not + SpecializationUtil.hasSpecialization(Trailer, specializations) and not + SpecializationUtil.hasSpecialization(ConveyorBelt, specializations) +end + +function CpShovelPositions.register(typeManager, typeName, specializations) + if CpShovelPositions.prerequisitesPresent(specializations) then + typeManager:addSpecialization(typeName, CpShovelPositions.SPEC_NAME) + end +end + +function CpShovelPositions.registerEventListeners(vehicleType) + SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpShovelPositions) + SpecializationUtil.registerEventListener(vehicleType, "onDraw", CpShovelPositions) + SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", CpShovelPositions) + SpecializationUtil.registerEventListener(vehicleType, "onPostAttach", CpShovelPositions) +end + +function CpShovelPositions.registerFunctions(vehicleType) + SpecializationUtil.registerFunction(vehicleType, "cpSetShovelState", CpShovelPositions.cpSetShovelState) + SpecializationUtil.registerFunction(vehicleType, "cpResetShovelState", CpShovelPositions.cpResetShovelState) + SpecializationUtil.registerFunction(vehicleType, "cpSetupShovelPositions", CpShovelPositions.cpSetupShovelPositions) + SpecializationUtil.registerFunction(vehicleType, "areCpShovelPositionsDirty", CpShovelPositions.areCpShovelPositionsDirty) + SpecializationUtil.registerFunction(vehicleType, "getCpShovelUnloadingPositionHeight", CpShovelPositions.getCpShovelUnloadingPositionHeight) +end + + +function CpShovelPositions:onLoad(savegame) + --- Register the spec: spec_ShovelPositions + self.spec_cpShovelPositions = self["spec_" .. CpShovelPositions.SPEC_NAME] + local spec = self.spec_cpShovelPositions + --- Current shovel state. + spec.state = CpShovelPositions.DEACTIVATED + spec.isDirty = false +end + +function CpShovelPositions:onPostAttach() + if self.spec_cpShovelPositions then + CpShovelPositions.cpSetupShovelPositions(self) + end +end + +function CpShovelPositions:onDraw() + if CpShovelPositions.DEBUG and self:getRootVehicle() then + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + if shovelNode then + DebugUtil.drawDebugNode(shovelNode, "shovelNode") + end + end +end + +function CpShovelPositions:consoleCommandSetShovelState(state) + local vehicle = g_currentMission.controlledVehicle + if not vehicle then + CpUtil.info("Not entered a valid vehicle!") + end + state = tonumber(state) + if state == nil or state < 0 or state > CpShovelPositions.NUM_STATES then + CpUtil.infoVehicle(vehicle, "No valid state(0 - %d) was given!", CpShovelPositions.NUM_STATES) + return + end + if not vehicle:getIsAIActive() then + local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) + if found then + shovels[1]:cpSetShovelState(state) + else + CpUtil.infoVehicle(vehicle, "No valid vehicle/implement with a shovel was found!") + end + else + CpUtil.infoVehicle(vehicle, "Error, AI is active!") + end +end + +--- Changes the current shovel state position. +function CpShovelPositions:cpSetShovelState(state) + local spec = self.spec_cpShovelPositions + if spec.state ~= state then + spec.state = state + if state == CpShovelPositions.DEACTIVATED then + ImplementUtil.stopMovingTool(spec.armVehicle, spec.armTool) + ImplementUtil.stopMovingTool(spec.shovelVehicle, spec.shovelTool) + end + end +end + +--- Deactivates the shovel position control. +function CpShovelPositions:cpResetShovelState() + CpShovelPositions.debug(self, "Reset shovelPositionState.") + local spec = self.spec_cpShovelPositions + spec.state = CpShovelPositions.DEACTIVATED + ImplementUtil.stopMovingTool(spec.armVehicle, spec.armTool) + ImplementUtil.stopMovingTool(spec.shovelVehicle, spec.shovelTool) +end + +function CpShovelPositions:areCpShovelPositionsDirty() + local spec = self.spec_cpShovelPositions + return spec.isDirty +end + +--- Sets the relevant moving tools. +function CpShovelPositions:cpSetupShovelPositions() + local spec = self.spec_cpShovelPositions + spec.shovelToolIx = nil + spec.armToolIx = nil + spec.shovelTool = nil + spec.armTool = nil + local rootVehicle = self:getRootVehicle() + local childVehicles = rootVehicle:getChildVehicles() + for _, vehicle in ipairs(childVehicles) do + if vehicle.spec_cylindered then + for i, tool in pairs(vehicle.spec_cylindered.movingTools) do + if tool.controlGroupIndex ~= nil then + if tool.axis == "AXIS_FRONTLOADER_ARM" then + spec.armToolIx = i + spec.armTool = tool + spec.armVehicle = vehicle + spec.armProjectionNode = CpUtil.createNode("CpShovelArmProjectionNode", + 0, 0, 0, getParent(tool.node)) + elseif tool.axis == "AXIS_FRONTLOADER_TOOL" then + spec.shovelToolIx = i + spec.shovelTool = tool + spec.shovelVehicle = vehicle + spec.shovelProjectionNode = CpUtil.createNode("CpShovelProjectionNode", + 0, 0, 0, getParent(tool.node)) + end + end + end + end + end +end + +function CpShovelPositions:onUpdateTick(dt) + local spec = self.spec_cpShovelPositions + if spec.shovelToolIx == nil or spec.armToolIx == nil then + return + end + if spec.state == CpShovelPositions.LOADING then + CpShovelPositions.updateLoadingPosition(self, dt) + elseif spec.state == CpShovelPositions.TRANSPORT then + CpShovelPositions.updateTransportPosition(self, dt) + elseif spec.state == CpShovelPositions.PRE_UNLOAD then + CpShovelPositions.updatePreUnloadPosition(self, dt) + elseif spec.state == CpShovelPositions.UNLOADING then + CpShovelPositions.updateUnloadingPosition(self, dt) + end +end + +--- Changes the shovel angle dependent on the selected position. +function CpShovelPositions.setShovelPosition(dt, spec, shovel, shovelNode, angle, limits) + local min, max = unpack(limits) + local targetAngle = math.rad(min) + math.rad(max - min)/2 + if math.deg(angle) < max and math.deg(angle) > min then + ImplementUtil.stopMovingTool(spec.shovelVehicle, spec.shovelTool) + return false + end + + + local curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(spec.shovelTool.node) + local oldRot = curRot[spec.shovelTool.rotationAxis] + local radius = calcDistanceFrom(shovelNode, spec.shovelTool.node) + local x, y, z = getTranslation(spec.shovelTool.node) + + setTranslation(spec.shovelProjectionNode, x, y, z) + + setRotation(spec.shovelProjectionNode, targetAngle - math.pi/2, 0, 0) + + local sx, sy, sz = getWorldTranslation(shovelNode) + local tx, _, tz = getWorldTranslation(spec.shovelTool.node) + local px, py, pz = localToWorld(spec.shovelProjectionNode, 0, 0, radius) + + if CpShovelPositions.DEBUG then + DebugUtil.drawDebugCircleAtNode(spec.shovelTool.node, radius, 30, nil, true) + + DebugUtil.drawDebugLine(px, py, pz, sx, sy, sz) + DebugUtil.drawDebugLine(px, py, pz, tx, py, tz) + end + + local yRot = math.atan2(MathUtil.vector3Length(px - sx, py - sy, pz - sz), + MathUtil.vector3Length(px - tx, py - py, pz - tz)) + + local dyRot = 0 + if angle > targetAngle then + dyRot = -yRot + else + dyRot = yRot + end + + CpShovelPositions.debug(shovel, + "Shovel position(%d) angle: %.2f, targetAngle: %.2f, yRot: %.2f, oldRot: %.2f", + spec.state, math.deg(angle), math.deg(targetAngle), math.deg(dyRot), math.deg(oldRot)) + + return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, + MathUtil.clamp(oldRot + dyRot , spec.shovelTool.rotMin, spec.shovelTool.rotMax)) +end + +--- Changes the front loader angle dependent on the selected position, relative to a target height. +function CpShovelPositions.setArmPosition(dt, spec, shovel, shovelNode, limits) + --- Interval in which the shovel height should be in. + local min, max = unpack(limits) + local targetHeight = min + (max - min)/2 + + local attacherJointNode = shovel.spec_attachable.attacherJoint.node + local _, shovelY, shovelR = localToLocal(attacherJointNode, shovelNode, 0, 0, 0) + + local x, y, z = getWorldTranslation(attacherJointNode) + local dy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) + + local targetAttacherHeight = dy + shovelY + targetHeight + local diff = targetAttacherHeight - y + + CpShovelPositions.debug(shovel, "shovel => y: %.2f, z: %.2f, targetAttacherHeight: %.2f", shovelY, shovelR, targetAttacherHeight) + + if math.abs(diff) < (max - min)/2 then + ImplementUtil.stopMovingTool(spec.armVehicle, spec.armTool) + return false + end + + local curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(spec.armTool.node) + local oldRot = curRot[spec.armTool.rotationAxis] + + + setWorldTranslation(spec.armProjectionNode, x, targetAttacherHeight, z) + + + local _, ay, _ = localToLocal(spec.armTool.node, spec.armVehicle.rootNode, 0, 0, 0) + + local nodeDiff = MathUtil.clamp( targetHeight - ay , -shovelR, shovelR) + ay + + local ax, _, az = getWorldTranslation(spec.armTool.node) + local sx, sy, sz = getWorldTranslation(spec.shovelTool.node) + + if CpShovelPositions.DEBUG then + DebugUtil.drawDebugCircleAtNode(spec.armTool.node, shovelR, 30, nil, true) + + DebugUtil.drawDebugNode(spec.armProjectionNode, "Projection node", false, 0) + + DebugUtil.drawDebugLine(x, targetAttacherHeight, z, sx, sy, sz) + DebugUtil.drawDebugLine(x, targetAttacherHeight, z, ax, targetAttacherHeight, az) -- y + end + local yRot = math.atan2(MathUtil.vector3Length(x - sx, targetAttacherHeight - sy, z - sz), + MathUtil.vector3Length(x - ax, 0, z - az)) + + if diff < 0 then + yRot = yRot + else + yRot = -yRot + end + + CpShovelPositions.debug(shovel, + "Arm position(%d) height diff: %.2f, targetHeight: %.2f, old angle: %.2f, yRot: %.2f", + spec.state, diff, targetHeight, math.deg(oldRot), math.deg(yRot)) + + return ImplementUtil.moveMovingToolToRotation(spec.armVehicle, spec.armTool, dt, + MathUtil.clamp(oldRot + yRot , spec.armTool.rotMin, spec.armTool.rotMax)) +end + +function CpShovelPositions:updateLoadingPosition(dt) + local spec = self.spec_cpShovelPositions + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local isDirty + if angle then + isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS) + isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:updateTransportPosition(dt) + local spec = self.spec_cpShovelPositions + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local isDirty + if angle then + isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS) + isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:updatePreUnloadPosition(dt) + local spec = self.spec_cpShovelPositions + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local isDirty + if angle then + isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS) + isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:updateUnloadingPosition(dt) + local spec = self.spec_cpShovelPositions + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local isDirty + if angle and maxAngle then + isDirty = CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) + isDirty = isDirty or CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, {math.deg(maxAngle), math.deg(maxAngle) + 2}) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:getCpShovelUnloadingPositionHeight() + return CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS +end + +--- Gets all relevant shovel data. +function CpShovelPositions:getShovelData() + local shovelSpec = self.spec_shovel + if shovelSpec == nil then + CpShovelPositions.debug(self, "Shovel spec not found!") + return + end + local info = shovelSpec.shovelDischargeInfo + if info == nil or info.node == nil then + CpShovelPositions.debugt(self, "Info or node not found!") + return + end + if info.maxSpeedAngle == nil or info.minSpeedAngle == nil then + CpShovelPositions.debug(self, "maxSpeedAngle or minSpeedAngle not found!") + return + end + + if shovelSpec.shovelNodes == nil then + CpShovelPositions.debug(self, "Shovel nodes not found!") + return + end + + if shovelSpec.shovelNodes[1] == nil then + CpShovelPositions.debug(self, "Shovel nodes index 0 not found!") + return + end + + if shovelSpec.shovelNodes[1].node == nil then + CpShovelPositions.debug(self, "Shovel node not found!") + return + end + local _, dy, _ = localDirectionToWorld(info.node, 0, 0, 1) + local angle = math.acos(dy) + local factor = math.max(0, math.min(1, (angle - info.minSpeedAngle) / (info.maxSpeedAngle - info.minSpeedAngle))) + return angle, shovelSpec.shovelNodes[1].node, info.maxSpeedAngle, info.minSpeedAngle, factor +end + + + +function CpShovelPositions.debug(implement, ...) + if CpShovelPositions.DEBUG then + CpUtil.infoImplement(implement, ...) + end +end \ No newline at end of file diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua new file mode 100644 index 000000000..0fc40dd6f --- /dev/null +++ b/scripts/trigger/TriggerManager.lua @@ -0,0 +1,44 @@ +--- Links all the needed trigger, except bunker silos to trigger wrappers. +TriggerManager = CpObject() + +function TriggerManager:init() + self.unloadSilos = {} +end + +function TriggerManager:addUnloadingSilo(silo) + if silo.exactFillRootNode ~= nil then + self.unloadSilos[silo.exactFillRootNode] = CpTrigger(silo, silo.exactFillRootNode) + end +end + +function TriggerManager:removeUnloadingSilo(silo) + if silo.exactFillRootNode ~= nil then + if self.unloadSilos[silo.exactFillRootNode] then + self.unloadSilos[silo.exactFillRootNode]:delete() + self.unloadSilos[silo.exactFillRootNode] = nil + end + end +end + +function TriggerManager:getUnloadTriggerForNode(node) + return self.unloadSilos[node] +end + + +g_triggerManager = TriggerManager() + +local function addUnloadingSilo(silo, superFunc, ...) + local ret = superFunc(silo, ...) + g_triggerManager:addUnloadingSilo(silo) + return ret +end + +UnloadTrigger.load = Utils.overwrittenFunction(UnloadTrigger.load, addUnloadingSilo) + + +local function removeUnloadingSilo(silo, ...) + g_triggerManager:removeUnloadingSilo(silo) +end + +UnloadTrigger.delete = Utils.prependedFunction(UnloadTrigger.delete, removeUnloadingSilo) + diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua new file mode 100644 index 000000000..6729789f1 --- /dev/null +++ b/scripts/trigger/TriggerWrapper.lua @@ -0,0 +1,20 @@ + +---@class CpTrigger +CpTrigger = CpObject() + +function CpTrigger:init(trigger, node) + self.trigger = trigger + self.node = node +end + +function CpTrigger:delete() + +end + +function CpTrigger:getNode() + return self.node +end + +function CpTrigger:getTrigger() + return self.trigger +end \ No newline at end of file From b102c581c9c9be5a0e08043ef7c1dd5af9266b61 Mon Sep 17 00:00:00 2001 From: schwiti6190 Date: Wed, 5 Jul 2023 19:03:42 +0000 Subject: [PATCH 005/107] Updated translations --- translations/translation_br.xml | 3 +++ translations/translation_cs.xml | 3 +++ translations/translation_ct.xml | 3 +++ translations/translation_cz.xml | 3 +++ translations/translation_da.xml | 3 +++ translations/translation_de.xml | 3 +++ translations/translation_ea.xml | 3 +++ translations/translation_en.xml | 3 +++ translations/translation_es.xml | 3 +++ translations/translation_fc.xml | 3 +++ translations/translation_fi.xml | 3 +++ translations/translation_fr.xml | 3 +++ translations/translation_hu.xml | 3 +++ translations/translation_it.xml | 3 +++ translations/translation_jp.xml | 3 +++ translations/translation_kr.xml | 3 +++ translations/translation_nl.xml | 3 +++ translations/translation_no.xml | 3 +++ translations/translation_pl.xml | 3 +++ translations/translation_pt.xml | 3 +++ translations/translation_ro.xml | 3 +++ translations/translation_ru.xml | 3 +++ translations/translation_sv.xml | 3 +++ translations/translation_tr.xml | 3 +++ 24 files changed, 72 insertions(+) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index 5ddb82098..ad251013c 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 35d8fd82a..046448b0f 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml index de972a2f7..5f3785b2b 100644 --- a/translations/translation_ct.xml +++ b/translations/translation_ct.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 865c96a0c..2a0b74f6b 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_da.xml b/translations/translation_da.xml index 78d396796..3f42cd746 100644 --- a/translations/translation_da.xml +++ b/translations/translation_da.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 920aaac57..63a4045ac 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml index 28bdebd77..b96cffca8 100644 --- a/translations/translation_ea.xml +++ b/translations/translation_ea.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 056b09749..ca149cc2d 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index e8639b195..106e4f123 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_fc.xml b/translations/translation_fc.xml index f5dfdcdfd..c6c370537 100644 --- a/translations/translation_fc.xml +++ b/translations/translation_fc.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_fi.xml b/translations/translation_fi.xml index 18a5c3445..6f9113620 100644 --- a/translations/translation_fi.xml +++ b/translations/translation_fi.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index cad16a88e..7fe3d9197 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index b21bfd0a1..603d2bdfd 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 6db335ca6..306b20e9c 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index cb2541d09..ab3407769 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_kr.xml b/translations/translation_kr.xml index 45800dfbb..ca2647db7 100644 --- a/translations/translation_kr.xml +++ b/translations/translation_kr.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 62b0470bb..9dad80708 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_no.xml b/translations/translation_no.xml index 07bb8d178..911f3562a 100644 --- a/translations/translation_no.xml +++ b/translations/translation_no.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index 4fb5e7f30..e1e2f7f30 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 765e19c9e..9c9ef92aa 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_ro.xml b/translations/translation_ro.xml index 976b29198..ae794202e 100644 --- a/translations/translation_ro.xml +++ b/translations/translation_ro.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index b2c114421..141be2eb0 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_sv.xml b/translations/translation_sv.xml index 63bbb2930..38a3614af 100644 --- a/translations/translation_sv.xml +++ b/translations/translation_sv.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 12c65739d..d504d7ed4 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -25,6 +25,7 @@ + @@ -93,6 +94,8 @@ + + From cdd4dd1dd809c371e00e6ff24d55e01965fbb1d7 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 5 Jul 2023 21:21:48 +0200 Subject: [PATCH 006/107] Translation fix and adjustment --- config/MasterTranslations.xml | 4 + .../ai/AIDriveStrategyShovelSiloLoader.lua | 76 +++---------------- scripts/ai/controllers/ShovelController.lua | 6 +- 3 files changed, 19 insertions(+), 67 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index ed8a652c8..518eaa41e 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -305,6 +305,10 @@ + + + + diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 11f8019a2..7fb7cdf8c 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -40,7 +40,6 @@ AIDriveStrategyShovelSiloLoader.myStates = { DRIVING_OUT_OF_SILO = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, WAITING_FOR_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD_POSITION = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, - DRIVING_TO_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, UNLOADING = {shovelPosition = ShovelController.POSITIONS.UNLOADING, shovelMovingSpeed = 0}, } @@ -145,12 +144,6 @@ function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) self:startPathfindingToUnloadPosition() elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then self.state = self.states.WAITING_FOR_TRAILER - elseif self.state == self.states.DRIVING_TO_TRAILER then - local course = Course.createFromNodeToNode(self.vehicle, self.vehicle:getAIDirectionNode(), self.unloadNode, - 0, 0, 0, 3, false) - self:startCourse(course, 1) - - self.state = self.states.DRIVING_TO_UNLOAD elseif self.state == self.states.DRIVING_TO_UNLOAD then self.state = self.states.UNLOADING @@ -211,12 +204,13 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) elseif self.state == self.states.WAITING_FOR_TRAILER then self:setMaxSpeed(0) self:searchForTrailerToUnloadInto() - elseif self.state == self.states.DRIVING_TO_TRAILER then - self:setMaxSpeed(self.settings.fieldSpeed:getValue()) elseif self.state == self.states.DRIVING_TO_UNLOAD then self:setMaxSpeed(self.settings.reverseSpeed:getValue()) - if self.shovelController:isShovelOverTrailer(self.targetTrailer.trailer) then - self.state = self.states.UNLOADING + if self.targetTrailer then + if self.shovelController:isShovelOverTrailer(self.targetTrailer.exactFillRootNode) then + self.state = self.states.UNLOADING + self:setMaxSpeed(0) + end end elseif self.state == self.states.UNLOADING then self:setMaxSpeed(0) @@ -351,10 +345,13 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() exactFillRootNode = exactFillRootNode, trailer = trailer } - -- self:debug("Found valid trailer %s attached to %s with a distance of %.2fm to the silo front center for fill type %s in fill unit %d.", - -- CpUtil.getName(vehicle), CpUtil.getName(vehicle.rootVehicle), - -- self.maxValidTrailerDistance, g_fillTypeManager:getFillTypeTitleByIndex(fillType), fillUnitIndex) - self:startPathfindingToTrailer(trailer, exactFillRootNode) + local dx, _, dz = getWorldTranslation(exactFillRootNode) + local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) + local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, -3, 0, 3, false) + local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) + self:startCourse(course, firstWpIx) + self.state = self.states.DRIVING_TO_UNLOAD end end @@ -395,55 +392,6 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, end end ---- Find an alignment path to the heap course. -function AIDriveStrategyShovelSiloLoader:startPathfindingToTrailer(trailer, exactFillRootNode) - if not self.pathfinder or not self.pathfinder:isActive() then - self.state = self.states.WAITING_FOR_PATHFINDER - local dx, _, _ = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) - - local loadingLeftSide = dx > 0 - - local x, y, z = localToLocal(exactFillRootNode, trailer.rootNode, 0, 0, 0) - - local gx, gy, gz = localToWorld(trailer.rootNode, x, y, z) - local dirX, dirZ = localDirectionToWorld(trailer.rootNode, loadingLeftSide and 1 or -1, 0, 0) - local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) - setTranslation(self.unloadNode, gx, gy, gz) - setRotation(self.unloadNode, 0, yRot, 0) - - local spaceToTrailer = math.max(self.turningRadius, self.safeSpaceToTrailer) - - self.pathfindingStartedAt = g_currentMission.time - local done, path, goalNodeInvalid - self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( - self.vehicle, self.unloadNode, - 0, -spaceToTrailer, true, - nil, {}, nil, - 0, nil, true - ) - if done then - return self:onPathfindingDoneToTrailer(path, goalNodeInvalid) - else - self:setPathfindingDoneCallback(self, self.onPathfindingDoneToTrailer) - end - else - self:debug('Pathfinder already active') - end - return true -end - -function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToTrailer(path, goalNodeInvalid) - if path and #path > 2 then - self:debug("Found alignment path to trailer.") - local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) - self:startCourse(course, 1) - self.state = self.states.DRIVING_TO_TRAILER - else - self:debug("Failed to find valid path to trailer!") - self.state = self.sates.WAITING_FOR_TRAILER - end -end - ---------------------------------------------------------------- --- Silo work ---------------------------------------------------------------- diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index adc7efd12..08774f4ae 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -57,12 +57,12 @@ function ShovelController:getDischargeNode() end --- Is the shovel node over the trailer? ----@param trailer table +---@param refNode number ---@param margin number|nil ---@return boolean -function ShovelController:isShovelOverTrailer(trailer, margin) +function ShovelController:isShovelOverTrailer(refNode, margin) local node = self:getShovelNode() - local x, y, z = localToLocal(trailer.rootNode, node, 0, 0, 0) + local x, y, z = localToLocal(refNode, node, 0, 0, 0) margin = margin or 0 return z < margin end From 5da1079554038ca25323c95e76a1c3e1fac6c684 Mon Sep 17 00:00:00 2001 From: schwiti6190 Date: Wed, 5 Jul 2023 19:22:09 +0000 Subject: [PATCH 007/107] Updated translations --- translations/translation_br.xml | 1 + translations/translation_cs.xml | 1 + translations/translation_ct.xml | 1 + translations/translation_cz.xml | 1 + translations/translation_da.xml | 1 + translations/translation_de.xml | 1 + translations/translation_ea.xml | 1 + translations/translation_en.xml | 1 + translations/translation_es.xml | 1 + translations/translation_fc.xml | 1 + translations/translation_fi.xml | 1 + translations/translation_fr.xml | 1 + translations/translation_hu.xml | 1 + translations/translation_it.xml | 1 + translations/translation_jp.xml | 1 + translations/translation_kr.xml | 1 + translations/translation_nl.xml | 1 + translations/translation_no.xml | 1 + translations/translation_pl.xml | 1 + translations/translation_pt.xml | 1 + translations/translation_ro.xml | 1 + translations/translation_ru.xml | 1 + translations/translation_sv.xml | 1 + translations/translation_tr.xml | 1 + 24 files changed, 24 insertions(+) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index ad251013c..b4e68c90f 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 046448b0f..37d34c151 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml index 5f3785b2b..29f9b8820 100644 --- a/translations/translation_ct.xml +++ b/translations/translation_ct.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 2a0b74f6b..e5bfe8b43 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_da.xml b/translations/translation_da.xml index 3f42cd746..71b70cb3e 100644 --- a/translations/translation_da.xml +++ b/translations/translation_da.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 63a4045ac..6893d93a0 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml index b96cffca8..487297825 100644 --- a/translations/translation_ea.xml +++ b/translations/translation_ea.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index ca149cc2d..454e60989 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index 106e4f123..d9278b066 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_fc.xml b/translations/translation_fc.xml index c6c370537..1e82fa96b 100644 --- a/translations/translation_fc.xml +++ b/translations/translation_fc.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_fi.xml b/translations/translation_fi.xml index 6f9113620..15f07d006 100644 --- a/translations/translation_fi.xml +++ b/translations/translation_fi.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index 7fe3d9197..b7aca1aa1 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 603d2bdfd..7e9e1e8a0 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 306b20e9c..bb65ac788 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index ab3407769..32edfee7c 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_kr.xml b/translations/translation_kr.xml index ca2647db7..75bb4f709 100644 --- a/translations/translation_kr.xml +++ b/translations/translation_kr.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 9dad80708..89bb6efb2 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_no.xml b/translations/translation_no.xml index 911f3562a..4491229ea 100644 --- a/translations/translation_no.xml +++ b/translations/translation_no.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index e1e2f7f30..fdf218218 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 9c9ef92aa..44394a95f 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_ro.xml b/translations/translation_ro.xml index ae794202e..0cbfb5ef2 100644 --- a/translations/translation_ro.xml +++ b/translations/translation_ro.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 141be2eb0..6b842b5d0 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_sv.xml b/translations/translation_sv.xml index 38a3614af..4f125e18a 100644 --- a/translations/translation_sv.xml +++ b/translations/translation_sv.xml @@ -94,6 +94,7 @@ + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index d504d7ed4..c2b85d87c 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -94,6 +94,7 @@ + From d9b98c96f02fa754c99042d1953c7fba69cbb1be Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:42:56 +0200 Subject: [PATCH 008/107] WIP --- Courseplay.lua | 2 + .../CombineUnloaderJobParameterSetup.xml | 2 +- modDesc.xml | 1 + .../ai/AIDriveStrategyShovelSiloLoader.lua | 22 +++- scripts/ai/controllers/ShovelController.lua | 5 +- scripts/ai/jobs/CpAIJobSiloLoader.lua | 35 +++-- scripts/ai/jobs/CpJobParameters.lua | 7 +- .../CpAIParameterUnloadingStation.lua | 2 +- scripts/gui/CoursePlot.lua | 53 ++------ scripts/gui/CpAIFrameExtended.lua | 83 +++++++----- scripts/gui/CpGuiUtil.lua | 67 ++++++++++ scripts/gui/FieldPlot.lua | 2 +- scripts/gui/UnloadingTriggerPlot.lua | 49 +++++++ scripts/silo/BunkerSiloVehicleController.lua | 22 +++- scripts/silo/BunkerSiloWrapper.lua | 8 -- scripts/trigger/TriggerManager.lua | 124 +++++++++++++++++- scripts/trigger/TriggerWrapper.lua | 6 + 17 files changed, 371 insertions(+), 119 deletions(-) create mode 100644 scripts/gui/UnloadingTriggerPlot.lua diff --git a/Courseplay.lua b/Courseplay.lua index f4ff27aea..ff40bf12d 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -194,12 +194,14 @@ FSCareerMissionInfo.saveToXMLFile = Utils.prependedFunction(FSCareerMissionInfo. function Courseplay:update(dt) g_devHelper:update() g_bunkerSiloManager:update(dt) + g_triggerManager:update(dt) end function Courseplay:draw() if not g_gui:getIsGuiVisible() then g_vineScanner:draw() g_bunkerSiloManager:draw() + g_triggerManager:draw() end g_devHelper:draw() CpDebug:draw() diff --git a/config/jobParameters/CombineUnloaderJobParameterSetup.xml b/config/jobParameters/CombineUnloaderJobParameterSetup.xml index 21cd74d8e..92b203fe4 100644 --- a/config/jobParameters/CombineUnloaderJobParameterSetup.xml +++ b/config/jobParameters/CombineUnloaderJobParameterSetup.xml @@ -27,7 +27,7 @@ - + diff --git a/modDesc.xml b/modDesc.xml index b57225da1..b0d9fc3e8 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -381,6 +381,7 @@ Changelog 7.1.0.0: + diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 7fb7cdf8c..bb5b13975 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -42,6 +42,7 @@ AIDriveStrategyShovelSiloLoader.myStates = { DRIVING_TO_UNLOAD_POSITION = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, UNLOADING = {shovelPosition = ShovelController.POSITIONS.UNLOADING, shovelMovingSpeed = 0}, + REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, } AIDriveStrategyShovelSiloLoader.safeSpaceToTrailer = 5 @@ -104,7 +105,7 @@ end function AIDriveStrategyShovelSiloLoader:initializeImplementControllers(vehicle) self:addImplementController(vehicle, MotorController, Motorized, {}, nil) self:addImplementController(vehicle, WearableController, Wearable, {}, nil) - + ---@type table, ShovelController self.shovelImplement, self.shovelController = self:addImplementController(vehicle, ShovelController, Shovel, {}, nil) end @@ -146,7 +147,12 @@ function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) self.state = self.states.WAITING_FOR_TRAILER elseif self.state == self.states.DRIVING_TO_UNLOAD then self.state = self.states.UNLOADING - + elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then + if self.shovelController:isEmpty() then + self:startDrivingToSilo() + else + self.state = self.states.WAITING_FOR_TRAILER + end --self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new()) end end @@ -215,8 +221,10 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) elseif self.state == self.states.UNLOADING then self:setMaxSpeed(0) if self:hasFinishedUnloading() then - self:startDrivingToSilo() + self:startReversingAwayFromUnloading() end + elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then + self:setMaxSpeed(self.settings.fieldSpeed:getValue()) end if self.state.properties.shovelPosition then if not self.frozen and self.shovelController:moveShovelToPosition(self.state.properties.shovelPosition) then @@ -440,7 +448,7 @@ end --- Unloading ---------------------------------------------------------------- function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() - if self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then + if self.targetTrailer and self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) return true end @@ -452,3 +460,9 @@ function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() return false end +function AIDriveStrategyShovelSiloLoader:startReversingAwayFromUnloading() + local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) + local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, 0 ) + self:startCourse(course, 1) + self.state = self.states.REVERSING_AWAY_FROM_UNLOAD +end diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 08774f4ae..f4e193e03 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -62,9 +62,10 @@ end ---@return boolean function ShovelController:isShovelOverTrailer(refNode, margin) local node = self:getShovelNode() - local x, y, z = localToLocal(refNode, node, 0, 0, 0) + local _, _, distShovelToRoot = localToLocal(node, self.implement.rootVehicle:getAIDirectionNode(), 0, 0, 0) + local _, _, distTrailerToRoot = localToLocal(refNode, self.implement.rootVehicle:getAIDirectionNode(), 0, 0, 0) margin = margin or 0 - return z < margin + return ( distTrailerToRoot - distShovelToRoot ) < margin end function ShovelController:moveShovelToLoadingPosition() diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index d3640814c..bffb77301 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -47,8 +47,8 @@ function CpAIJobSiloLoader:getCanStartJob() return self.hasValidPosition end ----@param vehicle Vehicle ----@param mission Mission +---@param vehicle table +---@param mission table ---@param farmId number ---@param isDirectStart boolean disables the drive to by giants ---@param isStartPositionInvalid boolean resets the drive to target position by giants and the field position to the vehicle position. @@ -104,7 +104,10 @@ function CpAIJobSiloLoader:validate(farmId) self.heapPlot:setVisible(false) self.heap = nil self.bunkerSilo = nil + self.unloadStation = nil + self.unloadTrigger = nil self.hasValidPosition = false + self:getCpJobParameters().unloadStation:setValue(-1) local isValid, errorMessage = CpAIJob.validate(self, farmId) if not isValid then return isValid, errorMessage @@ -132,12 +135,23 @@ function CpAIJobSiloLoader:validate(farmId) if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then --- Validate the trigger setup - local found, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) - - return false, g_i18n:getText("CP_error_no_unload_trigger_found") + local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) + if found then + self.unloadStation = unloadStation + self.unloadTrigger = unloadTrigger + if unloadStation == nil then + return false, g_i18n:getText("CP_error_no_unload_trigger_found") + end + local id = NetworkUtil.getObjectId(unloadStation) + if id ~= nil then + self:getCpJobParameters().unloadStation:setValue(id) + self:getCpJobParameters().unloadStation:validateUnloadingStation() + end + else + return false, g_i18n:getText("CP_error_no_unload_trigger_found") + end end - return isValid, errorMessage end @@ -170,16 +184,19 @@ end ---@return table|nil function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) local x, z = unloadPosition:getPosition() - local angle = unloadPosition:getAngle() - if x == nil or angle == nil then + local dirX, dirZ = unloadPosition:getDirection() + if x == nil or dirX == nil then return false end - return false + return g_triggerManager:getDischargeableUnloadTriggerAt( x, z, dirX, dirZ, 5, 5) end function CpAIJobSiloLoader:drawSilos(map) self.heapPlot:draw(map) g_bunkerSiloManager:drawSilos(map, self.bunkerSilo) + if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then + g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger) + end end diff --git a/scripts/ai/jobs/CpJobParameters.lua b/scripts/ai/jobs/CpJobParameters.lua index 9fec909c1..5aff5addb 100644 --- a/scripts/ai/jobs/CpJobParameters.lua +++ b/scripts/ai/jobs/CpJobParameters.lua @@ -214,8 +214,8 @@ function CpCombineUnloaderJobParameters:isFieldUnloadDisabled() return self.useGiantsUnload:getValue() end -function CpCombineUnloaderJobParameters:isUnloadStationSelectorDisabled() - return self:isGiantsUnloadDisabled() or not self.useGiantsUnload:getValue() +function CpCombineUnloaderJobParameters:isUnloadStationSelectorVisible() + return not self:isGiantsUnloadDisabled() and self.useGiantsUnload:getValue() end function CpCombineUnloaderJobParameters:isFieldUnloadPositionSelectorDisabled() @@ -227,7 +227,6 @@ function CpCombineUnloaderJobParameters:isFieldUnloadTipSideDisabled() return self:isFieldUnloadDisabled() or self:hasPipe() or not self.useFieldUnload:getValue() end - function CpCombineUnloaderJobParameters:hasPipe() local vehicle = self.job:getVehicle() if vehicle then @@ -348,7 +347,7 @@ function CpSiloLoaderJobParameters:isShovelSiloLoadDisabled() end function CpSiloLoaderJobParameters:isUnloadPositionDisabled() - return self:isShovelSiloLoadDisabled() or self.unloadAt == CpSiloLoaderJobParameters.UNLOAD_TRAILER + return false --self:isShovelSiloLoadDisabled() or self.unloadAt == CpSiloLoaderJobParameters.UNLOAD_TRAILER end function CpSiloLoaderJobParameters:isUnloadStationDisabled() diff --git a/scripts/ai/parameters/CpAIParameterUnloadingStation.lua b/scripts/ai/parameters/CpAIParameterUnloadingStation.lua index 1afe6ecd4..22c39d61c 100644 --- a/scripts/ai/parameters/CpAIParameterUnloadingStation.lua +++ b/scripts/ai/parameters/CpAIParameterUnloadingStation.lua @@ -41,7 +41,7 @@ end --- Applies the current position to the map hotspot. function CpAIParameterUnloadingStation:applyToMapHotspot(mapHotspot) - if not self:getCanBeChanged() then + if not self:getIsVisible() then return false end local unloadingStation = self:getUnloadingStation() diff --git a/scripts/gui/CoursePlot.lua b/scripts/gui/CoursePlot.lua index 8b9edded8..aa60429ba 100644 --- a/scripts/gui/CoursePlot.lua +++ b/scripts/gui/CoursePlot.lua @@ -79,44 +79,11 @@ function CoursePlot:setStopPosition( x, z ) self.stopPosition.x, self.stopPosition.z = x, z end -function CoursePlot:worldToScreen(map, worldX, worldZ, isHudMap) - local objectX = (worldX + map.worldCenterOffsetX) / map.worldSizeX * 0.5 + 0.25 - local objectZ = (worldZ + map.worldCenterOffsetZ) / map.worldSizeZ * 0.5 + 0.25 - local x, y, _, _ = map.fullScreenLayout:getMapObjectPosition(objectX, objectZ, 0, 0, 0, true) - local rot = 0 - local visible = true - if isHudMap then - --- The plot is displayed in the hud. - objectX = (worldX + map.worldCenterOffsetX) / map.worldSizeX * map.mapExtensionScaleFactor + map.mapExtensionOffsetX - objectZ = (worldZ + map.worldCenterOffsetZ) / map.worldSizeZ * map.mapExtensionScaleFactor + map.mapExtensionOffsetZ - - x, y, rot, visible = map.layout:getMapObjectPosition(objectX, objectZ, 0, 0, 0, false) - if map.state == IngameMap.STATE_MINIMAP_ROUND and map.layout.rotateWithMap then - x, y, rot, visible = self:getMapObjectPositionCircleLayoutFix(map.layout, objectX, objectZ, 0, 0, 0, false) - end - end - return x, y, rot, visible -end - ---- Giants was not so kind, as to allow getting the positions even, if the object is outside the map range ... ---- This is a custom version for: IngameMapLayoutCircle:getMapObjectPosition(...) -function CoursePlot:getMapObjectPositionCircleLayoutFix(layout, objectU, objectV, width, height, rot, persistent) - local mapWidth, mapHeight = layout:getMapSize() - local mapX, mapY = layout:getMapPosition() - local objectX = objectU * mapWidth + mapX - local objectY = (1 - objectV) * mapHeight + mapY - objectX, objectY, rot = layout:rotateWithMap(objectX, objectY, rot, persistent) - objectX = objectX - width * 0.5 - objectY = objectY - height * 0.5 - - return objectX, objectY, rot, true -end - -function CoursePlot:screenToWorld( x, y ) - local worldX = ((x - self.x) / self.scaleX) - self.worldOffsetX - local worldZ = ((y - self.y - self.height) / -self.scaleZ) - self.worldOffsetZ - return worldX, worldZ -end +-- function CoursePlot:screenToWorld( x, y ) +-- local worldX = ((x - self.x) / self.scaleX) - self.worldOffsetX +-- local worldZ = ((y - self.y - self.height) / -self.scaleZ) - self.worldOffsetZ +-- return worldX, worldZ +-- end -- Draw the waypoints in the screen area defined in new(), the bottom left corner -- is at worldX/worldZ coordinates, the size shown is worldWidth wide (and high) @@ -129,14 +96,14 @@ function CoursePlot:drawPoints(map, points, isHudMap) if points and #points > 1 then -- I know this is in helpers.lua already but that code has too many dependencies -- on global variables and vehicle.cp. - local wp, np, startX, startY, endX, endY, dx, dz, dx2D, dy2D, width, rotation, r, g, b, sv, ev + local wp, np, startX, startY, endX, endY, dx, dz, dx2D, dy2D, width, rotation, r, g, b, sv, ev, _ -- render a line between subsequent waypoints for i = 1, #points - 1 do wp = points[ i ] np = points[ i + 1 ] - startX, startY, _, sv = self:worldToScreen(map, wp.x, wp.z, isHudMap) - endX, endY, _, ev = self:worldToScreen(map, np.x, np.z, isHudMap) + startX, startY, _, sv = CpGuiUtil.worldToScreen(map, wp.x, wp.z, isHudMap) + endX, endY, _, ev = CpGuiUtil.worldToScreen(map, np.x, np.z, isHudMap) -- render only if it is on the plot area if startX and startY and endX and endY then @@ -176,7 +143,7 @@ function CoursePlot:draw(map, isHudMap) -- render a sign marking the end of the course if self.stopPosition.x and self.stopPosition.z then - local x, y, rotation = self:worldToScreen( map,self.stopPosition.x, self.stopPosition.z, isHudMap) + local x, y, rotation = CpGuiUtil.worldToScreen( map,self.stopPosition.x, self.stopPosition.z, isHudMap) if x and y then setOverlayColor( self.stopSignOverlayId, 1, 1, 1, 1 ) renderOverlay( self.stopSignOverlayId, @@ -188,7 +155,7 @@ function CoursePlot:draw(map, isHudMap) -- render a sign marking the current position used as a starting location for the course if self.startPosition.x and self.startPosition.z then - local x, y, rotation = self:worldToScreen(map, self.startPosition.x, self.startPosition.z, isHudMap) + local x, y, rotation = CpGuiUtil.worldToScreen(map, self.startPosition.x, self.startPosition.z, isHudMap) if x and y then setOverlayColor( self.startSignOverlayId, 1, 1, 1, 0.8 ) renderOverlay( self.startSignOverlayId, diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index b7d393fec..3ce7d0915 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -346,6 +346,10 @@ function CpInGameMenuAIFrameExtended:setMapSelectionItem(hotspot) if param:applyToMapHotspot(self.fieldSiloAiTargetMapHotspot) then g_currentMission:addMapHotspot(self.fieldSiloAiTargetMapHotspot) end + elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then + if param:applyToMapHotspot(self.loadAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) + end elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then if param:applyToMapHotspot(self.unloadAiTargetMapHotspot) then g_currentMission:addMapHotspot(self.unloadAiTargetMapHotspot) @@ -356,10 +360,6 @@ function CpInGameMenuAIFrameExtended:setMapSelectionItem(hotspot) if param:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) end - elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then - if param:applyToMapHotspot(self.loadAiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) - end end end end @@ -613,57 +613,68 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) self.aiTargetMapHotspot:setWorldRotation(angle) end - else - element:updateTitle() + end + end +end +InGameMenuAIFrame.updateParameterValueTexts = Utils.overwrittenFunction(InGameMenuAIFrame.updateParameterValueTexts, + CpInGameMenuAIFrameExtended.updateParameterValueTexts) - if parameterType == AIParameterType.UNLOADING_STATION then - if parameter.applyToMapHotspot then - if parameter:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then - g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) - end - else - local unloadingStation = parameter:getUnloadingStation() +function CpInGameMenuAIFrameExtended:updateWarnings() + for _, element in ipairs(self.currentJobElements) do + local parameter = element.aiParameter + local parameterType = parameter:getType() + if parameterType == AIParameterType.TEXT then + local title = element:getDescendantByName("title") - if unloadingStation ~= nil then - local placeable = unloadingStation.owningPlaceable + title:setText(parameter:getString()) + elseif parameterType == AIParameterType.UNLOADING_STATION then + element:updateTitle() + if parameter.applyToMapHotspot then + if parameter:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then + g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) + end + else + local unloadingStation = parameter:getUnloadingStation() - if placeable ~= nil and placeable.getHotspot ~= nil then - local hotspot = placeable:getHotspot(1) + if unloadingStation ~= nil then + local placeable = unloadingStation.owningPlaceable - if hotspot ~= nil then - local x, z = hotspot:getWorldPosition() + if placeable ~= nil and placeable.getHotspot ~= nil then + local hotspot = placeable:getHotspot(1) - self.aiUnloadingMarkerHotspot:setWorldPosition(x, z) - g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) - end + if hotspot ~= nil then + local x, z = hotspot:getWorldPosition() + self.aiUnloadingMarkerHotspot:setWorldPosition(x, z) + g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) end end end - elseif parameterType == AIParameterType.LOADING_STATION then - local loadingStation = parameter:getLoadingStation() + end + elseif parameterType == AIParameterType.UNLOADING_STATION then + element:updateTitle() + local loadingStation = parameter:getLoadingStation() - if loadingStation ~= nil and parameter:getCanBeChanged() then - local placeable = loadingStation.owningPlaceable + if loadingStation ~= nil and parameter:getCanBeChanged() then + local placeable = loadingStation.owningPlaceable - if placeable ~= nil and placeable.getHotspot ~= nil then - local hotspot = placeable:getHotspot(1) + if placeable ~= nil and placeable.getHotspot ~= nil then + local hotspot = placeable:getHotspot(1) - if hotspot ~= nil then - local x, z = hotspot:getWorldPosition() + if hotspot ~= nil then + local x, z = hotspot:getWorldPosition() - self.aiLoadingMarkerHotspot:setWorldPosition(x, z) - g_currentMission:addMapHotspot(self.aiLoadingMarkerHotspot) - end + self.aiLoadingMarkerHotspot:setWorldPosition(x, z) + g_currentMission:addMapHotspot(self.aiLoadingMarkerHotspot) end end end end - element:setDisabled(not parameter:getCanBeChanged()) end end -InGameMenuAIFrame.updateParameterValueTexts = Utils.overwrittenFunction(InGameMenuAIFrame.updateParameterValueTexts, - CpInGameMenuAIFrameExtended.updateParameterValueTexts) + +InGameMenuAIFrame.updateWarnings = Utils.appendedFunction(InGameMenuAIFrame.updateWarnings, + CpInGameMenuAIFrameExtended.updateWarnings) --- Enables clickable field hotspots. function CpInGameMenuAIFrameExtended:onClickHotspot(element,hotspot) diff --git a/scripts/gui/CpGuiUtil.lua b/scripts/gui/CpGuiUtil.lua index 4a3fd484b..7922faf61 100644 --- a/scripts/gui/CpGuiUtil.lua +++ b/scripts/gui/CpGuiUtil.lua @@ -169,6 +169,15 @@ function CpGuiUtil.getGenericSubTitleElementFromLayout(rootElement) return CpGuiUtil.getFirstElementWithProfileName(rootElement, "settingsMenuSubtitle") end +--- Gets the rgb color in 0-1 from 0-255. +---@param r number +---@param g number +---@param b number +---@param alpha number|nil +---@return number +---@return number +---@return number +---@return number function CpGuiUtil.getNormalizedRgb(r, g, b, alpha) return r / 255, g / 255, b / 255, alpha end @@ -205,6 +214,64 @@ function CpGuiUtil.setTarget(element, target) element.targetName = target.name end +------------------------------------------------ +--- Plots +------------------------------------------------ + +--- Translates the world coordinates to screen coordinates. +---@param map table +---@param worldX number +---@param worldZ number +---@param isHudMap boolean|nil If the hud minimap is used. +---@return number +---@return number +---@return number +---@return boolean +function CpGuiUtil.worldToScreen(map, worldX, worldZ, isHudMap) + local objectX = (worldX + map.worldCenterOffsetX) / map.worldSizeX * 0.5 + 0.25 + local objectZ = (worldZ + map.worldCenterOffsetZ) / map.worldSizeZ * 0.5 + 0.25 + local x, y, _, _ = map.fullScreenLayout:getMapObjectPosition(objectX, objectZ, 0, 0, 0, true) + local rot = 0 + local visible = true + if isHudMap then + --- The plot is displayed in the hud. + objectX = (worldX + map.worldCenterOffsetX) / map.worldSizeX * map.mapExtensionScaleFactor + map.mapExtensionOffsetX + objectZ = (worldZ + map.worldCenterOffsetZ) / map.worldSizeZ * map.mapExtensionScaleFactor + map.mapExtensionOffsetZ + + x, y, rot, visible = map.layout:getMapObjectPosition(objectX, objectZ, 0, 0, 0, false) + if map.state == IngameMap.STATE_MINIMAP_ROUND and map.layout.rotateWithMap then + x, y, rot, visible = CpGuiUtil.getMapObjectPositionCircleLayoutFix(map.layout, objectX, objectZ, 0, 0, 0, false) + end + end + return x, y, rot, visible +end + +--- Giants was not so kind, as to allow getting the positions even, if the object is outside the map range ... +--- This is a custom version for: IngameMapLayoutCircle:getMapObjectPosition(...) +---@param layout table +---@param objectU number +---@param objectV number +---@param width number +---@param height number +---@param rot number +---@param persistent boolean +---@return number +---@return number +---@return number +---@return boolean +function CpGuiUtil.getMapObjectPositionCircleLayoutFix(layout, objectU, objectV, width, height, rot, persistent) + local mapWidth, mapHeight = layout:getMapSize() + local mapX, mapY = layout:getMapPosition() + local objectX = objectU * mapWidth + mapX + local objectY = (1 - objectV) * mapHeight + mapY + objectX, objectY, rot = layout:rotateWithMap(objectX, objectY, rot, persistent) + objectX = objectX - width * 0.5 + objectY = objectY - height * 0.5 + + return objectX, objectY, rot, true +end + + ------------------------------------------------ --- Hud ------------------------------------------------ diff --git a/scripts/gui/FieldPlot.lua b/scripts/gui/FieldPlot.lua index fa8f3a415..ed7f3694e 100644 --- a/scripts/gui/FieldPlot.lua +++ b/scripts/gui/FieldPlot.lua @@ -48,7 +48,7 @@ function FieldPlot:draw(map) local lastWp = self.waypoints and #self.waypoints>0 and self.waypoints[#self.waypoints] if self.drawLastWp and lastWp then - local x, y = self:worldToScreen(map, lastWp.x, lastWp.z ) + local x, y = CpGuiUtil.worldToScreen(map, lastWp.x, lastWp.z ) local signSizeMeters = 1.5* self.lineThickness local zoom = map.fullScreenLayout:getIconZoom() local width, height = signSizeMeters * map.uiScale * zoom, signSizeMeters * map.uiScale * zoom * g_screenAspectRatio diff --git a/scripts/gui/UnloadingTriggerPlot.lua b/scripts/gui/UnloadingTriggerPlot.lua new file mode 100644 index 000000000..b8f84607a --- /dev/null +++ b/scripts/gui/UnloadingTriggerPlot.lua @@ -0,0 +1,49 @@ +--- Draws the bunker silo dimensions on the in game map. +---@class UnloadingTriggerPlot +UnloadingTriggerPlot = CpObject() + +function UnloadingTriggerPlot:init(node) + self.courseOverlayId = createImageOverlay('dataS/scripts/shared/graph_pixel.dds') + self.isVisible = false + -- the normal FS22 blue + self.color = {CpGuiUtil.getNormalizedRgb(42, 193, 237)} + -- a lighter shade of the same color + self.lightColor = {CpGuiUtil.getNormalizedRgb(45, 207, 255)} + -- a darker shade of the same color + self.darkColor = {CpGuiUtil.getNormalizedRgb(19, 87, 107)} + self.lineThickness = 2 / g_screenHeight -- 2 pixels + self.isHighlighted = false + local _ + self.x, _, self.z = getWorldTranslation(node) +end + +function UnloadingTriggerPlot:draw(map) + local r, g, b = unpack(self.color) + setOverlayColor( self.courseOverlayId, r, g, b, 0.8 ) + local x, y = CpGuiUtil.worldToScreen(map, self.x, self.z, false) + + local s1x, s1y = CpGuiUtil.worldToScreen(map, self.x - 5, self.z - 5, false) + local e1x, e1y = CpGuiUtil.worldToScreen(map, self.x + 5, self.z + 5, false) + local s2x, s2y = CpGuiUtil.worldToScreen(map, self.x + 5, self.z - 5, false) + local e2x, e2y = CpGuiUtil.worldToScreen(map, self.x - 5, self.z + 5, false) + + --- Create a coss through the node position + local mapRotation = map.layout:getMapRotation() + local dx2D = e1x - s1x + local dy2D = ( e1y - s1y ) / g_screenAspectRatio + local width = MathUtil.vector2Length(dx2D, dy2D) + local rotation = MathUtil.getYRotationFromDirection(10, 10) - math.pi * 0.5 + mapRotation + setOverlayRotation( self.courseOverlayId, rotation, 0, 0 ) + renderOverlay( self.courseOverlayId, s1x, s1y, width, self.lineThickness ) + + dx2D = e2x - s2x + dy2D = ( e2y - s2y ) / g_screenAspectRatio + width = MathUtil.vector2Length(dx2D, dy2D) + rotation = MathUtil.getYRotationFromDirection(-10, 10) - math.pi * 0.5 + mapRotation + setOverlayRotation( self.courseOverlayId, rotation, 0, 0 ) + renderOverlay( self.courseOverlayId, s2x, s2y, width, self.lineThickness ) +end + +function UnloadingTriggerPlot:setHighlighted(highlighted) + self.isHighlighted = highlighted +end \ No newline at end of file diff --git a/scripts/silo/BunkerSiloVehicleController.lua b/scripts/silo/BunkerSiloVehicleController.lua index caf5ac979..f7fa57df0 100644 --- a/scripts/silo/BunkerSiloVehicleController.lua +++ b/scripts/silo/BunkerSiloVehicleController.lua @@ -29,8 +29,6 @@ function CpBunkerSiloVehicleController:init(silo, vehicle, driveStrategy, direct ^ | ]] - - if dsz > dhz then self:debug("Silo needs to be inverted.") self.isInverted = true @@ -44,8 +42,6 @@ function CpBunkerSiloVehicleController:init(silo, vehicle, driveStrategy, direct | v | wx sx ]] - - self.isInverted = true elseif dsz < 0 and dhz > 0 then self:debug("Start distance: dsz: %.2f, dhz: %.2f", dsz, dhz) @@ -317,6 +313,24 @@ CpBunkerSiloLoaderController = CpObject(CpBunkerSiloVehicleController) function CpBunkerSiloLoaderController:init(silo, vehicle, driveStrategy) CpBunkerSiloVehicleController.init(self, silo, vehicle, driveStrategy, vehicle:getAIDirectionNode()) + + + local sx, sz = self.silo:getStartPosition() + local hx, hz = self.silo:getHeightPosition() + local dx, _, dz = getWorldTranslation(vehicle:getAIDirectionNode()) + self.isInverted = false + + if MathUtil.vector2Length(sx-dx, sz-dz) < MathUtil.vector2Length(hx-dx, hz-dz) then + if self.silo.siloMode == self.silo.SIDE_MODES.ONE_SIDED_INVERTED then + self.isInverted = true + end + else + self.isInverted = true + if self.silo.siloMode == self.silo.SIDE_MODES.ONE_SIDED then + self.isInverted = false + end + end + end --- Gets the next line with the most fill level. diff --git a/scripts/silo/BunkerSiloWrapper.lua b/scripts/silo/BunkerSiloWrapper.lua index 7ef3fc4f0..5eeb15573 100644 --- a/scripts/silo/BunkerSiloWrapper.lua +++ b/scripts/silo/BunkerSiloWrapper.lua @@ -704,11 +704,3 @@ function CpBunkerSilo:getDebugData() end return data end - -function CpBunkerSilo:getFillType() - return self.silo.outputFillType -end - -function CpBunkerSilo:getTotalFillLevel() - return self.silo.fillLevel -end \ No newline at end of file diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua index 0fc40dd6f..401fdfa08 100644 --- a/scripts/trigger/TriggerManager.lua +++ b/scripts/trigger/TriggerManager.lua @@ -1,29 +1,141 @@ --- Links all the needed trigger, except bunker silos to trigger wrappers. +---@class TriggerManager TriggerManager = CpObject() function TriggerManager:init() - self.unloadSilos = {} + ---@type table + self.unloadTriggers = {} + ---@type table + self.dischargeableUnloadTriggers = {} end function TriggerManager:addUnloadingSilo(silo) if silo.exactFillRootNode ~= nil then - self.unloadSilos[silo.exactFillRootNode] = CpTrigger(silo, silo.exactFillRootNode) + self.unloadTriggers[silo.exactFillRootNode] = CpTrigger(silo, silo.exactFillRootNode) + if silo:getIsToolTypeAllowed(ToolType.DISCHARGEABLE) then + self.dischargeableUnloadTriggers[silo.exactFillRootNode] = self.unloadTriggers[silo.exactFillRootNode] + end end end function TriggerManager:removeUnloadingSilo(silo) if silo.exactFillRootNode ~= nil then - if self.unloadSilos[silo.exactFillRootNode] then - self.unloadSilos[silo.exactFillRootNode]:delete() - self.unloadSilos[silo.exactFillRootNode] = nil + if self.unloadTriggers[silo.exactFillRootNode] then + self.unloadTriggers[silo.exactFillRootNode]:delete() + self.unloadTriggers[silo.exactFillRootNode] = nil + self.dischargeableUnloadTriggers[silo.exactFillRootNode] = nil end end end function TriggerManager:getUnloadTriggerForNode(node) - return self.unloadSilos[node] + return self.unloadTriggers[node] +end + +--- Gets the first trigger found in the defined area. +---@param triggers table +---@param x number +---@param z number +---@param dirX number +---@param dirZ number +---@param width number +---@param length number +---@return boolean +---@return table|nil +---@return table|nil +function TriggerManager:getTriggerAt(triggers, x, z, dirX, dirZ, width, length) + local angle = MathUtil.getYRotationFromDirection(dirX, dirZ) + local dirWX, dirWZ = MathUtil.getDirectionFromYRotation(angle + math.pi/2) + local sx, sz = x + dirWX * width/2, z + dirWZ * width/2 + + local area = { + { + x = sx, + z = sz + }, + { + x = sx - dirWX * width, + z = sz - dirWZ * width + }, + { + x = sx - dirWX * width + dirX * length, + z = sz - dirWZ * width + dirZ * length + }, + { + x = sx + dirX * length, + z = sz + dirZ * length + }, + { + x = sx, + z = sz + }, + } + local dx, _, dz + for node, trigger in pairs(triggers) do + dx, _, dz = getWorldTranslation(node) + if CpMathUtil.isPointInPolygon(area, dx, dz) then + return true, trigger, trigger:getTrigger():getTarget() + end + end + return false end +--- Gets the first unload trigger found in the defined area. +---@param x number +---@param z number +---@param dirX number +---@param dirZ number +---@param width number +---@param length number +---@return boolean +---@return table|nil +---@return table|nil +function TriggerManager:getUnloadTriggerAt(x, z, dirX, dirZ, width, length) + return self:getTriggerAt(self.unloadTriggers, x, z, dirX, dirZ, width, length) +end + +--- Gets the first dischargeable unload trigger found in the defined area. +---@param x number +---@param z number +---@param dirX number +---@param dirZ number +---@param width number +---@param length number +---@return boolean +---@return table|nil +---@return table|nil +function TriggerManager:getDischargeableUnloadTriggerAt(x, z, dirX, dirZ, width, length) + return self:getTriggerAt(self.dischargeableUnloadTriggers, x, z, dirX, dirZ, width, length) +end + +function TriggerManager:update(dt) + +end + +--- Draws all bunker silos onto the ai map. +---@param map table map to draw to. +---@param selected CpTrigger silo that gets highlighted. +function TriggerManager:drawUnloadTriggers(map, selected) + for _, trigger in pairs(self.unloadTriggers) do + trigger:drawPlot(map, selected) + end +end + +--- Draws all bunker silos onto the ai map. +---@param map table map to draw to. +---@param selected CpTrigger silo that gets highlighted. +function TriggerManager:drawDischargeableTriggers(map, selected) + for _, trigger in pairs(self.dischargeableUnloadTriggers) do + trigger:drawPlot(map, selected) + end +end + +function TriggerManager:draw() + for node, trigger in pairs(self.unloadTriggers) do + local text = string.format("%s:\n %d", getName(node), node ) + CpUtil.drawDebugNode(node, false, 2, text) + end +end g_triggerManager = TriggerManager() diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua index 6729789f1..6fc538f0e 100644 --- a/scripts/trigger/TriggerWrapper.lua +++ b/scripts/trigger/TriggerWrapper.lua @@ -5,6 +5,7 @@ CpTrigger = CpObject() function CpTrigger:init(trigger, node) self.trigger = trigger self.node = node + self.plot = UnloadingTriggerPlot(self.node) end function CpTrigger:delete() @@ -17,4 +18,9 @@ end function CpTrigger:getTrigger() return self.trigger +end + +function CpTrigger:drawPlot(map, selectedTrigger) + self.plot:setHighlighted(self == selectedTrigger) + self.plot:draw(map) end \ No newline at end of file From 9148e2025d4551528eb279bb8162b1485d33f1f8 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 7 Jul 2023 22:06:24 +0200 Subject: [PATCH 009/107] Adjustment and tries to fix bug --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 5 +-- scripts/ai/ImplementUtil.lua | 6 ++-- scripts/ai/jobs/CpAIJobSiloLoader.lua | 35 +++++++++++-------- scripts/silo/BunkerSiloVehicleController.lua | 4 +-- scripts/specializations/CpShovelPositions.lua | 35 ++++++++++++++++--- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index bb5b13975..12ecd1824 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -86,8 +86,9 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) self.jobParameters = jobParameters local position = jobParameters.unloadPosition - self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", position.x, position.z, position.angle ) - + if position.x and position.angle then + self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", position.x, position.z, position.angle ) + end if self.bunkerSilo ~= nil then self:debug("Bunker silo was found.") self.silo = self.bunkerSilo diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index c7cf60699..32af4adc4 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -435,8 +435,10 @@ function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, di local function debug(str, ...) if not suppressLog then - CpUtil.debugVehicle(CpDebug.DBG_SILO, implementToLoadFrom.rootVehicle, - str, ...) + if g_updateLoopIndex % 100 == 0 then + CpUtil.debugVehicle(CpDebug.DBG_SILO, implementToLoadFrom.rootVehicle, + str, ...) + end end end diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index bffb77301..07b75d236 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -132,26 +132,31 @@ function CpAIJobSiloLoader:validate(farmId) else return false, g_i18n:getText("CP_error_no_heap_found") end - - if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then - --- Validate the trigger setup - local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) - if found then - self.unloadStation = unloadStation - self.unloadTrigger = unloadTrigger - if unloadStation == nil then + if not AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then + if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then + --- Validate the trigger setup + local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) + if found then + self.unloadStation = unloadStation + self.unloadTrigger = unloadTrigger + if unloadStation == nil then + return false, g_i18n:getText("CP_error_no_unload_trigger_found") + end + local id = NetworkUtil.getObjectId(unloadStation) + if id ~= nil then + self:getCpJobParameters().unloadStation:setValue(id) + self:getCpJobParameters().unloadStation:validateUnloadingStation() + end + else return false, g_i18n:getText("CP_error_no_unload_trigger_found") end - local id = NetworkUtil.getObjectId(unloadStation) - if id ~= nil then - self:getCpJobParameters().unloadStation:setValue(id) - self:getCpJobParameters().unloadStation:validateUnloadingStation() - end - else + end + local unloadPosition = self:getCpJobParameters() + if unloadPosition.x == nil or unloadPosition.angle == nil then return false, g_i18n:getText("CP_error_no_unload_trigger_found") end + end - return isValid, errorMessage end diff --git a/scripts/silo/BunkerSiloVehicleController.lua b/scripts/silo/BunkerSiloVehicleController.lua index f7fa57df0..ff013ec0f 100644 --- a/scripts/silo/BunkerSiloVehicleController.lua +++ b/scripts/silo/BunkerSiloVehicleController.lua @@ -321,12 +321,12 @@ function CpBunkerSiloLoaderController:init(silo, vehicle, driveStrategy) self.isInverted = false if MathUtil.vector2Length(sx-dx, sz-dz) < MathUtil.vector2Length(hx-dx, hz-dz) then - if self.silo.siloMode == self.silo.SIDE_MODES.ONE_SIDED_INVERTED then + if self.silo.siloMode == CpBunkerSilo.SIDE_MODES.ONE_SIDED_INVERTED then self.isInverted = true end else self.isInverted = true - if self.silo.siloMode == self.silo.SIDE_MODES.ONE_SIDED then + if self.silo.siloMode == CpBunkerSilo.SIDE_MODES.ONE_SIDED then self.isInverted = false end end diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 2873e8eac..0ff2ed870 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -55,7 +55,7 @@ CpShovelPositions = { 5 }, }, - DEBUG = false + DEBUG = true } CpShovelPositions.MOD_NAME = g_currentModName CpShovelPositions.NAME = ".cpShovelPositions" @@ -258,12 +258,37 @@ function CpShovelPositions.setShovelPosition(dt, spec, shovel, shovelNode, angle dyRot = yRot end + local tool = spec.shovelTool + if tool.rotSpeed == nil then + return + end + + local spec = shovel.spec_cylindered + tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) + local rotSpeed = MathUtil.clamp(dyRot * tool.rotSpeed, tool.rotSpeed/3, 0.5) + if dyRot < 0 then + rotSpeed=rotSpeed*(-1) + end + CpShovelPositions.debug(shovel, - "Shovel position(%d) angle: %.2f, targetAngle: %.2f, yRot: %.2f, oldRot: %.2f", - spec.state, math.deg(angle), math.deg(targetAngle), math.deg(dyRot), math.deg(oldRot)) + "Shovel position angle: %.2f, targetAngle: %.2f, yRot: %.2f, oldRot: %.2f, rotSpeed: %.5f, rotMin: %.2f, rotMax: %.2f", + math.deg(angle), math.deg(targetAngle), math.deg(dyRot), math.deg(oldRot), + rotSpeed, math.deg(tool.rotMin), math.deg(tool.rotMax)) + + if math.abs(dyRot) < math.pi/(2*180) or rotSpeed == 0 then + ImplementUtil.stopMovingTool(shovel, tool) + return false + end + if Cylindered.setToolRotation(shovel, tool, rotSpeed, dt, dyRot) then + Cylindered.setDirty(shovel, tool) + + shovel:raiseDirtyFlags(tool.dirtyFlag) + shovel:raiseDirtyFlags(spec.cylinderedDirtyFlag) + return true + end - return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, - MathUtil.clamp(oldRot + dyRot , spec.shovelTool.rotMin, spec.shovelTool.rotMax)) + -- return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, + -- MathUtil.clamp(oldRot + dyRot , spec.shovelTool.rotMin, spec.shovelTool.rotMax)) end --- Changes the front loader angle dependent on the selected position, relative to a target height. From 146f44d33e969a84a26af87b76ce78e556811131 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 9 Jul 2023 13:41:19 +0200 Subject: [PATCH 010/107] Multiple adjustements and unload trigger support --- config/VehicleConfigurations.xml | 5 + .../ai/AIDriveStrategyShovelSiloLoader.lua | 222 ++++++++++++++---- scripts/ai/jobs/CpAIJobSiloLoader.lua | 2 +- scripts/ai/jobs/CpJobParameters.lua | 2 +- .../parameters/CpAIParameterPositionAngle.lua | 2 +- scripts/ai/tasks/CpAITaskSiloLoader.lua | 4 +- scripts/gui/CpAIFrameExtended.lua | 183 ++++++++------- .../specializations/CpAISiloLoaderWorker.lua | 3 +- scripts/specializations/CpShovelPositions.lua | 7 +- scripts/trigger/TriggerManager.lua | 6 +- 10 files changed, 283 insertions(+), 153 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index b58b8e625..73f809328 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -314,6 +314,11 @@ You can define the following custom settings: ignoreCollisionBoxesWhenFolded = "true" /> + + + diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 12ecd1824..d9cd0bdce 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -36,17 +36,24 @@ local AIDriveStrategyShovelSiloLoader_mt = Class(AIDriveStrategyShovelSiloLoader AIDriveStrategyShovelSiloLoader.myStates = { DRIVING_ALIGNMENT_COURSE = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_INTO_SILO = {shovelPosition = ShovelController.POSITIONS.LOADING, shovelMovingSpeed = 0}, DRIVING_OUT_OF_SILO = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + WAITING_FOR_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_TO_UNLOAD_POSITION = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, + DRIVING_TO_UNLOAD_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, UNLOADING = {shovelPosition = ShovelController.POSITIONS.UNLOADING, shovelMovingSpeed = 0}, REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, } AIDriveStrategyShovelSiloLoader.safeSpaceToTrailer = 5 -AIDriveStrategyShovelSiloLoader.maxValidTrailerDistance = 30 +AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 +AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 15 +AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 +AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 7 function AIDriveStrategyShovelSiloLoader.new(customMt) if customMt == nil then @@ -65,19 +72,28 @@ function AIDriveStrategyShovelSiloLoader:delete() self.siloController = nil end CpUtil.destroyNode(self.heapNode) - CpUtil.destroyNode(self.unloadNode) CpUtil.destroyNode(self.unloadPositionNode) + CpUtil.destroyNode(self.siloFrontNode) end function AIDriveStrategyShovelSiloLoader:getGeneratedCourse(jobParameters) return nil end +---@param bunkerSilo CpBunkerSilo +---@param heapSilo CpHeapBunkerSilo function AIDriveStrategyShovelSiloLoader:setSiloAndHeap(bunkerSilo, heapSilo) self.bunkerSilo = bunkerSilo self.heapSilo = heapSilo end +---@param unloadTrigger CpTrigger +---@param unloadStation table +function AIDriveStrategyShovelSiloLoader:setUnloadTriggerAndStation(unloadTrigger, unloadStation) + self.unloadTrigger = unloadTrigger + self.unloadStation = unloadStation +end + function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) -- to always have a valid course (for the traffic conflict detector mainly) @@ -85,9 +101,20 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) self:startCourse(self.course, 1) self.jobParameters = jobParameters - local position = jobParameters.unloadPosition - if position.x and position.angle then - self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", position.x, position.z, position.angle ) + self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", 0, 0, 0) + + self.isUnloadingAtTrailerActive = jobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRAILER + if not self.isUnloadingAtTrailerActive then + self:debug("Starting shovel silo to unload into unload trigger.") + local x, y, z = getWorldTranslation(self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode()) + setTranslation(self.unloadPositionNode, x, y, z) + local position = jobParameters.unloadPosition + local dirX, dirZ = position:getDirection() + setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1) + local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -self.distShovelUnloadStationPreUnload) + setTranslation(self.unloadPositionNode, dx, dy, dz) + else + self:debug("Starting shovel silo to unload into trailer.") end if self.bunkerSilo ~= nil then self:debug("Bunker silo was found.") @@ -97,6 +124,9 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) self.silo = self.heapSilo end + local cx, cz = self.silo:getFrontCenter() + self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, 0) + self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) end @@ -128,7 +158,8 @@ function AIDriveStrategyShovelSiloLoader:setAllStaticParameters() self.siloEndProximitySensor = SingleForwardLookingProximitySensorPack(self.vehicle, self.shovelController:getShovelNode(), 5, 1) self.heapNode = CpUtil.createNode("heapNode", 0, 0, 0, nil) - self.unloadNode = CpUtil.createNode("unloadNode", 0, 0, 0, nil) + + self.lastTrailerSearch = 0 end ----------------------------------------------------------------------------------------------------------------------- @@ -139,20 +170,26 @@ function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) if self.state == self.states.DRIVING_ALIGNMENT_COURSE then local course = self:getRememberedCourseAndIx() self:startCourse(course, 1) - self.state = self.states.DRIVING_INTO_SILO + self:setNewState(self.states.DRIVING_INTO_SILO) elseif self.state == self.states.DRIVING_INTO_SILO then self:startDrivingOutOfSilo() elseif self.state == self.states.DRIVING_OUT_OF_SILO then - self:startPathfindingToUnloadPosition() + if self.isUnloadingAtTrailerActive then + self:setNewState(self.states.WAITING_FOR_TRAILER) + else + self:startPathfindingToUnloadPosition() + end + elseif self.state == self.states.DRIVING_TO_UNLOAD_TRAILER then + self:approachTrailerForUnloading() elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then - self.state = self.states.WAITING_FOR_TRAILER + self:approachUnloadStationForUnloading() elseif self.state == self.states.DRIVING_TO_UNLOAD then - self.state = self.states.UNLOADING + self:setNewState(self.states.UNLOADING) elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then if self.shovelController:isEmpty() then self:startDrivingToSilo() else - self.state = self.states.WAITING_FOR_TRAILER + self:setNewState(self.states.WAITING_FOR_TRAILER) end --self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new()) end @@ -165,7 +202,7 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:updateLowFrequencyImplementControllers() local moveForwards = not self.ppc:isReversing() - local gx, gz + local gx, gz, _ ---------------------------------------------------------------- if not moveForwards then @@ -210,19 +247,30 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:setMaxSpeed(self.settings.fieldSpeed:getValue()) elseif self.state == self.states.WAITING_FOR_TRAILER then self:setMaxSpeed(0) - self:searchForTrailerToUnloadInto() + if (g_time - self.lastTrailerSearch) > self.searchForTrailerDelaySec * 1000 then + self:searchForTrailerToUnloadInto() + self.lastTrailerSearch = g_time + end elseif self.state == self.states.DRIVING_TO_UNLOAD then self:setMaxSpeed(self.settings.reverseSpeed:getValue()) - if self.targetTrailer then - if self.shovelController:isShovelOverTrailer(self.targetTrailer.exactFillRootNode) then - self.state = self.states.UNLOADING - self:setMaxSpeed(0) - end + local refNode + if self.isUnloadingAtTrailerActive then + refNode = self.targetTrailer.exactFillRootNode + else + refNode = self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode() + end + if self.shovelController:isShovelOverTrailer(refNode) then + self:setNewState(self.states.UNLOADING) + self:setMaxSpeed(0) end elseif self.state == self.states.UNLOADING then self:setMaxSpeed(0) - if self:hasFinishedUnloading() then - self:startReversingAwayFromUnloading() + if self:hasFinishedUnloading() then + if self.isUnloadingAtTrailerActive then + self:startReversingAwayFromUnloading() + else + self:startDrivingToSilo() + end end elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then self:setMaxSpeed(self.settings.fieldSpeed:getValue()) @@ -255,7 +303,6 @@ function AIDriveStrategyShovelSiloLoader:update(dt) CpUtil.drawDebugNode(self.heapNode, false, 3) end if self.targetTrailer then - CpUtil.drawDebugNode(self.unloadNode, false, 3) CpUtil.drawDebugNode(self.targetTrailer.exactFillRootNode, false, 3, "ExactFillRootNode") end CpUtil.drawDebugNode(self.unloadPositionNode, false, 3) @@ -278,6 +325,11 @@ function AIDriveStrategyShovelSiloLoader:getProximitySensorWidth() return self.vehicle.size.width - 0.5 end +function AIDriveStrategyShovelSiloLoader:setNewState(newState) + self:debug("Changed State from %s to %s", self.state.name, newState.name) + self.state = newState +end + ---------------------------------------------------------------- --- Pathfinding ---------------------------------------------------------------- @@ -286,10 +338,8 @@ end ---@param course table heap course function AIDriveStrategyShovelSiloLoader:startPathfindingToStart(course) if not self.pathfinder or not self.pathfinder:isActive() then - self.state = self.states.WAITING_FOR_PATHFINDER + self:setNewState(self.states.WAITING_FOR_PATHFINDER) self:rememberCourse(course, 1) - - self.pathfindingStartedAt = g_currentMission.time local done, path local fm = self:getFrontAndBackMarkers() self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToWaypoint( @@ -311,24 +361,24 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToStart(path) self:debug("Found alignment path to the course for the heap.") local alignmentCourse = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) self:startCourse(alignmentCourse, 1) - self.state = self.states.DRIVING_ALIGNMENT_COURSE + self:setNewState(self.states.DRIVING_ALIGNMENT_COURSE) else local course = self:getRememberedCourseAndIx() self:debug("No alignment path found!") self:startCourse(course, 1) - self.state = self.states.DRIVING_INTO_SILO + self:setNewState(self.states.DRIVING_INTO_SILO) end end function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() - self:debugSparse("Searching for an trailer nearby.") + self:debug("Searching for an trailer nearby.") local function getClosestTrailerAndDistance() local closestDistance = math.huge local closestTrailer = nil for i, vehicle in pairs(g_currentMission.vehicles) do if SpecializationUtil.hasSpecialization(Trailer, vehicle.specializations) and AIUtil.isStopped(vehicle.rootVehicle) then - local dist = calcDistanceFrom(vehicle.rootNode, self.unloadPositionNode) + local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode) if dist < closestDistance then closestDistance = dist closestTrailer = vehicle @@ -339,9 +389,11 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() end local trailer, dist = getClosestTrailerAndDistance() if not trailer then + self:debug("No valid trailer found anywhere!") return end - if dist > 20 then + if dist > self.maxValidTrailerDistanceToSiloFront then + self:debug("No Trailer with the max distance found, closest: %.2f", dist) return end self:debug("Found a trailer %s within distance %.2f", CpUtil.getName(trailer), dist) @@ -354,29 +406,36 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() exactFillRootNode = exactFillRootNode, trailer = trailer } - local dx, _, dz = getWorldTranslation(exactFillRootNode) - local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) - local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, - 0, -3, 0, 3, false) - local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) - self:startCourse(course, firstWpIx) - self.state = self.states.DRIVING_TO_UNLOAD + + local _, _, distShovelDirectionNode = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) + --self.distShovelTrailerPreUnload + local dirX, _, dirZ = localDirectionToWorld(trailer.rootNode, 0, 0, 1) + local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) + local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) + if dx > 0 then + local x, y, z = localToWorld(trailer.rootNode, dx + distShovelDirectionNode + self.distShovelTrailerPreUnload, 0, 0) + setTranslation(self.unloadPositionNode, x, y, z) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) + + else + local x, y, z = localToWorld(trailer.rootNode, dx - distShovelDirectionNode -self.distShovelTrailerPreUnload, 0, 0) + setTranslation(self.unloadPositionNode, x, y, z) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) + end + self:startPathfindingToTrailer() + end end function AIDriveStrategyShovelSiloLoader:startPathfindingToUnloadPosition() if not self.pathfinder or not self.pathfinder:isActive() then - self.state = self.states.WAITING_FOR_PATHFINDER - - - local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) - self.pathfindingStartedAt = g_currentMission.time + self:setNewState(self.states.WAITING_FOR_PATHFINDER) local done, path, goalNodeInvalid self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( self.vehicle, self.unloadPositionNode, - 0, -2*spaceToTrailer, true, + 0, 0, true, nil, {}, nil, - 0, nil, true + 0, nil, false ) if done then return self:onPathfindingDoneToUnloadPosition(path, goalNodeInvalid) @@ -394,13 +453,45 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, self:debug("Found path to unloading station.") local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) self:startCourse(course, 1) - self.state = self.states.DRIVING_TO_UNLOAD_POSITION + self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) else self:debug("Failed to drive close to unload position.") - self.state = self.states.WAITING_FOR_TRAILER + --self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) end end +function AIDriveStrategyShovelSiloLoader:startPathfindingToTrailer() + if not self.pathfinder or not self.pathfinder:isActive() then + self:setNewState(self.states.WAITING_FOR_PATHFINDER) + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( + self.vehicle, self.unloadPositionNode, + 0, 0, true, + nil, {}, nil, + 0, nil, false + ) + if done then + return self:onPathfindingDoneToTrailer(path, goalNodeInvalid) + else + self:setPathfindingDoneCallback(self, self.onPathfindingDoneToTrailer) + end + else + self:debug('Pathfinder already active') + end + return true +end + +function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToTrailer(path, goalNodeInvalid) + if path and #path > 2 then + self:debug("Found path to unloading station.") + local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + self:startCourse(course, 1) + self:setNewState(self.states.DRIVING_TO_UNLOAD_TRAILER) + else + self:debug("Failed to find path to trailer!") + self:setNewState(self.states.WAITING_FOR_TRAILER) + end +end ---------------------------------------------------------------- --- Silo work ---------------------------------------------------------------- @@ -416,13 +507,13 @@ function AIDriveStrategyShovelSiloLoader:startDrivingToSilo() local distance = siloCourse:getDistanceBetweenVehicleAndWaypoint(self.vehicle, 1) - if distance > 2 * self.turningRadius then + if distance > 1.5 * self.turningRadius then self:debug("Start driving to silo with pathfinder.") self:startPathfindingToStart(siloCourse) else self:debug("Start driving into the silo.") self:startCourse(siloCourse, 1) - self.state = self.states.DRIVING_INTO_SILO + self:setNewState(self.states.DRIVING_INTO_SILO) end end @@ -438,7 +529,27 @@ function AIDriveStrategyShovelSiloLoader:startDrivingOutOfSilo() ix = reverseCourse:getNumberOfWaypoints() end self:startCourse(reverseCourse, ix) - self.state = self.states.DRIVING_OUT_OF_SILO + self:setNewState(self.states.DRIVING_OUT_OF_SILO) +end + +function AIDriveStrategyShovelSiloLoader:approachTrailerForUnloading() + local dx, _, dz = getWorldTranslation(self.targetTrailer.exactFillRootNode) + local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) + local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, -3, 0, 3, false) + local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) + self:startCourse(course, firstWpIx) + self:setNewState(self.states.DRIVING_TO_UNLOAD) +end + +function AIDriveStrategyShovelSiloLoader:approachUnloadStationForUnloading() + local dx, _, dz = getWorldTranslation(self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode()) + local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) + local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, -3, 0, 3, false) + local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) + self:startCourse(course, firstWpIx) + self:setNewState(self.states.DRIVING_TO_UNLOAD) end function AIDriveStrategyShovelSiloLoader:getWorkWidth() @@ -449,9 +560,16 @@ end --- Unloading ---------------------------------------------------------------- function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() - if self.targetTrailer and self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then - self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) - return true + if self.isUnloadingAtTrailerActive then + if self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then + self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) + return true + end + else + if self.unloadTrigger:getTrigger():getFillUnitFreeCapacity(1, self.shovelController:getDischargeFillType(), self.vehicle:getOwnerFarmId()) then + self:debug("Unload Trigger is full.") + return true + end end if self.shovelController:isEmpty() then self:debug("Finished unloading, as the shovel is empty.") @@ -465,5 +583,5 @@ function AIDriveStrategyShovelSiloLoader:startReversingAwayFromUnloading() local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, 0 ) self:startCourse(course, 1) - self.state = self.states.REVERSING_AWAY_FROM_UNLOAD + self:setNewState(self.states.REVERSING_AWAY_FROM_UNLOAD) end diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 07b75d236..2f290a512 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -151,7 +151,7 @@ function CpAIJobSiloLoader:validate(farmId) return false, g_i18n:getText("CP_error_no_unload_trigger_found") end end - local unloadPosition = self:getCpJobParameters() + local unloadPosition = self:getCpJobParameters().unloadPosition if unloadPosition.x == nil or unloadPosition.angle == nil then return false, g_i18n:getText("CP_error_no_unload_trigger_found") end diff --git a/scripts/ai/jobs/CpJobParameters.lua b/scripts/ai/jobs/CpJobParameters.lua index 5aff5addb..7f41b3964 100644 --- a/scripts/ai/jobs/CpJobParameters.lua +++ b/scripts/ai/jobs/CpJobParameters.lua @@ -347,7 +347,7 @@ function CpSiloLoaderJobParameters:isShovelSiloLoadDisabled() end function CpSiloLoaderJobParameters:isUnloadPositionDisabled() - return false --self:isShovelSiloLoadDisabled() or self.unloadAt == CpSiloLoaderJobParameters.UNLOAD_TRAILER + return self:isShovelSiloLoadDisabled() or self.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRAILER end function CpSiloLoaderJobParameters:isUnloadStationDisabled() diff --git a/scripts/ai/parameters/CpAIParameterPositionAngle.lua b/scripts/ai/parameters/CpAIParameterPositionAngle.lua index dbdac4b0a..8561c3698 100644 --- a/scripts/ai/parameters/CpAIParameterPositionAngle.lua +++ b/scripts/ai/parameters/CpAIParameterPositionAngle.lua @@ -109,7 +109,7 @@ end function CpAIParameterPosition:isAlmostEqualTo(otherPosition) local x, z = otherPosition:getPosition() if x ~= nil and self.x ~= nil then - return MathUtil.vector2Length(self.x - x, self.z - z) <= 1 + return MathUtil.vector2Length(self.x - x, self.z - z) <= 1 end return false end diff --git a/scripts/ai/tasks/CpAITaskSiloLoader.lua b/scripts/ai/tasks/CpAITaskSiloLoader.lua index b4a6feb0c..ddfc5333a 100644 --- a/scripts/ai/tasks/CpAITaskSiloLoader.lua +++ b/scripts/ai/tasks/CpAITaskSiloLoader.lua @@ -1,5 +1,6 @@ --- Bunker silo task ---@class CpAITaskSiloLoader +---@field job table CpAITaskSiloLoader = {} local CpAITaskSiloLoader_mt = Class(CpAITaskSiloLoader, AITask) @@ -32,7 +33,8 @@ end function CpAITaskSiloLoader:start() if self.isServer then - self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap) + local _, unloadTrigger, unloadStation = self.job:getUnloadTriggerAt(self.job:getCpJobParameters().unloadPosition) + self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap, unloadTrigger, unloadStation) end CpAITaskSiloLoader:superClass().start(self) diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 3ce7d0915..21f4cb161 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -101,7 +101,7 @@ function CpInGameMenuAIFrameExtended:onAIFrameLoadMapFinished() g_messageCenter:subscribe(MessageType.GUI_AFTER_CLOSE, onCloseInGameMenu, g_currentMission.inGameMenu) g_messageCenter:subscribe(MessageType.GUI_BEFORE_OPEN, onOpenInGameMenu, g_currentMission.inGameMenu) --- Closes the course generator settings with the back button. - local function onClickBack(pageAI,superFunc) + local function onClickBack(pageAI,superFunc) if pageAI.mode == CpInGameMenuAIFrameExtended.MODE_COURSE_GENERATOR then pageAI:onClickOpenCloseCourseGenerator() return @@ -111,19 +111,27 @@ function CpInGameMenuAIFrameExtended:onAIFrameLoadMapFinished() return end CpInGameMenuAIFrameExtended.resetHotspots(self) - return superFunc(pageAI) + superFunc(pageAI) + if pageAI:getIsPicking() then + self:updateParameterValueTexts() + end + end - self.buttonBack.onClickCallback = Utils.overwrittenFunction(self.buttonBack.onClickCallback,onClickBack) - self.ingameMapBase.drawHotspotsOnly = Utils.appendedFunction(self.ingameMapBase.drawHotspotsOnly , CpInGameMenuAIFrameExtended.draw) + self.buttonBack.onClickCallback = Utils.overwrittenFunction( + self.buttonBack.onClickCallback,onClickBack) + + self.ingameMapBase.drawHotspotsOnly = Utils.appendedFunction( + self.ingameMapBase.drawHotspotsOnly , CpInGameMenuAIFrameExtended.draw) - --- Adds a second map hotspot for field position. + --- Adds the ai target hotspot. + self.driveToAiTargetMapHotspot = AITargetHotspot.new() self.fieldSiloAiTargetMapHotspot = AITargetHotspot.new() self.fieldSiloAiTargetMapHotspot.icon:setUVs(CpInGameMenuAIFrameExtended.positionUvs) --- Without angle - self.unloadAiTargetMapHotspot = AITargetHotspot.new() - self.loadAiTargetMapHotspot = AITargetHotspot.new() + self.rawAiTargetMapHotspot = self.aiTargetMapHotspot + self.ingameMap.onClickHotspotCallback = Utils.appendedFunction(self.ingameMap.onClickHotspotCallback, CpInGameMenuAIFrameExtended.onClickHotspot) @@ -197,6 +205,17 @@ end InGameMenuAIFrame.updateContextInputBarVisibility = Utils.appendedFunction(InGameMenuAIFrame.updateContextInputBarVisibility,CpInGameMenuAIFrameExtended.updateContextInputBarVisibility) +function CpInGameMenuAIFrameExtended:setJobMenuVisible(visible) + if not visible then + g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) + g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) + end +end +InGameMenuAIFrame.setJobMenuVisible = Utils.appendedFunction(InGameMenuAIFrame.setJobMenuVisible, CpInGameMenuAIFrameExtended.setJobMenuVisible) + function CpInGameMenuAIFrameExtended:isCreateFieldBorderBtnVisible() local visible = self.mode == CpInGameMenuAIFrameExtended.MODE_DRAW_FIELD_BORDER or self.mode == InGameMenuAIFrame.MODE_OVERVIEW and self.currentHotspot == nil @@ -321,64 +340,64 @@ end --- Updates the visibility of the vehicle settings on select/unselect of a vehicle in the ai menu page. --- Also updates the field position map hotspot. function CpInGameMenuAIFrameExtended:setMapSelectionItem(hotspot) + g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) - if hotspot ~= nil then - local vehicle = InGameMenuMapUtil.getHotspotVehicle(hotspot) - self.lastVehicle = vehicle - self.hudVehicle = nil - if vehicle then - if vehicle.getJob ~= nil then - local job = vehicle:getJob() - - if job ~= nil then - if job.getCpJobParameters ~= nil then - local parameters = job:getCpJobParameters():getAiTargetMapHotspotParameters() - for i, param in pairs(parameters) do - if param:is_a(CpAIParameterPosition) then - if param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.DRIVE_TO then - if param:applyToMapHotspot(self.aiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.aiTargetMapHotspot) - end - elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then - if param:applyToMapHotspot(self.fieldSiloAiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.fieldSiloAiTargetMapHotspot) - end - elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then - if param:applyToMapHotspot(self.loadAiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) - end - elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then - if param:applyToMapHotspot(self.unloadAiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.unloadAiTargetMapHotspot) - end - end - elseif param:is_a(CpAIParameterUnloadingStation) then - g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) - if param:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then - g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) - end - end - end + if hotspot == nil then + return + end + local vehicle = InGameMenuMapUtil.getHotspotVehicle(hotspot) + self.lastVehicle = vehicle + self.hudVehicle = nil + if vehicle == nil or vehicle.getJob == nil then + return + end + local job = vehicle:getJob() + if job == nil or job.getCpJobParameters == nil then + return + end + if vehicle:getIsCpActive() then + local parameters = job:getCpJobParameters():getAiTargetMapHotspotParameters() + for i, param in pairs(parameters) do + if param:is_a(CpAIParameterPosition) then + if param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.DRIVE_TO then + if param:applyToMapHotspot(self.driveToAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.driveToAiTargetMapHotspot) end - if job.getTarget ~= nil then - local x, z, rot = job:getTarget() - - self.aiTargetMapHotspot:setWorldPosition(x, z) - - if rot ~= nil then - self.aiTargetMapHotspot:setWorldRotation(rot + math.pi) - end - - g_currentMission:addMapHotspot(self.aiTargetMapHotspot) + elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then + if param:applyToMapHotspot(self.fieldSiloAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.fieldSiloAiTargetMapHotspot) + end + elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then + if param:applyToMapHotspot(self.loadAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.loadAiTargetMapHotspot) + end + elseif param:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then + if param:applyToMapHotspot(self.unloadAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.unloadAiTargetMapHotspot) end end - + elseif param:is_a(CpAIParameterUnloadingStation) then + g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) + if param:applyToMapHotspot(self.aiUnloadingMarkerHotspot) then + g_currentMission:addMapHotspot(self.aiUnloadingMarkerHotspot) + end end end end + if job.getTarget ~= nil then + local x, z, rot = job:getTarget() + + self.aiTargetMapHotspot:setWorldPosition(x, z) + + if rot ~= nil then + self.aiTargetMapHotspot:setWorldRotation(rot + math.pi) + end + + g_currentMission:addMapHotspot(self.aiTargetMapHotspot) + end g_currentMission.inGameMenu:updatePages() end InGameMenuAIFrame.setMapSelectionItem = Utils.appendedFunction(InGameMenuAIFrame.setMapSelectionItem, CpInGameMenuAIFrameExtended.setMapSelectionItem) @@ -406,6 +425,7 @@ function CpInGameMenuAIFrameExtended:onAIFrameClose() self.courseGeneratorLayout:setVisible(false) self.contextBox:setVisible(true) self.lastHotspot = self.currentHotspot + g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) @@ -470,20 +490,20 @@ function CpInGameMenuAIFrameExtended:draw() end function CpInGameMenuAIFrameExtended:delete() + if self.driveToAiTargetMapHotspot then + self.driveToAiTargetMapHotspot:delete() + self.driveToAiTargetMapHotspot = nil + end if self.fieldSiloAiTargetMapHotspot ~= nil then self.fieldSiloAiTargetMapHotspot:delete() - self.fieldSiloAiTargetMapHotspot = nil end - if self.unloadAiTargetMapHotspot ~= nil then self.unloadAiTargetMapHotspot:delete() - self.unloadAiTargetMapHotspot = nil end if self.loadAiTargetMapHotspot ~= nil then self.loadAiTargetMapHotspot:delete() - self.loadAiTargetMapHotspot = nil end end @@ -499,16 +519,8 @@ function CpInGameMenuAIFrameExtended:onClickPositionParameter(superFunc, element end InGameMenuAIFrame.onClickPositionParameter = Utils.overwrittenFunction( InGameMenuAIFrame.onClickPositionParameter, CpInGameMenuAIFrameExtended.onClickPositionParameter) - -function CpInGameMenuAIFrameExtended:onClickPositionRotationParameter(superFunc, element, ...) - local parameter = element.aiParameter - if parameter:getCanBeChanged() then - --- Checks if the position setting is not disabled - superFunc(self, element, ...) - end -end InGameMenuAIFrame.onClickPositionRotationParameter = Utils.overwrittenFunction( - InGameMenuAIFrame.onClickPositionRotationParameter, CpInGameMenuAIFrameExtended.onClickPositionRotationParameter) + InGameMenuAIFrame.onClickPositionRotationParameter, CpInGameMenuAIFrameExtended.onClickPositionParameter) --- Ugly hack to swap the main AI hotspot with the field position hotspot, @@ -517,25 +529,23 @@ function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, if parameter and parameter.getPositionType then CpInGameMenuAIFrameExtended.resetHotspots(self) - if parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then - local mapHotspot = self.aiTargetMapHotspot + if parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.DRIVE_TO then + self.aiTargetMapHotspot = self.driveToAiTargetMapHotspot + self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.DRIVE_TO + elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then self.aiTargetMapHotspot = self.fieldSiloAiTargetMapHotspot - self.fieldSiloAiTargetMapHotspot = mapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then - local mapHotspot = self.aiTargetMapHotspot self.aiTargetMapHotspot = self.unloadAiTargetMapHotspot - self.unloadAiTargetMapHotspot = mapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then - local mapHotspot = self.aiTargetMapHotspot self.aiTargetMapHotspot = self.loadAiTargetMapHotspot - self.loadAiTargetMapHotspot = mapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.LOAD end end callback = Utils.appendedFunction(callback,function (finished, x, z) CpInGameMenuAIFrameExtended.resetHotspots(self) + self:updateParameterValueTexts() end) superFunc(self, parameter, callback, ...) @@ -547,20 +557,10 @@ InGameMenuAIFrame.startPickPositionAndRotation = Utils.overwrittenFunction(InGam CpInGameMenuAIFrameExtended.startPickingPosition) function CpInGameMenuAIFrameExtended:resetHotspots() - if self.currentPickingMapHotspotType == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then - local mapHotspot = self.aiTargetMapHotspot - self.aiTargetMapHotspot = self.fieldSiloAiTargetMapHotspot - self.fieldSiloAiTargetMapHotspot = mapHotspot - self.currentPickingMapHotspotType = nil - elseif self.currentPickingMapHotspotType == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then - local mapHotspot = self.aiTargetMapHotspot - self.aiTargetMapHotspot = self.unloadAiTargetMapHotspot - self.unloadAiTargetMapHotspot = mapHotspot - self.currentPickingMapHotspotType = nil - end + self.aiTargetMapHotspot = self.rawAiTargetMapHotspot + self.currentPickingMapHotspotType = nil end - --- Added support for the cp field target position. function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) if self.currentJobElements == nil then @@ -569,6 +569,7 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) g_currentMission:removeMapHotspot(self.aiLoadingMarkerHotspot) g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) + g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) @@ -583,8 +584,8 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) elseif parameter.is_a and parameter:is_a(CpAIParameterPosition) then element:setText(parameter:getString()) if parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.DRIVE_TO then - if parameter:applyToMapHotspot(self.aiTargetMapHotspot) then - g_currentMission:addMapHotspot(self.aiTargetMapHotspot) + if parameter:applyToMapHotspot(self.driveToAiTargetMapHotspot) then + g_currentMission:addMapHotspot(self.driveToAiTargetMapHotspot) end elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.FIELD_OR_SILO then if parameter:applyToMapHotspot(self.fieldSiloAiTargetMapHotspot) then @@ -676,6 +677,10 @@ end InGameMenuAIFrame.updateWarnings = Utils.appendedFunction(InGameMenuAIFrame.updateWarnings, CpInGameMenuAIFrameExtended.updateWarnings) +-------------------------------------------- +--- Custom fields +-------------------------------------------- + --- Enables clickable field hotspots. function CpInGameMenuAIFrameExtended:onClickHotspot(element,hotspot) if hotspot then diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index 0b8771950..58d485318 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -178,7 +178,7 @@ function CpAISiloLoaderWorker:startCpAtLastWp(superFunc, ...) end end -function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap) +function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger, unloadStation) if self.isServer then local strategy if AIUtil.hasAIImplementWithSpecialization(self, ConveyorBelt) then @@ -189,6 +189,7 @@ function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, strategy = AIDriveStrategyShovelSiloLoader.new() end strategy:setSiloAndHeap(bunkerSilo, heap) + strategy:setUnloadTriggerAndStation(unloadTrigger, unloadStation) strategy:setAIVehicle(self, jobParameters) self:startCpWithStrategy(strategy) end diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 0ff2ed870..cf7c8c27d 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -55,7 +55,7 @@ CpShovelPositions = { 5 }, }, - DEBUG = true + DEBUG = false } CpShovelPositions.MOD_NAME = g_currentModName CpShovelPositions.NAME = ".cpShovelPositions" @@ -64,9 +64,8 @@ CpShovelPositions.KEY = "." .. CpShovelPositions.SPEC_NAME function CpShovelPositions.initSpecialization() local schema = Vehicle.xmlSchemaSavegame - if CpShovelPositions.DEBUG then - g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelState', 'cpSetShovelState', 'consoleCommandSetShovelState', CpShovelPositions) - end + g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelState', 'cpSetShovelState', 'consoleCommandSetShovelState', CpShovelPositions) + end function CpShovelPositions.prerequisitesPresent(specializations) diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua index 401fdfa08..3c84f4c22 100644 --- a/scripts/trigger/TriggerManager.lua +++ b/scripts/trigger/TriggerManager.lua @@ -101,9 +101,9 @@ end ---@param dirZ number ---@param width number ---@param length number ----@return boolean ----@return table|nil ----@return table|nil +---@return boolean found? +---@return table|nil unload trigger +---@return table|nil unload station/placeable function TriggerManager:getDischargeableUnloadTriggerAt(x, z, dirX, dirZ, width, length) return self:getTriggerAt(self.dischargeableUnloadTriggers, x, z, dirX, dirZ, width, length) end From 5c43613fffa6a2ac2339e87c89f26e8fa56aa0ed Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 9 Jul 2023 14:12:23 +0200 Subject: [PATCH 011/107] Bug fix and adjustments for unloading into trailer --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 22 +++++++++++++------ scripts/ai/jobs/CpAIJobSiloLoader.lua | 9 ++++---- scripts/silo/BunkerSiloWrapper.lua | 4 ++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index d9cd0bdce..a37c47c17 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -62,6 +62,7 @@ function AIDriveStrategyShovelSiloLoader.new(customMt) local self = AIDriveStrategyCourse.new(customMt) AIDriveStrategyCourse.initStates(self, AIDriveStrategyShovelSiloLoader.myStates) self.state = self.states.INITIAL + self.debugChannel = CpDebug.DBG_SILO return self end @@ -219,7 +220,11 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) return end if self.shovelController:isFull() then - self:startPathfindingToUnloadPosition() + if self.isUnloadingAtTrailerActive then + self:setNewState(self.states.WAITING_FOR_TRAILER) + else + self:startPathfindingToUnloadPosition() + end else self:startDrivingToSilo() end @@ -245,6 +250,8 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then self:setMaxSpeed(self.settings.fieldSpeed:getValue()) + elseif self.state == self.states.DRIVING_TO_UNLOAD_TRAILER then + self:setMaxSpeed(self.settings.fieldSpeed:getValue()) elseif self.state == self.states.WAITING_FOR_TRAILER then self:setMaxSpeed(0) if (g_time - self.lastTrailerSearch) > self.searchForTrailerDelaySec * 1000 then @@ -406,24 +413,25 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() exactFillRootNode = exactFillRootNode, trailer = trailer } - + self:debug("Unloading to trailer %s in distance %.2f.", CpUtil.getName(trailer), dist) local _, _, distShovelDirectionNode = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) --self.distShovelTrailerPreUnload local dirX, _, dirZ = localDirectionToWorld(trailer.rootNode, 0, 0, 1) local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) if dx > 0 then - local x, y, z = localToWorld(trailer.rootNode, dx + distShovelDirectionNode + self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, dx - distShovelDirectionNode - self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) - setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) else - local x, y, z = localToWorld(trailer.rootNode, dx - distShovelDirectionNode -self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, dx + distShovelDirectionNode + self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) - setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) end self:startPathfindingToTrailer() - + else + self:debug("Unloading into trailer %s not possible.", CpUtil.getName(trailer)) end end diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 2f290a512..914e2eac5 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -150,12 +150,11 @@ function CpAIJobSiloLoader:validate(farmId) else return false, g_i18n:getText("CP_error_no_unload_trigger_found") end + local unloadPosition = self:getCpJobParameters().unloadPosition + if unloadPosition.x == nil or unloadPosition.angle == nil then + return false, g_i18n:getText("CP_error_no_unload_trigger_found") + end end - local unloadPosition = self:getCpJobParameters().unloadPosition - if unloadPosition.x == nil or unloadPosition.angle == nil then - return false, g_i18n:getText("CP_error_no_unload_trigger_found") - end - end return isValid, errorMessage end diff --git a/scripts/silo/BunkerSiloWrapper.lua b/scripts/silo/BunkerSiloWrapper.lua index 5eeb15573..2ddfe1a4f 100644 --- a/scripts/silo/BunkerSiloWrapper.lua +++ b/scripts/silo/BunkerSiloWrapper.lua @@ -205,6 +205,10 @@ function CpSilo:getTotalFillLevel() return 0 end +function CpSilo:isTheSameSilo() + --- override +end + --- Heap Bunker Silo --- Wrapper for a heap. ---@class CpHeapBunkerSilo :CpSilo From 6b17e4c68df60d66eca23d72515a2145666dc92e Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 9 Jul 2023 14:20:20 +0200 Subject: [PATCH 012/107] Small adjustment --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index a37c47c17..19e42e23c 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -420,12 +420,12 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) if dx > 0 then - local x, y, z = localToWorld(trailer.rootNode, dx - distShovelDirectionNode - self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, dx + math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) else - local x, y, z = localToWorld(trailer.rootNode, dx + distShovelDirectionNode + self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, dx - math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) end @@ -491,7 +491,7 @@ end function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToTrailer(path, goalNodeInvalid) if path and #path > 2 then - self:debug("Found path to unloading station.") + self:debug("Found path to trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) self:startCourse(course, 1) self:setNewState(self.states.DRIVING_TO_UNLOAD_TRAILER) From 6a08ca735e523b58d8bee5b7980b3f5c3dcaab5b Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 9 Jul 2023 14:21:34 +0200 Subject: [PATCH 013/107] Another stupid fix .. --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 19e42e23c..0c0f4300f 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -415,17 +415,16 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() } self:debug("Unloading to trailer %s in distance %.2f.", CpUtil.getName(trailer), dist) local _, _, distShovelDirectionNode = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) - --self.distShovelTrailerPreUnload local dirX, _, dirZ = localDirectionToWorld(trailer.rootNode, 0, 0, 1) local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) if dx > 0 then - local x, y, z = localToWorld(trailer.rootNode, dx + math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) else - local x, y, z = localToWorld(trailer.rootNode, dx - math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, - math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) end From 2cf45fe06afb95f914154a8740e1143ee915f51e Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:02:12 +0200 Subject: [PATCH 014/107] Adjustments --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 26 +- scripts/ai/controllers/ShovelController.lua | 9 + scripts/specializations/CpShovelPositions.lua | 231 +++++++++++------- 3 files changed, 164 insertions(+), 102 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 0c0f4300f..2a8b90c82 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -267,17 +267,20 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) refNode = self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode() end if self.shovelController:isShovelOverTrailer(refNode) then - self:setNewState(self.states.UNLOADING) - self:setMaxSpeed(0) + -- self:setNewState(self.states.UNLOADING) + -- self:setMaxSpeed(0) + end + if not self.isUnloadingAtTrailerActive then + if self.shovelController:isShovelOverTrailer(refNode, 3) and self.shovelController:canDischarge() then + self:setNewState(self.states.UNLOADING) + self:setMaxSpeed(0) + end end + elseif self.state == self.states.UNLOADING then self:setMaxSpeed(0) if self:hasFinishedUnloading() then - if self.isUnloadingAtTrailerActive then - self:startReversingAwayFromUnloading() - else - self:startDrivingToSilo() - end + self:startReversingAwayFromUnloading() end elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then self:setMaxSpeed(self.settings.fieldSpeed:getValue()) @@ -422,9 +425,8 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() local x, y, z = localToWorld(trailer.rootNode, math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) - else - local x, y, z = localToWorld(trailer.rootNode, - math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) + local x, y, z = localToWorld(trailer.rootNode, -math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) setTranslation(self.unloadPositionNode, x, y, z) setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) end @@ -553,7 +555,7 @@ function AIDriveStrategyShovelSiloLoader:approachUnloadStationForUnloading() local dx, _, dz = getWorldTranslation(self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode()) local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, - 0, -3, 0, 3, false) + 0, -3, 3, 2, false) local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) self:startCourse(course, firstWpIx) self:setNewState(self.states.DRIVING_TO_UNLOAD) @@ -573,9 +575,9 @@ function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() return true end else - if self.unloadTrigger:getTrigger():getFillUnitFreeCapacity(1, self.shovelController:getDischargeFillType(), self.vehicle:getOwnerFarmId()) then + if self.unloadTrigger:getTrigger():getFillUnitFreeCapacity(1, self.shovelController:getDischargeFillType(), self.vehicle:getOwnerFarmId()) <= 0 then self:debug("Unload Trigger is full.") - return true + return false end end if self.shovelController:isEmpty() then diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index f4e193e03..96ac9da32 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -56,6 +56,15 @@ function ShovelController:getDischargeNode() return self.implement:getCurrentDischargeNode() end +function ShovelController:canDischarge() + local dischargeNode = self:getDischargeNode() + local spec = self.implement.spec_dischargeable + if not spec.isAsyncRaycastActive then + self.implement:updateRaycast(dischargeNode) + end + return dischargeNode.dischargeHit +end + --- Is the shovel node over the trailer? ---@param refNode number ---@param margin number|nil diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index cf7c8c27d..8d24151c4 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -41,8 +41,8 @@ CpShovelPositions = { }, PRE_UNLOAD_POSITION = { ARM_LIMITS = { - 3, - 4 + 7, + 7 }, SHOVEL_LIMITS = { 43, @@ -51,11 +51,11 @@ CpShovelPositions = { }, UNLOADING_POSITION = { ARM_LIMITS = { - 4, - 5 + 7, + 7 }, }, - DEBUG = false + DEBUG = true } CpShovelPositions.MOD_NAME = g_currentModName CpShovelPositions.NAME = ".cpShovelPositions" @@ -65,7 +65,7 @@ CpShovelPositions.KEY = "." .. CpShovelPositions.SPEC_NAME function CpShovelPositions.initSpecialization() local schema = Vehicle.xmlSchemaSavegame g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelState', 'cpSetShovelState', 'consoleCommandSetShovelState', CpShovelPositions) - + g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelArmLimit', 'cpSetShovelArmLimit', 'consoleCommandSetPreUnloadArmLimit', CpShovelPositions) end function CpShovelPositions.prerequisitesPresent(specializations) @@ -120,26 +120,45 @@ function CpShovelPositions:onDraw() end end -function CpShovelPositions:consoleCommandSetShovelState(state) +local function executeConsoleCommand(func, ...) local vehicle = g_currentMission.controlledVehicle if not vehicle then CpUtil.info("Not entered a valid vehicle!") + return false end - state = tonumber(state) - if state == nil or state < 0 or state > CpShovelPositions.NUM_STATES then - CpUtil.infoVehicle(vehicle, "No valid state(0 - %d) was given!", CpShovelPositions.NUM_STATES) - return - end - if not vehicle:getIsAIActive() then - local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) - if found then - shovels[1]:cpSetShovelState(state) - else - CpUtil.infoVehicle(vehicle, "No valid vehicle/implement with a shovel was found!") - end - else + if vehicle:getIsAIActive() then CpUtil.infoVehicle(vehicle, "Error, AI is active!") + return false + end + local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) + if not found then + CpUtil.infoVehicle(vehicle, "No shovel implement found!") + return false end + return func(shovels[1], ...) +end + +function CpShovelPositions:consoleCommandSetShovelState(state) + return executeConsoleCommand(function(shovelImplement, state) + state = tonumber(state) + if state == nil or state < 0 or state > CpShovelPositions.NUM_STATES then + CpUtil.infoVehicle(shovelImplement, "No valid state(0 - %d) was given!", CpShovelPositions.NUM_STATES) + return false + end + shovelImplement:cpSetShovelState(state) + end, state) +end + +function CpShovelPositions:consoleCommandSetPreUnloadArmLimit(min, max) + return executeConsoleCommand(function(shovelImplement, min, max) + min = tonumber(min) + max = tonumber(max) + if min == nil or max == nil then + CpUtil.infoVehicle(shovelImplement, "No valid limits given! min: %s, max: %s", tostring(min), tostring(max)) + return false + end + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS = { min, max } + end, min, max) end --- Changes the current shovel state position. @@ -186,7 +205,9 @@ function CpShovelPositions:cpSetupShovelPositions() spec.armTool = tool spec.armVehicle = vehicle spec.armProjectionNode = CpUtil.createNode("CpShovelArmProjectionNode", - 0, 0, 0, getParent(tool.node)) + 0, 0, 0, vehicle.rootNode) + spec.armToolRefNode = CpUtil.createNode("CpShovelArmToolRefNode", + 0, 0, 0, vehicle.rootNode) elseif tool.axis == "AXIS_FRONTLOADER_TOOL" then spec.shovelToolIx = i spec.shovelTool = tool @@ -220,74 +241,101 @@ end function CpShovelPositions.setShovelPosition(dt, spec, shovel, shovelNode, angle, limits) local min, max = unpack(limits) local targetAngle = math.rad(min) + math.rad(max - min)/2 - if math.deg(angle) < max and math.deg(angle) > min then - ImplementUtil.stopMovingTool(spec.shovelVehicle, spec.shovelTool) - return false - end - - + local deltaAngle = targetAngle - angle local curRot = {} curRot[1], curRot[2], curRot[3] = getRotation(spec.shovelTool.node) local oldRot = curRot[spec.shovelTool.rotationAxis] - local radius = calcDistanceFrom(shovelNode, spec.shovelTool.node) - local x, y, z = getTranslation(spec.shovelTool.node) - - setTranslation(spec.shovelProjectionNode, x, y, z) - - setRotation(spec.shovelProjectionNode, targetAngle - math.pi/2, 0, 0) + local goalAngle = MathUtil.clamp(oldRot + deltaAngle, spec.shovelTool.rotMin, spec.shovelTool.rotMax) + return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, + goalAngle) +end - local sx, sy, sz = getWorldTranslation(shovelNode) - local tx, _, tz = getWorldTranslation(spec.shovelTool.node) - local px, py, pz = localToWorld(spec.shovelProjectionNode, 0, 0, radius) - - if CpShovelPositions.DEBUG then - DebugUtil.drawDebugCircleAtNode(spec.shovelTool.node, radius, 30, nil, true) +function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits) + local min, max = unpack(shovelLimits) + local targetAngle = math.rad(min) + math.rad(max - min)/2 + min, max = unpack(armLimits) + local targetHeight = min + (max - min)/2 - DebugUtil.drawDebugLine(px, py, pz, sx, sy, sz) - DebugUtil.drawDebugLine(px, py, pz, tx, py, tz) - end + local shovelTool = self.spec_cpShovelPositions.shovelTool + local armTool = self.spec_cpShovelPositions.armTool + local shovelVehicle = self.spec_cpShovelPositions.shovelVehicle + local armVehicle = self.spec_cpShovelPositions.armVehicle - local yRot = math.atan2(MathUtil.vector3Length(px - sx, py - sy, pz - sz), - MathUtil.vector3Length(px - tx, py - py, pz - tz)) + local curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(shovelTool.node) + local oldShovelRot = curRot[shovelTool.rotationAxis] - local dyRot = 0 - if angle > targetAngle then - dyRot = -yRot - else - dyRot = yRot - end + local curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(armTool.node) + local oldArmRot = curRot[armTool.rotationAxis] + local armProjectionNode = self.spec_cpShovelPositions.armProjectionNode + local armToolRefNode = self.spec_cpShovelPositions.armToolRefNode + local radius = calcDistanceFrom(shovelTool.node, armTool.node) - local tool = spec.shovelTool - if tool.rotSpeed == nil then - return - end - - local spec = shovel.spec_cylindered - tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) - local rotSpeed = MathUtil.clamp(dyRot * tool.rotSpeed, tool.rotSpeed/3, 0.5) - if dyRot < 0 then - rotSpeed=rotSpeed*(-1) - end - - CpShovelPositions.debug(shovel, - "Shovel position angle: %.2f, targetAngle: %.2f, yRot: %.2f, oldRot: %.2f, rotSpeed: %.5f, rotMin: %.2f, rotMax: %.2f", - math.deg(angle), math.deg(targetAngle), math.deg(dyRot), math.deg(oldRot), - rotSpeed, math.deg(tool.rotMin), math.deg(tool.rotMax)) - - if math.abs(dyRot) < math.pi/(2*180) or rotSpeed == 0 then - ImplementUtil.stopMovingTool(shovel, tool) - return false + local attacherJointNode = self.spec_attachable.attacherJoint.node + local angle, shovelNode = CpShovelPositions.getShovelData(self) + local _, shovelY, _ = localToLocal(shovelNode, attacherJointNode, 0, 0, 0) + + local _, ty, tz = localToLocal(getChildAt(armTool.node, 0), armVehicle.rootNode, 0, 0, 0) + local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) + local sx, sy, sz = 0, targetHeight - shovelY, 0 + local ex, ey, ez = 0, targetHeight - shovelY, 20 + local yMax = ay + radius + if sy > yMax then + sy = yMax - 0.01 + ey = yMax - 0.01 end - if Cylindered.setToolRotation(shovel, tool, rotSpeed, dt, dyRot) then - Cylindered.setDirty(shovel, tool) - - shovel:raiseDirtyFlags(tool.dirtyFlag) - shovel:raiseDirtyFlags(spec.cylinderedDirtyFlag) - return true + local hasIntersection, i1z, i1y, i2z, i2y = MathUtil.getCircleLineIntersection(az, ay, radius, + sz, sy, ez, ey) + local wsx, wsy, wsz = localToWorld(armVehicle.rootNode, sx, sy, sz) + local wex, wey, wez = localToWorld(armVehicle.rootNode, ex, ey, ez) + DebugUtil.drawDebugLine(wsx, wsy, wsz, wex, wey, wez) + + DebugUtil.drawDebugCircleAtNode(armVehicle.rootNode, radius, 30, nil, + true, {ax, ay, az}) + CpUtil.drawDebugNode(armVehicle.rootNode) + CpUtil.drawDebugNode(armTool.node) + CpUtil.drawDebugNode(shovelTool.node) + local isDirty + if hasIntersection then + setTranslation(armProjectionNode, 0, i1y, i1z) + setTranslation(armToolRefNode, ax, ay, az) + local _, shy, shz = localToLocal(shovelTool.node, armVehicle.rootNode, 0, 0, 0) + local dirZ, dirY = MathUtil.vector2Normalize(shz - az, shy - ay) + local yRot = MathUtil.getYRotationFromDirection(-dirZ, dirY) + math.pi/2 + + CpUtil.drawDebugNode(armProjectionNode) + CpUtil.drawDebugNode(armToolRefNode) + local alpha = math.atan2(i1y - ay, i1z - az) + local beta = -math.atan2(i2y - ay, i2z - az) + local debugData = { + { + name = "alpha", value = math.deg(alpha) + }, + { + name = "deltaAlphaOld", value = math.deg(MathUtil.getAngleDifference(alpha, oldArmRot)) + }, + { + name = "old", value = math.deg(oldArmRot) + }, + { + name = "deltaAlpha", value = math.deg(MathUtil.getAngleDifference(alpha, yRot)) + }, + { + name = "dirRot", value = math.deg(yRot) + }, + { + name = "distAlpha", value = MathUtil.vector2Length(i1z - tz, i1y - ty) + }, + } + DebugUtil.renderTable(0.4, 0.4, 0.018, debugData, 0) + local angle = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference(alpha, yRot), armTool.rotMin, armTool.rotMax) + isDirty = ImplementUtil.moveMovingToolToRotation(armVehicle, armTool, dt, angle) end - - -- return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, - -- MathUtil.clamp(oldRot + dyRot , spec.shovelTool.rotMin, spec.shovelTool.rotMax)) + local deltaAngle = targetAngle - angle + local goalAngle = MathUtil.clamp(oldShovelRot + deltaAngle, shovelTool.rotMin, shovelTool.rotMax) + isDirty = isDirty or ImplementUtil.moveMovingToolToRotation(shovelVehicle, shovelTool, dt, goalAngle) + return isDirty end --- Changes the front loader angle dependent on the selected position, relative to a target height. @@ -316,10 +364,8 @@ function CpShovelPositions.setArmPosition(dt, spec, shovel, shovelNode, limits) curRot[1], curRot[2], curRot[3] = getRotation(spec.armTool.node) local oldRot = curRot[spec.armTool.rotationAxis] - setWorldTranslation(spec.armProjectionNode, x, targetAttacherHeight, z) - local _, ay, _ = localToLocal(spec.armTool.node, spec.armVehicle.rootNode, 0, 0, 0) local nodeDiff = MathUtil.clamp( targetHeight - ay , -shovelR, shovelR) + ay @@ -357,8 +403,9 @@ function CpShovelPositions:updateLoadingPosition(dt) local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) local isDirty if angle then - isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS) - isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) + isDirty = CpShovelPositions.setShovelPosition2(self, dt, + CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) + -- isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) end spec.isDirty = isDirty end @@ -368,8 +415,10 @@ function CpShovelPositions:updateTransportPosition(dt) local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) local isDirty if angle then - isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS) - isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) + isDirty = CpShovelPositions.setShovelPosition2(self, dt, + CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) + -- isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS) + -- isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) end spec.isDirty = isDirty end @@ -378,9 +427,11 @@ function CpShovelPositions:updatePreUnloadPosition(dt) local spec = self.spec_cpShovelPositions local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) local isDirty - if angle then - isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS) - isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) + if angle then + isDirty = CpShovelPositions.setShovelPosition2(self, dt, + CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS, CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS) + --isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS) + --isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) end spec.isDirty = isDirty end @@ -390,8 +441,8 @@ function CpShovelPositions:updateUnloadingPosition(dt) local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) local isDirty if angle and maxAngle then - isDirty = CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) - isDirty = isDirty or CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, {math.deg(maxAngle), math.deg(maxAngle) + 2}) + isDirty = CpShovelPositions.setShovelPosition2(self, dt, + {math.deg(maxAngle), math.deg(maxAngle) + 2}, CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS) end spec.isDirty = isDirty end From 3217780d7d4dddc713490eae3ee6eaf3c9f56718 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:02:55 +0200 Subject: [PATCH 015/107] small fix --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 2a8b90c82..116cf4ad1 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -267,8 +267,8 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) refNode = self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode() end if self.shovelController:isShovelOverTrailer(refNode) then - -- self:setNewState(self.states.UNLOADING) - -- self:setMaxSpeed(0) + self:setNewState(self.states.UNLOADING) + self:setMaxSpeed(0) end if not self.isUnloadingAtTrailerActive then if self.shovelController:isShovelOverTrailer(refNode, 3) and self.shovelController:canDischarge() then From 3f09f3f20cba21a3aa78cf56d554cab1c1bac07f Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:32:51 +0200 Subject: [PATCH 016/107] Maybe this one works better --- scripts/ai/controllers/ShovelController.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 96ac9da32..6f9d52055 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -60,7 +60,10 @@ function ShovelController:canDischarge() local dischargeNode = self:getDischargeNode() local spec = self.implement.spec_dischargeable if not spec.isAsyncRaycastActive then + local oldNode = dischargeNode.raycast.node + dischargeNode.raycast.node = self.implement.spec_attachable.attacherJoint.node self.implement:updateRaycast(dischargeNode) + dischargeNode.raycast.node = oldNode end return dischargeNode.dischargeHit end From b258ab08bcf560e7b571bf81dbc9b2ec05d14b73 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:36:24 +0200 Subject: [PATCH 017/107] logic fix --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 116cf4ad1..26f3c728b 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -569,6 +569,10 @@ end --- Unloading ---------------------------------------------------------------- function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() + if self.shovelController:isEmpty() then + self:debug("Finished unloading, as the shovel is empty.") + return true + end if self.isUnloadingAtTrailerActive then if self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) @@ -580,10 +584,6 @@ function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() return false end end - if self.shovelController:isEmpty() then - self:debug("Finished unloading, as the shovel is empty.") - return true - end return false end From ac1a0cd2fefafd4b0dc5a9c05418e4f07902f882 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:48:38 +0200 Subject: [PATCH 018/107] Added debug infos --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 26f3c728b..42efcac54 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -309,6 +309,7 @@ function AIDriveStrategyShovelSiloLoader:update(dt) if self.silo then self.silo:drawDebug() end + self.siloController:draw() if self.heapSilo then CpUtil.drawDebugNode(self.heapNode, false, 3) end @@ -465,7 +466,7 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) else self:debug("Failed to drive close to unload position.") - --self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) + self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) end end @@ -509,13 +510,9 @@ function AIDriveStrategyShovelSiloLoader:startDrivingToSilo() local startPos, endPos = self.siloController:getTarget(self:getWorkWidth()) local x, z = unpack(startPos) local dx, dz = unpack(endPos) - local siloCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, 0, 0, 3, 3, false) - - local distance = siloCourse:getDistanceBetweenVehicleAndWaypoint(self.vehicle, 1) - if distance > 1.5 * self.turningRadius then self:debug("Start driving to silo with pathfinder.") self:startPathfindingToStart(siloCourse) @@ -530,7 +527,6 @@ function AIDriveStrategyShovelSiloLoader:startDrivingOutOfSilo() local startPos, endPos = self.siloController:getLastTarget() local x, z = unpack(endPos) local dx, dz = unpack(startPos) - local reverseCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, 0, 0, 6, 3, true) local ix = reverseCourse:getNextRevWaypointIxFromVehiclePosition(1, self.vehicle:getAIDirectionNode(), 10) @@ -584,7 +580,6 @@ function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() return false end end - return false end From d455c35b6f6a354d5e87c5d64763c32b6d96cac2 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:06:21 +0200 Subject: [PATCH 019/107] Diff unload station improvements and added high rise shovel unload --- config/VehicleConfigurations.xml | 7 +- .../ai/AIDriveStrategyShovelSiloLoader.lua | 206 +++++++++++------- scripts/ai/ImplementUtil.lua | 56 +++-- scripts/ai/controllers/ShovelController.lua | 7 + scripts/ai/jobs/CpAIJobSiloLoader.lua | 28 ++- scripts/ai/tasks/CpAITaskSiloLoader.lua | 4 +- scripts/config/VehicleConfigurations.lua | 1 + scripts/gui/CpAIFrameExtended.lua | 8 +- scripts/gui/UnloadingTriggerPlot.lua | 14 +- .../specializations/CpAIBunkerSiloWorker.lua | 3 +- .../specializations/CpAISiloLoaderWorker.lua | 4 +- scripts/specializations/CpShovelPositions.lua | 22 +- scripts/trigger/TriggerManager.lua | 31 ++- scripts/trigger/TriggerWrapper.lua | 31 ++- 14 files changed, 286 insertions(+), 136 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 73f809328..778c285c4 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -97,6 +97,9 @@ You can define the following custom settings: Overrides the automatic selection of a moving tool for example for the shield controller of a snowcat. Used to control the target tilt. +- shovelMovingToolIx: number + If the shovel is a high dump shovel then this moving tool ix is needed. + - modName: Name of the .zip file (without '.zip') In case a Mod has the same .xml filename for the vehicle/implement, as a default giants vehicle/implement, add the name of the .zip file to prevent conflicts. For example: "FS22_exampleMod". @@ -317,7 +320,9 @@ You can define the following custom settings: - + diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 42efcac54..6308574f2 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -89,10 +89,8 @@ function AIDriveStrategyShovelSiloLoader:setSiloAndHeap(bunkerSilo, heapSilo) end ---@param unloadTrigger CpTrigger ----@param unloadStation table -function AIDriveStrategyShovelSiloLoader:setUnloadTriggerAndStation(unloadTrigger, unloadStation) +function AIDriveStrategyShovelSiloLoader:setUnloadTrigger(unloadTrigger) self.unloadTrigger = unloadTrigger - self.unloadStation = unloadStation end function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) @@ -104,15 +102,19 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) self.jobParameters = jobParameters self.unloadPositionNode = CpUtil.createNode("unloadPositionNode", 0, 0, 0) + --- Is the unload target a trailer? self.isUnloadingAtTrailerActive = jobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRAILER if not self.isUnloadingAtTrailerActive then self:debug("Starting shovel silo to unload into unload trigger.") - local x, y, z = getWorldTranslation(self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode()) + --- Uses the exactFillRootNode from the trigger + --- and the direction of the unload position marker + --- to place the unload position node slightly in front. + local x, y, z = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode()) setTranslation(self.unloadPositionNode, x, y, z) local position = jobParameters.unloadPosition local dirX, dirZ = position:getDirection() setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1) - local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -self.distShovelUnloadStationPreUnload) + local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -math.max(self.distShovelTrailerPreUnload, self.turningRadius)) setTranslation(self.unloadPositionNode, dx, dy, dz) else self:debug("Starting shovel silo to unload into trailer.") @@ -192,7 +194,6 @@ function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) else self:setNewState(self.states.WAITING_FOR_TRAILER) end - --self.vehicle:stopCurrentAIJob(AIMessageSuccessFinishedJob.new()) end end end @@ -258,13 +259,17 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:searchForTrailerToUnloadInto() self.lastTrailerSearch = g_time end + if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then + DebugUtil.drawDebugCircleAtNode(self.siloFrontNode, self.maxValidTrailerDistanceToSiloFront, + math.ceil(self.maxValidTrailerDistanceToSiloFront), nil, false, {0, 3, 0}) + end elseif self.state == self.states.DRIVING_TO_UNLOAD then self:setMaxSpeed(self.settings.reverseSpeed:getValue()) local refNode if self.isUnloadingAtTrailerActive then refNode = self.targetTrailer.exactFillRootNode else - refNode = self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode() + refNode = self.unloadTrigger:getFillUnitExactFillRootNode() end if self.shovelController:isShovelOverTrailer(refNode) then self:setNewState(self.states.UNLOADING) @@ -336,17 +341,111 @@ function AIDriveStrategyShovelSiloLoader:getProximitySensorWidth() return self.vehicle.size.width - 0.5 end +function AIDriveStrategyShovelSiloLoader:getWorkWidth() + return self.settings.bunkerSiloWorkWidth:getValue() +end + function AIDriveStrategyShovelSiloLoader:setNewState(newState) self:debug("Changed State from %s to %s", self.state.name, newState.name) self.state = newState end +--- Is the trailer valid or not? +---@param trailer table +---@param trailerToIgnore table|nil +---@return boolean +---@return table|nil +function AIDriveStrategyShovelSiloLoader:isValidTrailer(trailer, trailerToIgnore) + local function debug(...) + self:debug("%s attached to: %s => %s", CpUtil.getName(trailer), + trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle", string.format(...)) + end + if not SpecializationUtil.hasSpecialization(Trailer, trailer.specializations) then + return false + end + if trailer.rootVehicle and not AIUtil.isStopped(trailer.rootVehicle) then + self:debug("is not stopped!", CpUtil.getName(trailer)) + return false + end + if trailerToIgnore and table.hasElement(trailerToIgnore, trailer) then + debug("will be ignored!", CpUtil.getName(trailer)) + return false + end + local canLoad, fillUnitIndex, fillType, exactFillRootNode = + ImplementUtil.getCanLoadTo(trailer, self.shovelImplement, + nil, debug) + if not canLoad or exactFillRootNode == nil then + debug("can't be used!", CpUtil.getName(trailer)) + return false + end + return true, { fillUnitIndex = fillUnitIndex, + fillType = fillType, + exactFillRootNode = exactFillRootNode, + trailer = trailer } +end + +--- Gets the closest trailer data with the distance +---@param trailerToIgnore table|nil optional trailers that will be ignored. +---@return table|nil +---@return number +function AIDriveStrategyShovelSiloLoader:getClosestTrailerAndDistance(trailerToIgnore) + local closestDistance = math.huge + local closestTrailerData = nil + for i, vehicle in pairs(g_currentMission.vehicles) do + local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode) + if dist < closestDistance then + local valid, trailerData = self:isValidTrailer(vehicle, trailerToIgnore) + if valid then + closestDistance = dist + closestTrailerData = trailerData + end + end + end + return closestTrailerData, closestDistance +end + +--- Searches for trailer to unload into in a maximum radius relative to the silo front center. +function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() + self:debug("Searching for an trailer nearby.") + local trailerData, dist = self:getClosestTrailerAndDistance({}) + if not trailerData then + self:debug("No valid trailer found anywhere!") + return + end + local trailer = trailerData.trailer + if dist > self.maxValidTrailerDistanceToSiloFront then + self:debug("Closest Trailer %s attached to %s with the distance %.2fm/%.2fm found is to far away!", + CpUtil.getName(trailer), trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle", + dist, self.maxValidTrailerDistanceToSiloFront) + return + end + --- Sets the unload position node in front of the closest side of the trailer. + self:debug("Found a valid trailer %s within distance %.2f", CpUtil.getName(trailer), dist) + self.targetTrailer = trailerData + local _, _, distShovelDirectionNode = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) + local dirX, _, dirZ = localDirectionToWorld(trailer.rootNode, 0, 0, 1) + local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) + local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) + if dx > 0 then + local x, y, z = localToWorld(trailer.rootNode, math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) + setTranslation(self.unloadPositionNode, x, y, z) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) + else + local x, y, z = localToWorld(trailer.rootNode, -math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) + setTranslation(self.unloadPositionNode, x, y, z) + setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) + end + self:startPathfindingToTrailer() +end + + + ---------------------------------------------------------------- --- Pathfinding ---------------------------------------------------------------- ---- Find an alignment path to the heap course. ----@param course table heap course +--- Find an alignment path to the silo lane course. +---@param course table silo lane course function AIDriveStrategyShovelSiloLoader:startPathfindingToStart(course) if not self.pathfinder or not self.pathfinder:isActive() then self:setNewState(self.states.WAITING_FOR_PATHFINDER) @@ -369,74 +468,19 @@ end function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToStart(path) if path and #path > 2 then - self:debug("Found alignment path to the course for the heap.") + self:debug("Found alignment path to the course for the silo.") local alignmentCourse = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) self:startCourse(alignmentCourse, 1) self:setNewState(self.states.DRIVING_ALIGNMENT_COURSE) else local course = self:getRememberedCourseAndIx() - self:debug("No alignment path found!") + self:debug("No alignment path found, so driving directly to the course!") self:startCourse(course, 1) self:setNewState(self.states.DRIVING_INTO_SILO) end end - -function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() - self:debug("Searching for an trailer nearby.") - local function getClosestTrailerAndDistance() - local closestDistance = math.huge - local closestTrailer = nil - for i, vehicle in pairs(g_currentMission.vehicles) do - if SpecializationUtil.hasSpecialization(Trailer, vehicle.specializations) and AIUtil.isStopped(vehicle.rootVehicle) then - local dist = calcDistanceFrom(vehicle.rootNode, self.siloFrontNode) - if dist < closestDistance then - closestDistance = dist - closestTrailer = vehicle - end - end - end - return closestTrailer, closestDistance - end - local trailer, dist = getClosestTrailerAndDistance() - if not trailer then - self:debug("No valid trailer found anywhere!") - return - end - if dist > self.maxValidTrailerDistanceToSiloFront then - self:debug("No Trailer with the max distance found, closest: %.2f", dist) - return - end - self:debug("Found a trailer %s within distance %.2f", CpUtil.getName(trailer), dist) - local canLoad, fillUnitIndex, fillType, exactFillRootNode = - ImplementUtil.getCanLoadTo(trailer, self.shovelImplement) - if canLoad and exactFillRootNode ~= nil then - self.targetTrailer = { - fillUnitIndex = fillUnitIndex, - fillType = fillType, - exactFillRootNode = exactFillRootNode, - trailer = trailer - } - self:debug("Unloading to trailer %s in distance %.2f.", CpUtil.getName(trailer), dist) - local _, _, distShovelDirectionNode = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) - local dirX, _, dirZ = localDirectionToWorld(trailer.rootNode, 0, 0, 1) - local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) - local dx, _, dz = localToLocal(self.shovelController:getShovelNode(), trailer.rootNode, 0, 0, 0) - if dx > 0 then - local x, y, z = localToWorld(trailer.rootNode, math.abs(distShovelDirectionNode) + self.distShovelTrailerPreUnload, 0, 0) - setTranslation(self.unloadPositionNode, x, y, z) - setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot - math.pi/2), 0) - else - local x, y, z = localToWorld(trailer.rootNode, -math.abs(distShovelDirectionNode) - self.distShovelTrailerPreUnload, 0, 0) - setTranslation(self.unloadPositionNode, x, y, z) - setRotation(self.unloadPositionNode, 0, MathUtil.getValidLimit(yRot + math.pi/2), 0) - end - self:startPathfindingToTrailer() - else - self:debug("Unloading into trailer %s not possible.", CpUtil.getName(trailer)) - end -end - +--- Starts Pathfinding to the position node in front of a unload trigger. function AIDriveStrategyShovelSiloLoader:startPathfindingToUnloadPosition() if not self.pathfinder or not self.pathfinder:isActive() then self:setNewState(self.states.WAITING_FOR_PATHFINDER) @@ -466,10 +510,11 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) else self:debug("Failed to drive close to unload position.") - self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) + -- self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) end end +--- Starts Pathfinding to the position node in front of the trailer side. function AIDriveStrategyShovelSiloLoader:startPathfindingToTrailer() if not self.pathfinder or not self.pathfinder:isActive() then self:setNewState(self.states.WAITING_FOR_PATHFINDER) @@ -506,24 +551,27 @@ end --- Silo work ---------------------------------------------------------------- +--- Starts driving into the silo lane function AIDriveStrategyShovelSiloLoader:startDrivingToSilo() + --- Creates a straight course in the silo. local startPos, endPos = self.siloController:getTarget(self:getWorkWidth()) local x, z = unpack(startPos) local dx, dz = unpack(endPos) local siloCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, 0, 0, 3, 3, false) local distance = siloCourse:getDistanceBetweenVehicleAndWaypoint(self.vehicle, 1) - if distance > 1.5 * self.turningRadius then + if distance > self.turningRadius then self:debug("Start driving to silo with pathfinder.") self:startPathfindingToStart(siloCourse) else - self:debug("Start driving into the silo.") + self:debug("Start driving into the silo directly.") self:startCourse(siloCourse, 1) self:setNewState(self.states.DRIVING_INTO_SILO) end end function AIDriveStrategyShovelSiloLoader:startDrivingOutOfSilo() + --- Creates the straight reverse course. local startPos, endPos = self.siloController:getLastTarget() local x, z = unpack(endPos) local dx, dz = unpack(startPos) @@ -537,6 +585,7 @@ function AIDriveStrategyShovelSiloLoader:startDrivingOutOfSilo() self:setNewState(self.states.DRIVING_OUT_OF_SILO) end +--- Drives from the position node in front of the trailer to the trailer, so the unloading can begin after that. function AIDriveStrategyShovelSiloLoader:approachTrailerForUnloading() local dx, _, dz = getWorldTranslation(self.targetTrailer.exactFillRootNode) local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) @@ -547,8 +596,9 @@ function AIDriveStrategyShovelSiloLoader:approachTrailerForUnloading() self:setNewState(self.states.DRIVING_TO_UNLOAD) end +--- Drives from the position node in front of the trigger to the unload trigger, so the unloading can begin after that. function AIDriveStrategyShovelSiloLoader:approachUnloadStationForUnloading() - local dx, _, dz = getWorldTranslation(self.unloadTrigger:getTrigger():getFillUnitExactFillRootNode()) + local dx, _, dz = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode()) local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, 0, -3, 3, 2, false) @@ -557,13 +607,12 @@ function AIDriveStrategyShovelSiloLoader:approachUnloadStationForUnloading() self:setNewState(self.states.DRIVING_TO_UNLOAD) end -function AIDriveStrategyShovelSiloLoader:getWorkWidth() - return self.settings.bunkerSiloWorkWidth:getValue() -end - ---------------------------------------------------------------- --- Unloading ---------------------------------------------------------------- + +--- Is the unloading finished? +---@return boolean function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() if self.shovelController:isEmpty() then self:debug("Finished unloading, as the shovel is empty.") @@ -572,17 +621,18 @@ function AIDriveStrategyShovelSiloLoader:hasFinishedUnloading() if self.isUnloadingAtTrailerActive then if self.targetTrailer.trailer:getFillUnitFreeCapacity(self.targetTrailer.fillUnitIndex) <= 0 then self:debug("Trailer is full, abort unloading into trailer %s.", CpUtil.getName(self.targetTrailer.trailer)) + self.targetTrailer = nil return true end else - if self.unloadTrigger:getTrigger():getFillUnitFreeCapacity(1, self.shovelController:getDischargeFillType(), self.vehicle:getOwnerFarmId()) <= 0 then - self:debug("Unload Trigger is full.") - return false + if self.unloadTrigger:getFillUnitFreeCapacity(1, self.shovelController:getDischargeFillType(), self.vehicle:getOwnerFarmId()) <= 0 then + self:debugSparse("Unload Trigger is full.") end end return false end +--- Starts reverse straight to make some space to the trailer or unload trigger. function AIDriveStrategyShovelSiloLoader:startReversingAwayFromUnloading() local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, 0 ) diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 32af4adc4..1d58762ec 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -431,14 +431,11 @@ end ---@return number|nil target implement fill unit ix to load into. ---@return number|nil fill type to load ---@return number|nil target exact fill root node -function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, dischargeNode, suppressLog) +function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, dischargeNode, debugFunc) local function debug(str, ...) - if not suppressLog then - if g_updateLoopIndex % 100 == 0 then - CpUtil.debugVehicle(CpDebug.DBG_SILO, implementToLoadFrom.rootVehicle, - str, ...) - end + if debugFunc then + debugFunc(str, ...) end end @@ -457,24 +454,39 @@ function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, di return false, nil, nil, nil end + --- Is the fill unit a valid load target? + ---@param fillUnitIndex number + ---@return boolean + ---@return number|nil + ---@return number|nil + local function canLoad(fillUnitIndex) + if not loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, fillType) then + debug("Fill unit(%d) doesn't support fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) + return false + end + if not loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, fillType) then + debug("Fill unit(%d) doesn't allow fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) + return false + end + if loadTargetImplement.getFillUnitFreeCapacity and + loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, fillType, implementToLoadFrom:getActiveFarm()) <= 0 then + debug("Fill unit(%d) is full with fill type %s!", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) + return false + end + if loadTargetImplement.getIsFillAllowedFromFarm and + not loadTargetImplement:getIsFillAllowedFromFarm(implementToLoadFrom:getActiveFarm()) then + debug("Fill unit(%d) filling to target farm %s from %s not allowed!", + fillUnitIndex, loadTargetImplement:getOwnerFarmId(), implementToLoadFrom:getActiveFarm()) + return false + end + return true, fillUnitIndex, loadTargetImplement:getFillUnitExactFillRootNode(fillUnitIndex) + end + local validTarget, targetFillUnitIndex, exactFillRootNode for fillUnitIndex, fillUnit in pairs(loadTargetImplement:getFillUnits()) do - if loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, fillType) then - if loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, fillType) then - if loadTargetImplement.getFillUnitFreeCapacity == nil or loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, fillType, implementToLoadFrom:getActiveFarm()) > 0 then - if loadTargetImplement.getIsFillAllowedFromFarm == nil or loadTargetImplement:getIsFillAllowedFromFarm(implementToLoadFrom:getActiveFarm()) then - validTarget, targetFillUnitIndex, exactFillRootNode = true, fillUnitIndex, loadTargetImplement:getFillUnitExactFillRootNode(fillUnitIndex) - else - debug("Fill unit(%d) filling to target farm %s from %s not allowed!", fillUnitIndex, loadTargetImplement:getOwnerFarmId(), implementToLoadFrom:getActiveFarm()) - end - else - debug("Fill unit(%d) is full with fill type %s!", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) - end - else - debug("Fill unit(%d) doesn't allow fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) - end - else - debug("Fill unit(%d) doesn't support fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeTitleByIndex(fillType)) + validTarget, targetFillUnitIndex, exactFillRootNode = canLoad(fillUnitIndex) + if validTarget then + break end end diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 6f9d52055..a1ec9020d 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -77,9 +77,16 @@ function ShovelController:isShovelOverTrailer(refNode, margin) local _, _, distShovelToRoot = localToLocal(node, self.implement.rootVehicle:getAIDirectionNode(), 0, 0, 0) local _, _, distTrailerToRoot = localToLocal(refNode, self.implement.rootVehicle:getAIDirectionNode(), 0, 0, 0) margin = margin or 0 + if self:isHighDumpShovel() then + margin = margin + 1 + end return ( distTrailerToRoot - distShovelToRoot ) < margin end +function ShovelController:isHighDumpShovel() + return g_vehicleConfigurations:get(self.implement, "shovelMovingToolIx") ~= nil +end + function ShovelController:moveShovelToLoadingPosition() return self:moveShovelToPosition(self.POSITIONS.LOADING) end diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 914e2eac5..90d5c427a 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -186,31 +186,47 @@ end ---@param unloadPosition CpAIParameterPositionAngle ---@return boolean ---@return table|nil +---@return table|nil function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) local x, z = unloadPosition:getPosition() local dirX, dirZ = unloadPosition:getDirection() if x == nil or dirX == nil then return false end - return g_triggerManager:getDischargeableUnloadTriggerAt( x, z, dirX, dirZ, 5, 5) + local fillType + local silo = self.heap or self.bunkerSilo + if silo then + fillType = silo:getFillType() + end + local found, trigger, station = g_triggerManager:getDischargeableUnloadTriggerAt( x, z, dirX, dirZ, 5, 25) + if found and fillType~=nil then + if not trigger:getIsFillTypeAllowed(fillType) then + --- Fill type is not supported by the trigger. + return false + end + end + return found, trigger, station end function CpAIJobSiloLoader:drawSilos(map) self.heapPlot:draw(map) g_bunkerSiloManager:drawSilos(map, self.bunkerSilo) if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then - g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger) + local fillType + local silo = self.heap or self.bunkerSilo + if silo then + fillType = silo:getFillType() + end + g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillType) end end ---- Gets the giants unload station. +--- Gets the unload station. function CpAIJobSiloLoader:getUnloadingStations() local unloadingStations = {} for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do - if g_currentMission.accessHandler:canPlayerAccess(unloadingStation) and unloadingStation:isa(UnloadingStation) then - table.insert(unloadingStations, unloadingStation) - end + table.insert(unloadingStations, unloadingStation) end return unloadingStations end diff --git a/scripts/ai/tasks/CpAITaskSiloLoader.lua b/scripts/ai/tasks/CpAITaskSiloLoader.lua index ddfc5333a..31f856bbe 100644 --- a/scripts/ai/tasks/CpAITaskSiloLoader.lua +++ b/scripts/ai/tasks/CpAITaskSiloLoader.lua @@ -33,8 +33,8 @@ end function CpAITaskSiloLoader:start() if self.isServer then - local _, unloadTrigger, unloadStation = self.job:getUnloadTriggerAt(self.job:getCpJobParameters().unloadPosition) - self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap, unloadTrigger, unloadStation) + local _, unloadTrigger, _ = self.job:getUnloadTriggerAt(self.job:getCpJobParameters().unloadPosition) + self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap, unloadTrigger) end CpAITaskSiloLoader:superClass().start(self) diff --git a/scripts/config/VehicleConfigurations.lua b/scripts/config/VehicleConfigurations.lua index fb8a8509b..d5fe91b0e 100644 --- a/scripts/config/VehicleConfigurations.lua +++ b/scripts/config/VehicleConfigurations.lua @@ -47,6 +47,7 @@ VehicleConfigurations.attributes = { useVehicleSizeForMarkers = XMLValueType.BOOL, armMovingToolIx = XMLValueType.INT, movingToolIx = XMLValueType.INT, + shovelMovingToolIx = XMLValueType.INT, ignoreBaleCollisionForward = XMLValueType.BOOL, } diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 21f4cb161..3737974d1 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -547,7 +547,7 @@ function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, CpInGameMenuAIFrameExtended.resetHotspots(self) self:updateParameterValueTexts() end) - + g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) superFunc(self, parameter, callback, ...) end InGameMenuAIFrame.startPickPosition = Utils.overwrittenFunction(InGameMenuAIFrame.startPickPosition, @@ -603,16 +603,16 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) elseif parameterType == AIParameterType.POSITION or parameterType == AIParameterType.POSITION_ANGLE then element:setText(parameter:getString()) - g_currentMission:addMapHotspot(self.aiTargetMapHotspot) + g_currentMission:addMapHotspot(self.rawAiTargetMapHotspot) local x, z = parameter:getPosition() - self.aiTargetMapHotspot:setWorldPosition(x, z) + self.rawAiTargetMapHotspot:setWorldPosition(x, z) if parameterType == AIParameterType.POSITION_ANGLE then local angle = parameter:getAngle() + math.pi - self.aiTargetMapHotspot:setWorldRotation(angle) + self.rawAiTargetMapHotspot:setWorldRotation(angle) end end end diff --git a/scripts/gui/UnloadingTriggerPlot.lua b/scripts/gui/UnloadingTriggerPlot.lua index b8f84607a..c2913504b 100644 --- a/scripts/gui/UnloadingTriggerPlot.lua +++ b/scripts/gui/UnloadingTriggerPlot.lua @@ -5,13 +5,14 @@ UnloadingTriggerPlot = CpObject() function UnloadingTriggerPlot:init(node) self.courseOverlayId = createImageOverlay('dataS/scripts/shared/graph_pixel.dds') self.isVisible = false - -- the normal FS22 blue - self.color = {CpGuiUtil.getNormalizedRgb(42, 193, 237)} + -- the normal FS22 blue -- 0.9900 0.4640 0.0010 1 + --self.color = {CpGuiUtil.getNormalizedRgb(42, 193, 237)} + self.color = {CpGuiUtil.getNormalizedRgb(255, 128, 0)} -- a lighter shade of the same color - self.lightColor = {CpGuiUtil.getNormalizedRgb(45, 207, 255)} + self.lightColor = {CpGuiUtil.getNormalizedRgb(213, 255, 0)} -- a darker shade of the same color self.darkColor = {CpGuiUtil.getNormalizedRgb(19, 87, 107)} - self.lineThickness = 2 / g_screenHeight -- 2 pixels + self.lineThickness = 4 / g_screenHeight -- 4 pixels self.isHighlighted = false local _ self.x, _, self.z = getWorldTranslation(node) @@ -19,8 +20,11 @@ end function UnloadingTriggerPlot:draw(map) local r, g, b = unpack(self.color) + if self.isHighlighted then + r, g, b = unpack(self.lightColor) + end setOverlayColor( self.courseOverlayId, r, g, b, 0.8 ) - local x, y = CpGuiUtil.worldToScreen(map, self.x, self.z, false) + -- local x, y = CpGuiUtil.worldToScreen(map, self.x, self.z, false) local s1x, s1y = CpGuiUtil.worldToScreen(map, self.x - 5, self.z - 5, false) local e1x, e1y = CpGuiUtil.worldToScreen(map, self.x + 5, self.z + 5, false) diff --git a/scripts/specializations/CpAIBunkerSiloWorker.lua b/scripts/specializations/CpAIBunkerSiloWorker.lua index 2a6828256..0a66d1596 100644 --- a/scripts/specializations/CpAIBunkerSiloWorker.lua +++ b/scripts/specializations/CpAIBunkerSiloWorker.lua @@ -101,8 +101,7 @@ function CpAIBunkerSiloWorker:getCanStartCpBunkerSiloWorker() and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, Leveler)) - and not (AIUtil.hasChildVehicleWithSpecialization(self, Shovel) - and AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt)) + and not AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end function CpAIBunkerSiloWorker:getCanStartCp(superFunc) diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index 58d485318..252cec82f 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -178,7 +178,7 @@ function CpAISiloLoaderWorker:startCpAtLastWp(superFunc, ...) end end -function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger, unloadStation) +function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger) if self.isServer then local strategy if AIUtil.hasAIImplementWithSpecialization(self, ConveyorBelt) then @@ -187,9 +187,9 @@ function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, else CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting a shovel silo loader strategy.") strategy = AIDriveStrategyShovelSiloLoader.new() + strategy:setUnloadTrigger(unloadTrigger) end strategy:setSiloAndHeap(bunkerSilo, heap) - strategy:setUnloadTriggerAndStation(unloadTrigger, unloadStation) strategy:setAIVehicle(self, jobParameters) self:startCpWithStrategy(strategy) end diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 8d24151c4..092d41467 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -250,7 +250,7 @@ function CpShovelPositions.setShovelPosition(dt, spec, shovel, shovelNode, angle goalAngle) end -function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits) +function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits, useHighDumpShovel) local min, max = unpack(shovelLimits) local targetAngle = math.rad(min) + math.rad(max - min)/2 min, max = unpack(armLimits) @@ -332,9 +332,24 @@ function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits) local angle = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference(alpha, yRot), armTool.rotMin, armTool.rotMax) isDirty = ImplementUtil.moveMovingToolToRotation(armVehicle, armTool, dt, angle) end + + local highDumpShovelIx = g_vehicleConfigurations:get(self, "shovelMovingToolIx") + if highDumpShovelIx then + local tool = self.spec_cylindered.movingTools[highDumpShovelIx] + if useHighDumpShovel then + local _, dy, _ = localDirectionToWorld(getParent(tool.node), 0, 0, 1) + angle = math.acos(dy) + targetAngle = math.pi/2 + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMax) or isDirty + else + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMin) or isDirty + end + end local deltaAngle = targetAngle - angle local goalAngle = MathUtil.clamp(oldShovelRot + deltaAngle, shovelTool.rotMin, shovelTool.rotMax) - isDirty = isDirty or ImplementUtil.moveMovingToolToRotation(shovelVehicle, shovelTool, dt, goalAngle) + isDirty = ImplementUtil.moveMovingToolToRotation(shovelVehicle, + shovelTool, dt, goalAngle) or isDirty + return isDirty end @@ -442,7 +457,8 @@ function CpShovelPositions:updateUnloadingPosition(dt) local isDirty if angle and maxAngle then isDirty = CpShovelPositions.setShovelPosition2(self, dt, - {math.deg(maxAngle), math.deg(maxAngle) + 2}, CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS) + {math.deg(maxAngle), math.deg(maxAngle) + 2}, + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, true) end spec.isDirty = isDirty end diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua index 3c84f4c22..31a8e111f 100644 --- a/scripts/trigger/TriggerManager.lua +++ b/scripts/trigger/TriggerManager.lua @@ -1,7 +1,8 @@ ---- Links all the needed trigger, except bunker silos to trigger wrappers. +--- Stores all the relevant giants triggers expect bunker silos. +--- For now only unload triggers are supported. ---@class TriggerManager TriggerManager = CpObject() - +TriggerManager.DEBUG = true function TriggerManager:init() ---@type table self.unloadTriggers = {} @@ -9,6 +10,8 @@ function TriggerManager:init() self.dischargeableUnloadTriggers = {} end +--- Adds an unload trigger. +---@param silo table UnloadTrigger function TriggerManager:addUnloadingSilo(silo) if silo.exactFillRootNode ~= nil then self.unloadTriggers[silo.exactFillRootNode] = CpTrigger(silo, silo.exactFillRootNode) @@ -18,6 +21,8 @@ function TriggerManager:addUnloadingSilo(silo) end end +--- Removes the unload trigger, as it got removed for example sold. +---@param silo table UnloadTrigger function TriggerManager:removeUnloadingSilo(silo) if silo.exactFillRootNode ~= nil then if self.unloadTriggers[silo.exactFillRootNode] then @@ -28,6 +33,9 @@ function TriggerManager:removeUnloadingSilo(silo) end end +--- Gets the unload trigger from the exactFillRootNode. +---@param node number exactFillRootNode +---@return CpTrigger function TriggerManager:getUnloadTriggerForNode(node) return self.unloadTriggers[node] end @@ -41,7 +49,7 @@ end ---@param width number ---@param length number ---@return boolean ----@return table|nil +---@return CpTrigger|nil ---@return table|nil function TriggerManager:getTriggerAt(triggers, x, z, dirX, dirZ, width, length) local angle = MathUtil.getYRotationFromDirection(dirX, dirZ) @@ -74,7 +82,7 @@ function TriggerManager:getTriggerAt(triggers, x, z, dirX, dirZ, width, length) for node, trigger in pairs(triggers) do dx, _, dz = getWorldTranslation(node) if CpMathUtil.isPointInPolygon(area, dx, dz) then - return true, trigger, trigger:getTrigger():getTarget() + return true, trigger, trigger:getTarget() end end return false @@ -88,7 +96,7 @@ end ---@param width number ---@param length number ---@return boolean ----@return table|nil +---@return CpTrigger|nil ---@return table|nil function TriggerManager:getUnloadTriggerAt(x, z, dirX, dirZ, width, length) return self:getTriggerAt(self.unloadTriggers, x, z, dirX, dirZ, width, length) @@ -102,7 +110,7 @@ end ---@param width number ---@param length number ---@return boolean found? ----@return table|nil unload trigger +---@return CpTrigger|nil unload trigger ---@return table|nil unload station/placeable function TriggerManager:getDischargeableUnloadTriggerAt(x, z, dirX, dirZ, width, length) return self:getTriggerAt(self.dischargeableUnloadTriggers, x, z, dirX, dirZ, width, length) @@ -124,16 +132,19 @@ end --- Draws all bunker silos onto the ai map. ---@param map table map to draw to. ---@param selected CpTrigger silo that gets highlighted. -function TriggerManager:drawDischargeableTriggers(map, selected) +---@param fillType number|nil fill type that needs to be supported. +function TriggerManager:drawDischargeableTriggers(map, selected, fillType) for _, trigger in pairs(self.dischargeableUnloadTriggers) do - trigger:drawPlot(map, selected) + trigger:drawPlot(map, selected, fillType) end end function TriggerManager:draw() for node, trigger in pairs(self.unloadTriggers) do - local text = string.format("%s:\n %d", getName(node), node ) - CpUtil.drawDebugNode(node, false, 2, text) + if self.DEBUG then + local text = string.format("%s:\n %d", getName(node), node ) + CpUtil.drawDebugNode(node, false, 2, text) + end end end diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua index 6fc538f0e..c22283690 100644 --- a/scripts/trigger/TriggerWrapper.lua +++ b/scripts/trigger/TriggerWrapper.lua @@ -1,4 +1,5 @@ +--- Wrapper for a trigger. ---@class CpTrigger CpTrigger = CpObject() @@ -20,7 +21,35 @@ function CpTrigger:getTrigger() return self.trigger end -function CpTrigger:drawPlot(map, selectedTrigger) +function CpTrigger:getTarget() + return self.trigger:getTarget() +end + +function CpTrigger:getFillUnitExactFillRootNode(fillUnitIndex) + return self.trigger:getFillUnitExactFillRootNode(fillUnitIndex) +end + +function CpTrigger:getFillUnitFreeCapacity(fillUnitIndex, fillTypeIndex, farmId) + return self.trigger:getFillUnitFreeCapacity(fillUnitIndex, fillTypeIndex, farmId) +end + +--- Is the fill type allowed ? +---@param fillType any +---@return boolean +function CpTrigger:getIsFillTypeAllowed(fillType) + return self.trigger:getIsFillTypeAllowed(fillType) +end + +---@param map table +---@param selectedTrigger CpTrigger +---@param fillType number|nil +function CpTrigger:drawPlot(map, selectedTrigger, fillType) + if fillType and fillType ~= FillType.UNKNOWN then + if not self.trigger:getIsFillTypeAllowed(fillType) then + --- Fill type is not allowed. + return + end + end self.plot:setHighlighted(self == selectedTrigger) self.plot:draw(map) end \ No newline at end of file From 0775bc016ee8f3565f71c5d84a98e976ab933e50 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:33:19 +0200 Subject: [PATCH 020/107] Some more shovel position adjustments and technical debt reduction --- scripts/specializations/CpShovelPositions.lua | 299 ++++++++---------- 1 file changed, 128 insertions(+), 171 deletions(-) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 092d41467..4898229c4 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -95,30 +95,9 @@ function CpShovelPositions.registerFunctions(vehicleType) SpecializationUtil.registerFunction(vehicleType, "getCpShovelUnloadingPositionHeight", CpShovelPositions.getCpShovelUnloadingPositionHeight) end - -function CpShovelPositions:onLoad(savegame) - --- Register the spec: spec_ShovelPositions - self.spec_cpShovelPositions = self["spec_" .. CpShovelPositions.SPEC_NAME] - local spec = self.spec_cpShovelPositions - --- Current shovel state. - spec.state = CpShovelPositions.DEACTIVATED - spec.isDirty = false -end - -function CpShovelPositions:onPostAttach() - if self.spec_cpShovelPositions then - CpShovelPositions.cpSetupShovelPositions(self) - end -end - -function CpShovelPositions:onDraw() - if CpShovelPositions.DEBUG and self:getRootVehicle() then - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) - if shovelNode then - DebugUtil.drawDebugNode(shovelNode, "shovelNode") - end - end -end +-------------------------------------------- +--- Console Commands +-------------------------------------------- local function executeConsoleCommand(func, ...) local vehicle = g_currentMission.controlledVehicle @@ -161,6 +140,50 @@ function CpShovelPositions:consoleCommandSetPreUnloadArmLimit(min, max) end, min, max) end +-------------------------------------------- +--- Event Listener +-------------------------------------------- + +function CpShovelPositions:onLoad(savegame) + --- Register the spec: spec_ShovelPositions + self.spec_cpShovelPositions = self["spec_" .. CpShovelPositions.SPEC_NAME] + local spec = self.spec_cpShovelPositions + --- Current shovel state. + spec.state = CpShovelPositions.DEACTIVATED + spec.isDirty = false +end + +function CpShovelPositions:onPostAttach() + if self.spec_cpShovelPositions then + CpShovelPositions.cpSetupShovelPositions(self) + end +end + +function CpShovelPositions:onDraw() + if CpShovelPositions.DEBUG and self:getRootVehicle() then + local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + if shovelNode then + DebugUtil.drawDebugNode(shovelNode, "shovelNode") + end + end +end + +function CpShovelPositions:onUpdateTick(dt) + local spec = self.spec_cpShovelPositions + if spec.shovelToolIx == nil or spec.armToolIx == nil then + return + end + if spec.state == CpShovelPositions.LOADING then + CpShovelPositions.updateLoadingPosition(self, dt) + elseif spec.state == CpShovelPositions.TRANSPORT then + CpShovelPositions.updateTransportPosition(self, dt) + elseif spec.state == CpShovelPositions.PRE_UNLOAD then + CpShovelPositions.updatePreUnloadPosition(self, dt) + elseif spec.state == CpShovelPositions.UNLOADING then + CpShovelPositions.updateUnloadingPosition(self, dt) + end +end + --- Changes the current shovel state position. function CpShovelPositions:cpSetShovelState(state) local spec = self.spec_cpShovelPositions @@ -182,6 +205,7 @@ function CpShovelPositions:cpResetShovelState() ImplementUtil.stopMovingTool(spec.shovelVehicle, spec.shovelTool) end +--- Is the current target shovel position not yet reached? function CpShovelPositions:areCpShovelPositionsDirty() local spec = self.spec_cpShovelPositions return spec.isDirty @@ -212,8 +236,6 @@ function CpShovelPositions:cpSetupShovelPositions() spec.shovelToolIx = i spec.shovelTool = tool spec.shovelVehicle = vehicle - spec.shovelProjectionNode = CpUtil.createNode("CpShovelProjectionNode", - 0, 0, 0, getParent(tool.node)) end end end @@ -221,39 +243,19 @@ function CpShovelPositions:cpSetupShovelPositions() end end -function CpShovelPositions:onUpdateTick(dt) - local spec = self.spec_cpShovelPositions - if spec.shovelToolIx == nil or spec.armToolIx == nil then - return - end - if spec.state == CpShovelPositions.LOADING then - CpShovelPositions.updateLoadingPosition(self, dt) - elseif spec.state == CpShovelPositions.TRANSPORT then - CpShovelPositions.updateTransportPosition(self, dt) - elseif spec.state == CpShovelPositions.PRE_UNLOAD then - CpShovelPositions.updatePreUnloadPosition(self, dt) - elseif spec.state == CpShovelPositions.UNLOADING then - CpShovelPositions.updateUnloadingPosition(self, dt) - end -end - ---- Changes the shovel angle dependent on the selected position. -function CpShovelPositions.setShovelPosition(dt, spec, shovel, shovelNode, angle, limits) - local min, max = unpack(limits) - local targetAngle = math.rad(min) + math.rad(max - min)/2 - local deltaAngle = targetAngle - angle - local curRot = {} - curRot[1], curRot[2], curRot[3] = getRotation(spec.shovelTool.node) - local oldRot = curRot[spec.shovelTool.rotationAxis] - local goalAngle = MathUtil.clamp(oldRot + deltaAngle, spec.shovelTool.rotMin, spec.shovelTool.rotMax) - return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, spec.shovelTool, dt, - goalAngle) -end - -function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits, useHighDumpShovel) +--- Sets the current shovel position values, like the arm and shovel rotations. +---@param dt number +---@param shovelLimits table +---@param armLimits table +---@param useHighDumpShovel boolean|nil +---@return boolean|nil +function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHighDumpShovel) local min, max = unpack(shovelLimits) + --- Target angle of the shovel node, which is at the end of the shovel. local targetAngle = math.rad(min) + math.rad(max - min)/2 min, max = unpack(armLimits) + --- Target height of the arm. + --- This is relative to the attacher joint of the shovel. local targetHeight = min + (max - min)/2 local shovelTool = self.spec_cpShovelPositions.shovelTool @@ -265,71 +267,45 @@ function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits, useHi curRot[1], curRot[2], curRot[3] = getRotation(shovelTool.node) local oldShovelRot = curRot[shovelTool.rotationAxis] - local curRot = {} + curRot = {} curRot[1], curRot[2], curRot[3] = getRotation(armTool.node) local oldArmRot = curRot[armTool.rotationAxis] + local armProjectionNode = self.spec_cpShovelPositions.armProjectionNode local armToolRefNode = self.spec_cpShovelPositions.armToolRefNode - local radius = calcDistanceFrom(shovelTool.node, armTool.node) + + local radiusArmToolToShovelTool = calcDistanceFrom(shovelTool.node, armTool.node) local attacherJointNode = self.spec_attachable.attacherJoint.node local angle, shovelNode = CpShovelPositions.getShovelData(self) local _, shovelY, _ = localToLocal(shovelNode, attacherJointNode, 0, 0, 0) + --- All values will be calculated in the coordinate system from the vehicle root node. + local _, ty, tz = localToLocal(getChildAt(armTool.node, 0), armVehicle.rootNode, 0, 0, 0) local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) local sx, sy, sz = 0, targetHeight - shovelY, 0 local ex, ey, ez = 0, targetHeight - shovelY, 20 - local yMax = ay + radius + local yMax = ay + radiusArmToolToShovelTool if sy > yMax then + --- Makes sure the target height is still reachable sy = yMax - 0.01 ey = yMax - 0.01 end - local hasIntersection, i1z, i1y, i2z, i2y = MathUtil.getCircleLineIntersection(az, ay, radius, + local hasIntersection, i1z, i1y, i2z, i2y = MathUtil.getCircleLineIntersection(az, ay, radiusArmToolToShovelTool, sz, sy, ez, ey) - local wsx, wsy, wsz = localToWorld(armVehicle.rootNode, sx, sy, sz) - local wex, wey, wez = localToWorld(armVehicle.rootNode, ex, ey, ez) - DebugUtil.drawDebugLine(wsx, wsy, wsz, wex, wey, wez) - - DebugUtil.drawDebugCircleAtNode(armVehicle.rootNode, radius, 30, nil, - true, {ax, ay, az}) - CpUtil.drawDebugNode(armVehicle.rootNode) - CpUtil.drawDebugNode(armTool.node) - CpUtil.drawDebugNode(shovelTool.node) - local isDirty + + local isDirty, alpha, oldRotRelativeArmRot if hasIntersection then setTranslation(armProjectionNode, 0, i1y, i1z) setTranslation(armToolRefNode, ax, ay, az) local _, shy, shz = localToLocal(shovelTool.node, armVehicle.rootNode, 0, 0, 0) local dirZ, dirY = MathUtil.vector2Normalize(shz - az, shy - ay) - local yRot = MathUtil.getYRotationFromDirection(-dirZ, dirY) + math.pi/2 + oldRotRelativeArmRot = MathUtil.getYRotationFromDirection(-dirZ, dirY) + math.pi/2 - CpUtil.drawDebugNode(armProjectionNode) - CpUtil.drawDebugNode(armToolRefNode) - local alpha = math.atan2(i1y - ay, i1z - az) + alpha = math.atan2(i1y - ay, i1z - az) local beta = -math.atan2(i2y - ay, i2z - az) - local debugData = { - { - name = "alpha", value = math.deg(alpha) - }, - { - name = "deltaAlphaOld", value = math.deg(MathUtil.getAngleDifference(alpha, oldArmRot)) - }, - { - name = "old", value = math.deg(oldArmRot) - }, - { - name = "deltaAlpha", value = math.deg(MathUtil.getAngleDifference(alpha, yRot)) - }, - { - name = "dirRot", value = math.deg(yRot) - }, - { - name = "distAlpha", value = MathUtil.vector2Length(i1z - tz, i1y - ty) - }, - } - DebugUtil.renderTable(0.4, 0.4, 0.018, debugData, 0) - local angle = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference(alpha, yRot), armTool.rotMin, armTool.rotMax) + local angle = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference(alpha, oldRotRelativeArmRot), armTool.rotMin, armTool.rotMax) isDirty = ImplementUtil.moveMovingToolToRotation(armVehicle, armTool, dt, angle) end @@ -339,7 +315,7 @@ function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits, useHi if useHighDumpShovel then local _, dy, _ = localDirectionToWorld(getParent(tool.node), 0, 0, 1) angle = math.acos(dy) - targetAngle = math.pi/2 + targetAngle = math.pi/2 - math.pi/8 isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMax) or isDirty else isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMin) or isDirty @@ -350,113 +326,96 @@ function CpShovelPositions:setShovelPosition2(dt, shovelLimits, armLimits, useHi isDirty = ImplementUtil.moveMovingToolToRotation(shovelVehicle, shovelTool, dt, goalAngle) or isDirty - return isDirty -end - ---- Changes the front loader angle dependent on the selected position, relative to a target height. -function CpShovelPositions.setArmPosition(dt, spec, shovel, shovelNode, limits) - --- Interval in which the shovel height should be in. - local min, max = unpack(limits) - local targetHeight = min + (max - min)/2 - - local attacherJointNode = shovel.spec_attachable.attacherJoint.node - local _, shovelY, shovelR = localToLocal(attacherJointNode, shovelNode, 0, 0, 0) - - local x, y, z = getWorldTranslation(attacherJointNode) - local dy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) - - local targetAttacherHeight = dy + shovelY + targetHeight - local diff = targetAttacherHeight - y - - CpShovelPositions.debug(shovel, "shovel => y: %.2f, z: %.2f, targetAttacherHeight: %.2f", shovelY, shovelR, targetAttacherHeight) - - if math.abs(diff) < (max - min)/2 then - ImplementUtil.stopMovingTool(spec.armVehicle, spec.armTool) - return false - end - - local curRot = {} - curRot[1], curRot[2], curRot[3] = getRotation(spec.armTool.node) - local oldRot = curRot[spec.armTool.rotationAxis] - - setWorldTranslation(spec.armProjectionNode, x, targetAttacherHeight, z) - - local _, ay, _ = localToLocal(spec.armTool.node, spec.armVehicle.rootNode, 0, 0, 0) - - local nodeDiff = MathUtil.clamp( targetHeight - ay , -shovelR, shovelR) + ay - - local ax, _, az = getWorldTranslation(spec.armTool.node) - local sx, sy, sz = getWorldTranslation(spec.shovelTool.node) - - if CpShovelPositions.DEBUG then - DebugUtil.drawDebugCircleAtNode(spec.armTool.node, shovelR, 30, nil, true) - - DebugUtil.drawDebugNode(spec.armProjectionNode, "Projection node", false, 0) - - DebugUtil.drawDebugLine(x, targetAttacherHeight, z, sx, sy, sz) - DebugUtil.drawDebugLine(x, targetAttacherHeight, z, ax, targetAttacherHeight, az) -- y - end - local yRot = math.atan2(MathUtil.vector3Length(x - sx, targetAttacherHeight - sy, z - sz), - MathUtil.vector3Length(x - ax, 0, z - az)) + --- Debug information + local wsx, wsy, wsz = localToWorld(armVehicle.rootNode, sx, sy, sz) + local wex, wey, wez = localToWorld(armVehicle.rootNode, ex, ey, ez) + if g_currentMission.controlledVehicle == self.rootVehicle and CpDebug:isChannelActive(CpDebug.DBG_SILO) then + DebugUtil.drawDebugLine(wsx, wsy, wsz, wex, wey, wez) + DebugUtil.drawDebugCircleAtNode(armVehicle.rootNode, radiusArmToolToShovelTool, 30, nil, + true, {ax, ay, az}) + CpUtil.drawDebugNode(armVehicle.rootNode) + CpUtil.drawDebugNode(armTool.node) + CpUtil.drawDebugNode(shovelTool.node) + CpUtil.drawDebugNode(armProjectionNode) + CpUtil.drawDebugNode(armToolRefNode) - if diff < 0 then - yRot = yRot - else - yRot = -yRot + local debugData = {} + if hasIntersection then + table.insert(debugData, { + value = "", + name = "Arm Rotation" }) + table.insert(debugData, { + name = "alpha", value = math.deg(alpha) }) + table.insert(debugData, { + name = "deltaAlphaOld", value = math.deg(MathUtil.getAngleDifference(alpha, oldArmRot)) }) + table.insert(debugData, { + name = "old", value = math.deg(oldArmRot) }) + table.insert(debugData, { + name = "deltaAlpha", value = math.deg(MathUtil.getAngleDifference(alpha, oldRotRelativeArmRot)) }) + table.insert(debugData, { + name = "dirRot", value = math.deg(oldRotRelativeArmRot) }) + table.insert(debugData, { + name = "distAlpha", value = MathUtil.vector2Length(i1z - tz, i1y - ty) }) + + table.insert(debugData, { + value = "", + name = "", + columnOffset = 0.12 + }) + end + table.insert(debugData, { + value = "", + name = "Shovel Rotation" }) + table.insert(debugData, { + name = "angle", value = math.deg(angle) }) + table.insert(debugData, { + name = "deltaAngle", value = math.deg(deltaAngle) }) + table.insert(debugData, { + name = "targetAngle", value = math.deg(targetAngle) }) + DebugUtil.renderTable(0.4, 0.4, 0.018, debugData, 0) end - - CpShovelPositions.debug(shovel, - "Arm position(%d) height diff: %.2f, targetHeight: %.2f, old angle: %.2f, yRot: %.2f", - spec.state, diff, targetHeight, math.deg(oldRot), math.deg(yRot)) - - return ImplementUtil.moveMovingToolToRotation(spec.armVehicle, spec.armTool, dt, - MathUtil.clamp(oldRot + yRot , spec.armTool.rotMin, spec.armTool.rotMax)) + return isDirty end function CpShovelPositions:updateLoadingPosition(dt) local spec = self.spec_cpShovelPositions - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local angle = CpShovelPositions.getShovelData(self) local isDirty if angle then - isDirty = CpShovelPositions.setShovelPosition2(self, dt, + isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) - -- isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) end spec.isDirty = isDirty end function CpShovelPositions:updateTransportPosition(dt) local spec = self.spec_cpShovelPositions - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local angle = CpShovelPositions.getShovelData(self) local isDirty if angle then - isDirty = CpShovelPositions.setShovelPosition2(self, dt, + isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) - -- isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS) - -- isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) end spec.isDirty = isDirty end function CpShovelPositions:updatePreUnloadPosition(dt) local spec = self.spec_cpShovelPositions - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local angle = CpShovelPositions.getShovelData(self) local isDirty if angle then - isDirty = CpShovelPositions.setShovelPosition2(self, dt, + isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS, CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS) - --isDirty = CpShovelPositions.setShovelPosition(dt, spec, self, shovelNode, angle, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS) - --isDirty = isDirty or CpShovelPositions.setArmPosition(dt, spec, self, shovelNode, self:getCpShovelUnloadingPositionHeight()) end spec.isDirty = isDirty end function CpShovelPositions:updateUnloadingPosition(dt) local spec = self.spec_cpShovelPositions - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) + local angle, _, maxAngle = CpShovelPositions.getShovelData(self) local isDirty if angle and maxAngle then - isDirty = CpShovelPositions.setShovelPosition2(self, dt, + isDirty = CpShovelPositions.setShovelPosition(self, dt, {math.deg(maxAngle), math.deg(maxAngle) + 2}, CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, true) end @@ -504,8 +463,6 @@ function CpShovelPositions:getShovelData() return angle, shovelSpec.shovelNodes[1].node, info.maxSpeedAngle, info.minSpeedAngle, factor end - - function CpShovelPositions.debug(implement, ...) if CpShovelPositions.DEBUG then CpUtil.infoImplement(implement, ...) From 10f3013607eda265fd78344bf2d9da28d68d4842 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:55:35 +0200 Subject: [PATCH 021/107] Fix silo loader bug --- scripts/ai/controllers/ShovelController.lua | 23 +++++-------------- .../specializations/CpAISiloLoaderWorker.lua | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index a1ec9020d..d1bb49bf4 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -87,29 +87,18 @@ function ShovelController:isHighDumpShovel() return g_vehicleConfigurations:get(self.implement, "shovelMovingToolIx") ~= nil end -function ShovelController:moveShovelToLoadingPosition() - return self:moveShovelToPosition(self.POSITIONS.LOADING) -end - -function ShovelController:moveShovelToTransportPosition() - return self:moveShovelToPosition(self.POSITIONS.TRANSPORT) -end - -function ShovelController:moveShovelToPreUnloadPosition() - return self:moveShovelToPosition(self.POSITIONS.PRE_UNLOADING) -end - -function ShovelController:moveShovelToUnloadPosition() - return self:moveShovelToPosition(self.POSITIONS.UNLOADING) -end - function ShovelController:onFinished() - self.implement:cpResetShovelState() + if self.implement.cpResetShovelState then + self.implement:cpResetShovelState() + end end ---@param pos number shovel position 1-4 ---@return boolean reached? function ShovelController:moveShovelToPosition(pos) + if self.implement.cpSetShovelState == nil then + return false + end self.implement:cpSetShovelState(pos) return self.implement:areCpShovelPositionsDirty() end diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index 252cec82f..fe2ff2eca 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -181,7 +181,7 @@ end function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger) if self.isServer then local strategy - if AIUtil.hasAIImplementWithSpecialization(self, ConveyorBelt) then + if SpecializationUtil.hasSpecialization(ConveyorBelt, self.specializations) then CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting a silo loader strategy.") strategy = AIDriveStrategySiloLoader.new() else From a23d87b60f75015ddcbf116d4c7168cdde40e8c2 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:42:34 +0200 Subject: [PATCH 022/107] Hopefully fixes hud bugs and WIP sugarbeet shovel --- scripts/ai/controllers/ShovelController.lua | 15 +++++++++++++++ scripts/gui/hud/CpBaseHud.lua | 8 ++++---- scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua | 4 +++- scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 5 ++++- scripts/specializations/CpAIBunkerSiloWorker.lua | 5 ++--- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index d1bb49bf4..1de8d6c27 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -13,6 +13,7 @@ function ShovelController:init(vehicle, implement) ImplementController.init(self, vehicle, implement) self.shovelSpec = self.implement.spec_shovel self.shovelNode = self.shovelSpec.shovelNodes[1] + self.turnOnSpec = self.implement.spec_turnOnVehicle end function ShovelController:update() @@ -96,6 +97,20 @@ end ---@param pos number shovel position 1-4 ---@return boolean reached? function ShovelController:moveShovelToPosition(pos) + if self.turnOnSpec then + if pos == ShovelController.POSITIONS.UNLOADING then + if not self.implement:getIsTurnedOn() and self.implement:getCanBeTurnedOn() then + self.implement:setIsTurnedOn(true) + self:debug("Turning on the shovel.") + end + return false + else + if self.implement:getIsTurnedOn() then + self.implement:setIsTurnedOn(false) + self:debug("turning off the shovel.") + end + end + end if self.implement.cpSetShovelState == nil then return false end diff --git a/scripts/gui/hud/CpBaseHud.lua b/scripts/gui/hud/CpBaseHud.lua index e4383ef88..310330a20 100644 --- a/scripts/gui/hud/CpBaseHud.lua +++ b/scripts/gui/hud/CpBaseHud.lua @@ -424,14 +424,14 @@ function CpBaseHud:getActiveHudPage(vehicle) return self.combineUnloaderLayout elseif vehicle:getCanStartCpBaleFinder() and not vehicle:hasCpCourse() then return self.baleFinderLayout - elseif vehicle:getCanStartCpBunkerSiloWorker() and vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO - or (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) - and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel)) then - return self.bunkerSiloWorkerLayout elseif vehicle:getCanStartCpSiloLoaderWorker() and (AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) or vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING) then return self.siloLoaderWorkerLayout + elseif vehicle:getCanStartCpBunkerSiloWorker() and vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO + or (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) + and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel)) then + return self.bunkerSiloWorkerLayout else return self.fieldworkLayout end diff --git a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua index dc5489420..60e60011a 100644 --- a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua +++ b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua @@ -103,9 +103,11 @@ end function CpBunkerSiloWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) return AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel) - end function CpBunkerSiloWorkerHudPageElement:getStartingPointBtnText(vehicle) + if self:isStartingPointBtnDisabled(vehicle) then + return vehicle:getCpStartText() + end return vehicle:getCpStartingPointSetting():getString() end \ No newline at end of file diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index e8e7aaec6..6382aae28 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -148,9 +148,12 @@ function CpSiloLoaderWorkerHudPageElement:arePositionEqual(parameters, otherPara end function CpSiloLoaderWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) - return AIUtil.hasAIImplementWithSpecialization(vehicle, ConveyorBelt) + return AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) end function CpSiloLoaderWorkerHudPageElement:getStartingPointBtnText(vehicle) + if self:isStartingPointBtnDisabled(vehicle) then + return vehicle:getCpStartText() + end return vehicle:getCpStartingPointSetting():getString() end \ No newline at end of file diff --git a/scripts/specializations/CpAIBunkerSiloWorker.lua b/scripts/specializations/CpAIBunkerSiloWorker.lua index 0a66d1596..cb3759fe0 100644 --- a/scripts/specializations/CpAIBunkerSiloWorker.lua +++ b/scripts/specializations/CpAIBunkerSiloWorker.lua @@ -98,9 +98,8 @@ function CpAIBunkerSiloWorker:getCanStartCpBunkerSiloWorker() return not self:getCanStartCpFieldWork() and not self:getCanStartCpBaleFinder() and not self:getCanStartCpCombineUnloader() - - and (not self:hasCpCourse() or - AIUtil.hasChildVehicleWithSpecialization(self, Leveler)) + and not self:getCanStartCpSiloLoaderWorker() + and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, Leveler)) and not AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end From 27bc77cef900f8ecdc637fc4ee9ce2c648383f20 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:13:27 +0200 Subject: [PATCH 023/107] Improvement and bug fixes --- scripts/gui/hud/CpBaseHud.lua | 10 ++++------ .../specializations/CpAIBunkerSiloWorker.lua | 8 +++----- .../specializations/CpAISiloLoaderWorker.lua | 19 +++++++++---------- scripts/specializations/CpShovelPositions.lua | 13 ++++++++++--- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/scripts/gui/hud/CpBaseHud.lua b/scripts/gui/hud/CpBaseHud.lua index 310330a20..ef3812608 100644 --- a/scripts/gui/hud/CpBaseHud.lua +++ b/scripts/gui/hud/CpBaseHud.lua @@ -424,13 +424,11 @@ function CpBaseHud:getActiveHudPage(vehicle) return self.combineUnloaderLayout elseif vehicle:getCanStartCpBaleFinder() and not vehicle:hasCpCourse() then return self.baleFinderLayout - elseif vehicle:getCanStartCpSiloLoaderWorker() and - (AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) or - vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING) then + elseif vehicle:getCanStartCpSiloLoaderWorker() and (vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING + or AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt)) then return self.siloLoaderWorkerLayout - elseif vehicle:getCanStartCpBunkerSiloWorker() and vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO - or (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) - and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel)) then + elseif vehicle:getCanStartCpBunkerSiloWorker() and (vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO + or AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler)) then return self.bunkerSiloWorkerLayout else return self.fieldworkLayout diff --git a/scripts/specializations/CpAIBunkerSiloWorker.lua b/scripts/specializations/CpAIBunkerSiloWorker.lua index cb3759fe0..fb07d3732 100644 --- a/scripts/specializations/CpAIBunkerSiloWorker.lua +++ b/scripts/specializations/CpAIBunkerSiloWorker.lua @@ -95,12 +95,13 @@ end --- Is the bunker silo allowed? function CpAIBunkerSiloWorker:getCanStartCpBunkerSiloWorker() + if AIUtil.hasChildVehicleWithSpecialization(self, Shovel) then + return false + end return not self:getCanStartCpFieldWork() and not self:getCanStartCpBaleFinder() and not self:getCanStartCpCombineUnloader() and not self:getCanStartCpSiloLoaderWorker() - and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, Leveler)) - and not AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end function CpAIBunkerSiloWorker:getCanStartCp(superFunc) @@ -110,9 +111,6 @@ end function CpAIBunkerSiloWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAIBunkerSiloWorker local job = self:getCanStartCpBunkerSiloWorker() and spec.cpJob - if isStartedByHud then - job = self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO and job - end return superFunc(self, isStartedByHud) or job end diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index fe2ff2eca..0425953b8 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -97,7 +97,6 @@ end function CpAISiloLoaderWorker:getCanStartCpSiloLoaderWorker() return not self:getCanStartCpFieldWork() and not self:getCanStartCpBaleFinder() - and (not self:hasCpCourse() or AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt)) and not self:getCanStartCpCombineUnloader() and AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end @@ -108,16 +107,16 @@ end function CpAISiloLoaderWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAISiloLoaderWorker - if AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) then - return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob - elseif isStartedByHud then - if self:getCanStartCpSiloLoaderWorker() - and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING then - return superFunc(self, isStartedByHud) or spec.cpJob - end + -- if AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) then + -- return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob + -- elseif isStartedByHud then + -- if self:getCanStartCpSiloLoaderWorker() + -- and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING then + -- return superFunc(self, isStartedByHud) or spec.cpJob + -- end - end - return superFunc(self, isStartedByHud) + -- end + return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob end function CpAISiloLoaderWorker:getCpStartText(superFunc) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 4898229c4..a24785177 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -278,14 +278,21 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig local attacherJointNode = self.spec_attachable.attacherJoint.node local angle, shovelNode = CpShovelPositions.getShovelData(self) - local _, shovelY, _ = localToLocal(shovelNode, attacherJointNode, 0, 0, 0) + local _, shovelY, _ = localToLocal(self.rootNode, attacherJointNode, 0, 0, 0) + --- local tempNode = createTransformGroup("tempVehicleSizeCenter") + -- link(vehicle.rootNode, tempNode) + -- setTranslation(tempNode, vehicle.size.widthOffset, vehicle.size.heightOffset + vehicle.size.height / 2, vehicle.size.lengthOffset) + -- DebugUtil.drawDebugCube(tempNode, vehicle.size.width, vehicle.size.height, vehicle.size.length, 0, 0, 1) + -- delete(tempNode) + + --- All values will be calculated in the coordinate system from the vehicle root node. local _, ty, tz = localToLocal(getChildAt(armTool.node, 0), armVehicle.rootNode, 0, 0, 0) local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) - local sx, sy, sz = 0, targetHeight - shovelY, 0 - local ex, ey, ez = 0, targetHeight - shovelY, 20 + local sx, sy, sz = 0, targetHeight - shovelY - self.size.heightOffset, 0 + local ex, ey, ez = 0, targetHeight - shovelY - self.size.heightOffset, 20 local yMax = ay + radiusArmToolToShovelTool if sy > yMax then --- Makes sure the target height is still reachable From a9fc27235eb7dfb86c6aea1950fa5211edd56b98 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:36:39 +0200 Subject: [PATCH 024/107] Minor adjustment --- scripts/ai/ImplementUtil.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 1d58762ec..9f36aa676 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -431,6 +431,7 @@ end ---@return number|nil target implement fill unit ix to load into. ---@return number|nil fill type to load ---@return number|nil target exact fill root node +---@return number|nil alternative fill type, when the implement gets turned on function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, dischargeNode, debugFunc) local function debug(str, ...) @@ -448,7 +449,15 @@ function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, di end local fillType = implementToLoadFrom:getDischargeFillType(dischargeNode) - + local alternativeFillType + if implementToLoadFrom.spec_turnOnVehicle then + --- The discharge node flips when the implement gets turned on. + --- The fill type might be different then. + local turnOnDischargeNode = implementToLoadFrom.spec_turnOnVehicle.activateableDischargeNode + if turnOnDischargeNode then + alternativeFillType = implementToLoadFrom:getDischargeFillType(turnOnDischargeNode) + end + end if fillType == nil or fillType == FillType.UNKNOWN then debug("No valid fill type to load!") return false, nil, nil, nil @@ -460,16 +469,19 @@ function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, di ---@return number|nil ---@return number|nil local function canLoad(fillUnitIndex) - if not loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, fillType) then + if not loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, fillType) and + not loadTargetImplement:getFillUnitSupportsFillType(fillUnitIndex, alternativeFillType) then debug("Fill unit(%d) doesn't support fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) return false end - if not loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, fillType) then + if not loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, fillType) and + not loadTargetImplement:getFillUnitAllowsFillType(fillUnitIndex, alternativeFillType) then debug("Fill unit(%d) doesn't allow fill type %s", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) return false end if loadTargetImplement.getFillUnitFreeCapacity and - loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, fillType, implementToLoadFrom:getActiveFarm()) <= 0 then + loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, fillType, implementToLoadFrom:getActiveFarm()) <= 0 and + loadTargetImplement:getFillUnitFreeCapacity(fillUnitIndex, alternativeFillType, implementToLoadFrom:getActiveFarm()) <= 0 then debug("Fill unit(%d) is full with fill type %s!", fillUnitIndex, g_fillTypeManager:getFillTypeNameByIndex(fillType)) return false end @@ -490,6 +502,6 @@ function ImplementUtil.getCanLoadTo(loadTargetImplement, implementToLoadFrom, di end end - return validTarget, targetFillUnitIndex, fillType, exactFillRootNode + return validTarget, targetFillUnitIndex, fillType, exactFillRootNode, alternativeFillType end From 9c2434cfa62530f9a00a881b9c78be048143f7bb Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:33:51 +0200 Subject: [PATCH 025/107] Some more bug fixes --- Courseplay.lua | 14 ++-- scripts/specializations/CourseplaySpec.lua | 5 +- scripts/specializations/CpAIBaleFinder.lua | 2 +- .../specializations/CpAIBunkerSiloWorker.lua | 5 +- scripts/specializations/CpAIFieldWorker.lua | 19 +++-- .../specializations/CpAISiloLoaderWorker.lua | 15 ++-- scripts/specializations/CpAIWorker.lua | 12 ---- .../CpCourseGeneratorSettings.lua | 15 ++-- scripts/specializations/CpCourseManager.lua | 16 +++-- scripts/specializations/CpGamePadHud.lua | 2 +- scripts/specializations/CpHud.lua | 2 +- scripts/specializations/CpInfoTexts.lua | 2 +- scripts/specializations/CpVehicleSettings.lua | 71 +++++++++---------- 13 files changed, 80 insertions(+), 100 deletions(-) diff --git a/Courseplay.lua b/Courseplay.lua index ff40bf12d..5622fef55 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -325,19 +325,13 @@ end function Courseplay.register(typeManager) --- TODO: make this function async. for typeName, typeEntry in pairs(typeManager.types) do + CpAIWorker.register(typeManager, typeName, typeEntry.specializations) if CourseplaySpec.prerequisitesPresent(typeEntry.specializations) then typeManager:addSpecialization(typeName, Courseplay.MOD_NAME .. ".courseplaySpec") end - if CpVehicleSettings.prerequisitesPresent(typeEntry.specializations) then - typeManager:addSpecialization(typeName, Courseplay.MOD_NAME .. ".cpVehicleSettings") - end - if CpCourseGeneratorSettings.prerequisitesPresent(typeEntry.specializations) then - typeManager:addSpecialization(typeName, Courseplay.MOD_NAME .. ".cpCourseGeneratorSettings") - end - if CpCourseManager.prerequisitesPresent(typeEntry.specializations) then - typeManager:addSpecialization(typeName, Courseplay.MOD_NAME .. ".cpCourseManager") - end - CpAIWorker.register(typeManager, typeName, typeEntry.specializations) + CpVehicleSettings.register(typeManager, typeName, typeEntry.specializations) + CpCourseGeneratorSettings.register(typeManager, typeName, typeEntry.specializations) + CpCourseManager.register(typeManager, typeName, typeEntry.specializations) CpAIFieldWorker.register(typeManager, typeName, typeEntry.specializations) CpAIBaleFinder.register(typeManager, typeName, typeEntry.specializations) CpAICombineUnloader.register(typeManager, typeName, typeEntry.specializations) diff --git a/scripts/specializations/CourseplaySpec.lua b/scripts/specializations/CourseplaySpec.lua index 8894d1472..cf3bae77a 100644 --- a/scripts/specializations/CourseplaySpec.lua +++ b/scripts/specializations/CourseplaySpec.lua @@ -12,7 +12,7 @@ function CourseplaySpec.initSpecialization() end function CourseplaySpec.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(AIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CourseplaySpec.registerEventListeners(vehicleType) @@ -34,13 +34,10 @@ function CourseplaySpec.registerOverwrittenFunctions(vehicleType) end - function CourseplaySpec:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) end - - ------------------------------------------------------------------------------------------------------------------------ --- Event listeners --------------------------------------------------------------------------------------------------------------------------- diff --git a/scripts/specializations/CpAIBaleFinder.lua b/scripts/specializations/CpAIBaleFinder.lua index d0ba354c3..51adf5f96 100644 --- a/scripts/specializations/CpAIBaleFinder.lua +++ b/scripts/specializations/CpAIBaleFinder.lua @@ -19,7 +19,7 @@ function CpAIBaleFinder.initSpecialization() end function CpAIBaleFinder.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(CpAIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpAIBaleFinder.register(typeManager,typeName,specializations) diff --git a/scripts/specializations/CpAIBunkerSiloWorker.lua b/scripts/specializations/CpAIBunkerSiloWorker.lua index fb07d3732..1132a81f2 100644 --- a/scripts/specializations/CpAIBunkerSiloWorker.lua +++ b/scripts/specializations/CpAIBunkerSiloWorker.lua @@ -18,7 +18,7 @@ function CpAIBunkerSiloWorker.initSpecialization() end function CpAIBunkerSiloWorker.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(CpAIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpAIBunkerSiloWorker.register(typeManager,typeName,specializations) @@ -111,6 +111,9 @@ end function CpAIBunkerSiloWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAIBunkerSiloWorker local job = self:getCanStartCpBunkerSiloWorker() and spec.cpJob + if isStartedByHud and not AIUtil.hasChildVehicleWithSpecialization(self, Leveler) then + job = self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO and job + end return superFunc(self, isStartedByHud) or job end diff --git a/scripts/specializations/CpAIFieldWorker.lua b/scripts/specializations/CpAIFieldWorker.lua index c16ef3294..1c6a9bb2c 100644 --- a/scripts/specializations/CpAIFieldWorker.lua +++ b/scripts/specializations/CpAIFieldWorker.lua @@ -39,8 +39,7 @@ function CpAIFieldWorker.registerEventListeners(vehicleType) SpecializationUtil.registerEventListener(vehicleType, "onCpFull", CpAIFieldWorker) SpecializationUtil.registerEventListener(vehicleType, "onCpFinished", CpAIFieldWorker) - SpecializationUtil.registerEventListener(vehicleType, "onPostDetachImplement", CpAIFieldWorker) - SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", CpAIFieldWorker) + SpecializationUtil.registerEventListener(vehicleType, "onStateChange", CpAIFieldWorker) SpecializationUtil.registerEventListener(vehicleType, 'onCpCourseChange', CpAIFieldWorker) SpecializationUtil.registerEventListener(vehicleType, 'onCpADStartedByPlayer', CpAIFieldWorker) @@ -107,22 +106,20 @@ function CpAIFieldWorker:saveToXMLFile(xmlFile, baseKey, usedModNames) spec.cpJobStartAtLastWp:getCpJobParameters():saveToXMLFile(xmlFile, baseKey.. ".cpJobStartAtLastWp") end -function CpAIFieldWorker:onCpCourseChange() - local spec = self.spec_cpAIFieldWorker - spec.cpJob:getCpJobParameters():validateSettings() -end - -function CpAIFieldWorker:onPostDetachImplement() +function CpAIFieldWorker:onStateChange(state, data) local spec = self.spec_cpAIFieldWorker - spec.cpJob:getCpJobParameters():validateSettings() + if state == Vehicle.STATE_CHANGE_ATTACH then + spec.cpJob:getCpJobParameters():validateSettings() + elseif state == Vehicle.STATE_CHANGE_DETACH then + spec.cpJob:getCpJobParameters():validateSettings() + end end -function CpAIFieldWorker:onPostAttachImplement() +function CpAIFieldWorker:onCpCourseChange() local spec = self.spec_cpAIFieldWorker spec.cpJob:getCpJobParameters():validateSettings() end - function CpAIFieldWorker:getCpStartingPointSetting() local spec = self.spec_cpAIFieldWorker return spec.cpJob:getCpJobParameters().startAt diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index 0425953b8..c470e7af9 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -107,16 +107,11 @@ end function CpAISiloLoaderWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAISiloLoaderWorker - -- if AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) then - -- return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob - -- elseif isStartedByHud then - -- if self:getCanStartCpSiloLoaderWorker() - -- and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING then - -- return superFunc(self, isStartedByHud) or spec.cpJob - -- end - - -- end - return superFunc(self, isStartedByHud) or self:getCanStartCpSiloLoaderWorker() and spec.cpJob + local job = self:getCanStartCpSiloLoaderWorker() and spec.cpJob + if isStartedByHud and not AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) then + job = self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING and job + end + return superFunc(self, isStartedByHud) or job end function CpAISiloLoaderWorker:getCpStartText(superFunc) diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index a600e569d..5b3ca1ca9 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -42,8 +42,6 @@ function CpAIWorker.registerEventListeners(vehicleType) SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", CpAIWorker) - SpecializationUtil.registerEventListener(vehicleType, "onPreDetachImplement", CpAIWorker) - SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onUpdate", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", CpAIWorker) @@ -93,16 +91,6 @@ function CpAIWorker:onLoadFinished() end -function CpAIWorker:onPreDetachImplement(implement) - local spec = self.spec_cpAIWorker -end - -function CpAIWorker:onPostAttachImplement(object) - local spec = self.spec_cpAIWorker - -end - - function CpAIWorker:onLeaveVehicle(wasEntered) if wasEntered then CpJobSyncOnLeaveEvent.sendEvent(self) diff --git a/scripts/specializations/CpCourseGeneratorSettings.lua b/scripts/specializations/CpCourseGeneratorSettings.lua index b443bfc22..7b23a4b9c 100644 --- a/scripts/specializations/CpCourseGeneratorSettings.lua +++ b/scripts/specializations/CpCourseGeneratorSettings.lua @@ -6,9 +6,12 @@ CpCourseGeneratorSettings = {} CpCourseGeneratorSettings.MOD_NAME = g_currentModName -CpCourseGeneratorSettings.KEY = "."..CpCourseGeneratorSettings.MOD_NAME..".cpCourseGeneratorSettings" CpCourseGeneratorSettings.SETTINGS_KEY = ".settings" CpCourseGeneratorSettings.VINE_SETTINGS_KEY = ".vineSettings" +CpCourseGeneratorSettings.NAME = ".cpCourseGeneratorSettings" +CpCourseGeneratorSettings.SPEC_NAME = CpCourseGeneratorSettings.MOD_NAME .. CpCourseGeneratorSettings.NAME +CpCourseGeneratorSettings.KEY = "." .. CpCourseGeneratorSettings.MOD_NAME .. CpCourseGeneratorSettings.NAME + function CpCourseGeneratorSettings.initSpecialization() local schema = Vehicle.xmlSchemaSavegame --- Old save format @@ -27,9 +30,14 @@ function CpCourseGeneratorSettings.initSpecialization() CpCourseGeneratorSettings.registerConsoleCommands() end +function CpCourseGeneratorSettings.register(typeManager,typeName,specializations) + if CpCourseGeneratorSettings.prerequisitesPresent(specializations) then + typeManager:addSpecialization(typeName, CpCourseGeneratorSettings.SPEC_NAME) + end +end function CpCourseGeneratorSettings.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(AIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpCourseGeneratorSettings.registerEvents(vehicleType) @@ -79,8 +87,7 @@ end function CpCourseGeneratorSettings:onLoad(savegame) --- Register the spec: spec_cpCourseGeneratorSettings - local specName = CpCourseGeneratorSettings.MOD_NAME .. ".cpCourseGeneratorSettings" - self.spec_cpCourseGeneratorSettings = self["spec_" .. specName] + self.spec_cpCourseGeneratorSettings = self["spec_" .. CpCourseGeneratorSettings.SPEC_NAME] local spec = self.spec_cpCourseGeneratorSettings spec.gui = g_currentMission.inGameMenu.pageAI --- Clones the generic settings to create different settings containers for each vehicle. diff --git a/scripts/specializations/CpCourseManager.lua b/scripts/specializations/CpCourseManager.lua index 25ede6744..1a1ef23bf 100644 --- a/scripts/specializations/CpCourseManager.lua +++ b/scripts/specializations/CpCourseManager.lua @@ -5,8 +5,9 @@ CpCourseManager = {} CpCourseManager.MOD_NAME = g_currentModName - -CpCourseManager.KEY = "."..CpCourseManager.MOD_NAME..".cpCourseManager" +CpCourseManager.NAME = ".cpCourseManager" +CpCourseManager.SPEC_NAME = CpCourseManager.MOD_NAME .. CpCourseManager.NAME +CpCourseManager.KEY = "." .. CpCourseManager.MOD_NAME .. CpCourseManager.NAME CpCourseManager.xmlKey = "Course" CpCourseManager.rootKey = "AssignedCourses" CpCourseManager.rootKeyFileManager = "Courses" @@ -43,8 +44,14 @@ function CpCourseManager.initSpecialization() schema:register(XMLValueType.INT, key .. "#assignedCoursesID", "Assigned Courses id.") end +function CpCourseManager.register(typeManager,typeName,specializations) + if CpCourseManager.prerequisitesPresent(specializations) then + typeManager:addSpecialization(typeName, CpCourseManager.SPEC_NAME) + end +end + function CpCourseManager.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(AIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpCourseManager.registerEventListeners(vehicleType) @@ -113,8 +120,7 @@ end function CpCourseManager:onLoad(savegame) --- Register the spec: spec_cpCourseManager - local specName = CpCourseManager.MOD_NAME .. ".cpCourseManager" - self.spec_cpCourseManager = self["spec_" .. specName] + self.spec_cpCourseManager = self["spec_" .. CpCourseManager.SPEC_NAME] local spec = self.spec_cpCourseManager spec.coursePlot = CoursePlot(g_currentMission.inGameMenu.ingameMap) diff --git a/scripts/specializations/CpGamePadHud.lua b/scripts/specializations/CpGamePadHud.lua index 9837bdb2e..d608cf851 100644 --- a/scripts/specializations/CpGamePadHud.lua +++ b/scripts/specializations/CpGamePadHud.lua @@ -44,7 +44,7 @@ function CpGamePadHud.initSpecialization() end function CpGamePadHud.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(AIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpGamePadHud.register(typeManager, typeName, specializations) diff --git a/scripts/specializations/CpHud.lua b/scripts/specializations/CpHud.lua index fbef606ef..23f7b63df 100644 --- a/scripts/specializations/CpHud.lua +++ b/scripts/specializations/CpHud.lua @@ -16,7 +16,7 @@ function CpHud.initSpecialization() end function CpHud.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(CpAIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpHud.register(typeManager,typeName,specializations) diff --git a/scripts/specializations/CpInfoTexts.lua b/scripts/specializations/CpInfoTexts.lua index 9aaf790e9..8b78daecc 100644 --- a/scripts/specializations/CpInfoTexts.lua +++ b/scripts/specializations/CpInfoTexts.lua @@ -19,7 +19,7 @@ end function CpInfoTexts.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(CpAIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpInfoTexts.registerEventListeners(vehicleType) diff --git a/scripts/specializations/CpVehicleSettings.lua b/scripts/specializations/CpVehicleSettings.lua index 8ee13db07..6a6b1616d 100644 --- a/scripts/specializations/CpVehicleSettings.lua +++ b/scripts/specializations/CpVehicleSettings.lua @@ -6,9 +6,12 @@ CpVehicleSettings = {} CpVehicleSettings.MOD_NAME = g_currentModName -CpVehicleSettings.KEY = "."..CpVehicleSettings.MOD_NAME..".cpVehicleSettings" CpVehicleSettings.SETTINGS_KEY = ".settings" CpVehicleSettings.USER_KEY = ".users" +CpVehicleSettings.NAME = ".cpVehicleSettings" +CpVehicleSettings.SPEC_NAME = CpVehicleSettings.MOD_NAME .. CpVehicleSettings.NAME +CpVehicleSettings.KEY = "." .. CpVehicleSettings.MOD_NAME .. CpVehicleSettings.NAME + function CpVehicleSettings.initSpecialization() local schema = Vehicle.xmlSchemaSavegame --- Old xml schema for settings @@ -30,9 +33,14 @@ function CpVehicleSettings.initSpecialization() CpVehicleSettings.registerConsoleCommands() end +function CpVehicleSettings.register(typeManager,typeName,specializations) + if CpVehicleSettings.prerequisitesPresent(specializations) then + typeManager:addSpecialization(typeName, CpVehicleSettings.SPEC_NAME) + end +end function CpVehicleSettings.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(AIFieldWorker, specializations) + return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) end function CpVehicleSettings.registerEvents(vehicleType) @@ -44,8 +52,6 @@ function CpVehicleSettings.registerEventListeners(vehicleType) -- SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", CpVehicleSettings) - SpecializationUtil.registerEventListener(vehicleType, "onPreDetachImplement", CpVehicleSettings) - SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onCpUnitChanged", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onReadStream", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", CpVehicleSettings) @@ -74,8 +80,7 @@ end function CpVehicleSettings:onLoad(savegame) --- Register the spec: spec_CpVehicleSettings - local specName = CpVehicleSettings.MOD_NAME .. ".cpVehicleSettings" - self.spec_cpVehicleSettings = self["spec_" .. specName] + self.spec_cpVehicleSettings = self["spec_" .. CpVehicleSettings.SPEC_NAME] local spec = self.spec_cpVehicleSettings --- Clones the generic settings to create different settings containers for each vehicle. @@ -90,43 +95,11 @@ function CpVehicleSettings:onLoadFinished() spec.wasLoaded = nil end ---- TODO: These are only applied on a implement an not on a single vehicle. ---- This means self driving vehicle are not getting these vehicle configuration values. -function CpVehicleSettings:onPostAttachImplement(object) - --- Only apply these values, if were are not loading from a savegame. - local spec = self.spec_cpVehicleSettings - if spec.wasLoaded then - return - end - - CpVehicleSettings.setAutomaticWorkWidthAndOffset(self) - CpVehicleSettings.setAutomaticBunkerSiloWorkWidth(self) - CpVehicleSettings.setAutomaticBaleCollectorOffset(self) - - CpVehicleSettings.setFromVehicleConfiguration(self, object, spec.raiseImplementLate, 'raiseLate') - CpVehicleSettings.setFromVehicleConfiguration(self, object, spec.lowerImplementEarly, 'lowerEarly') - CpVehicleSettings.setFromVehicleConfiguration(self, object, spec.bunkerSiloWorkWidth, 'workingWidth') - CpVehicleSettings.validateSettings(self) -end - -function CpVehicleSettings:onPreDetachImplement(implement) - --- Only apply these values, if were are not loading from a savegame. - local spec = self.spec_cpVehicleSettings - if spec.wasLoaded then - return - end - - CpVehicleSettings.setAutomaticWorkWidthAndOffset(self, implement.object) - CpVehicleSettings.setAutomaticBunkerSiloWorkWidth(self, implement.object) - - CpVehicleSettings.resetToDefault(self, implement.object, spec.raiseImplementLate, 'raiseLate', false) - CpVehicleSettings.resetToDefault(self, implement.object, spec.lowerImplementEarly, 'lowerEarly', false) - CpVehicleSettings.validateSettings(self) -end --- Changes the sprayer work width on fill type change, as it might depend on the loaded fill type. --- For example Lime and Fertilizer might have a different work width. function CpVehicleSettings:onStateChange(state, data) + local spec = self.spec_cpVehicleSettings if state == Vehicle.STATE_CHANGE_FILLTYPE_CHANGE and self:getIsSynchronized() then local _, hasSprayer = AIUtil.getAllChildVehiclesWithSpecialization(self, Sprayer, nil) if hasSprayer then @@ -137,6 +110,26 @@ function CpVehicleSettings:onStateChange(state, data) self:getCourseGeneratorSettings().workWidth:setFloatValue(width) end end + elseif state == Vehicle.STATE_CHANGE_ATTACH then + CpVehicleSettings.setAutomaticWorkWidthAndOffset(self) + CpVehicleSettings.setAutomaticBunkerSiloWorkWidth(self) + CpVehicleSettings.setAutomaticBaleCollectorOffset(self) + CpVehicleSettings.setFromVehicleConfiguration(self, data.attachedVehicle, + spec.raiseImplementLate, 'raiseLate') + CpVehicleSettings.setFromVehicleConfiguration(self, data.attachedVehicle, + spec.lowerImplementEarly, 'lowerEarly') + CpVehicleSettings.setFromVehicleConfiguration(self, data.attachedVehicle, + spec.bunkerSiloWorkWidth, 'workingWidth') + CpVehicleSettings.validateSettings(self) + elseif state == Vehicle.STATE_CHANGE_DETACH then + CpVehicleSettings.setAutomaticWorkWidthAndOffset(self, data.attachedVehicle) + CpVehicleSettings.setAutomaticBunkerSiloWorkWidth(self, data.attachedVehicle) + + CpVehicleSettings.resetToDefault(self, data.attachedVehicle, spec.raiseImplementLate, + 'raiseLate', false) + CpVehicleSettings.resetToDefault(self, data.attachedVehicle, spec.lowerImplementEarly, + 'lowerEarly', false) + CpVehicleSettings.validateSettings(self) end end From 225946ad09acf5891dd092d14e3e68bc5a04c9eb Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:43:51 +0200 Subject: [PATCH 026/107] Some Adjustments --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 19 ++++++++++++------- scripts/ai/controllers/ShovelController.lua | 3 +++ scripts/pathfinder/PathfinderUtil.lua | 12 ++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 6308574f2..de270f980 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -128,7 +128,11 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) end local cx, cz = self.silo:getFrontCenter() - self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, 0) + local dirX, dirZ = self.silo:getLengthDirection() + local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) + self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, yRot) + self.siloAreaToAvoid = PathfinderUtil.NodeArea(self.siloFrontNode, -self.silo:getWidth()/2, + 0, self.silo:getWidth(), self.silo:getLength()) self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) end @@ -313,6 +317,7 @@ function AIDriveStrategyShovelSiloLoader:update(dt) end if self.silo then self.silo:drawDebug() + self.siloAreaToAvoid:drawDebug() end self.siloController:draw() if self.heapSilo then @@ -438,8 +443,6 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() self:startPathfindingToTrailer() end - - ---------------------------------------------------------------- --- Pathfinding ---------------------------------------------------------------- @@ -454,7 +457,8 @@ function AIDriveStrategyShovelSiloLoader:startPathfindingToStart(course) local fm = self:getFrontAndBackMarkers() self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToWaypoint( self.vehicle, course, 1, 0, -(fm + 4), - true, nil) + true, nil, nil, + nil, 0, self.siloAreaToAvoid) if done then return self:onPathfindingDoneToStart(path) else @@ -489,7 +493,7 @@ function AIDriveStrategyShovelSiloLoader:startPathfindingToUnloadPosition() self.vehicle, self.unloadPositionNode, 0, 0, true, nil, {}, nil, - 0, nil, false + 0, self.siloAreaToAvoid, false ) if done then return self:onPathfindingDoneToUnloadPosition(path, goalNodeInvalid) @@ -523,7 +527,7 @@ function AIDriveStrategyShovelSiloLoader:startPathfindingToTrailer() self.vehicle, self.unloadPositionNode, 0, 0, true, nil, {}, nil, - 0, nil, false + 0, self.siloAreaToAvoid, false ) if done then return self:onPathfindingDoneToTrailer(path, goalNodeInvalid) @@ -635,7 +639,8 @@ end --- Starts reverse straight to make some space to the trailer or unload trigger. function AIDriveStrategyShovelSiloLoader:startReversingAwayFromUnloading() local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) - local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, 0 ) + local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, + 0, self.vehicle.rootNode ) self:startCourse(course, 1) self:setNewState(self.states.REVERSING_AWAY_FROM_UNLOAD) end diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 1de8d6c27..47b2e73e1 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -103,6 +103,9 @@ function ShovelController:moveShovelToPosition(pos) self.implement:setIsTurnedOn(true) self:debug("Turning on the shovel.") end + if not self.implement:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OBJECT then + self.implement:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT) + end return false else if self.implement:getIsTurnedOn() then diff --git a/scripts/pathfinder/PathfinderUtil.lua b/scripts/pathfinder/PathfinderUtil.lua index 4565bfa01..a2423dfae 100644 --- a/scripts/pathfinder/PathfinderUtil.lua +++ b/scripts/pathfinder/PathfinderUtil.lua @@ -914,13 +914,13 @@ end ---@param xOffset number side offset of the goal from the goalWaypoint ---@param zOffset number length offset of the goal from the goalWaypoint ---@param allowReverse boolean allow reverse driving ----@param fieldNum number if > 0, the pathfinding is restricted to the given field and its vicinity. Otherwise the +---@param fieldNum number|nil if > 0, the pathfinding is restricted to the given field and its vicinity. Otherwise the --- pathfinding considers any collision-free path valid, also outside of the field. ----@param vehiclesToIgnore table[] list of vehicles to ignore for the collision detection (optional) ----@param maxFruitPercent number maximum percentage of fruit present before a node is marked as invalid (optional) ----@param offFieldPenalty number penalty to apply to nodes off the field ----@param areaToAvoid PathfinderUtil.NodeArea nodes in this area will be penalized so the path will most likely avoid it ----@param areaToIgnoreFruit PathfinderUtil.Area area to ignore fruit +---@param vehiclesToIgnore table[]|nil list of vehicles to ignore for the collision detection (optional) +---@param maxFruitPercent number|nil maximum percentage of fruit present before a node is marked as invalid (optional) +---@param offFieldPenalty number|nil penalty to apply to nodes off the field +---@param areaToAvoid PathfinderUtil.NodeArea|nil nodes in this area will be penalized so the path will most likely avoid it +---@param areaToIgnoreFruit PathfinderUtil.Area|nil area to ignore fruit ---@return PathfinderInterface pathfinder ---@return boolean done finished pathfinding? ---@return table|nil path that was found? From 7a6275043193aab0c06726eb7e46677ac0005fac Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:15:48 +0200 Subject: [PATCH 027/107] Gamepad hud bug fixes --- config/gamePadHud/SiloLoaderGamePadHudPage.xml | 1 + config/jobParameters/JobParameterSetup.xml | 6 +++--- scripts/ai/jobs/CpJobParameters.lua | 12 ++++++++++++ scripts/gui/CpGamePadHudScreen.lua | 3 +++ scripts/gui/hud/CpBaseHud.lua | 5 +++-- scripts/specializations/CpGamePadHud.lua | 11 ++++++----- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/config/gamePadHud/SiloLoaderGamePadHudPage.xml b/config/gamePadHud/SiloLoaderGamePadHudPage.xml index c406daddd..0969a7251 100644 --- a/config/gamePadHud/SiloLoaderGamePadHudPage.xml +++ b/config/gamePadHud/SiloLoaderGamePadHudPage.xml @@ -5,5 +5,6 @@ --> + diff --git a/config/jobParameters/JobParameterSetup.xml b/config/jobParameters/JobParameterSetup.xml index 4d7c917dc..ef081c84c 100644 --- a/config/jobParameters/JobParameterSetup.xml +++ b/config/jobParameters/JobParameterSetup.xml @@ -15,9 +15,9 @@ - 1 - 2 - 3 + 1 + 2 + 3 4 5 diff --git a/scripts/ai/jobs/CpJobParameters.lua b/scripts/ai/jobs/CpJobParameters.lua index 7f41b3964..39f871a82 100644 --- a/scripts/ai/jobs/CpJobParameters.lua +++ b/scripts/ai/jobs/CpJobParameters.lua @@ -137,6 +137,18 @@ function CpJobParameters:isSiloLoadingHudModeDisabled() return self:isAIMenuJob() end +function CpJobParameters:isFieldWorkHudModeDisabled() + local vehicle = self.job:getVehicle() + if vehicle then + if (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) and + not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel)) or + AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then + return true + end + end + return false +end + --- Callback raised by a setting and executed as an vehicle event. ---@param callbackStr string event to be raised ---@param setting AIParameterSettingList setting that raised the callback. diff --git a/scripts/gui/CpGamePadHudScreen.lua b/scripts/gui/CpGamePadHudScreen.lua index f5950b3e9..836c11611 100644 --- a/scripts/gui/CpGamePadHudScreen.lua +++ b/scripts/gui/CpGamePadHudScreen.lua @@ -192,6 +192,9 @@ function CpGamePadHudFieldWorkScreen:update(dt, ...) if self.vehicle:getCanStartCpBunkerSiloWorker() and self.vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO and not AIUtil.hasChildVehicleWithSpecialization(self.vehicle, Leveler) then self.vehicle:reopenCpGamePadHud() + elseif self.vehicle:getCanStartCpSiloLoaderWorker() and self.vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING + and not AIUtil.hasChildVehicleWithSpecialization(self.vehicle, ConveyorBelt) then + self.vehicle:reopenCpGamePadHud() end end diff --git a/scripts/gui/hud/CpBaseHud.lua b/scripts/gui/hud/CpBaseHud.lua index ef3812608..5c1e2284b 100644 --- a/scripts/gui/hud/CpBaseHud.lua +++ b/scripts/gui/hud/CpBaseHud.lua @@ -427,8 +427,9 @@ function CpBaseHud:getActiveHudPage(vehicle) elseif vehicle:getCanStartCpSiloLoaderWorker() and (vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING or AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt)) then return self.siloLoaderWorkerLayout - elseif vehicle:getCanStartCpBunkerSiloWorker() and (vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO - or AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler)) then + elseif vehicle:getCanStartCpBunkerSiloWorker() and (vehicle:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO or + (AIUtil.hasChildVehicleWithSpecialization(vehicle, Leveler) + and not AIUtil.hasChildVehicleWithSpecialization(vehicle, Shovel))) then return self.bunkerSiloWorkerLayout else return self.fieldworkLayout diff --git a/scripts/specializations/CpGamePadHud.lua b/scripts/specializations/CpGamePadHud.lua index d608cf851..c9fdc8994 100644 --- a/scripts/specializations/CpGamePadHud.lua +++ b/scripts/specializations/CpGamePadHud.lua @@ -194,12 +194,13 @@ function CpGamePadHud:actionEventOpenCloseDisplay() page = CpGamePadHud.UNLOADER_PAGE elseif self:getCanStartCpBaleFinder() then page = CpGamePadHud.BALE_LOADER_PAGE - elseif self:getCanStartCpBunkerSiloWorker() and self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO - or AIUtil.hasChildVehicleWithSpecialization(self, Leveler) then - page = CpGamePadHud.BUNKER_SILO_PAGE - elseif self:getCanStartCpSiloLoaderWorker() and ( AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) - or self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING) then + elseif self:getCanStartCpSiloLoaderWorker() and (self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_SILO_LOADING or + AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt)) then page = CpGamePadHud.SILO_LOADER_PAGE + elseif self:getCanStartCpBunkerSiloWorker() and (self:getCpStartingPointSetting():getValue() == CpJobParameters.START_AT_BUNKER_SILO + or (AIUtil.hasChildVehicleWithSpecialization(self, Leveler) + and not AIUtil.hasChildVehicleWithSpecialization(self, Shovel))) then + page = CpGamePadHud.BUNKER_SILO_PAGE else page = CpGamePadHud.FIELDWORK_PAGE end From 9c919201211cd72710e34f6df35d4cb16fc9071b Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 19:30:22 +0200 Subject: [PATCH 028/107] PR fixes --- modDesc.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/modDesc.xml b/modDesc.xml index b0d9fc3e8..57a5b1f9a 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -343,16 +343,6 @@ Changelog 7.1.0.0: - - - - - - - - - - From 7b52833b33b85490a7afc8d9f0359f6fe91d2a19 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 19:32:18 +0200 Subject: [PATCH 029/107] Rebase Fixes 2 --- modDesc.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modDesc.xml b/modDesc.xml index 57a5b1f9a..ecfa9d4d5 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -286,6 +286,13 @@ Changelog 7.1.0.0: + + + + + + + From f586bddb23bfc8c9f61eba08b0fc6d1a5a815278 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:07:49 +0200 Subject: [PATCH 030/107] Some more code improvements TODO: Is the collision Strategy even needed ? --- Courseplay.lua | 3 - modDesc.xml | 1 - scripts/ai/AIDriveStrategyCourse.lua | 4 +- scripts/specializations/CpAIWorker.lua | 204 ++++++++++++++++--------- 4 files changed, 130 insertions(+), 82 deletions(-) diff --git a/Courseplay.lua b/Courseplay.lua index 5622fef55..8f538ee57 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -326,9 +326,6 @@ function Courseplay.register(typeManager) --- TODO: make this function async. for typeName, typeEntry in pairs(typeManager.types) do CpAIWorker.register(typeManager, typeName, typeEntry.specializations) - if CourseplaySpec.prerequisitesPresent(typeEntry.specializations) then - typeManager:addSpecialization(typeName, Courseplay.MOD_NAME .. ".courseplaySpec") - end CpVehicleSettings.register(typeManager, typeName, typeEntry.specializations) CpCourseGeneratorSettings.register(typeManager, typeName, typeEntry.specializations) CpCourseManager.register(typeManager, typeName, typeEntry.specializations) diff --git a/modDesc.xml b/modDesc.xml index ecfa9d4d5..3122d9935 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -418,7 +418,6 @@ Changelog 7.1.0.0: - diff --git a/scripts/ai/AIDriveStrategyCourse.lua b/scripts/ai/AIDriveStrategyCourse.lua index 283a110fd..4396dcfe0 100644 --- a/scripts/ai/AIDriveStrategyCourse.lua +++ b/scripts/ai/AIDriveStrategyCourse.lua @@ -602,13 +602,13 @@ end --------------------------------------------------------------------------------------------------------------------------- function AIDriveStrategyCourse:disableCollisionDetection() if self.vehicle then - CourseplaySpec.disableCollisionDetection(self.vehicle) + CpAIWorker.disableCollisionDetection(self.vehicle) end end function AIDriveStrategyCourse:enableCollisionDetection() if self.vehicle then - CourseplaySpec.enableCollisionDetection(self.vehicle) + CpAIWorker.enableCollisionDetection(self.vehicle) end end diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 5b3ca1ca9..aa7ca137f 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -28,6 +28,9 @@ function CpAIWorker.register(typeManager, typeName, specializations) end function CpAIWorker.registerEvents(vehicleType) + SpecializationUtil.registerEvent(vehicleType, "onCpUnitChanged") + SpecializationUtil.registerEvent(vehicleType, "onCpDrawHudMap") + SpecializationUtil.registerEvent(vehicleType, "onCpFinished") SpecializationUtil.registerEvent(vehicleType, "onCpEmpty") SpecializationUtil.registerEvent(vehicleType, "onCpFull") @@ -68,6 +71,7 @@ function CpAIWorker.registerFunctions(vehicleType) SpecializationUtil.registerFunction(vehicleType, "cpHold", CpAIWorker.cpHold) SpecializationUtil.registerFunction(vehicleType, "cpBrakeToStop", CpAIWorker.cpBrakeToStop) SpecializationUtil.registerFunction(vehicleType, "getCpDriveStrategy", CpAIWorker.getCpDriveStrategy) + SpecializationUtil.registerFunction(vehicleType, 'getCpReverseDrivingDirectionNode', CpAIWorker.getCpReverseDrivingDirectionNode) end function CpAIWorker.registerOverwrittenFunctions(vehicleType) @@ -75,9 +79,11 @@ function CpAIWorker.registerOverwrittenFunctions(vehicleType) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCanMotorRun', CpAIWorker.getCanMotorRun) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'stopFieldWorker', CpAIWorker.stopFieldWorker) end ------------------------------------------------------------------------------------------------------------------------- + +--------------------------------------------------- --- Event listeners ---------------------------------------------------------------------------------------------------------------------------- +--------------------------------------------------- + function CpAIWorker:onLoad(savegame) --- Register the spec: spec_CpAIWorker self.spec_cpAIWorker = self["spec_" .. CpAIWorker.SPEC_NAME] @@ -85,6 +91,13 @@ function CpAIWorker:onLoad(savegame) --- Flag to make sure the motor isn't being turned on again by giants code, when we want it turned off. spec.motorDisabled = false spec.driveStrategy = nil + g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.USE_MILES], CpAIWorker.onUnitChanged, self) + g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.USE_ACRE], CpAIWorker.onUnitChanged, self) + g_messageCenter:subscribe(MessageType.CP_DISTANCE_UNIT_CHANGED, CpAIWorker.onUnitChanged, self) +end + +function CpAIWorker:onUnitChanged() + SpecializationUtil.raiseEvent(self,"onCpUnitChanged") end function CpAIWorker:onLoadFinished() @@ -136,6 +149,14 @@ function CpAIWorker:onRegisterActionEvents(isActiveForInput, isActiveForInputIgn end end +function CpAIWorker:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) + CpAIWorker.updateActionEvents(self) +end + +----------------------------------------------- +--- Action input events +----------------------------------------------- + --- Updates the action event visibility and text. function CpAIWorker:updateActionEvents() local spec = self.spec_cpAIWorker @@ -173,72 +194,6 @@ function CpAIWorker:updateActionEvents() end end -function CpAIWorker:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) - CpAIWorker.updateActionEvents(self) -end - - ---- Used to enable/disable release of the helper ---- and handles post release functionality with for example auto drive. ---- TODO: This function is a mess and desperately needs a better solution! -function CpAIWorker:stopCurrentAIJob(superFunc, message, ...) - if message then - CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "stop message: %s", message:getMessage()) - else - CpUtil.infoVehicle(self, "no stop message was given.") - return superFunc(self, message, ...) - end - local releaseMessage, hasFinished, event, isOnlyShownOnPlayerStart = g_infoTextManager:getInfoTextDataByAIMessage(message) - - CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "finished: %s, event: %s", - tostring(hasFinished), tostring(event)) - - local wasCpActive = self:getIsCpActive() - if wasCpActive then - local driveStrategy = self:getCpDriveStrategy() - if driveStrategy then - -- TODO: this isn't needed if we do not return a 0 < maxSpeed < 0.5, should either be exactly 0 or greater than 0.5 - local maxSpeed = driveStrategy and driveStrategy:getMaxSpeed() - if message:isa(AIMessageErrorBlockedByObject) then - if self.spec_aiFieldWorker.didNotMoveTimer and self.spec_aiFieldWorker.didNotMoveTimer < 0 then - if maxSpeed and maxSpeed < 1 then - -- disable the Giants timeout which dismisses the AI worker if it does not move for 5 seconds - -- since we often stop for instance in convoy mode when waiting for another vehicle to turn - -- (when we do this, we set our maxSpeed to 0). So we also check our maxSpeed, this way the Giants timer will - -- fire if we are blocked (thus have a maxSpeed > 0 but not moving) - CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, 'Overriding the Giants did not move timer, with speed: %.2f', maxSpeed) - return - else - CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, 'Giants did not move timer triggered, with speed: %.2f!', maxSpeed) - end - end - end - driveStrategy:onFinished() - end - end - self:resetCpAllActiveInfoTexts() - --- Only add the info text, if it's available and nobody is in the vehicle. - if not self:getIsControlled() and releaseMessage and not isOnlyShownOnPlayerStart then - self:setCpInfoTextActive(releaseMessage) - end - superFunc(self, message,...) - if wasCpActive then - if event then - SpecializationUtil.raiseEvent(self, event) - end - if hasFinished and self:getCpSettings().foldImplementAtEnd:getValue() then - --- Folds implements at the end if the setting is active. - self:prepareForAIDriving() - end - - end -end - - ------------------------------------------------ ---- Action input events ------------------------------------------------ - function CpAIWorker:changeStartingPoint() local startingPointSetting = self:getCpStartingPointSetting() startingPointSetting:setNextItem() @@ -289,6 +244,10 @@ function CpAIWorker:cpStartStopDriver(isStartedByHud) end end +----------------------------------------------- +--- Status getter functions +----------------------------------------------- + --- Is a cp worker active ? --- Every cp job should be an instance of type CpAIJob. function CpAIWorker:getIsCpActive() @@ -297,23 +256,24 @@ end --- Is cp drive to field work active function CpAIWorker:getIsCpDriveToFieldWorkActive() - return self:getIsCpActive() and self.driveToFieldWorkStartStrategy ~= nil + local spec = self.spec_cpAIWorker + return self:getIsCpActive() and spec.driveToTask ~=nil end --- Is a cp job ready to be started? function CpAIWorker:getCanStartCp() - return false + --- override end --- Gets the job to be started by the hud or the keybinding. function CpAIWorker:getCpStartableJob() - + --- override end --- Gets the additional action event start text, --- for example the starting point. function CpAIWorker:getCpStartText() - return "" + --- override end --- Makes sure giants isn't turning the motor back on, when we have turned it off. @@ -324,6 +284,64 @@ function CpAIWorker:getCanMotorRun(superFunc, ...) return superFunc(self, ...) end +----------------------------------------------- +--- Strategy handling +----------------------------------------------- + + +--- Used to enable/disable release of the helper +--- and handles post release functionality with for example auto drive. +function CpAIWorker:stopCurrentAIJob(superFunc, message, ...) + if message then + CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "stop message: %s", message:getMessage()) + else + CpUtil.infoVehicle(self, "no stop message was given.") + return superFunc(self, message, ...) + end + local releaseMessage, hasFinished, event, isOnlyShownOnPlayerStart = g_infoTextManager:getInfoTextDataByAIMessage(message) + + CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "finished: %s, event: %s", + tostring(hasFinished), tostring(event)) + local wasCpActive = self:getIsCpActive() + if wasCpActive then + local driveStrategy = self:getCpDriveStrategy() + if driveStrategy then + -- TODO: this isn't needed if we do not return a 0 < maxSpeed < 0.5, should either be exactly 0 or greater than 0.5 + local maxSpeed = driveStrategy and driveStrategy:getMaxSpeed() + if message:isa(AIMessageErrorBlockedByObject) then + if self.spec_aiFieldWorker.didNotMoveTimer and self.spec_aiFieldWorker.didNotMoveTimer < 0 then + if maxSpeed and maxSpeed < 1 then + -- disable the Giants timeout which dismisses the AI worker if it does not move for 5 seconds + -- since we often stop for instance in convoy mode when waiting for another vehicle to turn + -- (when we do this, we set our maxSpeed to 0). So we also check our maxSpeed, this way the Giants timer will + -- fire if we are blocked (thus have a maxSpeed > 0 but not moving) + CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, 'Overriding the Giants did not move timer, with speed: %.2f', maxSpeed) + return + else + CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, 'Giants did not move timer triggered, with speed: %.2f!', maxSpeed) + end + end + end + driveStrategy:onFinished() + end + end + self:resetCpAllActiveInfoTexts() + --- Only add the info text, if it's available and nobody is in the vehicle. + if not self:getIsControlled() and releaseMessage and not isOnlyShownOnPlayerStart then + self:setCpInfoTextActive(releaseMessage) + end + superFunc(self, message,...) + if wasCpActive then + if event then + SpecializationUtil.raiseEvent(self, event) + end + if hasFinished and self:getCpSettings().foldImplementAtEnd:getValue() then + --- Folds implements at the end if the setting is active. + self:prepareForAIDriving() + end + end +end + function CpAIWorker:startCpDriveTo(task, jobParameters) local spec = self.spec_cpAIWorker spec.driveToTask = task @@ -452,12 +470,10 @@ function CpAIWorker:stopCpDriver() --- Reset the flag. local spec = self.spec_cpAIWorker spec.motorDisabled = false - if spec.driveStrategy then spec.driveStrategy:delete() spec.driveStrategy = nil end - if self.isServer then WheelsUtil.updateWheelsPhysics(self, 0, 0, 0, true, true) end @@ -472,9 +488,7 @@ function CpAIWorker:stopCpDriver() if actionController ~= nil then actionController:resetCurrentState() end - self:raiseAIEvent("onAIFieldWorkerEnd", "onAIImplementEnd") - end function CpAIWorker:getCpDriveStrategy() @@ -482,6 +496,44 @@ function CpAIWorker:getCpDriveStrategy() return spec.driveStrategy end +function CpAIWorker:getCpReverseDrivingDirectionNode() + local spec = self.spec_cpAIWorker + if not spec.reverseDrivingDirectionNode and SpecializationUtil.hasSpecialization(ReverseDriving, self.specializations) then + spec.reverseDrivingDirectionNode = + CpUtil.createNewLinkedNode(self, "realReverseDrivingDirectionNode", self:getAIDirectionNode()) + setRotation(spec.reverseDrivingDirectionNode, 0, math.pi, 0) + end + return spec.reverseDrivingDirectionNode +end + +--- TODO: Do we really need the AIDriveStrategyCollision from giants, as this one is only active for fieldwork? +function CpAIWorker:isCollisionDetectionEnabled() + local spec = self.spec_cpAIWorker + return spec.collisionDetectionEnabled +end + +function CpAIWorker:enableCollisionDetection() + local spec = self.spec_cpAIWorker + spec.collisionDetectionEnabled = true +end + +function CpAIWorker:disableCollisionDetection() + local spec = self.spec_cpAIWorker + spec.collisionDetectionEnabled = false +end + +function CpAIWorker:getCollisionCheckActive(superFunc,...) + local spec = self.spec_cpAIWorker + if spec.collisionDetectionEnabled then + return superFunc(self,...) + else + return false + end +end +AIDriveStrategyCollision.getCollisionCheckActive = Utils.overwrittenFunction( + AIDriveStrategyCollision.getCollisionCheckActive, CpAIWorker.getCollisionCheckActive +) + function CpAIWorker:stopFieldWorker(superFunc, ...) --- Reset the flag. self.spec_cpAIWorker.motorDisabled = false From bf8f6ad0c83c712148b7f4eefc976ec2214eb431 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:23:43 +0200 Subject: [PATCH 031/107] Small delete bug fix --- scripts/ai/Markers.lua | 2 +- scripts/specializations/CpAIWorker.lua | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/ai/Markers.lua b/scripts/ai/Markers.lua index 764d04301..e0674a111 100644 --- a/scripts/ai/Markers.lua +++ b/scripts/ai/Markers.lua @@ -119,4 +119,4 @@ function Markers.getMarkerNodes(vehicle) local frontMarker = Markers.getFrontMarkerNode(vehicle) local backMarker = Markers.getBackMarkerNode(vehicle) return frontMarker, backMarker, g_vehicleMarkers[vehicle].frontMarkerOffset, g_vehicleMarkers[vehicle].backMarkerOffset -end +end \ No newline at end of file diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index aa7ca137f..95a27b95c 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -48,6 +48,7 @@ function CpAIWorker.registerEventListeners(vehicleType) SpecializationUtil.registerEventListener(vehicleType, "onUpdate", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", CpAIWorker) + SpecializationUtil.registerEventListener(vehicleType, "onPreDelete", CpAIWorker) --- Autodrive events SpecializationUtil.registerEventListener(vehicleType, "onStopAutoDrive", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onStartAutoDrive", CpAIWorker) @@ -153,6 +154,10 @@ function CpAIWorker:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSel CpAIWorker.updateActionEvents(self) end +function CpAIWorker:onPreDelete() + +end + ----------------------------------------------- --- Action input events ----------------------------------------------- From 10173cafe3712c237d899b6f153e479f378c843b Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:34:05 +0200 Subject: [PATCH 032/107] fix --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index de270f980..921ddfcbd 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -53,7 +53,7 @@ AIDriveStrategyShovelSiloLoader.safeSpaceToTrailer = 5 AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 15 AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 -AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 7 +AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8 function AIDriveStrategyShovelSiloLoader.new(customMt) if customMt == nil then @@ -114,7 +114,7 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) local position = jobParameters.unloadPosition local dirX, dirZ = position:getDirection() setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1) - local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -math.max(self.distShovelTrailerPreUnload, self.turningRadius)) + local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -math.max(self.distShovelUnloadStationPreUnload, self.turningRadius)) setTranslation(self.unloadPositionNode, dx, dy, dz) else self:debug("Starting shovel silo to unload into trailer.") From a412157db47f4a5c130a7041af7ef8e5164b4517 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:49:40 +0200 Subject: [PATCH 033/107] Might improve trigger unload --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 13 ++----------- scripts/ai/controllers/ShovelController.lua | 8 +++++++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 921ddfcbd..b824b3dae 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -49,9 +49,8 @@ AIDriveStrategyShovelSiloLoader.myStates = { REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, } -AIDriveStrategyShovelSiloLoader.safeSpaceToTrailer = 5 AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 -AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 15 +AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 30 AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8 @@ -161,11 +160,8 @@ function AIDriveStrategyShovelSiloLoader:setAllStaticParameters() self.proximityController = ProximityController(self.vehicle, self:getWorkWidth()) self.proximityController:registerIgnoreObjectCallback(self, self.ignoreProximityObject) self:setFrontAndBackMarkers() - self.siloEndProximitySensor = SingleForwardLookingProximitySensorPack(self.vehicle, self.shovelController:getShovelNode(), 5, 1) - self.heapNode = CpUtil.createNode("heapNode", 0, 0, 0, nil) - self.lastTrailerSearch = 0 end @@ -206,11 +202,8 @@ end --- implements are started/lowered etc. function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:updateLowFrequencyImplementControllers() - local moveForwards = not self.ppc:isReversing() local gx, gz, _ - - ---------------------------------------------------------------- if not moveForwards then local maxSpeed gx, gz, maxSpeed = self:getReverseDriveData() @@ -280,12 +273,11 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:setMaxSpeed(0) end if not self.isUnloadingAtTrailerActive then - if self.shovelController:isShovelOverTrailer(refNode, 3) and self.shovelController:canDischarge() then + if self.shovelController:isShovelOverTrailer(refNode, 3) and self.shovelController:canDischarge(self.unloadTrigger) then self:setNewState(self.states.UNLOADING) self:setMaxSpeed(0) end end - elseif self.state == self.states.UNLOADING then self:setMaxSpeed(0) if self:hasFinishedUnloading() then @@ -301,7 +293,6 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) end end end - self:limitSpeed() return gx, gz, moveForwards, self.maxSpeed, 100 end diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 47b2e73e1..f9de73a72 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -57,7 +57,10 @@ function ShovelController:getDischargeNode() return self.implement:getCurrentDischargeNode() end -function ShovelController:canDischarge() +--- Checks if the shovel raycast has found an unload target. +---@param targetTrigger any +---@return boolean +function ShovelController:canDischarge(targetTrigger) local dischargeNode = self:getDischargeNode() local spec = self.implement.spec_dischargeable if not spec.isAsyncRaycastActive then @@ -66,6 +69,9 @@ function ShovelController:canDischarge() self.implement:updateRaycast(dischargeNode) dischargeNode.raycast.node = oldNode end + if targetTrigger and targetTrigger ~= self.implement:getDischargeTargetObject(dischargeNode) then + return false + end return dischargeNode.dischargeHit end From b47776c47bb74a97bd5b511ae29f3e0b6b9bc57d Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:53:09 +0200 Subject: [PATCH 034/107] Minor Adjustment --- scripts/ai/controllers/ShovelController.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index f9de73a72..1fae21c4c 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -58,7 +58,7 @@ function ShovelController:getDischargeNode() end --- Checks if the shovel raycast has found an unload target. ----@param targetTrigger any +---@param targetTrigger CpTrigger|nil ---@return boolean function ShovelController:canDischarge(targetTrigger) local dischargeNode = self:getDischargeNode() @@ -69,7 +69,7 @@ function ShovelController:canDischarge(targetTrigger) self.implement:updateRaycast(dischargeNode) dischargeNode.raycast.node = oldNode end - if targetTrigger and targetTrigger ~= self.implement:getDischargeTargetObject(dischargeNode) then + if targetTrigger and targetTrigger:getTrigger() ~= self.implement:getDischargeTargetObject(dischargeNode) then return false end return dischargeNode.dischargeHit From 67856d9423653a6f6be93bfdd802e329b621ab47 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 13 Jul 2023 16:03:09 +0200 Subject: [PATCH 035/107] Added loading position offset setting with vehicle configuration. Added a setting to control the height of the loading shovel position, similar to the xOffset in the fieldwork mode. --- config/MasterTranslations.xml | 8 +++++++ config/VehicleConfigurations.xml | 3 +++ config/VehicleSettingsSetup.xml | 1 + .../gamePadHud/SiloLoaderGamePadHudPage.xml | 1 + scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 7 ++++++ scripts/specializations/CpShovelPositions.lua | 23 ++++++++++++++----- scripts/specializations/CpVehicleSettings.lua | 9 +++++++- 7 files changed, 45 insertions(+), 7 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 518eaa41e..fba7fcaaa 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -613,6 +613,14 @@ + + + + + + + + diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 778c285c4..274ab95f1 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -97,6 +97,9 @@ You can define the following custom settings: Overrides the automatic selection of a moving tool for example for the shield controller of a snowcat. Used to control the target tilt. +- loadingShovelOffset: number -1 : 1 + Offset relative to the calculated loading shovel position, needs to be set for a shovel implement. + - shovelMovingToolIx: number If the shovel is a high dump shovel then this moving tool ix is needed. diff --git a/config/VehicleSettingsSetup.xml b/config/VehicleSettingsSetup.xml index aeb5cb720..c4f5e46ee 100644 --- a/config/VehicleSettingsSetup.xml +++ b/config/VehicleSettingsSetup.xml @@ -76,6 +76,7 @@ + diff --git a/config/gamePadHud/SiloLoaderGamePadHudPage.xml b/config/gamePadHud/SiloLoaderGamePadHudPage.xml index 0969a7251..34fc6f86f 100644 --- a/config/gamePadHud/SiloLoaderGamePadHudPage.xml +++ b/config/gamePadHud/SiloLoaderGamePadHudPage.xml @@ -7,4 +7,5 @@ + diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index 6382aae28..4e840ff02 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -43,6 +43,9 @@ function CpSiloLoaderWorkerHudPageElement:setupElements(baseHud, vehicle, lines, local x, y = unpack(lines[4].right) self.fillLevelProgressText = CpTextHudElement.new(self, x, y, CpBaseHud.defaultFontSize, RenderText.ALIGN_RIGHT) + --- Shovel loading height offset. + self.loadingShovelHeightOffsetBtn = baseHud:addLineTextButton(self, 2, CpBaseHud.defaultFontSize, + vehicle:getCpSettings().loadingShovelHeightOffset) CpGuiUtil.addCopyAndPasteButtons(self, baseHud, vehicle, lines, wMargin, hMargin, 1) @@ -85,6 +88,10 @@ function CpSiloLoaderWorkerHudPageElement:updateContent(vehicle, status) self.workWidthBtn:setTextDetails(workWidth:getTitle(), workWidth:getString()) self.workWidthBtn:setVisible(workWidth:getIsVisible()) + local loadingHeightOffset = vehicle:getCpSettings().loadingShovelHeightOffset + self.loadingShovelHeightOffsetBtn:setTextDetails(loadingHeightOffset:getTitle(), loadingHeightOffset:getString()) + self.loadingShovelHeightOffsetBtn:setVisible(loadingHeightOffset:getIsVisible()) + self.fillLevelProgressText:setTextDetails(status:getSiloFillLevelPercentageLeftOver()) --- Update copy and paste buttons diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index a24785177..f0eef4cdb 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -170,7 +170,7 @@ end function CpShovelPositions:onUpdateTick(dt) local spec = self.spec_cpShovelPositions - if spec.shovelToolIx == nil or spec.armToolIx == nil then + if spec.shovelToolIx == nil or spec.armToolIx == nil or self.rootVehicle == nil then return end if spec.state == CpShovelPositions.LOADING then @@ -248,8 +248,9 @@ end ---@param shovelLimits table ---@param armLimits table ---@param useHighDumpShovel boolean|nil +---@param heightOffset number|nil ---@return boolean|nil -function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHighDumpShovel) +function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHighDumpShovel, heightOffset) local min, max = unpack(shovelLimits) --- Target angle of the shovel node, which is at the end of the shovel. local targetAngle = math.rad(min) + math.rad(max - min)/2 @@ -279,7 +280,8 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig local attacherJointNode = self.spec_attachable.attacherJoint.node local angle, shovelNode = CpShovelPositions.getShovelData(self) local _, shovelY, _ = localToLocal(self.rootNode, attacherJointNode, 0, 0, 0) - + heightOffset = heightOffset or 0 + --- local tempNode = createTransformGroup("tempVehicleSizeCenter") -- link(vehicle.rootNode, tempNode) -- setTranslation(tempNode, vehicle.size.widthOffset, vehicle.size.heightOffset + vehicle.size.height / 2, vehicle.size.lengthOffset) @@ -291,14 +293,20 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig local _, ty, tz = localToLocal(getChildAt(armTool.node, 0), armVehicle.rootNode, 0, 0, 0) local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) - local sx, sy, sz = 0, targetHeight - shovelY - self.size.heightOffset, 0 - local ex, ey, ez = 0, targetHeight - shovelY - self.size.heightOffset, 20 + local sx, sy, sz = 0, targetHeight - shovelY - self.size.heightOffset + heightOffset, 0 + local ex, ey, ez = 0, targetHeight - shovelY - self.size.heightOffset + heightOffset, 20 local yMax = ay + radiusArmToolToShovelTool + local yMin = ay - radiusArmToolToShovelTool if sy > yMax then --- Makes sure the target height is still reachable sy = yMax - 0.01 ey = yMax - 0.01 end + if sy < yMin then + --- Makes sure the target height is still reachable + sy = yMin + 0.01 + ey = yMin + 0.01 + end local hasIntersection, i1z, i1y, i2z, i2y = MathUtil.getCircleLineIntersection(az, ay, radiusArmToolToShovelTool, sz, sy, ez, ey) @@ -387,10 +395,13 @@ end function CpShovelPositions:updateLoadingPosition(dt) local spec = self.spec_cpShovelPositions local angle = CpShovelPositions.getShovelData(self) + local heightOffset = self.rootVehicle.getCpSettings and self.rootVehicle:getCpSettings().loadingShovelHeightOffset:getValue() local isDirty if angle then isDirty = CpShovelPositions.setShovelPosition(self, dt, - CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, CpShovelPositions.LOADING_POSITION.ARM_LIMITS) + CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, + CpShovelPositions.LOADING_POSITION.ARM_LIMITS, + nil, heightOffset) end spec.isDirty = isDirty end diff --git a/scripts/specializations/CpVehicleSettings.lua b/scripts/specializations/CpVehicleSettings.lua index 6a6b1616d..e1b321eb3 100644 --- a/scripts/specializations/CpVehicleSettings.lua +++ b/scripts/specializations/CpVehicleSettings.lua @@ -120,6 +120,9 @@ function CpVehicleSettings:onStateChange(state, data) spec.lowerImplementEarly, 'lowerEarly') CpVehicleSettings.setFromVehicleConfiguration(self, data.attachedVehicle, spec.bunkerSiloWorkWidth, 'workingWidth') + CpVehicleSettings.setFromVehicleConfiguration(self, data.attachedVehicle, + spec.loadingShovelHeightOffset, 'loadingShovelOffset') + CpVehicleSettings.validateSettings(self) elseif state == Vehicle.STATE_CHANGE_DETACH then CpVehicleSettings.setAutomaticWorkWidthAndOffset(self, data.attachedVehicle) @@ -362,7 +365,7 @@ function CpVehicleSettings:areCourseSettingsVisible() end function CpVehicleSettings:areBunkerSiloSettingsVisible() - return self:getCanStartCpBunkerSiloWorker() + return self:getCanStartCpBunkerSiloWorker() or self:getCanStartCpSiloLoaderWorker() end function CpVehicleSettings:areCombineUnloaderSettingsVisible() @@ -387,6 +390,10 @@ function CpVehicleSettings:setAutomaticBaleCollectorOffset() spec.baleCollectorOffset:setFloatValue(offset) end +function CpVehicleSettings:isLoadingShovelOffsetSettingVisible() + return self:getCanStartCpSiloLoaderWorker() and not AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) +end + --- Saves the user value changed on the server. function CpVehicleSettings:onCpUserSettingChanged(setting) if not self.isServer then From 76881f4d0f7a84c2144d6ed3e9b94658da3e4fc3 Mon Sep 17 00:00:00 2001 From: schwiti6190 Date: Thu, 13 Jul 2023 14:03:33 +0000 Subject: [PATCH 036/107] Updated translations --- translations/translation_br.xml | 2 ++ translations/translation_cs.xml | 2 ++ translations/translation_ct.xml | 2 ++ translations/translation_cz.xml | 2 ++ translations/translation_da.xml | 2 ++ translations/translation_de.xml | 2 ++ translations/translation_ea.xml | 2 ++ translations/translation_en.xml | 2 ++ translations/translation_es.xml | 2 ++ translations/translation_fc.xml | 2 ++ translations/translation_fi.xml | 2 ++ translations/translation_fr.xml | 2 ++ translations/translation_hu.xml | 2 ++ translations/translation_it.xml | 2 ++ translations/translation_jp.xml | 2 ++ translations/translation_kr.xml | 2 ++ translations/translation_nl.xml | 2 ++ translations/translation_no.xml | 2 ++ translations/translation_pl.xml | 2 ++ translations/translation_pt.xml | 2 ++ translations/translation_ro.xml | 2 ++ translations/translation_ru.xml | 2 ++ translations/translation_sv.xml | 2 ++ translations/translation_tr.xml | 2 ++ 24 files changed, 48 insertions(+) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index b4e68c90f..be6bef151 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 37d34c151..6610bdefa 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml index 29f9b8820..a3a918d83 100644 --- a/translations/translation_ct.xml +++ b/translations/translation_ct.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index e5bfe8b43..7fef8dbae 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_da.xml b/translations/translation_da.xml index 71b70cb3e..c00a87de7 100644 --- a/translations/translation_da.xml +++ b/translations/translation_da.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 6893d93a0..086fd4f34 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml index 487297825..2762891d8 100644 --- a/translations/translation_ea.xml +++ b/translations/translation_ea.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 454e60989..8984a854f 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index d9278b066..f738f12bd 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_fc.xml b/translations/translation_fc.xml index 1e82fa96b..910fff456 100644 --- a/translations/translation_fc.xml +++ b/translations/translation_fc.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_fi.xml b/translations/translation_fi.xml index 15f07d006..215325630 100644 --- a/translations/translation_fi.xml +++ b/translations/translation_fi.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index b7aca1aa1..29b037550 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 7e9e1e8a0..5c0a73d12 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index bb65ac788..947fe7e9a 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index 32edfee7c..f73714129 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_kr.xml b/translations/translation_kr.xml index 75bb4f709..12cbcccc5 100644 --- a/translations/translation_kr.xml +++ b/translations/translation_kr.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 89bb6efb2..69089d31a 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_no.xml b/translations/translation_no.xml index 4491229ea..80f1b0d33 100644 --- a/translations/translation_no.xml +++ b/translations/translation_no.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index fdf218218..264d59ebb 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 44394a95f..c62f0264d 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_ro.xml b/translations/translation_ro.xml index 0cbfb5ef2..e56c21353 100644 --- a/translations/translation_ro.xml +++ b/translations/translation_ro.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index 6b842b5d0..af5aa6612 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_sv.xml b/translations/translation_sv.xml index 4f125e18a..a3b8d6baa 100644 --- a/translations/translation_sv.xml +++ b/translations/translation_sv.xml @@ -181,6 +181,8 @@ + + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index c2b85d87c..574be7dff 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -181,6 +181,8 @@ + + From 4f5143f6b297d8ccf655d40c9414bdf9594c7c35 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 13 Jul 2023 19:51:54 +0200 Subject: [PATCH 037/107] Open shovels, which need to be opened and apply height offset also for Transport position --- scripts/specializations/CpShovelPositions.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index f0eef4cdb..eaca48dc9 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -331,9 +331,19 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig local _, dy, _ = localDirectionToWorld(getParent(tool.node), 0, 0, 1) angle = math.acos(dy) targetAngle = math.pi/2 - math.pi/8 - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMax) or isDirty + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty else - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.rotMin) or isDirty + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty + end + else + for i, tool in pairs(self.spec_cylindered.movingTools) do + if tool.axis then + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty + break + end end end local deltaAngle = targetAngle - angle @@ -409,10 +419,13 @@ end function CpShovelPositions:updateTransportPosition(dt) local spec = self.spec_cpShovelPositions local angle = CpShovelPositions.getShovelData(self) + local heightOffset = self.rootVehicle.getCpSettings and self.rootVehicle:getCpSettings().loadingShovelHeightOffset:getValue() local isDirty if angle then isDirty = CpShovelPositions.setShovelPosition(self, dt, - CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS) + CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, + CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS, + nil, heightOffset) end spec.isDirty = isDirty end From b54bb53330d7e2f92c1db5c1525ad74ee1a990d3 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 14 Jul 2023 08:27:04 +0200 Subject: [PATCH 038/107] Makes map hotspots invisible, while picking an unload trigger. --- scripts/gui/CpAIFrameExtended.lua | 59 +++++++++++++++++++------------ scripts/gui/CpGuiUtil.lua | 19 ++++++++++ 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 3737974d1..55490b208 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -16,17 +16,31 @@ CpInGameMenuAIFrameExtended.DELAY = 1 CpInGameMenuAIFrameExtended.hotspotFilterState = {} CpInGameMenuAIFrameExtended.validCustomFieldCreationHotspots = { --- Hotspots visible, while drawing a custom field border. - MapHotspot.CATEGORY_FIELD, --- MapHotspot.CATEGORY_UNLOADING, --- MapHotspot.CATEGORY_LOADING, --- MapHotspot.CATEGORY_PRODUCTION, - MapHotspot.CATEGORY_AI, - MapHotspot.CATEGORY_COMBINE, - MapHotspot.CATEGORY_STEERABLE, - MapHotspot.CATEGORY_PLAYER, --- MapHotspot.CATEGORY_SHOP, --- MapHotspot.CATEGORY_OTHER - CustomFieldHotspot.CATEGORY + [MapHotspot.CATEGORY_FIELD] = true, +-- [MapHotspot.CATEGORY_UNLOADING] = true, +-- [MapHotspot.CATEGORY_LOADING] = true, +-- [MapHotspot.CATEGORY_PRODUCTION] = true, + [MapHotspot.CATEGORY_AI] = true, + [MapHotspot.CATEGORY_COMBINE] = true, + [MapHotspot.CATEGORY_STEERABLE] = true, + [MapHotspot.CATEGORY_PLAYER] = true, +-- MapHotspot.CATEGORY_SHOP] = true, +-- MapHotspot.CATEGORY_OTHER] = true, + [CustomFieldHotspot.CATEGORY] = true +} + +CpInGameMenuAIFrameExtended.validPickingLoadingPositionHotspots = { + --- Hotspots visible, while picking a loading position. + [MapHotspot.CATEGORY_FIELD] = true, +-- [MapHotspot.CATEGORY_UNLOADING] = true, +-- [MapHotspot.CATEGORY_LOADING] = true, +-- [MapHotspot.CATEGORY_PRODUCTION] = true, + [MapHotspot.CATEGORY_AI] = true, + [MapHotspot.CATEGORY_COMBINE] = true, + [MapHotspot.CATEGORY_STEERABLE] = true, + [MapHotspot.CATEGORY_PLAYER] = true, +-- MapHotspot.CATEGORY_SHOP] = true, +-- MapHotspot.CATEGORY_OTHER] = true, } function CpInGameMenuAIFrameExtended:onAIFrameLoadMapFinished() @@ -538,6 +552,9 @@ function CpInGameMenuAIFrameExtended:startPickingPosition(superFunc, parameter, elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD then self.aiTargetMapHotspot = self.unloadAiTargetMapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.UNLOAD + CpInGameMenuAIFrameExtended.hotspotFilterState = {} + CpGuiUtil.saveAndDisableHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.hotspotFilterState) + CpGuiUtil.applyHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.validPickingLoadingPositionHotspots) elseif parameter:getPositionType() == CpAIParameterPositionAngle.POSITION_TYPES.LOAD then self.aiTargetMapHotspot = self.loadAiTargetMapHotspot self.currentPickingMapHotspotType = CpAIParameterPositionAngle.POSITION_TYPES.LOAD @@ -559,6 +576,10 @@ InGameMenuAIFrame.startPickPositionAndRotation = Utils.overwrittenFunction(InGam function CpInGameMenuAIFrameExtended:resetHotspots() self.aiTargetMapHotspot = self.rawAiTargetMapHotspot self.currentPickingMapHotspotType = nil + if CpInGameMenuAIFrameExtended.hotspotFilterState then + CpGuiUtil.applyHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.hotspotFilterState) + end + CpInGameMenuAIFrameExtended.hotspotFilterState = nil end --- Added support for the cp field target position. @@ -721,24 +742,18 @@ function InGameMenuAIFrame:onClickCreateFieldBorder() g_customFieldManager:addField(CpInGameMenuAIFrameExtended.curDrawPositions) CpInGameMenuAIFrameExtended.curDrawPositions = {} --- Restore hotspot filters here: - for k, v in pairs(self.ingameMapBase.filter) do - self.ingameMapBase:setHotspotFilter(k, CpInGameMenuAIFrameExtended.hotspotFilterState[k]) + if CpInGameMenuAIFrameExtended.hotspotFilterState then + CpGuiUtil.applyHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.hotspotFilterState) end - + CpInGameMenuAIFrameExtended.hotspotFilterState = nil else CpInGameMenuAIFrameExtended.curDrawPositions = {} self.drawingCustomFieldHeader:setVisible(true) self.mode = CpInGameMenuAIFrameExtended.MODE_DRAW_FIELD_BORDER CpInGameMenuAIFrameExtended.hotspotFilterState = {} --- Change the hotspot filter here: - for k, v in pairs(self.ingameMapBase.filter) do - CpInGameMenuAIFrameExtended.hotspotFilterState[k] = v - - self.ingameMapBase:setHotspotFilter(k, false) - end - for _,v in ipairs(CpInGameMenuAIFrameExtended.validCustomFieldCreationHotspots) do - self.ingameMapBase:setHotspotFilter(v, true) - end + CpGuiUtil.saveAndDisableHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.hotspotFilterState) + CpGuiUtil.applyHotspotFilters(self.ingameMapBase, CpInGameMenuAIFrameExtended.validCustomFieldCreationHotspots) end end diff --git a/scripts/gui/CpGuiUtil.lua b/scripts/gui/CpGuiUtil.lua index 7922faf61..7d920a159 100644 --- a/scripts/gui/CpGuiUtil.lua +++ b/scripts/gui/CpGuiUtil.lua @@ -214,6 +214,25 @@ function CpGuiUtil.setTarget(element, target) element.targetName = target.name end +--- Apply the defined filter to the map. +---@param map table ingame menu map +---@param hotspots table +function CpGuiUtil.applyHotspotFilters(map, hotspots) + for k, v in pairs(hotspots) do + map:setHotspotFilter(k, v) + end +end + +--- Saves the hotspot filters and disables these on the map. +---@param map table +---@param hotspots table +function CpGuiUtil.saveAndDisableHotspotFilters(map, hotspots) + for k, v in pairs(map.filter) do + map:setHotspotFilter(k, false) + hotspots[k] = v + end +end + ------------------------------------------------ --- Plots ------------------------------------------------ From 010520dae246b70f56090518b3fd6a85f0c76f8d Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 14 Jul 2023 16:12:35 +0200 Subject: [PATCH 039/107] minior bug fix --- scripts/ai/AIDriveStrategyCourse.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/ai/AIDriveStrategyCourse.lua b/scripts/ai/AIDriveStrategyCourse.lua index 4396dcfe0..6012b541d 100644 --- a/scripts/ai/AIDriveStrategyCourse.lua +++ b/scripts/ai/AIDriveStrategyCourse.lua @@ -111,8 +111,7 @@ function AIDriveStrategyCourse:setAIVehicle(vehicle, jobParameters) self:initializeImplementControllers(vehicle) self.ppc = PurePursuitController(vehicle) self.ppc:registerListeners(self, 'onWaypointPassed', 'onWaypointChange') - -- TODO_22 properly implement this in courseplaySpec - self.storage = vehicle.spec_courseplaySpec + self.storage = vehicle.spec_cpAIWorker self.settings = vehicle:getCpSettings() self.courseGeneratorSettings = vehicle:getCourseGeneratorSettings() From 0f002b4108898d74f8d730675729f76825eb54bb Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 14 Jul 2023 16:56:41 +0200 Subject: [PATCH 040/107] Update MasterTranslations.xml --- config/MasterTranslations.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index fba7fcaaa..56c677f85 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -614,12 +614,12 @@ - - + + - - + + From 1b46596cca89af9eda2ccda1977808993346d3f7 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 14 Jul 2023 14:56:58 +0000 Subject: [PATCH 041/107] Updated translations --- translations/translation_de.xml | 4 ++-- translations/translation_en.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 086fd4f34..69ffb7756 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -181,8 +181,8 @@ - - + + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 8984a854f..baa42d9e3 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -181,8 +181,8 @@ - - + + From 8a3c014f29d32946999a44381332425e04c4516a Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 14 Jul 2023 19:00:43 +0200 Subject: [PATCH 042/107] Adjustment for filltype converter --- scripts/ai/ImplementUtil.lua | 2 +- scripts/ai/jobs/CpAIJobSiloLoader.lua | 33 +++++++++++++++++++++++---- scripts/gui/CpAIFrameExtended.lua | 4 ++-- scripts/trigger/TriggerManager.lua | 6 ++--- scripts/trigger/TriggerWrapper.lua | 15 ++++++++---- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 9f36aa676..1fa1db3fa 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -426,7 +426,7 @@ end ---@param loadTargetImplement table ---@param implementToLoadFrom table ---@param dischargeNode table|nil optional otherwise the current selected node is used. ----@param suppressLog boolean|nil +---@param debugFunc function|nil ---@return boolean is loading possible? ---@return number|nil target implement fill unit ix to load into. ---@return number|nil fill type to load diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 90d5c427a..9a9d5b74f 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -202,7 +202,15 @@ function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) if found and fillType~=nil then if not trigger:getIsFillTypeAllowed(fillType) then --- Fill type is not supported by the trigger. - return false + found = false + local convertedOutputFillTypes = self:getConvertedFillTypes() + for _, convertedFillType in ipairs(convertedOutputFillTypes) do + --- Checks possible found fill type conversions + if trigger:getIsFillTypeAllowed(convertedFillType) then + found = true + break + end + end end end return found, trigger, station @@ -212,12 +220,12 @@ function CpAIJobSiloLoader:drawSilos(map) self.heapPlot:draw(map) g_bunkerSiloManager:drawSilos(map, self.bunkerSilo) if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then - local fillType + local fillTypes = self:getConvertedFillTypes() local silo = self.heap or self.bunkerSilo if silo then - fillType = silo:getFillType() + table.insert(fillTypes, silo:getFillType()) end - g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillType) + g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillTypes) end end @@ -230,3 +238,20 @@ function CpAIJobSiloLoader:getUnloadingStations() end return unloadingStations end + +--- Gets converted fill types if there are any. +---@return table +function CpAIJobSiloLoader:getConvertedFillTypes() + local fillTypes = {} + local vehicle = self:getVehicle() + if vehicle then + local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) + local spec = found and shovels[1].spec_turnOnVehicle + if spec and spec.activateableDischargeNode and spec.activateableDischargeNode.fillTypeConverter then + for _, data in pairs(spec.activateableDischargeNode.fillTypeConverter) do + table.insert(fillTypes, data.targetFillTypeIndex) + end + end + end + return fillTypes +end \ No newline at end of file diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 55490b208..3038501f9 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -588,8 +588,6 @@ function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) return end g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) - g_currentMission:removeMapHotspot(self.aiLoadingMarkerHotspot) - g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) @@ -642,6 +640,8 @@ InGameMenuAIFrame.updateParameterValueTexts = Utils.overwrittenFunction(InGameMe CpInGameMenuAIFrameExtended.updateParameterValueTexts) function CpInGameMenuAIFrameExtended:updateWarnings() + g_currentMission:removeMapHotspot(self.aiLoadingMarkerHotspot) + g_currentMission:removeMapHotspot(self.aiUnloadingMarkerHotspot) for _, element in ipairs(self.currentJobElements) do local parameter = element.aiParameter local parameterType = parameter:getType() diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua index 31a8e111f..d841002da 100644 --- a/scripts/trigger/TriggerManager.lua +++ b/scripts/trigger/TriggerManager.lua @@ -132,10 +132,10 @@ end --- Draws all bunker silos onto the ai map. ---@param map table map to draw to. ---@param selected CpTrigger silo that gets highlighted. ----@param fillType number|nil fill type that needs to be supported. -function TriggerManager:drawDischargeableTriggers(map, selected, fillType) +---@param fillTypes table|nil fill type that needs to be supported. +function TriggerManager:drawDischargeableTriggers(map, selected, fillTypes) for _, trigger in pairs(self.dischargeableUnloadTriggers) do - trigger:drawPlot(map, selected, fillType) + trigger:drawPlot(map, selected, fillTypes) end end diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua index c22283690..f9b7966f5 100644 --- a/scripts/trigger/TriggerWrapper.lua +++ b/scripts/trigger/TriggerWrapper.lua @@ -42,11 +42,16 @@ end ---@param map table ---@param selectedTrigger CpTrigger ----@param fillType number|nil -function CpTrigger:drawPlot(map, selectedTrigger, fillType) - if fillType and fillType ~= FillType.UNKNOWN then - if not self.trigger:getIsFillTypeAllowed(fillType) then - --- Fill type is not allowed. +---@param fillTypes table|nil +function CpTrigger:drawPlot(map, selectedTrigger, fillTypes) + if fillTypes then + local found = false + for i, fillType in pairs(fillTypes) do + if self.trigger:getIsFillTypeAllowed(fillType) then + found = true + end + end + if not found then return end end From 51be63d009325adefa125d6da020c2eda92f3ec1 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 14 Jul 2023 19:07:24 +0200 Subject: [PATCH 043/107] Slightly reduces unloading arm height --- scripts/specializations/CpShovelPositions.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index eaca48dc9..691cfbfca 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -41,8 +41,8 @@ CpShovelPositions = { }, PRE_UNLOAD_POSITION = { ARM_LIMITS = { - 7, - 7 + 2.7, + 2.8 }, SHOVEL_LIMITS = { 43, @@ -51,8 +51,8 @@ CpShovelPositions = { }, UNLOADING_POSITION = { ARM_LIMITS = { - 7, - 7 + 2.7, + 2.8 }, }, DEBUG = true From acfdbdcd2b0f4b97ac510c4030473f56f172e4bc Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 14 Jul 2023 19:43:07 +0200 Subject: [PATCH 044/107] Only enable change, when the vehicle is stopped --- config/VehicleSettingsSetup.xml | 3 ++- scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 1 + scripts/specializations/CpVehicleSettings.lua | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config/VehicleSettingsSetup.xml b/config/VehicleSettingsSetup.xml index c4f5e46ee..9ac6b2868 100644 --- a/config/VehicleSettingsSetup.xml +++ b/config/VehicleSettingsSetup.xml @@ -76,7 +76,8 @@ - + diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index 4e840ff02..91ebc0aa3 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -91,6 +91,7 @@ function CpSiloLoaderWorkerHudPageElement:updateContent(vehicle, status) local loadingHeightOffset = vehicle:getCpSettings().loadingShovelHeightOffset self.loadingShovelHeightOffsetBtn:setTextDetails(loadingHeightOffset:getTitle(), loadingHeightOffset:getString()) self.loadingShovelHeightOffsetBtn:setVisible(loadingHeightOffset:getIsVisible()) + self.loadingShovelHeightOffsetBtn:setDisabled(loadingHeightOffset:getIsDisabled()) self.fillLevelProgressText:setTextDetails(status:getSiloFillLevelPercentageLeftOver()) diff --git a/scripts/specializations/CpVehicleSettings.lua b/scripts/specializations/CpVehicleSettings.lua index e1b321eb3..7e3c60375 100644 --- a/scripts/specializations/CpVehicleSettings.lua +++ b/scripts/specializations/CpVehicleSettings.lua @@ -394,6 +394,10 @@ function CpVehicleSettings:isLoadingShovelOffsetSettingVisible() return self:getCanStartCpSiloLoaderWorker() and not AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) end +function CpVehicleSettings:isLoadingShovelOffsetSettingDisabled() + return not AIUtil.isStopped(self) +end + --- Saves the user value changed on the server. function CpVehicleSettings:onCpUserSettingChanged(setting) if not self.isServer then From c99bdc8f5b97264907a872b7813d54297e45e4d8 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 14 Jul 2023 19:52:07 +0200 Subject: [PATCH 045/107] Increase the map zoom. --- Courseplay.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Courseplay.lua b/Courseplay.lua index 8f538ee57..cb8a022f9 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -192,9 +192,21 @@ end FSCareerMissionInfo.saveToXMLFile = Utils.prependedFunction(FSCareerMissionInfo.saveToXMLFile, Courseplay.saveToXMLFile) function Courseplay:update(dt) - g_devHelper:update() - g_bunkerSiloManager:update(dt) - g_triggerManager:update(dt) + g_devHelper:update() + g_bunkerSiloManager:update(dt) + g_triggerManager:update(dt) + if not self.postInit then + -- Doubles the map zoom. Mainly to make it easier to set targets for unload triggers. + self.postInit = true + local function setIngameMapFix(mapElement) + local factor = 2*mapElement.terrainSize/2048 + mapElement.zoomMax = mapElement.zoomMax * factor + mapElement.zoomDefault = mapElement.zoomDefault * factor + mapElement.mapZoom = mapElement.zoomDefault + end + setIngameMapFix(g_currentMission.inGameMenu.pageAI.ingameMap) + setIngameMapFix(g_currentMission.inGameMenu.pageMapOverview.ingameMap) + end end function Courseplay:draw() From 8f2c6a43c6012adaefb1d70f226b61385c80050c Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:24:57 +0200 Subject: [PATCH 046/107] Rebase fix --- Courseplay.lua | 2 +- scripts/specializations/CourseplaySpec.lua | 106 --------------------- 2 files changed, 1 insertion(+), 107 deletions(-) delete mode 100644 scripts/specializations/CourseplaySpec.lua diff --git a/Courseplay.lua b/Courseplay.lua index cb8a022f9..d9c7192bc 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -157,7 +157,7 @@ end function Courseplay.drawHudMap(map) if g_Courseplay.globalSettings.drawOntoTheHudMap:getValue() then local vehicle = g_currentMission.controlledVehicle - if vehicle and vehicle:getIsEntered() and not g_gui:getIsGuiVisible() and vehicle.spec_courseplaySpec and not vehicle.spec_locomotive then + if vehicle and vehicle:getIsEntered() and not g_gui:getIsGuiVisible() and vehicle.spec_cpAIWorker and not vehicle.spec_locomotive then SpecializationUtil.raiseEvent(vehicle, "onCpDrawHudMap", map) end end diff --git a/scripts/specializations/CourseplaySpec.lua b/scripts/specializations/CourseplaySpec.lua deleted file mode 100644 index cf3bae77a..000000000 --- a/scripts/specializations/CourseplaySpec.lua +++ /dev/null @@ -1,106 +0,0 @@ ---- Cp ai driver spec - ----@class CourseplaySpec -CourseplaySpec = {} - -CourseplaySpec.MOD_NAME = g_currentModName - -CourseplaySpec.KEY = "."..CourseplaySpec.MOD_NAME..".courseplaySpec." - -function CourseplaySpec.initSpecialization() - local schema = Vehicle.xmlSchemaSavegame -end - -function CourseplaySpec.prerequisitesPresent(specializations) - return SpecializationUtil.hasSpecialization(CpAIWorker, specializations) -end - -function CourseplaySpec.registerEventListeners(vehicleType) - SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onLoad", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onEnterVehicle", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", CourseplaySpec) - SpecializationUtil.registerEventListener(vehicleType, "onDraw", CourseplaySpec) -end - -function CourseplaySpec.registerEvents(vehicleType) - SpecializationUtil.registerEvent(vehicleType, "onCpUnitChanged") - SpecializationUtil.registerEvent(vehicleType, "onCpDrawHudMap") -end - -function CourseplaySpec.registerOverwrittenFunctions(vehicleType) - -end - -function CourseplaySpec:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) - -end - ------------------------------------------------------------------------------------------------------------------------- ---- Event listeners ---------------------------------------------------------------------------------------------------------------------------- -function CourseplaySpec:onLoad(savegame) - --- Register the spec: spec_courseplaySpec - local specName = CourseplaySpec.MOD_NAME .. ".courseplaySpec" - self.spec_courseplaySpec = self["spec_" .. specName] - g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.USE_MILES], CourseplaySpec.onUnitChanged, self) - g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.USE_ACRE], CourseplaySpec.onUnitChanged, self) - g_messageCenter:subscribe(MessageType.CP_DISTANCE_UNIT_CHANGED, CourseplaySpec.onUnitChanged, self) -end - -function CourseplaySpec:onUnitChanged() - SpecializationUtil.raiseEvent(self,"onCpUnitChanged") -end - -function CourseplaySpec:onPostLoad(savegame) - -end - -function CourseplaySpec:saveToXMLFile(xmlFile, baseKey, usedModNames) - -end - -function CourseplaySpec:onEnterVehicle(isControlling) - -end - -function CourseplaySpec:onLeaveVehicle(wasEntered) - -end - -function CourseplaySpec:isCollisionDetectionEnabled() - return self.collisionDetectionEnabled -end - -function CourseplaySpec:enableCollisionDetection() - self.collisionDetectionEnabled = true -end - -function CourseplaySpec:disableCollisionDetection() - self.collisionDetectionEnabled = false -end - ---- This is to be able to disable the built-in AIDriveStrategyCollision check from our drive strategies -function CourseplaySpec:getCollisionCheckActive(superFunc,...) - if self.collisionDetectionEnabled then - return superFunc(self,...) - else - return false - end -end - ---- Enriches the status data for the hud here. -function CourseplaySpec:onUpdateTick() - -end - -function CourseplaySpec:onDraw() - -end - - -AIDriveStrategyCollision.getCollisionCheckActive = Utils.overwrittenFunction( - AIDriveStrategyCollision.getCollisionCheckActive, CourseplaySpec.getCollisionCheckActive -) From 154ca1bc03c895da06b4eb57985a2870e85734f1 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:53:50 +0200 Subject: [PATCH 047/107] Small fix --- scripts/ai/Markers.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/ai/Markers.lua b/scripts/ai/Markers.lua index e0674a111..2aeb6d8b2 100644 --- a/scripts/ai/Markers.lua +++ b/scripts/ai/Markers.lua @@ -36,23 +36,23 @@ local function setBackMarkerNode(vehicle, measuredBackDistance) if AIUtil.hasImplementsOnTheBack(vehicle) then local lastImplement lastImplement, backMarkerOffset = AIUtil.getLastAttachedImplement(vehicle) - referenceNode = AIUtil.getDirectionNode(vehicle) + referenceNode = AIUtil.getDirectionNode(vehicle) CpUtil.debugVehicle(CpDebug.DBG_IMPLEMENTS, vehicle, 'Using the last implement\'s rear distance for the back marker node, %d m from root node', backMarkerOffset) elseif measuredBackDistance then - referenceNode = AIUtil.getDirectionNode(vehicle) + referenceNode = AIUtil.getDirectionNode(vehicle) backMarkerOffset = -measuredBackDistance CpUtil.debugVehicle(CpDebug.DBG_IMPLEMENTS, vehicle, 'back marker node on measured back distance %.1f', measuredBackDistance) elseif reverserNode then -- if there is a reverser node, use that, mainly because that most likely will turn with an implement -- or with the back component of an articulated vehicle. Just need to find out the distance correctly - local dx, _, dz = localToLocal(reverserNode, AIUtil.getDirectionNode(vehicle), 0, 0, 0) + local dx, _, dz = localToLocal(reverserNode, AIUtil.getDirectionNode(vehicle), 0, 0, 0) local dBetweenRootAndReverserNode = MathUtil.vector2Length(dx, dz) backMarkerOffset = dBetweenRootAndReverserNode - vehicle.size.length / 2 - vehicle.size.lengthOffset referenceNode = reverserNode CpUtil.debugVehicle(CpDebug.DBG_IMPLEMENTS, vehicle, 'Using the %s node for the back marker node %d m from root node (%d m between root and reverser)', debugText, backMarkerOffset, dBetweenRootAndReverserNode) else - referenceNode = AIUtil.getDirectionNode(vehicle) + referenceNode = AIUtil.getDirectionNode(vehicle) backMarkerOffset = - vehicle.size.length / 2 + vehicle.size.lengthOffset CpUtil.debugVehicle(CpDebug.DBG_IMPLEMENTS, vehicle, 'Using the vehicle\'s root node for the back marker node, %d m from root node', backMarkerOffset) end From dcf92079d29a599b8911e55199562845ec9b6d57 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:15:26 +0200 Subject: [PATCH 048/107] Added closing of foldable shovels during transport --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 4 +- scripts/specializations/CpShovelPositions.lua | 48 ++++++++++++------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index b824b3dae..8ad3724df 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -130,8 +130,8 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) local dirX, dirZ = self.silo:getLengthDirection() local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, yRot) - self.siloAreaToAvoid = PathfinderUtil.NodeArea(self.siloFrontNode, -self.silo:getWidth()/2, - 0, self.silo:getWidth(), self.silo:getLength()) + self.siloAreaToAvoid = PathfinderUtil.NodeArea(self.siloFrontNode, -self.silo:getWidth()/2 - 1, + -1, self.silo:getWidth() + 2, self.silo:getLength() + 2) self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) end diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 691cfbfca..79e6c73fe 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -247,10 +247,11 @@ end ---@param dt number ---@param shovelLimits table ---@param armLimits table ----@param useHighDumpShovel boolean|nil +---@param isLoading boolean|nil ---@param heightOffset number|nil +---@param isUnloading boolean|nil ---@return boolean|nil -function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHighDumpShovel, heightOffset) +function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, isLoading, heightOffset, isUnloading) local min, max = unpack(shovelLimits) --- Target angle of the shovel node, which is at the end of the shovel. local targetAngle = math.rad(min) + math.rad(max - min)/2 @@ -312,6 +313,7 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig local isDirty, alpha, oldRotRelativeArmRot if hasIntersection then + --- Controls the arm height setTranslation(armProjectionNode, 0, i1y, i1z) setTranslation(armToolRefNode, ax, ay, az) local _, shy, shz = localToLocal(shovelTool.node, armVehicle.rootNode, 0, 0, 0) @@ -324,24 +326,31 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig isDirty = ImplementUtil.moveMovingToolToRotation(armVehicle, armTool, dt, angle) end + local highDumpShovelTool local highDumpShovelIx = g_vehicleConfigurations:get(self, "shovelMovingToolIx") - if highDumpShovelIx then - local tool = self.spec_cylindered.movingTools[highDumpShovelIx] - if useHighDumpShovel then - local _, dy, _ = localDirectionToWorld(getParent(tool.node), 0, 0, 1) + if highDumpShovelIx ~= nil then + highDumpShovelTool = self.spec_cylindered.movingTools[highDumpShovelIx] + if isUnloading then + --- Makes sure the shovel is almost vertical for the high dump functionality + local _, dy, _ = localDirectionToWorld(getParent(highDumpShovelTool.node), 0, 0, 1) angle = math.acos(dy) - targetAngle = math.pi/2 - math.pi/8 - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty + targetAngle = math.pi/2 - math.pi/6 else - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty + isDirty = ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, + highDumpShovelTool.invertAxis and highDumpShovelTool.rotMax or highDumpShovelTool.rotMin) or isDirty end else for i, tool in pairs(self.spec_cylindered.movingTools) do if tool.axis then - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty + if isLoading or isUnloading then + --- Opens the shovel for loading and unloading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty + else + --- Closes the shovel after loading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty + end break end end @@ -351,6 +360,12 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, useHig isDirty = ImplementUtil.moveMovingToolToRotation(shovelVehicle, shovelTool, dt, goalAngle) or isDirty + if isUnloading and highDumpShovelTool then + --- Uses the high dump shovel functionality. + isDirty = isDirty or ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, + highDumpShovelTool.invertAxis and highDumpShovelTool.rotMin or highDumpShovelTool.rotMax) + end + --- Debug information local wsx, wsy, wsz = localToWorld(armVehicle.rootNode, sx, sy, sz) local wex, wey, wez = localToWorld(armVehicle.rootNode, ex, ey, ez) @@ -411,7 +426,7 @@ function CpShovelPositions:updateLoadingPosition(dt) isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, CpShovelPositions.LOADING_POSITION.ARM_LIMITS, - nil, heightOffset) + true, heightOffset) end spec.isDirty = isDirty end @@ -425,7 +440,7 @@ function CpShovelPositions:updateTransportPosition(dt) isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS, - nil, heightOffset) + false, heightOffset) end spec.isDirty = isDirty end @@ -448,7 +463,8 @@ function CpShovelPositions:updateUnloadingPosition(dt) if angle and maxAngle then isDirty = CpShovelPositions.setShovelPosition(self, dt, {math.deg(maxAngle), math.deg(maxAngle) + 2}, - CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, true) + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, false, + nil, true) end spec.isDirty = isDirty end From 30e6624ba029b1ebc5956fa216dcdcbf83eaaf72 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Sun, 20 Aug 2023 14:31:43 +0200 Subject: [PATCH 049/107] expand the reverse/forward change path of the pathfinder by 2m --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 8ad3724df..c644c8128 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -465,6 +465,7 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToStart(path) if path and #path > 2 then self:debug("Found alignment path to the course for the silo.") local alignmentCourse = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + alignmentCourse:adjustForTowedImplements(2) self:startCourse(alignmentCourse, 1) self:setNewState(self.states.DRIVING_ALIGNMENT_COURSE) else @@ -501,6 +502,7 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, if path and #path > 2 then self:debug("Found path to unloading station.") local course = Course(self.vehicle, CourseGenerator.pointsToXzInPlace(path), true) + course:adjustForTowedImplements(2) self:startCourse(course, 1) self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) else From 9f97f31253e35f2d7e08239a8ec0b6608a006082 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:33:19 +0200 Subject: [PATCH 050/107] Fix for shovels and more debug options --- config/VehicleConfigurations.xml | 3 + scripts/ai/ImplementUtil.lua | 6 +- scripts/ai/controllers/ShovelController.lua | 35 +++++++- scripts/specializations/CpShovelPositions.lua | 83 +++++++++++++++---- 4 files changed, 110 insertions(+), 17 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 274ab95f1..818f02d98 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -340,6 +340,9 @@ You can define the following custom settings: + open factor: %.2f, inverted: %s", + self.shovelNode.movingToolActivation.openFactor, + tostring(self.shovelNode.movingToolActivation.isInverted)) + end + if self.shovelSpec.shovelDischargeInfo.node then + self:debug("min angle: %.2f, max angle: %.2f", + math.deg(self.shovelSpec.shovelDischargeInfo.minSpeedAngle), + math.deg(self.shovelSpec.shovelDischargeInfo.maxSpeedAngle)) + end + end + self:debug("--Shovel Debug finished--") +end + +function ShovelController:debug(...) + if self.isConsoleCommand then + --- Ignore vehicle debug setting, if the pipe controller was created by a console command. + self:info(...) + return + end + ImplementController.debug(self, ...) +end \ No newline at end of file diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 79e6c73fe..ad93e568a 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -64,8 +64,16 @@ CpShovelPositions.KEY = "." .. CpShovelPositions.SPEC_NAME function CpShovelPositions.initSpecialization() local schema = Vehicle.xmlSchemaSavegame - g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelState', 'cpSetShovelState', 'consoleCommandSetShovelState', CpShovelPositions) - g_devHelper.consoleCommands:registerConsoleCommand('cpSetShovelArmLimit', 'cpSetShovelArmLimit', 'consoleCommandSetPreUnloadArmLimit', CpShovelPositions) + + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsPrintShovelDebug", + "Prints debug information for the shovel", + "consoleCommandPrintShovelDebug", CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetState", + "Set's the current shovel state", + "consoleCommandSetShovelState", CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetArmLimit", + "Set's the arm max limit", + "consoleCommandSetPreUnloadArmLimit", CpShovelPositions) end function CpShovelPositions.prerequisitesPresent(specializations) @@ -140,6 +148,49 @@ function CpShovelPositions:consoleCommandSetPreUnloadArmLimit(min, max) end, min, max) end +function CpShovelPositions:consoleCommandPrintShovelDebug(cylinderedDepth) + return executeConsoleCommand(function(shovelImplement) + --- Position debug + CpUtil.infoImplement(shovelImplement, "-- Position debug --") + local spec = shovelImplement.spec_cpShovelPositions + if spec then + CpUtil.infoImplement(shovelImplement, " arm tool %s -> %s", + CpUtil.getName(spec.armVehicle), tostring(spec.armToolIx)) + CpUtil.infoImplement(shovelImplement, " shovel tool %s -> %s", + CpUtil.getName(spec.shovelVehicle), tostring(spec.shovelToolIx)) + local highDumpShovelIx = g_vehicleConfigurations:get(shovelImplement, "shovelMovingToolIx") + CpUtil.infoImplement(shovelImplement, " shovel high dump %s -> %s", + CpUtil.getName(shovelImplement), tostring(highDumpShovelIx)) + end + + CpUtil.infoImplement(shovelImplement, "-- Position debug --") + --- Shovel debug + local controller = ShovelController(shovelImplement.rootVehicle, shovelImplement, true) + controller:printShovelDebug() + controller:delete() + --- Cylindered debug here + CpUtil.infoImplement(shovelImplement, "-- Cylindered debug --") + cylinderedDepth = cylinderedDepth and tonumber(cylinderedDepth) or 0 + + local childVehicles = shovelImplement.rootVehicle:getChildVehicles() + for _, vehicle in ipairs(childVehicles) do + if vehicle.spec_cylindered then + for ix, tool in pairs(vehicle.spec_cylindered.movingTools) do + CpUtil.infoImplement(shovelImplement, " %s => ix: %d ", + CpUtil.getName(vehicle), ix) + if cylinderedDepth > 0 then + CpUtil.infoImplement(shovelImplement, " %s", + DebugUtil.debugTableToString(tool, " ", 0, cylinderedDepth)) + end + CpUtil.infoImplement(shovelImplement, " %s => ix: %d finished", + CpUtil.getName(vehicle), ix) + end + end + end + CpUtil.infoImplement(shovelImplement, "-- Cylindered debug finished --") + end) +end + -------------------------------------------- --- Event Listener -------------------------------------------- @@ -339,19 +390,23 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, isLoad isDirty = ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, highDumpShovelTool.invertAxis and highDumpShovelTool.rotMax or highDumpShovelTool.rotMin) or isDirty end - else - for i, tool in pairs(self.spec_cylindered.movingTools) do - if tool.axis then - if isLoading or isUnloading then - --- Opens the shovel for loading and unloading - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty - else - --- Closes the shovel after loading - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty + else + local shovelData = ImplementUtil.getShovelNode(self) + if shovelData.movingToolActivation then + --- The shovel has a moving tool for grabbing. + for i, tool in pairs(self.spec_cylindered.movingTools) do + if tool.axis then + if isLoading or isUnloading then + --- Opens the shovel for loading and unloading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty + else + --- Closes the shovel after loading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty + end + break end - break end end end From 2d4c0242ac9625872845cca84ec6922e5c5139e1 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:02:46 +0200 Subject: [PATCH 051/107] WIP Help Menu --- config/HelpMenu.xml | 14 ++++++++++++++ config/MasterTranslations.xml | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/config/HelpMenu.xml b/config/HelpMenu.xml index 15eb062bd..9785ee2a5 100644 --- a/config/HelpMenu.xml +++ b/config/HelpMenu.xml @@ -222,5 +222,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 56c677f85..fe68114b7 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2496,6 +2496,33 @@ Anschließend kann der Helfer gestartet werden. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +]]> + + + + + + + + + + + + + + + From cb3ef13739dd65efca354b1db3dc9d0c3b58d459 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:06:02 +0200 Subject: [PATCH 052/107] Update MasterTranslations.xml --- config/MasterTranslations.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index fe68114b7..6992e540a 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2507,14 +2507,14 @@ Now it's possible to start the helper. Radlader wie er funktioniert und was er kann. ]]> @@ -2522,7 +2522,7 @@ So wird er eingestellt und gestartet. Zusatzinfos zum Trigger mit Bild. ]]> From 84379ec900d60e1b60f8bb99f35c4235ade6722e Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:07:58 +0200 Subject: [PATCH 053/107] Update MasterTranslations.xml --- config/MasterTranslations.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 6992e540a..afbbc689a 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2509,6 +2509,7 @@ Radlader wie er funktioniert und was er kann. + Date: Thu, 24 Aug 2023 12:08:20 +0000 Subject: [PATCH 054/107] Updated translations --- translations/translation_br.xml | 10 ++++++++++ translations/translation_cs.xml | 10 ++++++++++ translations/translation_ct.xml | 10 ++++++++++ translations/translation_cz.xml | 10 ++++++++++ translations/translation_da.xml | 10 ++++++++++ translations/translation_de.xml | 10 ++++++++++ translations/translation_ea.xml | 10 ++++++++++ translations/translation_en.xml | 10 ++++++++++ translations/translation_es.xml | 10 ++++++++++ translations/translation_fc.xml | 10 ++++++++++ translations/translation_fi.xml | 10 ++++++++++ translations/translation_fr.xml | 10 ++++++++++ translations/translation_hu.xml | 10 ++++++++++ translations/translation_it.xml | 10 ++++++++++ translations/translation_jp.xml | 10 ++++++++++ translations/translation_kr.xml | 10 ++++++++++ translations/translation_nl.xml | 10 ++++++++++ translations/translation_no.xml | 10 ++++++++++ translations/translation_pl.xml | 10 ++++++++++ translations/translation_pt.xml | 10 ++++++++++ translations/translation_ro.xml | 10 ++++++++++ translations/translation_ru.xml | 10 ++++++++++ translations/translation_sv.xml | 10 ++++++++++ translations/translation_tr.xml | 10 ++++++++++ 24 files changed, 240 insertions(+) diff --git a/translations/translation_br.xml b/translations/translation_br.xml index be6bef151..944682db2 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -822,6 +822,16 @@ A carreta pode ser descarregado usando Courseplay Para iniciar, o marcador de carregamento no menu AI precisa ser definido. O marcador detecta montes ou silos e os destaca no mapa. Agora é possível iniciar o auxiliar. +"/> + + + + diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 6610bdefa..d5940bfa2 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -806,6 +806,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml index a3a918d83..f228f09fd 100644 --- a/translations/translation_ct.xml +++ b/translations/translation_ct.xml @@ -805,6 +805,16 @@ Courseplay能夠在青貯筒倉中壓實或分佈糠秕,或在楔形筒倉中 要啟動AI選單上的裝載標記,需要進行設定。 這個標記可以檢測到堆或筒倉,並在地圖上突出顯示它們。 那麼現在可以啟用小幫手了。 +"/> + + + + diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 7fef8dbae..0054bb17a 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -803,6 +803,16 @@ Nakladač může být vyložen pomocí CP. Chcete-li spustit nakládací značku v nabídce AI, je třeba ji nastavit. Značka detekuje hromady nebo silážní jámy a zvýrazňuje je na mapě. Nyní je možné spustit pracovníka. +"/> + + + + diff --git a/translations/translation_da.xml b/translations/translation_da.xml index c00a87de7..4a0da7b09 100644 --- a/translations/translation_da.xml +++ b/translations/translation_da.xml @@ -816,6 +816,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 69ffb7756..93770f193 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -829,6 +829,16 @@ Bedenkt dabei, dass das automatische Abtanken mit CP in Fahrtrichtung des Laders Als erstes muss auf der Helferkarte der Lade-Marker gesetzt werden. Dieses erkennt vorhandende Haufen oder Silos und markiert den ausgewählten. Anschließend kann der Helfer gestartet werden. +"/> + + + + diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml index 2762891d8..858cb1456 100644 --- a/translations/translation_ea.xml +++ b/translations/translation_ea.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index baa42d9e3..b1bf7ddca 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -829,6 +829,16 @@ Keep in mind that the automatic unloading with CP goes in the same direction as To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_es.xml b/translations/translation_es.xml index f738f12bd..921039eb2 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -814,6 +814,16 @@ El cargador se puede descargar con un descargador de Courseplay. Para iniciar el marcador de carga, en el menú AI debe configurarse. El marcador detecta montones o silos y los resalta en el mapa. Ahora es posible iniciar el ayudante. +"/> + + + + diff --git a/translations/translation_fc.xml b/translations/translation_fc.xml index 910fff456..4c3014c70 100644 --- a/translations/translation_fc.xml +++ b/translations/translation_fc.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_fi.xml b/translations/translation_fi.xml index 215325630..cbf81baeb 100644 --- a/translations/translation_fi.xml +++ b/translations/translation_fi.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index 29b037550..b1c0c8bfb 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -794,6 +794,16 @@ La chargeuse peut être vidée par une tâche de déchargement Courseplay. + + + + diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index 5c0a73d12..f1e87e787 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -812,6 +812,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 947fe7e9a..6e23a5e16 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -818,6 +818,16 @@ Il caricatore può essere scaricato con uno scaricatore Courseplay. Per avviare l'indicatore di caricamento nel menu AI è necessario impostare. Il marcatore rileva cumuli o silos e li evidenzia sulla mappa. Ora è possibile avviare l'helper. +"/> + + + + diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index f73714129..a5720e9bf 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -812,6 +812,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_kr.xml b/translations/translation_kr.xml index 12cbcccc5..cf23f5518 100644 --- a/translations/translation_kr.xml +++ b/translations/translation_kr.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 69089d31a..5efc6fbe4 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -812,6 +812,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_no.xml b/translations/translation_no.xml index 80f1b0d33..7a0f717f4 100644 --- a/translations/translation_no.xml +++ b/translations/translation_no.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index 264d59ebb..ac53e7c7b 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -810,6 +810,16 @@ Załadunkowy możne zostać rozładowany poprzez rozładunkowego Courseplay. Aby rozpocząć załadunek, punkt załadunkowy musi zostać ustawiony w menu SI. Znacznik wykrywa pryzmę lub silos i podświetla to na mapie. Terze możesz rozpocząć pracę pomocnika. +"/> + + + + diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index c62f0264d..a767432e6 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -807,6 +807,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_ro.xml b/translations/translation_ro.xml index e56c21353..fb5282a21 100644 --- a/translations/translation_ro.xml +++ b/translations/translation_ro.xml @@ -815,6 +815,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index af5aa6612..79d07c5dd 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -815,6 +815,16 @@ Courseplay способен прессовать или распределять Чтобы начать необходимо установить погрузочный маркер в меню ИИ. Маркер обнаруживает кучи или бункеры и выделяет их на карте. Теперь можно запускать помощника. +"/> + + + + diff --git a/translations/translation_sv.xml b/translations/translation_sv.xml index a3b8d6baa..16b167639 100644 --- a/translations/translation_sv.xml +++ b/translations/translation_sv.xml @@ -810,6 +810,16 @@ Lastaren kan lossas med en Courseplay-avlastare. För att starta laddningsmarkören på AI-menyn måste ställas in. Markören upptäcker högar eller silos och markerar dem på kartan. Nu är det möjligt att starta hjälparen. +"/> + + + + diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 574be7dff..74aba105c 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -812,6 +812,16 @@ The loader can be unloaded with an Courseplay unloader. To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. +"/> + + + + From 4def8fbcccc8ed25da00e40908378c700520e61b Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:28:49 +0200 Subject: [PATCH 055/107] WIP DE --- config/HelpMenu.xml | 2 +- config/MasterTranslations.xml | 27 ++++++++++++++++++++++----- img/helpmenu/shovelloadertrigger.dds | Bin 0 -> 524416 bytes 3 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 img/helpmenu/shovelloadertrigger.dds diff --git a/config/HelpMenu.xml b/config/HelpMenu.xml index 9785ee2a5..f6667d36b 100644 --- a/config/HelpMenu.xml +++ b/config/HelpMenu.xml @@ -232,7 +232,7 @@ - + diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index afbbc689a..6068736c8 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2499,12 +2499,18 @@ Now it's possible to start the helper. ]]> - - + + dM?a_^lp_qJUVgvueN?Cg$t<7$cqMCAlQ%t_)C#7y`f^pD^YGvL4I-=}$rU}N<4 z?7a2V4ZoN4_wUx0xr{8lu*BB^KL_}Cfaifba~^P&)fyXHj)ss#zw=KcySH~2pzS}2 ztToz<47@&44l;u_2HXC_jmU1 z9^YSle=+Z|-hZ?Ce?9jn#+d)p%g=!Mfcb#=fcb#ufjfI1!2F+HTt;*9jr{dD`+fYo zzq5b$`2OPii}gFK-(mhw4Nq@2pC@~Nm=Bl_m=Bl_cpkX3=K;)r%>Ntt6Y%f;&i>uw z`-|@{*6*->hxw2BkNJ=10jv*TeE{nNpTBwFZ~Qmb8ri|oK{Q6s!6%XL&+kXuf7BS~ z|4lDF1M>j$0Q2DU#{>Mk|9t)KC-{%<`_R#$p-;m73H{)scz?Y+!MzkN+5z`O0;Y-n6EW9K;%ygko%^5zZ9|LMhL#Lyf0>u>h^pSRxu{(CW(uzm`D zU!y}tTwS;BOqqisSem|bnP%UWYLiT=J?W6=lXnKJA6-#xbdo%8p9vis>8 zzwdu9$FHApwSq{PG2?ps{^Q2j{+}9d-z+@<^APh8^KkO)uG{tZMdU&*&bQ5x7pIvpTJQ7hJ#X zk?oP|CAtp9eZ_q);QtJGeH`zv{~Yrl^B?o%^T(6hm2>#KZmi!<6aF^>k4U209z8yq%QApWaV;bp-rF_+%KT22T>gm=5CbfH*8NHV+| zZ%$M*kJa(IHrQ!O#)Fz4=M&f(|##?SYy=Re|nGVu0Q5@d24 z_^EqiO#>HYMI>`NREc=(eUJC9IM?fgnrvjh+e4Lz--MH;`;cAVSU?{a5`?Hir9zUB zgz0+Ve5qPRFV<&qJ)Tr)N7-gYSv^5qE);V$CQ}~T&XehoBK|i>$-T&r;DZ<9KaWR} znE#mn@Fd+f>-0>PUV!Jv8_$pUy5Q?_C+kJGivMlES4PmMM)rMGu!3$w_Wekt8~ODu zm8!U3e>lpr<8ga;5&2}2-5J&N0p=~k8r2Dq6?FLPq=F!5zmGGZ2D_eRh%@S{B)W-D zNJ}+{t>;RPi7A_3vG_d`>&~_4D#Isxi*{GP1$0CoI8=g(OT) zOcUhg!8qMVuLS=-%Hy9IkK6m_4}e`C_DVR&fA5wc?w?U7LqcCi*(S&fjH>49;rq_f z$}?cs6G4_;o%>R$>`!`Vsfb;*Sm%f2HhdFmAa}Gb> zH-5gSj{ka+Uv*rE?CR*~DCFh$i2{8l6Y{?s%GIMOzcV zBIYG}{X^jYx7dp-_ZhRe9DY+cz8`{<*l6U+O{M_E4a8inR&y2Q31&#W&FK925{VR@ zZxhM~fOr7Z1y0+1Ky3d{FCI^ppZ~-5i|5B1&yV=J;OlZH>qV2z|FL|&<`=>4r*x*g z3S?Km9BPafkyS}SlUa@O@L%vPTC~qNQ|yuQ1RZ55Vok_GJXTVbMza=Yp>k>5V-<8x4*pO>)_*<`)Y6tG(o#K^xt8cY$3xDpRtiS&zr zx`0OCTaxLg=Yswqn3v({fcOE2!zUY!yD78DU_U8?@6oD!v|oXQgm?h>0gev15&tti zp1SE;7l`?f`41j4?Ei;8pm*|oj`bCMe?QIhJ=y$^bEk2z5#{$q&E{d`_xC}4eKy$b z!v=!^@jtVUrkl#Dk^};;zoRUeMG$;1^4AANRdK)lr9MkC;2$llB1n##?ZX<@eL}|71onKNus9gBptyrZlV`Fi;ds(iINljGhr=QFg5SYp z3dZvRJk2k?f&2;*2`NSAU0KPpC?8NDnb$Dg@&GXZG5<0DG5<0DG5@EQ|6_G?C=O6k zA_abCla)Q!g2=C4!5n3feXT8a+M$l0YmN?^kpCUjm8r~dmx}olZ&|) z=-)q640Qk+e>_hR%K3QgX+m}j${!4O=9?Bm zA0UAsQCdW1lf}iCcA&mMYLlrHvIhz2viX^fMw^lcud6%zWo8rPC4m0W+4<(1$e+Ly zjPV~`H^hIbbo%81VE$wNWBz0QWBz0Q-wOUu*vqKyzv6L5f;dX6H0u$M&7tDrg}`aR zZXRU!#r^snWtR(QNCf0Q$kT`Shmn5(HeU~V0ugpg1=(QF?>%$871{ZD`RDQmATL15 z%aEcvKdGRW94fOZO}bzZ?GN?&LHo~uety7nAz=xCK7fv=(H?<#0>lBr9c5hkJZU0t zpK;F8QqVV$UXY)kn~VAkB}#eF7ZBAcju$v3JuB!RM9aqO)et*b(!fMWi?>`C&cp)0|pZuPeiEvoo^ufKaq5MP=8@t zb-FvA2jF8_6wh!;9iRuaQ_0B^&}Tl_ALydYA=|e0v2EACf2+or|Cs-n|Cs-n|Cs-` zlK&_U@S?Zb2mB=0<&~Sq?CLT8qqsZbe`abO-DvEW2?RW_T}Sf$|JUKB_waeFG8|5dYs;zy31HGvG`8G&(Lny~&H>70EX6|3my@ zxz8Ix{tRb*ZO!#uM}OzHI=8l< zc>p2j%R$6v$iHtz^?7w)I+y>raU;a*rQpw>t2gvj*-#$$tsn1HzmNHk`H%UJ`H%UJ`F|_<{~q_ORB5Rp zh}P>JjYc6KFhOkS3)UlhT?2st)Q?BHtga6A`{O|0osP1@lsRa(YzBWm4T^vm`uq6l zchlnig8Tz~WT#8Keu(pfoi53U*X<=sTqxgPB&rX=I)EasJ!r1{!!u$oP17}BaBSrI z!yZnTXRbcn%@XS!jFQ#%a<{n$)T}>DyyWR9qv;_c;+-khXrnORm_*Y);up^(i0k|u z;@@5>D&FBwZ>$)CJO-9iKhom>NsPYh1<4SjEZZ)F_XRtKU`*!@J7g=tgFzgA@9+O$ zykN55$Nb0q$Nb0q$Nb0qzm@!-u>X-94UNFMIz^PG!fZlufFafgb%7hXra-^~{&r%1 zmr9G)@!`+&0*%k8?Hqx$3HtqNg2+$5kxO^Ah?>fFQ}jqZ63&$Z)$gAV%Ium-_Pg!e zr7Z1V^2g4gte5Vv-Q8lr8Ff5mVxa2O~l^{Akae-^~w-G0;$5c&gwzkY2=*RgcYP+3#i!oE|5sNcWI z(v=mcj_?rW zYGI}<9)S6e`H%UJ`H%UJ`9HP%Z=;f{`$8yA&L=_@halen@+e!0;{F5^YDD?;%uyD& zpDj+SPqvNa1G2Fq^1R4oueKnsxi%WKABViXR8LO@qlCPG47Ug61qetU^aqrdSVT?} zTKBJ!%LPBdT)oCZh>#4Y6P$g;st51{rC4z>z8#XIEzxvttTuKL(pG(u56%L9hpy?bW{oBGe z^Z+Emu|U@-K*HDszel2X$Y8Ve6L$Gj zNz8xDf6RZ(f6RZ(|Ec7EocmSBS0K&@het=Dj!rD93Yv=`A5Y|rYEXYY!sZI6fc?Kc z(mOiN|Dg$qwu3&wCX}~tGQD~3F|hONpz#l~|9J^jt^17iN%p`s%^6r9kbw1rQNA9L z+~i*ic0a+4YS%(Pzu_nwCgmrDXTI@`Z=k+DrF7VHQnry>BNit-t`Eu_?o=sc|I6nu zS#s~(4eitnc+K5#SQgM-4bGA8B4gV^{5f(5<&O;!O-6|8$E5D0aug3}4P7pTanv5D zDZB_{g3rLXfOY;%pr0w;`j@|K<;r)Hw)*BYQst z{%*9sFSOsuL-xGAM-@Rby(!j(`s~2^dfqtqN7-v*{6}?x^+JQGG#=M~`B(3DH9|ii zDHP<#`vKbn#WwKw!*aqZ=;}jsZ*wIeJ%Oe*7L*Sdqr-yBh0qru>}9#~^TKbOIdiE9 z`u=&FY($!TA=w~4c<`;xV#S3^`tx%)^vjk%c<*zIH(Zn<{!8^a%JRHYT}?4pK}TJJ z8sNHikRj%7Sfr>CxN0w}Azv_B(Xv+GF6%2ao7aG!fM{wg*sN^J5)DU{jQVZi*1uf) z3$p*W`NQ*DI){vR-Fw&9x4}w6-}?TyzBy;+Wf@B;3rV#c>D$h+D`Qk z`wRY_|5nmjoo?Ti<)F4lwnwg)C_kaNuei@;T&+OYV`5$3Pb%^Hztc%+k;F*m~-13x_U11{cPfa(QDU-YCF!FJxJ z3_%guCn{hVsQc>^h6n4pyP>Wi_5RT5ld?WhZLK%1UqsD5#VL@8Kte&9+;~? zqx?QJESjs|tgPgxGyC>zR?-;})^v6grP6)_q7W%!--6}rTb)vnAhXzA~D88VC7g{%=zB;*j4Q#i5eXi>L+cATmK5yXf`G1HyI&yU6dhSn* zr;7iz#*z|CwVhLIEH*OmnviHN`Y3x9e|~eX51#KoAM-uF&-gy0xpU$hkjm@QA>&zP}RKSypVH6MGt zq+RBZRW9v-m4~2Q;Ob)FeaO`u8&JQl93BqxA1^thtg8zzkhQzi^!IC&t(q{QX1`C(jzl+k+ngFVQ0M5nh01*Gz zD6G`)f4}ATgo5&y7 z1bKw79Q)Oq{jvmc)p>WUAJ!id46sLw6>YMg|D5{y+7eQ}um9xg&5CxJDwnx$@rF6_ z88Z?RE~|6oub6^=L3#hZ|C9NmRBux@x`$(PH@K9l&|3p*K@aHNpPBer$?M8{j~(m! z!!u`;_h)3Je%rcOe^>ASz4wd9jte!K)2DyDXBFwERs5Dtn?fSVT>k9Yrxa&*w_ogK z@2jIVI}?&RPEfgK#?IXvkNf8X?`0b{Yyj{7tFUfjax#hoEbH#>MiSxxd7o7exP9$^ z%>UcL&w=OpJA0nT_ZQ#a8|ihE%Kt&&f4tAXSsmxG&*xi+_&Oa$Yee3Umpz;xCy=u1oRbkFvwNAP(_$_ujp8^$yDGUAF9< z=jSX{Rc_HS;OCF|Y&}x_Nm)G+3MX#JQM^lBiG8!ASP>&)s;5hG6h4BX|JhosV1st= z?~DU#?*JkA&tonnO)punq&-v^t_)cI>6z8)|B!v@U*7f3kt-D6RJ`;2V!c%A^^Wvh zR^NN?g3K<@D)H+NKm713kpQf2DDmze>NpQ84QYNdB>Jv7SSz@{?xM_Jpjv+h@(F%; zrjBj^`Cav!-KRk>$hr_2ivt|Jll=df|Cs;p`6Dr>FsE+BDg0ba&T}!T{73db__xdR zH5KvrzmJWf_`adkWJ2rif%)%1d3*v`Uw5qkF2f*M9Aa9Az~2u2{-Ayz`~jM%{V;{% z|9&T0AAqJSIq`LUX^-f#Ya@47D$V*mnF!{z_qGb!TC1zkN)v_n4t zf*>pVmVt`tU9}!vW#jA=2`o5 zz9bbxA0V#$wBmFK^%eAbUiy`~U$Oz>9g>Lm5fbqK@T$6#lv|pb>Wf_1<4WKqvXQTy z-CK2Bab}N|$}cZ2jIWbaU0ibO=0@mjt6AGN_an5F8o5NFFs`L+Q*(P7+8vD)F*wfZAev_{(OQdT8t z&k657Y;B-?%MugXteX`&-4>He8I-+nkjXe}49WyUUA;-=yQ!}{%`W-zGezVUHhR86 zd04gu4*Wzr)co;BQlT@K` zg9`c#h}j*!pe}GdcmY0J z-(bvt%>R$_Ghj|(PTkI&!q4aAJfD-r|1nO5ApQ^be}!p$oj(@V?L+)GMUg!}Khxvi zXKbfnoj>T~1MBZNk=>8h;Thxq^|I+h-j6a&2;~vzET=1MiX+r9<(hdXt&7MAfd7Iu zb?XnHfzPU~ugOZ^9F3e&7AwA%>6W^byUFL1($Y37XNq?Z2cTczZmNo=M^U~WvGzsi zBbX}wb8lDX?7geyvv>(a`<^z1#S+ymlD{f^*(s6T)q7Hw@Y?1AuKX}Hxbbh=*G$>e{iG1Q3oZ#J3Z{P%%^nGJD!L#eJb1^V-Y`M;nM z>~J=Sj(COFGTCrGPnB*L;P~}*&xXt0Z%nQ~>Lg_VC2~GisZ%!8HJV12&PsU7ij4_pUSN=h_sj$WT>CU9FfYjt$zyGhDPg$RR_Sv*QpFK&v6%9Fs?GMHM|D^nsjE3zuE1a?? zL(#Wf%9FCHs+OPH6pv8$#;ArXhrWe`FEdR(Q(SLp+J^clyvQ&IJ}vhrw#WR({Kx#q z{GYCz!sk6Xddp<;pBV)H1Gl1E48!C7e_Bvpo~hKJL-G41&COl=j49%={J)MeC-YJE z|7A7IU%H_(7gP>9ArMR!Jd zUg%%Gul9_(SkdfuOI)x$(Ym5^y(2{&i+Pvtg}(i$A8&sa)F&3b$~|i?i@^EWY5IZw z#0{2KntrSEn|eOK!T-n3H44Hs%#8F5sQ=H5gvOto<;o43TveK!D`#pO50dgnNTKlV zFSwLk`A?5MeVCMIlX?01xoMz>EWc-4LA&g2(GQCr-O(mX5yOj|;Q6POnE#mnnE#mn zn4^=!(dl|!Czt;yPrZmV4M&40|1ZME!qCr$8wltEi2wfi^P5qBelY(Nk=@_ma>e8R zAGGr);sKi|9YKhiKwqG9OARSd|JM*HKCqCSDGs$Xg)Ukbisz>$?o5IFc_$N6w?o~2 zUT)YU08an!v7a>|yWcess-UbX;>z5}Z}}U7S@%||o_@@@iG+SW6$ZsD@zGzqr5E=+ zML|6xf$aXCE1i}|Cs-n z|Cs;Nl~ee-cuzfWqYa%z+*;`v<~)n>(c zVVA0`s#pP>rhP;)ybtyHcBBV@8UXc&3F3R%%I?>J`<$IMzkOW4o8mMLxgRTmb%=(w z8!p58K%$>|*vk-q$nQey2pyqp2M<1JQ})YLe3uR7BWTO(p-;govUiDZIm!ckOrd~o zfbsmlqZ3^Jq!RNV^B?mc^B;3`ayU9&uj}OVAL`f4W)tGS^S^^HBmcin7YLxfy$ind z;4}M-GsW|y(lPtL!TCP>AFb3A0Pd%VOH0{U5bWaA7hBTOF^(Bc$WXZ+D)RFBGdB8G-?2f_R?iS8r_6FC^0$wku`pZfdl7 zS!PlBL;4kKg29K%dE$!EmisU4nH~4{U)WQm*9Dx{W^LszC0bjdFThN(+5G1rWS3|9 z&6XT_in#c=Tau%II)GUOv>k(OMBClpw`OK}?ws&b?EqRJ6ee;>GrEE=G zy0SD+^L;(d%6_zNedUw#6;eVl&v#24F6YO)dg!XYf;9$ z#94b&%I(fj@v8DyORk-Zbj{hih}4-bL)P92p|q~Dm;~-St0e9;`P(9XYDQzR;*^YU zY6i}qlrbF3NRA>X+xN;~j4Kbysvc&3jp_(=B2je?@PGandq!>GA6R~*xmgN*hNEeX z=F93;;%F<=-Jg>q@9mD>vf$9{+kV@rB<;2qoc6?FYw9?af)gd&FX&bgGks3Y#dph5wDb`B@;3fSWh_V3!&0r`EUPv{HN));zH4@ z)|pFd4%Ra>x3*E1R$XbE^{|2m4S(CLua@Xa*O_)(9g0w~`9Vf`LU^L-r7pzr?G;x~ z^vlkRq`vg@G_cG4NB+7z2kHs7N9rpP|6}P)Q<}VA78|`*f$VnXa<3N2^o%B7f7Wg) zH}{&3D^DT6^vv^boKZhQZh7b6&K$*IN-D9Xqwn{vSyQ+f#(g>qvGU2F%wJcRn#v?^ z4mH>L+n+#sfNK4ovLZ5Z@0GGeP#3sfMQ{70+@IKPn(*I=`~Tk>J!QJiU--N+&v4&@ z&#u=_1^-R3;2+}uXfzrvf`0ruL#YAz|1}kFzWG?m+ajqu7G7Jjk!!JsD#3bAI_7az z?WWEPAN=>L#c18$Kih1uO3x$Y0k)~+uzr@fxA(dEZPvGq!*7}^7H`-@-R(Paq*eiT zvU#*(Gx*&Tc^uAwI)UWR``UvKm+YqcMy?gCDu0xG@wr8rhk*0t*EE%{mRLzsdC!Zp z_O9AmQ_C<8ct5{f(|K5*&3*aYwcuf@KB;T?;6bE+@JaWPU{-z7v2*r%66*bH-}ylu zs{4Q1CoUvG-w;$adK{a%OSg6JU$*+E3v-$hqTB2L2e_@$KzE}f)RrygEhwKa&$Q08 zR=Nq%DcLv0-g|EFJ; z3$Gw_1cWvQpStdUI`Q8LFU)_;e>|Um_W2y^Ls%dBG{2w8=D$^j`s{nWasH1E4i`aQ zoXHT>q4oRn^YbepOCGKJOJ_s1dugRV3{~(+0*RXhUXtdPdQQJ=Ch28rgTbsvNcEB{ zBfHAq7A;wjIV*eXeI(fLY8T|k)gCxdpCdmk8=QB3;IR^}JjV7)-!^Vmrp`(fSoJf- zl|RhSvz8Z;sfmeCzFo3#sRau8ca^7zUtGRq3B>!$>+Ac9As;}_J*_a*fWQ9bo=#Oz z_7K@KeBOOTc3$|BI?6@y2itGGOqzT*<^JpFk{sZ^%42h)c)#CJ9Q;!$7xn*1-1(#5 ztnvLd))uhCItJ!YCOvI(YEahIScTr+j3;Hx4#JJ;j~d{}1z@Akc63qx|%f^*QDS z=EkSt27X@djOS&t`H%Y88i4n4|NrplHMCBzVM{R72mG(dE6+oD0JJ|W-36=QhP_Ra zAMeQ)$JnQRYxR#%&7<}Z^u?jhaTo^W*U>OL7VA&RW{t9}4dU-x2?p}yN%@~GY&`$2 zpgzJr2J7@B`e-`Gnxi<#((`Ne2Zedj`w54&MtLBnqA%=mP-b;uiNk75%1BHUwCzdR zx?;r&-N6#*@1xVT_REe7znL59I}CAufwcKq6o;tSDVU(_gs{q$nSuNek`~5{^nk#d zb&#*uFB|DD@3x!zzbkb&G*}Px<+Vk8%inHT_~iV1WIs8)k^7Yx`|hv$Wt%AdH-6*m z2fZdWbIEga_5Cu9_Lm8BH=LAt>ljN=wu$5~niV_L(4U3)Kf7h;ftYLO&e<&|vU3q^Ptz=T-#3As?RmZ*^8WL;zWsU)#Mu*g zJl>k}rzn=nE7e-Mu5>rp{TVOxtV8vF7LG*9g?xYQ z(6H$+l}gT;5eR@CPd3k5=0kOe>FF&m45KRBmF|093cCz<_= zod@XdqxG|G{W8G!dbHnj`T3eTkWT>B2C19DZ%{CFWpy#g`Sa(~!K_u}^7)>wLq9($ zON4a+?>m0z*Xh0&WpnD=W$SZQpE>S7p(kMeWBz|sTmf?hbLD1o1wT*I<9V8V{#&WQ zRlD5^eAk%P1yCIzdd`YKA2>GH*n;!|;J&Y2wvxQZw;_b=;6rH%iC}+=dV9`6-hVb} z(#g-KeJ54C4EpMEm(CRbM~kN$)y4hrhZ@ac3heRWH%)UOAMeUf8%1A1fH2Wg6_O@A{I9Jg-&i~R^OCF(0uU>`yoDeF1 zH&BCkE%iJHdHhvLrF}NZ5u_KSra95^(zm<-aex$Zxl7Z`g+2hzgAMf;er>J!(}4pA zRv&LW06f2EbxT{r(I5TC*VmX2i?5ZJS6IRR&&|zSH%DHdl$yC9W&?eM`8q$mSbtu` z5K(O!?C)34Eb&_p^vhB+>*(3XgISg}I_(@-|2H)J?z90G zPt|>zeEuJz;_CpxIsoRNg>Znq-phvK`T!mF5omp&@DWM+E?FBDYGDVFe>~%BUt7{I zJ0V(ETB<{Nc7Ofl|9)o{@KM$I#b?het)!+m5JGizZ<)4iK|D4YilI(m6FE@R@Z@3e z+iStz<}5uSj6G2U{q!6%ZDqx^8s$O~@SmFc%Nm8h;0Hgru;;vzNSr6Qu;+x(9x!Pf zQ154g#(m)LPZCHyVU+i;F$H!aJ%EhSzD*RYGjMcS;t}uzoY(l8k)D$2T@HDGCuQb} zEu)8iepJuc2DwXjQGuWV`X8MTmabc;gSrBtG}Es-3CD@N zVRom8>xID>^yZ!N$v1J}dVpzK8jb`H%UJ z`9D=$!N-{%dd5`npBY5!0KAvCKBf=sA_vomYLp+C&e-F6fTeFxlmmTrm!#J|)t~jY z2-*b0y1e}|_bxZzrQAfRVl3f={P|+D7W(jQqReKq9rEeKm6eqNluv)@wM#CpJVC4p z)jGeSPawUS3l?lvw9BAfPC2swQ&Ur=kD6$N?4Wuc+-uHy*97R=<*;IID6IqpHv9x4vf`0$YWWMMwDped>>N|?`j_Ncf z)!K06pA!TE!G+%(`g!=*%Z^?6HTNq}W&l+IE6L&E;i#2bC|;vG&P@>4CyltfOf$t# zCb7Nsg7YG%cc|`rW2X3^FzR)?P~HJ&VPY!e8Bo>rFTV)ahfJtO*Xh0Mcm4JJBxB5f z%zw;(%zw<~>A~fxx=*))|6{zeLjRqY^AAy*C?*tQAwEDk?RMtd)?L)LZQBwr1FwC( zFH|$i97S*WXl%10M^Sk+nUAE)Rv(u~8GBj{^u(9+LOlR%cYh_S3&>H}V=OC$`uYWx zmF#Bl9}Hg}HQP|#U1H*_A&3w_9zo{M_Uxj*qgCai<1$PmGa=KUZ}8Ca-bxbee>!*F znPAp&VTRXF9|rsV+HjA56|6TT;=MMM4buKI-vH|46N_E>P1~RcL0k>JS$68Zwv9vJeT%IcSOYPH+v6hfsSy}bbS|AV&u+IH}7 zupj>ORrZrWo4}{`Atkt4s~p{|P*)r$K3@bcS5j#PR-KW|MN8vkK+w z(W?AX@XIGz$))5c&ly0>dB0?U`$@yoB@sx|w|u z)iDH0?M{cRolH)yPX45vo~-RK|1tkD|1tlkiYxdy(?idga{dF~3Fo`cHt>rxN4tAb z{+}h8WgReXA$((T0B7)T{&>Xa<8l9dpRmFrBCODt*Y6@gZ>UdFL8mm3LVpvICxj{| z!EK`!l2?une&GAy-%m&T*ZKq;bUc;~S`Nh}8~S_xPxvDaCg}o}`2J&bZZHAj^}Qk6 z(euKMNtUYUn<#F7tTBVP3j6{YzL84E=M!@Ii4IgZNUS(5ZI>++?-Yw|lg0B-yI;(I z%zw;(%zw<~>A~fxx=&Ni|G&kvt>kIs4@Vpx9c~@xFz3U0Ji+A;mwW&{+Fumv3t>NG zeLeK`hw<_ZLB@@EIhKcLB|~w)d=WJ&Lh*JupXzZr2ok=p9YSO12gIOpRgx+?CM%yn zvOb9pBY9AGp%x_M^^+VV4^f{aFK?>0n`-{MjJ3uBRe`G3XfrbKx}k);P{2<6|~H_GKi1{P5%5PMn^s^Thne_CL1&@qGVz=Npvk!@%@;Z z`*FMRzhbCOM*A~8JBD^qG)tuK7_uU1e<1?zYfH`hBkk~cDsRV-Lk8vY%(2L9Ijt%^ z{)gvb?4Lh>Kl*-r`-?KVl1RV1AKs^P^L7luc-w0=>-L~AJ^xr_j+_oB*yH<&66wd# z{$aZZ`r&xt#KZ^C_f&bMKZtKXfBpmg@HvdfdH(ngbR62lyQ?40r=Yb}*zTZcIB}MR z{l+-KUdU*qD*}SMdqL9vCHv9&h6ThnH*BYZ28HhU`}5)aDEPbb9I{u*i1=i8~7Z$IbPSzBB7I?4yixLl?vVK}TR(VAYuSXg>O zz7FqK6AbNwe4uJea$;@S8MJ-#=lr@&TAsq~#{Wg~A}RwWMcUma%Y;Vy$08SHdGK1K zuu@mUy-Ru`yQrOgg@tIlH=xhYj-f?zUgE4-Uv;#}7JTuGU+87q;@iLE*+pg6)y+pd z&NJoZvFv8$Yw&sjxII5JGxb=+Av5LY=f3m2mCA%V|6ZsREG;Z7e4rot55NoYUw6EA z9?}=!G`@Iu|4Op7v~=AVM;iz0fa5zsk;v~~T(UAND{IG45tRuI29chUpPQSD^cUzh zoLc88BJcZ4em>{})cr7i$>WHx2UGx`XQn3BAiW~Lv#YKLzNdx16Z5|~o-cU2aPjkU z9(aDm{729F&3_IMzeAzRWhF+JtIzziGM+wAp{3JHczU8TvYiCUK)v^bRKVEf-|lz8 zb{R3hAL#>@ojmE79?;~z`RDifzV8$KZwG&Rq_-)(4P@_*7W=DNXxv>nSv>l<{UZJY zcY*(7JYOUS4%gMyp-RC*0QK1ST}Kh$0jqKTe*s>-kwuDOB9QmO&?1Gz!q05!bjb2@ zRrxGiGtPep1>2{#bhaT5=c7Er))f{4z9%nSSy-4q&VS^0ShVE1F61}pJoo1B#`wQ( z!v6o8pB?cZ@SnwkUIOj^=m+}yMEQ22=~yI9ktz@^de-Bh@?imA>)Ex?~tK&+uxIIGJ@aaP1m0)WNa>Xt&yi+<_&`$ zP+Z~+HeH#l^PFb;t-M@jGxkP?Rv`U|NoES-vYMekS~iY z^>UWz_5kXC$4N(&Eh70YPZ;&BBa*A2eh&Gklc8=O=?BfVZIGYe$P}+@gYB5)k?rr3 zl+DR@!}t?Rh7owi^pD|=3GS`Gj{go=AE@tCax&s-;R&Afg$VMuj}rTb=ExIyRk|;L z{r|-U^XK=D`wNi$4)(wQ7$+YWj~o~B@& z|Bpd`z&AiaKz4flPF^CaC^VT&*W2ff`TY;a`JajW`VfGK$1NZL!LqaEsfo3Lcb<2| z`wUL-|5?x2en)zCQJfe}kMux41Rv6C=E&`!03c2u9ccWUelc-9ZiXDIcRWNL9XUFJ z_J`yy(DOk3j$AKM{Y7zKai7b$S}_A&uCiKV_xA3BPb^XWANK#>Hu3$B(<`Rg{5J`9 zD(3&qwf~XbAB`d&g|3#WkzN1B4^_Q9{cO2C5HziZuC2~!Zth{|cg}3{@=*VjLAs)K zwp>J{`~3b(>NXi)KuVFHpt(C7d%cX*XUvm&UWa)I`nR0^{5`L5Jwq)m$S)!aRA`W1 z)T-^GN&Rbu1Y>IN41vJwhaL7m*9eWUj3V)z!^Qba)W=eaof1Hdv?E9w_fc zdIGAm&mLRPx2lNDCK;xM9v`QVOYVf6_tez4y`PVn#d&j8n9+~oy5aT`s4)#0Gfx_x^mws(weANLo)`+o-h>OcaAWi=q% zWV@*EF@uc|hnMmEG~MZibqG{69a4i|9zOp)Y?lb~v>Lb#@?T+psUYDvA2^Tk^Jzr; zJprH3vyPqb#CdgkA-(|bqwQQR%pi*Qqy3yM8#~U#-}6|>_}_Ws{ZEP>fcbyx>g+#` z!?)`5&)dF!l)jA4%gH#-#PxyE;SV{VDc2Lx zb!ud4S6tai>O-7lRa`=U`U#n6CG|%w4CFi9K*Dvtyfa-NA{amV`x-+`>Sg%*dSly% zUoT59;a3IC=sK3Oy#<#Fq0g-)Xu4D=B9Q;&JqcO8XsjNSr{c1b2I|M(e^imhg~QND z|3dOY7C~H9)AVjiL;#+6u(}Q5sjE9z1buA;l6g`Ga6Y4nEgR#0@Ae{C$1d#@W5_09 ze|EEGp&0F#rW4|_fd1fk;{Eqz{PRJ2Y&(!ENfDQ5^7xCWJ|V{@Ame(#yu>5O-cPQU zOjP+%RIfnyDiQoyDtXu`C22>GSDz~zkI$kh)+y37i|~RXye}>?%AP= z+nWH2ONCa7usF3(Df>i$U{sqT7I7CpaNnXGLzmS&c(Lp|&%=7Jx-@xJQa-#MRXz#) zX^O?a$9IK!`qc`8;6q%1r|*yW>7T)IGCXm8pv1!X!sE})E!G2kKJ@noG%6LT53219 zQ$gx46*4UhTn~tQFi1jNqdt8*NHzHFE5~K`xP;GNQ*#(w*ah~zl->q;kdPXs6~{R_JoWg&Mjk0$^K{5dd>QCZEO^lyLHiX*NC)yOH2GMFI~0+U z46kRL&$mq--JPH>VE$wNWByMKXKxdI=4dhEeT;2#8Of4rCR&O7$M#TVJP*$V>!9&~ zPcR*u5R)Ax5K?&Kji0^@Lg_?!73?XEAM?SyO*!u zv6J9rcU>w(byQ=#515^5ST{X5s)AMAh|ye?WbTHIT-xvF@A0>B{oV}zcUj#*9-j{p zXWFgP1p>5trdU-Gv`b4iCehklOMKNgKOIE+0AJt*I~OcSj>Jvso$6#p(2!HYsQKyX z#j72!t64VuyRqv%8vVHIe(DvM5$xSvE@O!i;@Y(zxBbU`j_MT(O() zA7Q^LAM_3`CxJ{wdO(JcjiLAhCqW>%6S#YWJ^_>B-~YJdVE$wNWB%XK{2xL*O0JG- zTtSv%WRj8|lg`we&<5 z9Md`p{;|lv?pM`YhzI)5sI}&xg?4Y`s#H%GOW z#e}Z!4*{Unz(;Ur=vq3flERME)ZR#=k!Bgl2kIz8vS0{Fg2?Y5lPo&~<9Zk$6l$ZV zEMK;+;D)<2`IpoyN#I{1=sU_PCJJ%;i&+6(G1%d8{^$Rr{J%EN|9kF_NfqFrxaWR? z(3k?2P2>u$+wFd3A*^#}sczM#!111YZozXMWt&JTIA(^*7VGN)-5ns|9AZOdYavcV z_=n2&85@aY$50vkUFi%Hm&qS4(f85w(r7#=)R+t_P<#NE)AJNj31VBVqz3Vyl)Awm zU?qv_bQjhG{_(m1%zw;(%>O%z|084ey)Pr4AI6Nv?fc;<_~C&^!;xN;cV8G9j8?|; z?kz#{DK&U-?IG1_#YQf;5am`gp=dErzff$k8_d6HuS!aoGoQ}x-I;{u^_Iw>+ARUo zm;Gapq`uG80~{nkFJajng@qZUy^-%3t02+udG>U;vNFGPe`Mt=92Z-;V+dK6E>BP7 ziL(}tKzc0l?LFq8o%rtAEUwCI2t3(al~k9Rn2_8Bae>y-^)<%V)YoEc%!d5t-dGsv z0XDv5JU#*0+wXDTh`7J&imPlzUhY54{RzFmWmJ>|gNC^MugTTqJ~hUFgRp%gH`NiQ ze{vSqg@d^KPdg{%d03~Gh4)Xyu8Q?QP0Yiq!~GS2_}+?&aNz(4T+ob(hpL}H*9AU)tD#OJ|J;!FW9`{{z}!xZ@G zW3=`p*z1IgrE|a^Krj&+-7WO@>@$K)XX&A``Xm^KhsqwI(ECGW@Qt_xj}GI5F?^G8 zf&PFz3h6LjMS{9ic3dbTmVE7{2+BX;@8ri)AiuDya{IMI;17_vnxQTM^ni4JE!G1* zyB>h~kNJ=JKb`sS^8s&+qAI4M7X0}{z^p}fxP4SrfcPBf*%3)c{_%RoOzJPnEY-ov zYVe;E_Gra@VBf=b{(VLfp`v3FVS8axINStvP#tAqseseVzT^qlx3>Og`%JM;XEM*0 zui!R&GBc6hAe9I>DE=Pl-4Q{@iB@)lK5?JiVhI3fEMJJe`kHmf`Ys$R7}>py^G(uUA+RxT9FD;K7w`{g(g@;6zf)$>l@7lxn?cN#Howc$Ux0c6o*VMu z3ni|jP3XD^cp09e>$raiGy~l@_ebGJFv0JCR3iQx3?>88xAM#L^Q*!CZzwhB5D#f^ z0DRdBvX>#^66}9!4~#)hj2DAn+ynOhvB=sI#QQ(?tRhjKq(dg=W_W2ol9ni4iTvxSj+4=Wz{_#8g zbIgCtf6V`B#DAZ!$p!g!B3sCe^1$rOXxyJ4d`s&`9DW>l%!9l!2soQ~`XZ9BgernC z4_lm3`U1q8gJvxqpO^jKWlw-#9@+Q3>?YFNlni#c>?|qZfIJoflMnpwFL_$Q5KFRd z)ld;pcmr# z93oJ9f7k9Lg1b~J?h1cKf+I*j7uG#j)#An!Z|9CT1(ZY9WFKU+v-cC$@c z7YwlH8HwTx$wX(Ku60fC9B~Ht`F}s;_dwRzefuNS9#b%20X>GRsxTRVw_Ls?!@~o; zhr>zWS+DT*$j@)RQXIGUS9O;dTi30pQQl(1pzGKFDDSWT{sjM#pT%Gp4my=tT!X39 z&{3AfMSWksY)!Jx;7Mz!Jzuu6P)I!?{~5+%D3o4X0`(Ai`FZ=n?*V=m>Bu<$M|#9u ze>gLhWe-zyWvDeCSND6C{N<3COS62=JI@1eA&&3`$AsKCKE7VUeTVI6H*8lqZ20jr zRY~5=%*>I&rL)-R)vJHc{|P-{EG}@>c+dSDUTPik=QE6}aZHZ+^^x9y#-kuR%2LFo z@cbN+ZREo4Gl$9s)Z~mAJQNq$VCRZZoIurL;da3LD$pA`%52IpNh-!C=S2jO+5Ez}BJ#Kp^gABJ14#EW&qQ9}Es38`cl`kKAM+pc|F-16uOyzA9|7Jg zN}SbE?O2{(G;ZGqS-Jr6g?V|vtN_kIJviivwaF~bptf4Mk!yj(d*D407^OXbKwN-^ z<1L-5$grm=xhIlj4L9*QEF1B9)6w(a%O-%S{oZrX6R}uUw2t%)sO`G)d}JT4@9fM# zet)+_kbrpaIvT0~zk)Kk8r6~CrvSSj{0SniDKAgm(n%0r;8sth%bO^$RXxy8Dgpt6 znE@{6_4xfK&Zd$xW@WP3T@7la2c&d4q3R6s1l6P2+{Qg=_}r=7avXKJ1R|GC+q(?J#kk(udfPW5w_ z#$q1e{-r|X=g)@yKw1Ee{3B$uzuN`%4t!U}ht)5BW`DpK|A!6f#q*?W&J?DiS z&Jg{F=U0&gcLeIwQ%U4spO8P;b5KYSgY=J|0Y5xg*uMbY6V)T&XNNq#s}=sQI!*{P zGo^yNBKfnf4x2_livNi3=Q8iPfB*LG?bqo6hpyKHI?7U!A76Kv8sk6WJnA2F{dONe z-mF0RpeVk8@)3*3CzDW}2#NzN_bzW9?~{k?0zQKu4Sn(A2^O1cI zS^7jUSJm`oE5z-?Zpj1vGsQG3;k*!;t9MI~T|d&}=KRgR|A^=9yJt~7xv)J8c%H9$ zNFn9|&l6F;Ih2Ljc={9)?0oa13KT~;2KD1lCW(MIbL1Pjnx5yN@7W_1;6LLSaJjX$ zb;Tc^FH8!W%GZN8foFgl;_P-OE3FFH<*LG2cUld;zWX9&-JTb z%-DEk7m2ukB786@Fx&(60>>e)j_L(oQ!}}MsfYTBJkUSj`o<)JMkDBh+F|HNSC!t7Wmw1YV}UqwGJ|65v*tkY9T&JIib~TmE(N#w5Sr zPlU{QwA1NSO?>`IrC{e0Iz2s|{+Dbb3HVR^SwaY7u`sccTE7?_&nnycRV*j(I&A$a ztb;_;#Qo(DDH|FNI6raUKk%=``viVe|3A$CkC=yVjUI5b=0UulPp3B^zrHhQGNHWj zVN;hj9qPszW>5`v^I-W{G^d~s8PRHjx_J@T64GebfW2+53`@V@fP5^!k3Z6rCilBD zAg}%hd%_Ijd`qWP@_xJi`r^2wDjA1MBIFJZZD~?PjXA85>j8S422AZ z)yn!Lz?pe%3Gyp=GPb$j7NI!pOP&-loCoCdHyUAcKFZ$@7)-$~@7to>+={?S@JH17 zd*6h9KBPb@O+|T!(EiV~uuGpLn3Wke^nSkt`2)7~U&*SB2JPq1W^?Nn35evbi(v0x zeWSQ!6BUe_%`1Nd67N*$DX%3nWK%a zKlVgBlVLf$w>qEojOht8#i%|o$$CVFw)?VmET7;Dj`}3zmq+=S@|E(H%$94*zN7pb z|0Cm^kHo;q={D>f!A zf!8RD@(U8B``Lk5H(S1LDcc_#j)iHKz5m_1#DxFX-rI+@b)ISBC!*=-qpay}yag6)xP_e|~{O;#S0{lU2{6VmJ^v`Pvd~`VH zJ@@^5-Ve?L=i?IG8{S;6FUR-%X2na{1vlS(b25&RsUiLk_yPgqYt0Wemz@9U{#h%A zH=W7@s4e>nVQ@c+H2L!|qG@C&?ZQKUY7iq{Os z<78i8f5~wg6n?(zFycmh?~1_#uffN)ycB|pT^6pbG=ge z^GZK&(&Mq)$D+hb&9*S=_dJ#=w;)ixy*1_%jso}0Xne!89KmcIcYUe`{NEe2^&iYr zYrTo!XTx3#>BNNPFTMiR#Q!PZAV_mNYRg}K zDccowmQlRocf;YJ$D;7R?o}l(ZjbyPap(dZ=Z8l|EU4?scDrj-Y+{y(i#>-qwwS4Y1zV(f8aF8t9^F=#7aEa%n1IPb=>HU|E|D$T7)ao3IQhvtnu-8uc z|0lNSGP=%#|2tFu|Feq!|HUN#o5IjXB>w*q_!{~DP(S)5R4PgAKS&kyw31R%a&@_4 z$n(QqD|eblHolAd=o5E0ty4!%ILyyKQ4Rm!-!02Oe!|t(vzsFDh<7|%;meKb)xM~&kqtg7|qt##5XX-VYt|pJAQ0fmU z`qHWa=qelPrJl#3lU&y%?;Agvx*yM_?+dS|`dP%|yZiUWi0{Yy$H(bC-JA1SmAHO7 zzJIQjCe?gdpgJtuvy1D$iER=$6W@jl`T%x+a0KG zm3k!d$DI+kKy_u#akoHv?m;=`{sH3gg`#L)i~4WX9f8s7kDy`-dbwfwyI=~PPnLH; zUq^RN91qJ+`t3OW7tyb7wU$?)K3|<_^aUsn(blEYklwsK>UQ0jiv%~Ufq$Z#?|j#3l}!y+PwGL)Me#WI8WG1vZSIu17ndE_LRqhenf%t z|Nellfb@FwgOL#{pYH!3whkYF{RFrA0{H*iB@`#AIPwc+PD1u1|9_^J_@C;hm_HsEcI3 z4751JGf)ZO(>M=eDVex&>AkwuYKRo`ec z>0k&3%#T#!j03kM^{}KJsr!9~F4U#k2lM?n%>Sp_tCyNbuhiqs_kFt{&ToM1@RE8* ztV0cZpvFAW<3{&dO^b7UP>Bzihx(}w-!U%5sPEnx6%Ze|Q4d)>7wN&PK!4oUJo9@Kt03<*7gbz&`re${Acddu)GSyO@3sBeJnzKc}05eWu@^pM})-5nZ> zLSIl-Sk&8&%7P$td%leNf5iDKJeIHM%OoM!16>_SeTOvHm59Gko!7P1lweZ$I+6ysFw-$0vg8)QeGYR`VY8fKVT> zWz=IaS2Vha_l;UXP_AESN0ArTv$(I&aH;pxnf(7E@xT4?reyuz()N$`|4Ao+@+6fb zcb~%ld(iDz06{!i?kpV~jI~l9{qp5|L$~%myG*O~qTWC0|KHmCiV_F7wHNmraRB;x za-~iVdjBnD{r%H<>PTeVc4EuVvKKEl6?`~cSK3$=9lslWp60T(Yw3S2zTeKHe%QXPr#kHc|eeUkq_k!QM%>I|L)|7&*1awY6gn2yQ#lsUDg-(%f?`csc> zKl)E`zF0q1GZ?9JejEFGY+|p!qqKLKW~;ZJ>|R&IVy0?P;f0{6=vd)8P@D{^1VKf& zTG0AR&?mEKU?f5Iw0ojO>HjdqgOzmuwSE2}x?e)4A!Q#doIE7+|4jRLVSj$9@sN*q zaq$FtPRDom?gW(C z&n~t|?yd0SixM{L|Aqgj!d%_kkKD3aTi6#Ij`tznF8L{s55IXbKCnN2Yp*@BYVh;O zGnmUZ*^$Sle!F7o|Mz+e|0Ui3Px`@C_&)lcas&o;H{}YMzCfJ(M&u2AJL8KsRYhFF zJFp87*BHAdv?Z&%AX9Z?2+H-)>-%=oyV;&`S^VT`^b<%UsZ|y|mKU>Cg?-|d_o7+y zhtCw`%3o=_MrVlC9NAjB3VDHd;-9)7Wro7e_+OrO4Mu9Ip1s!LG(T3lO5JTLDtz|W zyGl(vDiaU9_ZrSDBlCd|aJ`@_3_YNlfKjWWeK`hFyVU#h?EGJAe<;cSqf6U~rw!u0 z+py+J;y2rqe7&?i$i>RQG^&x1u79F}$x^a_POe6?ZQp2IXGp4VsMFLCSwc>{by! z6#_WUBlx>phK35BME(NxsHlfU{|f|)Ud8|S`9l>nCqjk3kE7KzDPhcf)N&Z-w*>kS zR9CArl3lGU#vCie-2`oyF|{AA`nUk||H9_^ne6#XjfZ@^i{t;9u3tU)Yj2<>PH_UW z)6+)wK+8llWe?aqqz8mD!U?hm>=DEVs=uPQp^kv+_eH0}{e9s4-jLb-W8{GgeWT*$ z5$Nj~WZ*nH%e0g)*zjgm)u5b>{JqoBSalQh|B){SZr)U)x>09L^MC4lWU5mj9Uh9e zKkRfw8 zr})F3y_r-efc%0Adc@Ev@ESh(TSJX0*gW+84yesbFMdE>=lEm4vb?x_@~8&D49U=LnAW<&*v&~6I2ffzD`KcGjf5Ky_TM=aWSZS9QK6#awk^k`Y7xBmgM?4>h|dKd$-ZLW8_mct#y&XXQ3N(aOba|{v_rQ zsZ=jI-hTwgE*_{0@ihGKc6Jz@Nn8kqsv{aYw zbdf#Kz6|5_U=JW)(CTsh=}z>giOwkDqc$Om@c{H>3j0Ri9;Uu}M=Q;Ny=~>roun&R zh=8!d{{w*m(zzXXjE_@aeO>IZMDuv;wGr1Y@WkSv&FVe!H+hA|%#7cSd%n_i3~$f&T8%V8BRt?$dV})*#Zm z7`(L}I>3|{Xdm@9AulkP+DGS{6L`M+Ej9nAmf|uMhbj(v;BQhKoci?r1qJ?!Bh9{M zm5|sm5(=o_s4Ttihi+1l>K2js9|7*x{)0W)Y{_h|CBFC^Ww#>p+H*}=sy!W5=uQF#bJ68|3}{Hd^A=fFR{R|EpK^uxj`+7r1|#;0 z_4-`l*L%z&%>gQhp@^sf_+EUMq9tN*6#5eAH}xZ=ARqKo9SHTmAR}PkiT{rXfg>#6 zsfN7}q`3=0e;@i$Pi*lc0Tjl0W1NBT)WM!Cv(*tjfH;B1=tq_b%2AjH_J|29&ym2{xZ=f$(M__e&lzO+8@u(}AFL*nW^4r1N z9*bj4>Nr~cz%pwLn)0Yu7r`&R&YW3A=m4UAU%$v;0KX0F z9Vo7A($=&Nc(ug0VyhMU1)fsNc&zTr;ZLf=MIh`DBGb_Ap=_?UFcGXcg3L7%uf!X` z+fmB@R>h>C9RJ6^>a>IZD9hiNr~uv%#54bj@_-)V6$lHrQXW|F#GEuo&m9{!QbTI@HkIKhy_&1xQXhLSEpwug}m)c9HM>{@v;8dsDEh$rL0z;5+bWGcsxZ z57_~iYX0vV_`cTGZ+t`H|4oNBL4_Fe6RN5jP@h@g7om?sc@A39$%%D@-X9m?pF)=p z@%N?ec#pNA9(@BgF#`-=-v4*}gI@EjdC@xiX-{9n{pQvUl=@;}9Gi2q490dWWW z0oWa&eB7e9w)`9u~ zJ>mmV#ZN~4zMJ%fTaS+)L!DfyGne86f4UQXI{FMU@@x|l)(7hj#$;_LNS`d3m!g7b$x3~>OZI7FZ5HX*=LG$_uQa_&^y5 z=k(vZkoVzdaNWTlW!6KOtNfc4b~n{YKCq1R?9W`Gucmx{`udv{qXtw8puVElYv_3n zb?m`lvGI+Kdh~}n8i}Xs9CdYwk6#l0$5n=Zy}0`zcpCI?T)K^Pi|zKVrWdn)KBJMo zPYfO0M1Hcvh58ccDcZ)P0@)v;&3e)EhY5qGzhEWce1fJAhpV9<=trCZ^^i*Zhvq~J ztHRJN6x_Ae``-Lnw!6-1u0+1UjNjWlqF$ZUyL{@PQHcx8gzuCL&(} zLxB2fR3Lt`v;4tk|Np6R`H%a0csJf%-_a3u zc+oG7zIef@E${)_xK1Z^rx4A=FEiVgOr! zaIrZ)s_5qi`gAI?D|U?_)86#8rmD7%+^>YXnpAfq!wvphE83hCXE56;LwfWnIL142 zDIabgcL}5$jC6y$ZL}u~GQi?L%kDM<1OR0CYveCe-g(oeO(&`DPz-nrsgF+dQvcp> zRuH~x(Ek8BQ0$Ok!cX&#>k|X8LvTL5AN2Ll=DLHfC+(!!{J)v@eJg(O#zsTEeXvve z0r*2~k%i25ic6ep-Iw~keZm{`{}Ph2`ir!#%=WIP@K|%p8?fIRYh#GF9JREzPMlQc z3m}g}eIKIOkCk{+O1+=HCO@b72=wz)*I1IrMcMs4@;^r8e@NePEF28h)xW%=;1_rF zp}(%WDz>QTgDp6(%EK=jpl=-O@#G$bu9Fi64*Ab?T(`^-WjiQ2?F|7SD^3JP5r0-U|EHx<}eCnGcw7JD-QT~D1C#S zh5AdeKW5rDAIIm+|L40;7RK*gZTown{ypU9cX$pv>cH<2I|c*X5C2URO(*Vu467`B zx`Ni$$@S05=k)%;Qn6AN{(S#eOK3eotJo6pSiV|PY#Na0cv@X1baU#O`tm!^(9Kce z4E?A>!1vo?(H_Fj79^|{yTRNVHPlBft^28O@i!|h@Gq&Z;Fo)Tx%T!WYxFt@)X4HZ z^^wToRyFDkHyt{3km3%-h(DA5LBJ~+=s3aVL{S~tA#xw=4?}&TuTV?h>+hS2^UsI- zPIkcSI4>l@C)cixuQ$o))1dQpVSpT+Cz9Zb{Xr{zF7@w@(K>;j)9R)9D5?0=8yh>D ziuwlddzu>P3%OI*^^X)MXpKpow95OWe7auY-BJ(DgGBiq>?Qo%gLndToXk>BC)K~3 zC!($cmIq5JWhp{)mu&d|&}Yg+AE}^4-@d=FO`!Qw{#`Vu1^%$HXwe5aA74RW;26HY z41}5dep^&ubJSvlZPf8nmzI64HENnv-ZMFb#y|Pt~Bl z)QC$b!=0auW6u74JV1Ra{isK=Iw|kbla*;K?xpLgHF));&)HR&dDX=MnEx+!-&|e) zKaDfI&M!ayeDB9}-mjYC{l)f%S4&YQt$Ly1w)F`^kMu%A`TAgwG~?QjpNsjDw7ubX z_t!?yd)ClVD%vV)DgIyVZwYoa9k8f$nf}yyjm>m^#9uZCc#L$7oY5%pMacH>L8V`y zHX8X2botP3KGs>1t|eljFz>)Qc0H9m*d`$*iEn_S-) zlIw0-@$(q12P~xHK=)JJ?-uIo4K@F7qb)-AmCY<0$j)dU?L&SCb(GpRrEd~ntJpK0 z1F}qdd)2Xl4`0XeDtBX!5bkr_AUG+0vH#a{%X6p?k)*z%4`2_9<6C-(NB?Kc1-rpx zX&KM;998-wPT2SOslTrXx=|;#ppMV+;qbfJuCWOl#UmVLLDTI=)~S>Ad-rZ@#rY1S zFVSqSu)j0>iM*oX(Vz_fAO8Ae|0+&#mi7SYV+pG^k36~zV!Zqw#9nSIqyj-6vP~?_X{Ed!hV2lz$gGBJlSyH^4q@+kHRyx>+o+gSY*t z!`t3qZlJpO?F}2&3u>2rd&7O}$z7Ux#qV_p!R-w{ZRpAG$^R+Y0fR1KL&L68?v3vJ(@FUG;=O*&29CcF9QSiGJdV-|?L;Yjv zRjD^n;{CrUH6ox}kN+1O8ZD;yw{Mr%vrz8qT>qU^zvzYcO=}0dY5M*lRB6t19`zKz zs5EyIKf0n8+uevec%+!pcM2Z2DtRM8TR?W-BIJ3zxQ}g}Ug+|mKcd14U8#&bI|L-2 ze**eQ-6C{}p&KwE1~3x3Coc8T9@UA!AyCMU7dz}*h`ou{xh&isFM z^Z9J;;45_>FVyoU-gk9)?CCgv1Mz-k(3tl3w>R9@pjYL(i1#~VJ&y8*L&aL;1y)q% zED@wuF}cRECQMY1KKXwANmH&A0RLM=@Oz)~{~@rxz2OAn0WR1PuaX^rIR&r-b_`W2 z^#t06-xU#;miSx@TO$VWfM|}(R40J0!|>!vpEt*4(q|DzgGJ;| z26Q^rG4Rsc4mrF~@Ao|UyIWz;)k{*$LcEVWyqR>N!26CU^8F;t_aN#Keq|L!(uG2w zJ?1Y{r(%YNc;6Tlg9hXkayxpeH8s#Pf}qcvn2&5W`;AoZH#$(LrTV^)uHQ@I_D;Cc2FRlGD+x^48|MBDrCgc2wPe$xc^Fzt}{$!kA@%K>=MZE3tMCp1-=L&Y)}SPu zvL+-q{hbB64%ESQHAP}G`=PuK=VBkW+Q<(key4hXT5AjZWYW=d*HV3-%Wb#zq91_z zH{q(E@}l_*)Tc@MQG!aVr#=XGBvN&JV_TLvhFXI{A7LH>)?4Z`l)Wfg+e%5 zsUD8^#nkm844JFz(TRFRDK@@e(Z_|KUI+i(UPgT)P~-#QAAkxlXw=+;x_#=`CyYDW zD;FOk{12Ue>a#EKAFak5LL86urc8dy-#65QXEW(=imwzoF+WR-=eUvRxX&{qh%BRgexe0N&}{U?8qKGXkr zdgqfgUqhqI&>aK+XF~r{x$cR?=v-a@%l$pf{};O7uFn5=x%V3%|AKHF^SLR`??#-T z{C#WaOn)D|pZ51VJc#q}gTH_H6y8T!_8ETu>3l!a`*b{D1M&ud(*W54`Px1uFVLn^ zX}33gy23t~t`D%-mHL1kLn%Apqw)e5bUo&b{}$?gRrHY5stD!lDX(9Gdj7GIiPodl zP+kJ>qkB|{Y%(i4J)6x_y1c~QAN3;jeIx7uvKJ0DkG4Gu-R8wXQGDv)OW6pwBW@2} zsEexO`~A~ZF0vn>H;nvL#gXc)qQ245%_Fa@h+q!%z1v>Owt@He$2*mJSXusXSc4Om z<)!VE2YBir?2<93?N2N6)VE>a66Lq9$Kc1oF0iZ_- zL4gf#wq-@3Ge!Esl)rcY)e4x42>!pRM5oc{DE?n??X?ATe-g5sm>2iy66_b|f98LB zX4B8n)v~Xz^uI&l{SNf+r{esl@_r%e`Ip1rPuBB;_wDzse`if+e&ipO{Qd>R`Dfz$ znLaTDKDVaDR}|Z2NWSLuwi{N9`I+SPH-|WK>Y$cC=UobAj6oRukw+30os#S z`hC38&Y%4Lo{qLvN}S(n3mvUKob8G<+Izu=6(DfJiVJyRnuLI|zsx=Uo~)wcU~uz@ zzQkVjV%I(Oxv&e&YtzSs+B4<_c04)AQ9R9yN{fWE}+FUNFv^r~exZ>%`k%oGoPop7SAFIVa1~q7k4@}wt`-bz>I-|c^ z(f3#EfRD!MuuOA_T~)Uq$x|agW{w9)7tmKY9ADb*TcpDrfLnX%=lkRDX1k-~Hkv;f zoiQg(pSFEBuP0j*Y5w7mg#fKneY)i-Dy4Iv%?{Q%WueZY)5LcH%T&%QhDy2h#g za%7<1LG}Gs`xyEtW^xkcl z8yqPwdu2tg@R^?kgJBtZj~Ru~^?%<|j)YNGJN$I3wKjb$n)|aD2cr4DT0PYTH2acu ze5c|y&Hbgk#O+54^fVv%zBi#CI2tJ089!E?jPqkofRfjLDVy^8i1sV_i{yC*6VM^X zJVC#a<{20>)B@=$k0#^+%J<_iTI&1rb;cB(#`$2Mop!%2+8$y4KXpGX6rP{y@4VFe zi;wrwxSg!)gTJrT^*PP=Oy>7rQ|gnpH(b|nO}?N``qR&kQ=DJh6Kz!D{0>_S@iY4P zyl3+C+57q~{`bKDc&!}BS#B)$V{TvIQ9&Tx{^4iI&mR~$X*Am`(fcZkmuPi;$Oqh4 zX(;sdPW20*PVkg?0Okqp2!>CM2TX8%z-5mIpsrt%>W*zGso!1rS|jEAwNk5@bQYqr zS)}-Xq(2;_etnyBQ>&(?cNy-R>p|E7D66YY&I=OZb0=SQ;8oDI>DJynwcn=`*7~GU zl{@Mn-ba`na}F_I2YNbhQNN#KEOOocm$K0gD2Ac;Ogj6U(f^0C{lR;;fsgS8>EphJ z=%;<^$cX6kPbERKI$8*6Y@SC@jm7)sK^dFIzDmH{5s$9kNKbZ zpPsd}Juu(*>s4}oXTe659cpo1hmw6e^~9M25~2*588f4x!#H z)yI#yH-EZQ5PHl?pT3~=>uX-EmJAYfa4^TAC5|~zy(=71_piwgXtmobP&E(U?=})E zlfV6!rwgE`6LrwMM)>2wcFc_q20x5YU%n2yJWJahg?!5p#xnvNL?6;`2|SP9O6CNPWPZ z=u=U=!RQZ~KODvcAiGkY<}AnjlWsXa@oZs+ZkH?%90|7> zFhWLI(NaG!g2H_9GOYNXX~&HZjKp1rAnLS6GEuNKxv%5HJ0pXb%l@rHi%*_BIWg5Y zcwc3HC)oo^zkqkDZ*W>(pt~j6FOXq0rLHsE0T*cp(EPxmmcS1(+x0bDsw>Ro*RO33 zH6N|Uq+R&Q)bH+=Lj|9&$rWUAKvh`tNY+45ME<`gTV*H;DEfVaV>X)C=Z3%iI{N<2 z-6rF$z0o_38LFP=pLjQ$gaAGdJ->43=^TZB4VuN=p&RooV-eS%Fz3Ozci55DUXX`2 z|KhCnXAw^rj6j7)n5d{Q*I_>N7IAYA>G&2QE^z&k$D8aYVw)RKA80MFFk||pK=A`j z&5PNj6HU6o!9FeK8F?%pNcs8nxpmBqc}DBhVuv)cb=wIIa8Ce!st&_|c}p^9|!M=>Y&t&MqV zA=ZlHVvZAzLvwSt7;3`8p4-BTZV>7LgUVcLl}b-K(L?cP0;CT; zB)_ksd_`-EJcc=h)gMZ371b|_Vsj7a_dY503)_?Ng2*-=dsLtvJ)CEu-7U>Kkzl`%vugULM?11oSXoJ!>SU|eMqN=k{(GhZC zE+Od&PU#3a?KH2pc(k(ljg20Qpw$#m9kx6%e&T%bZQ=c%lGl$>d|xuWXr~pr_TXny z=5sDnd*;`iXo{JA zT>F&ypZTBp|I$ByZ(#1KAzxKZ_3iM6q0diqdAf7#4b$TM;7#Iv>fbL{^!g_K{aNeu z&YgXb!#fd#ROYAmr8t;F!y^+)r3bx!=dD5i)L2;1m0L)oebcMBe zrR{)Z-=Ncud9kG?;Quki16*C1>3G0`&kOvv33Hn~*0Sd|Cg(T3ky^F&Crv6z6ty)c zmAOu;W$zfZUQs-`<`ep$*X61_GP1F^Fr)4IBkT1pchvcr-aFOc-{9^h@Okt5ZQIwc z%QE^+y3fWB6}SCR6uun zBJbbY+M2nkzG*=6I-c7&eJ&5h`8!GjD3GFge46~Wl)o=2dVPk@)LfplzpwbsAI+a> zzQ28W&JwY?Wk^|vDy5Uq=WA|mmT4VosXSx7eI~7m@KDVgm?h*0^{QgE-&z(zSoI5zP&lWd>~<5 zyS5DW;`29xP#d570^O&$>J4R;g`6c_`2q$z7BBf4t-*(CP8z$ri}Dqf9ORka+L()e zxS&6)eVy8$slpt+oRz|0t$oi|a+Z(|Ue1yqeHO>HpIr0CYC#w`o?KJ+1tHgFKWTgk z9=R+A9{>GkrC1`vu_sR`vpT92|GdyQO8Pm7GX%f84RHqK?QiXU-GX`m7u5+OOl19b z$;IgjQJ=mMuLE_4npWA0d4whPT|ax|XN+I7!!PE4=6~XS=Kt9}H>cywv5j{(?KVht zN#3`H`pDmJ4w|Ow`KR>xlm7lY#XX(TbbcQ^jQsuu#Q7(70`Eut=)>RqkeKkQWLi=0 zB*%B0bq$Yfwu<-1TtkoJ|Cy~7XXF3<f??hk0Xt#;-`Qj)h!q0TP_>PC^`|$nW z5?f=g;pB14&&B(v|2%023{{dHpvB-8Wxjwe84u{xY7N^PewVBdNX{8TeZXU-1w+l1 zZ)|)Od4bH{*U1i$Pc9fcU=jE~=enHhHmu26vi1*azjv)AQDFRm&qw*~%1DR14*E)! z$H$|nztf{$-}M7@>Y0$p|I5>PYFBKt_!j(bD|GOeVs5TcCkXd$%W9WD9QeC?w0ZqR$X_GfI5B~^l@|5xcpwnZ5PS5V9Dwm+D zz;r$8-y=01q=$^6?{!O|wylop^FjM&R2zQeoFKxYk9vI-b_3cCPzRWihq^<|g+x9- zI9g2gA%8wPwqWl6>G*S@@0tIZ|LJ*S{=YE(KenZ-DJ}Eiq=_a=;XJ-CzY?#KZ2h#2aqFaZPyPxFL;B^LpyI)@jE}9iRIDrEdDYRaL91 zZ+IqWc{yIE^ZWU}Puc;<3rvp(D0zWozrY6A0Td5N)(JWrY3{Ib@6gb(jZsU7cWY`s z>!r`bBkH00_`{Oh zMfG`(Xw*%8aKY|_6(bv~vvdfcFKvhNPDj3eY5R2zF4y3yePmA@Dr-4bg8ScF@Pn-O zHG1&6KzezmkhPrDbG4Z3xMSp=`ijO#+X(9aLS>d(S+34PS%6=bUs*x(AO82#;D6Ky zeyS#y>ip0L2;LSqMgTRldXc?=xYJ^aCj|X^rC(o<`~!4)(62v$zCBHiv9Bwi{%)QV zIs1Lj{LlPP4g5#x#p3TujDKzUN2sW`h2NHF1`zW0WG395HG$f$>+{jrt$xbJUz+( z@txsUU;jU^THi{T{s>-2-+@srj}E1ek2-*r#VI>s_3G-?7t8-?JAvj480Ay)0+og? zFZuFt!+KMIHJQm`=B;PIs-IiZIuxiz+LAp*44?k>8^8eOdT{N#( zk^*(;6WE)G4^e%*Wh^>Kb^oN}=K+se?NRrqY6Ky(z;DiKhfa=41v}u-n!!j_741s{ zUiZ`=GH%6dr0bX8!|@z%Hwa4|b&}9uoSWS{Dhd1J-&?CQ7R!g;T(b;vJ}zZ`4-A3L zBLI`O_Y+i4P>%Tl9i{8kCS#Gd19}1W##(nuH)vAlcP3-tH>g%j>+n;ZAf3Nnt4j9k zLzvIIQD1LFKOgbFJIeKZ7iwoR|1$D=y58l6kIDeWKPhZdE|AaB^*8{gD%HNAEavXhnD@_m# zfd791{J-^e@V_zb*Y{03f2spCnTnM-0qB4A1o8&oki&SQ(!KPQY$${ybK^cN7Rh?lmuT z<9mam*uVbp7oGNKM34CVs2DmKwY-=;8ptf9dO$tuTs1Y{N58+@Me%#22ck3az7pRT zNso737TN;Q-+#ao=##WczkVP9eZG=CE50K3n`1UgW0@!%;gI-_7>tGxLAC4llVT`JdwX z7r_5r{{sI%XGxO(Pd)Aw|L+VX`TyeL|1hMUB3LHN%7cyDtFkADsJpJlSKTF!x}RG?j+0!Q|9~C@0d&(ZfR+I z=9foNC$O~rq5H)SK`0mXU()@p)seI0GuK0p_Zrjy_Wh;$J7?zq7r_78%g_INQTK%x z<=-=|$sE~>jQ@Z3;|3n@E~Mi=6RJW10O}DhD*u08{O{F!ng7q7uNuHZ{_QQtYwD!kvFDl+ zMstO|F=xp?8(00kOTDH#+bHG#Y!~U|IieBQK4WfHLBY*M>G;2HqoZK!u3fu6y=`eb z;ri+8()RZ)RfDd`Z@+{(lJakUbIh70W_!&2<_8i{p4zd`6u)G#QPY(qoU_D1VP?Q^}OXrd&}kL{~Lahv%J7RDuVAHQmOi|ZbqG-G;O7NKzg5E zi_2V*n0fd~;C4!<$9)?ABc2?h`aq0f590qltkU_W{r~iF(%+MMAK(12%RMgQxXa6x zfpW^?z44o4HU)cWt?Dpw%O^1rB8k6~bkH;fDXaT?D1%9?TVv^^p zCHsu13#m@?|L2>YGH%_q^%1HM>`-^)rTKqpyBGevt7>U`uHZ&J|2w8U^`cCTE@#Pq zD?mPCRb^Pc`tV_gt)sLpYtK*o1qSHtcImtFX}(axXEc(|zIoG$T&mj>McYZT3z|c5 z9sOKv9%%DIHv?s}$-MrrcE`^3{PE9cw+~LgUYCmhcai^(_|h410FM82{2#`}>HF?# zc|XPfkDZqP|LJ;zOWpAl^8bJQ>C=CtJodxZO~|j#ju)>-e?R3psg6%<9x@F*O7Yx8 z;RN_w6lH;UJhd*L<(mF~=UUI}xHupBz1(T%GyS-(9S_lY+UyixhzEng^S=(sd;|F3 zMDhO=|LbU;plfh&FqQvbfbsv?;l7vnzp4uUKV46c1lFhezDFppZgWmVwKW#}d~lHN zi=j}zYoF=eTYf$E$HuI7n(Rk?0*|7C-;KV$Tp?0n4qo^2I@IIu(w_KP>sQCdWjSZb zlh+Rpu3B}^KRl9ERbCc6VCl&+7U@(a(Bnm&e&I3jzOSoCnXiN46f)`W+9hcpt^Otb z?~`8NnA{R3T;RO^+~MV=;{P*vKb;R?{%8Kb`uX2)Kpz_QVN<_0OL4iy`{`OpX6us$<`^a0Cpece^e z|B4+DPubH_YCWD>cYB-u=>dfIRz0`zq{&`UhN~Gu9iOY}M-Ti>$$l_L!W@o7q7sSUeXCNZGW>Z3$nr5%_IN2-KcX# zu7^St~&#aeMOnx89Nuzh7Sy^k;5)9enU1?1a>L@d`T!u=ufG z2jc(n{_%0*Z#tjzSk>^AkTE&L-yGl`B z72MG9Z}-D*GH!49>H5wdDgRfPe>*%L>v2>jj8;d)m9ztfH=WLZC*dEN#XsW z0C@i$@V?Zu0rh^K{t+4WRD%#bQMIAr^-}1_Ie!Peg|*PBThe|2{eM36zs(O^z5M5K z!*X}6-TvJN(BBys#SfaW;(Gs(O(TaKWC zyk;CP9MAc>Ef#U@RPqeAa^eB>D|*xCcdiwE0xLxbzLE~%s8@yfMAGkX4HbXAJ8Uf{ zoj}OWWYTJHj40=AE-O>c8+O2Se}7GHq15vN{C)I)NiTr+^Lu)#?F}&t>BpvdpLCak zqsB#~|GE{03=4&4yiG%w=cZI_vhSX9utJupaUeLp&)0;w*M{&`Ql0A zwby{}TmR3}L->5av#aEW8~iBDKepwu($T~~5zQMa?s9wH$8&KA8|?Js^ZQH7-xqs5 zng5ypng1`G|I^PO*#Sj&P+s#@i>2{J^esdGw>4y^`89|Kh}#?f3_HL8I{Ab*6;#)y2Nds}{`Y3h%&bJO%HCErSx47)dJ=rqmp;Ez4k>i3Z*DcGHW#x9eo$QND z`n%#Q_u{;(Hsd_#yibkuCwboo-X|R;!Ch4a-Y0)w(N|nsMst8X7T6h?G-pZUS~$F) z-fz>_0bFE?+qOu@l{xk;A#O(|H!yA*|VgtW5vMUbbVmd zvQF)YcwG9LwydCk5p?)ISweFk$S(Umd6OU>&}XzP{(W{&V?r=KnPA z@k(~UY;a;O_vw{#UT>g|sR#f{^Jz9!4Wh5s79{jq5EC(KUxgrkh!1d@4utD$T z^!Y$%KOX&F3=aIjk(|3Seg1A)UO9lia2(f))$PRheBEAdu^~TDX3W&1PjQFS9f>q< zF8gQG$zz+Wl;8JF`ulzG_sbik9;wdW@V>(PKcqN6c>g)fQw#=*Q7>Ln|AU@rzrr`r zyS-BO$CYxPbX~3c4ZEpM-{u^PqHnJhbDul(=*#y>yS2poK~rWM#rNMm6dt-#{w_Yx zQ{xZJ|IGXB|6h80{dM5yq5&Ki`OpdswAVl%aL61^+X14T;sFBX1zuRM^(5l~V^No| zp)zNQ6Z$B_Po6YU9f2GVJW2D`!z0C3JL<8VZ&g~Ms~)uuAg}(RCvqy@CF2j0p=-B@_!oqJB$?cn{BYmD9=CBGjXANX*4!*%fYBatcI?`M{uN$tGB5cbegvji zkdC!PKIU|O;`f*LdR#6XS;)s77(hG~eyb(wc2C6vI;cJX@qkZP2&xPv9-zIY(?jt9 z#~8%}a+Z@FFmU9gDKjuicEIC;N(X$Yy-QzY9I{sp>SBg;|-vIR86@NcBf_naBoS)_3loi-ZTGSew@7nH=XhV7{8*_2T)$XF=)>kk!YqE7q;==KD_=PxwewjTBT z@b{B-eGUKcV5$9Jc&M)aG2(sbz8h+cU8)Y=kC%Tx5(e>gUYOUJ`JefJdVYm*VD4~$ z;sHrJfabGPeZa%1`hZcy1JE}pWaueA9`ky=sXD>Q`T(U)Fg!fjH#o<(_3!(TFDBmidHW^B-*-hC)BZli`76+$Uf)E#?^EWz=ymyAhkAwQl^I9o4o5D= zeq;V;{%8LGc>SNLya3e)zzzsmC+&dk4a+9$0}MU+s1LA+<0sO6gSC;FbcAN&=ga(g zs(+t!dr;5+LkaN<==DL*9sT>{?^maJzu|uP`wwG2eJakc`1^(V%Dk70!87KaD}#6V zKFyE&#Qe|v&;0-K@_(A|?!Z5&PLS&VPK^gpeZUtg5DzfIk8X&0cfGIJ0ZP9>!eq5X z22*wb^aEz<&$~RIr~Q4Ue_zq-OZM??fZow$J--lbOvm}ZL%iP-9!mN9I?|o%)#uNu zKRtcDc%2{nmhZ!rdmrWt|I>M(-^l+Q>i)5x1e*7ZH9~u9IzRFI%X>Z8F8}!L@>B8x zsD~Sx)DIYiju7bwB>M%Vj?PIAL!XfhrUdiaki9U<5O{~39KExrv6{p&~e zkNqlLACS}&96Nlu;sGgtfA3TGP+ec~A|=0HC?PMgVLi=nSMvM9ze4vgRo6%U{$E4k zDc;xC8?~I@=jUwxJZFS~3-x}?n*ZUahr_lo_2Zn^9H2Ai|I{}L9WM0wf$yVsho_L) z9+2!8u#ldhp!5qsK;RPB2c+}+M)><{eEKP!-gG^G+TVW&af|Yhn9lDj_540vzIr}i zXL`o?{Fs*)(mt4b{x7L_MIu!t(2*Q_+Zoo>JfMdiVE@euK^U`|&7ad_YTBqMwqx2f z&9kC=!Tits&$#SuO4bQZj|W&cCF23qFF<;N85)WQ3>>F;0O|xIWCzfE0mK6^XJ{xq zJnOu`nfZOx^H1vZdXx41m`nK6$^QLhoFBUJrNM5ahU)r!y3Sm_f6S{3iC3?b{hs3g z5%TBh{+x5YX#d|(5Bjgt)Eupj{_BZj2P{3=7z<)wr4|I5<4IgU{YB?Q?|Ge@>u{y) z+l73dee|48<^_`T1#WBTC@nILCg%+bIz7!BM14SyVh2|#!pW_J&`8g%dny-I9@jm2rW9Q`i z)9r!_;{VE)uBJ{!&zJK1RguO<;{RjE`X?SNU8g4gpU(Ny|INHVH@u&|&hvG@@_C)< zKc?#ht&OB3)KOX)n$i#0-tcc!C%B9H1tjkazI49;@&ZPyGn%vm!q%jo&``6a*afsw zoSLA{(Z%th=rp9CGh(`V%S$~1~fBwWb_<5b5&+A9smpcF`lktH2AA^q2 z@TTi19#D{q2dFYmi3f~DJ+AHR|NA|ivLs|TJSB>f+>(6XlYTF6Z}^hfACT0<`@X(D zZ)zSN`uEdwc#+>Xi@~}kit`s|dQr#Ab$#>K-}|WjGavYf&u?yV=i}smyM3&d{FTV+ zzjr@YDhN);rt;}N|MY*;-lX@u&dqh;=WuSH!%KO+r{@Lqumi-FgPX2f?@{ssDLp~W zcJ$@t=6C9-USI0zM7{n`*Bg2S%pp#%8`gVnapUv!&&l7HP-l?l{ptR`66dGv$K;|Huclgj;4+|9`swAB_xB{@>!TkL^BS$yM8HBhh!W1wj`Z`~({l>?gJG-dJ*SG|Gq)X{rk-O%=?#-r!N<7&OQI9`~EaFwGOBCX!V07 zf@`pf{QszZc=+vuJz2p(p?1f}v1$=<$loS z*B^N)TcgwITIJ#RM2kGc{QvRVJB+i8vy8Ks4rlp!;^%2TK2IMD|2Nj6@Be_M&QV)M z>b@0tZ64X(q|ekFhvDx}wElI1`JefJK0YsuC-JtWG z|C8+hGt}2cA`zPRQx$c)(Dw)3pYpN(gLkSmdSkJS{J-S&>QnsR%K86~#|~h;V!WD< zc*XmY_a*PkOXn9{4F7}vTW8Pznwm&@-k-at_NXNz&pu{%E^QZtV(5lHb+9^1=o7_? zBR|U)q@cKYv>|Ht^x_|N!%>F}SQr-l7I%`yL*>*_T%cI#U7{XekG zH5jRS?@s9cTFcDD?bBa*m;+>jJpgM!D8$PAKezS|U1Mq4&3TZCTlL?myeVng8dO|M`0J^`_^L_W|z%-Us{~ zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d zFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&dFb*&d TFb*&dFb*&dFb>RL9QgkMJ!A(+ literal 0 HcmV?d00001 From 2a778ded2fc3503f44364526cdabc142eda37691 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 12:29:20 +0000 Subject: [PATCH 056/107] Updated translations --- translations/translation_de.xml | 25 +++++++++++++++++++++---- translations/translation_en.xml | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 93770f193..33aa49ecf 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -830,15 +830,32 @@ Als erstes muss auf der Helferkarte der Lade-Marker gesetzt werden. Dieses erkennt vorhandende Haufen oder Silos und markiert den ausgewählten. Anschließend kann der Helfer gestartet werden. "/> - + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index b1bf7ddca..3728c719f 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -830,7 +830,7 @@ To start the loading-marker on the AI menu needs to be set. The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. "/> - + From 58bce751bee00e0d12579f11d2b79fae48533de4 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:37:54 +0200 Subject: [PATCH 057/107] TPS extension --- config/HelpMenu.xml | 3 +++ config/MasterTranslations.xml | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/config/HelpMenu.xml b/config/HelpMenu.xml index f6667d36b..42b8e24ca 100644 --- a/config/HelpMenu.xml +++ b/config/HelpMenu.xml @@ -234,6 +234,9 @@ + diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 6068736c8..d63fbdc8f 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2543,6 +2543,17 @@ Das Kreuz sollte jetzt wie im Bild dar gestellt gelb sein. WIP ]]> + + From 7c14f92337aa15772d4548f42fc5fb400cfeedf5 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 24 Aug 2023 14:40:27 +0200 Subject: [PATCH 058/107] ups --- config/HelpMenu.xml | 3 +-- config/MasterTranslations.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config/HelpMenu.xml b/config/HelpMenu.xml index 42b8e24ca..0be47f0d8 100644 --- a/config/HelpMenu.xml +++ b/config/HelpMenu.xml @@ -237,7 +237,6 @@ - - + \ No newline at end of file diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index d63fbdc8f..f7cee8016 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2543,7 +2543,6 @@ Das Kreuz sollte jetzt wie im Bild dar gestellt gelb sein. WIP ]]> - + + toolOffsetX + noReverse + turnRadius + workingWidth + balerUnloadDistance + directionNodeOffsetZ + implementWheelAlwaysOnGround + ignoreCollisionBoxesWhenFolded + baleCollectorOffset + disableUnfolding + raiseLate + lowerEarly + useVehicleSizeForMarkers + armMovingToolIx + movingToolIx + shovelMovingToolIx + ignoreBaleCollisionForward + fixWheelLoaderDirectionNodeByMovingToolIx + @@ -343,6 +367,9 @@ You can define the following custom settings: + . --- ---@class VehicleConfigurations : CpObject VehicleConfigurations = CpObject() -VehicleConfigurations.XML_KEY = "VehicleConfigurations.Vehicle" +VehicleConfigurations.BASE_KEY = "VehicleConfigurations" +VehicleConfigurations.XML_KEY = VehicleConfigurations.BASE_KEY .. ".Vehicle" +VehicleConfigurations.XML_CONFIGURATION_KEY = VehicleConfigurations.BASE_KEY .. ".Configurations.Configuration" VehicleConfigurations.MOD_NAME = g_currentModName ---- All attributes and the data type. -VehicleConfigurations.attributes = { - toolOffsetX = XMLValueType.FLOAT, - noReverse = XMLValueType.BOOL, - turnRadius = XMLValueType.FLOAT, - workingWidth = XMLValueType.FLOAT, - balerUnloadDistance = XMLValueType.FLOAT, - directionNodeOffsetZ = XMLValueType.FLOAT, - implementWheelAlwaysOnGround = XMLValueType.BOOL, - ignoreCollisionBoxesWhenFolded = XMLValueType.BOOL, - baleCollectorOffset = XMLValueType.FLOAT, - disableUnfolding = XMLValueType.BOOL, - raiseLate = XMLValueType.BOOL, - lowerEarly = XMLValueType.BOOL, - useVehicleSizeForMarkers = XMLValueType.BOOL, - armMovingToolIx = XMLValueType.INT, - movingToolIx = XMLValueType.INT, - shovelMovingToolIx = XMLValueType.INT, - ignoreBaleCollisionForward = XMLValueType.BOOL, -} - function VehicleConfigurations:init() self.vehicleConfigurations = {} self.modVehicleConfigurations = {} + self.attributes = {} if g_currentMission then self:loadFromXml() end @@ -64,17 +46,15 @@ function VehicleConfigurations:registerXmlSchema() self.xmlSchema = XMLSchema.new("vehicleConfigurations") self.xmlSchema:register(XMLValueType.STRING,self.XML_KEY.."(?)#name","Configuration name") self.xmlSchema:register(XMLValueType.STRING,self.XML_KEY.."(?)#modName","Mod name") --- Optional to avoid conflict for xml files with the same name. - for name,xmlType in pairs(VehicleConfigurations.attributes) do - self.xmlSchema:register(xmlType,self.XML_KEY.."(?)#"..name,"Configuration value") - end + self.xmlSchema:register(XMLValueType.STRING,self.XML_CONFIGURATION_KEY.."(?)#type","Configuration value type") + self.xmlSchema:register(XMLValueType.STRING,self.XML_CONFIGURATION_KEY.."(?)","Configuration name") end function VehicleConfigurations:loadFromXml() - self:registerXmlSchema() self.xmlFileName = Utils.getFilename('config/VehicleConfigurations.xml', Courseplay.BASE_DIRECTORY) - self.xmlFile = self:loadXmlFile(self.xmlFileName) + self:registerXmlSchema() + self.xmlFile = self:loadXmlFile(self.xmlFileName, true) self.userXmlFileName = getUserProfileAppPath() .. 'modSettings/'..VehicleConfigurations.MOD_NAME..'/vehicleConfigurations.xml' - self.userXmlFile = self:loadXmlFile(self.userXmlFileName) end @@ -113,19 +93,39 @@ function VehicleConfigurations:readVehicle(xmlFile, vehicleElement) end end -function VehicleConfigurations:loadXmlFile(fileName) + +function VehicleConfigurations:loadXmlFile(fileName, loadConfig) CpUtil.info('Loading vehicle configuration from %s ...', fileName) local xmlFile = XMLFile.loadIfExists("vehicleConfigurationsXmlFile",fileName, self.xmlSchema) if xmlFile then - xmlFile:iterate(self.XML_KEY, function (ix, key) - self:readVehicle(xmlFile, key) - end) + if not loadConfig or self:loadConfigurations(xmlFile) then + xmlFile:iterate(self.XML_KEY, function (ix, key) + self:readVehicle(xmlFile, key) + end) + end xmlFile:delete() else CpUtil.info('Vehicle configuration file %s does not exist.', fileName) end end +function VehicleConfigurations:loadConfigurations(xmlFile) + self.attributes = {} + xmlFile:iterate(self.XML_CONFIGURATION_KEY, function(ix, key) + local type = xmlFile:getValue(key .. "#type"):upper() + local name = xmlFile:getValue(key) + self.attributes[name] = XMLValueType[type] + if self.attributes[name] == nil then + CpUtil.info("Vehicle configuration %s has no valid type for %s!", name, type) + end + end) + for name, xmlType in pairs(self.attributes) do + CpUtil.info("Registered %s", name) + self.xmlSchema:register(xmlType, self.XML_KEY.."(?)#"..name, "Configuration value") + end + return true +end + --- Get a custom configuration value for a single vehicle/implement --- @param object table vehicle or implement object. This function uses the object's configFileName to uniquely --- identify the vehicle/implement. diff --git a/scripts/dev/DevHelper.lua b/scripts/dev/DevHelper.lua index 00c8fcc7a..b78cee93f 100644 --- a/scripts/dev/DevHelper.lua +++ b/scripts/dev/DevHelper.lua @@ -262,10 +262,22 @@ function DevHelper:showAIMarkers() CpUtil.drawDebugNode(frontMarker, false, 3) CpUtil.drawDebugNode(backMarker, false, 3) - if self.vehicle:getAIDirectionNode() then + local directionNode = self.vehicle:getAIDirectionNode() + if directionNode then CpUtil.drawDebugNode(self.vehicle:getAIDirectionNode(), false , 3, "AiDirectionNode") end - + local reverseNode = self.vehicle:getAIReverserNode() + if reverseNode and reverseNode ~= directionNode then + CpUtil.drawDebugNode(reverseNode, false , 3, "AiReverseNode") + end + local steeringNode = self.vehicle:getAISteeringNode() + if steeringNode and steeringNode ~= directionNode then + CpUtil.drawDebugNode(steeringNode, false , 3, "AiSteeringNode") + end + local articulatedAxisReverseNode = self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode + if articulatedAxisReverseNode and articulatedAxisReverseNode ~= directionNode and articulatedAxisReverseNode ~= reverseNode then + CpUtil.drawDebugNode(articulatedAxisReverseNode, false , 3, "AiArticulatedAxisReverseNode") + end end function DevHelper:togglePpcControlledNode() diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index c470e7af9..dbbd3b5c0 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -54,6 +54,7 @@ function CpAISiloLoaderWorker.registerOverwrittenFunctions(vehicleType) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCpStartText', CpAISiloLoaderWorker.getCpStartText) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'startCpAtFirstWp', CpAISiloLoaderWorker.startCpAtFirstWp) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'startCpAtLastWp', CpAISiloLoaderWorker.startCpAtLastWp) + SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIDirectionNode", CpAISiloLoaderWorker.getAIDirectionNode) end ------------------------------------------------------------------------------------------------------------------------ --- Event listeners @@ -191,4 +192,17 @@ end function CpAISiloLoaderWorker:stopCpSiloLoaderWorker() self:stopCpDriver() +end + +--- Fixes the Direction for the platinum wheel loader, as +--- their direction is not updated base on the rotation. +--- So we use the parent node of the arm tool node. +---@param superFunc any +function CpAISiloLoaderWorker:getAIDirectionNode(superFunc) + if not self:getIsCpActive() then return superFunc(self) end + local movingToolIx = g_vehicleConfigurations:get(self, "fixWheelLoaderDirectionNodeByMovingToolIx") + if movingToolIx ~= nil then + return getParent(self.spec_cylindered.movingTools[movingToolIx].node) + end + return superFunc(self) end \ No newline at end of file From e66e3e59586de1cd6e024549041cd6c8d55d8dd9 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Sat, 2 Sep 2023 08:17:32 -0400 Subject: [PATCH 061/107] fix: workaround for Platinum reversing --- scripts/specializations/CpAISiloLoaderWorker.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index dbbd3b5c0..d2661bba7 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -201,7 +201,15 @@ end function CpAISiloLoaderWorker:getAIDirectionNode(superFunc) if not self:getIsCpActive() then return superFunc(self) end local movingToolIx = g_vehicleConfigurations:get(self, "fixWheelLoaderDirectionNodeByMovingToolIx") - if movingToolIx ~= nil then + if movingToolIx ~= nil then + -- Fix the Platinum wheel loader's "revereser" node, which is pointing backwards instead forwards + -- like on every other articulated axis vehicle + -- TODO: this should probably be in an appended function to ArticulatedAxis + -- TODO: maybe even add a new node and use that, instead of rotating the original node + -- TODO: add a separate vehicle config entry just for this, in case not all the Platinum vehicles are screwed up the same way? + if self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode then + setRotation(self.spec_articulatedAxis.aiRevereserNode, 0, 0, 0) + end return getParent(self.spec_cylindered.movingTools[movingToolIx].node) end return superFunc(self) From 87782a65901ea7e9e3c4eb708de43688bfb80cfe Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Sat, 2 Sep 2023 08:20:12 -0400 Subject: [PATCH 062/107] refactor: use reverser node for articulate axis --- scripts/ai/AIReverseDriver.lua | 136 ++++++++++++++++----------------- 1 file changed, 65 insertions(+), 71 deletions(-) diff --git a/scripts/ai/AIReverseDriver.lua b/scripts/ai/AIReverseDriver.lua index a8c4eb3da..8db253599 100644 --- a/scripts/ai/AIReverseDriver.lua +++ b/scripts/ai/AIReverseDriver.lua @@ -63,90 +63,84 @@ function AIReverseDriver:debug(...) end function AIReverseDriver:getDriveData() - if self.reversingImplement == nil then - -- no wheeled implement, simple reversing the PPC can handle by itself - return nil - end + if self.reversingImplement == nil then + -- no wheeled implement, simple reversing the PPC can handle by itself + return nil + end - -- if there's a reverser node on the tool, use that, otherwise the steering node - -- the reverser direction node, if exists, works better for tools with offset or for - -- rotating plows where it remains oriented and placed correctly - local trailerNode = AIVehicleUtil.getAIToolReverserDirectionNode(self.vehicle) or self.reversingImplement.steeringAxleNode - local trailerFrontNode = self.reversingImplement.reversingProperties.frontNode - - local tx, ty, tz = self.ppc:getGoalPointPosition() - local lxTrailer, lzTrailer = AIVehicleUtil.getDriveDirection(trailerNode, tx, ty, tz) - self:showDirection(trailerNode, lxTrailer, lzTrailer, 1, 0, 0) - - local maxTractorAngle = math.rad(75) - - -- for articulated vehicles use the articulated axis' rotation node as it is a better indicator or the - -- vehicle's orientation than the direction node which often turns/moves with an articulated vehicle part - -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already - local tractorNode - local useArticulatedAxisRotationNode = SpecializationUtil.hasSpecialization(ArticulatedAxis, self.vehicle.specializations) and self.vehicle.spec_articulatedAxis.rotationNode - if useArticulatedAxisRotationNode then - tractorNode = self.vehicle.spec_articulatedAxis.rotationNode - else - tractorNode = self.vehicle:getAIDirectionNode() - end + local trailerNode = self.reversingImplement.steeringAxleNode + local xTipper, yTipper, zTipper = getWorldTranslation(trailerNode); - local lx, lz, angleDiff + local trailerFrontNode = self.reversingImplement.reversingProperties.frontNode + local xFrontNode,yFrontNode,zFrontNode = getWorldTranslation(trailerFrontNode) - if self.reversingImplement.reversingProperties.isPivot then - -- The trailer/implement has a front axle (or dolly) with a draw bar. - -- The current Courseplay dev team has no idea how this works :), this is magic - -- from the old code, written by Satissis (Claus). - -- TODO: adapt a documented algorithm for these trailers - local xTrailer, yTrailer, zTrailer = getWorldTranslation(trailerNode); - local xFrontNode, yFrontNode, zFrontNode = getWorldTranslation(trailerFrontNode) - local lxFrontNode, lzFrontNode = AIVehicleUtil.getDriveDirection(trailerFrontNode, xTrailer, yTrailer, zTrailer) - self:showDirection(trailerFrontNode, lxFrontNode, lzFrontNode, 0, 1, 0) + local tx, ty, tz = self.ppc:getGoalPointPosition() - local lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(tractorNode, xFrontNode, yFrontNode, zFrontNode) - self:showDirection(tractorNode, lxTractor, lzTractor, 0, 0.7, 0) + local lxTipper, lzTipper = AIVehicleUtil.getDriveDirection(trailerNode, tx, ty, tz) - local rotDelta = (self.reversingImplement.reversingProperties.nodeDistance * - (0.5 - (0.023 * self.reversingImplement.reversingProperties.nodeDistance - 0.073))) - local trailerToWaypointAngle = self:getLocalYRotationToPoint(trailerNode, tx, ty, tz, -1) * rotDelta - trailerToWaypointAngle = MathUtil.clamp(trailerToWaypointAngle, -math.rad(90), math.rad(90)) + self:showDirection(trailerNode, lxTipper, lzTipper, 1, 0, 0) - local dollyToTrailerAngle = self:getLocalYRotationToPoint(trailerFrontNode, xTrailer, yTrailer, zTrailer, -1) + local lxFrontNode, lzFrontNode = AIVehicleUtil.getDriveDirection(trailerFrontNode, xTipper, yTipper, zTipper) - local tractorToDollyAngle = self:getLocalYRotationToPoint(tractorNode, xFrontNode, yFrontNode, zFrontNode, -1) + local lxTractor, lzTractor = 0, 0 - local rearAngleDiff = (dollyToTrailerAngle - trailerToWaypointAngle) - rearAngleDiff = MathUtil.clamp(rearAngleDiff, -math.rad(45), math.rad(45)) + local maxTractorAngle = math.rad(75) - local frontAngleDiff = (tractorToDollyAngle - dollyToTrailerAngle) - frontAngleDiff = MathUtil.clamp(frontAngleDiff, -math.rad(45), math.rad(45)) + -- for articulated vehicles use the articulated axis' rotation node as it is a better indicator or the + -- vehicle's orientation than the direction node which often turns/moves with an articulated vehicle part + -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already + local tractorNode + if self.vehicle.spec_articulatedAxis then + tractorNode = AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) + else + tractorNode = self.vehicle:getAIDirectionNode() + end - angleDiff = (frontAngleDiff - rearAngleDiff) * - (1.5 - (self.reversingImplement.reversingProperties.nodeDistance * 0.4 - 0.9) + rotDelta) - angleDiff = MathUtil.clamp(angleDiff, -math.rad(45), math.rad(45)) + local lx, lz, angleDiff + + if self.reversingImplement.reversingProperties.isPivot then + self:showDirection(trailerFrontNode, lxFrontNode, lzFrontNode, 0, 1, 0) - lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) - else - -- the trailer/implement is like a semi-trailer, has a rear axle only, the front of the implement - -- is supported by the tractor - local crossTrackError, orientationError, curvatureError, currentHitchAngle = self:calculateErrors(tractorNode, trailerNode) - angleDiff = self:calculateHitchCorrectionAngle(crossTrackError, orientationError, curvatureError, currentHitchAngle) - angleDiff = MathUtil.clamp(angleDiff, -maxTractorAngle, maxTractorAngle) + lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(tractorNode, xFrontNode, yFrontNode, zFrontNode) + self:showDirection(tractorNode,lxTractor, lzTractor, 0, 0.7, 0) - lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) - end + local rotDelta = (self.reversingImplement.reversingProperties.nodeDistance * + (0.5 - (0.023 * self.reversingImplement.reversingProperties.nodeDistance - 0.073))) + local trailerToWaypointAngle = self:getLocalYRotationToPoint(trailerNode, tx, ty, tz, -1) * rotDelta + trailerToWaypointAngle = MathUtil.clamp(trailerToWaypointAngle, -math.rad(90), math.rad(90)) - self:showDirection(tractorNode, lx, lz, 0.7, 0, 1) - -- do a little bit of damping if using the articulated axis as lx tends to oscillate around 0 which results in the - -- speed adjustment kicking in and slowing down the vehicle. - if useArticulatedAxisRotationNode and math.abs(lx) < 0.04 then - lx = 0 - end - -- construct an artificial goal point to drive to - lx, lz = -lx * self.ppc:getLookaheadDistance(), -lz * self.ppc:getLookaheadDistance() - -- AIDriveStrategy wants a global position to drive to (which it later converts to local, but whatever...) - local gx, _, gz = localToWorld(self.vehicle:getAIDirectionNode(), lx, 0, lz) - return gx, gz, false, self.settings.reverseSpeed:getValue() + local dollyToTrailerAngle = self:getLocalYRotationToPoint(trailerFrontNode, xTipper, yTipper, zTipper, -1) + + local tractorToDollyAngle = self:getLocalYRotationToPoint(tractorNode, xFrontNode, yFrontNode, zFrontNode, -1) + + local rearAngleDiff = (dollyToTrailerAngle - trailerToWaypointAngle) + rearAngleDiff = MathUtil.clamp(rearAngleDiff, -math.rad(45), math.rad(45)) + + local frontAngleDiff = (tractorToDollyAngle - dollyToTrailerAngle) + frontAngleDiff = MathUtil.clamp(frontAngleDiff, -math.rad(45), math.rad(45)) + + angleDiff = (frontAngleDiff - rearAngleDiff) * + (1.5 - (self.reversingImplement.reversingProperties.nodeDistance * 0.4 - 0.9) + rotDelta) + angleDiff = MathUtil.clamp(angleDiff, -math.rad(45), math.rad(45)) + + lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) + else + local crossTrackError, orientationError, curvatureError, currentHitchAngle = self:calculateErrors(tractorNode, trailerNode) + angleDiff = self:calculateHitchCorrectionAngle(crossTrackError, orientationError, curvatureError, currentHitchAngle) + angleDiff = MathUtil.clamp(angleDiff, -maxTractorAngle, maxTractorAngle) + + lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) + end + + self:showDirection(tractorNode, lx, lz, 0.7, 0, 1) + -- do a little bit of damping if using the articulated axis as lx tends to oscillate around 0 which results in the + -- speed adjustment kicking in and slowing down the vehicle. + if useArticulatedAxisRotationNode and math.abs(lx) < 0.04 then lx = 0 end + -- construct an artificial goal point to drive to + lx, lz = -lx * self.ppc:getLookaheadDistance(), -lz * self.ppc:getLookaheadDistance() + -- AIDriveStrategy wants a global position to drive to (which it later converts to local, but whatever...) + local gx, _, gz = localToWorld(self.vehicle:getAIDirectionNode(), lx, 0, lz) + return gx, gz, false, self.settings.reverseSpeed:getValue() end function AIReverseDriver:getLocalYRotationToPoint(node, x, y, z, direction) From 46fa1f6f2decbcf8df41bac369a651a8a865b3ba Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Sat, 2 Sep 2023 08:39:13 -0400 Subject: [PATCH 063/107] doc: reverser node comment updated --- scripts/ai/AIReverseDriver.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ai/AIReverseDriver.lua b/scripts/ai/AIReverseDriver.lua index 8db253599..ab3f9f3ff 100644 --- a/scripts/ai/AIReverseDriver.lua +++ b/scripts/ai/AIReverseDriver.lua @@ -86,7 +86,7 @@ function AIReverseDriver:getDriveData() local maxTractorAngle = math.rad(75) - -- for articulated vehicles use the articulated axis' rotation node as it is a better indicator or the + -- for articulated vehicles use the articulated axis' reverser node as it is a better indicator or the -- vehicle's orientation than the direction node which often turns/moves with an articulated vehicle part -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already local tractorNode From 6032bb2302232a904a01a618922251c3ca42d92a Mon Sep 17 00:00:00 2001 From: Tensuko Date: Sat, 2 Sep 2023 14:54:23 +0200 Subject: [PATCH 064/107] Add config for l120H and add turn radius for both wheelloader --- config/VehicleConfigurations.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index b3c537c92..1ab06411b 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -369,6 +369,14 @@ You can define the following custom settings: /> + + From 9933b5ce96a329d3e63b868ec85c5bcb6d3453be Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sat, 2 Sep 2023 18:51:24 +0200 Subject: [PATCH 065/107] Callstack and code adjustments fix and code adjustments --- config/VehicleConfigurations.xml | 6 + .../ai/AIDriveStrategyShovelSiloLoader.lua | 10 +- scripts/ai/ImplementUtil.lua | 3 +- scripts/ai/Markers.lua | 37 ++++- scripts/ai/controllers/ShovelController.lua | 2 +- scripts/config/VehicleConfigurations.lua | 2 +- scripts/dev/DevHelper.lua | 31 ++-- .../specializations/CpAISiloLoaderWorker.lua | 22 --- scripts/specializations/CpAIWorker.lua | 35 ++++ scripts/specializations/CpShovelPositions.lua | 151 +++++++++++------- 10 files changed, 198 insertions(+), 101 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 1ab06411b..8b6dc6c0e 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -116,6 +116,9 @@ You can define the following custom settings: Fixes the ai direction node for the platinum wheel loaders, as their direction is not changed based on the rotation. As a fix the parent node of the arm moving tool ix is used. +- articulatedAxisReverseNodeInverted + Is the reverse node for articulated axis vehicles rotated by 180 degree? + --> @@ -137,6 +140,7 @@ You can define the following custom settings: shovelMovingToolIx ignoreBaleCollisionForward fixWheelLoaderDirectionNodeByMovingToolIx + articulatedAxisReverseNodeInverted @@ -369,6 +373,7 @@ You can define the following custom settings: /> diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 023c622ef..302de4f70 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -130,8 +130,8 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) local dirX, dirZ = self.silo:getLengthDirection() local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, yRot) - self.siloAreaToAvoid = PathfinderUtil.NodeArea(self.siloFrontNode, -self.silo:getWidth()/2 - 1, - -1, self.silo:getWidth() + 2, self.silo:getLength() + 2) + self.siloAreaToAvoid = PathfinderUtil.NodeArea(self.siloFrontNode, -self.silo:getWidth()/2 - 3, + -3, self.silo:getWidth() + 6, self.silo:getLength() + 6) self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) end @@ -159,7 +159,9 @@ function AIDriveStrategyShovelSiloLoader:setAllStaticParameters() self.reverser = AIReverseDriver(self.vehicle, self.ppc) self.proximityController = ProximityController(self.vehicle, self:getWorkWidth()) self.proximityController:registerIgnoreObjectCallback(self, self.ignoreProximityObject) - self:setFrontAndBackMarkers() + Markers.setMarkerNodes(self.vehicle) + self.frontMarkerNode, self.backMarkerNode, self.frontMarkerDistance, self.backMarkerDistance = + Markers.getMarkerNodes(self.vehicle) self.siloEndProximitySensor = SingleForwardLookingProximitySensorPack(self.vehicle, self.shovelController:getShovelNode(), 5, 1) self.heapNode = CpUtil.createNode("heapNode", 0, 0, 0, nil) self.lastTrailerSearch = 0 @@ -635,7 +637,7 @@ end function AIDriveStrategyShovelSiloLoader:startReversingAwayFromUnloading() local _, _, spaceToTrailer = localToLocal(self.shovelController:getShovelNode(), self.vehicle:getAIDirectionNode(), 0, 0, 0) local course = Course.createStraightReverseCourse(self.vehicle, 2*spaceToTrailer, - 0, self.vehicle.rootNode ) + 0, self.vehicle:getAIDirectionNode() ) self:startCourse(course, 1) self:setNewState(self.states.REVERSING_AWAY_FROM_UNLOAD) end diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 236e24ddc..dd1b62ac8 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -366,9 +366,10 @@ end ---@param tool table moving tool ---@param dt number ---@param rotTarget number target rotation in radiant +---@return boolean function ImplementUtil.moveMovingToolToRotation(implement, tool, dt, rotTarget) if tool.rotSpeed == nil then - return + return false end local spec = implement.spec_cylindered tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) diff --git a/scripts/ai/Markers.lua b/scripts/ai/Markers.lua index 2aeb6d8b2..217a04583 100644 --- a/scripts/ai/Markers.lua +++ b/scripts/ai/Markers.lua @@ -15,6 +15,13 @@ Markers = {} +function Markers.registerConsoleCommands() + g_devHelper.consoleCommands:registerConsoleCommand("cpFrontAndBackerMarkerCalculate", + "Calculates the front and back markers", "consoleCommandReload", Markers) + g_devHelper.consoleCommands:registerConsoleCommand("cpFrontAndBackerMarkerPrintDebug", + "Print Marker data", "consoleCommandPrintDebug", Markers) +end +Markers.registerConsoleCommands() -- a global table with the vehicle as the key to persist the marker nodes we don't want to leak through jobs -- and also don't want to deal with keeping track when to delete them g_vehicleMarkers = {} @@ -119,4 +126,32 @@ function Markers.getMarkerNodes(vehicle) local frontMarker = Markers.getFrontMarkerNode(vehicle) local backMarker = Markers.getBackMarkerNode(vehicle) return frontMarker, backMarker, g_vehicleMarkers[vehicle].frontMarkerOffset, g_vehicleMarkers[vehicle].backMarkerOffset -end \ No newline at end of file +end + +-------------------------------------------- +--- Console Commands +-------------------------------------------- + +function Markers:consoleCommandReload(backDistance) + local vehicle = g_currentMission.controlledVehicle + if not vehicle then + CpUtil.info("No valid vehicle entered!") + return + end + if backDistance then + backDistance = tonumber(backDistance) + end + Markers.setMarkerNodes(vehicle, backDistance) + Markers:consoleCommandPrintDebug() +end + +function Markers:consoleCommandPrintDebug() + local vehicle = g_currentMission.controlledVehicle + if not vehicle then + CpUtil.info("No valid vehicle entered!") + return + end + local _, frontMarkerDistance = Markers.getFrontMarkerNode(vehicle) + local _, backMarkerDistance = Markers.getBackMarkerNode(vehicle) + CpUtil.infoVehicle(vehicle, "Front distance: %.2f, back distance: %.2f", frontMarkerDistance, backMarkerDistance) +end diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 9abeb7c45..7d5034bd3 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -116,7 +116,7 @@ function ShovelController:calculateMinimalUnloadingHeight(triggerNode) self.TRIGGER_HEIGHT_RAYCAST_COLLISION_MASK) if not self.objectWasHit then self:debug("Finished raycast with minimal height: %.2f", i) - self.implement:setCpShovelMinimalUnloadHeight(i + 0.25) + self.implement:setCpShovelMinimalUnloadHeight(i + 1) return true end end diff --git a/scripts/config/VehicleConfigurations.lua b/scripts/config/VehicleConfigurations.lua index f5b917c3a..f56edeee1 100644 --- a/scripts/config/VehicleConfigurations.lua +++ b/scripts/config/VehicleConfigurations.lua @@ -133,7 +133,7 @@ end --- @return any|nil the value of the configuration attribute or nil if there's no custom config for it function VehicleConfigurations:get(object, attribute) if not self:isValidAttribute(attribute) then - CpUtil.infoImplement(object, "The given attribute name: %s is not valid!", attribute) + CpUtil.infoImplement(object, "The given vehicle config attribute name: %s is not valid!", attribute) return end if object and object.configFileNameClean then diff --git a/scripts/dev/DevHelper.lua b/scripts/dev/DevHelper.lua index b78cee93f..e21336e55 100644 --- a/scripts/dev/DevHelper.lua +++ b/scripts/dev/DevHelper.lua @@ -39,15 +39,26 @@ function DevHelper:debug(...) CpUtil.info(string.format(...)) end +--- Makes sure deleting of the selected vehicle can be detected +function DevHelper:removedSelectedVehicle() + self.vehicle = nil +end + function DevHelper:update() if not self.isEnabled then return end local lx, lz, hasCollision, vehicle -- make sure not calling this for something which does not have courseplay installed (only ones with spec_aiVehicle) - if g_currentMission.controlledVehicle and g_currentMission.controlledVehicle.spec_aiVehicle then - + if g_currentMission.controlledVehicle and g_currentMission.controlledVehicle.spec_cpAIWorker then + if self.vehicle ~= g_currentMission.controlledVehicle then + if self.vehicle then + self.vehicle:removeDeleteListener(self, "removedSelectedVehicle") + end + --self.vehicleData = PathfinderUtil.VehicleData(g_currentMission.controlledVehicle, true) + end self.vehicle = g_currentMission.controlledVehicle + self.vehicle:addDeleteListener(self, "removedSelectedVehicle") self.node = g_currentMission.controlledVehicle:getAIDirectionNode() lx, _, lz = localDirectionToWorld(self.node, 0, 0, 1) @@ -264,19 +275,19 @@ function DevHelper:showAIMarkers() local directionNode = self.vehicle:getAIDirectionNode() if directionNode then - CpUtil.drawDebugNode(self.vehicle:getAIDirectionNode(), false , 3, "AiDirectionNode") + CpUtil.drawDebugNode(self.vehicle:getAIDirectionNode(), false , 4, "AiDirectionNode") end local reverseNode = self.vehicle:getAIReverserNode() - if reverseNode and reverseNode ~= directionNode then - CpUtil.drawDebugNode(reverseNode, false , 3, "AiReverseNode") + if reverseNode then + CpUtil.drawDebugNode(reverseNode, false , 4.5, "AiReverseNode") end local steeringNode = self.vehicle:getAISteeringNode() - if steeringNode and steeringNode ~= directionNode then - CpUtil.drawDebugNode(steeringNode, false , 3, "AiSteeringNode") + if steeringNode then + CpUtil.drawDebugNode(steeringNode, false , 5, "AiSteeringNode") end - local articulatedAxisReverseNode = self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode - if articulatedAxisReverseNode and articulatedAxisReverseNode ~= directionNode and articulatedAxisReverseNode ~= reverseNode then - CpUtil.drawDebugNode(articulatedAxisReverseNode, false , 3, "AiArticulatedAxisReverseNode") + local articulatedAxisReverseNode = AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) + if articulatedAxisReverseNode then + CpUtil.drawDebugNode(articulatedAxisReverseNode, false , 5.5, "AiArticulatedAxisReverseNode") end end diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index d2661bba7..da718578f 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -54,7 +54,6 @@ function CpAISiloLoaderWorker.registerOverwrittenFunctions(vehicleType) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCpStartText', CpAISiloLoaderWorker.getCpStartText) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'startCpAtFirstWp', CpAISiloLoaderWorker.startCpAtFirstWp) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'startCpAtLastWp', CpAISiloLoaderWorker.startCpAtLastWp) - SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIDirectionNode", CpAISiloLoaderWorker.getAIDirectionNode) end ------------------------------------------------------------------------------------------------------------------------ --- Event listeners @@ -193,24 +192,3 @@ end function CpAISiloLoaderWorker:stopCpSiloLoaderWorker() self:stopCpDriver() end - ---- Fixes the Direction for the platinum wheel loader, as ---- their direction is not updated base on the rotation. ---- So we use the parent node of the arm tool node. ----@param superFunc any -function CpAISiloLoaderWorker:getAIDirectionNode(superFunc) - if not self:getIsCpActive() then return superFunc(self) end - local movingToolIx = g_vehicleConfigurations:get(self, "fixWheelLoaderDirectionNodeByMovingToolIx") - if movingToolIx ~= nil then - -- Fix the Platinum wheel loader's "revereser" node, which is pointing backwards instead forwards - -- like on every other articulated axis vehicle - -- TODO: this should probably be in an appended function to ArticulatedAxis - -- TODO: maybe even add a new node and use that, instead of rotating the original node - -- TODO: add a separate vehicle config entry just for this, in case not all the Platinum vehicles are screwed up the same way? - if self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode then - setRotation(self.spec_articulatedAxis.aiRevereserNode, 0, 0, 0) - end - return getParent(self.spec_cylindered.movingTools[movingToolIx].node) - end - return superFunc(self) -end \ No newline at end of file diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 95a27b95c..934824cb6 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -79,6 +79,8 @@ function CpAIWorker.registerOverwrittenFunctions(vehicleType) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'stopCurrentAIJob', CpAIWorker.stopCurrentAIJob) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'getCanMotorRun', CpAIWorker.getCanMotorRun) SpecializationUtil.registerOverwrittenFunction(vehicleType, 'stopFieldWorker', CpAIWorker.stopFieldWorker) + SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIReverserNode", CpAIWorker.getAIReverserNode) + SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIDirectionNode", CpAIWorker.getAIDirectionNode) end --------------------------------------------------- @@ -511,6 +513,39 @@ function CpAIWorker:getCpReverseDrivingDirectionNode() return spec.reverseDrivingDirectionNode end +--- Fixes the ai reverse node rotation for articulated axis vehicles, +--- if the node is pointing backwards and not forwards. +function CpAIWorker:getAIReverserNode(superFunc) + local spec = self.spec_cpAIWorker + -- if not self:getIsCpActive() then return superFunc(self) end + if self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode then + if g_vehicleConfigurations:get(self, "articulatedAxisReverseNodeInverted") then + if not spec.articulatedAxisReverseNode then + spec.articulatedAxisReverseNode = CpUtil.createNode( + "cpAiRevereserNode", 0, 0, 0, + getParent(self.spec_articulatedAxis.aiRevereserNode)) + end + return spec.articulatedAxisReverseNode + end + end + return superFunc(self) +end + +--- Fixes the Direction for the platinum wheel loader, as +--- their direction is not updated base on the rotation. +--- So we use the parent node of the arm tool node. +---@param superFunc any +function CpAIWorker:getAIDirectionNode(superFunc) + -- if not self:getIsCpActive() then return superFunc(self) end + local movingToolIx = g_vehicleConfigurations:get(self, "fixWheelLoaderDirectionNodeByMovingToolIx") + if movingToolIx ~= nil then + return getParent(self.spec_cylindered.movingTools[movingToolIx].node) + end + return superFunc(self) +end + + + --- TODO: Do we really need the AIDriveStrategyCollision from giants, as this one is only active for fieldwork? function CpAIWorker:isCollisionDetectionEnabled() local spec = self.spec_cpAIWorker diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 605929aec..1348fd804 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -211,6 +211,8 @@ function CpShovelPositions:onLoad(savegame) spec.state = CpShovelPositions.DEACTIVATED spec.isDirty = false spec.minimalShovelUnloadHeight = 4 + spec.highDumpMovingToolIx = g_vehicleConfigurations:get(self, "shovelMovingToolIx") + spec.isHighDumpShovel = spec.highDumpMovingToolIx ~= nil end function CpShovelPositions:onPostAttach() @@ -234,13 +236,13 @@ function CpShovelPositions:onUpdateTick(dt) return end if spec.state == CpShovelPositions.LOADING then - CpShovelPositions.updateLoadingPosition(self, dt) + CpUtil.try(CpShovelPositions.updateLoadingPosition, self, dt) elseif spec.state == CpShovelPositions.TRANSPORT then - CpShovelPositions.updateTransportPosition(self, dt) + CpUtil.try(CpShovelPositions.updateTransportPosition, self, dt) elseif spec.state == CpShovelPositions.PRE_UNLOAD then - CpShovelPositions.updatePreUnloadPosition(self, dt) + CpUtil.try(CpShovelPositions.updatePreUnloadPosition, self, dt) elseif spec.state == CpShovelPositions.UNLOADING then - CpShovelPositions.updateUnloadingPosition(self, dt) + CpUtil.try(CpShovelPositions.updateUnloadingPosition, self, dt) end end @@ -307,6 +309,54 @@ function CpShovelPositions:cpSetupShovelPositions() end end +function CpShovelPositions:controlShovelPosition(dt, targetAngle) + local spec = self.spec_cpShovelPositions + local shovelData = ImplementUtil.getShovelNode(self) + local isDirty = false + if shovelData.movingToolActivation and not spec.isHighDumpShovel then + --- The shovel has a moving tool for grabbing. + for i, tool in pairs(self.spec_cylindered.movingTools) do + if tool.axis and tool.rotMax ~= nil and tool.rotMin ~= nil then + if spec.state ~= CpShovelPositions.TRANSPORT then + --- Opens the shovel for loading and unloading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMin or tool.rotMax) + else + --- Closes the shovel after loading or unloading + isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, + tool.invertAxis and tool.rotMax or tool.rotMin) + end + break + end + end + end + local curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(spec.shovelTool.node) + local oldShovelRot = curRot[spec.shovelTool.rotationAxis] + local goalAngle = MathUtil.clamp(oldShovelRot + targetAngle, spec.shovelTool.rotMin, spec.shovelTool.rotMax) + return ImplementUtil.moveMovingToolToRotation(spec.shovelVehicle, + spec.shovelTool, dt, goalAngle) or isDirty +end + +function CpShovelPositions:unfoldHighDumpShovel(dt) + local highDumpShovelTool = self.spec_cylindered.movingTools[self.spec_cpShovelPositions.highDumpMovingToolIx] + local _, dy, _ = localDirectionToWorld(getParent(highDumpShovelTool.node), 0, 0, 1) + local angle = math.acos(dy) or 0 + local targetAngle = math.pi/2 - math.pi/6 + if CpShovelPositions.controlShovelPosition(self, dt, targetAngle - angle) then + return true, angle, targetAngle + end + local isDirty = ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, + highDumpShovelTool.invertAxis and highDumpShovelTool.rotMin or highDumpShovelTool.rotMax) + return isDirty, angle, targetAngle +end + +function CpShovelPositions:foldHighDumpShovel(dt) + local highDumpShovelTool = self.spec_cylindered.movingTools[self.spec_cpShovelPositions.highDumpMovingToolIx] + return ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, + highDumpShovelTool.invertAxis and highDumpShovelTool.rotMax or highDumpShovelTool.rotMin) +end + --- Sets the current shovel position values, like the arm and shovel rotations. ---@param dt number ---@param shovelLimits table @@ -318,6 +368,7 @@ end function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, isLoading, heightOffset, isUnloading) heightOffset = heightOffset or 0 + local spec = self.spec_cpShovelPositions local min, max = unpack(shovelLimits) --- Target angle of the shovel node, which is at the end of the shovel. local targetAngle = math.rad(min) + math.rad(max - min)/2 @@ -325,11 +376,11 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, --- Target height of the arm. --- This is relative to the attacher joint of the shovel. local targetHeight = min + (max - min)/2 - local shovelTool = self.spec_cpShovelPositions.shovelTool - local armTool = self.spec_cpShovelPositions.armTool - local shovelVehicle = self.spec_cpShovelPositions.shovelVehicle - local armVehicle = self.spec_cpShovelPositions.armVehicle - local minimalTargetHeight = self.spec_cpShovelPositions.minimalShovelUnloadHeight + local shovelTool = spec.shovelTool + local armTool = spec.armTool + local shovelVehicle = spec.shovelVehicle + local armVehicle = spec.armVehicle + local minimalTargetHeight = spec.minimalShovelUnloadHeight local curRot = {} curRot[1], curRot[2], curRot[3] = getRotation(shovelTool.node) local oldShovelRot = curRot[shovelTool.rotationAxis] @@ -338,8 +389,8 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, curRot[1], curRot[2], curRot[3] = getRotation(armTool.node) local oldArmRot = curRot[armTool.rotationAxis] - local armProjectionNode = self.spec_cpShovelPositions.armProjectionNode - local armToolRefNode = self.spec_cpShovelPositions.armToolRefNode + local armProjectionNode = spec.armProjectionNode + local armToolRefNode = spec.armToolRefNode local radiusArmToolToShovelTool = calcDistanceFrom(shovelTool.node, armTool.node) @@ -354,7 +405,8 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) local wx, _, wz = getWorldTranslation(armVehicle.rootNode) local deltaY = 0 - if isUnloading then + + if spec.state == CpShovelPositions.PRE_UNLOAD or spec.state == CpShovelPositions.UNLOADING then deltaY = minimalTargetHeight - ay end local by = shovelY @@ -406,8 +458,21 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, local hasIntersection, i1z, i1y, i2z, i2y = MathUtil.getCircleLineIntersection( az, ay, radiusArmToolToShovelTool, sz, sy, ez, ey) - - local isDirty, alpha, oldRotRelativeArmRot + + local isDirty, skipArm + if spec.state == CpShovelPositions.UNLOADING or spec.state == CpShovelPositions.PRE_UNLOAD then + if spec.isHighDumpShovel then + if spec.state == CpShovelPositions.UNLOADING then + isDirty, angle, targetAngle = CpShovelPositions.unfoldHighDumpShovel(self, dt) + end + else + isDirty = CpShovelPositions.controlShovelPosition(self, dt, targetAngle - angle) + end + if spec.state == CpShovelPositions.UNLOADING and isDirty then + skipArm = true + end + end + local alpha, oldRotRelativeArmRot = 0, 0 if hasIntersection then --- Controls the arm height setTranslation(armProjectionNode, 0, i1y, i1z) @@ -418,57 +483,21 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, alpha = math.atan2(i1y - ay, i1z - az) local beta = -math.atan2(i2y - ay, i2z - az) - local angle = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference( + local a = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference( alpha, oldRotRelativeArmRot), armTool.rotMin, armTool.rotMax) - isDirty = ImplementUtil.moveMovingToolToRotation( - armVehicle, armTool, dt, angle) + if not skipArm then + isDirty = ImplementUtil.moveMovingToolToRotation( + armVehicle, armTool, dt, a) or isDirty + end end - --- Controls the arm extension - - - local highDumpShovelTool - local highDumpShovelIx = g_vehicleConfigurations:get(self, "shovelMovingToolIx") - if highDumpShovelIx ~= nil then - highDumpShovelTool = self.spec_cylindered.movingTools[highDumpShovelIx] - if isUnloading then - --- Makes sure the shovel is almost vertical for the high dump functionality - local _, dy, _ = localDirectionToWorld(getParent(highDumpShovelTool.node), 0, 0, 1) - angle = math.acos(dy) - targetAngle = math.pi/2 - math.pi/6 - else - isDirty = ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, - highDumpShovelTool.invertAxis and highDumpShovelTool.rotMax or highDumpShovelTool.rotMin) or isDirty + if spec.state ~= CpShovelPositions.UNLOADING then + if spec.isHighDumpShovel then + isDirty = CpShovelPositions.foldHighDumpShovel(self, dt) or isDirty end - else - local shovelData = ImplementUtil.getShovelNode(self) - if shovelData.movingToolActivation then - --- The shovel has a moving tool for grabbing. - for i, tool in pairs(self.spec_cylindered.movingTools) do - if tool.axis then - if isLoading or isUnloading then - --- Opens the shovel for loading and unloading - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMin or tool.rotMax) or isDirty - else - --- Closes the shovel after loading - isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, - tool.invertAxis and tool.rotMax or tool.rotMin) or isDirty - end - break - end - end - end - end - local deltaAngle = targetAngle - angle - local goalAngle = MathUtil.clamp(oldShovelRot + deltaAngle, shovelTool.rotMin, shovelTool.rotMax) - isDirty = ImplementUtil.moveMovingToolToRotation(shovelVehicle, - shovelTool, dt, goalAngle) or isDirty - if isUnloading and highDumpShovelTool then - --- Uses the high dump shovel functionality. - isDirty = isDirty or ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, - highDumpShovelTool.invertAxis and highDumpShovelTool.rotMin or highDumpShovelTool.rotMax) + isDirty = isDirty or CpShovelPositions.controlShovelPosition(self, dt, targetAngle - angle) end + --- Debug information if g_currentMission.controlledVehicle == shovelVehicle.rootVehicle and CpDebug:isChannelActive(CpDebug.DBG_SILO, shovelVehicle.rootVehicle) then @@ -517,7 +546,7 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, table.insert(debugData, { name = "angle", value = math.deg(angle) }) table.insert(debugData, { - name = "deltaAngle", value = math.deg(deltaAngle) }) + name = "deltaAngle", value = math.deg(targetAngle - angle) }) table.insert(debugData, { name = "targetAngle", value = math.deg(targetAngle) }) table.insert(debugData, { From 0e727c09f85a41b79bd112b4a5aba155622a9480 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 3 Sep 2023 10:16:37 +0200 Subject: [PATCH 066/107] Some more adjustments and fixes --- .../ai/AIDriveStrategyShovelSiloLoader.lua | 61 +++- scripts/ai/ImplementUtil.lua | 11 +- scripts/ai/controllers/ShovelController.lua | 177 ++++++++++- scripts/ai/controllers/TrailerController.lua | 6 +- scripts/ai/jobs/CpAIJobSiloLoader.lua | 6 +- scripts/ai/tasks/CpAITaskSiloLoader.lua | 4 +- .../specializations/CpAISiloLoaderWorker.lua | 4 +- scripts/specializations/CpAIWorker.lua | 2 +- scripts/specializations/CpShovelPositions.lua | 284 +++++++++--------- scripts/trigger/TriggerWrapper.lua | 8 +- 10 files changed, 389 insertions(+), 174 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 302de4f70..5653b0c6e 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -36,12 +36,10 @@ local AIDriveStrategyShovelSiloLoader_mt = Class(AIDriveStrategyShovelSiloLoader AIDriveStrategyShovelSiloLoader.myStates = { DRIVING_ALIGNMENT_COURSE = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, - DRIVING_INTO_SILO = {shovelPosition = ShovelController.POSITIONS.LOADING, shovelMovingSpeed = 0}, DRIVING_OUT_OF_SILO = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, - + DRIVING_TEMPORARY_OUT_OF_SILO = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, WAITING_FOR_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, - DRIVING_TO_UNLOAD_POSITION = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD_TRAILER = {shovelPosition = ShovelController.POSITIONS.TRANSPORT}, DRIVING_TO_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, @@ -53,7 +51,7 @@ AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 30 AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8 - +AIDriveStrategyShovelSiloLoader.isStuckMs = 1000 * 15 function AIDriveStrategyShovelSiloLoader.new(customMt) if customMt == nil then customMt = AIDriveStrategyShovelSiloLoader_mt @@ -74,6 +72,7 @@ function AIDriveStrategyShovelSiloLoader:delete() CpUtil.destroyNode(self.heapNode) CpUtil.destroyNode(self.unloadPositionNode) CpUtil.destroyNode(self.siloFrontNode) + self.isStuckTimer:delete() end function AIDriveStrategyShovelSiloLoader:getGeneratedCourse(jobParameters) @@ -88,8 +87,10 @@ function AIDriveStrategyShovelSiloLoader:setSiloAndHeap(bunkerSilo, heapSilo) end ---@param unloadTrigger CpTrigger -function AIDriveStrategyShovelSiloLoader:setUnloadTrigger(unloadTrigger) +---@param unloadStation table +function AIDriveStrategyShovelSiloLoader:setUnloadTriggerAndStation(unloadTrigger, unloadStation) self.unloadTrigger = unloadTrigger + self.unloadStation = unloadStation end function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) @@ -108,12 +109,13 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) --- Uses the exactFillRootNode from the trigger --- and the direction of the unload position marker --- to place the unload position node slightly in front. - local x, y, z = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode()) + local x, y, z = getWorldTranslation(self.unloadTrigger:getFillUnitExactFillRootNode(1)) setTranslation(self.unloadPositionNode, x, y, z) local position = jobParameters.unloadPosition local dirX, dirZ = position:getDirection() setDirection(self.unloadPositionNode, dirX, 0, dirZ, 0, 0, 1) - local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -math.max(self.distShovelUnloadStationPreUnload, self.turningRadius)) + local dx, dy, dz = localToWorld(self.unloadPositionNode, 0, 0, -math.max( + self.distShovelUnloadStationPreUnload, self.turningRadius, 1.5 * AIUtil.getLength(self.vehicle))) setTranslation(self.unloadPositionNode, dx, dy, dz) else self:debug("Starting shovel silo to unload into trailer.") @@ -165,6 +167,17 @@ function AIDriveStrategyShovelSiloLoader:setAllStaticParameters() self.siloEndProximitySensor = SingleForwardLookingProximitySensorPack(self.vehicle, self.shovelController:getShovelNode(), 5, 1) self.heapNode = CpUtil.createNode("heapNode", 0, 0, 0, nil) self.lastTrailerSearch = 0 + self.isStuckTimer = Timer.new(self.isStuckMs) + self.isStuckTimer:setFinishCallback(function () + if self.frozen then + return + end + if self.state == self.states.DRIVING_INTO_SILO then + self:debug("Was stuck trying to drive into the bunker silo.") + self:startDrivingOutOfSilo() + self:setNewState(self.states.DRIVING_TEMPORARY_OUT_OF_SILO) + end + end) end ----------------------------------------------------------------------------------------------------------------------- @@ -184,6 +197,8 @@ function AIDriveStrategyShovelSiloLoader:onWaypointPassed(ix, course) else self:startPathfindingToUnloadPosition() end + elseif self.state == self.states.DRIVING_TEMPORARY_OUT_OF_SILO then + self:startDrivingToSilo({self.siloController:getLastTarget()}) elseif self.state == self.states.DRIVING_TO_UNLOAD_TRAILER then self:approachTrailerForUnloading() elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then @@ -235,7 +250,9 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:setMaxSpeed(0) elseif self.state == self.states.DRIVING_INTO_SILO then self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) - + if AIUtil.isStopped(self.vehicle) and not self.proximityController:isStopped() then + self.isStuckTimer:startIfNotRunning() + end local _, _, closestObject = self.siloEndProximitySensor:getClosestObjectDistanceAndRootVehicle() local isEndReached, maxSpeed = self.siloController:isEndReached(self.shovelController:getShovelNode(), 0) if self.silo:isTheSameSilo(closestObject) or isEndReached then @@ -248,6 +265,8 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) end elseif self.state == self.states.DRIVING_OUT_OF_SILO then self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) + elseif self.state == self.states.DRIVING_TEMPORARY_OUT_OF_SILO then + self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) elseif self.state == self.states.DRIVING_TO_UNLOAD_POSITION then self:setMaxSpeed(self.settings.fieldSpeed:getValue()) elseif self.state == self.states.DRIVING_TO_UNLOAD_TRAILER then @@ -268,7 +287,7 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) if self.isUnloadingAtTrailerActive then refNode = self.targetTrailer.exactFillRootNode else - refNode = self.unloadTrigger:getFillUnitExactFillRootNode() + refNode = self.unloadTrigger:getFillUnitExactFillRootNode(1) end if self.shovelController:isShovelOverTrailer(refNode) then self:setNewState(self.states.UNLOADING) @@ -296,6 +315,7 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) end end self:limitSpeed() + self:checkProximitySensors(moveForwards) return gx, gz, moveForwards, self.maxSpeed, 100 end @@ -323,7 +343,7 @@ function AIDriveStrategyShovelSiloLoader:update(dt) end end ---- Ignores the bunker silo for the proximity sensors. +--- Ignores the bunker silo and the unload target for the proximity sensors. function AIDriveStrategyShovelSiloLoader:ignoreProximityObject(object, vehicle) if self.silo:isTheSameSilo(object) then return true @@ -332,6 +352,18 @@ function AIDriveStrategyShovelSiloLoader:ignoreProximityObject(object, vehicle) if object == nil then return true end + if object == self.unloadStation then + return true + end + if self.unloadTrigger:isTheSameObject(object) then + return true + end + if self.targetTrailer then + if object == self.targetTrailer.trailer then + return true + end + end + return false end function AIDriveStrategyShovelSiloLoader:getProximitySensorWidth() @@ -551,9 +583,14 @@ end ---------------------------------------------------------------- --- Starts driving into the silo lane -function AIDriveStrategyShovelSiloLoader:startDrivingToSilo() +function AIDriveStrategyShovelSiloLoader:startDrivingToSilo(target) --- Creates a straight course in the silo. - local startPos, endPos = self.siloController:getTarget(self:getWorkWidth()) + local startPos, endPos + if target then + startPos, endPos = unpack(target) + else + startPos, endPos = self.siloController:getTarget(self:getWorkWidth()) + end local x, z = unpack(startPos) local dx, dz = unpack(endPos) local siloCourse = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index dd1b62ac8..f7ccef7b1 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -375,10 +375,13 @@ function ImplementUtil.moveMovingToolToRotation(implement, tool, dt, rotTarget) tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) local oldRot = tool.curRot[tool.rotationAxis] local diff = rotTarget - oldRot - local rotSpeed = MathUtil.clamp(diff * tool.rotSpeed, tool.rotSpeed/3, 0.5) - if diff < 0 then - rotSpeed=rotSpeed*(-1) - end + local dir = MathUtil.sign(diff) + local rotSpeed = MathUtil.clamp( math.abs(diff) * math.abs(tool.rotSpeed), math.abs(tool.rotSpeed)/3, 0.5 ) + rotSpeed = dir * rotSpeed + --local rotSpeed = MathUtil.clamp(diff * tool.rotSpeed, tool.rotSpeed/3, 0.5) + -- if diff < 0 then + -- rotSpeed=rotSpeed*(-1) + -- end if math.abs(diff) < 0.03 or rotSpeed == 0 then ImplementUtil.stopMovingTool(implement, tool) return false diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 7d5034bd3..6ed36a59d 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -9,7 +9,7 @@ ShovelController.POSITIONS = { UNLOADING = 4, } ShovelController.MAX_TRIGGER_HEIGHT = 8 -ShovelController.MIN_TRIGGER_HEIGHT = 2 +ShovelController.MIN_TRIGGER_HEIGHT = 1 ShovelController.TRIGGER_HEIGHT_RAYCAST_COLLISION_MASK = CollisionFlag.STATIC_WORLD + CollisionFlag.STATIC_OBJECTS + CollisionFlag.STATIC_OBJECT + CollisionFlag.VEHICLE @@ -19,10 +19,74 @@ function ShovelController:init(vehicle, implement, isConsoleCommand) self.shovelNode = ImplementUtil.getShovelNode(implement) self.turnOnSpec = self.implement.spec_turnOnVehicle self.isConsoleCommand = isConsoleCommand + self.isSugarCaneTrailer = self.implement.spec_trailer ~= nil + self.sugarCaneTrailer = { + isDischargeActive = false, + isDischargingTimer = CpTemporaryObject(false), + movingTool = nil, + isMovingToolDirty = false, + isDischargingToGround = false + } + if self.isSugarCaneTrailer then + --- Find the moving tool for the sugar cane trailer + for i, tool in pairs(implement.cylindered.movingTools) do + if tool.axis then + self.sugarCaneTrailer.movingTool = tool + end + end + end +end + +function ShovelController:getDriveData() + local maxSpeed + if self.isSugarCaneTrailer then + if self.sugarCaneTrailer.isDischargeActive then + if self.sugarCaneTrailer.isDischargingTimer:get() then + --- Waiting until the discharging stopped or + --- the trailer is empty + maxSpeed = 0 + self:debugSparse("Waiting for unloading!") + end + + -- if self.trailerSpec.tipState == Trailer.TIPSTATE_OPENING then + -- --- Trailer not yet ready to unload. + -- maxSpeed = 0 + -- self:debugSparse("Waiting for trailer animation opening!") + -- end + if self:isEmpty() then + --- Waiting for the trailer animation to finish. + maxSpeed = 0 + self:debugSparse("Waiting for trailer animation closing!") + end + else + -- ImplementUtil.moveMovingToolToRotation(self.implement, + -- self.sugarCaneTrailerMovingTool, dt , ) + end + end + return nil, nil, nil, maxSpeed end -function ShovelController:update() - +function ShovelController:update(dt) + --- Sugar cane trailer discharge + if self.isSugarCaneTrailer then + if self.sugarCaneTrailer.isDischargeActive then + if self:isEmpty() then + self:finishedSugarCaneTrailerDischarge() + end + if self.implement:getCanDischargeToGround(self.dischargeData.dischargeNode) then + --- Update discharge timer + self.sugarCaneTrailer.isDischargingTimer:set(true, 500) + if not self:isDischarging() then + -- self.implement:setDischargeState(Dischargeable.DISCHARGE_STATE_GROUND) + end + end + -- ImplementUtil.moveMovingToolToRotation(self.implement, + -- self.sugarCaneTrailerMovingTool, dt , ) + else + -- ImplementUtil.moveMovingToolToRotation(self.implement, + -- self.sugarCaneTrailerMovingTool, dt , ) + end + end end function ShovelController:getShovelNode() @@ -104,22 +168,33 @@ end ---@return boolean function ShovelController:calculateMinimalUnloadingHeight(triggerNode) local sx, sy, sz = getWorldTranslation(self.vehicle:getAIDirectionNode()) - local tx, ty, tz = getWorldTranslation(triggerNode) + local tx, ty, tz + if triggerNode then + tx, ty, tz = getWorldTranslation(triggerNode) + else + local dirX, _, dirZ = localDirectionToWorld(self.vehicle:getAIDirectionNode(), 0, 0, 1) + local _, frontMarkerDistance = Markers.getFrontMarkerNode(self.vehicle) + tx, ty, tz = sx + dirX * (frontMarkerDistance + 4), sy, sz + dirZ * (frontMarkerDistance + 4) + end local length = MathUtil.vector2Length(tx - sx, tz - sz) + 0.25 local dx, dy, dz = tx - sx, ty - sy, tz -sz - local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz) - for i=self.MIN_TRIGGER_HEIGHT, self.MAX_TRIGGER_HEIGHT, 0.25 do + local _, terrainHeight, _ = getWorldTranslation(self.vehicle.rootNode) + local maxHeightObjectHit = 0 + for i=self.MIN_TRIGGER_HEIGHT, self.MAX_TRIGGER_HEIGHT, 0.1 do self.objectWasHit = false raycastAll(sx, terrainHeight + i, sz, dx, 0, dz, "calculateMinimalUnloadingHeightRaycastCallback", length, self, self.TRIGGER_HEIGHT_RAYCAST_COLLISION_MASK) - if not self.objectWasHit then - self:debug("Finished raycast with minimal height: %.2f", i) - self.implement:setCpShovelMinimalUnloadHeight(i + 1) - return true + if self.objectWasHit then + maxHeightObjectHit = i end end + if maxHeightObjectHit > 0 then + self:debug("Finished raycast with minimal height: %.2f", maxHeightObjectHit) + self.implement:setCpShovelMinimalUnloadHeight(maxHeightObjectHit + 0.5) + return true + end self:debug("Could not find a valid minimal height, so we use the maximum: %.2f", self.MAX_TRIGGER_HEIGHT) self.implement:setCpShovelMinimalUnloadHeight(self.MAX_TRIGGER_HEIGHT) return false @@ -129,8 +204,8 @@ function ShovelController:calculateMinimalUnloadingHeightRaycastCallback(hitObje if hitObjectId then local object = g_currentMission.nodeToObject[hitObjectId] if object then - self:debug("Object: %s was hit!", CpUtil.getName(object)) if object ~= self.vehicle and object ~= self.implement then + self:debug("Object: %s was hit!", CpUtil.getName(object)) self.objectWasHit = true return true end @@ -177,6 +252,86 @@ function ShovelController:moveShovelToPosition(pos) return self.implement:areCpShovelPositionsDirty() end +-------------------------------------------- +--- Sugar cane trailer functions +-------------------------------------------- + +--- Gets the dischargeNode and offset from a selected tip side. +---@param tipSideID number +---@param isTippingToGroundNeeded boolean +---@return table|nil dischargeNodeIndex +---@return table|nil dischargeNode +---@return number|nil xOffset +function ShovelController:getDischargeNodeAndOffsetForTipSide(tipSideID, isTippingToGroundNeeded) + local dischargeNode = self:getDischargeNode() + return dischargeNode.index, dischargeNode, self:getDischargeXOffset(dischargeNode) +end + +--- Gets the x offset of the discharge node relative to the implement root. +function ShovelController:getDischargeXOffset(dischargeNode) + local node = dischargeNode.node + local xOffset, _ ,_ = localToLocal(node, self.implement.rootNode, 0, 0, 0) + return xOffset +end + +--- Starts AI Discharge to an object/trailer. +---@param dischargeNode table discharge node to use. +---@return boolean success +function ShovelController:startDischarge(dischargeNode) + self.sugarCaneTrailer.isDischargeActive = true + return true +end + +--- Starts discharging to the ground if possible. +function ShovelController:startDischargeToGround(dischargeNode) + self.sugarCaneTrailer.isDischargeActive = true + self.sugarCaneTrailer.isDischargingToGround = true + -- self.isDischargingToGround = true + -- self.dischargeData = { + -- dischargeNode = dischargeNode, + -- } + -- local tipSide = self.trailerSpec.dischargeNodeIndexToTipSide[dischargeNode.index] + -- if tipSide ~= nil then + -- self.implement:setPreferedTipSide(tipSide.index) + -- end + return true +end + +--- Callback for the drive strategy, when the unloading finished. +function ShovelController:setFinishDischargeCallback(finishDischargeCallback) + self.sugarCaneTrailer.finishDischargeCallback = finishDischargeCallback +end + +--- Callback for ai discharge. +function ShovelController:finishedSugarCaneTrailerDischarge() + self:debug("Finished unloading.") + if self.sugarCaneTrailer.finishDischargeCallback then + self.sugarCaneTrailer.finishDischargeCallback(self.driveStrategy, self) + end + self.sugarCaneTrailer.isDischargeActive = false + self.sugarCaneTrailer.isDischargingToGround = false +end + +function ShovelController:prepareForUnload() + return true +end + +function ShovelController:isDischarging() + return self.implement:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF +end + +--- Gets the discharge node z offset relative to the root vehicle direction node. +function ShovelController:getUnloadOffsetZ(dischargeNode) + local node = dischargeNode.node + local dist = ImplementUtil.getDistanceToImplementNode(self.vehicle:getAIDirectionNode(), + self.implement, node) + return dist +end + +-------------------------------------------- +--- Debug functions +-------------------------------------------- + function ShovelController:printShovelDebug() self:debug("--Shovel Debug--") if self.shovelNode then diff --git a/scripts/ai/controllers/TrailerController.lua b/scripts/ai/controllers/TrailerController.lua index d667e53a1..88e5c70a7 100644 --- a/scripts/ai/controllers/TrailerController.lua +++ b/scripts/ai/controllers/TrailerController.lua @@ -59,9 +59,9 @@ end --- Gets the dischargeNode and offset from a selected tip side. ---@param tipSideID number ---@param isTippingToGroundNeeded boolean ----@return table dischargeNodeIndex ----@return table dischargeNode ----@return number xOffset +---@return table|nil dischargeNodeIndex +---@return table|nil dischargeNode +---@return number|nil xOffset function TrailerController:getDischargeNodeAndOffsetForTipSide(tipSideID, isTippingToGroundNeeded) local tipSide = self.trailerSpec.tipSides[tipSideID] if not tipSide then diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 9a9d5b74f..881e5e883 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -184,9 +184,9 @@ end --- Gets the unload trigger at the unload position. ---@param unloadPosition CpAIParameterPositionAngle ----@return boolean ----@return table|nil ----@return table|nil +---@return boolean found? +---@return table|nil Trigger +---@return table|nil unloadStation function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) local x, z = unloadPosition:getPosition() local dirX, dirZ = unloadPosition:getDirection() diff --git a/scripts/ai/tasks/CpAITaskSiloLoader.lua b/scripts/ai/tasks/CpAITaskSiloLoader.lua index 31f856bbe..ddfc5333a 100644 --- a/scripts/ai/tasks/CpAITaskSiloLoader.lua +++ b/scripts/ai/tasks/CpAITaskSiloLoader.lua @@ -33,8 +33,8 @@ end function CpAITaskSiloLoader:start() if self.isServer then - local _, unloadTrigger, _ = self.job:getUnloadTriggerAt(self.job:getCpJobParameters().unloadPosition) - self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap, unloadTrigger) + local _, unloadTrigger, unloadStation = self.job:getUnloadTriggerAt(self.job:getCpJobParameters().unloadPosition) + self.vehicle:startCpSiloLoaderWorker(self.job:getCpJobParameters(), self.silo, self.heap, unloadTrigger, unloadStation) end CpAITaskSiloLoader:superClass().start(self) diff --git a/scripts/specializations/CpAISiloLoaderWorker.lua b/scripts/specializations/CpAISiloLoaderWorker.lua index da718578f..4ec9f4486 100644 --- a/scripts/specializations/CpAISiloLoaderWorker.lua +++ b/scripts/specializations/CpAISiloLoaderWorker.lua @@ -172,7 +172,7 @@ function CpAISiloLoaderWorker:startCpAtLastWp(superFunc, ...) end end -function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger) +function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, heap, unloadTrigger, unloadStation) if self.isServer then local strategy if SpecializationUtil.hasSpecialization(ConveyorBelt, self.specializations) then @@ -181,7 +181,7 @@ function CpAISiloLoaderWorker:startCpSiloLoaderWorker(jobParameters, bunkerSilo, else CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting a shovel silo loader strategy.") strategy = AIDriveStrategyShovelSiloLoader.new() - strategy:setUnloadTrigger(unloadTrigger) + strategy:setUnloadTriggerAndStation(unloadTrigger, unloadStation) end strategy:setSiloAndHeap(bunkerSilo, heap) strategy:setAIVehicle(self, jobParameters) diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 934824cb6..250c3c44f 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -563,7 +563,7 @@ function CpAIWorker:disableCollisionDetection() end function CpAIWorker:getCollisionCheckActive(superFunc,...) - local spec = self.spec_cpAIWorker + local spec = self.vehicle.spec_cpAIWorker if spec.collisionDetectionEnabled then return superFunc(self,...) else diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 1348fd804..726c64caf 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -58,18 +58,7 @@ CpShovelPositions.KEY = "." .. CpShovelPositions.SPEC_NAME function CpShovelPositions.initSpecialization() local schema = Vehicle.xmlSchemaSavegame - g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsPrintShovelDebug", - "Prints debug information for the shovel", - "consoleCommandPrintShovelDebug", CpShovelPositions) - g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetState", - "Set's the current shovel state", - "consoleCommandSetShovelState", CpShovelPositions) - g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetArmLimit", - "Set's the arm max limit", - "consoleCommandSetPreUnloadArmLimit", CpShovelPositions) - g_devHelper.consoleCommands:registerConsoleCommand('cpShovelPositionsSetMinimalUnloadHeight', - 'cpSetShovelSetMinimalUnloadHeight', - 'consoleCommandSetMinimalUnloadHeight', CpShovelPositions) + CpShovelPositions.initConsoleCommands() end function CpShovelPositions.prerequisitesPresent(specializations) @@ -99,106 +88,6 @@ function CpShovelPositions.registerFunctions(vehicleType) SpecializationUtil.registerFunction(vehicleType, "setCpShovelMinimalUnloadHeight", CpShovelPositions.setCpShovelMinimalUnloadHeight) end --------------------------------------------- ---- Console Commands --------------------------------------------- - -local function executeConsoleCommand(func, ...) - local vehicle = g_currentMission.controlledVehicle - if not vehicle then - CpUtil.info("Not entered a valid vehicle!") - return false - end - -- if vehicle:getIsAIActive() then - -- CpUtil.infoVehicle(vehicle, "Error, AI is active!") - -- return false - -- end - local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) - if not found then - CpUtil.infoVehicle(vehicle, "No shovel implement found!") - return false - end - return func(shovels[1], ...) -end - -function CpShovelPositions:consoleCommandSetShovelState(state) - return executeConsoleCommand(function(shovelImplement, state) - state = tonumber(state) - if state == nil or state < 0 or state > CpShovelPositions.NUM_STATES then - CpUtil.infoVehicle(shovelImplement, "No valid state(0 - %d) was given!", CpShovelPositions.NUM_STATES) - return false - end - shovelImplement:cpSetShovelState(state) - end, state) -end - -function CpShovelPositions:consoleCommandSetPreUnloadArmLimit(min, max) - return executeConsoleCommand(function(shovelImplement, min, max) - min = tonumber(min) - max = tonumber(max) - if min == nil or max == nil then - CpUtil.infoVehicle(shovelImplement, "No valid limits given! min: %s, max: %s", tostring(min), tostring(max)) - return false - end - CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS = { min, max } - end, min, max) -end - -function CpShovelPositions:consoleCommandPrintShovelDebug(cylinderedDepth) - return executeConsoleCommand(function(shovelImplement) - --- Position debug - CpUtil.infoImplement(shovelImplement, "-- Position debug --") - local spec = shovelImplement.spec_cpShovelPositions - if spec then - CpUtil.infoImplement(shovelImplement, " arm tool %s -> %s", - CpUtil.getName(spec.armVehicle), tostring(spec.armToolIx)) - CpUtil.infoImplement(shovelImplement, " shovel tool %s -> %s", - CpUtil.getName(spec.shovelVehicle), tostring(spec.shovelToolIx)) - local highDumpShovelIx = g_vehicleConfigurations:get(shovelImplement, "shovelMovingToolIx") - CpUtil.infoImplement(shovelImplement, " shovel high dump %s -> %s", - CpUtil.getName(shovelImplement), tostring(highDumpShovelIx)) - end - - CpUtil.infoImplement(shovelImplement, "-- Position debug --") - --- Shovel debug - local controller = ShovelController(shovelImplement.rootVehicle, shovelImplement, true) - controller:printShovelDebug() - controller:delete() - --- Cylindered debug here - CpUtil.infoImplement(shovelImplement, "-- Cylindered debug --") - cylinderedDepth = cylinderedDepth and tonumber(cylinderedDepth) or 0 - - local childVehicles = shovelImplement.rootVehicle:getChildVehicles() - for _, vehicle in ipairs(childVehicles) do - if vehicle.spec_cylindered then - for ix, tool in pairs(vehicle.spec_cylindered.movingTools) do - CpUtil.infoImplement(shovelImplement, " %s => ix: %d ", - CpUtil.getName(vehicle), ix) - if cylinderedDepth > 0 then - CpUtil.infoImplement(shovelImplement, " %s", - DebugUtil.debugTableToString(tool, " ", 0, cylinderedDepth)) - end - CpUtil.infoImplement(shovelImplement, " %s => ix: %d finished", - CpUtil.getName(vehicle), ix) - end - end - end - CpUtil.infoImplement(shovelImplement, "-- Cylindered debug finished --") - end) -end - -function CpShovelPositions:consoleCommandSetMinimalUnloadHeight(height) - return executeConsoleCommand(function(shovelImplement, height) - height = tonumber(height) - if height == nil then - CpUtil.infoVehicle(shovelImplement, "No valid height given! height: %s", tostring(height)) - return false - end - local spec = shovelImplement.spec_cpShovelPositions - spec.minimalShovelUnloadHeight = height - end, height) -end - -------------------------------------------- --- Event Listener -------------------------------------------- @@ -361,12 +250,9 @@ end ---@param dt number ---@param shovelLimits table ---@param armLimits table ----@param isLoading boolean|nil ---@param heightOffset number|nil ----@param isUnloading boolean|nil ---@return boolean|nil -function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, - isLoading, heightOffset, isUnloading) +function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, heightOffset) heightOffset = heightOffset or 0 local spec = self.spec_cpShovelPositions local min, max = unpack(shovelLimits) @@ -404,11 +290,7 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, local _, ty, tz = localToLocal(getChildAt(armTool.node, 0), armVehicle.rootNode, 0, 0, 0) local ax, ay, az = localToLocal(armTool.node, armVehicle.rootNode, 0, 0, 0) local wx, _, wz = getWorldTranslation(armVehicle.rootNode) - local deltaY = 0 - if spec.state == CpShovelPositions.PRE_UNLOAD or spec.state == CpShovelPositions.UNLOADING then - deltaY = minimalTargetHeight - ay - end local by = shovelY if self.spec_foliageBending and self.spec_foliageBending.bendingNodes[1] then local bending = self.spec_foliageBending.bendingNodes[1] @@ -436,12 +318,15 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, _, by, _ = worldToLocal(shovelTool.node, sx, by2, sz) end end - - local sx, sy, sz = 0, -by + targetHeight + heightOffset + deltaY, 0 - local ex, ey, ez = 0, -by + targetHeight + heightOffset + deltaY, 20 + local deltaY = 0 + if spec.state == CpShovelPositions.PRE_UNLOAD or spec.state == CpShovelPositions.UNLOADING then + targetHeight = minimalTargetHeight + end + local sx, sy, sz = 0, -by + targetHeight + heightOffset, 0 + local ex, ey, ez = 0, sy, 20 local wsx, wsy, wsz = localToWorld(armVehicle.rootNode, sx, sy, sz) local wex, wey, wez = localToWorld(armVehicle.rootNode, ex, ey, ez) - local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wsx, 0, wsz) + local _, terrainHeight, _ = getWorldTranslation(self.rootVehicle.rootNode, 0, 0, 0) local yMax = ay + radiusArmToolToShovelTool local yMin = ay - radiusArmToolToShovelTool @@ -472,6 +357,15 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, skipArm = true end end + if spec.state ~= CpShovelPositions.UNLOADING then + if spec.isHighDumpShovel then + isDirty = CpShovelPositions.foldHighDumpShovel(self, dt) or isDirty + if isDirty then + skipArm = true + end + end + end + local alpha, oldRotRelativeArmRot = 0, 0 if hasIntersection then --- Controls the arm height @@ -492,9 +386,6 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, end if spec.state ~= CpShovelPositions.UNLOADING then - if spec.isHighDumpShovel then - isDirty = CpShovelPositions.foldHighDumpShovel(self, dt) or isDirty - end isDirty = isDirty or CpShovelPositions.controlShovelPosition(self, dt, targetAngle - angle) end @@ -583,8 +474,7 @@ function CpShovelPositions:updateLoadingPosition(dt) if angle then isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.LOADING_POSITION.SHOVEL_LIMITS, - CpShovelPositions.LOADING_POSITION.ARM_LIMITS, - true, heightOffset) + CpShovelPositions.LOADING_POSITION.ARM_LIMITS, heightOffset) end spec.isDirty = isDirty end @@ -597,8 +487,7 @@ function CpShovelPositions:updateTransportPosition(dt) if angle then isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.TRANSPORT_POSITION.SHOVEL_LIMITS, - CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS, - false, heightOffset) + CpShovelPositions.TRANSPORT_POSITION.ARM_LIMITS, heightOffset) end spec.isDirty = isDirty end @@ -610,8 +499,7 @@ function CpShovelPositions:updatePreUnloadPosition(dt) if angle then isDirty = CpShovelPositions.setShovelPosition(self, dt, CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS, - CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, - false, nil) + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, nil) end spec.isDirty = isDirty end @@ -623,8 +511,7 @@ function CpShovelPositions:updateUnloadingPosition(dt) if angle and maxAngle then isDirty = CpShovelPositions.setShovelPosition(self, dt, {math.deg(maxAngle), math.deg(maxAngle) + 2}, - CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, false, - nil, true) + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, nil) end spec.isDirty = isDirty end @@ -660,4 +547,131 @@ function CpShovelPositions.debug(implement, ...) if CpShovelPositions.DEBUG then CpUtil.infoImplement(implement, ...) end +end + + +-------------------------------------------- +--- Console Commands +-------------------------------------------- + +function CpShovelPositions.initConsoleCommands() + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsPrintShovelDebug", + "Prints debug information for the shovel", + "consoleCommandPrintShovelDebug", CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetState", + "Set's the current shovel state", + "consoleCommandSetShovelState", CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand("cpShovelPositionsSetArmLimit", + "Set's the arm max limit", + "consoleCommandSetPreUnloadArmLimit", CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand('cpShovelPositionsSetMinimalUnloadHeight', + 'Sets the minimal unload height to a fixed value', + 'consoleCommandSetMinimalUnloadHeight', CpShovelPositions) + g_devHelper.consoleCommands:registerConsoleCommand('cpShovelPositionsMeasureAndSetMinUnloadHeight', + 'Measures and sets the minimal unload height', + 'consoleCommandMeasureAndSetMinimalUnloadHeight', CpShovelPositions) +end + +local function executeConsoleCommand(func, ...) + local vehicle = g_currentMission.controlledVehicle + if not vehicle then + CpUtil.info("Not entered a valid vehicle!") + return false + end + -- if vehicle:getIsAIActive() then + -- CpUtil.infoVehicle(vehicle, "Error, AI is active!") + -- return false + -- end + local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(vehicle, Shovel) + if not found then + CpUtil.infoVehicle(vehicle, "No shovel implement found!") + return false + end + return func(shovels[1], ...) +end + +function CpShovelPositions:consoleCommandSetShovelState(state) + return executeConsoleCommand(function(shovelImplement, state) + state = tonumber(state) + if state == nil or state < 0 or state > CpShovelPositions.NUM_STATES then + CpUtil.infoVehicle(shovelImplement, "No valid state(0 - %d) was given!", CpShovelPositions.NUM_STATES) + return false + end + shovelImplement:cpSetShovelState(state) + end, state) +end + +function CpShovelPositions:consoleCommandSetPreUnloadArmLimit(min, max) + return executeConsoleCommand(function(shovelImplement, min, max) + min = tonumber(min) + max = tonumber(max) + if min == nil or max == nil then + CpUtil.infoVehicle(shovelImplement, "No valid limits given! min: %s, max: %s", tostring(min), tostring(max)) + return false + end + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS = { min, max } + end, min, max) +end + +function CpShovelPositions:consoleCommandPrintShovelDebug(cylinderedDepth) + return executeConsoleCommand(function(shovelImplement) + --- Position debug + CpUtil.infoImplement(shovelImplement, "-- Position debug --") + local spec = shovelImplement.spec_cpShovelPositions + if spec then + CpUtil.infoImplement(shovelImplement, " arm tool %s -> %s", + CpUtil.getName(spec.armVehicle), tostring(spec.armToolIx)) + CpUtil.infoImplement(shovelImplement, " shovel tool %s -> %s", + CpUtil.getName(spec.shovelVehicle), tostring(spec.shovelToolIx)) + local highDumpShovelIx = g_vehicleConfigurations:get(shovelImplement, "shovelMovingToolIx") + CpUtil.infoImplement(shovelImplement, " shovel high dump %s -> %s", + CpUtil.getName(shovelImplement), tostring(highDumpShovelIx)) + end + + CpUtil.infoImplement(shovelImplement, "-- Position debug --") + --- Shovel debug + local controller = ShovelController(shovelImplement.rootVehicle, shovelImplement, true) + controller:printShovelDebug() + controller:delete() + --- Cylindered debug here + CpUtil.infoImplement(shovelImplement, "-- Cylindered debug --") + cylinderedDepth = cylinderedDepth and tonumber(cylinderedDepth) or 0 + + local childVehicles = shovelImplement.rootVehicle:getChildVehicles() + for _, vehicle in ipairs(childVehicles) do + if vehicle.spec_cylindered then + for ix, tool in pairs(vehicle.spec_cylindered.movingTools) do + CpUtil.infoImplement(shovelImplement, " %s => ix: %d ", + CpUtil.getName(vehicle), ix) + if cylinderedDepth > 0 then + CpUtil.infoImplement(shovelImplement, " %s", + DebugUtil.debugTableToString(tool, " ", 0, cylinderedDepth)) + end + CpUtil.infoImplement(shovelImplement, " %s => ix: %d finished", + CpUtil.getName(vehicle), ix) + end + end + end + CpUtil.infoImplement(shovelImplement, "-- Cylindered debug finished --") + end) +end + +function CpShovelPositions:consoleCommandSetMinimalUnloadHeight(height) + return executeConsoleCommand(function(shovelImplement, height) + height = tonumber(height) + if height == nil then + CpUtil.infoVehicle(shovelImplement, "No valid height given! height: %s", tostring(height)) + return false + end + local spec = shovelImplement.spec_cpShovelPositions + spec.minimalShovelUnloadHeight = height + end, height) +end + +function CpShovelPositions:consoleCommandMeasureAndSetMinimalUnloadHeight() + return executeConsoleCommand(function(shovelImplement) + local controller = ShovelController(shovelImplement.rootVehicle, shovelImplement, true) + controller:calculateMinimalUnloadingHeight() + controller:delete() + end) end \ No newline at end of file diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua index bc4548712..369cab4de 100644 --- a/scripts/trigger/TriggerWrapper.lua +++ b/scripts/trigger/TriggerWrapper.lua @@ -59,4 +59,10 @@ function CpTrigger:drawPlot(map, selectedTrigger, fillTypes) end self.plot:setHighlighted(self == selectedTrigger) self.plot:draw(map) -end \ No newline at end of file +end + +function CpTrigger:isTheSameObject(otherObject) + if self.trigger:getTarget().owningPlaceable == otherObject then + return true + end +end From 8fbc32de85653194f6e2084dd5cca9c1f3d258bf Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 3 Sep 2023 10:23:13 +0200 Subject: [PATCH 067/107] small fix --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 5653b0c6e..a55143956 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -355,7 +355,7 @@ function AIDriveStrategyShovelSiloLoader:ignoreProximityObject(object, vehicle) if object == self.unloadStation then return true end - if self.unloadTrigger:isTheSameObject(object) then + if self.unloadTrigger and self.unloadTrigger:isTheSameObject(object) then return true end if self.targetTrailer then From b6cc4194e73aa566ec9b468c6356afef6dbb45bf Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 3 Sep 2023 10:50:42 +0200 Subject: [PATCH 068/107] Applies the shovel loading state, when hud value was changed --- config/VehicleSettingsSetup.xml | 2 +- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 9 +++++++-- scripts/specializations/CpShovelPositions.lua | 17 +++++++++++++++++ scripts/specializations/CpVehicleSettings.lua | 10 ++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/config/VehicleSettingsSetup.xml b/config/VehicleSettingsSetup.xml index 9ac6b2868..3456b6eed 100644 --- a/config/VehicleSettingsSetup.xml +++ b/config/VehicleSettingsSetup.xml @@ -77,7 +77,7 @@ + isVisible="isLoadingShovelOffsetSettingVisible" isDisabled="isLoadingShovelOffsetSettingDisabled" onChangeCallback="onCpLoadingShovelOffsetSettingChanged"/> diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index a55143956..75e61908c 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -440,6 +440,7 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() local trailerData, dist = self:getClosestTrailerAndDistance({}) if not trailerData then self:debug("No valid trailer found anywhere!") + self:setInfoText(InfoTextManager.WAITING_FOR_UNLOADER) return end local trailer = trailerData.trailer @@ -447,8 +448,10 @@ function AIDriveStrategyShovelSiloLoader:searchForTrailerToUnloadInto() self:debug("Closest Trailer %s attached to %s with the distance %.2fm/%.2fm found is to far away!", CpUtil.getName(trailer), trailer.rootVehicle and CpUtil.getName(trailer.rootVehicle) or "no root vehicle", dist, self.maxValidTrailerDistanceToSiloFront) + self:setInfoText(InfoTextManager.WAITING_FOR_UNLOADER) return end + self:clearInfoText(InfoTextManager.WAITING_FOR_UNLOADER) --- Sets the unload position node in front of the closest side of the trailer. self:debug("Found a valid trailer %s within distance %.2f", CpUtil.getName(trailer), dist) self.targetTrailer = trailerData @@ -541,7 +544,7 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToUnloadPosition(path, self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) else self:debug("Failed to drive close to unload position.") - -- self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) + self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) end end @@ -575,7 +578,9 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToTrailer(path, goalNo self:setNewState(self.states.DRIVING_TO_UNLOAD_TRAILER) else self:debug("Failed to find path to trailer!") - self:setNewState(self.states.WAITING_FOR_TRAILER) + ---self:setNewState(self.states.WAITING_FOR_TRAILER) + --- Later on we might try another approach? + self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) end end ---------------------------------------------------------------- diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 726c64caf..c1bd435db 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -82,6 +82,8 @@ end function CpShovelPositions.registerFunctions(vehicleType) SpecializationUtil.registerFunction(vehicleType, "cpSetShovelState", CpShovelPositions.cpSetShovelState) + SpecializationUtil.registerFunction(vehicleType, "cpSetTemporaryShovelState", CpShovelPositions.cpSetTemporaryShovelState) + SpecializationUtil.registerFunction(vehicleType, "cpSetTemporaryLoadingShovelState", CpShovelPositions.cpSetTemporaryLoadingShovelState) SpecializationUtil.registerFunction(vehicleType, "cpResetShovelState", CpShovelPositions.cpResetShovelState) SpecializationUtil.registerFunction(vehicleType, "cpSetupShovelPositions", CpShovelPositions.cpSetupShovelPositions) SpecializationUtil.registerFunction(vehicleType, "areCpShovelPositionsDirty", CpShovelPositions.areCpShovelPositionsDirty) @@ -133,11 +135,16 @@ function CpShovelPositions:onUpdateTick(dt) elseif spec.state == CpShovelPositions.UNLOADING then CpUtil.try(CpShovelPositions.updateUnloadingPosition, self, dt) end + if spec.resetStateWhenReached and not self:areCpShovelPositionsDirty() then + self:cpSetShovelState(CpShovelPositions.DEACTIVATED) + spec.resetStateWhenReached = false + end end --- Changes the current shovel state position. function CpShovelPositions:cpSetShovelState(state) local spec = self.spec_cpShovelPositions + spec.resetWhenReached = false if spec.state ~= state then spec.state = state if state == CpShovelPositions.DEACTIVATED then @@ -147,6 +154,16 @@ function CpShovelPositions:cpSetShovelState(state) end end +function CpShovelPositions:cpSetTemporaryShovelState(state) + local spec = self.spec_cpShovelPositions + self:cpSetShovelState(state) + spec.resetStateWhenReached = true +end + +function CpShovelPositions:cpSetTemporaryLoadingShovelState() + self:cpSetTemporaryShovelState(CpShovelPositions.LOADING) +end + --- Deactivates the shovel position control. function CpShovelPositions:cpResetShovelState() CpShovelPositions.debug(self, "Reset shovelPositionState.") diff --git a/scripts/specializations/CpVehicleSettings.lua b/scripts/specializations/CpVehicleSettings.lua index 7e3c60375..7d2e397c8 100644 --- a/scripts/specializations/CpVehicleSettings.lua +++ b/scripts/specializations/CpVehicleSettings.lua @@ -45,6 +45,7 @@ end function CpVehicleSettings.registerEvents(vehicleType) SpecializationUtil.registerEvent(vehicleType, 'onCpUserSettingChanged') + SpecializationUtil.registerEvent(vehicleType, 'onCpLoadingShovelOffsetSettingChanged') end @@ -57,6 +58,7 @@ function CpVehicleSettings.registerEventListeners(vehicleType) SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onStateChange", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onCpUserSettingChanged", CpVehicleSettings) + SpecializationUtil.registerEventListener(vehicleType, 'onCpLoadingShovelOffsetSettingChanged', CpVehicleSettings) end function CpVehicleSettings.registerFunctions(vehicleType) @@ -398,6 +400,14 @@ function CpVehicleSettings:isLoadingShovelOffsetSettingDisabled() return not AIUtil.isStopped(self) end +function CpVehicleSettings:onCpLoadingShovelOffsetSettingChanged() + local shovels, found = AIUtil.getAllChildVehiclesWithSpecialization(self, Shovel) + if not found then + return false + end + shovels[1]:cpSetTemporaryLoadingShovelState() +end + --- Saves the user value changed on the server. function CpVehicleSettings:onCpUserSettingChanged(setting) if not self.isServer then From 51e408d9f0adc1e577465f13404a72b7c01476b5 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 3 Sep 2023 12:57:16 +0200 Subject: [PATCH 069/107] Updated comments --- Courseplay.lua | 2 +- .../ai/AIDriveStrategyShovelSiloLoader.lua | 14 ++++++++++- scripts/ai/controllers/ShovelController.lua | 11 ++++++--- scripts/ai/jobs/CpAIJobSiloLoader.lua | 18 ++++++++++----- scripts/gui/CpAIFrameExtended.lua | 7 ++++-- scripts/gui/UnloadingTriggerPlot.lua | 2 +- scripts/specializations/CpAIWorker.lua | 1 + scripts/specializations/CpShovelPositions.lua | 23 ++++++++++++++++++- scripts/trigger/TriggerWrapper.lua | 1 + 9 files changed, 64 insertions(+), 15 deletions(-) diff --git a/Courseplay.lua b/Courseplay.lua index d9c7192bc..2dcb6efa0 100644 --- a/Courseplay.lua +++ b/Courseplay.lua @@ -196,7 +196,7 @@ function Courseplay:update(dt) g_bunkerSiloManager:update(dt) g_triggerManager:update(dt) if not self.postInit then - -- Doubles the map zoom. Mainly to make it easier to set targets for unload triggers. + -- Doubles the map zoom for 4x Maps. Mainly to make it easier to set targets for unload triggers. self.postInit = true local function setIngameMapFix(mapElement) local factor = 2*mapElement.terrainSize/2048 diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 75e61908c..7d036fe83 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -1,6 +1,6 @@ --[[ This file is part of Courseplay (https://github.com/Courseplay/courseplay) -Copyright (C) 2022 +Copyright (C) 2023 Courseplay Dev Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,6 +16,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ]] +--[[ + +This drive strategy implements: + - Loading from an bunker silo or a heap on a field with a wheel loader. + - Dumping the picked up fill level to an unload trigger oder a trailer. + - Automatically setting the shovel/arm Positions of the wheel loader. + +]] + + + ---@class AIDriveStrategyShovelSiloLoader : AIDriveStrategyCourse ---@field shovelController ShovelController AIDriveStrategyShovelSiloLoader = {} @@ -251,6 +262,7 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) elseif self.state == self.states.DRIVING_INTO_SILO then self:setMaxSpeed(self.settings.bunkerSiloSpeed:getValue()) if AIUtil.isStopped(self.vehicle) and not self.proximityController:isStopped() then + --- Updates the is stuck timer self.isStuckTimer:startIfNotRunning() end local _, _, closestObject = self.siloEndProximitySensor:getClosestObjectDistanceAndRootVehicle() diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 6ed36a59d..3a46b13c1 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -19,6 +19,7 @@ function ShovelController:init(vehicle, implement, isConsoleCommand) self.shovelNode = ImplementUtil.getShovelNode(implement) self.turnOnSpec = self.implement.spec_turnOnVehicle self.isConsoleCommand = isConsoleCommand + --- Sugar can unlading is still WIP self.isSugarCaneTrailer = self.implement.spec_trailer ~= nil self.sugarCaneTrailer = { isDischargeActive = false, @@ -40,6 +41,7 @@ end function ShovelController:getDriveData() local maxSpeed if self.isSugarCaneTrailer then + --- Sugar cane trailer discharge if self.sugarCaneTrailer.isDischargeActive then if self.sugarCaneTrailer.isDischargingTimer:get() then --- Waiting until the discharging stopped or @@ -67,8 +69,8 @@ function ShovelController:getDriveData() end function ShovelController:update(dt) - --- Sugar cane trailer discharge if self.isSugarCaneTrailer then + --- Sugar cane trailer discharge if self.sugarCaneTrailer.isDischargeActive then if self:isEmpty() then self:finishedSugarCaneTrailerDischarge() @@ -164,7 +166,7 @@ function ShovelController:isHighDumpShovel() end --- Calculates the minimal unloading height for the trigger. ----@param triggerNode any +---@param triggerNode number|nil ---@return boolean function ShovelController:calculateMinimalUnloadingHeight(triggerNode) local sx, sy, sz = getWorldTranslation(self.vehicle:getAIDirectionNode()) @@ -200,6 +202,7 @@ function ShovelController:calculateMinimalUnloadingHeight(triggerNode) return false end +--- Callback checks if an object was hit. function ShovelController:calculateMinimalUnloadingHeightRaycastCallback(hitObjectId, x, y, z, distance, nx, ny, nz, subShapeIndex, shapeId, isLast) if hitObjectId then local object = g_currentMission.nodeToObject[hitObjectId] @@ -225,6 +228,8 @@ function ShovelController:onFinished() end end +--- Applies the given shovel position and +--- enables shovels that need an activation for unloading. ---@param pos number shovel position 1-4 ---@return boolean reached? function ShovelController:moveShovelToPosition(pos) @@ -253,7 +258,7 @@ function ShovelController:moveShovelToPosition(pos) end -------------------------------------------- ---- Sugar cane trailer functions +--- WIP! Sugar cane trailer functions -------------------------------------------- --- Gets the dischargeNode and offset from a selected tip side. diff --git a/scripts/ai/jobs/CpAIJobSiloLoader.lua b/scripts/ai/jobs/CpAIJobSiloLoader.lua index 881e5e883..dc3fdc8ca 100644 --- a/scripts/ai/jobs/CpAIJobSiloLoader.lua +++ b/scripts/ai/jobs/CpAIJobSiloLoader.lua @@ -1,5 +1,5 @@ ---- Job for stationary loader. ----@class CpAIJobSiloLoader : CpAIJobFieldWork +--- AI Job for silo loader like the ropa maus or wheel loaders. +---@class CpAIJobSiloLoader : CpAIJob ---@field heapPlot HeapPlot ---@field heapNode number CpAIJobSiloLoader = { @@ -79,7 +79,6 @@ function CpAIJobSiloLoader:applyCurrentState(vehicle, mission, farmId, isDirectS end end - function CpAIJobSiloLoader:setValues() CpAIJob.setValues(self) local vehicle = self.vehicleParameter:getVehicle() @@ -134,7 +133,7 @@ function CpAIJobSiloLoader:validate(farmId) end if not AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then - --- Validate the trigger setup + --- Validates the unload trigger setup local found, unloadTrigger, unloadStation = self:getUnloadTriggerAt(self.cpJobParameters.unloadPosition) if found then self.unloadStation = unloadStation @@ -144,6 +143,9 @@ function CpAIJobSiloLoader:validate(farmId) end local id = NetworkUtil.getObjectId(unloadStation) if id ~= nil then + CpUtil.debugVehicle(CpDebug.DBG_SILO, vehicle, + "Found a valid unload trigger: %s for %s", + CpUtil.getName(unloadTrigger), CpUtil.getName(unloadStation)) self:getCpJobParameters().unloadStation:setValue(id) self:getCpJobParameters().unloadStation:validateUnloadingStation() end @@ -199,7 +201,9 @@ function CpAIJobSiloLoader:getUnloadTriggerAt(unloadPosition) fillType = silo:getFillType() end local found, trigger, station = g_triggerManager:getDischargeableUnloadTriggerAt( x, z, dirX, dirZ, 5, 25) - if found and fillType~=nil then + if found and fillType ~= nil then + --- Additional check if the fill type of the silo + --- matches with the fill type of the unload target. if not trigger:getIsFillTypeAllowed(fillType) then --- Fill type is not supported by the trigger. found = false @@ -230,10 +234,12 @@ function CpAIJobSiloLoader:drawSilos(map) end ---- Gets the unload station. +--- Gets all the unloading stations. function CpAIJobSiloLoader:getUnloadingStations() local unloadingStations = {} for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do + --- TODO: Maybe a few stations need to be ignored? + --- For example stations that have no possible correct fill type table.insert(unloadingStations, unloadingStation) end return unloadingStations diff --git a/scripts/gui/CpAIFrameExtended.lua b/scripts/gui/CpAIFrameExtended.lua index 3038501f9..c3d48e1e1 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -14,8 +14,8 @@ CpInGameMenuAIFrameExtended.curDrawPositions={} CpInGameMenuAIFrameExtended.drawDelay = g_updateLoopIndex CpInGameMenuAIFrameExtended.DELAY = 1 CpInGameMenuAIFrameExtended.hotspotFilterState = {} +--- Hotspots visible, while drawing a custom field border. CpInGameMenuAIFrameExtended.validCustomFieldCreationHotspots = { - --- Hotspots visible, while drawing a custom field border. [MapHotspot.CATEGORY_FIELD] = true, -- [MapHotspot.CATEGORY_UNLOADING] = true, -- [MapHotspot.CATEGORY_LOADING] = true, @@ -29,8 +29,8 @@ CpInGameMenuAIFrameExtended.validCustomFieldCreationHotspots = { [CustomFieldHotspot.CATEGORY] = true } +--- Hotspots visible, while picking a loading position. CpInGameMenuAIFrameExtended.validPickingLoadingPositionHotspots = { - --- Hotspots visible, while picking a loading position. [MapHotspot.CATEGORY_FIELD] = true, -- [MapHotspot.CATEGORY_UNLOADING] = true, -- [MapHotspot.CATEGORY_LOADING] = true, @@ -155,6 +155,7 @@ end InGameMenuAIFrame.onLoadMapFinished = Utils.appendedFunction(InGameMenuAIFrame.onLoadMapFinished, CpInGameMenuAIFrameExtended.onAIFrameLoadMapFinished) +--- Creates alternative buttons, which are put into the button layout. function CpInGameMenuAIFrameExtended:setupButtons() local function createBtn(prefab, text, callback) local btn = prefab:clone(prefab.parent) @@ -221,6 +222,7 @@ InGameMenuAIFrame.updateContextInputBarVisibility = Utils.appendedFunction(InGam function CpInGameMenuAIFrameExtended:setJobMenuVisible(visible) if not visible then + --- Removes the map hotspot, if the job menu of the vehicle is closed. g_currentMission:removeMapHotspot(self.driveToAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.fieldSiloAiTargetMapHotspot) g_currentMission:removeMapHotspot(self.unloadAiTargetMapHotspot) @@ -289,6 +291,7 @@ function InGameMenuAIFrame:onClickOpenCloseCourseGenerator() end end +--- Generates the correct course generator layout and binds the settings to the gui elements. function CpInGameMenuAIFrameExtended:bindCourseGeneratorSettings() local vehicle = InGameMenuMapUtil.getHotspotVehicle(self.currentHotspot) if vehicle ~=nil and vehicle.getCourseGeneratorSettings then diff --git a/scripts/gui/UnloadingTriggerPlot.lua b/scripts/gui/UnloadingTriggerPlot.lua index c2913504b..18e69421d 100644 --- a/scripts/gui/UnloadingTriggerPlot.lua +++ b/scripts/gui/UnloadingTriggerPlot.lua @@ -1,4 +1,4 @@ ---- Draws the bunker silo dimensions on the in game map. +--- Draws an unloading triggers as a X Symbol on the in game menu map. ---@class UnloadingTriggerPlot UnloadingTriggerPlot = CpObject() diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 250c3c44f..92ddfbf56 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -547,6 +547,7 @@ end --- TODO: Do we really need the AIDriveStrategyCollision from giants, as this one is only active for fieldwork? +--- Maybe there is already a unique cp logic implemented, that catches the use cases. function CpAIWorker:isCollisionDetectionEnabled() local spec = self.spec_cpAIWorker return spec.collisionDetectionEnabled diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index c1bd435db..44bec80ff 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -205,6 +205,7 @@ function CpShovelPositions:cpSetupShovelPositions() spec.shovelTool = tool spec.shovelVehicle = vehicle elseif tool.axis == "AXIS_FRONTLOADER_ARM2" then + --- WIP for telescope loaders spec.armExtendToolIx = i spec.armExtendTool = tool spec.armExtendVehicle = vehicle @@ -215,12 +216,18 @@ function CpShovelPositions:cpSetupShovelPositions() end end +--- Controls the shovel rotation. +--- Also opens/closes the shovel if needed. +---@param dt number +---@param targetAngle number +---@return boolean isDirty function CpShovelPositions:controlShovelPosition(dt, targetAngle) local spec = self.spec_cpShovelPositions local shovelData = ImplementUtil.getShovelNode(self) local isDirty = false if shovelData.movingToolActivation and not spec.isHighDumpShovel then --- The shovel has a moving tool for grabbing. + --- So it needs to be opened for loading and unloading. for i, tool in pairs(self.spec_cylindered.movingTools) do if tool.axis and tool.rotMax ~= nil and tool.rotMin ~= nil then if spec.state ~= CpShovelPositions.TRANSPORT then @@ -244,6 +251,12 @@ function CpShovelPositions:controlShovelPosition(dt, targetAngle) spec.shovelTool, dt, goalAngle) or isDirty end +--- Performs the unloading with a high dump shovel. +--- This kind of a shovel has a moving tool for unloading. +---@param dt number +---@return boolean isDirty +---@return number angle +---@return number targetAngle function CpShovelPositions:unfoldHighDumpShovel(dt) local highDumpShovelTool = self.spec_cylindered.movingTools[self.spec_cpShovelPositions.highDumpMovingToolIx] local _, dy, _ = localDirectionToWorld(getParent(highDumpShovelTool.node), 0, 0, 1) @@ -257,6 +270,9 @@ function CpShovelPositions:unfoldHighDumpShovel(dt) return isDirty, angle, targetAngle end +--- Folds the high dump shovel back to the normal position. +---@param dt number +---@return boolean isDirty function CpShovelPositions:foldHighDumpShovel(dt) local highDumpShovelTool = self.spec_cylindered.movingTools[self.spec_cpShovelPositions.highDumpMovingToolIx] return ImplementUtil.moveMovingToolToRotation(self, highDumpShovelTool, dt, @@ -268,7 +284,7 @@ end ---@param shovelLimits table ---@param armLimits table ---@param heightOffset number|nil ----@return boolean|nil +---@return boolean function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, heightOffset) heightOffset = heightOffset or 0 local spec = self.spec_cpShovelPositions @@ -310,8 +326,11 @@ function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, height local by = shovelY if self.spec_foliageBending and self.spec_foliageBending.bendingNodes[1] then + --- The foliage bending area can be used to get relatively exact dimensions of the shovel. local bending = self.spec_foliageBending.bendingNodes[1] if bending.id ~= nil and bending.node ~= nil then + --- Trying to find the lowest point of shovel. + --- This might be front tip or near the attacher. local sx, _, sz = localToWorld(shovelTool.node, 0, 0, 0) local bx1, by1, bz1 = localToWorld(bending.node, 0, 0, bending.minZ) local bx2, by2, bz2 = localToWorld(bending.node, 0, 0, bending.maxZ) @@ -533,6 +552,8 @@ function CpShovelPositions:updateUnloadingPosition(dt) spec.isDirty = isDirty end +--- Sets the unload height target of the lowest part of the shovel. +---@param height number function CpShovelPositions:setCpShovelMinimalUnloadHeight(height) local spec = self.spec_cpShovelPositions spec.minimalShovelUnloadHeight = height diff --git a/scripts/trigger/TriggerWrapper.lua b/scripts/trigger/TriggerWrapper.lua index 369cab4de..549db2ab9 100644 --- a/scripts/trigger/TriggerWrapper.lua +++ b/scripts/trigger/TriggerWrapper.lua @@ -61,6 +61,7 @@ function CpTrigger:drawPlot(map, selectedTrigger, fillTypes) self.plot:draw(map) end +--- Is the trigger part of the given object? function CpTrigger:isTheSameObject(otherObject) if self.trigger:getTarget().owningPlaceable == otherObject then return true From 2d07785b9a045c2a7dd3e9d269203776b00bebda Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:01:24 +0200 Subject: [PATCH 070/107] Small callstack fixes --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 4 ++-- scripts/ai/AIReverseDriver.lua | 7 +++++-- scripts/ai/ProximityController.lua | 9 ++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 7d036fe83..98b7a8805 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -332,8 +332,6 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) end function AIDriveStrategyShovelSiloLoader:update(dt) - AIDriveStrategyCourse.update(self) - self:updateImplementControllers(dt) if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then if self.course:isTemporary() then self.course:draw() @@ -353,6 +351,8 @@ function AIDriveStrategyShovelSiloLoader:update(dt) end CpUtil.drawDebugNode(self.unloadPositionNode, false, 3) end + self:updateImplementControllers(dt) + AIDriveStrategyCourse.update(self) end --- Ignores the bunker silo and the unload target for the proximity sensors. diff --git a/scripts/ai/AIReverseDriver.lua b/scripts/ai/AIReverseDriver.lua index ab3f9f3ff..fedb55bd2 100644 --- a/scripts/ai/AIReverseDriver.lua +++ b/scripts/ai/AIReverseDriver.lua @@ -91,8 +91,11 @@ function AIReverseDriver:getDriveData() -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already local tractorNode if self.vehicle.spec_articulatedAxis then - tractorNode = AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) - else + --- TODO consolidate this with AIUtil.getReverserNode() maybe ?? + local node = self.vehicle.getAIReverserNode and self.vehicle:getAIReverserNode() + tractorNode = node or AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) + end + if not tractorNode then tractorNode = self.vehicle:getAIDirectionNode() end diff --git a/scripts/ai/ProximityController.lua b/scripts/ai/ProximityController.lua index 4045723dc..3cad859df 100644 --- a/scripts/ai/ProximityController.lua +++ b/scripts/ai/ProximityController.lua @@ -161,10 +161,6 @@ end ---@return boolean|nil direction is forwards if true or nil ---@return number maximum speed adjusted to slow down (or 0 to stop) when obstacles are ahead, otherwise maxSpeed function ProximityController:getDriveData(maxSpeed, moveForwards) - - --- Resets the traffic info text. - self.vehicle:resetCpActiveInfoText(InfoTextManager.BLOCKED_BY_OBJECT) - local d, vehicle, object, range, deg, dAvg, hitTerrain = math.huge, nil, nil, 10, 0, 0, false local pack = moveForwards and self.forwardLookingProximitySensorPack or self.backwardLookingProximitySensorPack if pack then @@ -173,12 +169,14 @@ function ProximityController:getDriveData(maxSpeed, moveForwards) end if self:ignoreObject(object, vehicle, moveForwards, hitTerrain) then self:setState(self.states.NO_OBSTACLE, 'No obstacle') + self.vehicle:resetCpActiveInfoText(InfoTextManager.BLOCKED_BY_OBJECT) return nil, nil, nil, maxSpeed end if object then --- Makes sure the detected object is not an implement or the root vehicle. for _, childVehicle in pairs(self.vehicle:getChildVehicles()) do if object == childVehicle then + self.vehicle:resetCpActiveInfoText(InfoTextManager.BLOCKED_BY_OBJECT) return nil, nil, nil, maxSpeed end end @@ -228,8 +226,9 @@ function ProximityController:getDriveData(maxSpeed, moveForwards) end if self.showBlockedByObjectMessageTimer:get() then self.vehicle:setCpInfoTextActive(InfoTextManager.BLOCKED_BY_OBJECT) + else + self.vehicle:resetCpActiveInfoText(InfoTextManager.BLOCKED_BY_OBJECT) end - return nil, nil, nil, maxSpeed end From c59fbd638bfaa9f5c906f3de1c5fcc4bceef8217 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:14:21 +0200 Subject: [PATCH 071/107] Added missing unfolding at the start --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 98b7a8805..45dc073bf 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -147,6 +147,8 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) -3, self.silo:getWidth() + 6, self.silo:getLength() + 6) self.siloController = CpBunkerSiloLoaderController(self.silo, self.vehicle, self) + + self.vehicle:raiseAIEvent("onAIFieldWorkerStart", "onAIImplementStart") end ----------------------------------------------------------------------------------------------------------------------- From b7c754072be922aea146bb62fb74ad6b3da8d9b1 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Wed, 6 Sep 2023 19:31:57 +0200 Subject: [PATCH 072/107] DE Update --- config/MasterTranslations.xml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index f7cee8016..acf86bc9f 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2508,9 +2508,16 @@ Mit einem Radlader oder Tranktor mit Frontlader und Schaufel, lassen sich Bunker Die Schaufelpositionen werden dabei automatisch auf die richtige Position gesetzt. Ist der Modus im HUD nicht direkt zu sehen, sollte er durch das durchschalten der "Startposition" gewählt werden können. -Schaufeln mit gebiss öffnen dieses sogar beim laden und abladen und schließen dieses zum Transport. +Im HUD erscheint dann das gewohnte Ziel Icon um direkt mit dem richtigen Modus auf der Helfer Karte zu landen. +Außerdem gibt es eine Anzeige des Fortschritts, die anzeigt, wie viel % des erkannten Haufens bereits abgearbeitet wurde. +Die Arbeitsbreite muss eventuell auch angepasst werden, wenn ihr ein Bunker Silo leeren möchtet. -Die Schaufel zum zerkleinern von Zuckerrüben wird auch unterstützt. +Der Versatz Schaufelhöhe dient dazu, die höhe über dem Boden anzupassen. Diese Höhe sollte relativ genau sein, damit auch alles eingesammelt wird, +oder damit der Radlader nicht an Kanten fest hängt. Wird der Wert verändert, fährt die Schaufel automatisch auf die Position und die eingestellte Höhe. +Zum zurück setzen einfach auf den Text klicken. Der Wert geht von +1 bis -1 in 0,1er Schritten. + +Schaufeln mit gebiss öffnen dieses sogar beim laden und abladen und schließen dieses zum Transport, +sogar die Schaufel zum zerkleinern von Zuckerrüben wird mit allen Funktionen unterstützt. ]]> Date: Wed, 6 Sep 2023 17:32:22 +0000 Subject: [PATCH 073/107] Updated translations --- translations/translation_de.xml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 33aa49ecf..021438955 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -836,9 +836,16 @@ Mit einem Radlader oder Tranktor mit Frontlader und Schaufel, lassen sich Bunker Die Schaufelpositionen werden dabei automatisch auf die richtige Position gesetzt. Ist der Modus im HUD nicht direkt zu sehen, sollte er durch das durchschalten der "Startposition" gewählt werden können. -Schaufeln mit gebiss öffnen dieses sogar beim laden und abladen und schließen dieses zum Transport. +Im HUD erscheint dann das gewohnte Ziel Icon um direkt mit dem richtigen Modus auf der Helfer Karte zu landen. +Außerdem gibt es eine Anzeige des Fortschritts, die anzeigt, wie viel % des erkannten Haufens bereits abgearbeitet wurde. +Die Arbeitsbreite muss eventuell auch angepasst werden, wenn ihr ein Bunker Silo leeren möchtet. -Die Schaufel zum zerkleinern von Zuckerrüben wird auch unterstützt. +Der Versatz Schaufelhöhe dient dazu, die höhe über dem Boden anzupassen. Diese Höhe sollte relativ genau sein, damit auch alles eingesammelt wird, +oder damit der Radlader nicht an Kanten fest hängt. Wird der Wert verändert, fährt die Schaufel automatisch auf die Position und die eingestellte Höhe. +Zum zurück setzen einfach auf den Text klicken. Der Wert geht von +1 bis -1 in 0,1er Schritten. + +Schaufeln mit gebiss öffnen dieses sogar beim laden und abladen und schließen dieses zum Transport, +sogar die Schaufel zum zerkleinern von Zuckerrüben wird mit allen Funktionen unterstützt. "/> + + + From 5efffee42923de5541b87f8adbddda86629c43b9 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:30:26 +0200 Subject: [PATCH 075/107] Vehicle config reload fix --- scripts/config/VehicleConfigurations.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/config/VehicleConfigurations.lua b/scripts/config/VehicleConfigurations.lua index f56edeee1..f6162b72d 100644 --- a/scripts/config/VehicleConfigurations.lua +++ b/scripts/config/VehicleConfigurations.lua @@ -51,6 +51,9 @@ function VehicleConfigurations:registerXmlSchema() end function VehicleConfigurations:loadFromXml() + self.vehicleConfigurations = {} + self.modVehicleConfigurations = {} + self.attributes = {} self.xmlFileName = Utils.getFilename('config/VehicleConfigurations.xml', Courseplay.BASE_DIRECTORY) self:registerXmlSchema() self.xmlFile = self:loadXmlFile(self.xmlFileName, true) @@ -254,7 +257,10 @@ function VehicleConfigurations:consoleCommandPrintSingleAttributeValuesForVehicl local values = self:queryAttributeValues(vehicle, attribute) for _, data in pairs(values) do if data.found then - CpUtil.infoVehicle(data.implement, "%s", tostring(data.value)) + CpUtil.infoVehicle(data.implement, "(%s => Mod: %s) %s", + tostring(data.implement.configFileNameClean), + tostring(data.implement.customEnvironment ~= nil and data.implement.customEnvironment or false), + tostring(data.value)) else CpUtil.infoVehicle(data.implement, "not found") end @@ -273,7 +279,10 @@ function VehicleConfigurations:consoleCommandPrintAllAttributeValuesForVehicleAn CpUtil.info("Found the following for %s: ....", attribute) for _, data in pairs(values) do if data.found then - CpUtil.infoVehicle(data.implement, "%s", tostring(data.value)) + CpUtil.infoVehicle(data.implement, "(%s => Mod: %s) %s", + tostring(data.implement.configFileNameClean), + tostring(data.implement.customEnvironment ~= nil and data.implement.customEnvironment or false), + tostring(data.value)) end end end @@ -282,7 +291,8 @@ end function VehicleConfigurations:consoleCommandPrintAttributeNames() for attribute, xmlValueType in pairs(self.attributes) do - CpUtil.info("Attribute: %s => %s", attribute, XMLValueType.TYPES[xmlValueType].name) + CpUtil.info("Attribute: %s => %s", + attribute, XMLValueType.TYPES[xmlValueType].name) end end From 5f52acd8e0fdc5181dc03f3f2cf11a57792c162b Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:45:21 +0200 Subject: [PATCH 076/107] Fixed vehicle config typo --- scripts/config/VehicleConfigurations.lua | 45 +++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/scripts/config/VehicleConfigurations.lua b/scripts/config/VehicleConfigurations.lua index f6162b72d..cef9e48fe 100644 --- a/scripts/config/VehicleConfigurations.lua +++ b/scripts/config/VehicleConfigurations.lua @@ -139,17 +139,20 @@ function VehicleConfigurations:get(object, attribute) CpUtil.infoImplement(object, "The given vehicle config attribute name: %s is not valid!", attribute) return end + local function getConfigName(data) + if data[object.configFileNameClean] then + return data[object.configFileNameClean][attribute] + elseif data[object.configFileNameClean..".xml"] then + return data[object.configFileNameClean..".xml"][attribute] + end + end if object and object.configFileNameClean then local modName = object.customEnvironment - if self.modVehicleConfigurations[modName] then + if modName and self.modVehicleConfigurations[modName] then --- If a mod name was given, then also check the xml filename. - if self.modVehicleConfigurations[modName][object.configFileNameClean] then - return self.modVehicleConfigurations[modName][object.configFileNameClean][attribute] - end - elseif self.vehicleConfigurations[object.configFileNameClean] then - return self.vehicleConfigurations[object.configFileNameClean][attribute] - elseif self.vehicleConfigurations[object.configFileNameClean..".xml"] then - return self.vehicleConfigurations[object.configFileNameClean..".xml"][attribute] + return getConfigName(self.modVehicleConfigurations[modName]) + else + return getConfigName(self.vehicleConfigurations) end end end @@ -225,6 +228,12 @@ function VehicleConfigurations:registerConsoleCommands() g_devHelper.consoleCommands:registerConsoleCommand("cpVehicleConfigurationsListAttributes", "Prints all valid attribute names", "consoleCommandPrintAttributeNames", self) + g_devHelper.consoleCommands:registerConsoleCommand("cpVehicleConfigurationsPrintAttributes", + "Prints all normal attributes", + "consoleCommandPrintAllNormalVehicleConfigurations", self) + g_devHelper.consoleCommands:registerConsoleCommand("cpVehicleConfigurationsPrintModAttributes", + "Prints all mod name attributes", + "consoleCommandPrintAllModNameVehicleConfigurations", self) end function VehicleConfigurations:consoleCommandReload() @@ -289,6 +298,26 @@ function VehicleConfigurations:consoleCommandPrintAllAttributeValuesForVehicleAn end end +function VehicleConfigurations:consoleCommandPrintAllModNameVehicleConfigurations() + for modName, configurations in pairs(self.modVehicleConfigurations) do + for name, configs in pairs(configurations) do + for n, v in pairs(configs) do + CpUtil.info("(%s => Mod: %s) %s => %s", + name, modName, n, v) + end + end + end +end + +function VehicleConfigurations:consoleCommandPrintAllNormalVehicleConfigurations() + for name, configs in pairs(self.vehicleConfigurations) do + for n, v in pairs(configs) do + CpUtil.info("(%s) %s => %s", + name, n, v) + end + end +end + function VehicleConfigurations:consoleCommandPrintAttributeNames() for attribute, xmlValueType in pairs(self.attributes) do CpUtil.info("Attribute: %s => %s", From 98681f741ac10781200a662da03ef2df89a304d7 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:50:24 +0200 Subject: [PATCH 077/107] Added missing heap filllevel percentage --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 45dc073bf..d5ed15f45 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -138,6 +138,8 @@ function AIDriveStrategyShovelSiloLoader:startWithoutCourse(jobParameters) self:debug("Heap was found.") self.silo = self.heapSilo end + --- fill level, when the driver is started + self.fillLevelLeftOverSinceStart = self.silo:getTotalFillLevel() local cx, cz = self.silo:getFrontCenter() local dirX, dirZ = self.silo:getLengthDirection() @@ -357,6 +359,10 @@ function AIDriveStrategyShovelSiloLoader:update(dt) AIDriveStrategyCourse.update(self) end +function AIDriveStrategyShovelSiloLoader:updateCpStatus(status) + status:setSiloLoaderStatus(self.silo:getTotalFillLevel(), self.fillLevelLeftOverSinceStart) +end + --- Ignores the bunker silo and the unload target for the proximity sensors. function AIDriveStrategyShovelSiloLoader:ignoreProximityObject(object, vehicle) if self.silo:isTheSameSilo(object) then From cf91cda1009a2563f8948b7c1ad480f398cbbb54 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Thu, 7 Sep 2023 18:58:01 +0200 Subject: [PATCH 078/107] EN Translation WIP --- config/MasterTranslations.xml | 42 +++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index acf86bc9f..b3857447d 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2505,22 +2505,35 @@ Now it's possible to start the helper. @@ -2528,13 +2541,19 @@ WIP Um einen Radlader Fahrer zu starten, müssen zunächst über das Ziel Icon im HUD die Lade- und Entladeposition gewählt werden. Die Ladeposition wird wie auch bereits beim Lader mit der Ladeposition gewählt. Ein blauer Ramen entsteht um den Haufen. -Die Abladeposition hängt davon ab, ob du in einen Anhänger oder in einen Trigger entladen möchtest. +Die Abladeposition hängt davon ab, ob du in einen Anhänger oder in einen Lager entladen möchtest. In einen Anhänger funktioniert automatisch und braucht keine Position. Es wird der naheste, stehende Anhänger gewählt und seitlich angefahren. Möchtest du in ein Lager entladen, muss zunächst das Ziel umgestellt werden und anschließend den Marker auf den Trigger gesetzt werden. ]]> @@ -2547,7 +2566,12 @@ Anschließend musst du noch die richtung wählen, in die der Fahrer den Trigger Das Kreuz sollte jetzt wie im Bild dar gestellt gelb sein. ]]> From 66e4f825a426589f9331fe28ffcee7a993512860 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 8 Sep 2023 18:19:10 +0200 Subject: [PATCH 080/107] Some Translation adjustments --- config/MasterTranslations.xml | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index b3857447d..a6c7bccea 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2504,54 +2504,54 @@ Now it's possible to start the helper. @@ -2563,7 +2563,7 @@ Da Gebäude mehrere Trigger haben können, muss dieser selber gewählt werden. Die verfügbaren Trigger werden als orangene Kreuze da gestellt. Um einen Trigger zu wählen, setzt du den Punkt in der mitte des Runden Icons einfach mittig auf das Kreuz. Anschließend musst du noch die richtung wählen, in die der Fahrer den Trigger anfahren soll, da es keine klare Richtung eines Triggers gibt. -Das Kreuz sollte jetzt wie im Bild dar gestellt gelb sein. +Das Kreuz sollte jetzt, wie im Bild da gestellt, gelb sein. ]]> Date: Fri, 8 Sep 2023 16:19:34 +0000 Subject: [PATCH 081/107] Updated translations --- translations/translation_de.xml | 20 ++++++++++---------- translations/translation_en.xml | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 10996c965..30e3413bf 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -832,29 +832,29 @@ Anschließend kann der Helfer gestartet werden. "/> diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 0ab4c7d95..48851b82f 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -832,27 +832,27 @@ Now it's possible to start the helper. "/> From eed839320cb98aa2601663aa0cadb2c743074f0e Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:33:12 +0200 Subject: [PATCH 082/107] Changed silo loader status to liter and shovel grabber adjustment --- scripts/gui/CpGuiUtil.lua | 20 +++++++++++++++++++ scripts/gui/CpStatus.lua | 15 +++++++++----- scripts/specializations/CpShovelPositions.lua | 2 +- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/scripts/gui/CpGuiUtil.lua b/scripts/gui/CpGuiUtil.lua index 7d920a159..489cadcd7 100644 --- a/scripts/gui/CpGuiUtil.lua +++ b/scripts/gui/CpGuiUtil.lua @@ -512,3 +512,23 @@ function CpGuiUtil.openGlobalSettingsGui(vehicle) local inGameMenu = CpGuiUtil.preOpeningInGameMenu(vehicle) inGameMenu:goToPage(inGameMenu.pageCpGlobalSettings) end + +CpGuiUtil.UNIT_EXTENSIONS = { + "k", + "M", + "G", + "T" +} +--- Converts the number into kilo, mega, giga or tera units with the correct symbol. +---@param num number +---@return number +---@return string +function CpGuiUtil.getFixedUnitValueWithUnitSymbol(num) + for i=4, 1, -1 do + local delta = math.pow(10, 3 * i) + if num >= delta then + return num / delta, CpGuiUtil.UNIT_EXTENSIONS[i] + end + end + return num, "" +end \ No newline at end of file diff --git a/scripts/gui/CpStatus.lua b/scripts/gui/CpStatus.lua index e756d37e6..528779123 100644 --- a/scripts/gui/CpStatus.lua +++ b/scripts/gui/CpStatus.lua @@ -132,11 +132,16 @@ function CpStatus:getCompactionText(withoutPercentageSymbol) end function CpStatus:getSiloFillLevelPercentageLeftOver(withoutPercentageSymbol) - if self.isActive and self.fillLevelPercentageLeftOver ~=nil then - if withoutPercentageSymbol then - return tostring(self.fillLevelPercentageLeftOver) - end - return string.format('%d%%', self.fillLevelPercentageLeftOver) + -- if self.isActive and self.fillLevelPercentageLeftOver ~=nil then + -- if withoutPercentageSymbol then + -- return tostring(self.fillLevelPercentageLeftOver) + -- end + -- return string.format('%d%%', self.fillLevelPercentageLeftOver) + -- end + -- return '--' + if self.isActive and self.fillLevelLeftOver ~= nil then + local value, unitExtension = CpGuiUtil.getFixedUnitValueWithUnitSymbol(self.fillLevelLeftOver) + return string.format("%.1f %s%s", value, unitExtension, g_i18n:getText("unit_literShort")) end return '--' end diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 44bec80ff..360dd1d66 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -230,7 +230,7 @@ function CpShovelPositions:controlShovelPosition(dt, targetAngle) --- So it needs to be opened for loading and unloading. for i, tool in pairs(self.spec_cylindered.movingTools) do if tool.axis and tool.rotMax ~= nil and tool.rotMin ~= nil then - if spec.state ~= CpShovelPositions.TRANSPORT then + if spec.state == CpShovelPositions.LOADING or spec.state == CpShovelPositions.UNLOADING then --- Opens the shovel for loading and unloading isDirty = ImplementUtil.moveMovingToolToRotation(self, tool, dt, tool.invertAxis and tool.rotMin or tool.rotMax) From fc7dab8910fadfa750f21d23f3235c58b9931563 Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 8 Sep 2023 18:51:35 +0200 Subject: [PATCH 083/107] Translation adjustment and recheck time reduced Info for Translator: Line 96 "Progress" -> "Liters left" --- config/MasterTranslations.xml | 4 ++-- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index a6c7bccea..364deb16b 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -302,8 +302,8 @@ - - + + diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index d5ed15f45..43ec9c93d 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -59,7 +59,7 @@ AIDriveStrategyShovelSiloLoader.myStates = { } AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 -AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 30 +AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 10 AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8 AIDriveStrategyShovelSiloLoader.isStuckMs = 1000 * 15 From dccce59851ebb4459719eac78c463a90e325d69c Mon Sep 17 00:00:00 2001 From: Tensuko Date: Fri, 8 Sep 2023 16:51:54 +0000 Subject: [PATCH 084/107] Updated translations --- translations/translation_de.xml | 2 +- translations/translation_en.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/translations/translation_de.xml b/translations/translation_de.xml index 30e3413bf..9de92e8c7 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -93,7 +93,7 @@ - + diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 48851b82f..8741c813d 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -93,7 +93,7 @@ - + From f4e197152242785c87f9af566075060fee862aa3 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 8 Sep 2023 18:51:59 +0200 Subject: [PATCH 085/107] Updated comments --- scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index 91ebc0aa3..89567521d 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -35,11 +35,11 @@ function CpSiloLoaderWorkerHudPageElement:setupElements(baseHud, vehicle, lines, end) - --- Progress of fill level removed since the start of the driver. + --- Displays the fill level of current worked on heap. local x, y = unpack(lines[4].left) self.fillLevelProgressLabel = CpTextHudElement.new(self , x , y, CpBaseHud.defaultFontSize) self.fillLevelProgressLabel:setTextDetails(g_i18n:getText("CP_siloLoader_fillLevelProgress")) - --- Progress of fill level removed since the start of the driver. + --- Displays the fill level of current worked on heap. local x, y = unpack(lines[4].right) self.fillLevelProgressText = CpTextHudElement.new(self, x, y, CpBaseHud.defaultFontSize, RenderText.ALIGN_RIGHT) From 98fc18c6812a702838884f6cd1e1185a7fc3f4ae Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:42:00 +0200 Subject: [PATCH 086/107] Timing callstack fix --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 43ec9c93d..61eefd69a 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -293,10 +293,6 @@ function AIDriveStrategyShovelSiloLoader:getDriveData(dt, vX, vY, vZ) self:searchForTrailerToUnloadInto() self.lastTrailerSearch = g_time end - if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then - DebugUtil.drawDebugCircleAtNode(self.siloFrontNode, self.maxValidTrailerDistanceToSiloFront, - math.ceil(self.maxValidTrailerDistanceToSiloFront), nil, false, {0, 3, 0}) - end elseif self.state == self.states.DRIVING_TO_UNLOAD then self:setMaxSpeed(self.settings.reverseSpeed:getValue()) local refNode @@ -337,6 +333,10 @@ end function AIDriveStrategyShovelSiloLoader:update(dt) if CpDebug:isChannelActive(CpDebug.DBG_SILO, self.vehicle) then + if self.siloFrontNode and self.state == self.states.WAITING_FOR_TRAILER then + DebugUtil.drawDebugCircleAtNode(self.siloFrontNode, self.maxValidTrailerDistanceToSiloFront, + math.ceil(self.maxValidTrailerDistanceToSiloFront), nil, false, {0, 3, 0}) + end if self.course:isTemporary() then self.course:draw() elseif self.ppc:getCourse():isTemporary() then From 6a46f82862460e8758bd7ba2528d52ee808d6d0d Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 8 Sep 2023 20:41:12 +0200 Subject: [PATCH 087/107] minior debug adjustment --- scripts/specializations/CpShovelPositions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 360dd1d66..0cae82b68 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -583,7 +583,7 @@ end function CpShovelPositions.debug(implement, ...) if CpShovelPositions.DEBUG then - CpUtil.infoImplement(implement, ...) + CpUtil.debugImplement(implement, ...) end end From cbb38b4213a2f5f866e597b1c580869fd28cd224 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 09:59:11 +0200 Subject: [PATCH 088/107] PR Adjustments --- config/VehicleConfigurations.xml | 8 +++++--- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 2 +- scripts/ai/ImplementUtil.lua | 4 ---- scripts/gui/CoursePlot.lua | 6 ------ scripts/gui/CpStatus.lua | 7 ------- scripts/pathfinder/PathfinderUtil.lua | 4 ++-- scripts/specializations/CpAIWorker.lua | 16 +++------------- 7 files changed, 11 insertions(+), 36 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 3e69c4bee..261a3d082 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -98,11 +98,12 @@ You can define the following custom settings: Used to control the target tilt. - loadingShovelOffset: number -1 : 1 - Offset relative to the calculated loading shovel position, needs to be set for a shovel implement. + Offset to the loading and transport shovel position in meter for the height of the shovel. - shovelMovingToolIx: number - If the shovel is a high dump shovel then this moving tool ix is needed. - + If the shovel is a high dump shovel then the moving tool index of the correct one is needed. + Usually this index is 1 for the first moving tool. + - modName: Name of the .zip file (without '.zip') In case a Mod has the same .xml filename for the vehicle/implement, as a default giants vehicle/implement, add the name of the .zip file to prevent conflicts. For example: "FS22_exampleMod". @@ -138,6 +139,7 @@ You can define the following custom settings: armMovingToolIx movingToolIx shovelMovingToolIx + loadingShovelOffset ignoreBaleCollisionForward fixWheelLoaderDirectionNodeByMovingToolIx articulatedAxisReverseNodeInverted diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 61eefd69a..3c117e4c8 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -1,5 +1,5 @@ --[[ -This file is part of Courseplay (https://github.com/Courseplay/courseplay) +This file is part of Courseplay (https://github.com/Courseplay/Courseplay_FS22) Copyright (C) 2023 Courseplay Dev Team This program is free software: you can redistribute it and/or modify diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index f7ccef7b1..82defb206 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -378,10 +378,6 @@ function ImplementUtil.moveMovingToolToRotation(implement, tool, dt, rotTarget) local dir = MathUtil.sign(diff) local rotSpeed = MathUtil.clamp( math.abs(diff) * math.abs(tool.rotSpeed), math.abs(tool.rotSpeed)/3, 0.5 ) rotSpeed = dir * rotSpeed - --local rotSpeed = MathUtil.clamp(diff * tool.rotSpeed, tool.rotSpeed/3, 0.5) - -- if diff < 0 then - -- rotSpeed=rotSpeed*(-1) - -- end if math.abs(diff) < 0.03 or rotSpeed == 0 then ImplementUtil.stopMovingTool(implement, tool) return false diff --git a/scripts/gui/CoursePlot.lua b/scripts/gui/CoursePlot.lua index aa60429ba..6069f97f0 100644 --- a/scripts/gui/CoursePlot.lua +++ b/scripts/gui/CoursePlot.lua @@ -79,12 +79,6 @@ function CoursePlot:setStopPosition( x, z ) self.stopPosition.x, self.stopPosition.z = x, z end --- function CoursePlot:screenToWorld( x, y ) --- local worldX = ((x - self.x) / self.scaleX) - self.worldOffsetX --- local worldZ = ((y - self.y - self.height) / -self.scaleZ) - self.worldOffsetZ --- return worldX, worldZ --- end - -- Draw the waypoints in the screen area defined in new(), the bottom left corner -- is at worldX/worldZ coordinates, the size shown is worldWidth wide (and high) function CoursePlot:drawPoints(map, points, isHudMap) diff --git a/scripts/gui/CpStatus.lua b/scripts/gui/CpStatus.lua index 528779123..ce261d9a8 100644 --- a/scripts/gui/CpStatus.lua +++ b/scripts/gui/CpStatus.lua @@ -132,13 +132,6 @@ function CpStatus:getCompactionText(withoutPercentageSymbol) end function CpStatus:getSiloFillLevelPercentageLeftOver(withoutPercentageSymbol) - -- if self.isActive and self.fillLevelPercentageLeftOver ~=nil then - -- if withoutPercentageSymbol then - -- return tostring(self.fillLevelPercentageLeftOver) - -- end - -- return string.format('%d%%', self.fillLevelPercentageLeftOver) - -- end - -- return '--' if self.isActive and self.fillLevelLeftOver ~= nil then local value, unitExtension = CpGuiUtil.getFixedUnitValueWithUnitSymbol(self.fillLevelLeftOver) return string.format("%.1f %s%s", value, unitExtension, g_i18n:getText("unit_literShort")) diff --git a/scripts/pathfinder/PathfinderUtil.lua b/scripts/pathfinder/PathfinderUtil.lua index a2423dfae..32b19e645 100644 --- a/scripts/pathfinder/PathfinderUtil.lua +++ b/scripts/pathfinder/PathfinderUtil.lua @@ -890,8 +890,8 @@ function PathfinderUtil.getNodePositionAndDirection(node, xOffset, zOffset) end ---@param vehicle table ----@param xOffset|nil ----@param zOffset|nil +---@param xOffset number|nil +---@param zOffset number|nil ---@return State3D position/heading of vehicle function PathfinderUtil.getVehiclePositionAsState3D(vehicle, xOffset, zOffset) local x, z, yRot = PathfinderUtil.getNodePositionAndDirection( AIUtil.getDirectionNode(vehicle), xOffset, zOffset) diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 92ddfbf56..5532b9210 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -72,7 +72,6 @@ function CpAIWorker.registerFunctions(vehicleType) SpecializationUtil.registerFunction(vehicleType, "cpHold", CpAIWorker.cpHold) SpecializationUtil.registerFunction(vehicleType, "cpBrakeToStop", CpAIWorker.cpBrakeToStop) SpecializationUtil.registerFunction(vehicleType, "getCpDriveStrategy", CpAIWorker.getCpDriveStrategy) - SpecializationUtil.registerFunction(vehicleType, 'getCpReverseDrivingDirectionNode', CpAIWorker.getCpReverseDrivingDirectionNode) end function CpAIWorker.registerOverwrittenFunctions(vehicleType) @@ -503,21 +502,12 @@ function CpAIWorker:getCpDriveStrategy() return spec.driveStrategy end -function CpAIWorker:getCpReverseDrivingDirectionNode() - local spec = self.spec_cpAIWorker - if not spec.reverseDrivingDirectionNode and SpecializationUtil.hasSpecialization(ReverseDriving, self.specializations) then - spec.reverseDrivingDirectionNode = - CpUtil.createNewLinkedNode(self, "realReverseDrivingDirectionNode", self:getAIDirectionNode()) - setRotation(spec.reverseDrivingDirectionNode, 0, math.pi, 0) - end - return spec.reverseDrivingDirectionNode -end - --- Fixes the ai reverse node rotation for articulated axis vehicles, --- if the node is pointing backwards and not forwards. +--- TODO: Consider consolidation with AIUtil.getArticulatedAxisVehicleReverserNode function CpAIWorker:getAIReverserNode(superFunc) local spec = self.spec_cpAIWorker - -- if not self:getIsCpActive() then return superFunc(self) end + if not self:getIsCpActive() then return superFunc(self) end if self.spec_articulatedAxis and self.spec_articulatedAxis.aiRevereserNode then if g_vehicleConfigurations:get(self, "articulatedAxisReverseNodeInverted") then if not spec.articulatedAxisReverseNode then @@ -536,7 +526,7 @@ end --- So we use the parent node of the arm tool node. ---@param superFunc any function CpAIWorker:getAIDirectionNode(superFunc) - -- if not self:getIsCpActive() then return superFunc(self) end + if not self:getIsCpActive() then return superFunc(self) end local movingToolIx = g_vehicleConfigurations:get(self, "fixWheelLoaderDirectionNodeByMovingToolIx") if movingToolIx ~= nil then return getParent(self.spec_cylindered.movingTools[movingToolIx].node) From 5bd671d0bd0b76d3baf0517590275784e1568bc9 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 10:42:15 +0200 Subject: [PATCH 089/107] Small adjustment for vehicle config --- config/VehicleSettingsSetup.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/VehicleSettingsSetup.xml b/config/VehicleSettingsSetup.xml index 3456b6eed..adbfe7500 100644 --- a/config/VehicleSettingsSetup.xml +++ b/config/VehicleSettingsSetup.xml @@ -77,7 +77,8 @@ + isVisible="isLoadingShovelOffsetSettingVisible" isDisabled="isLoadingShovelOffsetSettingDisabled" + onChangeCallback="onCpLoadingShovelOffsetSettingChanged" vehicleConfiguration="loadingShovelOffset"/> From 1dc8ef47e98c190ac67e857eef487fee46ffd559 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 10:57:53 +0200 Subject: [PATCH 090/107] debug fix --- scripts/trigger/TriggerManager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua index d841002da..20386277f 100644 --- a/scripts/trigger/TriggerManager.lua +++ b/scripts/trigger/TriggerManager.lua @@ -2,7 +2,7 @@ --- For now only unload triggers are supported. ---@class TriggerManager TriggerManager = CpObject() -TriggerManager.DEBUG = true +TriggerManager.DEBUG = false function TriggerManager:init() ---@type table self.unloadTriggers = {} From 2ea4cfdb9ff83c56f8952dca3eea9f5a73529223 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 11:37:19 +0200 Subject: [PATCH 091/107] Fixes shovel controller mp for now --- scripts/ai/controllers/ShovelController.lua | 2 +- scripts/specializations/CpAIWorker.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 3a46b13c1..22d4ca547 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -222,7 +222,7 @@ function ShovelController:calculateMinimalUnloadingHeightRaycastCallback(hitObje return false end -function ShovelController:onFinished() +function ShovelController:delete() if self.implement.cpResetShovelState then self.implement:cpResetShovelState() end diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 5532b9210..1fcdcbedb 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -297,6 +297,8 @@ end --- Used to enable/disable release of the helper --- and handles post release functionality with for example auto drive. +--- TODO: this might by only called on the client, so +--- server depended code has to be moved to stopJob or similar code. function CpAIWorker:stopCurrentAIJob(superFunc, message, ...) if message then CpUtil.debugVehicle(CpDebug.DBG_FIELDWORK, self, "stop message: %s", message:getMessage()) From 33c1d9634a8481122f066787ad852d9f81ba0bbd Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 12:08:58 +0200 Subject: [PATCH 092/107] Bug fix ... --- scripts/CpUtil.lua | 5 ++--- scripts/editor/CourseEditor.lua | 4 +++- scripts/specializations/CpAIWorker.lua | 1 + scripts/specializations/CpShovelPositions.lua | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/CpUtil.lua b/scripts/CpUtil.lua index 65f2436ad..aa7de4cdf 100644 --- a/scripts/CpUtil.lua +++ b/scripts/CpUtil.lua @@ -169,11 +169,10 @@ end --- Debug function for implements, that calls CpUtil.debugVehicle, with the root vehicle. ---@param channel number ---@param implement table ----@param ... unknown -function CpUtil.debugImplement(channel, implement, ...) +function CpUtil.debugImplement(channel, implement, str, ...) local rootVehicle = implement and implement.rootVehicle or implement CpUtil.debugVehicle(channel, rootVehicle, - CpUtil.getName(implement) .. ': ' .. string.format(...)) + CpUtil.getName(implement) .. ": " .. str, ...) end function CpUtil.isVehicleDebugActive(vehicle) diff --git a/scripts/editor/CourseEditor.lua b/scripts/editor/CourseEditor.lua index e11782857..a2e34485e 100644 --- a/scripts/editor/CourseEditor.lua +++ b/scripts/editor/CourseEditor.lua @@ -114,7 +114,9 @@ function CourseEditor:showYesNoDialog(title, callbackFunc) end function CourseEditor:delete() - self.courseDisplay:delete() + if self.courseDisplay then + self.courseDisplay:delete() + end end --- Updates the course display, when a waypoint change happened. diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 1fcdcbedb..5e39040fc 100644 --- a/scripts/specializations/CpAIWorker.lua +++ b/scripts/specializations/CpAIWorker.lua @@ -468,6 +468,7 @@ function CpAIWorker:cpHold(ms) end end +---@param strategy AIDriveStrategyCourse function CpAIWorker:startCpWithStrategy(strategy) local spec = self.spec_cpAIWorker spec.driveStrategy = strategy diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 0cae82b68..d8e73eb00 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -583,7 +583,7 @@ end function CpShovelPositions.debug(implement, ...) if CpShovelPositions.DEBUG then - CpUtil.debugImplement(implement, ...) + CpUtil.debugImplement(CpDebug.DBG_SILO, implement, ...) end end From ec2e6b5091ac92db70f1b28ef0e501bcfe0fb3c5 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 12:33:18 +0200 Subject: [PATCH 093/107] Added additional mp safety check for shovel positions --- scripts/specializations/CpShovelPositions.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index d8e73eb00..82ef7e1ea 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -143,6 +143,7 @@ end --- Changes the current shovel state position. function CpShovelPositions:cpSetShovelState(state) + if not self.isServer then return end local spec = self.spec_cpShovelPositions spec.resetWhenReached = false if spec.state ~= state then @@ -155,6 +156,7 @@ function CpShovelPositions:cpSetShovelState(state) end function CpShovelPositions:cpSetTemporaryShovelState(state) + if not self.isServer then return end local spec = self.spec_cpShovelPositions self:cpSetShovelState(state) spec.resetStateWhenReached = true @@ -166,6 +168,7 @@ end --- Deactivates the shovel position control. function CpShovelPositions:cpResetShovelState() + if not self.isServer then return end CpShovelPositions.debug(self, "Reset shovelPositionState.") local spec = self.spec_cpShovelPositions spec.state = CpShovelPositions.DEACTIVATED From 330314aeb00f9a45ee09b026f62923c937c51132 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 14:34:26 +0200 Subject: [PATCH 094/107] Update modDesc.xml --- modDesc.xml | 82 ++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/modDesc.xml b/modDesc.xml index 3122d9935..d03191a74 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -222,11 +222,24 @@ Changelog 7.1.0.0: + + + + + + + + + + + + + @@ -238,21 +251,20 @@ Changelog 7.1.0.0: - - - - - + + + + @@ -261,17 +273,30 @@ Changelog 7.1.0.0: + + + + + + + + + + + + + - + @@ -283,16 +308,8 @@ Changelog 7.1.0.0: - - - - - - - - - - + + @@ -327,35 +344,14 @@ Changelog 7.1.0.0: - - - - - - - - - - - - - - - - - - - - - - - + + @@ -363,6 +359,7 @@ Changelog 7.1.0.0: + @@ -370,9 +367,7 @@ Changelog 7.1.0.0: - - - + @@ -387,6 +382,7 @@ Changelog 7.1.0.0: + @@ -395,6 +391,7 @@ Changelog 7.1.0.0: + @@ -407,6 +404,7 @@ Changelog 7.1.0.0: + From 4ac14e786b68208d9848ea6d5b6a434f363e53e3 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 14:38:21 +0200 Subject: [PATCH 095/107] Rebase fix --- modDesc.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modDesc.xml b/modDesc.xml index d03191a74..56bea87ef 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -297,19 +297,6 @@ Changelog 7.1.0.0: - - - - - - - - - - - - - @@ -345,6 +332,19 @@ Changelog 7.1.0.0: + + + + + + + + + + + + + From e8544649d712038ef89a72df9a10783d2e83d37a Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sun, 10 Sep 2023 20:32:55 +0200 Subject: [PATCH 096/107] Added vehicle config for premos --- config/VehicleConfigurations.xml | 16 +++++++++++++++- scripts/ai/controllers/PipeController.lua | 16 ++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index 261a3d082..d50e7001c 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -117,9 +117,16 @@ You can define the following custom settings: Fixes the ai direction node for the platinum wheel loaders, as their direction is not changed based on the rotation. As a fix the parent node of the arm moving tool ix is used. -- articulatedAxisReverseNodeInverted +- articulatedAxisReverseNodeInverted: boolean Is the reverse node for articulated axis vehicles rotated by 180 degree? +- unloadXOffset: number + Offset of the discharge node or a given pipe for unloading into a trailer. + For pipes this overwrites the measured pipe offset. + +- disablePipeMovingToolCorrection: boolean + Disables the pipe height adjustment for trailers of a few pipe implements/vehicles. + --> @@ -143,6 +150,8 @@ You can define the following custom settings: ignoreBaleCollisionForward fixWheelLoaderDirectionNodeByMovingToolIx articulatedAxisReverseNodeInverted + disablePipeMovingToolCorrection + unloadOffsetX @@ -535,4 +544,9 @@ You can define the following custom settings: turnRadius = "10" /> + + diff --git a/scripts/ai/controllers/PipeController.lua b/scripts/ai/controllers/PipeController.lua index 2ed47da1e..2040c0574 100644 --- a/scripts/ai/controllers/PipeController.lua +++ b/scripts/ai/controllers/PipeController.lua @@ -293,7 +293,11 @@ function PipeController:measurePipeProperties() currentPipeState, targetPipeState, pipeAnimTime) self:printFoldableDebug() self:printPipeDebug() - + local configOffsetX = g_vehicleConfigurations:get(self.implement, "unloadOffsetX" ) + if configOffsetX ~= nil then + self:debug("Setting pipe x offset to configured %.2f x offset", configOffsetX) + self.pipeOffsetX = configOffsetX + end end --- Unfolds the pipe completely to measure the pipe properties. @@ -403,6 +407,13 @@ end function PipeController:setupMoveablePipe() self.validMovingTools = {} + self.hasPipeMovingTools = false + self.tempBaseNode = CpUtil.createNode("tempBaseNode", 0, 0, 0) + self.tempDependedNode = CpUtil.createNode("tempDependedNode", 0, 0, 0) + if g_vehicleConfigurations:get(self.implement, "disablePipeMovingToolCorrection") then + --- Moveable pipe will not be adjusted based on a nearby trailer. + return + end if self.cylinderedSpec and self.pipeSpec.numAutoAimingStates <= 0 then for i, m in ipairs(self.cylinderedSpec.movingTools) do -- Gets only the pipe moving tools. @@ -437,9 +448,6 @@ function PipeController:setupMoveablePipe() end end - self.tempBaseNode = CpUtil.createNode("tempBaseNode", 0, 0, 0) - self.tempDependedNode = CpUtil.createNode("tempDependedNode", 0, 0, 0) - self:debug("Number of moveable pipe elements found: %d", #self.validMovingTools) end From 43c026edddf6e904cee404ac189e637a7d1bd63e Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:17:14 +0200 Subject: [PATCH 097/107] Translation adjustments --- config/MasterTranslations.xml | 72 ++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index 364deb16b..d86e4a427 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -2505,55 +2505,59 @@ Now it's possible to start the helper. @@ -2561,17 +2565,17 @@ If you want to unload into a storage, you need to switch the target and then mar Die Auswahl eines Triggers wirkt zunächst kompliziert, ist aber im Prinzip recht einfach. Da Gebäude mehrere Trigger haben können, muss dieser selber gewählt werden. Die verfügbaren Trigger werden als orangene Kreuze da gestellt. -Um einen Trigger zu wählen, setzt du den Punkt in der mitte des Runden Icons einfach mittig auf das Kreuz. -Anschließend musst du noch die richtung wählen, in die der Fahrer den Trigger anfahren soll, da es keine klare Richtung eines Triggers gibt. +Um einen Trigger zu wählen, setzt du den Punkt in der Mitte des runden Icons einfach mittig auf das Kreuz. +Die Richtung des Pfeils entspricht hierbei die Anfahrtsrichtung vom Helfer. Das Kreuz sollte jetzt, wie im Bild da gestellt, gelb sein. ]]> diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 8741c813d..b84ef98c0 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -832,37 +832,40 @@ Now it's possible to start the helper. "/> From c24c1b6f6a7bc15592466997b85c0051c6f6a070 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 12 Sep 2023 19:19:42 +0200 Subject: [PATCH 099/107] Fixes hud bug --- scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua index 89567521d..4d5068109 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -156,7 +156,7 @@ function CpSiloLoaderWorkerHudPageElement:arePositionEqual(parameters, otherPara end function CpSiloLoaderWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) - return AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) + return AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) or vehicle:getIsCpActive() end function CpSiloLoaderWorkerHudPageElement:getStartingPointBtnText(vehicle) From 867576f41d3fff896efc8d31422ed5e720478326 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Sat, 16 Sep 2023 17:39:18 +0200 Subject: [PATCH 100/107] Rebase fix --- scripts/ai/AIReverseDriver.lua | 141 +++++++++++++++++---------------- 1 file changed, 72 insertions(+), 69 deletions(-) diff --git a/scripts/ai/AIReverseDriver.lua b/scripts/ai/AIReverseDriver.lua index fedb55bd2..249767095 100644 --- a/scripts/ai/AIReverseDriver.lua +++ b/scripts/ai/AIReverseDriver.lua @@ -63,87 +63,90 @@ function AIReverseDriver:debug(...) end function AIReverseDriver:getDriveData() - if self.reversingImplement == nil then - -- no wheeled implement, simple reversing the PPC can handle by itself - return nil - end - - local trailerNode = self.reversingImplement.steeringAxleNode - local xTipper, yTipper, zTipper = getWorldTranslation(trailerNode); - - local trailerFrontNode = self.reversingImplement.reversingProperties.frontNode - local xFrontNode,yFrontNode,zFrontNode = getWorldTranslation(trailerFrontNode) - - local tx, ty, tz = self.ppc:getGoalPointPosition() - - local lxTipper, lzTipper = AIVehicleUtil.getDriveDirection(trailerNode, tx, ty, tz) - - self:showDirection(trailerNode, lxTipper, lzTipper, 1, 0, 0) - - local lxFrontNode, lzFrontNode = AIVehicleUtil.getDriveDirection(trailerFrontNode, xTipper, yTipper, zTipper) - - local lxTractor, lzTractor = 0, 0 + if self.reversingImplement == nil then + -- no wheeled implement, simple reversing the PPC can handle by itself + return nil + end - local maxTractorAngle = math.rad(75) + -- if there's a reverser node on the tool, use that, otherwise the steering node + -- the reverser direction node, if exists, works better for tools with offset or for + -- rotating plows where it remains oriented and placed correctly + local trailerNode = AIVehicleUtil.getAIToolReverserDirectionNode(self.vehicle) or self.reversingImplement.steeringAxleNode + local trailerFrontNode = self.reversingImplement.reversingProperties.frontNode + + local tx, ty, tz = self.ppc:getGoalPointPosition() + local lxTrailer, lzTrailer = AIVehicleUtil.getDriveDirection(trailerNode, tx, ty, tz) + self:showDirection(trailerNode, lxTrailer, lzTrailer, 1, 0, 0) + + local maxTractorAngle = math.rad(75) + + -- for articulated vehicles use the articulated axis' rotation node as it is a better indicator or the + -- vehicle's orientation than the direction node which often turns/moves with an articulated vehicle part + -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already + local tractorNode + local useArticulatedAxisRotationNode = SpecializationUtil.hasSpecialization(ArticulatedAxis, self.vehicle.specializations) and self.vehicle.spec_articulatedAxis.rotationNode + if useArticulatedAxisRotationNode then + tractorNode = self.vehicle.spec_articulatedAxis.rotationNode + else + tractorNode = self.vehicle:getAIDirectionNode() + end - -- for articulated vehicles use the articulated axis' reverser node as it is a better indicator or the - -- vehicle's orientation than the direction node which often turns/moves with an articulated vehicle part - -- TODO: consolidate this with AITurn:getTurnNode() and if getAIDirectionNode() considers this already - local tractorNode - if self.vehicle.spec_articulatedAxis then - --- TODO consolidate this with AIUtil.getReverserNode() maybe ?? - local node = self.vehicle.getAIReverserNode and self.vehicle:getAIReverserNode() - tractorNode = node or AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) - end - if not tractorNode then - tractorNode = self.vehicle:getAIDirectionNode() - end + local lx, lz, angleDiff - local lx, lz, angleDiff - - if self.reversingImplement.reversingProperties.isPivot then - self:showDirection(trailerFrontNode, lxFrontNode, lzFrontNode, 0, 1, 0) + if self.reversingImplement.reversingProperties.isPivot then + -- The trailer/implement has a front axle (or dolly) with a draw bar. + -- The current Courseplay dev team has no idea how this works :), this is magic + -- from the old code, written by Satissis (Claus). + -- TODO: adapt a documented algorithm for these trailers + local xTrailer, yTrailer, zTrailer = getWorldTranslation(trailerNode); + local xFrontNode, yFrontNode, zFrontNode = getWorldTranslation(trailerFrontNode) + local lxFrontNode, lzFrontNode = AIVehicleUtil.getDriveDirection(trailerFrontNode, xTrailer, yTrailer, zTrailer) + self:showDirection(trailerFrontNode, lxFrontNode, lzFrontNode, 0, 1, 0) - lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(tractorNode, xFrontNode, yFrontNode, zFrontNode) - self:showDirection(tractorNode,lxTractor, lzTractor, 0, 0.7, 0) + local lxTractor, lzTractor = AIVehicleUtil.getDriveDirection(tractorNode, xFrontNode, yFrontNode, zFrontNode) + self:showDirection(tractorNode, lxTractor, lzTractor, 0, 0.7, 0) - local rotDelta = (self.reversingImplement.reversingProperties.nodeDistance * - (0.5 - (0.023 * self.reversingImplement.reversingProperties.nodeDistance - 0.073))) - local trailerToWaypointAngle = self:getLocalYRotationToPoint(trailerNode, tx, ty, tz, -1) * rotDelta - trailerToWaypointAngle = MathUtil.clamp(trailerToWaypointAngle, -math.rad(90), math.rad(90)) + local rotDelta = (self.reversingImplement.reversingProperties.nodeDistance * + (0.5 - (0.023 * self.reversingImplement.reversingProperties.nodeDistance - 0.073))) + local trailerToWaypointAngle = self:getLocalYRotationToPoint(trailerNode, tx, ty, tz, -1) * rotDelta + trailerToWaypointAngle = MathUtil.clamp(trailerToWaypointAngle, -math.rad(90), math.rad(90)) - local dollyToTrailerAngle = self:getLocalYRotationToPoint(trailerFrontNode, xTipper, yTipper, zTipper, -1) + local dollyToTrailerAngle = self:getLocalYRotationToPoint(trailerFrontNode, xTrailer, yTrailer, zTrailer, -1) - local tractorToDollyAngle = self:getLocalYRotationToPoint(tractorNode, xFrontNode, yFrontNode, zFrontNode, -1) + local tractorToDollyAngle = self:getLocalYRotationToPoint(tractorNode, xFrontNode, yFrontNode, zFrontNode, -1) - local rearAngleDiff = (dollyToTrailerAngle - trailerToWaypointAngle) - rearAngleDiff = MathUtil.clamp(rearAngleDiff, -math.rad(45), math.rad(45)) + local rearAngleDiff = (dollyToTrailerAngle - trailerToWaypointAngle) + rearAngleDiff = MathUtil.clamp(rearAngleDiff, -math.rad(45), math.rad(45)) - local frontAngleDiff = (tractorToDollyAngle - dollyToTrailerAngle) - frontAngleDiff = MathUtil.clamp(frontAngleDiff, -math.rad(45), math.rad(45)) + local frontAngleDiff = (tractorToDollyAngle - dollyToTrailerAngle) + frontAngleDiff = MathUtil.clamp(frontAngleDiff, -math.rad(45), math.rad(45)) - angleDiff = (frontAngleDiff - rearAngleDiff) * - (1.5 - (self.reversingImplement.reversingProperties.nodeDistance * 0.4 - 0.9) + rotDelta) - angleDiff = MathUtil.clamp(angleDiff, -math.rad(45), math.rad(45)) + angleDiff = (frontAngleDiff - rearAngleDiff) * + (1.5 - (self.reversingImplement.reversingProperties.nodeDistance * 0.4 - 0.9) + rotDelta) + angleDiff = MathUtil.clamp(angleDiff, -math.rad(45), math.rad(45)) - lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) - else - local crossTrackError, orientationError, curvatureError, currentHitchAngle = self:calculateErrors(tractorNode, trailerNode) - angleDiff = self:calculateHitchCorrectionAngle(crossTrackError, orientationError, curvatureError, currentHitchAngle) - angleDiff = MathUtil.clamp(angleDiff, -maxTractorAngle, maxTractorAngle) + lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) + else + -- the trailer/implement is like a semi-trailer, has a rear axle only, the front of the implement + -- is supported by the tractor + local crossTrackError, orientationError, curvatureError, currentHitchAngle = self:calculateErrors(tractorNode, trailerNode) + angleDiff = self:calculateHitchCorrectionAngle(crossTrackError, orientationError, curvatureError, currentHitchAngle) + angleDiff = MathUtil.clamp(angleDiff, -maxTractorAngle, maxTractorAngle) - lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) - end + lx, lz = MathUtil.getDirectionFromYRotation(angleDiff) + end - self:showDirection(tractorNode, lx, lz, 0.7, 0, 1) - -- do a little bit of damping if using the articulated axis as lx tends to oscillate around 0 which results in the - -- speed adjustment kicking in and slowing down the vehicle. - if useArticulatedAxisRotationNode and math.abs(lx) < 0.04 then lx = 0 end - -- construct an artificial goal point to drive to - lx, lz = -lx * self.ppc:getLookaheadDistance(), -lz * self.ppc:getLookaheadDistance() - -- AIDriveStrategy wants a global position to drive to (which it later converts to local, but whatever...) - local gx, _, gz = localToWorld(self.vehicle:getAIDirectionNode(), lx, 0, lz) - return gx, gz, false, self.settings.reverseSpeed:getValue() + self:showDirection(tractorNode, lx, lz, 0.7, 0, 1) + -- do a little bit of damping if using the articulated axis as lx tends to oscillate around 0 which results in the + -- speed adjustment kicking in and slowing down the vehicle. + if useArticulatedAxisRotationNode and math.abs(lx) < 0.04 then + lx = 0 + end + -- construct an artificial goal point to drive to + lx, lz = -lx * self.ppc:getLookaheadDistance(), -lz * self.ppc:getLookaheadDistance() + -- AIDriveStrategy wants a global position to drive to (which it later converts to local, but whatever...) + local gx, _, gz = localToWorld(self.vehicle:getAIDirectionNode(), lx, 0, lz) + return gx, gz, false, self.settings.reverseSpeed:getValue() end function AIReverseDriver:getLocalYRotationToPoint(node, x, y, z, direction) @@ -273,4 +276,4 @@ function AIReverseDriver:calculateHitchCorrectionAngle(crossTrackError, orientat end return correctionAngle -end +end \ No newline at end of file From d9b40823ff4114a2cd4e148cb08c17078a099ec0 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 19 Sep 2023 19:44:48 +0200 Subject: [PATCH 101/107] Removed leftover debug --- scripts/specializations/CpShovelPositions.lua | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/specializations/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua index 82ef7e1ea..0ac058084 100644 --- a/scripts/specializations/CpShovelPositions.lua +++ b/scripts/specializations/CpShovelPositions.lua @@ -113,12 +113,7 @@ function CpShovelPositions:onPostAttach() end function CpShovelPositions:onDraw() - if CpShovelPositions.DEBUG and self:getRootVehicle() then - local angle, shovelNode, maxAngle, minAngle, factor = CpShovelPositions.getShovelData(self) - if shovelNode then - DebugUtil.drawDebugNode(shovelNode, "shovelNode") - end - end + end function CpShovelPositions:onUpdateTick(dt) From 1e682d87674ac3e4cb9507f040aeaeb429f00e8d Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Tue, 19 Sep 2023 19:58:20 +0200 Subject: [PATCH 102/107] Stops the driver if no path to the silo is found. --- scripts/ai/AIDriveStrategyShovelSiloLoader.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua index 3c117e4c8..1f28a76aa 100644 --- a/scripts/ai/AIDriveStrategyShovelSiloLoader.lua +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -526,10 +526,9 @@ function AIDriveStrategyShovelSiloLoader:onPathfindingDoneToStart(path) self:startCourse(alignmentCourse, 1) self:setNewState(self.states.DRIVING_ALIGNMENT_COURSE) else - local course = self:getRememberedCourseAndIx() - self:debug("No alignment path found, so driving directly to the course!") - self:startCourse(course, 1) - self:setNewState(self.states.DRIVING_INTO_SILO) + self:debug("No path to the silo found!") + self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) + --- TODO: Might need to consider a retry to another silo lane end end From ad8afd60b3b7e6e94696ccef4a5b873cc59cfc5e Mon Sep 17 00:00:00 2001 From: schwiti6190 Date: Tue, 19 Sep 2023 18:23:29 +0000 Subject: [PATCH 103/107] Updated translations --- translations/translation_hu.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index f1e87e787..13002836c 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -25,7 +25,7 @@ - + From d4656428b429d4bffe4ad0d76af26190d8dddbfd Mon Sep 17 00:00:00 2001 From: Tensuko Date: Wed, 20 Sep 2023 20:57:17 +0200 Subject: [PATCH 104/107] Push Release 7.3.1.0 - fix for #2768 - fix for #2777 - fix for #2761 and other plow improvements. - Improvement for reverse driving with offset tools (e.g. plows or potato harvester) - Plows default noReverse configuration was removed, User who want them to not drive in reverse turn off "turn on field" or can also turn on "use pathfinder in turns". - Added shovel mode back in. #2254 Please check the in-game help menu for how to set up the new mode. We can't support all kinds of shovel vehicles (like bobcats) as they were built inconsistently (like the tool arms are sometimes flipped, and so on). If it turns out to be a lot of non-working shovel vehicles, we'll consider implementing some kind of Tool Position Saver (like our separate mod) or find some another solution, until then, we would prefer keeping the automatic control. We were not able to test all different kinds of combinations. If you have a vehicle that does weird things, pls post a screenshot (with debug on) in our Issue: #2783 --- modDesc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modDesc.xml b/modDesc.xml index 56bea87ef..859e12116 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -1,6 +1,6 @@ - 7.3.0.3 + 7.3.1.0 <en>CoursePlay</en> From d6695e7aafe985371ffe9041963b66cad549a73b Mon Sep 17 00:00:00 2001 From: Roby1164 <casrob1964@gmail.com> Date: Wed, 20 Sep 2023 22:28:28 +0200 Subject: [PATCH 105/107] Update translation_it.xml --- translations/translation_it.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 6e23a5e16..f05e3034a 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -819,7 +819,7 @@ Per avviare l'indicatore di caricamento nel menu AI è necessario impostare. Il marcatore rileva cumuli o silos e li evidenzia sulla mappa. Ora è possibile avviare l'helper. "/> - <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoader_title" text="Autista di pale gommate"/> <text name="CP_help_page_shovelLoaderGeneral_text" text=" WIP "/> From 7533e757448d63d39958424c4807dc6cc32b8758 Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:21:47 +0200 Subject: [PATCH 106/107] Updated ReadMe what works text --- README.md | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a717a1158..056fe0ef0 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,32 @@ ## What Works * **Multiplayer support** -* Fieldwork and vine work with courses from the course generator. -* Course manager for saving/loading/renaming/moving and deleting of courses. -* Wrapping and collecting of bales with or without a course. -* Unloading of combines with unloaders. -* Creating heaps of sugar beets or other fruits on the field with the combine unloader. -* Unloading silo loaders, like the ``ROPA Maus`` and letting AD or Giants unload the trailer after that. -* Compacting of a bunker silo or pushing the chaff into the back of a silo. -* Let the combine unload into nearby trailers (combine self unload). -* Field work with up to 5 vehicles on the same course (multi tool course). -* Creating custom fields by recording a course or drawing on the AI Map. -* Course editor to edit fieldwork courses or custom field borders. +* Fieldwork mode: + * Course generator for complex fields with many option like headlands or beets with combines and so on .. + * Up to 5 workers with the same tools can work together on a field with the same course (multi tools) + * Generate courses for vine work + * Save/load/rename/move courses + * Load courses for baling, straw or grass collection and so on + * Combines can automatically unload into nearby trailers (combine self unload) +* Bale collector mode: + * Wrapping bales on a field without a course + * Collecting bales on the field without a course and unloading them with [AutoDrive](https://github.com/Stephan-S/FS22_AutoDrive) +* Combine unloader mode: + * Unload combines on the field + * Sending the giants helper or [AutoDrive](https://github.com/Stephan-S/FS22_AutoDrive) to unload at an unload station + * Creating heaps of sugar beets or other fruits on the field + * Unloading a loader vehicle, like the ``ROPA Maus`` and letting [AutoDrive](https://github.com/Stephan-S/FS22_AutoDrive) or Giants unload the trailer after that +* Silo load mode: + * Loading from a heap or bunker silo with loader, like the ``ROPA Maus`` + * Using a wheel loader or a front loader to load from a heap or a bunker silo and unload to: + * Unloading to nearby trailers + * Unloading to an unloading station, which needs to be selected on the AI menu +* Bunker silo mode: + * Compacting the silo with or without tools like this one [Silo distributor](https://www.farming-simulator.com/mod.php?lang=de&country=de&mod_id=242708&title=fs2022) + * Using a shield in a silo with a back wall to push the chaff to the back of silo +* Misc: + * Creating custom fields by recording the boarder with a vehicle or drawing on the AI Map. + * Course editor in the buy menu to edit courses or custom fields. * Mod support with [AutoDrive](https://github.com/Stephan-S/FS22_AutoDrive): * Sending the fieldwork driver to refill seeds/fertilizers and so on. * Sending the fieldworker/ bale collector to unload collected straw and so on. From ede02e169f5965621f6273d131f67801b060e897 Mon Sep 17 00:00:00 2001 From: Tensuko <theta-darkphoenix@gmx.net> Date: Thu, 21 Sep 2023 17:26:54 +0200 Subject: [PATCH 107/107] increse skip row for vine generator to 4 #2787 --- config/VineCourseGeneratorSettingsSetup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/VineCourseGeneratorSettingsSetup.xml b/config/VineCourseGeneratorSettingsSetup.xml index fe7f220c9..f191e4802 100644 --- a/config/VineCourseGeneratorSettingsSetup.xml +++ b/config/VineCourseGeneratorSettingsSetup.xml @@ -21,6 +21,6 @@ </Texts> </Setting> <Setting classType="AIParameterSettingList" name="vineMultiTools" min="1" max="5" default="1"/> - <Setting classType="AIParameterSettingList" name="vineRowsToSkip" min="0" max="2" default="0"/> + <Setting classType="AIParameterSettingList" name="vineRowsToSkip" min="0" max="4" default="0"/> </SettingSubTitle> </Settings>