diff --git a/Courseplay.lua b/Courseplay.lua index 737948e89..2dcb6efa0 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 @@ -192,14 +192,28 @@ end FSCareerMissionInfo.saveToXMLFile = Utils.prependedFunction(FSCareerMissionInfo.saveToXMLFile, Courseplay.saveToXMLFile) function Courseplay:update(dt) - g_devHelper:update() - g_bunkerSiloManager:update(dt) + g_devHelper:update() + g_bunkerSiloManager:update(dt) + g_triggerManager:update(dt) + if not self.postInit then + -- 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 + 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() if not g_gui:getIsGuiVisible() then g_vineScanner:draw() g_bunkerSiloManager:draw() + g_triggerManager:draw() end g_devHelper:draw() CpDebug:draw() @@ -323,27 +337,19 @@ end function Courseplay.register(typeManager) --- TODO: make this function async. for typeName, typeEntry in pairs(typeManager.types) do - 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) - 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/README.md b/README.md index 80fe575dd..fc112f5f8 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,32 @@ You can download [here](https://github.com/pops64/Courseplay_FS22/releases/lates * Chopper Support * **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. diff --git a/config/HelpMenu.xml b/config/HelpMenu.xml index 15eb062bd..0be47f0d8 100644 --- a/config/HelpMenu.xml +++ b/config/HelpMenu.xml @@ -222,5 +222,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index d03637cfc..a669275e5 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -79,6 +79,10 @@ + + + + @@ -302,9 +306,21 @@ - - - + + + + + + + + + + + + + + + @@ -601,6 +617,14 @@ + + + + + + + + @@ -2476,6 +2500,96 @@ The marker detects heaps or silos and highlights them on the map. Now it's possible to start the helper. ]]> + + + + + + + + + + + + + + + + + diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml index b58b8e625..d50e7001c 100644 --- a/config/VehicleConfigurations.xml +++ b/config/VehicleConfigurations.xml @@ -97,6 +97,13 @@ 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 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 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". @@ -106,8 +113,46 @@ You can define the following custom settings: Ignore bales detected by the forward-looking proximity controller if any implement has this set to true. This can be set for bale pushers to prevent the Courseplay driver stopping when a bale is blocking the way. +- fixWheelLoaderDirectionNodeByMovingToolIx + 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: 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. + --> + + toolOffsetX + noReverse + turnRadius + workingWidth + balerUnloadDistance + directionNodeOffsetZ + implementWheelAlwaysOnGround + ignoreCollisionBoxesWhenFolded + baleCollectorOffset + disableUnfolding + raiseLate + lowerEarly + useVehicleSizeForMarkers + armMovingToolIx + movingToolIx + shovelMovingToolIx + loadingShovelOffset + ignoreBaleCollisionForward + fixWheelLoaderDirectionNodeByMovingToolIx + articulatedAxisReverseNodeInverted + disablePipeMovingToolCorrection + unloadOffsetX + @@ -314,6 +359,13 @@ You can define the following custom settings: ignoreCollisionBoxesWhenFolded = "true" /> + + + @@ -327,6 +379,22 @@ You can define the following custom settings: + + + + + + + + + diff --git a/config/VehicleSettingsSetup.xml b/config/VehicleSettingsSetup.xml index aeb5cb720..adbfe7500 100644 --- a/config/VehicleSettingsSetup.xml +++ b/config/VehicleSettingsSetup.xml @@ -76,6 +76,9 @@ + 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 @@ - + diff --git a/config/gamePadHud/SiloLoaderGamePadHudPage.xml b/config/gamePadHud/SiloLoaderGamePadHudPage.xml index c406daddd..34fc6f86f 100644 --- a/config/gamePadHud/SiloLoaderGamePadHudPage.xml +++ b/config/gamePadHud/SiloLoaderGamePadHudPage.xml @@ -5,5 +5,7 @@ --> + + diff --git a/config/jobParameters/CombineUnloaderJobParameterSetup.xml b/config/jobParameters/CombineUnloaderJobParameterSetup.xml index 343ee708f..1f60669dd 100644 --- a/config/jobParameters/CombineUnloaderJobParameterSetup.xml +++ b/config/jobParameters/CombineUnloaderJobParameterSetup.xml @@ -29,7 +29,7 @@ - + diff --git a/config/jobParameters/JobParameterSetup.xml b/config/jobParameters/JobParameterSetup.xml index fa003f020..ef081c84c 100644 --- a/config/jobParameters/JobParameterSetup.xml +++ b/config/jobParameters/JobParameterSetup.xml @@ -15,16 +15,18 @@ - 1 - 2 - 3 + 1 + 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/img/helpmenu/shovelloadertrigger.dds b/img/helpmenu/shovelloadertrigger.dds new file mode 100644 index 000000000..b0e406bec Binary files /dev/null and b/img/helpmenu/shovelloadertrigger.dds differ diff --git a/modDesc.xml b/modDesc.xml index cebd38979..d58679340 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -2,6 +2,7 @@ 7.3.0.6 Pops64 + 7.3.1.1 <en>CoursePlay - Chopper Support</en> <cs>自动作业 - Chopper Support</cs> @@ -222,11 +223,24 @@ Changelog 7.1.0.0: <extraSourceFiles> <sourceFile filename="scripts/CpObject.lua"/> <sourceFile filename="Courseplay.lua"/> + <sourceFile filename="scripts/CpUtil.lua"/> + <sourceFile filename="scripts/debug/CpDebug.lua"/> <sourceFile filename="scripts/debug/CourseRecorder.lua"/> <sourceFile filename="scripts/dev/ConsoleCommands.lua"/> <sourceFile filename="scripts/dev/DevHelper.lua"/> + <sourceFile filename="scripts/config/VehicleConfigurations.lua"/> + + <sourceFile filename="scripts/util/CpMathUtil.lua"/> + <sourceFile filename="scripts/util/MovingAverage.lua"/> + <sourceFile filename="scripts/util/CpRemainingTime.lua"/> + + <sourceFile filename="scripts/CpSettingsUtil.lua"/> + <sourceFile filename="scripts/Waypoint.lua"/> + <sourceFile filename="scripts/Course.lua"/> + <sourceFile filename="scripts/CpGlobalSettings.lua"/> + <sourceFile filename="scripts/courseManager/FileSystem.lua"/> <sourceFile filename="scripts/courseManager/AssignedCoursesManager.lua"/> <sourceFile filename="scripts/courseGenerator/CourseGenerator.lua"/> @@ -238,19 +252,20 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/courseGenerator/track.lua"/> <sourceFile filename="scripts/courseGenerator/geo.lua"/> <sourceFile filename="scripts/courseGenerator/Vector.lua"/> - <sourceFile filename="scripts/Waypoint.lua"/> - <sourceFile filename="scripts/Course.lua"/> - <sourceFile filename="scripts/util/CpMathUtil.lua"/> - <sourceFile filename="scripts/util/MovingAverage.lua"/> - <sourceFile filename="scripts/util/CpRemainingTime.lua"/> + <sourceFile filename="scripts/field/CpFieldUtil.lua"/> <sourceFile filename="scripts/field/CustomField.lua"/> <sourceFile filename="scripts/field/CustomFieldManager.lua"/> <sourceFile filename="scripts/field/FieldScanner.lua"/> <sourceFile filename="scripts/field/VineScanner.lua"/> + <sourceFile filename="scripts/silo/BunkerSiloVehicleController.lua"/> <sourceFile filename="scripts/silo/BunkerSiloWrapper.lua"/> <sourceFile filename="scripts/silo/BunkerSiloManager.lua"/> + + <sourceFile filename="scripts/trigger/TriggerWrapper.lua"/> + <sourceFile filename="scripts/trigger/TriggerManager.lua"/> + <sourceFile filename="scripts/pathfinder/BinaryHeap.lua"/> <sourceFile filename="scripts/pathfinder/State3D.lua"/> <sourceFile filename="scripts/pathfinder/AnalyticSolution.lua"/> @@ -259,11 +274,24 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/pathfinder/ReedsSheppSolver.lua"/> <sourceFile filename="scripts/pathfinder/HybridAStar.lua"/> <sourceFile filename="scripts/pathfinder/PathfinderUtil.lua"/> + <sourceFile filename="scripts/ai/AIUtil.lua"/> <sourceFile filename="scripts/ai/ImplementUtil.lua"/> <sourceFile filename="scripts/ai/ProximityController.lua"/> <sourceFile filename="scripts/ai/FieldWorkerProximityController.lua"/> <sourceFile filename="scripts/ai/CollisionAvoidanceController.lua"/> + <sourceFile filename="scripts/ai/AIMessages.lua"/> + <sourceFile filename="scripts/ai/Markers.lua"/> + <sourceFile filename="scripts/ai/ProximitySensor.lua"/> + <sourceFile filename="scripts/ai/BaleToCollect.lua"/> + <sourceFile filename="scripts/ai/FillLevelManager.lua"/> + <sourceFile filename="scripts/ai/InfoTextsManager.lua"/> + <sourceFile filename="scripts/ai/PurePursuitController.lua"/> + <sourceFile filename="scripts/ai/SelfUnloadHelper.lua"/> + <sourceFile filename="scripts/ai/VehicleScanner.lua"/> + <sourceFile filename="scripts/ai/WorkWidthUtil.lua"/> + <sourceFile filename="scripts/ai/AIReverseDriver.lua"/> + <sourceFile filename="scripts/ai/turns/AITurn.lua"/> <sourceFile filename="scripts/ai/turns/Corner.lua"/> <sourceFile filename="scripts/ai/turns/TurnContext.lua"/> @@ -321,19 +349,27 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/ai/controllers/StumpCutterController.lua"/> <sourceFile filename="scripts/ai/controllers/FoldableController.lua"/> <sourceFile filename="scripts/ai/controllers/PlowController.lua"/> - <sourceFile filename="scripts/ai/BaleToCollect.lua"/> - <sourceFile filename="scripts/ai/FillLevelManager.lua"/> - <sourceFile filename="scripts/ai/InfoTextsManager.lua"/> - <sourceFile filename="scripts/ai/PurePursuitController.lua"/> - <sourceFile filename="scripts/ai/SelfUnloadHelper.lua"/> - <sourceFile filename="scripts/ai/VehicleScanner.lua"/> - <sourceFile filename="scripts/ai/WorkWidthUtil.lua"/> + + <sourceFile filename="scripts/ai/AIDriveStrategyCourse.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyDriveToFieldWorkStart.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyAttachHeader.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyFieldWorkCourse.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyPlowCourse.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyCombineCourse.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyFindBales.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyUnloadCombine.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyVineFieldWorkCourse.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyBunkerSilo.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategySiloLoader.lua"/> + <sourceFile filename="scripts/ai/AIDriveStrategyShovelSiloLoader.lua"/> + <sourceFile filename="scripts/ai/parameters/AIParameterSettingInterface.lua"/> <sourceFile filename="scripts/ai/parameters/AIParameterSetting.lua"/> <sourceFile filename="scripts/ai/parameters/AIParameterSettingList.lua"/> <sourceFile filename="scripts/ai/parameters/AIParameterBooleanSetting.lua"/> <sourceFile filename="scripts/ai/parameters/CpAIParameterUnloadingStation.lua"/> <sourceFile filename="scripts/ai/parameters/CpAIParameterPositionAngle.lua"/> + <sourceFile filename="scripts/ai/jobs/CpJobParameters.lua"/> <sourceFile filename="scripts/ai/jobs/CpAIJob.lua"/> <sourceFile filename="scripts/ai/jobs/CpAIJobFieldWork.lua"/> @@ -341,6 +377,7 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/ai/jobs/CpAIJobCombineUnloader.lua"/> <sourceFile filename="scripts/ai/jobs/CpAIJobBunkerSilo.lua"/> <sourceFile filename="scripts/ai/jobs/CpAIJobSiloLoader.lua"/> + <sourceFile filename="scripts/ai/tasks/CpAITaskFieldWork.lua"/> <sourceFile filename="scripts/ai/tasks/CpAITaskBaleFinder.lua"/> <sourceFile filename="scripts/ai/tasks/CpAITaskCombineUnloader.lua"/> @@ -348,14 +385,13 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/ai/tasks/CpAITaskAttachHeader.lua"/> <sourceFile filename="scripts/ai/tasks/CpAITaskBunkerSilo.lua"/> <sourceFile filename="scripts/ai/tasks/CpAITaskSiloLoader.lua"/> - <sourceFile filename="scripts/CpSettingsUtil.lua"/> - <sourceFile filename="scripts/CpGlobalSettings.lua"/> - <sourceFile filename="scripts/config/VehicleConfigurations.lua"/> + <sourceFile filename="scripts/gui/CpGuiUtil.lua"/> <sourceFile filename="scripts/gui/CoursePlot.lua"/> <sourceFile filename="scripts/gui/FieldPlot.lua"/> <sourceFile filename="scripts/gui/BunkerSiloPlot.lua"/> <sourceFile filename="scripts/gui/HeapPlot.lua"/> + <sourceFile filename="scripts/gui/UnloadingTriggerPlot.lua"/> <sourceFile filename="scripts/gui/CpStatus.lua"/> <sourceFile filename="scripts/gui/CpAIHotspots.lua"/> <sourceFile filename="scripts/gui/CpAIFrameExtended.lua"/> @@ -364,6 +400,7 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/gui/CpCourseManagerFrame.lua"/> <sourceFile filename="scripts/gui/CourseDisplay.lua"/> <sourceFile filename="scripts/gui/CpGamePadHudScreen.lua"/> + <sourceFile filename="scripts/gui/hud/HudElements.lua"/> <sourceFile filename="scripts/gui/hud/CpBaseHud.lua"/> <sourceFile filename="scripts/gui/hud/CpHudInfoTexts.lua"/> @@ -372,6 +409,7 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/gui/hud/CpCombineUnloaderHudPage.lua"/> <sourceFile filename="scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua"/> <sourceFile filename="scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua"/> + <sourceFile filename="scripts/editor/brushes/BaseBrush.lua"/> <sourceFile filename="scripts/editor/brushes/basic/InsertWaypoint.lua"/> <sourceFile filename="scripts/editor/brushes/basic/MoveWaypoint.lua"/> @@ -384,6 +422,7 @@ Changelog 7.1.0.0: <sourceFile filename="scripts/editor/brushes/advanced/CurveWaypoints.lua"/> <sourceFile filename="scripts/editor/EditorCourseWrapper.lua"/> <sourceFile filename="scripts/editor/CourseEditor.lua"/> + <sourceFile filename="scripts/events/CoursesEvent.lua"/> <sourceFile filename="scripts/events/CpJoinEvent.lua"/> <sourceFile filename="scripts/events/GlobalSettingsEvent.lua"/> @@ -395,7 +434,6 @@ Changelog 7.1.0.0: </extraSourceFiles> <specializations> - <specialization name="courseplaySpec" className="CourseplaySpec" filename="scripts/specializations/CourseplaySpec.lua" /> <specialization name="cpVehicleSettings" className="CpVehicleSettings" filename="scripts/specializations/CpVehicleSettings.lua" /> <specialization name="cpCourseGeneratorSettings" className="CpCourseGeneratorSettings" filename="scripts/specializations/CpCourseGeneratorSettings.lua" /> <specialization name="cpCourseManager" className="CpCourseManager" filename="scripts/specializations/CpCourseManager.lua"/> @@ -408,6 +446,7 @@ Changelog 7.1.0.0: <specialization name="cpGamePadHud" className="CpGamePadHud" filename="scripts/specializations/CpGamePadHud.lua"/> <specialization name="cpHud" className="CpHud" filename="scripts/specializations/CpHud.lua"/> <specialization name="cpInfoTexts" className="CpInfoTexts" filename="scripts/specializations/CpInfoTexts.lua"/> + <specialization name="cpShovelPositions" className="CpShovelPositions" filename="scripts/specializations/CpShovelPositions.lua"/> </specializations> <inputBinding> diff --git a/scripts/Course.lua b/scripts/Course.lua index be962b0f2..7eacc1031 100644 --- a/scripts/Course.lua +++ b/scripts/Course.lua @@ -914,6 +914,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/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/ai/AIDriveStrategyCourse.lua b/scripts/ai/AIDriveStrategyCourse.lua index 283a110fd..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() @@ -602,13 +601,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/ai/AIDriveStrategyShovelSiloLoader.lua b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua new file mode 100644 index 000000000..1f28a76aa --- /dev/null +++ b/scripts/ai/AIDriveStrategyShovelSiloLoader.lua @@ -0,0 +1,704 @@ +--[[ +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 +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 <http://www.gnu.org/licenses/>. +]] + +--[[ + +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 = {} +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}, + 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}, + UNLOADING = {shovelPosition = ShovelController.POSITIONS.UNLOADING, shovelMovingSpeed = 0}, + REVERSING_AWAY_FROM_UNLOAD = {shovelPosition = ShovelController.POSITIONS.PRE_UNLOADING, shovelMovingSpeed = 0}, +} + +AIDriveStrategyShovelSiloLoader.maxValidTrailerDistanceToSiloFront = 30 +AIDriveStrategyShovelSiloLoader.searchForTrailerDelaySec = 10 +AIDriveStrategyShovelSiloLoader.distShovelTrailerPreUnload = 7 +AIDriveStrategyShovelSiloLoader.distShovelUnloadStationPreUnload = 8 +AIDriveStrategyShovelSiloLoader.isStuckMs = 1000 * 15 +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 + self.debugChannel = CpDebug.DBG_SILO + 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.unloadPositionNode) + CpUtil.destroyNode(self.siloFrontNode) + self.isStuckTimer:delete() +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) + self.course = Course.createStraightForwardCourse(self.vehicle, 25) + self:startCourse(self.course, 1) + + 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.") + --- 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(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, 1.5 * AIUtil.getLength(self.vehicle))) + 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.") + self.silo = self.bunkerSilo + else + 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() + local yRot = MathUtil.getYRotationFromDirection(dirX, dirZ) + self.siloFrontNode = CpUtil.createNode("siloFrontNode", cx, cz, yRot) + 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) + + self.vehicle:raiseAIEvent("onAIFieldWorkerStart", "onAIImplementStart") +end + +----------------------------------------------------------------------------------------------------------------------- +--- Implement handling +----------------------------------------------------------------------------------------------------------------------- +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 + +--- 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) + 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 + 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 + +----------------------------------------------------------------------------------------------------------------------- +--- 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: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 + if self.isUnloadingAtTrailerActive then + self:setNewState(self.states.WAITING_FOR_TRAILER) + 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 + self:approachUnloadStationForUnloading() + elseif self.state == self.states.DRIVING_TO_UNLOAD then + self:setNewState(self.states.UNLOADING) + elseif self.state == self.states.REVERSING_AWAY_FROM_UNLOAD then + if self.shovelController:isEmpty() then + self:startDrivingToSilo() + else + self:setNewState(self.states.WAITING_FOR_TRAILER) + end + 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 + if self.isUnloadingAtTrailerActive then + self:setNewState(self.states.WAITING_FOR_TRAILER) + else + self:startPathfindingToUnloadPosition() + end + 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()) + 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() + 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_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 + 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 + self:searchForTrailerToUnloadInto() + self.lastTrailerSearch = g_time + 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:getFillUnitExactFillRootNode(1) + end + if self.shovelController:isShovelOverTrailer(refNode) then + self:setNewState(self.states.UNLOADING) + self:setMaxSpeed(0) + end + if not self.isUnloadingAtTrailerActive 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 + 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 + if self.state.properties.shovelMovingSpeed ~= nil then + self:setMaxSpeed(self.state.properties.shovelMovingSpeed) + end + end + end + self:limitSpeed() + self:checkProximitySensors(moveForwards) + return gx, gz, moveForwards, self.maxSpeed, 100 +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 + self.ppc:getCourse():draw() + end + if self.silo then + self.silo:drawDebug() + self.siloAreaToAvoid:drawDebug() + end + self.siloController:draw() + if self.heapSilo then + CpUtil.drawDebugNode(self.heapNode, false, 3) + end + if self.targetTrailer then + CpUtil.drawDebugNode(self.targetTrailer.exactFillRootNode, false, 3, "ExactFillRootNode") + end + CpUtil.drawDebugNode(self.unloadPositionNode, false, 3) + end + self:updateImplementControllers(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 + return true + end + --- This ignores the terrain. + if object == nil then + return true + end + if object == self.unloadStation then + return true + end + if self.unloadTrigger and 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() + -- a bit less as size.width always has plenty of buffer + 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!") + self:setInfoText(InfoTextManager.WAITING_FOR_UNLOADER) + 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) + 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 + 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 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) + self:rememberCourse(course, 1) + local done, path + local fm = self:getFrontAndBackMarkers() + self.pathfinder, done, path = PathfinderUtil.startPathfindingFromVehicleToWaypoint( + self.vehicle, course, 1, 0, -(fm + 4), + true, nil, nil, + nil, 0, self.siloAreaToAvoid) + 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 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 + 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 + +--- 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) + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( + self.vehicle, self.unloadPositionNode, + 0, 0, true, + nil, {}, nil, + 0, self.siloAreaToAvoid, false + ) + 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) + course:adjustForTowedImplements(2) + self:startCourse(course, 1) + self:setNewState(self.states.DRIVING_TO_UNLOAD_POSITION) + else + self:debug("Failed to drive close to unload position.") + 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) + local done, path, goalNodeInvalid + self.pathfinder, done, path, goalNodeInvalid = PathfinderUtil.startPathfindingFromVehicleToNode( + self.vehicle, self.unloadPositionNode, + 0, 0, true, + nil, {}, nil, + 0, self.siloAreaToAvoid, 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 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) + else + self:debug("Failed to find path to trailer!") + ---self:setNewState(self.states.WAITING_FOR_TRAILER) + --- Later on we might try another approach? + self.vehicle:stopCurrentAIJob(AIMessageCpErrorNoPathFound.new()) + end +end +---------------------------------------------------------------- +--- Silo work +---------------------------------------------------------------- + +--- Starts driving into the silo lane +function AIDriveStrategyShovelSiloLoader:startDrivingToSilo(target) + --- Creates a straight course in the silo. + 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, + 0, 0, 3, 3, false) + local distance = siloCourse:getDistanceBetweenVehicleAndWaypoint(self.vehicle, 1) + if distance > self.turningRadius then + self:debug("Start driving to silo with pathfinder.") + self:startPathfindingToStart(siloCourse) + else + 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) + 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: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()) + 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) + self.shovelController:calculateMinimalUnloadingHeight(self.targetTrailer.exactFillRootNode) +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:getFillUnitExactFillRootNode()) + local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) + local course = Course.createFromTwoWorldPositions(self.vehicle, x, z, dx, dz, + 0, -3, 3, 2, false) + local firstWpIx = course:getNearestWaypoints(self.vehicle:getAIDirectionNode()) + self:startCourse(course, firstWpIx) + self:setNewState(self.states.DRIVING_TO_UNLOAD) + self.shovelController:calculateMinimalUnloadingHeight(self.unloadTrigger:getFillUnitExactFillRootNode()) +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.") + 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)) + self.targetTrailer = nil + return true + end + else + 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, self.vehicle:getAIDirectionNode() ) + self:startCourse(course, 1) + self:setNewState(self.states.REVERSING_AWAY_FROM_UNLOAD) +end diff --git a/scripts/ai/AIReverseDriver.lua b/scripts/ai/AIReverseDriver.lua index a8c4eb3da..249767095 100644 --- a/scripts/ai/AIReverseDriver.lua +++ b/scripts/ai/AIReverseDriver.lua @@ -276,4 +276,4 @@ function AIReverseDriver:calculateHitchCorrectionAngle(crossTrackError, orientat end return correctionAngle -end +end \ No newline at end of file diff --git a/scripts/ai/ImplementUtil.lua b/scripts/ai/ImplementUtil.lua index 8536cd752..82defb206 100644 --- a/scripts/ai/ImplementUtil.lua +++ b/scripts/ai/ImplementUtil.lua @@ -366,18 +366,18 @@ 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) 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 if math.abs(diff) < 0.03 or rotSpeed == 0 then ImplementUtil.stopMovingTool(implement, tool) return false @@ -404,9 +404,16 @@ function ImplementUtil.stopMovingTool(implement, tool) end 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] + return object.spec_leveler and object.spec_leveler.nodes and object.spec_leveler.nodes[1] +end + +function ImplementUtil.getShovelNode(object) + return object.spec_shovel and object.spec_shovel.shovelNodes and object.spec_shovel.shovelNodes[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 +424,88 @@ 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 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 +---@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, ...) + if debugFunc then + debugFunc(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) + 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 + 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) 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) 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 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 + 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 + validTarget, targetFillUnitIndex, exactFillRootNode = canLoad(fillUnitIndex) + if validTarget then + break + end + end + + return validTarget, targetFillUnitIndex, fillType, exactFillRootNode, alternativeFillType +end + diff --git a/scripts/ai/Markers.lua b/scripts/ai/Markers.lua index c02916442..a266c2a07 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 = {} @@ -36,16 +43,16 @@ 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 @@ -123,3 +130,31 @@ function Markers.getMarkerNodes(vehicle) local backMarker = Markers.getBackMarkerNode(vehicle) return frontMarker, backMarker, g_vehicleMarkers[vehicle].frontMarkerOffset, g_vehicleMarkers[vehicle].backMarkerOffset 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/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 diff --git a/scripts/ai/controllers/PipeController.lua b/scripts/ai/controllers/PipeController.lua index 46ce63346..0f5e8f512 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 function PipeController:getPipesBaseNode() @@ -431,6 +435,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. @@ -465,9 +476,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 diff --git a/scripts/ai/controllers/ShovelController.lua b/scripts/ai/controllers/ShovelController.lua index 7a840dd6e..22d4ca547 100644 --- a/scripts/ai/controllers/ShovelController.lua +++ b/scripts/ai/controllers/ShovelController.lua @@ -1,14 +1,94 @@ ---@class ShovelController : ImplementController ShovelController = CpObject(ImplementController) -function ShovelController:init(vehicle, implement) +ShovelController.POSITIONS = { + DEACTIVATED = 0, + LOADING = 1, + TRANSPORT = 2, + PRE_UNLOADING = 3, + UNLOADING = 4, +} +ShovelController.MAX_TRIGGER_HEIGHT = 8 +ShovelController.MIN_TRIGGER_HEIGHT = 1 +ShovelController.TRIGGER_HEIGHT_RAYCAST_COLLISION_MASK = CollisionFlag.STATIC_WORLD + CollisionFlag.STATIC_OBJECTS + + CollisionFlag.STATIC_OBJECT + CollisionFlag.VEHICLE + +function ShovelController:init(vehicle, implement, isConsoleCommand) ImplementController.init(self, vehicle, implement) self.shovelSpec = self.implement.spec_shovel - self.shovelNode = self.shovelSpec.shovelNodes[1] + 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, + 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 + --- Sugar cane trailer discharge + 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) + if self.isSugarCaneTrailer then + --- Sugar cane trailer discharge + 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() @@ -16,11 +96,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 +120,249 @@ function ShovelController:getShovelFillType() return self.shovelSpec.loadingFillType end -function ShovelController:isReadyToLoad() - return self:getShovelFillType() == FillType.UNKNOWN and self:getFillLevelPercentage() < 0.5 +function ShovelController:getDischargeFillType() + return self.implement:getDischargeFillType(self:getDischargeNode()) +end + +function ShovelController:getDischargeNode() + return self.implement:getCurrentDischargeNode() +end + +--- Checks if the shovel raycast has found an unload target. +---@param targetTrigger CpTrigger|nil +---@return boolean +function ShovelController:canDischarge(targetTrigger) + 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 + if targetTrigger and targetTrigger:getTrigger() ~= self.implement:getDischargeTargetObject(dischargeNode) then + return false + end + return dischargeNode.dischargeHit +end + +--- Is the shovel node over the trailer? +---@param refNode number +---@param margin number|nil +---@return boolean +function ShovelController:isShovelOverTrailer(refNode, margin) + local node = self:getShovelNode() + 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 + +--- Calculates the minimal unloading height for the trigger. +---@param triggerNode number|nil +---@return boolean +function ShovelController:calculateMinimalUnloadingHeight(triggerNode) + local sx, sy, sz = getWorldTranslation(self.vehicle:getAIDirectionNode()) + 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, _ = 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 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 +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] + if object then + if object ~= self.vehicle and object ~= self.implement then + self:debug("Object: %s was hit!", CpUtil.getName(object)) + self.objectWasHit = true + return true + end + else + self:debug("Shape was hit!") + self.objectWasHit = true + return true + end + return false + end + return false +end + +function ShovelController:delete() + if self.implement.cpResetShovelState then + self.implement:cpResetShovelState() + 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) + 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 + 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 + self.implement:setIsTurnedOn(false) + self:debug("turning off the shovel.") + end + end + end + if self.implement.cpSetShovelState == nil then + return false + end + self.implement:cpSetShovelState(pos) + return self.implement:areCpShovelPositionsDirty() +end + +-------------------------------------------- +--- WIP! 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 + self:debug("Fill unit index: %d, max pickup angle: %.2f, width: %.2f", + self.shovelNode.fillUnitIndex, + math.deg(self.shovelNode.maxPickupAngle), + self.shovelNode.width) + if self.shovelNode.movingToolActivation then + self:debug("Has moving tool activation => 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/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/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..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 = { @@ -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) @@ -46,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. @@ -68,8 +69,15 @@ function CpAIJobSiloLoader:applyCurrentState(vehicle, mission, farmId, isDirectS self.cpJobParameters.loadPosition:setPosition(x, z) self.cpJobParameters.loadPosition:setAngle(angle) end -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 function CpAIJobSiloLoader:setValues() CpAIJob.setValues(self) @@ -95,7 +103,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 @@ -120,7 +131,33 @@ function CpAIJobSiloLoader:validate(farmId) else return false, g_i18n:getText("CP_error_no_heap_found") end - + if not AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) then + if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then + --- Validates the unload 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 + 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 + 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 + end return isValid, errorMessage end @@ -147,7 +184,80 @@ function CpAIJobSiloLoader:getBunkerSiloOrHeap(loadPosition, node) return found, nil, heapSilo end +--- Gets the unload trigger at the unload position. +---@param unloadPosition CpAIParameterPositionAngle +---@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() + if x == nil or dirX == nil then + return false + end + 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 + --- 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 + 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 +end + function CpAIJobSiloLoader:drawSilos(map) self.heapPlot:draw(map) g_bunkerSiloManager:drawSilos(map, self.bunkerSilo) + if self.cpJobParameters.unloadAt:getValue() == CpSiloLoaderJobParameters.UNLOAD_TRIGGER then + local fillTypes = self:getConvertedFillTypes() + local silo = self.heap or self.bunkerSilo + if silo then + table.insert(fillTypes, silo:getFillType()) + end + g_triggerManager:drawDischargeableTriggers(map, self.unloadTrigger, fillTypes) + end +end + + +--- 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 +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/ai/jobs/CpJobParameters.lua b/scripts/ai/jobs/CpJobParameters.lua index 5c06ff3c7..b5987bfaf 100644 --- a/scripts/ai/jobs/CpJobParameters.lua +++ b/scripts/ai/jobs/CpJobParameters.lua @@ -127,6 +127,28 @@ 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 + +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. @@ -137,7 +159,6 @@ function CpJobParameters:raiseCallback(callbackStr, setting, ...) end end - function CpJobParameters:__tostring() return tostring(self.settings) end @@ -205,8 +226,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() @@ -218,7 +239,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 @@ -311,6 +331,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 +349,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:getValue() == 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..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 @@ -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/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/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/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)) diff --git a/scripts/config/VehicleConfigurations.lua b/scripts/config/VehicleConfigurations.lua index fb8a8509b..cef9e48fe 100644 --- a/scripts/config/VehicleConfigurations.lua +++ b/scripts/config/VehicleConfigurations.lua @@ -27,32 +27,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. --- ---@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, - ignoreBaleCollisionForward = XMLValueType.BOOL, -} - function VehicleConfigurations:init() self.vehicleConfigurations = {} self.modVehicleConfigurations = {} + self.attributes = {} if g_currentMission then self:loadFromXml() end @@ -63,17 +46,18 @@ 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.vehicleConfigurations = {} + self.modVehicleConfigurations = {} + self.attributes = {} 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 @@ -112,19 +96,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. @@ -132,20 +136,23 @@ 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 + 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 @@ -221,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() @@ -253,7 +266,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 @@ -272,16 +288,40 @@ 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 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", attribute, XMLValueType.TYPES[xmlValueType].name) + CpUtil.info("Attribute: %s => %s", + attribute, XMLValueType.TYPES[xmlValueType].name) end end diff --git a/scripts/dev/DevHelper.lua b/scripts/dev/DevHelper.lua index 00c8fcc7a..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) @@ -262,10 +273,22 @@ function DevHelper:showAIMarkers() CpUtil.drawDebugNode(frontMarker, false, 3) CpUtil.drawDebugNode(backMarker, false, 3) - if self.vehicle:getAIDirectionNode() then - CpUtil.drawDebugNode(self.vehicle:getAIDirectionNode(), false , 3, "AiDirectionNode") + local directionNode = self.vehicle:getAIDirectionNode() + if directionNode then + CpUtil.drawDebugNode(self.vehicle:getAIDirectionNode(), false , 4, "AiDirectionNode") end - + local reverseNode = self.vehicle:getAIReverserNode() + if reverseNode then + CpUtil.drawDebugNode(reverseNode, false , 4.5, "AiReverseNode") + end + local steeringNode = self.vehicle:getAISteeringNode() + if steeringNode then + CpUtil.drawDebugNode(steeringNode, false , 5, "AiSteeringNode") + end + local articulatedAxisReverseNode = AIUtil.getArticulatedAxisVehicleReverserNode(self.vehicle) + if articulatedAxisReverseNode then + CpUtil.drawDebugNode(articulatedAxisReverseNode, false , 5.5, "AiArticulatedAxisReverseNode") + end end function DevHelper:togglePpcControlledNode() 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/gui/CoursePlot.lua b/scripts/gui/CoursePlot.lua index 8b9edded8..6069f97f0 100644 --- a/scripts/gui/CoursePlot.lua +++ b/scripts/gui/CoursePlot.lua @@ -79,45 +79,6 @@ 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 - -- 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) @@ -129,14 +90,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 +137,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 +149,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 99f2d296d..c3d48e1e1 100644 --- a/scripts/gui/CpAIFrameExtended.lua +++ b/scripts/gui/CpAIFrameExtended.lua @@ -14,19 +14,33 @@ 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, --- 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 +} + +--- Hotspots visible, while picking a loading position. +CpInGameMenuAIFrameExtended.validPickingLoadingPositionHotspots = { + [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() @@ -101,7 +115,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,16 +125,26 @@ 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) - --- Adds a second map hotspot for field position. + self.ingameMapBase.drawHotspotsOnly = Utils.appendedFunction( + self.ingameMapBase.drawHotspotsOnly , CpInGameMenuAIFrameExtended.draw) + + --- 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) @@ -131,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) @@ -195,6 +220,18 @@ end InGameMenuAIFrame.updateContextInputBarVisibility = Utils.appendedFunction(InGameMenuAIFrame.updateContextInputBarVisibility,CpInGameMenuAIFrameExtended.updateContextInputBarVisibility) +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) + 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 @@ -254,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 @@ -319,59 +357,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.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) @@ -399,8 +442,10 @@ 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) CpInGameMenuAIFrameExtended.unbindCourseGeneratorSettings(self) g_currentMission.inGameMenu:updatePages() end @@ -462,72 +507,94 @@ 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 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) +InGameMenuAIFrame.onClickPositionRotationParameter = Utils.overwrittenFunction( + InGameMenuAIFrame.onClickPositionRotationParameter, CpInGameMenuAIFrameExtended.onClickPositionParameter) + + --- 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, ...) 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 + 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 end end callback = Utils.appendedFunction(callback,function (finished, x, z) CpInGameMenuAIFrameExtended.resetHotspots(self) + self:updateParameterValueTexts() end) - + g_currentMission:removeMapHotspot(self.aiTargetMapHotspot) 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 - 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 + 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. function CpInGameMenuAIFrameExtended:updateParameterValueTexts(superFunc, ...) if self.currentJobElements == nil then 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) + g_currentMission:removeMapHotspot(self.loadAiTargetMapHotspot) for _, element in ipairs(self.currentJobElements) do local parameter = element.aiParameter local parameterType = parameter:getType() @@ -539,8 +606,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 @@ -550,72 +617,93 @@ 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()) - 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 - 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() + 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() + 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) + +-------------------------------------------- +--- Custom fields +-------------------------------------------- --- Enables clickable field hotspots. function CpInGameMenuAIFrameExtended:onClickHotspot(element,hotspot) @@ -657,24 +745,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/CpGamePadHudScreen.lua b/scripts/gui/CpGamePadHudScreen.lua index 276724dfe..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 @@ -260,7 +263,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/CpGuiUtil.lua b/scripts/gui/CpGuiUtil.lua index 4a3fd484b..489cadcd7 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,83 @@ 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 +------------------------------------------------ + +--- 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 ------------------------------------------------ @@ -426,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..ce261d9a8 100644 --- a/scripts/gui/CpStatus.lua +++ b/scripts/gui/CpStatus.lua @@ -132,11 +132,9 @@ 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.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/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..18e69421d --- /dev/null +++ b/scripts/gui/UnloadingTriggerPlot.lua @@ -0,0 +1,53 @@ +--- Draws an unloading triggers as a X Symbol on the in game menu 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 -- 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(213, 255, 0)} + -- a darker shade of the same color + self.darkColor = {CpGuiUtil.getNormalizedRgb(19, 87, 107)} + self.lineThickness = 4 / g_screenHeight -- 4 pixels + self.isHighlighted = false + local _ + self.x, _, self.z = getWorldTranslation(node) +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 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/gui/hud/CpBaseHud.lua b/scripts/gui/hud/CpBaseHud.lua index 9e3df27a4..5c1e2284b 100644 --- a/scripts/gui/hud/CpBaseHud.lua +++ b/scripts/gui/hud/CpBaseHud.lua @@ -424,11 +424,13 @@ 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) then - return self.bunkerSiloWorkerLayout - elseif vehicle:getCanStartCpSiloLoaderWorker() 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 + return self.bunkerSiloWorkerLayout else return self.fieldworkLayout end diff --git a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua index b61ee59c4..60e60011a 100644 --- a/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua +++ b/scripts/gui/hud/CpBunkerSiloWorkerHudPage.lua @@ -101,5 +101,13 @@ 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) + 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 d6af9cdbd..4d5068109 100644 --- a/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua +++ b/scripts/gui/hud/CpSiloLoaderWorkerHudPage.lua @@ -35,14 +35,17 @@ 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) + --- 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,11 @@ 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.loadingShovelHeightOffsetBtn:setDisabled(loadingHeightOffset:getIsDisabled()) + self.fillLevelProgressText:setTextDetails(status:getSiloFillLevelPercentageLeftOver()) --- Update copy and paste buttons @@ -145,4 +153,15 @@ function CpSiloLoaderWorkerHudPageElement:arePositionEqual(parameters, otherPara return false end return true +end + +function CpSiloLoaderWorkerHudPageElement:isStartingPointBtnDisabled(vehicle) + return AIUtil.hasChildVehicleWithSpecialization(vehicle, ConveyorBelt) or vehicle:getIsCpActive() +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/pathfinder/PathfinderUtil.lua b/scripts/pathfinder/PathfinderUtil.lua index 29a5c062d..32b19e645 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) @@ -871,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) @@ -895,13 +914,17 @@ 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? +---@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/BunkerSiloVehicleController.lua b/scripts/silo/BunkerSiloVehicleController.lua index caf5ac979..ff013ec0f 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 == CpBunkerSilo.SIDE_MODES.ONE_SIDED_INVERTED then + self.isInverted = true + end + else + self.isInverted = true + if self.silo.siloMode == CpBunkerSilo.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 6c6062985..2ddfe1a4f 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 @@ -175,6 +205,10 @@ function CpSilo:getTotalFillLevel() return 0 end +function CpSilo:isTheSameSilo() + --- override +end + --- Heap Bunker Silo --- Wrapper for a heap. ---@class CpHeapBunkerSilo :CpSilo @@ -388,6 +422,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() @@ -666,11 +708,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/specializations/CourseplaySpec.lua b/scripts/specializations/CourseplaySpec.lua deleted file mode 100644 index 8894d1472..000000000 --- a/scripts/specializations/CourseplaySpec.lua +++ /dev/null @@ -1,109 +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(AIFieldWorker, 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 -) diff --git a/scripts/specializations/CpAIBaleFinder.lua b/scripts/specializations/CpAIBaleFinder.lua index 535e1197e..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) @@ -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..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) @@ -95,11 +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, nil)) end function CpAIBunkerSiloWorker:getCanStartCp(superFunc) @@ -112,7 +114,7 @@ function CpAIBunkerSiloWorker:getCpStartableJob(superFunc, isStartedByHud) if isStartedByHud and not AIUtil.hasChildVehicleWithSpecialization(self, Leveler) 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 69f1ef223..9a7b85712 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 fb0168958..42e125966 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 @@ -299,9 +296,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..4ec9f4486 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,11 @@ 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:getCanStartCpCombineUnloader() + and AIUtil.hasChildVehicleWithSpecialization(self, Shovel) end function CpAISiloLoaderWorker:getCanStartCp(superFunc) @@ -105,7 +107,11 @@ end function CpAISiloLoaderWorker:getCpStartableJob(superFunc, isStartedByHud) local spec = self.spec_cpAISiloLoaderWorker - return superFunc(self) 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) @@ -166,17 +172,23 @@ 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 = AIDriveStrategySiloLoader.new() - -- this also starts the strategy + local strategy + if SpecializationUtil.hasSpecialization(ConveyorBelt, self.specializations) 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() + strategy:setUnloadTriggerAndStation(unloadTrigger, unloadStation) + end strategy:setSiloAndHeap(bunkerSilo, heap) strategy:setAIVehicle(self, jobParameters) - CpUtil.debugVehicle(CpDebug.DBG_SILO, self, "Starting silo worker job.") self:startCpWithStrategy(strategy) end end function CpAISiloLoaderWorker:stopCpSiloLoaderWorker() self:stopCpDriver() -end \ No newline at end of file +end diff --git a/scripts/specializations/CpAIWorker.lua b/scripts/specializations/CpAIWorker.lua index 2034afe03..5e39040fc 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") @@ -42,11 +45,10 @@ 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) + SpecializationUtil.registerEventListener(vehicleType, "onPreDelete", CpAIWorker) --- Autodrive events SpecializationUtil.registerEventListener(vehicleType, "onStopAutoDrive", CpAIWorker) SpecializationUtil.registerEventListener(vehicleType, "onStartAutoDrive", CpAIWorker) @@ -76,10 +78,14 @@ 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 ------------------------------------------------------------------------------------------------------------------------- + +--------------------------------------------------- --- Event listeners ---------------------------------------------------------------------------------------------------------------------------- +--------------------------------------------------- + function CpAIWorker:onLoad(savegame) --- Register the spec: spec_CpAIWorker self.spec_cpAIWorker = self["spec_" .. CpAIWorker.SPEC_NAME] @@ -87,22 +93,19 @@ 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:onLoadFinished() - +function CpAIWorker:onUnitChanged() + SpecializationUtil.raiseEvent(self,"onCpUnitChanged") end -function CpAIWorker:onPreDetachImplement(implement) - local spec = self.spec_cpAIWorker -end - -function CpAIWorker:onPostAttachImplement(object) - local spec = self.spec_cpAIWorker - +function CpAIWorker:onLoadFinished() + end - function CpAIWorker:onLeaveVehicle(wasEntered) if wasEntered then CpJobSyncOnLeaveEvent.sendEvent(self) @@ -148,6 +151,18 @@ function CpAIWorker:onRegisterActionEvents(isActiveForInput, isActiveForInputIgn end end +function CpAIWorker:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) + CpAIWorker.updateActionEvents(self) +end + +function CpAIWorker:onPreDelete() + +end + +----------------------------------------------- +--- Action input events +----------------------------------------------- + --- Updates the action event visibility and text. function CpAIWorker:updateActionEvents() local spec = self.spec_cpAIWorker @@ -185,72 +200,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() @@ -265,7 +214,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. @@ -301,6 +250,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() @@ -309,23 +262,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. @@ -336,6 +290,66 @@ 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. +--- 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()) + 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 @@ -454,6 +468,7 @@ function CpAIWorker:cpHold(ms) end end +---@param strategy AIDriveStrategyCourse function CpAIWorker:startCpWithStrategy(strategy) local spec = self.spec_cpAIWorker spec.driveStrategy = strategy @@ -464,12 +479,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 @@ -484,9 +497,7 @@ function CpAIWorker:stopCpDriver() if actionController ~= nil then actionController:resetCurrentState() end - self:raiseAIEvent("onAIFieldWorkerEnd", "onAIImplementEnd") - end function CpAIWorker:getCpDriveStrategy() @@ -494,6 +505,69 @@ function CpAIWorker:getCpDriveStrategy() return spec.driveStrategy 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 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? +--- 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 +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.vehicle.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 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 1afa7e2ec..c9fdc8994 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) @@ -194,11 +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() 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 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/CpShovelPositions.lua b/scripts/specializations/CpShovelPositions.lua new file mode 100644 index 000000000..0ac058084 --- /dev/null +++ b/scripts/specializations/CpShovelPositions.lua @@ -0,0 +1,713 @@ +--[[ + 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 = { + 2.7, + 2.8 + }, + SHOVEL_LIMITS = { + 43, + 47 + }, + }, + DEBUG = true +} +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 + CpShovelPositions.initConsoleCommands() +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, "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) + SpecializationUtil.registerFunction(vehicleType, "setCpShovelMinimalUnloadHeight", CpShovelPositions.setCpShovelMinimalUnloadHeight) +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 + spec.minimalShovelUnloadHeight = 4 + spec.highDumpMovingToolIx = g_vehicleConfigurations:get(self, "shovelMovingToolIx") + spec.isHighDumpShovel = spec.highDumpMovingToolIx ~= nil +end + +function CpShovelPositions:onPostAttach() + if self.spec_cpShovelPositions then + CpShovelPositions.cpSetupShovelPositions(self) + end +end + +function CpShovelPositions:onDraw() + +end + +function CpShovelPositions:onUpdateTick(dt) + local spec = self.spec_cpShovelPositions + if spec.shovelToolIx == nil or spec.armToolIx == nil or self.rootVehicle == nil then + return + end + if spec.state == CpShovelPositions.LOADING then + CpUtil.try(CpShovelPositions.updateLoadingPosition, self, dt) + elseif spec.state == CpShovelPositions.TRANSPORT then + CpUtil.try(CpShovelPositions.updateTransportPosition, self, dt) + elseif spec.state == CpShovelPositions.PRE_UNLOAD then + CpUtil.try(CpShovelPositions.updatePreUnloadPosition, self, 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) + if not self.isServer then return end + local spec = self.spec_cpShovelPositions + spec.resetWhenReached = false + 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 + +function CpShovelPositions:cpSetTemporaryShovelState(state) + if not self.isServer then return end + 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() + if not self.isServer then return end + 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 + +--- Is the current target shovel position not yet reached? +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, 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 + spec.shovelVehicle = vehicle + elseif tool.axis == "AXIS_FRONTLOADER_ARM2" then + --- WIP for telescope loaders + spec.armExtendToolIx = i + spec.armExtendTool = tool + spec.armExtendVehicle = vehicle + end + end + end + end + 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.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) + 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 + +--- 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) + 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 + +--- 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, + 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 +---@param armLimits table +---@param heightOffset number|nil +---@return boolean +function CpShovelPositions:setShovelPosition(dt, shovelLimits, armLimits, heightOffset) + 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 + 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 = 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] + + curRot = {} + curRot[1], curRot[2], curRot[3] = getRotation(armTool.node) + local oldArmRot = curRot[armTool.rotationAxis] + + local armProjectionNode = spec.armProjectionNode + local armToolRefNode = spec.armToolRefNode + + local radiusArmToolToShovelTool = calcDistanceFrom(shovelTool.node, armTool.node) + + local attacherJointNode = self.spec_attachable.attacherJoint.node + local angle, shovelNode = CpShovelPositions.getShovelData(self) + --local _, shovelY, _ = localToLocal(self.rootNode, attacherJointNode, 0, 0, 0) + local _, shovelY, _ = localToLocal(armVehicle.rootNode, shovelVehicle.rootNode, 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 wx, _, wz = getWorldTranslation(armVehicle.rootNode) + + 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) + DebugUtil.drawDebugLine(bx1, by1, bz1, bx2, by2, bz2, 0, 0, 1) + if by1 < by2 then + _, by, _ = worldToLocal(shovelTool.node, sx, by1, sz) + DebugUtil.drawDebugLine(sx, by1, sz, sx, by1 - by, sz, 0, 0, 1) + else + _, by, _ = worldToLocal(shovelTool.node, sx, by2, sz) + DebugUtil.drawDebugLine(sx, by2, sz, sx, by2 - by, sz, 0, 0, 1) + end + end + else + local bx1, by1, bz1 = localToWorld(self.rootNode, 0, 0, self.size.lengthOffset + self.size.length/2) + local bx2, by2, bz2 = localToWorld(self.rootNode, 0, 0, self.size.lengthOffset - self.size.length/2) + DebugUtil.drawDebugLine(bx1, by1, bz1, bx2, by2, bz2, 0, 0, 1) + local sx, _, sz = localToWorld(shovelTool.node, 0, 0, 0) + if by1 < by2 then + _, by, _ = worldToLocal(shovelTool.node, sx, by1, sz) + else + _, by, _ = worldToLocal(shovelTool.node, sx, by2, sz) + end + end + 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, _ = getWorldTranslation(self.rootVehicle.rootNode, 0, 0, 0) + + 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) + + 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 + 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 + 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) + oldRotRelativeArmRot = MathUtil.getYRotationFromDirection(-dirZ, dirY) + math.pi/2 + + alpha = math.atan2(i1y - ay, i1z - az) + local beta = -math.atan2(i2y - ay, i2z - az) + local a = MathUtil.clamp(oldArmRot - MathUtil.getAngleDifference( + alpha, oldRotRelativeArmRot), armTool.rotMin, armTool.rotMax) + if not skipArm then + isDirty = ImplementUtil.moveMovingToolToRotation( + armVehicle, armTool, dt, a) or isDirty + end + end + + if spec.state ~= CpShovelPositions.UNLOADING then + 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 + DebugUtil.drawDebugLine(wsx, wsy, wsz, wex, wey, wez) + DebugUtil.drawDebugLine(wsx, terrainHeight + minimalTargetHeight , wsz, + wex, terrainHeight + minimalTargetHeight, wez, 0, 0, 1) + 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) + + 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 = "deltaY", value = deltaY}) + table.insert(debugData, { + name = "shovelY", value = shovelY}) + 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(targetAngle - angle) }) + table.insert(debugData, { + name = "targetAngle", value = math.deg(targetAngle) }) + table.insert(debugData, { + value = "", + name = "Diff:" }) + table.insert(debugData, { + name = "unload height", value = minimalTargetHeight }) + + DebugUtil.renderTable(0.4, 0.4, 0.018, + debugData, 0) + + if self.spec_foliageBending ~= nil then + local offset = 0.25 + + for _, bendingNode in ipairs(self.spec_foliageBending.bendingNodes) do + if bendingNode.id ~= nil then + DebugUtil.drawDebugRectangle(bendingNode.node, bendingNode.minX, bendingNode.maxX, bendingNode.minZ, bendingNode.maxZ, bendingNode.yOffset, 1, 0, 0) + DebugUtil.drawDebugRectangle(bendingNode.node, bendingNode.minX - offset, bendingNode.maxX + offset, bendingNode.minZ - offset, bendingNode.maxZ + offset, bendingNode.yOffset, 0, 1, 0) + DebugUtil.drawDebugNode(bendingNode.node, "Bending node") + end + end + end + + + end + return isDirty +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, heightOffset) + end + spec.isDirty = isDirty +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, heightOffset) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:updatePreUnloadPosition(dt) + local spec = self.spec_cpShovelPositions + local angle = CpShovelPositions.getShovelData(self) + local isDirty + if angle then + isDirty = CpShovelPositions.setShovelPosition(self, dt, + CpShovelPositions.PRE_UNLOAD_POSITION.SHOVEL_LIMITS, + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, nil) + end + spec.isDirty = isDirty +end + +function CpShovelPositions:updateUnloadingPosition(dt) + local spec = self.spec_cpShovelPositions + local angle, _, maxAngle = CpShovelPositions.getShovelData(self) + local isDirty + if angle and maxAngle then + isDirty = CpShovelPositions.setShovelPosition(self, dt, + {math.deg(maxAngle), math.deg(maxAngle) + 2}, + CpShovelPositions.PRE_UNLOAD_POSITION.ARM_LIMITS, nil) + end + 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 +end + +--- Gets all relevant shovel data. +function CpShovelPositions:getShovelData() + local shovelSpec = self.spec_shovel + local info = shovelSpec.shovelDischargeInfo + if info == nil or info.node == nil then + CpShovelPositions.debug(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[1] == nil then + CpShovelPositions.debug(self, "Shovel nodes index 0 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.debugImplement(CpDebug.DBG_SILO, 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/specializations/CpVehicleSettings.lua b/scripts/specializations/CpVehicleSettings.lua index 8ee13db07..7d2e397c8 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,13 +33,19 @@ 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) SpecializationUtil.registerEvent(vehicleType, 'onCpUserSettingChanged') + SpecializationUtil.registerEvent(vehicleType, 'onCpLoadingShovelOffsetSettingChanged') end @@ -44,13 +53,12 @@ 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) SpecializationUtil.registerEventListener(vehicleType, "onStateChange", CpVehicleSettings) SpecializationUtil.registerEventListener(vehicleType, "onCpUserSettingChanged", CpVehicleSettings) + SpecializationUtil.registerEventListener(vehicleType, 'onCpLoadingShovelOffsetSettingChanged', CpVehicleSettings) end function CpVehicleSettings.registerFunctions(vehicleType) @@ -74,8 +82,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 +97,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 +112,29 @@ 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.setFromVehicleConfiguration(self, data.attachedVehicle, + spec.loadingShovelHeightOffset, 'loadingShovelOffset') + + 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 @@ -369,7 +367,7 @@ function CpVehicleSettings:areCourseSettingsVisible() end function CpVehicleSettings:areBunkerSiloSettingsVisible() - return self:getCanStartCpBunkerSiloWorker() + return self:getCanStartCpBunkerSiloWorker() or self:getCanStartCpSiloLoaderWorker() end function CpVehicleSettings:areCombineUnloaderSettingsVisible() @@ -394,6 +392,22 @@ function CpVehicleSettings:setAutomaticBaleCollectorOffset() spec.baleCollectorOffset:setFloatValue(offset) end +function CpVehicleSettings:isLoadingShovelOffsetSettingVisible() + return self:getCanStartCpSiloLoaderWorker() and not AIUtil.hasChildVehicleWithSpecialization(self, ConveyorBelt) +end + +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 diff --git a/scripts/trigger/TriggerManager.lua b/scripts/trigger/TriggerManager.lua new file mode 100644 index 000000000..20386277f --- /dev/null +++ b/scripts/trigger/TriggerManager.lua @@ -0,0 +1,167 @@ +--- Stores all the relevant giants triggers expect bunker silos. +--- For now only unload triggers are supported. +---@class TriggerManager +TriggerManager = CpObject() +TriggerManager.DEBUG = false +function TriggerManager:init() + ---@type table<number,CpTrigger> + self.unloadTriggers = {} + ---@type table<number,CpTrigger> + 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) + if silo:getIsToolTypeAllowed(ToolType.DISCHARGEABLE) then + self.dischargeableUnloadTriggers[silo.exactFillRootNode] = self.unloadTriggers[silo.exactFillRootNode] + end + 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 + self.unloadTriggers[silo.exactFillRootNode]:delete() + self.unloadTriggers[silo.exactFillRootNode] = nil + self.dischargeableUnloadTriggers[silo.exactFillRootNode] = nil + end + end +end + +--- Gets the unload trigger from the exactFillRootNode. +---@param node number exactFillRootNode +---@return CpTrigger +function TriggerManager:getUnloadTriggerForNode(node) + return self.unloadTriggers[node] +end + +--- Gets the first trigger found in the defined area. +---@param triggers table<number,CpTrigger> +---@param x number +---@param z number +---@param dirX number +---@param dirZ number +---@param width number +---@param length number +---@return boolean +---@return CpTrigger|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: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 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) +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 found? +---@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) +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. +---@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, fillTypes) + end +end + +function TriggerManager:draw() + for node, trigger in pairs(self.unloadTriggers) do + if self.DEBUG then + local text = string.format("%s:\n %d", getName(node), node ) + CpUtil.drawDebugNode(node, false, 2, text) + end + end +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..549db2ab9 --- /dev/null +++ b/scripts/trigger/TriggerWrapper.lua @@ -0,0 +1,69 @@ + +--- Wrapper for a trigger. +---@class CpTrigger +CpTrigger = CpObject() + +function CpTrigger:init(trigger, node) + self.trigger = trigger + self.node = node + self.plot = UnloadingTriggerPlot(self.node) +end + +function CpTrigger:delete() + +end + +function CpTrigger:getNode() + return self.node +end + +function CpTrigger:getTrigger() + return self.trigger +end + +function CpTrigger:getTarget() + return self.trigger:getTarget() +end + +---@param fillUnitIndex number +---@return number +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 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 + self.plot:setHighlighted(self == selectedTrigger) + 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 + end +end diff --git a/translations/translation_br.xml b/translations/translation_br.xml index 6f6efed29..af21ffaf4 100644 --- a/translations/translation_br.xml +++ b/translations/translation_br.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="O veículo está muito longe do campo para começar."/> <text name="CP_error_no_bunkerSilo_found" text="Nenhum silo foi encontrado."/> <text name="CP_error_no_heap_found" text="Nenhum silo encontrado."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Posição para carregar"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Posição para carregar"/> <text name="CP_siloLoader_fillLevelProgress" text="Progressâo: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Configuração trabalho no silo"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Largura de trabalho"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Largura de trabalho. Valor: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -817,6 +823,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml index 4b52f01ad..b108f2dcc 100644 --- a/translations/translation_cs.xml +++ b/translations/translation_cs.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -801,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml index f260f53a4..f9ecd704d 100644 --- a/translations/translation_ct.xml +++ b/translations/translation_ct.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="車輛離田地太遠,無法啟動."/> <text name="CP_error_no_bunkerSilo_found" text="未發現筒倉"/> <text name="CP_error_no_heap_found" text="未發現筒倉"/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="裝載位置"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="裝載位置"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="筒倉工作設定"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="工作寬度"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="工作寬度 數值範圍: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -800,6 +806,16 @@ Courseplay能夠在青貯筒倉中壓實或分佈糠秕,或在楔形筒倉中 要啟動AI選單上的裝載標記,需要進行設定。 這個標記可以檢測到堆或筒倉,並在地圖上突出顯示它們。 那麼現在可以啟用小幫手了。 +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml index 09674798e..2f9cefff1 100644 --- a/translations/translation_cz.xml +++ b/translations/translation_cz.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vozidlo je příliš daleko od pole."/> <text name="CP_error_no_bunkerSilo_found" text="Silážní jáma nenalezena."/> <text name="CP_error_no_heap_found" text="Hromada nenalezena."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Nakládací pozice"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Nakládací pozice"/> <text name="CP_siloLoader_fillLevelProgress" text="Pokrok: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Nastavení pracovníka v jámě"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Pracovní šířka"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Pracovní šířka. Hodnota: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -322,7 +328,7 @@ <text name="CP_global_setting_drawOntoTheHudMap_tooltip" text="Umožňuje vykreslit trasu do mini mapy."/> <text name="CP_global_setting_infoTextHudActive_title" text="Zobrazit informační textové okno"/> <text name="CP_global_setting_infoTextHudActive_tooltip" text="Zobrazí informační textové okno."/> - <text name="CP_global_setting_infoTextHudActive_active_hide_without_message" text="Active but invisible without message"/> + <text name="CP_global_setting_infoTextHudActive_active_hide_without_message" text="Aktivní, ale neviditelný bez hlášení"/> <text name="CP_global_setting_infoTextHudPlayerMouseActive_title" text="Chování myši hráče"/> <text name="CP_global_setting_infoTextHudPlayerMouseActive_tooltip" text="Povolí/zakáže chování myši hráče pro zobrazení informačního textu mimo vozidlo."/> <text name="CP_global_setting_preferCustomFields_title" text="Preference vlastního pole"/> @@ -798,6 +804,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_da.xml b/translations/translation_da.xml index ba4b5edca..9c8a1c52c 100644 --- a/translations/translation_da.xml +++ b/translations/translation_da.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="Ingen silo fundet."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloarbejder indstilling"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Arbejdsbredde"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Arbejdsbredde. Værdi: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -811,6 +817,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_de.xml b/translations/translation_de.xml index a901c8288..410e8860e 100644 --- a/translations/translation_de.xml +++ b/translations/translation_de.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Fahrzeug ist zu weit vom Feld entfernt."/> <text name="CP_error_no_bunkerSilo_found" text="Keinen Silo gefunden."/> <text name="CP_error_no_heap_found" text="Keinen Haufen gefunden."/> + <text name="CP_error_no_unload_trigger_found" text="Keinen Abladepunkt gefunden."/> <!----> <!--AI job parameters--> <!----> @@ -93,7 +94,10 @@ <!----> <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Ladeposition"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Ladeposition"/> - <text name="CP_siloLoader_fillLevelProgress" text="Fortschritt: "/> + <text name="CP_siloLoader_fillLevelProgress" text="Liter übrig: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Abladen in:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Anhänger"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Abladestation"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloarbeiter Einstellungen"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Arbeitsbreite"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Arbeitsbreite. Wert: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Versatz Schaufelhöhe."/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Höhenversatz der Schaufel für die Ladeposition. Wert: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -823,6 +829,41 @@ 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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Radlader/Frontlader"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +Mit einem Radlader oder Traktor mit Frontlader und Schaufel lassen sich Haufen vom Boden oder aus einem Bunker Silo automatisch aufnehmen und in einen Anhänger oder Lager abladen. +Die Schaufel wird dabei automatisch an die richtigen Positionen gefahren. +Ist der Modus im HUD nicht direkt zu sehen, schalte die "Startposition" durch bis der Modus erscheint. + +Im HUD erscheint dann das gewohnte Ziel Icon um direkt mit dem richtigen Modus auf der Helfer Karte zu landen. +Es gibt eine Anzeige, die die verbleibenden Füllmenge des Haufens anzeigt. +Die Arbeitsbreite muss im HUD eventuell angepasst werden, wenn ihr ein Bunker Silo leeren möchtet, um nicht seitlich in die Wand zu fahren. + +Der Versatz der 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. +Ein Klick auf den Text, setzt den Wert auf 0 zurück. Der Wert lässt sich zwischen +1 bis -1 anpassen. + +Schaufeln mit Gebiss öffnen dieses automatisch beim laden und abladen und schließen dieses zum Transport. +Die Schaufel zum Zerkleinern von Zuckerrüben wird mit allen Funktionen unterstützt. +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +Um einen Radlader Fahrer zu starten, müssen zunächst über das Ziel Icon im HUD die Lade- und Entladepositionen gewählt werden. +Die Ladeposition wird wie auch bereits beim Lader gewählt. Ein blauer Ramen entsteht um den gefunden Haufen. + +Die Abladeposition hängt davon ab, ob du in einen Anhänger oder in eine Abladestation entladen möchtest. +Abladen in einen Anhänger funktioniert automatisch und braucht keine zusätzliche Position. +Es wird der naheste, stehende Anhänger gewählt und seitlich angefahren. + +Möchtest du in eine Abladestation entladen, muss zunächst das Ziel umgestellt werden und anschließend der Marker auf den Trigger gesetzt werden. +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +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. +Die Richtung des Pfeils entspricht hierbei die Anfahrtsrichtung vom Helfer. +Das Kreuz sollte jetzt, wie im Bild da gestellt, gelb sein. "/> <!----> <!--Input bindings--> diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml index 5e94095f8..4c57c90f5 100644 --- a/translations/translation_ea.xml +++ b/translations/translation_ea.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_en.xml b/translations/translation_en.xml index 17c62d658..82f77c139 100644 --- a/translations/translation_en.xml +++ b/translations/translation_en.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No heap found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -93,7 +94,10 @@ <!----> <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> - <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoader_fillLevelProgress" text="Liters left: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel hight offset."/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Offset for the shovel loading position. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -823,6 +829,43 @@ 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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheel loader/Front loader"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +This mode enables wheel loader and tractors with a front loader. +With a shovel attached a heap can be picked up from the ground or loading from a bunker silo can be accomplished. +The picked up fill level can be unloaded automatically into a nearby trailer or a selected unloading station like a BGA. +The shovel will automatically be moved to the correct positions for loading, unloading and so on. +In case the mode is not directly visable on the hud, you can toggle the "start position" until the shovel mode appears. + +The target icon on the hud can be used to open the AI map to select the loading and the unloading targets. +The hud also shows the leftover fill level of the heap or silo, while the helper is working. +If the loading target is a bunker silo, then the work width might need to be adjusted to avoid hitting the side walls of the silo. + +The hight offset setting is used to adjust the hight above the ground, as not all shovel can calculated correctly. +This should be checked, if the shovel is to low for loading and the driver can't turn anymore or the shovel is to high and fill level is missed to the ground. +If the value gets changed, the shovel will automatically move to the loading position to show the impact of the offset. +To reset the offset you need to click on the settings text in the hud. The value can be adjusted from +1 to -1 in 0.1 setps. + +Shovels with a silage grab will open and close automatically for loading and unloading. +The shovel to shred sugar beets is completly functional as well. +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +To start a wheel loader helper, you need to set the load and unload positions by clicking the target icon on the hud. +The loading position works the same like the one from the loader mode. A blue square will be created arround the heap. + +The unloading position depends if you want to unload into a trailer or into an unloading station. +Unloading into a trailer works automatically and doesn't need a dedicated unload position. The driver will check for the nearest standing trailer and unloads to it from the side. + +If you want to unload into an unloading station, you need to switch the target and then mark the trigger with the unloading position. +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +Selecting a trigger does look a bit complicated at first, but in reality it is really easy. +Every building can have more then one trigger so you have to choose the one you want to unload to. +All available triggers will be shown with an orange cross on the AI Map. +To select one place the middle of the round marker on the middle of the cross. +The driection of the arrow tells the helper from which direction he will approach the trigger for unloading. +Now your selection should look similar to the image. "/> <!----> <!--Input bindings--> diff --git a/translations/translation_es.xml b/translations/translation_es.xml index d0e538305..839a9aab4 100644 --- a/translations/translation_es.xml +++ b/translations/translation_es.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehículo demasiado lejos del campo."/> <text name="CP_error_no_bunkerSilo_found" text="No se encontró ningún Silo."/> <text name="CP_error_no_heap_found" text="No se encontró ningún montón."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Posición de carga"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Posición de carga"/> <text name="CP_siloLoader_fillLevelProgress" text="Progreso: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Ajustes de Trabajo en Silo"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Ancho de trabajo"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Ancho de trabajo. Valor: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -809,6 +815,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_fc.xml b/translations/translation_fc.xml index 513372322..f05343ef0 100644 --- a/translations/translation_fc.xml +++ b/translations/translation_fc.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_fi.xml b/translations/translation_fi.xml index c1aab329b..ae2266a62 100644 --- a/translations/translation_fi.xml +++ b/translations/translation_fi.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml index 1dade8980..b571e8006 100644 --- a/translations/translation_fr.xml +++ b/translations/translation_fr.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Le véhicule est trop éloigné du champ pour démarrer."/> <text name="CP_error_no_bunkerSilo_found" text="Aucun silo détecté."/> <text name="CP_error_no_heap_found" text="Aucun tas ou silo détecté."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Position de chargement"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Position de chargement"/> <text name="CP_siloLoader_fillLevelProgress" text="Progression: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Travail dans les silos"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Largeur de travail"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Largeur de travail de la lame ou du rouleau. Valeur: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -789,6 +795,16 @@ La chargeuse peut être vidée par une tâche de déchargement Courseplay. <text name="CP_help_page_siloLoaderBasic_text" text=" Placer le marqueur d'approvisionnement sur un tas ou un silo. Ils apparaissent en surbrillance sur la carte. Ensuite vous pouvez lancer la tâche. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml index ba963b5a2..13002836c 100644 --- a/translations/translation_hu.xml +++ b/translations/translation_hu.xml @@ -18,27 +18,28 @@ <text name="CP_error_no_course" text="Először hozz létre egy útvonalat"/> <text name="CP_error_could_not_generate_course" text="Hiba az útvonaltervezéskor"/> <text name="CP_error_not_on_field" text="Csak szántóföldön lehet munkát végezni"/> - <text name="CP_error_unloader_to_far_away_from_field" text="Az ürítő jármű túl messze van a mezőtől."/> + <text name="CP_error_unloader_to_far_away_from_field" text="Az kirakodó jármű túl messze van a mezőtől."/> <text name="CP_error_start_position_to_far_away_from_field" text="A kezdő pozíció túl messze van a mezőtől."/> - <text name="CP_error_giants_unloader_not_available" text="Giants unloader not available."/> - <text name="CP_error_fieldUnloadPosition_too_far_away_from_field" text="Field unload position is too far away from the field."/> - <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> - <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> - <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_giants_unloader_not_available" text="Giants kirakodó nem elérhető."/> + <text name="CP_error_fieldUnloadPosition_too_far_away_from_field" text="A szántóföldi kirakodó pozíció túl messze van a mezőtől."/> + <text name="CP_error_vehicle_too_far_away_from_field" text="A jármű túl messze van a mezőtől a kezdéshez."/> + <text name="CP_error_no_bunkerSilo_found" text="Siló nem található."/> + <text name="CP_error_no_heap_found" text="Siló nem található."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> <text name="CP_jobParameters_fieldPosition_title" text="Szántóföld:"/> - <text name="CP_jobParameters_bunkerSiloPosition_title" text="Silo position:"/> - <text name="CP_jobParameters_parkPosition_title" text="Park position:"/> + <text name="CP_jobParameters_bunkerSiloPosition_title" text="Siló pozíció:"/> + <text name="CP_jobParameters_parkPosition_title" text="Parkolási pozíció:"/> <text name="CP_jobParameters_subTitle_startAt" text="Útvonal kezdete:"/> <text name="CP_jobParameters_startAt_title" text="Útvonal kezdete:"/> <text name="CP_jobParameters_startAt_tooltip" text="Melyik útvonalpontnál kezdje a munkát"/> <text name="CP_jobParameters_startAt_nearest" text="legközelebbi pont"/> <text name="CP_jobParameters_startAt_first" text="első pont"/> <text name="CP_jobParameters_startAt_last" text="utoljára használt pont"/> - <text name="CP_jobParameters_startAt_bunkerSilo" text="Bunker silo mode"/> - <text name="CP_jobParameters_startAt_siloLoader" text="Silo load mode"/> + <text name="CP_jobParameters_startAt_bunkerSilo" text="Bunker siló mód"/> + <text name="CP_jobParameters_startAt_siloLoader" text="Silo töltési mód"/> <text name="CP_jobParameters_startAt_unload" text="kirakodó kombájn/aprító"/> <text name="CP_jobParameters_startAt_unload_siloLoader" text="Unload Silo loader"/> <text name="CP_jobParameters_startAt_bales" text="bálák összegyűjtése/csomagolása"/> @@ -58,50 +59,52 @@ <text name="CP_baleFinderJobParameters_baleWrapType_all_bales" text="összes"/> <text name="CP_baleFinderJobParameters_baleWrapType_only_wrapped_bales" text="csak a csomagoltak"/> <text name="CP_baleFinderJobParameters_baleWrapType_only_not_wrapped_bales" text="csak a nem csomagoltak"/> - <text name="CP_baleFinder_balesLeftover" text="Bales left:"/> + <text name="CP_baleFinder_balesLeftover" text="Maradék bálák:"/> <!----> <!--AI combine unloader job parameters--> <!----> - <text name="CP_combineUnloaderJobParameters_subTitle_unloadTarget" text="Unload: "/> - <text name="CP_combineUnloaderJobParameters_unloadTarget_title" text="Unload:"/> - <text name="CP_combineUnloaderJobParameters_unloadTarget_combine" text="Combine"/> - <text name="CP_combineUnloaderJobParameters_unloadTarget_chopper" text="Chopper"/> - <text name="CP_combineUnloaderJobParameters_unloadTarget_siloLoader" text="Loader"/> - <text name="CP_combineUnloaderJobParameters_subTitle_giantsUnloader" text="Giants unloader:"/> - <text name="CP_combineUnloaderJobParameters_useGiantsUnload_title" text="Use giants unloader:"/> - <text name="CP_combineUnloaderJobParameters_unloadingStation_title" text="Unloading station:"/> - <text name="CP_combineUnloaderJobParameters_subTitle_fieldUnload" text="Giants unloader:"/> - <text name="CP_combineUnloaderJobParameters_useFieldUnload_title" text="Use unload on field:"/> - <text name="CP_combineUnloaderJobParameters_fieldUnloadPosition_title" text="Field unloading position:"/> - <text name="CP_combineUnloaderJobParameters_unloadingTipSide_title" text="Trailer tip side:"/> + <text name="CP_combineUnloaderJobParameters_subTitle_unloadTarget" text="Kirakodás: "/> + <text name="CP_combineUnloaderJobParameters_unloadTarget_title" text="Kirakodás:"/> + <text name="CP_combineUnloaderJobParameters_unloadTarget_combine" text="Kombájn"/> + <text name="CP_combineUnloaderJobParameters_unloadTarget_siloLoader" text="Rakodó"/> + <text name="CP_combineUnloaderJobParameters_subTitle_giantsUnloader" text="Giants kirakodó:"/> + <text name="CP_combineUnloaderJobParameters_useGiantsUnload_title" text="Giants kirakodó használata:"/> + <text name="CP_combineUnloaderJobParameters_unloadingStation_title" text="Kirakodó állomás:"/> + <text name="CP_combineUnloaderJobParameters_subTitle_fieldUnload" text="Giants kirakodó:"/> + <text name="CP_combineUnloaderJobParameters_useFieldUnload_title" text="Szántóföldi kirakodás használata:"/> + <text name="CP_combineUnloaderJobParameters_fieldUnloadPosition_title" text="Szántóföldi kirakodó pozíció:"/> + <text name="CP_combineUnloaderJobParameters_unloadingTipSide_title" text="Pótkocsi billentési oldal:"/> <!----> <!--AI bunker silo worker job parameters--> <!----> - <text name="CP_bunkerSiloJobParameters_subTitle_drivingForwardsIntoSilo" text="Direction into the silo:"/> - <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_title" text="Direction into the silo:"/> - <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_forwards" text="Forwards"/> - <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_reverse" text="Reverse"/> - <text name="CP_bunkerSiloJobParameters_subTitle_waitAtParkPosition" text="Waiting for unloader:"/> - <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_title" text="Waiting for unloader:"/> - <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_silo" text="in silo"/> - <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_parkPosition" text="at park position"/> - <text name="CP_bunkerSiloJobParameters_stopWithCompactedSilo_title" text="Stop at 99% compaction"/> - <text name="CP_bunkerSiloJobParameters_subTitle_stopWithCompactedSilo" text="Stop at 99% compaction"/> - <text name="CP_bunkerSilo_compactionPercentage" text="Silo compaction:"/> + <text name="CP_bunkerSiloJobParameters_subTitle_drivingForwardsIntoSilo" text="Silóba hajtás iránya:"/> + <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_title" text="Silóba hajtás iránya:"/> + <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_forwards" text="Előre"/> + <text name="CP_bunkerSiloJobParameters_drivingForwardsIntoSilo_reverse" text="Hátra"/> + <text name="CP_bunkerSiloJobParameters_subTitle_waitAtParkPosition" text="Várakozás a kirakodóra:"/> + <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_title" text="Várakozás a kirakodóra:"/> + <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_silo" text="silóban"/> + <text name="CP_bunkerSiloJobParameters_waitAtParkPosition_parkPosition" text="parkolási pozíción"/> + <text name="CP_bunkerSiloJobParameters_stopWithCompactedSilo_title" text="Megállás 99% tömörítésnél"/> + <text name="CP_bunkerSiloJobParameters_subTitle_stopWithCompactedSilo" text="Megállás 99% tömörítésnél"/> + <text name="CP_bunkerSilo_compactionPercentage" text="Siló tömörítés:"/> <!----> <!--AI silo lader worker job parameters--> <!----> - <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> - <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> - <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Rakodási pozíció"/> + <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Rakodási pozíció"/> + <text name="CP_siloLoader_fillLevelProgress" text="Folyamat: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> <text name="CP_job_fieldWork" text="CP: Terepmunka"/> <text name="CP_job_baleCollect" text="CP: Bálagyűjtés/csomagolás"/> <text name="CP_job_combineUnload" text="CP: Kombájn ürítése"/> - <text name="CP_job_bunkerSilo" text="CP: Silo work"/> - <text name="CP_job_siloLoader" text="CP: Silo loading"/> + <text name="CP_job_bunkerSilo" text="CP: Siló munka"/> + <text name="CP_job_siloLoader" text="CP: Siló töltés"/> <text name="CP_ai_page_generate_course" text="CP: Útvonaltervezés"/> <text name="CP_ai_page_open_course_generator" text="CP: Útvonaltervező megnyitása/zárása."/> <!----> @@ -111,9 +114,9 @@ <text name="CP_ai_messageError" text="%s segítő váratlanul abbahagyta a munkát - letért az útvonalról!"/> <text name="CP_ai_messageErrorWrongBaleWrapType" text="%s segítő váratlanul abbahagyta a munkát - Nincs a kiválasztott típusú bála!"/> <text name="CP_ai_messageErrorNoPathFound" text="AI segítő %s váratlanul abbahagyta a munkát - útvonal nem található!"/> - <text name="CP_ai_messageErrorGroundUnloadNotSupported" text="AI worker %s has stopped work unexpectedly - unloading on the field not possible!"/> - <text name="CP_ai_messageErrorCutterNotSupported" text="AI worker %s has stopped work unexpectedly - attaching of the header is impossible!"/> - <text name="CP_ai_messageErrorAutomaticCutterAttachNotActive" text="AI worker %s has stopped work unexpectedly - automatic cutter attach disabled!"/> + <text name="CP_ai_messageErrorGroundUnloadNotSupported" text="AI munkás %s váratlanul abbahagyta a munkát - kirakodás a mezőn nem lehetséges!"/> + <text name="CP_ai_messageErrorCutterNotSupported" text="AI munkás %s váratlanul abbahagyta a munkát - vágóasztal felcsatolása lehetetlen!"/> + <text name="CP_ai_messageErrorAutomaticCutterAttachNotActive" text="AI munkás %s váratlanul abbahagyta a munkát - automatikus vágóasztal felcsatolás kikapcsolva!"/> <!----> <!--Vehicle settings--> <!----> @@ -142,8 +145,8 @@ <text name="CP_vehicle_setting_allowPathfinderTurns_tooltip" text="Engedélyezi az útkereső használatát fordulókban, pl. akadályok ill. termény kikerüléséhez"/> <text name="CP_vehicle_setting_foldImplementAtEnd_title" text="Hajtsa össze a munkagépet a munka végén"/> <text name="CP_vehicle_setting_foldImplementAtEnd_tooltip" text="Engedélyezi/letiltja a munkagépek automatikus összecsukását a munka végén."/> - <text name="CP_vehicle_setting_returnToStart_title" text="Return to starting point"/> - <text name="CP_vehicle_setting_returnToStart_tooltip" text="Return to starting point after finished fieldwork."/> + <text name="CP_vehicle_setting_returnToStart_title" text="Visszatérés a kezdőpontra"/> + <text name="CP_vehicle_setting_returnToStart_tooltip" text="Visszatérés a kezdőpontra a munka végeztével."/> <text name="CP_vehicle_setting_raiseImplementLate_title" text="Szerszám emelés"/> <text name="CP_vehicle_setting_raiseImplementLate_tooltip" text="Korán vagy késésel emelje fel a szerszámot a forduló kezdetén."/> <text name="CP_vehicle_setting_raiseImplementLate_early" text="korán"/> @@ -158,12 +161,12 @@ <text name="CP_vehicle_setting_avoidFruit_tooltip" text="Az AI megpróbálja elkerülni, hogy a terménybe hajtson"/> <text name="CP_vehicle_setting_toolOffsetX_title" text="Szerszámeltolás oldalra"/> <text name="CP_vehicle_setting_toolOffsetX_tooltip" text="Szerszámeltolás balra/jobbra"/> - <text name="CP_vehicle_setting_baleCollectorOffset_title" text="Bale pickup offset"/> - <text name="CP_vehicle_setting_baleCollectorOffset_tooltip" text="Offset for the bale collector"/> + <text name="CP_vehicle_setting_baleCollectorOffset_title" text="Bála felszedés eltolása"/> + <text name="CP_vehicle_setting_baleCollectorOffset_tooltip" text="Eltolás a bálagyűjtőkhöz"/> <text name="CP_vehicle_setting_toolOffsetZ_title" text="Szerszámeltolás hosszirányban"/> <text name="CP_vehicle_setting_toolOffsetZ_tooltip" text="Szerszámeltolás előre/hátra"/> - <text name="CP_vehicle_setting_fullThreshold_title" text="Empty when over:"/> - <text name="CP_vehicle_setting_fullThreshold_tooltip" text="TODO"/> + <text name="CP_vehicle_setting_fullThreshold_title" text="Kiürítési határérték:"/> + <text name="CP_vehicle_setting_fullThreshold_tooltip" text="A segítő kiüríti a pótkocsit, ha a töltési szint meghaladja a beállított értéket"/> <text name="CP_vehicle_setting_useAdditiveFillUnit_title" text="Szilázsadalék ellenőrzés"/> <text name="CP_vehicle_setting_useAdditiveFillUnit_tooltip" text="Álljon meg ha a szilázsadalék-tartály üres"/> <text name="CP_vehicle_setting_symmetricLaneChange_title" text="Szimmetrikus sávváltás"/> @@ -176,8 +179,10 @@ <!--Vehicle bunker silo settings--> <!----> <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> - <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> - <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Munkaszélesség"/> + <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Munkaszélesség. Értéke: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -189,8 +194,8 @@ <text name="CP_vehicle_setting_turnSpeed_tooltip" text="Maximális sebesség forduló közben. 2 - 25 km/ó"/> <text name="CP_vehicle_setting_reverseSpeed_title" text="Tolatás"/> <text name="CP_vehicle_setting_reverseSpeed_tooltip" text="Maximális sebesség tolatás közben. 2 - 25 km/ó"/> - <text name="CP_vehicle_setting_bunkerSiloSpeed_title" text="Silo"/> - <text name="CP_vehicle_setting_bunkerSiloSpeed_tooltip" text="Maximum speed while driving in a silo. Value: 2 - 25"/> + <text name="CP_vehicle_setting_bunkerSiloSpeed_title" text="Siló"/> + <text name="CP_vehicle_setting_bunkerSiloSpeed_tooltip" text="Maximális sebesség silóban. 2 - 25"/> <!----> <!--Vehicle settings sowing--> <!----> @@ -203,8 +208,8 @@ <!----> <!--Vehicle settings combine--> <!----> - <text name="CP_vehicle_setting_automaticCutterAttach_title" text="Automatic cutter attach"/> - <text name="CP_vehicle_setting_automaticCutterAttach_tooltip" text="Allows automatic attachment of a cutter, which needs to be attach as a trailer or on a seperate header trailer."/> + <text name="CP_vehicle_setting_automaticCutterAttach_title" text="Automatikus vágóasztal felcsatolás"/> + <text name="CP_vehicle_setting_automaticCutterAttach_tooltip" text="Engedélyezi egy vágóasztal automatikus felcsatolását, amely lehet vontatott, vagy speciális pótkocsin szállított vágóasztal."/> <text name="CP_vehicle_setting_pipeAlwaysUnfold_title" text="Mindig hajtsa ki a csövet"/> <text name="CP_vehicle_setting_pipeAlwaysUnfold_tooltip" text="Mindig hajtsa ki a csövet, amikor a kombájn megtelt és a kirakodóra vár."/> <text name="CP_vehicle_setting_stopForUnload_title" text="Ürítés csak álló helyzetben"/> @@ -300,12 +305,12 @@ <text name="CP_global_setting_fuelThreshold_tooltip" text="A segítő abbahagyja a munkát, ha az üzemanyagszint ez alá csökken."/> <text name="CP_global_setting_brokenThreshold_title" text="Maximum meghibásodás"/> <text name="CP_global_setting_brokenThreshold_tooltip" text="A segítő abbahagyja a munkát, ha a jármű/szerszám ennél jobban meghibásodik. 100% soha nem hagyja abba."/> - <text name="CP_global_setting_stopThreshingDuringRain_title" text="Stops harvester, while it's raining"/> - <text name="CP_global_setting_stopThreshingDuringRain_tooltip" text="If the setting is active, then all harvester wait for the raining to stop."/> - <text name="CP_global_setting_fruitDestruction_title" text="AI Helper fruit destruction"/> - <text name="CP_global_setting_fruitDestruction_tooltip" text="Enable/disable the fruit destruction for helpers."/> - <text name="CP_global_setting_fruitDestruction_only_cp" text="Only activated for Coursesplay helpers"/> - <text name="CP_global_setting_fruitDestruction_all_helpers" text="Activated for every helpers"/> + <text name="CP_global_setting_stopThreshingDuringRain_title" text="Kombájn megállítása esőben"/> + <text name="CP_global_setting_stopThreshingDuringRain_tooltip" text="Ha a beállítás aktív, akkor minden betakarító gép megvárja, amíg eláll az eső."/> + <text name="CP_global_setting_fruitDestruction_title" text="AI Segítő termény taposás"/> + <text name="CP_global_setting_fruitDestruction_tooltip" text="Engedélyezi/letiltja a termény taposást segítők számára."/> + <text name="CP_global_setting_fruitDestruction_only_cp" text="Csak Coursesplay segítőknek"/> + <text name="CP_global_setting_fruitDestruction_all_helpers" text="Minden segítőnek"/> <!----> <!--Global settings user settings--> <!----> @@ -366,7 +371,7 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü <text name="CP_courseManager_entryExistAlreadyError" text="A fájl már létezik a célmappában (%s)!"/> <text name="CP_courseManager_noAccessError" text="Nincs hozzáférés a mappához (%s)!"/> <text name="CP_courseManager_targetIsNoFolderError" text="A célhely nem mappa (%s)!"/> - <text name="CP_courseManager_targetIsNoCourseError" text="A célhely nem tanfolyam (%s)!"/> + <text name="CP_courseManager_targetIsNoCourseError" text="A célhely nem útvonal (%s)!"/> <text name="CP_courseManager_basicSettings" text="Útvonal kezelés"/> <text name="CP_courseManager_advancedSettings" text="Fájlok szerkesztése"/> <text name="CP_courseManager_activate" text="Aktiválás"/> @@ -413,13 +418,13 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü <text name="CP_infoTexts_drivingToSelfUnload" text="Pótkocsihoz vezetés"/> <text name="CP_infoTexts_outOfMoney" text="elfogyott a pénz"/> <text name="CP_infoTexts_waitForRainToFinish" text="Várja hogy elálljon az eső"/> - <text name="CP_infoTexts_waitForUnloader" text="Waiting on unloader"/> + <text name="CP_infoTexts_waitForUnloader" text="Várakozás a kirakodóra"/> <text name="CP_infoTexts_errorStopped" text="letért az útvonalról, megállt"/> <text name="CP_infoTexts_wrongBaleWrapType" text="Rossz bálatípus"/> <text name="CP_infoTexts_errorNoPathFound" text="Útvonal nem található"/> - <text name="CP_infoTexts_errorGroundUnloadNotSupported" text="Tipping to ground not possible"/> - <text name="CP_infoTexts_errorCutterNotSupported" text="Cutter not supported"/> - <text name="CP_infoTexts_errorAutomaticCutterAttachNotActive" text="Cutter attaching deactivated"/> + <text name="CP_infoTexts_errorGroundUnloadNotSupported" text="Talajra billentés nem lehetséges"/> + <text name="CP_infoTexts_errorCutterNotSupported" text="A vágóasztal nem támogatott"/> + <text name="CP_infoTexts_errorAutomaticCutterAttachNotActive" text="Vágóasztal felcsatolás kikapcsolva"/> <!----> <!--Custom field manager--> <!----> @@ -427,15 +432,15 @@ Az útvonal automatikusan mentésre kerül a szerkesztő bezárásakor és felü <text name="CP_customFieldManager_confirm_delete" text="Törlöd a %s szántóföldet?"/> <text name="CP_customFieldManager_delete" text="Szántóföld törlés"/> <text name="CP_customFieldManager_rename" text="Szántóföld átnevezés"/> - <text name="CP_customFieldManager_rename_error" text="Custom field name %s already in use."/> + <text name="CP_customFieldManager_rename_error" text="Egyedi mező név %s már használatban van."/> <text name="CP_customFieldManager_edit" text="Egyedi szántóföld szerkesztése"/> <text name="CP_customFieldManager_draw" text="Szántóföld rajzolás"/> <text name="CP_customFieldManager_draw_header" text="Jobb klikkel tudod a szántóföld határait megrajzolni"/> <text name="CP_customFieldManager_save" text="Szántóföld mentés"/> <text name="CP_controllerGui_startRecording" text="Szántóföld-határ rögzítés indítása"/> <text name="CP_controllerGui_stopRecording" text="Szántóföld-határ rögzítés leállítása"/> - <text name="CP_controllerGui_pauseRecording" text="Pause custom field recording."/> - <text name="CP_controllerGui_unpauseRecording" text="Unpause custom field recording."/> + <text name="CP_controllerGui_pauseRecording" text="Egyedi mező rögzítés szüneteltetése."/> + <text name="CP_controllerGui_unpauseRecording" text="Egyedi mező rögzítés folytatása."/> <!----> <!--Help menu--> <!----> @@ -807,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_it.xml b/translations/translation_it.xml index 037b2c050..10c9b2f18 100644 --- a/translations/translation_it.xml +++ b/translations/translation_it.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Il veicolo è troppo lontano dal campo per partire."/> <text name="CP_error_no_bunkerSilo_found" text="Nessun bunker silos trovato."/> <text name="CP_error_no_heap_found" text="Nessun cumulo trovato."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Posizione di caricamento"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Posizione di caricamento"/> <text name="CP_siloLoader_fillLevelProgress" text="Avanzamento: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Impostazioni del lavoratore del bunker silo"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Larghezza di lavoro"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Larghezza di lavoro. Valore: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -813,6 +819,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Autista di pale gommate"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml index 0c6ca5560..a03ff17b0 100644 --- a/translations/translation_jp.xml +++ b/translations/translation_jp.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -807,6 +813,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_kr.xml b/translations/translation_kr.xml index 57c0a3b3b..c140ab75f 100644 --- a/translations/translation_kr.xml +++ b/translations/translation_kr.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml index 1ece0e1c3..336ebc710 100644 --- a/translations/translation_nl.xml +++ b/translations/translation_nl.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -807,6 +813,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_no.xml b/translations/translation_no.xml index adb13ff92..19491c6fe 100644 --- a/translations/translation_no.xml +++ b/translations/translation_no.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml index ed0172fc2..7e6a75d8d 100644 --- a/translations/translation_pl.xml +++ b/translations/translation_pl.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Pojazd jest zbyt daleko pola, aby ruszyć."/> <text name="CP_error_no_bunkerSilo_found" text="Nie znaleziono silosu."/> <text name="CP_error_no_heap_found" text="Nie znaleziono pryzmy."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Pozycja załadunku"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Pozycja załadunku"/> <text name="CP_siloLoader_fillLevelProgress" text="Postęp: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Ustawienia pracownika silosu"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Szerokość robocza"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Szerokość robocza. Zakres: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -805,6 +811,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml index 8010bca72..d9be37c23 100644 --- a/translations/translation_pt.xml +++ b/translations/translation_pt.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="O veículo está muito longe para começar o trabalho no campo."/> <text name="CP_error_no_bunkerSilo_found" text="Não há silo à vista."/> <text name="CP_error_no_heap_found" text="Não encontrou nenhum silo."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Posição para carregar"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Posição para carregarn"/> <text name="CP_siloLoader_fillLevelProgress" text="Progresso: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Configurações de Compactar no Silo"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Largura Trabalho"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Largura Trabalho. Valor: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -802,6 +808,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_ro.xml b/translations/translation_ro.xml index d7acebfbc..9dadefaa9 100644 --- a/translations/translation_ro.xml +++ b/translations/translation_ro.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="No silo found."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloworker settings"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Work width"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Work width. Value: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml index f674767a3..cdcbb3c45 100644 --- a/translations/translation_ru.xml +++ b/translations/translation_ru.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Транспортное средство слишком далеко от поля, чтобы стартовать."/> <text name="CP_error_no_bunkerSilo_found" text="Силосную яму не найдено."/> <text name="CP_error_no_heap_found" text="Не найдена куча."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Позиция загрузки"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Позиция загрузки"/> <text name="CP_siloLoader_fillLevelProgress" text="Прогресс: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Настройки работника в силосной яме"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Рабочая ширина"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Рабочая ширина. Значение: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -810,6 +816,16 @@ Courseplay способен прессовать или распределять Чтобы начать необходимо установить погрузочный маркер в меню ИИ. Маркер обнаруживает кучи или бункеры и выделяет их на карте. Теперь можно запускать помощника. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_sv.xml b/translations/translation_sv.xml index f8f5e745b..800370f66 100644 --- a/translations/translation_sv.xml +++ b/translations/translation_sv.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Fordonet är för långt bort från fältet för att starta."/> <text name="CP_error_no_bunkerSilo_found" text="Ingen silo hittades."/> <text name="CP_error_no_heap_found" text="Ingen silo hittades."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Lastningspositionen"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Lastningspositionen"/> <text name="CP_siloLoader_fillLevelProgress" text="Framsteg: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Siloarbetarinställningar"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="Arbetsbredd"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Arbetsbredd. Värde: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -805,6 +811,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings--> diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml index 9c0a59b6d..ea6aa0325 100644 --- a/translations/translation_tr.xml +++ b/translations/translation_tr.xml @@ -25,6 +25,7 @@ <text name="CP_error_vehicle_too_far_away_from_field" text="Vehicle is too far away from the field to start."/> <text name="CP_error_no_bunkerSilo_found" text="Silo bulunamadı."/> <text name="CP_error_no_heap_found" text="No silo found."/> + <text name="CP_error_no_unload_trigger_found" text="No unloading point found."/> <!----> <!--AI job parameters--> <!----> @@ -94,6 +95,9 @@ <text name="CP_siloLoaderJobParameters_subTitle_loadPosition" text="Loading position"/> <text name="CP_siloLoaderJobParameters_loadPosition_title" text="Loading position"/> <text name="CP_siloLoader_fillLevelProgress" text="Progress: "/> + <text name="CP_siloLoaderJobParameters_subTitle_unloading" text="Unloading at:"/> + <text name="CP_siloLoaderJobParameters_unloadAt_trailer" text="Trailer"/> + <text name="CP_siloLoaderJobParameters_unloadAt_unloadTrigger" text="Unload station"/> <!----> <!--AI jobs--> <!----> @@ -178,6 +182,8 @@ <text name="CP_vehicle_setting_subTitle_bunkerSilo" text="Silo işçi ayarları"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_title" text="İle çalışmak"/> <text name="CP_vehicle_setting_bunkerSiloWorkWidth_tooltip" text="Birlikte çalışın. Değer: 0 - 50"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_title" text="Shovel loading offset. Value: -1 - 1"/> + <text name="CP_vehicle_setting_loadingShovelHeightOffset_tooltip" text="Shovel loading offset. Value: -1 - 1"/> <!----> <!--Vehicle settings speed--> <!----> @@ -807,6 +813,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. +"/> + <text name="CP_help_page_shovelLoader_title" text="Wheelloader Driver"/> + <text name="CP_help_page_shovelLoaderGeneral_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderBasic_text" text=" +WIP +"/> + <text name="CP_help_page_shovelLoaderTrigger_text" text=" +WIP "/> <!----> <!--Input bindings-->