LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

AISystem

Functions

addJob

Description
Definition
addJob()
Code
384function AISystem:addJob(job)
385 table.insert(self.activeJobs, job)
386end

addJobVehicle

Description
Definition
addJobVehicle()
Code
493function AISystem:addJobVehicle(vehicle)
494 table.addElement(self.activeJobVehicles, vehicle)
495end

addObstacle

Description
Definition
addObstacle()
Code
574function AISystem:addObstacle(node, centerOffsetX, centerOffsetY, centerOffsetZ, sizeX, sizeY, sizeZ, brakeAcceleration)
575 if self.isServer and self.navigationMap ~= nil and node ~= nil then
576 centerOffsetX = centerOffsetX or 0
577 centerOffsetY = centerOffsetY or 0
578 centerOffsetZ = centerOffsetZ or 0
579 sizeX = sizeX or 0
580 sizeY = sizeY or 0
581 sizeZ = sizeZ or 0
582 brakeAcceleration = brakeAcceleration or 0
583
584 addVehicleNavigationPhysicsObstacle(self.navigationMap, node, centerOffsetX, centerOffsetY, centerOffsetZ, sizeX, sizeY, sizeZ, brakeAcceleration)
585 end
586end

addRoadSpline

Description
Definition
addRoadSpline()
Code
180function AISystem:addRoadSpline(spline, maxWidth, maxTurningRadius)
181 if self.isServer and spline ~= nil then
182 if self.navigationMap ~= nil then
183 local defaultMaxWidth = maxWidth or self.defaultVehicleMaxWidth
184 local defaultMaxTurningRadius = maxTurningRadius or self.defaultVehicleMaxTurningRadius
185 addRoadsToVehicleNavigationMap(self.navigationMap, spline, defaultMaxWidth, defaultMaxTurningRadius)
186 table.addElement(self.roadSplines, spline)
187
188 -- directly set splines visible, e.g. if placeable was newly placed or reloaded while spline debug active
189 if self.splinesVisible then
190 setVisibility(spline, self.splinesVisible)
191 I3DUtil.interateRecursively(spline, function(node)
192 setVisibility(node, self.splinesVisible)
193 end)
194 end
195 else
196 table.addElement(self.delayedRoadSplines, spline)
197 end
198 end
199end

consoleCommandAICheckSplineInterference

Description
walk across all AI splines and check if they interfere with any objects
Definition
consoleCommandAICheckSplineInterference()
Code
937function AISystem:consoleCommandAICheckSplineInterference(stepLength, heightOffset, defaultSplineWidth)
938 local usage = "gsAISplineCheckInterference [stepLength] [heightOffset] [defaultSplineWidth]\nUse 'gsDebugManagerClearElements' to remove boxes."
939
940 self.debugLastPos = {}
941 self:debugDeleteInterferences()
942 self.debugInterferencePositions = {}
943 self.debugInterferences = {}
944
945 stepLength = tonumber(stepLength) or 5
946 heightOffset = tonumber(heightOffset) or 0.2
947 defaultSplineWidth = tonumber(defaultSplineWidth) or self.defaultVehicleMaxWidth
948 local collisionMask = CollisionMask.ALL - CollisionFlag.TERRAIN
949 local overlapFactor = 0.9 -- slightly overlap to reduce change of unchecked areas in curves
950
951 Logging.info("Checking interference: stepLength=%.2f heightOffset=%.2f defaultSplineWidth=%.2f", stepLength, heightOffset, defaultSplineWidth)
952
953 local numSplines = 0
954 local testPosition = createTransformGroup("testPosition")
955 local splineMaxWidth -- save spline max width as they might be inherited
956
957 local function checkInterference(spline)
958 numSplines = numSplines + 1
959 local splineWidth = getUserAttribute(spline, "maxWidth") or splineMaxWidth or defaultSplineWidth
960 local splineLength = getSplineLength(spline)
961 local stepSize = stepLength*overlapFactor / splineLength
962 for offset=0, 1+stepSize, stepSize do
963 local clampedSplineTime = MathUtil.clamp(offset, 0, 1)
964 local wx, wy, wz = getSplinePosition(spline, clampedSplineTime)
965 local dx, dy, dz = getSplineDirection(spline, clampedSplineTime)
966 setWorldTranslation(testPosition, wx, wy, wz)
967 setDirection(testPosition, dx, dy, dz, 0, 1, 0)
968
969 local rx, ry, rz = getWorldRotation(testPosition)
970 local sx, sy, sz = splineWidth/2, (self.vehicleMaxHeight/2)-heightOffset/2, stepLength/2
971 wy = wy + sy + heightOffset -- offset height by extent and 20 cm to avoid clipping with bridges
972
973 self.debugLastPos = {
974 rx=rx, ry=ry, rz=rz,
975 sx=sx, sy=sy, sz=sz,
976 wx=wx, wy=wy, wz=wz,
977 spline=spline,
978 }
979 overlapBox(wx, wy, wz, rx, ry, rz, sx, sy, sz, "splineInterferenceOverlapCallback", self, collisionMask, true, true, true, false)
980 end
981 end
982
983 local function filterSplines(node, depth)
984 if I3DUtil.getIsSpline(node) then
985 checkInterference(node)
986 else
987 -- save spline index for transform groups as it's inherited to child splines
988 splineMaxWidth = getUserAttribute(node, "maxWidth")
989 end
990 end
991
992 for _, aiSpline in ipairs(self.roadSplines) do
993 I3DUtil.interateRecursively(aiSpline, filterSplines)
994 end
995
996 delete(testPosition)
997 local numInterferences = table.size(self.debugInterferences)
998 self.debugInterferences = nil
999 self.debugLastPos = nil
1000
1001 return string.format("Checked %d splines, found %d interferences\n%s", numSplines, numInterferences, usage)
1002end

