Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 94 additions & 67 deletions scripts/Course.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1467,31 +1467,56 @@ function Course:getProgress(ix)
return self.waypoints[ix].dToHere / self.length, ix, ix == #self.waypoints
end

local function saveWaypointsToXml(waypoints, xmlFile, key)
for i, wp in ipairs(waypoints) do
wp:setXmlValue(xmlFile, key, i)
---@param compact boolean skip waypoints between row start and end (as for straight rows, these can be regenerated
--- easily after the course is loaded)
local function saveWaypointsToXml(waypoints, xmlFile, key, compact)
local i = 1
for _, wp in ipairs(waypoints) do
if not compact or (not wp:getRowNumber() or wp:isRowStart() or wp:isRowEnd()) then
wp:setXmlValue(xmlFile, key, i)
i = i + 1
end
end
end

-- The idea is not to store the waypoints of a fieldwork row (as it is just a straight line, unless we use the baseline
-- edge feature of the generator), only the start and the end
-- of the row (turn end and turn start waypoints). We still need those intermediate
-- waypoints though when working so the PPC does not put the targets kilometers away, so after loading a course, these
-- points can be generated by this function
local function addIntermediateWaypoints(d, waypoints, rowStart, rowEnd)
local dx, dz = (rowEnd.x - rowStart.x) / d, (rowEnd.z - rowStart.z) / d
for n = 1, (d / CourseGenerator.cRowWaypointDistance) - 1 do
local newWp = Waypoint({})
newWp.x = rowStart.x + n * CourseGenerator.cRowWaypointDistance * dx
newWp.z = rowStart.z + n * CourseGenerator.cRowWaypointDistance * dz
newWp:copyRowData(rowStart)
table.insert(waypoints, newWp)
end
end