consoleCommandAICostmapExport

Description
Definition
consoleCommandAICostmapExport()
Code
867function AISystem:consoleCommandAICostmapExport(imageFormatStr)
868 if not self.isServer then
869 return "gsAICostsExport is a server-only command"
870 end
871
872 if g_currentMission.missionInfo.savegameDirectory == nil then
873 return "Error: Savegame directory does not exist yet, please save the game first"
874 end
875
876 local imageFormat = BitmapUtil.FORMAT.PIXELMAP
877 if imageFormatStr ~= nil then
878 imageFormat = BitmapUtil.FORMAT[imageFormatStr:upper()]
879 if imageFormat == nil then
880 Logging.error("Unknown image format '%s'. Available formats: %s", imageFormatStr, table.concatKeys(BitmapUtil.FORMAT, ", "))
881 return "Error: Costmap export failed"
882 end
883 end
884
885 local terrainSizeHalf = self.mission.terrainSize * 0.5
886 local cellSizeHalf = self.cellSizeMeters / 2
887 local imageSize = self.mission.terrainSize
888
889 local isGreymap = imageFormat == BitmapUtil.FORMAT.GREYMAP
890 local colorBlocking = self.debug.colors.blocking
891
892 local function getPixelsIterator()
893 local stepZ = -terrainSizeHalf + cellSizeHalf
894 local stepX = -terrainSizeHalf + cellSizeHalf
895
896 local pixel = {0, 0, 0}
897
898 return function()
899 if stepZ > (terrainSizeHalf - cellSizeHalf) then
900 return nil
901 end
902
903 local worldPosY = getTerrainHeightAtWorldPos(self.mission.terrainRootNode, stepX, 0, stepZ)
904 local cost, isBlocking = getVehicleNavigationMapCostAtWorldPos(self.navigationMap, stepX, worldPosY, stepZ)
905
906 if isGreymap then
907 pixel[1] = cost
908 else
909 if isBlocking then
910 pixel[1], pixel[2], pixel[3] = colorBlocking[1], colorBlocking[2], colorBlocking[3]
911 else
912 pixel[1], pixel[2], pixel[3] = Utils.getGreenRedBlendedColor(cost / AISystem.COSTMAP_MAX_VALUE)
913 end
914 pixel[1], pixel[2], pixel[3] = pixel[1]*255, pixel[2]*255, pixel[3]*255
915 end
916
917 if stepX < (terrainSizeHalf - self.cellSizeMeters) then
918 stepX = stepX + self.cellSizeMeters
919 else
920 stepX = -terrainSizeHalf + cellSizeHalf
921 stepZ = stepZ + self.cellSizeMeters
922 end
923
924 return pixel
925 end
926 end
927
928 if not BitmapUtil.writeBitmapToFileFromIterator(getPixelsIterator, imageSize, imageSize, g_currentMission.missionInfo.savegameDirectory .. "/navigationMap", imageFormat) then
929 return "Error: Costmap export failed"
930 end
931
932 return "Finished costmap export"
933end

consoleCommandAIEnableDebug

Description
Definition
consoleCommandAIEnableDebug()
Code
730function AISystem:consoleCommandAIEnableDebug()
731 self.debugEnabled = not self.debugEnabled
732 return "debugEnabled=" .. tostring(self.debugEnabled)
733end

consoleCommandAISetAreaDirty

Description
Definition
consoleCommandAISetAreaDirty()
Code
853function AISystem:consoleCommandAISetAreaDirty(width)
854 if not self.isServer then
855 return "gsAICostsUpdate is a server-only command"
856 end
857
858 local x,_,z = getWorldTranslation(getCamera(0))
859 width = MathUtil.clamp(tonumber(width) or 30, 2, 1000)
860 local halfWidth = width / 2
861 self:setAreaDirty(x-halfWidth, z-halfWidth, x+halfWidth, z+halfWidth)
862 return string.format("Updated costmap in a %dx%d area", width, width)
863end

consoleCommandAISetLastTarget

Description
Definition
consoleCommandAISetLastTarget()
Code
671function AISystem:consoleCommandAISetLastTarget()
672 if not self.isServer then
673 return "gsAISetLastTarget is a server-only command"
674 end
675
676 if self.debug.target ~= nil then
677 local x = self.debug.target.x
678 local y = self.debug.target.y
679 local z = self.debug.target.z
680 local dirX = self.debug.target.dirX
681 local dirY = self.debug.target.dirY
682 local dirZ = self.debug.target.dirZ
683
684 local marker = self.debug.marker
685 if marker ~= nil then
686 setWorldTranslation(marker, x, y, z)
687 setDirection(marker, dirX, dirY, dirZ, 0, 1, 0)
688 end
689 return "Set target to last target"
690 else
691 return "No last target found"
692 end
693end

consoleCommandAISetTarget

Description
Definition
consoleCommandAISetTarget()
Code
624function AISystem:consoleCommandAISetTarget(offsetX, offsetZ)
625 if not self.isServer then
626 return "gsAISetTarget is a server-only command"
627 end
628
629 local x, y, z = 0, 0, 0
630 local dirX, dirY, dirZ, _ = 1, 0, 0, nil
631 if self.mission.controlPlayer then
632 if self.mission.player ~= nil and self.mission.player.isControlled and self.mission.player.rootNode ~= nil and self.mission.player.rootNode ~= 0 then
633 x, y, z = getWorldTranslation(self.mission.player.rootNode)
634 dirX, dirZ = -math.sin(self.mission.player.rotY), -math.cos(self.mission.player.rotY)
635 end
636 elseif self.mission.controlledVehicle ~= nil then
637 x, y, z = getWorldTranslation(self.mission.controlledVehicle.rootNode)
638 dirX, _, dirZ = localDirectionToWorld(self.mission.controlledVehicle.rootNode, 0, 0, 1)
639 else
640 x, y, z = getWorldTranslation(getCamera())
641 dirX, _, dirZ = localDirectionToWorld(getCamera(), 0, 0, -1)
642 end
643
644 local normX, _, normZ = MathUtil.crossProduct(0, 1, 0, dirX, dirY, dirZ)
645
646 offsetX = tonumber(offsetX) or 0
647 offsetZ = tonumber(offsetZ) or 0
648
649 x = x + dirX * offsetZ + normX * offsetX
650 z = z + dirZ * offsetZ + normZ * offsetX
651
652 self.debug.target = {}
653 self.debug.target.x = x
654 self.debug.target.y = y
655 self.debug.target.z = z
656 self.debug.target.dirX = dirX
657 self.debug.target.dirY = dirY
658 self.debug.target.dirZ = dirZ
659
660 local marker = self.debug.marker
661 if marker ~= nil then
662 setWorldTranslation(marker, x, y, z)
663 setDirection(marker, dirX, dirY, dirZ, 0, 1, 0)
664 end
665
666 return "Set AI Target"
667end

consoleCommandAIShowCosts

Description
Definition
consoleCommandAIShowCosts()
Code
606function AISystem:consoleCommandAIShowCosts()
607 if not self.isServer then
608 return "gsAICostsShow is a server-only command"
609 end
610
611 self.debug.isCostRenderingActive = not self.debug.isCostRenderingActive
612
613 if self.debug.isCostRenderingActive then
614 self.mission:addDrawable(self)
615 return "showCosts=true"
616 else
617 self.mission:removeDrawable(self)
618 return "showCosts=false"
619 end
620end

consoleCommandAIShowObstacles

Description
Definition
consoleCommandAIShowObstacles()
Code
840function AISystem:consoleCommandAIShowObstacles()
841 if not self.isServer then
842 return "gsAIObstaclesShow is a server-only command"
843 end
844
845 self.mapDebugRenderingEnabled = not self.mapDebugRenderingEnabled
846 enableVehicleNavigationMapDebugRendering(self.navigationMap, self.mapDebugRenderingEnabled)
847
848 return "AIShowObstacles=" .. tostring(self.mapDebugRenderingEnabled)
849end

consoleCommandAIStart

Description
Definition
consoleCommandAIStart()
Code
697function AISystem:consoleCommandAIStart()
698 if not self.isServer then
699 return "Only available on server"
700 end
701
702 if self.mission.controlledVehicle == nil then
703 return "Please enter a vehicle first"
704 end
705
706 local target = self.debug.target
707 if target == nil then
708 return "Please set a target first"
709 end
710
711 local job = g_currentMission.aiJobTypeManager:createJob(AIJobType.GOTO)
712 local angle = MathUtil.getYRotationFromDirection(target.dirX, target.dirZ)
713
714 job.vehicleParameter:setVehicle(self.mission.controlledVehicle)
715 job.positionAngleParameter:setPosition(target.x, target.z)
716 job.positionAngleParameter:setAngle(angle)
717 job:setValues()
718
719 local success, errorMessage = job:validate(g_currentMission.player.farmId)
720 if success then
721 self:startJob(job, g_currentMission.player.farmId)
722 return "Started ai..."
723 else
724 return "Error: " .. tostring(errorMessage)
725 end
726end

consoleCommandAIToggleAINodeDebug

Description
Definition
consoleCommandAIToggleAINodeDebug()
Code
797function AISystem:consoleCommandAIToggleAINodeDebug()
798 self.stationsAINodesVisible = not self.stationsAINodesVisible
799
800 if self.stationsAINodesVisible then
801 self.stationsAINodesDebugElements = {}
802
803 for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do
804 if unloadingStation:isa(UnloadingStation) then
805 local x, _, _, _, unloadTrigger = unloadingStation:getAITargetPositionAndDirection(FillType.UNKNOWN)
806 if x ~= nil then
807 local text = "UnloadingStation: " .. unloadingStation:getName()
808 local gizmo = DebugGizmo.new():createWithNode(unloadTrigger.aiNode, text, nil, nil, nil, false, true)
809 g_debugManager:addPermanentElement(gizmo)
810 table.insert(self.stationsAINodesDebugElements, gizmo)
811 end
812 end
813 end
814
815 for _, loadingStation in pairs(g_currentMission.storageSystem:getLoadingStations()) do
816 local x, _, _, _, loadTrigger = loadingStation:getAITargetPositionAndDirection(FillType.UNKNOWN)
817 if x ~= nil then
818 local text = "LoadingStation: " .. loadingStation:getName()
819 local gizmo = DebugGizmo.new():createWithNode(loadTrigger.aiNode, text, nil, nil, nil, false, true)
820 g_debugManager:addPermanentElement(gizmo)
821 table.insert(self.stationsAINodesDebugElements, gizmo)
822 end
823 end
824 else
825 for _, gizmo in pairs(self.stationsAINodesDebugElements) do
826 g_debugManager:removePermanentElement(gizmo)
827 gizmo:delete()
828 end
829 self.stationsAINodesDebugElements = nil
830 end
831
832 if self.stationsAINodesVisible then
833 Logging.warning("Nodes in reloaded placeables are not updated automatically. Toggle this command again to update the station nodes if placeables were reloaded.")
834 end
835 return "AISystem.stationsAINodesVisible=" .. tostring(self.stationsAINodesVisible)
836end