--- From XML -----------------------------------------------------------------------------------------------------------
local function createWaypointsFromXml(xmlFile, key)
local waypoints = {}
-- these are only saved for the row start waypoint, here we add them to all waypoints of the row
local rowNumber, leftSideWorked, rightSideWorked
local rowStart
xmlFile:iterate(key .. Waypoint.xmlKey, function(ix, wpKey)
table.insert(waypoints, Waypoint.createFromXmlFile(xmlFile, wpKey))
local last = waypoints[#waypoints].attributes
if last.rowStart then
rowNumber = last.rowNumber
leftSideWorked = last.leftSideWorked
rightSideWorked = last.rightSideWorked
elseif last.rowEnd then
rowNumber, leftSideWorked, rightSideWorked = nil, nil, nil
elseif rowNumber then
last.rowNumber = rowNumber
last.leftSideWorked = leftSideWorked
last.rightSideWorked = rightSideWorked
local wp = Waypoint.createFromXmlFile(xmlFile, wpKey)
if wp:isRowStart() then
rowStart = wp
elseif wp:isRowEnd() then
local d = wp:getDistanceFromOther(waypoints[#waypoints])
if waypoints[#waypoints]:isRowStart() and d > CourseGenerator.cRowWaypointDistance + 0.1 then
-- there is now intermediate waypoints between the row start and row end and they are further
-- apart than the row waypoint distance, add intermediate waypoints
addIntermediateWaypoints(d, waypoints, waypoints[#waypoints], wp)
end
rowStart = nil
elseif rowStart then
-- normal row waypoint, copy the row data from the row start waypoint
wp:copyRowData(rowStart)
end
table.insert(waypoints, wp)
end)
return waypoints
end
Expand All @@ -1504,44 +1529,49 @@ function Course:saveToXml(courseXml, courseKey)
CpUtil.setXmlValue(courseXml, courseKey .. '#headlandClockwise', self.headlandClockwise)
CpUtil.setXmlValue(courseXml, courseKey .. '#islandHeadlandClockwise', self.islandHeadlandClockwise)
courseXml:setValue(courseKey .. '#wasEdited', self.editedByCourseEditor)
saveWaypointsToXml(self.waypoints, courseXml, courseKey)
courseXml:setValue(courseKey .. '#compacted', self.compacted)
if self.nVehicles > 1 then
self.multiVehicleData:setXmlValue(courseXml, courseKey)
self.multiVehicleData:setXmlValue(courseXml, courseKey, self.compacted)
else
-- only write the current waypoints if we are not a multi-vehicle course
saveWaypointsToXml(self.waypoints, courseXml, courseKey, self.compacted)
end
end

---@param vehicle table
---@param courseXml XmlFile
---@param courseKey string key to the course in the XML
function Course.createFromXml(vehicle, courseXml, courseKey)
local name = courseXml:getValue(courseKey .. '#name')
local workWidth = courseXml:getValue(courseKey .. '#workWidth')
local numberOfHeadlands = courseXml:getValue(courseKey .. '#numHeadlands')
local multiTools = courseXml:getValue(courseKey .. '#multiTools')
local nVehicles = courseXml:getValue(courseKey .. '#nVehicles')
local headlandClockwise = courseXml:getValue(courseKey .. '#headlandClockwise')
local islandHeadlandClockwise = courseXml:getValue(courseKey .. '#islandHeadlandClockwise')
local wasEdited = courseXml:getValue(courseKey .. '#wasEdited', false)
local waypoints = createWaypointsFromXml(courseXml, courseKey)
if #waypoints == 0 then
CpUtil.debugVehicle(CpDebug.DBG_COURSES, vehicle, 'No waypoints loaded, trying old format')
courseXml:iterate(courseKey .. '.waypoints' .. Waypoint.xmlKey, function(ix, key)
local d
d = CpUtil.getXmlVectorValues(courseXml:getString(key))
table.insert(waypoints, Waypoint.initFromXmlFileLegacyFormat(d, ix))
end)
end
local course = Course(vehicle, waypoints)
course.name = name
course.workWidth = workWidth
course.numberOfHeadlands = numberOfHeadlands
course.nVehicles = nVehicles or 1
course.headlandClockwise = headlandClockwise
course.islandHeadlandClockwise = islandHeadlandClockwise
course.editedByCourseEditor = wasEdited
if nVehicles and nVehicles > 1 then
local course = Course(vehicle, {})
course.name = courseXml:getValue(courseKey .. '#name')
course.workWidth = courseXml:getValue(courseKey .. '#workWidth')
course.numberOfHeadlands = courseXml:getValue(courseKey .. '#numHeadlands')
course.nVehicles = courseXml:getValue(courseKey .. '#nVehicles', 1)
course.headlandClockwise = courseXml:getValue(courseKey .. '#headlandClockwise')
course.islandHeadlandClockwise = courseXml:getValue(courseKey .. '#islandHeadlandClockwise')
course.editedByCourseEditor = courseXml:getValue(courseKey .. '#compacted', false)
course.compacted = courseXml:getValue(courseKey .. '#compacted', false)
course.waypoints = {}
if not course.nVehicles or course.nVehicles == 1 then
-- TODO: not nVehicles for backwards compatibility, remove later
-- for multi-vehicle courses, we load the multi-vehicle data and restore the current course
-- from there, so we don't need to write the same course twice in the savegame
course.waypoints = createWaypointsFromXml(courseXml, courseKey)
if #course.waypoints == 0 then
CpUtil.debugVehicle(CpDebug.DBG_COURSES, vehicle, 'No waypoints loaded, trying old format')
courseXml:iterate(courseKey .. '.waypoints' .. Waypoint.xmlKey, function(ix, key)
local d
d = CpUtil.getXmlVectorValues(courseXml:getString(key))
table.insert(course.waypoints, Waypoint.initFromXmlFileLegacyFormat(d, ix))
end)
end
end
if course.nVehicles and course.nVehicles > 1 then
course.multiVehicleData = Course.MultiVehicleData.createFromXmlFile(courseXml, courseKey)
course:setPosition(course.multiVehicleData:getPosition())
vehicle:getCpLaneOffsetSetting():setValue(course.multiVehicleData:getPosition())
else
course:enrichWaypointData()
end
CpUtil.debugVehicle(CpDebug.DBG_COURSES, vehicle, 'Course with %d waypoints loaded.', #course.waypoints)
return course
Expand All @@ -1566,32 +1596,26 @@ function Course:writeStream(vehicle, streamId, connection)
end

function Course.createFromStream(vehicle, streamId, connection)
local name = streamReadString(streamId)
local workWidth = streamReadFloat32(streamId)
local numberOfHeadlands = streamReadInt32(streamId)
local nVehicles = streamReadInt32(streamId)
local headlandClockwise = CpUtil.streamReadBool(streamId)
local islandHeadlandClockwise = CpUtil.streamReadBool(streamId)
local course = Course(vehicle, {})
course.name = streamReadString(streamId)
course.workWidth = streamReadFloat32(streamId)
course.numberOfHeadlands = streamReadInt32(streamId)
course.nVehicles = streamReadInt32(streamId)
course.headlandClockwise = CpUtil.streamReadBool(streamId)
course.islandHeadlandClockwise = CpUtil.streamReadBool(streamId)
local numWaypoints = streamReadInt32(streamId)
local wasEdited = streamReadBool(streamId)
local waypoints = {}
course.editedByCourseEditor = streamReadBool(streamId)
course.waypoints = {}
for ix = 1, numWaypoints do
table.insert(waypoints, Waypoint.createFromStream(streamId, ix))
table.insert(course.waypoints, Waypoint.createFromStream(streamId, ix))
end
local course = Course(vehicle, waypoints)
course.name = name
course.workWidth = workWidth
course.numberOfHeadlands = numberOfHeadlands
course.nVehicles = nVehicles
course.headlandClockwise = headlandClockwise
course.islandHeadlandClockwise = islandHeadlandClockwise
course.editedByCourseEditor = wasEdited
if nVehicles > 1 then
course.multiVehicleData = Course.MultiVehicleData.createFromStream(streamId, nVehicles)
if course.nVehicles > 1 then
course.multiVehicleData = Course.MultiVehicleData.createFromStream(streamId, course.nVehicles)
vehicle:getCpLaneOffsetSetting():setValue(course.multiVehicleData:getPosition())
end
course:enrichWaypointData()
CpUtil.debugVehicle(CpDebug.DBG_MULTIPLAYER, vehicle, 'Course with %d waypoints, %d vehicles loaded.',
#course.waypoints, nVehicles)
#course.waypoints, course.nVehicles)
return course
end

Expand All @@ -1604,13 +1628,16 @@ local function createWaypointsFromGeneratedPath(path)
return waypoints
end

---@param straightRows boolean rows are straight, so are fully defined by the row start and end waypoints, therefore
--- waypoints between them don't have to be saved (for better performance) as they can be restored when loading
function Course.createFromGeneratedCourse(vehicle, generatedCourse, workWidth, numberOfHeadlands, nVehicles,
headlandClockwise, islandHeadlandClockwise)
headlandClockwise, islandHeadlandClockwise, straightRows)
local waypoints = createWaypointsFromGeneratedPath(generatedCourse:getPath())
local course = Course(vehicle or g_currentMission.controlledVehicle, waypoints)
course.workWidth = workWidth
course.numberOfHeadlands = numberOfHeadlands
course.nVehicles = nVehicles
course.compacted = straightRows
course.headlandClockwise = headlandClockwise
course.islandHeadlandClockwise = islandHeadlandClockwise
if course.nVehicles > 1 then
Expand Down Expand Up @@ -1705,15 +1732,15 @@ function Course.MultiVehicleData.registerXmlSchema(schema, baseKey)
Waypoint.registerXmlSchema(schema, key)
end

function Course.MultiVehicleData:setXmlValue(xmlFile, baseKey)
function Course.MultiVehicleData:setXmlValue(xmlFile, baseKey, compacted)
local mvdKey = baseKey .. Course.MultiVehicleData.key
xmlFile:setValue(mvdKey .. '#selectedPosition', self.position)
local i = 0
-- save the course for each position in the group
for position, waypoints in pairs(self.waypoints) do
local posKey = string.format("%s%s(%d)", mvdKey, '.waypoints', i)
xmlFile:setValue(posKey .. '#position', position)
saveWaypointsToXml(waypoints, xmlFile, posKey)
saveWaypointsToXml(waypoints, xmlFile, posKey, compacted)
i = i + 1
end
end
Expand Down
10 changes: 10 additions & 0 deletions scripts/Waypoint.lua
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ function Waypoint:getDistanceFromPoint(x, z)
return MathUtil.getPointPointDistance(x, z, self.x, self.z)
end

function Waypoint:getDistanceFromOther(other)
return self:getDistanceFromPoint(other.x, other.z)
end

function Waypoint:getDistanceFromVehicle(vehicle)
local vx, _, vz = getWorldTranslation(vehicle:getAIDirectionNode() or vehicle.rootNode)
return self:getDistanceFromPoint(vx, vz)
Expand Down Expand Up @@ -282,6 +286,12 @@ function Waypoint:setOnConnectingPath(onConnectingPath)
self.attributes:setOnConnectingPath(onConnectingPath)
end

function Waypoint:copyRowData(other)
self.attributes.rowNumber = other.attributes.rowNumber
self.attributes.leftSideWorked = other.attributes.leftSideWorked
self.attributes.rightSideWorked = other.attributes.rightSideWorked
end

-- a node related to a waypoint
---@class WaypointNode
WaypointNode = CpObject()
Expand Down
4 changes: 2 additions & 2 deletions scripts/courseGenerator/CourseGeneratorInterface.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function CourseGeneratorInterface.generate(fieldPolygon,

local course = Course.createFromGeneratedCourse(vehicle, CourseGeneratorInterface.generatedCourse,
settings.workWidth:getValue(), numberOfHeadlands, settings.multiTools:getValue(),
settings.headlandClockwise:getValue(), settings.islandHeadlandClockwise:getValue())
settings.headlandClockwise:getValue(), settings.islandHeadlandClockwise:getValue(), not settings.useBaseLineEdge:getValue())
course:setFieldPolygon(fieldPolygon)
return true, course
end
Expand Down Expand Up @@ -167,7 +167,7 @@ function CourseGeneratorInterface.generateVineCourse(
#CourseGeneratorInterface.generatedCourse:getCenterPath())

local course = Course.createFromGeneratedCourse(nil, CourseGeneratorInterface.generatedCourse,
workWidth, 0, multiTools)
workWidth, 0, multiTools, true, true, true)
course:setFieldPolygon(fieldPolygon)
return true, course
end
1 change: 1 addition & 0 deletions scripts/specializations/CpCourseManager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function CpCourseManager.registerXmlSchemaValues(schema,baseKey)
schema:register(XMLValueType.BOOL, baseKey .. "#headlandClockwise", "Headlands are clockwise.")
schema:register(XMLValueType.BOOL, baseKey .. "#islandHeadlandClockwise", "Headlands around islands are clockwise.")
schema:register(XMLValueType.BOOL, baseKey .. "#wasEdited", "Was the course edited by the course editor.")
schema:register(XMLValueType.BOOL, baseKey .. "#compacted", "Rows are compacted, only start and end is saved.")
schema:register(XMLValueType.STRING, baseKey .. ".waypoints", "Course serialized waypoints") -- old save format
Waypoint.registerXmlSchema(schema, baseKey)
Course.MultiVehicleData.registerXmlSchema(schema, baseKey)
Expand Down