consoleCommandAIToggleSplineVisibility

Description
Definition
consoleCommandAIToggleSplineVisibility()
Code
737function AISystem:consoleCommandAIToggleSplineVisibility()
738 self.splinesVisible = not self.splinesVisible
739
740 if self.splinesVisible then
741 self.splineDebugElements = {}
742 local debugMat = g_debugManager:getDebugMat()
743
744 -- collect all splines from AI system
745 for _, roadSplineOrTG in ipairs(self.roadSplines) do
746 if I3DUtil.getIsSpline(roadSplineOrTG) then
747 self.splineDebugElements[roadSplineOrTG] = true
748 end
749
750 -- iterate rec as node might be a TG with splines as children
751 I3DUtil.interateRecursively(roadSplineOrTG, function(node)
752 if I3DUtil.getIsSpline(node) then
753 self.splineDebugElements[node] = true
754 end
755 end)
756 end
757
758 -- set visible and create debug elements
759 for spline in pairs(self.splineDebugElements) do
760 DebugUtil.setNodeEffectivelyVisible(spline)
761 local r, g, b = unpack(DebugUtil.getDebugColor(spline))
762 setMaterial(spline, debugMat, 0)
763 setShaderParameter(spline, "color", r, g, b, 0, false)
764 setShaderParameter(spline, "alpha", 1, 0, 0, 0, false)
765
766 local debugElements = DebugUtil.getSplineDebugElements(spline)
767 self.splineDebugElements[spline] = debugElements
768 for elemName, debugElement in pairs(debugElements) do
769 if elemName == "splineAttributes" then
770 debugElement:setColor(r, g, b)
771 end
772 g_debugManager:addPermanentElement(debugElement)
773 end
774 end
775 else
776 -- remove existing debug elements
777 for spline, debugElements in pairs(self.splineDebugElements) do
778 for _, debugElement in pairs(debugElements) do
779 g_debugManager:removePermanentElement(debugElement)
780 end
781 if entityExists(spline) then
782 setVisibility(spline, false)
783 end
784 end
785 self.splineDebugElements = nil
786 end
787
788 if g_currentMission.trafficSystem ~= nil and g_currentMission.trafficSystem.rootNodeId ~= nil then
789 setVisibility(g_currentMission.trafficSystem.rootNodeId, self.splinesVisible)
790 end
791
792 return "AISystem.splinesVisible=" .. tostring(self.splinesVisible)
793end

debugDeleteInterferences

Description
Definition
debugDeleteInterferences()
Code
1025function AISystem:debugDeleteInterferences()
1026 if self.debugInterferencePositions ~= nil then
1027 for _, debugFunctionPair in ipairs(self.debugInterferencePositions) do
1028 g_debugManager:removePermanentFunction(debugFunctionPair)
1029 end
1030 end
1031end

delete

Description
Definition
delete()
Code
67function AISystem:delete()
68 --#debug log("AISystem:delete()")
69 if self.isServer then
70 for i=#self.activeJobs, 1, -1 do
71 local job = self.activeJobs[i]
72 self:stopJob(job, AIMessageErrorUnknown.new())
73 end
74 end
75
76 if self.navigationMap ~= nil then
77 delete(self.navigationMap)
78 self.navigationMap = nil
79 end
80
81 if self.debug ~= nil and self.debug.marker ~= nil then
82 delete(self.debug.marker)
83 end
84
85 self.mission:unregisterObjectToCallOnMissionStart(self)
86
87 removeConsoleCommand("gsAISetTarget")
88 removeConsoleCommand("gsAISetLastTarget")
89 removeConsoleCommand("gsAIStart")
90 removeConsoleCommand("gsAIEnableDebug")
91 removeConsoleCommand("gsAISplinesShow")
92 removeConsoleCommand("gsAISplinesCheckInterference")
93 removeConsoleCommand("gsAIStationsShow")
94 removeConsoleCommand("gsAIObstaclesShow")
95 removeConsoleCommand("gsAICostsShow")
96 removeConsoleCommand("gsAICostsUpdate")
97 removeConsoleCommand("gsAICostsExport")
98end

draw

Description
Definition
draw()
Code
525function AISystem:draw()
526 if self.debug.isCostRenderingActive then
527 local x,_,z = getWorldTranslation(getCamera(0))
528 if self.mission.controlledVehicle ~= nil then
529 local object = self.mission.controlledVehicle
530 if self.mission.controlledVehicle.selectedImplement ~= nil then
531 object = self.mission.controlledVehicle.selectedImplement.object
532 end
533 x,_,z = getWorldTranslation(object.components[1].node)
534 end
535
536 local cellSizeHalf = self.cellSizeMeters * 0.5
537 x = (math.floor(x / self.cellSizeMeters) * self.cellSizeMeters) + cellSizeHalf
538 z = (math.floor(z / self.cellSizeMeters) * self.cellSizeMeters) + cellSizeHalf
539
540 local range = 15 * self.cellSizeMeters
541 local terrainSizeHalf = self.mission.terrainSize * 0.5
542 local minX = math.max(x - range, -terrainSizeHalf + cellSizeHalf)
543 local minZ = math.max(z - range, -terrainSizeHalf + cellSizeHalf)
544 local maxX = math.min(x + range, terrainSizeHalf - cellSizeHalf)
545 local maxZ = math.min(z + range, terrainSizeHalf - cellSizeHalf)
546
547 for stepZ = minZ, maxZ, self.cellSizeMeters do
548 for stepX = minX, maxX, self.cellSizeMeters do
549
550 local worldPosX = stepX
551 local worldPosY = getTerrainHeightAtWorldPos(self.mission.terrainRootNode, stepX, 0, stepZ)
552 local worldPosZ = stepZ
553
554 local cost, isBlocking = getVehicleNavigationMapCostAtWorldPos(self.navigationMap, worldPosX, worldPosY, worldPosZ)
555 local color = self.debug.colors.default
556 if isBlocking then
557 color = self.debug.colors.blocking
558 else
559 local r, g, b = Utils.getGreenRedBlendedColor(cost / AISystem.COSTMAP_MAX_VALUE)
560
561 color[1] = r
562 color[2] = g
563 color[3] = b
564 end
565
566 Utils.renderTextAtWorldPosition(worldPosX, worldPosY, worldPosZ, string.format("%.1f", cost), getCorrectTextSize(0.015), 0, color)
567 end
568 end
569 end
570end

getActiveJobs

Description
Definition
getActiveJobs()
Code
396function AISystem:getActiveJobs()
397 return self.activeJobs
398end

getAgentStateName

Description
Definition
getAgentStateName()
Code
1035function AISystem.getAgentStateName(index)
1036 for k, v in pairs(AgentState) do
1037 if v == index then
1038 return k
1039 end
1040 end
1041
1042 return "UNKNOWN"
1043end

getAILimitedReached

Description
Definition
getAILimitedReached()
Code
505function AISystem:getAILimitedReached()
506 return #self.activeJobVehicles >= g_currentMission.maxNumHirables
507end

getJobById

Description
Definition
getJobById()
Code
481function AISystem:getJobById(jobId)
482 for _, job in ipairs(self.activeJobs) do
483 if job.jobId == jobId then
484 return job
485 end
486 end
487
488 return nil
489end

getJobByIndex

Description
Definition
getJobByIndex()
Code
463function AISystem:getJobByIndex(index)
464 return self.activeJobs[index]
465end

getNavigationMap

Description
Definition
getNavigationMap()
Code
317function AISystem:getNavigationMap()
318 return self.navigationMap
319end

getNavigationMapFilename

Description
Definition
getNavigationMapFilename()
Code
311function AISystem:getNavigationMapFilename()
312 return self.filename
313end

getNumActiveJobs

Description
Definition
getNumActiveJobs()
Code
390function AISystem:getNumActiveJobs()
391 return #self.activeJobs
392end

loadFromXMLFile

Description
Definition
loadFromXMLFile()
Code
260function AISystem:loadFromXMLFile(xmlFilename)
261 local xmlFile = XMLFile.load("aiSystemXML", xmlFilename)
262
263 local x = xmlFile:getFloat("aiSystem.debug.target#posX")
264 local y = xmlFile:getFloat("aiSystem.debug.target#posY")
265 local z = xmlFile:getFloat("aiSystem.debug.target#posZ")
266 local dirX = xmlFile:getFloat("aiSystem.debug.target#dirX")
267 local dirY = xmlFile:getFloat("aiSystem.debug.target#dirY")
268 local dirZ = xmlFile:getFloat("aiSystem.debug.target#dirZ")
269
270 if x ~= nil and y ~= nil and z ~= nil and dirX ~= nil and dirY ~= nil and dirZ ~= nil then
271 self.debug.target = {}
272 self.debug.target.x = x
273 self.debug.target.y = y
274 self.debug.target.z = z
275 self.debug.target.dirX = dirX
276 self.debug.target.dirY = dirY
277 self.debug.target.dirZ = dirZ
278 end
279
280 xmlFile:delete()
281end

loadMapData

Description
Load data on map load
Definition
loadMapData()
Return Values
booleantrueif loading was successful else false
Code
103function AISystem:loadMapData(xmlFile, missionInfo, baseDirectory)
104 if g_addCheatCommands then
105 if self.isServer then
106 addConsoleCommand("gsAISetTarget", "Sets AI Target", "consoleCommandAISetTarget", self)
107 addConsoleCommand("gsAISetLastTarget", "Sets AI Target to last position", "consoleCommandAISetLastTarget", self)
108 addConsoleCommand("gsAIStart", "Starts driving to target", "consoleCommandAIStart", self)
109 end
110 addConsoleCommand("gsAIEnableDebug", "Enables AI debugging", "consoleCommandAIEnableDebug", self)
111 addConsoleCommand("gsAISplinesShow", "Toggle AI system spline visibility", "consoleCommandAIToggleSplineVisibility", self)
112 addConsoleCommand("gsAISplinesCheckInterference", "Check if AI splines interfere with any objects", "consoleCommandAICheckSplineInterference", self)
113 addConsoleCommand("gsAIStationsShow", "Toggle AI system stations ai nodes visibility", "consoleCommandAIToggleAINodeDebug", self)
114 addConsoleCommand("gsAIObstaclesShow", "Shows the obstacles around the camera", "consoleCommandAIShowObstacles", self)
115 addConsoleCommand("gsAICostsShow", "Shows the costs per cell", "consoleCommandAIShowCosts", self)
116 addConsoleCommand("gsAICostsUpdate", "Update costmap given width around the camera", "consoleCommandAISetAreaDirty", self)
117 addConsoleCommand("gsAICostsExport", "Export costmap to image file", "consoleCommandAICostmapExport", self)
118 end
119
120 self.cellSizeMeters = 1
121 self.maxSlopeAngle = math.rad(15)
122 self.infoLayerName = "navigationCollision"
123 self.infoLayerChannel = 0
124 self.aiDrivableCollisionMask = CollisionFlag.AI_DRIVABLE
125 self.obstacleCollisionMask = CollisionFlag.STATIC_OBJECTS + CollisionFlag.AI_BLOCKING + CollisionFlag.STATIC_OBJECT
126 self.vehicleMaxHeight = 4
127 self.defaultVehicleMaxWidth = 6
128 self.defaultVehicleMaxTurningRadius = 20 -- TODO
129 self.isLeftHandTraffic = false
130
131 -- load map specific xml with custom settings
132 local relFilename = getXMLString(xmlFile, "map.aiSystem#filename")
133 if relFilename ~= nil then
134 local filepath = Utils.getFilename(relFilename, baseDirectory)
135 if filepath ~= nil then
136 local xmlFileAISystem = XMLFile.load("mapAISystem", filepath, AISystem.xmlSchema)
137 if xmlFileAISystem ~= nil then
138
139 self.maxSlopeAngle = xmlFileAISystem:getValue("aiSystem.maxSlopeAngle") or self.maxSlopeAngle
140 self.infoLayerName = xmlFileAISystem:getValue("aiSystem.blockedAreaInfoLayer#name") or self.infoLayerName
141 self.infoLayerChannel = xmlFileAISystem:getValue("aiSystem.blockedAreaInfoLayer#channel") or self.infoLayerChannel
142 self.vehicleMaxHeight = xmlFileAISystem:getValue("aiSystem.vehicleMaxHeight") or self.vehicleMaxHeight
143 self.isLeftHandTraffic = Utils.getNoNil(xmlFileAISystem:getValue("aiSystem.isLeftHandTraffic"), self.isLeftHandTraffic)
144
145 xmlFileAISystem:delete()
146 end
147 end
148 end
149
150 self.debugEnabled = g_isDevelopmentVersion
151 self.debug = {}
152 self.debug.target = nil
153 self.debug.isCostRenderingActive = false
154 self.debug.colors = {}
155 self.debug.colors.default = {0, 1, 0, 1}
156 self.debug.colors.blocking = {1, 0, 0, 1}
157
158 self.activeJobs = {}
159 self.jobsToRemove = {}
160
161 self.roadSplines = {}
162
163 g_i3DManager:loadI3DFileAsync("data/shared/aiMarker.i3d", false, false, self.onAIMarkerLoaded, self, nil)
164end

new

Description
Definition
new()
Code
37function AISystem.new(isServer, mission, customMt)
38 local self = setmetatable({}, customMt or AISystem_mt)
39
40 self.isServer = isServer
41 self.mission = mission
42 self.filename = "vehicleNavigationCostmap.dat"
43 self.navigationMap = nil
44
45 self.activeJobs = {}
46 self.activeJobVehicles = {}
47 self.delayedRoadSplines = {}
48
49 AISystem.xmlSchema = XMLSchema.new("aiSystem")
50 self:registerXMLPaths(AISystem.xmlSchema)
51
52 return self
53end

onAIMarkerLoaded

Description
Definition
onAIMarkerLoaded()
Code
168function AISystem:onAIMarkerLoaded(node, failedReason, args)
169 if node ~= 0 then
170 local marker = getChildAt(node, 0)
171 self.debug.marker = marker
172 link(getRootNode(), marker)
173
174 delete(node)
175 end
176end

onClientJoined

Description
Definition
onClientJoined()
Code
362function AISystem:onClientJoined(connection)
363 for _, job in ipairs(self.activeJobs) do
364 connection:sendEvent(AIJobStartEvent.new(job, job.startedFarmId))
365 end
366end

onCreateAIRoadSpline

Description
Definition
onCreateAIRoadSpline()
Code
27function AISystem.onCreateAIRoadSpline(_, node)
28 if node ~= nil and node ~= 0 then
29 local maxWidth = tonumber(getUserAttribute(node, "maxWidth"))
30 local maxTurningRadius = tonumber(getUserAttribute(node, "maxTurningRadius"))
31 g_currentMission.aiSystem:addRoadSpline(node, maxWidth, maxTurningRadius)
32 end
33end

onMissionStarted

Description
Definition
onMissionStarted()
Code
252function AISystem:onMissionStarted()
253 local worldSizeHalf = 0.5*self.mission.terrainSize
254 Logging.info("No vehicle navigation cost map found. Start scanning map...")
255 updateVehicleNavigationMap(self.navigationMap, -worldSizeHalf, worldSizeHalf, -worldSizeHalf, worldSizeHalf)
256end

onTerrainLoad

Description
Definition
onTerrainLoad()
Code
217function AISystem:onTerrainLoad(terrainNode)
218 if self.isServer then
219 self.navigationMap = createVehicleNavigationMap(self.cellSizeMeters, terrainNode, self.maxSlopeAngle, self.infoLayerName, self.infoLayerChannel, self.aiDrivableCollisionMask, self.obstacleCollisionMask, self.vehicleMaxHeight, self.isLeftHandTraffic)
220
221 if self.mission.trafficSystem ~= nil then
222 local roadSplineRootNodeId = self.mission.trafficSystem.rootNodeId
223 self:addRoadSpline(roadSplineRootNodeId)
224 end
225
226 for i=#self.delayedRoadSplines, 1, -1 do
227 local spline = table.remove(self.delayedRoadSplines, 1)
228 self:addRoadSpline(spline)
229 end
230
231 local missionInfo = self.mission.missionInfo
232 local loadFromSave = false
233 if missionInfo.isValid and missionInfo:getIsNavigationCollisionValid(self.mission) then
234 local path = missionInfo.savegameDirectory .. "/" .. self.filename
235 local success = loadVehicleNavigationCostMapFromFile(self.navigationMap, path)
236 if success then
237 Logging.info("Loaded navigation cost map from savegame")
238 loadFromSave = true
239 end
240 end
241
242 if not loadFromSave then
243 self.mission:registerObjectToCallOnMissionStart(self)
244 end
245 end
246
247 return true
248end

registerXMLPaths

Description
Definition
registerXMLPaths()
Code
57function AISystem:registerXMLPaths(schema)
58 schema:register(XMLValueType.ANGLE, "aiSystem.maxSlopeAngle", "Maximum terrain angle in degrees which is classified as drivable", 15)
59 schema:register(XMLValueType.STRING, "aiSystem.blockedAreaInfoLayer#name", "Map info layer name defining areas which are blocked for AI driving", "navigationCollision")
60 schema:register(XMLValueType.INT, "aiSystem.blockedAreaInfoLayer#channel", "Map info layer channel defining areas which are blocked for AI driving", 0)
61 schema:register(XMLValueType.FLOAT, "aiSystem.vehicleMaxHeight", "Maximum expected vehicle height used for generating the costmap, e.g. relevant for overhanging collisions", 4)
62 schema:register(XMLValueType.BOOL, "aiSystem.isLeftHandTraffic", "Map has left-hand traffic. This setting will only affect collision avoidance, traffic and ai splines need to be set up as left hand in map itself", false)
63end

removeJob

Description
Definition
removeJob()
Code
378function AISystem:removeJob(job)
379 table.insert(self.jobsToRemove, job)
380end

removeJobVehicle

Description
Definition
removeJobVehicle()
Code
499function AISystem:removeJobVehicle(vehicle)
500 table.removeElement(self.activeJobVehicles, vehicle)
501end

removeObstacle

Description
Definition
removeObstacle()
Code
590function AISystem:removeObstacle(node)
591 if self.isServer and self.navigationMap ~= nil and node ~= nil then
592 removeVehicleNavigationPhysicsObstacle(self.navigationMap, node)
593 end
594end

removeRoadSpline

Description
Definition
removeRoadSpline()
Code
203function AISystem:removeRoadSpline(spline)
204 if self.isServer and spline ~= nil then
205 if self.navigationMap ~= nil then
206 removeRoadsFromVehicleNavigationMap(self.navigationMap, spline)
207 setVisibility(spline, false)
208 table.removeElement(self.roadSplines, spline)
209 else
210 table.removeElement(self.delayedRoadSplines, spline)
211 end
212 end
213end

save

Description
Definition
save()
Code
285function AISystem:save(xmlFilename, usedModNames)
286 local xmlFile = XMLFile.create("aiSystemXML", xmlFilename, "aiSystem")
287 if xmlFile ~= nil then
288 local hasData = false
289 if self.debug.target ~= nil then
290 local target = self.debug.target
291 xmlFile:setFloat("aiSystem.debug.target#posX", target.x)
292 xmlFile:setFloat("aiSystem.debug.target#posY", target.y)
293 xmlFile:setFloat("aiSystem.debug.target#posZ", target.z)
294 xmlFile:setFloat("aiSystem.debug.target#dirX", target.dirX)
295 xmlFile:setFloat("aiSystem.debug.target#dirY", target.dirY)
296 xmlFile:setFloat("aiSystem.debug.target#dirZ", target.dirZ)
297
298 hasData = true
299 end
300
301 if hasData then
302 xmlFile:save()
303 end
304
305 xmlFile:delete()
306 end
307end

setAreaDirty

Description
Definition
setAreaDirty()
Code
370function AISystem:setAreaDirty(minX, maxX, minZ, maxZ)
371 if self.navigationMap ~= nil then
372 updateVehicleNavigationMap(self.navigationMap, minX, maxX, minZ, maxZ)
373 end
374end

setObstacleIsPassable

Description
Definition
setObstacleIsPassable()
Code
598function AISystem:setObstacleIsPassable(node, isPassable)
599 if self.isServer and self.navigationMap ~= nil and node ~= nil and setVehicleNavigationPhysicsObstacleIsPassable ~= nil then
600 setVehicleNavigationPhysicsObstacleIsPassable(self.navigationMap, node, isPassable)
601 end
602end

skipCurrentTask

Description
Definition
skipCurrentTask()
Code
447function AISystem:skipCurrentTask(job)
448-- if self.isServer then
449-- self:skipCurrentTaskInternal(job)
450-- else
451 g_client:getServerConnection():sendEvent(AIJobSkipTaskEvent.new(job))
452-- end
453end

skipCurrentTaskInternal

Description
Definition
skipCurrentTaskInternal()
Code
457function AISystem:skipCurrentTaskInternal(job)
458 job:skipCurrentTask()
459end

splineInterferenceOverlapCallback

Description
Definition
splineInterferenceOverlapCallback()
Code
1006function AISystem:splineInterferenceOverlapCallback(nodeId)
1007 if nodeId ~= 0 and nodeId ~= g_currentMission.terrainRootNode and not CollisionFlag.getHasFlagSet(nodeId, CollisionFlag.VEHICLE) and CollisionFlag.getHasFlagSet(nodeId, CollisionFlag.AI_BLOCKING) then
1008 local last = self.debugLastPos
1009
1010 local customWidth = getUserAttribute(nodeId, 'maxWidth')
1011 local customWidthText = customWidth and string.format(" (maxWidth UserAttribute: %.2f)", customWidth) or ""
1012
1013 Logging.info("found interference for spline '%s'%s with object '%s|%s' at %d %d %d", getName(last.spline), customWidthText, getName(getParent(nodeId)), getName(nodeId), last.wx, last.wy, last.wz)
1014
1015 local debugFunctionPair = {DebugUtil.drawOverlapBox, {last.wx, last.wy, last.wz, last.rx, last.ry, last.rz, last.sx, last.sy, last.sz}}
1016 table.insert(self.debugInterferencePositions, debugFunctionPair)
1017 g_debugManager:addPermanentFunction(debugFunctionPair)
1018
1019 self.debugInterferences[nodeId] = true
1020 end
1021end

startJob

Description
Definition
startJob()
Code
402function AISystem:startJob(job, startFarmId)
403 assert(self.isServer)
404 if self.isServer then
405 job:setId(AISystem.NEXT_JOB_ID)
406 AISystem.NEXT_JOB_ID = AISystem.NEXT_JOB_ID + 1
407
408 g_server:broadcastEvent(AIJobStartEvent.new(job, startFarmId))
409
410 self:startJobInternal(job, startFarmId)
411 end
412end

startJobInternal

Description
Definition
startJobInternal()
Code
416function AISystem:startJobInternal(job, startFarmId)
417 job:start(startFarmId)
418 self:addJob(job)
419
420 g_messageCenter:publish(MessageType.AI_JOB_STARTED, job, startFarmId)
421end

stopJob

Description
Definition
stopJob()
Code
425function AISystem:stopJob(job, aiMessage)
426 if self.isServer then
427 self:stopJobInternal(job, aiMessage)
428 g_server:broadcastEvent(AIJobStopEvent.new(job, aiMessage))
429 else
430 -- just send a request to the server.
431 -- server will broadcast the real job to all clients
432 g_client:getServerConnection():sendEvent(AIJobStopEvent.new(job, aiMessage))
433 end
434end

stopJobById

Description
Definition
stopJobById()
Code
469function AISystem:stopJobById(jobId, aiMessage, noEventSend)
470 local job = self:getJobById(jobId)
471 if job ~= nil then
472 self:stopJob(job, aiMessage, noEventSend)
473 return true
474 end
475
476 return false
477end

stopJobInternal

Description
Definition
stopJobInternal()
Code
438function AISystem:stopJobInternal(job, aiMessage)
439 job:stop(aiMessage)
440 self:removeJob(job)
441
442 g_messageCenter:publish(MessageType.AI_JOB_STOPPED, job, aiMessage)
443end

update

Description
Definition
update()
Code
323function AISystem:update(dt)
324 for i = #self.jobsToRemove, 1, -1 do
325 local job = self.jobsToRemove[i]
326 local jobId = job.jobId
327
328 table.removeElement(self.activeJobs, job)
329 table.remove(self.jobsToRemove, i)
330
331 g_messageCenter:publish(MessageType.AI_JOB_REMOVED, jobId)
332 end
333
334 for _, job in ipairs(self.activeJobs) do
335 job:update(dt)
336
337 if self.isServer and g_currentMission.isRunning then
338 local price = job:getPricePerMs()
339 if price > 0 then
340 local difficultyMultiplier = g_currentMission.missionInfo.buyPriceMultiplier
341 if GS_IS_MOBILE_VERSION then
342 difficultyMultiplier = difficultyMultiplier * 0.8
343 end
344
345 price = price * dt * difficultyMultiplier
346
347 g_currentMission:addMoney(-price, job.startedFarmId, MoneyType.AI, true)
348
349 local farm = g_farmManager:getFarmById(job.startedFarmId)
350 if farm ~= nil then
351 if farm:getBalance() + price < 0 then
352 self:stopJob(job, AIMessageErrorOutOfMoney.new())
353 end
354 end
355 end
356 end
357 end
358end