From 7ae1ce88fed0d612538170678d504013335ebf3c Mon Sep 17 00:00:00 2001
From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com>
Date: Tue, 12 Oct 2021 21:10:08 +0200
Subject: [PATCH] Reworked old code and some MP fixes.
- Moved all the old cp.workwidth to the Work width setting.
- Moved cp.laneNumber to the multi tools setting.
- Created a field edge path setting instead of cp.fieldEdge.
- Removed old cp.laneOffset
- Small MP toolOffsetX fix.
- Fixes custom fields in MP.
- Small work width fixe for mods.
- Enables automatic work width calculation for shovels.
---
AITurn.lua | 8 +-
BalerAIDriver.lua | 3 +-
BunkerSiloAIDriver.lua | 2 +-
CombineAIDriver.lua | 19 +-
CombineUnloadAIDriver.lua | 2 +-
CombineUnloadManager.lua | 4 +-
CpManager.lua | 1 +
Events/CustomFieldEvent.lua | 110 ++++++++
Events/SettingEvent.lua | 199 +++++++++----
FieldworkAIDriver.lua | 89 +++---
FillableFieldworkAIDriver.lua | 7 +-
GlobalSettings.lua | 9 +-
PlowAIDriver.lua | 8 +
ShieldAIDriver.lua | 2 +-
base.lua | 120 +-------
config/VehicleConfigurations.xml | 4 +
course-generator/CourseGeneratorSettings.lua | 226 ++++++++++++++-
course-generator/cp.lua | 2 -
course_management.lua | 2 -
courseplay.lua | 26 +-
courseplay_event.lua | 60 +---
fields.lua | 91 ++++++
gui/CourseGeneratorScreen.lua | 4 +-
hud.lua | 238 ++++++++--------
settings.lua | 279 +++----------------
start_stop.lua | 6 -
toolManager.lua | 191 +++++++++++--
translations/translation_br.xml | 2 +
translations/translation_cs.xml | 2 +
translations/translation_cz.xml | 2 +
translations/translation_de.xml | 2 +
translations/translation_en.xml | 2 +
translations/translation_es.xml | 2 +
translations/translation_fr.xml | 2 +
translations/translation_hu.xml | 2 +
translations/translation_it.xml | 2 +
translations/translation_jp.xml | 2 +
translations/translation_nl.xml | 2 +
translations/translation_pl.xml | 2 +
translations/translation_pt.xml | 2 +
translations/translation_ru.xml | 2 +
translations/translation_sl.xml | 2 +
turn.lua | 12 +-
43 files changed, 1029 insertions(+), 725 deletions(-)
create mode 100644 Events/CustomFieldEvent.lua
diff --git a/AITurn.lua b/AITurn.lua
index 73587ecb7..4c19a71a1 100644
--- a/AITurn.lua
+++ b/AITurn.lua
@@ -133,7 +133,8 @@ end
---@return boolean, number True if there's enough space to make a forward turn on the field. Also return the
---distance to reverse in order to be able to just make the turn on the field
function AITurn.canTurnOnField(turnContext, vehicle)
- local spaceNeededOnFieldForTurn = AIDriverUtil.getTurningRadius(vehicle) + vehicle.cp.workWidth / 2
+ local workWidth = vehicle.cp.courseGeneratorSettings.workWidth:get()
+ local spaceNeededOnFieldForTurn = AIDriverUtil.getTurningRadius(vehicle) + workWidth / 2
local distanceToFieldEdge = turnContext:getDistanceToFieldEdge(turnContext.vehicleAtTurnStartNode)
courseplay.debugVehicle(AITurn.debugChannel, vehicle, 'Space needed to turn on field %.1f m', spaceNeededOnFieldForTurn)
if distanceToFieldEdge then
@@ -635,9 +636,10 @@ function CombinePocketHeadlandTurn:generatePocketHeadlandTurn(turnContext)
local turnDiameter = self.vehicle.cp.settings.turnDiameter:get()
local turnRadius = turnDiameter / 2
-- this is how far we have to cut into the next headland (the position where the header will be after the turn)
- local offset = math.min(turnRadius + turnContext.frontMarkerDistance, self.vehicle.cp.workWidth)
+ local workWidth = vehicle.cp.courseGeneratorSettings.workWidth:get()
+ local offset = math.min(turnRadius + turnContext.frontMarkerDistance, workWidth)
local corner = turnContext:createCorner(self.vehicle, turnRadius)
- local d = -self.vehicle.cp.workWidth / 2 + turnContext.frontMarkerDistance
+ local d = -workWidth / 2 + turnContext.frontMarkerDistance
local wp = corner:getPointAtDistanceFromCornerStart(d + 2)
wp.speed = self.vehicle.cp.speeds.turn * 0.75
table.insert(cornerWaypoints, wp)
diff --git a/BalerAIDriver.lua b/BalerAIDriver.lua
index 9e41423ab..6989af814 100644
--- a/BalerAIDriver.lua
+++ b/BalerAIDriver.lua
@@ -61,7 +61,8 @@ function BalerAIDriver:startTurn(ix)
if self.isCombine then
self:debug('This vehicle is also a harvester, check check for special headland turns.')
self:setMarkers()
- self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self.vehicle.cp.workWidth,
+
+ self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self:getWorkWidth(),
self.frontMarkerDistance, self.backMarkerDistance,
self:getTurnEndSideOffset(), self:getTurnEndForwardOffset())
diff --git a/BunkerSiloAIDriver.lua b/BunkerSiloAIDriver.lua
index f4f3cd58b..0cf9149f3 100644
--- a/BunkerSiloAIDriver.lua
+++ b/BunkerSiloAIDriver.lua
@@ -206,7 +206,7 @@ function BunkerSiloAIDriver:isHeapSearchAllowed()
end
function BunkerSiloAIDriver:getWorkWidth()
- return self.vehicle.cp.workWidth
+ return self.courseGeneratorSettings.workWidth:get()
end
--- If true then the drive into silo course is reverse and
diff --git a/CombineAIDriver.lua b/CombineAIDriver.lua
index 48f3f375d..3a243c7b3 100644
--- a/CombineAIDriver.lua
+++ b/CombineAIDriver.lua
@@ -88,7 +88,7 @@ function CombineAIDriver:init(vehicle)
self:checkMarkers()
-- distance to keep to the right (>0) or left (<0) when pulling back to make room for the tractor
- self.pullBackRightSideOffset = math.abs(self.pipeOffsetX) - self.vehicle.cp.workWidth / 2 + 5
+ self.pullBackRightSideOffset = math.abs(self.pipeOffsetX) - self:getWorkWidth() / 2 + 5
self.pullBackRightSideOffset = self.pipeOnLeftSide and self.pullBackRightSideOffset or -self.pullBackRightSideOffset
-- should be at pullBackRightSideOffset to the right or left at pullBackDistanceStart
self.pullBackDistanceStart = self.settings.turnDiameter:get() --* 0.7
@@ -200,7 +200,7 @@ function CombineAIDriver:start(startingPoint)
-- we work with the traffic conflict detector and the proximity sensors instead
self:disableCollisionDetection()
self:fixMaxRotationLimit()
- local total, pipeInFruit = self.fieldworkCourse:setPipeInFruitMap(self.pipeOffsetX, self.vehicle.cp.workWidth)
+ local total, pipeInFruit = self.fieldworkCourse:setPipeInFruitMap(self.pipeOffsetX, self:getWorkWidth())
local ix = self.fieldworkCourse:getStartingWaypointIx(AIDriverUtil.getDirectionNode(self.vehicle), startingPoint)
self:shouldStrawSwathBeOn(ix)
self.fillLevelFullPercentage = self.normalFillLevelFullPercentage
@@ -580,9 +580,10 @@ function CombineAIDriver:checkFruit()
else
self.fruitLeft, self.fruitRight = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle)
end
- local x, _, z = localToWorld(self:getDirectionNode(), self.vehicle.cp.workWidth, 0, 0)
+ local workWidth = self:getWorkWidth()
+ local x, _, z = localToWorld(self:getDirectionNode(), workWidth, 0, 0)
self.fieldOnLeft = courseplay:isField(x, z, 1, 1)
- x, _, z = localToWorld(self:getDirectionNode(), -self.vehicle.cp.workWidth, 0, 0)
+ x, _, z = localToWorld(self:getDirectionNode(), -workWidth, 0, 0)
self.fieldOnRight = courseplay:isField(x, z, 1, 1)
self:debug('Fruit left: %.2f right %.2f, field on left %s, right %s',
self.fruitLeft, self.fruitRight, tostring(self.fieldOnLeft), tostring(self.fieldOnRight))
@@ -904,7 +905,7 @@ end
--- from node.
function CombineAIDriver:getAreaToAvoid()
if self:isWaitingForUnloadAfterPulledBack() then
- local xOffset = self.vehicle.cp.workWidth / 2
+ local xOffset = self:getWorkWidth() / 2
local zOffset = 0
local length = self.pullBackDistanceEnd
local width = self.pullBackRightSideOffset
@@ -1028,7 +1029,7 @@ function CombineAIDriver:startTurn(ix)
self:debug('Starting a combine turn.')
self:setMarkers()
- self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self.vehicle.cp.workWidth,
+ self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self:getWorkWidth(),
self.frontMarkerDistance, self.backMarkerDistance,
self:getTurnEndSideOffset(), self:getTurnEndForwardOffset())
@@ -1085,10 +1086,6 @@ function CombineAIDriver:getFieldworkCourse()
return self.fieldworkCourse
end
-function CombineAIDriver:getWorkWidth()
- return self.vehicle.cp.workWidth
-end
-
function CombineAIDriver:isChopper()
return self.combine:getFillUnitCapacity(self.combine.fillUnitIndex) > 10000000
end
@@ -1759,7 +1756,7 @@ end
function CombineAIDriver:addForwardProximitySensor()
self:setFrontMarkerNode(self.vehicle)
self.forwardLookingProximitySensorPack = WideForwardLookingProximitySensorPack(
- self.vehicle, self.ppc, self:getFrontMarkerNode(self.vehicle), self.proximitySensorRange, 1, self.vehicle.cp.workWidth)
+ self.vehicle, self.ppc, self:getFrontMarkerNode(self.vehicle), self.proximitySensorRange, 1, self:getWorkWidth())
end
--- Check the vehicle in the proximity sensor's range. If it is player driven, don't slow them down when hitting this
diff --git a/CombineUnloadAIDriver.lua b/CombineUnloadAIDriver.lua
index c44d61b82..263d70b52 100644
--- a/CombineUnloadAIDriver.lua
+++ b/CombineUnloadAIDriver.lua
@@ -1909,7 +1909,7 @@ function CombineUnloadAIDriver:startChopperTurn(ix)
self:setNewOnFieldState(self.states.HANDLE_CHOPPER_HEADLAND_TURN)
else
self.turnContext = TurnContext(self.followCourse, ix, self.aiDriverData,
- self.combineToUnload.cp.workWidth, self.frontMarkerDistance, self.backMarkerDistance, 0, 0)
+ self.combineToUnload.cp.driver:getWorkWidth(), self.frontMarkerDistance, self.backMarkerDistance, 0, 0)
local finishingRowCourse = self.turnContext:createFinishingRowCourse(self.vehicle)
self:startCourse(finishingRowCourse, 1)
self:setNewOnFieldState(self.states.HANDLE_CHOPPER_180_TURN)
diff --git a/CombineUnloadManager.lua b/CombineUnloadManager.lua
index cdbb8c9f8..0f1915840 100644
--- a/CombineUnloadManager.lua
+++ b/CombineUnloadManager.lua
@@ -455,7 +455,7 @@ end
function CombineUnloadManager:getPipeOffset(combine)
if self:getIsChopper(combine) then
- return (combine.cp.workWidth / 2) + 3
+ return (combine.cp.driver:getWorkWidth() / 2) + 3
elseif self:getIsCombine(combine) then
local pipeOffsetX, _ = combine.cp.driver:getPipeOffset()
return pipeOffsetX
@@ -531,7 +531,7 @@ function CombineUnloadManager:getOnFieldSituation(combine)
local rightDirX,_,rightDirZ = localDirectionToWorld(node, -1, 0, 0);
--set measurements of the box to check
local boxWidth = 3;
- local boxLength = 6 + combine.cp.workWidth/2;
+ local boxLength = 6 + combine.cp.driver:getWorkWidth()/2;
--to get the box centered divide the measurements by 2
local boxWidthCenter = boxWidth/2
diff --git a/CpManager.lua b/CpManager.lua
index 16d2316b5..59ae99b9e 100644
--- a/CpManager.lua
+++ b/CpManager.lua
@@ -235,6 +235,7 @@ function CpManager:deleteMap()
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- delete fields data and overlays
+ ---@class courseplay.fields.fieldData
courseplay.fields.fieldData = {};
courseplay.fields.curFieldScanIndex = 0;
courseplay.fields.allFieldsScanned = false;
diff --git a/Events/CustomFieldEvent.lua b/Events/CustomFieldEvent.lua
new file mode 100644
index 000000000..5972b50a3
--- /dev/null
+++ b/Events/CustomFieldEvent.lua
@@ -0,0 +1,110 @@
+--- This event is used to synchronize custom field,
+--- either from the server to a newly joined player
+--- or after a custom field was created to all user and server.
+---
+CustomFieldEvent = {}
+local CustomFieldEvent_mt = Class(CustomFieldEvent, Event)
+
+InitEventClass(CustomFieldEvent, "CustomFieldEvent")
+
+function CustomFieldEvent:emptyNew()
+ local self = Event:new(CustomFieldEvent_mt)
+ self.className = "CustomFieldEvent"
+ return self
+end
+
+--- Creates a new Event
+---@param field table
+function CustomFieldEvent:new(field)
+ self.field = field
+ self.debug("CustomFieldEvent:new()")
+ return self
+end
+
+--- Reads the serialized data on the receiving end of the event.
+function CustomFieldEvent:readStream(streamId, connection) -- wird aufgerufen wenn mich ein Event erreicht
+ self.field = CustomFieldEvent.readField(streamId)
+ self.debug("CustomFieldEvent:readStream()")
+ self.debug("Field name: %s, numPoints = %s ",tostring(self.field.name), tostring(self.field.numPoints))
+
+ self:run(connection);
+end
+
+--- Writes the serialized data from the sender.
+function CustomFieldEvent:writeStream(streamId, connection) -- Wird aufgrufen wenn ich ein event verschicke (merke: reihenfolge der Daten muss mit der bei readStream uebereinstimmen
+ self.debug("CustomFieldEvent:writeStream()")
+ self.debug("Field name: %s, numPoints = %s ",tostring(self.field.name), tostring(self.field.numPoints))
+ CustomFieldEvent.writeField(self.field,streamId)
+end
+
+--- Runs the event on the receiving end of the event.
+function CustomFieldEvent:run(connection) -- wir fuehren das empfangene event aus
+ self.debug("CustomFieldEvent:run()")
+ CpFieldUtil.saveFieldFromNetwork(self.field)
+ --- If the receiver was the client make sure every clients gets also updated.
+ if not connection:getIsServer() then
+ self.debug("Send CustomFieldEvent to clients")
+ g_server:broadcastEvent(CustomFieldEvent:new(self.field), nil, connection)
+ end
+end
+
+--- Sends an event to sync a custom created field.
+function CustomFieldEvent.sendEvent(field)
+ --- Only sync custom fields
+ if field.isCustom then
+ if g_server ~= nil then
+ CustomFieldEvent.debug("Send CustomFieldEvent to clients")
+ CustomFieldEvent.debug("Field name: %s",tostring(field.name))
+ g_server:broadcastEvent(CustomFieldEvent:new(field))
+ else
+ CustomFieldEvent.debug("Send CustomFieldEvent to server")
+ CustomFieldEvent.debug("Field name: %s",tostring(field.name))
+ g_client:getServerConnection():sendEvent(CustomFieldEvent:new(field))
+ end;
+ end
+end
+
+function CustomFieldEvent.debug(...)
+ courseplay.debugFormat(courseplay.DBG_MULTIPLAYER,...)
+end
+
+--- Writes a single custom field.
+function CustomFieldEvent.writeField(field,streamId)
+ local numPoints = field.numPoints or #field.points
+ streamDebugWriteString(streamId, field.name)
+ streamDebugWriteInt32(streamId, numPoints)
+ streamDebugWriteInt32(streamId, field.fieldNum)
+ streamDebugWriteInt32(streamId, field.dimensions.minX)
+ streamDebugWriteInt32(streamId, field.dimensions.maxX)
+ streamDebugWriteInt32(streamId, field.dimensions.minZ)
+ streamDebugWriteInt32(streamId, field.dimensions.maxZ)
+ for p = 1, numPoints do
+ streamDebugWriteFloat32(streamId, field.points[p].cx)
+ streamDebugWriteFloat32(streamId, field.points[p].cy)
+ streamDebugWriteFloat32(streamId, field.points[p].cz)
+ end
+
+end
+
+--- Reads a single custom field.
+function CustomFieldEvent.readField(streamId)
+ local field = {
+ dimensions = {},
+ points = {},
+ isCustom = true
+ }
+ field.name = streamDebugReadString(streamId)
+ field.numPoints = streamDebugReadInt32(streamId)
+ field.fieldNum = streamDebugReadInt32(streamId)
+ field.dimensions.minX = streamDebugReadInt32(streamId)
+ field.dimensions.maxX = streamDebugReadInt32(streamId)
+ field.dimensions.minZ = streamDebugReadInt32(streamId)
+ field.dimensions.maxZ = streamDebugReadInt32(streamId)
+ for p = 1, field.numPoints do
+ field.points[p] = {}
+ field.points[p].cx = streamDebugReadFloat32(streamId)
+ field.points[p].cy = streamDebugReadFloat32(streamId)
+ field.points[p].cz = streamDebugReadFloat32(streamId)
+ end
+ return field
+end
\ No newline at end of file
diff --git a/Events/SettingEvent.lua b/Events/SettingEvent.lua
index 19e8d9079..d32c26677 100644
--- a/Events/SettingEvent.lua
+++ b/Events/SettingEvent.lua
@@ -1,124 +1,218 @@
---- This event is used to synchronize Setting changes on run time.
+--- This event is used to synchronize global setting changes on run time.
--- Every setting event requires:
--- - a container name (parentName)
--- - a setting name
--- - an event index for the setting
--- - the value
---
-SettingEvent = {}
-local SettingEvent_mt = Class(SettingEvent, Event)
+GlobalSettingEvent = {}
+local GlobalSettingEvent_mt = Class(GlobalSettingEvent, Event)
-InitEventClass(SettingEvent, "SettingEvent")
+InitEventClass(GlobalSettingEvent, "GlobalSettingEvent")
-function SettingEvent:emptyNew()
- local self = Event:new(SettingEvent_mt)
- self.className = "SettingEvent"
+function GlobalSettingEvent:emptyNew()
+ local self = Event:new(GlobalSettingEvent_mt)
+ self.className = "GlobalSettingEvent"
return self
end
--- Creates a new Event
----@param vehicle table
---@param setting Setting
---@param eventData table
---@param value any
-function SettingEvent:new(vehicle,setting,eventData,value)
- self.vehicle = vehicle
+function GlobalSettingEvent:new(setting,eventData,value)
self.value = value
self.parentName,self.name,self.eventIx,self.writeFunc = self.decodeEventData(setting,eventData)
- self.debug("SettingEvent:new()")
+ self.debug("GlobalSettingEvent:new()")
return self
end
--- Reads the serialized data on the receiving end of the event.
-function SettingEvent:readStream(streamId, connection) -- wird aufgerufen wenn mich ein Event erreicht
+function GlobalSettingEvent:readStream(streamId, connection) -- wird aufgerufen wenn mich ein Event erreicht
self.parentName = streamReadString(streamId)
self.name = streamReadString(streamId)
self.eventIx = streamReadUInt8(streamId)
self.debug("Parent name: %s, Setting name: %s, eventIx: %d",self.parentName, self.name,self.eventIx)
- self.vehicle = nil
- if streamReadBool(streamId) then
- self.vehicle = NetworkUtil.getObject(streamReadInt32(streamId))
- self.debugVehicle(self.vehicle,"Vehicle setting")
- self.setting,self.eventData = self.encodeEventData(self.vehicle,self.parentName,self.name,self.eventIx)
- else
- self.debug("Global setting")
- self.setting,self.eventData = self.encodeEventData(nil,self.parentName,self.name,self.eventIx)
- end
-
+ self.setting,self.eventData = self.encodeEventData(self.parentName,self.name,self.eventIx)
if self.eventData.readFunc then
self.value = self.eventData.readFunc(streamId)
end
- self.debug("SettingEvent:readStream()")
+ self.debug("GlobalSettingEvent:readStream()")
self.debug("Parent name: %s, Setting name: %s, value: %s, eventIx: %d",self.parentName, self.name, tostring(self.value),self.eventIx)
self:run(connection);
end
--- Writes the serialized data from the sender.
-function SettingEvent:writeStream(streamId, connection) -- Wird aufgrufen wenn ich ein event verschicke (merke: reihenfolge der Daten muss mit der bei readStream uebereinstimmen
- self.debug("SettingEvent:writeStream()")
+function GlobalSettingEvent:writeStream(streamId, connection) -- Wird aufgrufen wenn ich ein event verschicke (merke: reihenfolge der Daten muss mit der bei readStream uebereinstimmen
+ self.debug("GlobalSettingEvent:writeStream()")
streamWriteString(streamId, self.parentName)
streamWriteString(streamId, self.name)
streamWriteUInt8(streamId,self.eventIx)
self.debug("Parent name: %s, Setting name: %s, value: %s, eventIx: %d",self.parentName, self.name, tostring(self.value),self.eventIx)
- if self.vehicle ~= nil then
- self.debugVehicle(self.vehicle,"Vehicle setting")
- streamWriteBool(streamId, true)
- streamWriteInt32(streamId, NetworkUtil.getObjectId(self.vehicle))
- else
- self.debug("Global setting")
- streamWriteBool(streamId, false)
- end
if self.writeFunc then
self.writeFunc(streamId, self.value)
end
end
--- Runs the event on the receiving end of the event.
-function SettingEvent:run(connection) -- wir fuehren das empfangene event aus
- self.debug("SettingEvent:run()")
+function GlobalSettingEvent:run(connection) -- wir fuehren das empfangene event aus
+ self.debug("GlobalSettingEvent:run()")
self.eventData.eventFunc(self.setting,self.value)
--- If the receiver was the client make sure every clients gets also updated.
if not connection:getIsServer() then
- self.debug("Send SettingEvent to clients")
- g_server:broadcastEvent(SettingEvent:new(self.vehicle,self.setting,self.eventData,self.value), nil, connection, self.vehicle)
+ self.debug("Send GlobalSettingEvent to clients")
+ g_server:broadcastEvent(GlobalSettingEvent:new(self.setting,self.eventData,self.value), nil, connection)
end
end
--- Sends an Event either:
--- - from the server to all clients or
--- - from the client to the server
----@param vehicle table
---@param setting Setting
---@param eventData table an event registers on the setting
---@param value any
-function SettingEvent.sendEvent(vehicle,setting,eventData,value)
+function GlobalSettingEvent.sendEvent(setting,eventData,value)
if g_server ~= nil then
- SettingEvent.debug("Send SettingEvent to clients")
- SettingEvent.debug("Setting name: %s",setting:getName())
- g_server:broadcastEvent(SettingEvent:new(vehicle,setting,eventData,value), nil, nil, vehicle)
+ GlobalSettingEvent.debug("Send GlobalSettingEvent to clients")
+ GlobalSettingEvent.debug("Setting name: %s",setting:getName())
+ g_server:broadcastEvent(GlobalSettingEvent:new(setting,eventData,value))
else
- SettingEvent.debug("Send SettingEvent to server")
- SettingEvent.debug("Setting name: %s",setting:getName())
- g_client:getServerConnection():sendEvent(SettingEvent:new(vehicle,setting,eventData,value))
+ GlobalSettingEvent.debug("Send GlobalSettingEvent to server")
+ GlobalSettingEvent.debug("Setting name: %s",setting:getName())
+ g_client:getServerConnection():sendEvent(GlobalSettingEvent:new(setting,eventData,value))
end;
end
-function SettingEvent.debug(...)
+function GlobalSettingEvent.debug(...)
courseplay.debugFormat(courseplay.DBG_MULTIPLAYER,...)
end
-function SettingEvent.debugVehicle(vehicle,...)
+--- Gets all relevant values from the setting event to send the event.
+---@param setting Setting
+---@param eventData table
+function GlobalSettingEvent.decodeEventData(setting,eventData)
+ local parentName = setting:getParentName()
+ local settingName = setting:getName()
+ local eventIx = eventData.ix
+ local writeFunc = eventData.writeFunc
+ return parentName,settingName,eventIx,writeFunc
+end
+
+--- Gets the setting and event back from all received values.
+---@param parentName table Name of the setting container
+---@param settingName string Name of the setting
+---@param eventIx number Event number received
+function GlobalSettingEvent.encodeEventData(parentName,settingName,eventIx)
+ local setting = courseplay[parentName][settingName]
+ return setting,setting:getEvent(eventIx)
+end
+
+
+--- This event is used to synchronize Setting changes on run time.
+--- Every setting event requires:
+--- - a container name (parentName)
+--- - a setting name
+--- - an event index for the setting
+--- - the value
+---
+VehicleSettingEvent = {}
+local VehicleSettingEvent_mt = Class(VehicleSettingEvent, Event)
+
+InitEventClass(VehicleSettingEvent, "VehicleSettingEvent")
+
+function VehicleSettingEvent:emptyNew()
+ local self = Event:new(VehicleSettingEvent_mt)
+ self.className = "VehicleSettingEvent"
+ return self
+end
+
+--- Creates a new Event
+---@param vehicle table
+---@param setting Setting
+---@param eventData table
+---@param value any
+function VehicleSettingEvent:new(vehicle,setting,eventData,value)
+ self.vehicle = vehicle
+ self.value = value
+ self.parentName,self.name,self.eventIx,self.writeFunc = self.decodeEventData(setting,eventData)
+ self.debug(self.vehicle,"VehicleSettingEvent:new()")
+ return self
+end
+
+--- Reads the serialized data on the receiving end of the event.
+function VehicleSettingEvent:readStream(streamId, connection) -- wird aufgerufen wenn mich ein Event erreicht
+ self.vehicle = NetworkUtil.getObject(streamReadInt32(streamId))
+ self.parentName = streamReadString(streamId)
+ self.name = streamReadString(streamId)
+ self.eventIx = streamReadUInt8(streamId)
+ self.debug(self.vehicle,"Parent name: %s, Setting name: %s, eventIx: %d",self.parentName, self.name,self.eventIx)
+ self.setting,self.eventData = self.encodeEventData(self.vehicle,self.parentName,self.name,self.eventIx)
+
+ if self.eventData.readFunc then
+ self.value = self.eventData.readFunc(streamId)
+ end
+
+ self.debug(self.vehicle,"VehicleSettingEvent:readStream()")
+ self.debug(self.vehicle,"Parent name: %s, Setting name: %s, value: %s, eventIx: %d",self.parentName, self.name, tostring(self.value),self.eventIx)
+
+ self:run(connection);
+end
+
+--- Writes the serialized data from the sender.
+function VehicleSettingEvent:writeStream(streamId, connection) -- Wird aufgrufen wenn ich ein event verschicke (merke: reihenfolge der Daten muss mit der bei readStream uebereinstimmen
+ streamWriteInt32(streamId, NetworkUtil.getObjectId(self.vehicle))
+ self.debug(self.vehicle,"VehicleSettingEvent:writeStream()")
+ streamWriteString(streamId, self.parentName)
+ streamWriteString(streamId, self.name)
+ streamWriteUInt8(streamId,self.eventIx)
+ self.debug(self.vehicle,"Parent name: %s, Setting name: %s, value: %s, eventIx: %d",self.parentName, self.name, tostring(self.value),self.eventIx)
+ if self.writeFunc then
+ self.writeFunc(streamId, self.value)
+ end
+end
+
+--- Runs the event on the receiving end of the event.
+function VehicleSettingEvent:run(connection) -- wir fuehren das empfangene event aus
+ self.debug(self.vehicle,"VehicleSettingEvent:run()")
+ self.eventData.eventFunc(self.setting,self.value)
+
+ --- If the receiver was the client make sure every clients gets also updated.
+ if not connection:getIsServer() then
+ self.debug(self.vehicle,"Send VehicleSettingEvent to clients")
+ g_server:broadcastEvent(VehicleSettingEvent:new(self.vehicle,self.setting,self.eventData,self.value), nil, connection, self.vehicle)
+ end
+end
+
+--- Sends an Event either:
+--- - from the server to all clients or
+--- - from the client to the server
+---@param vehicle table
+---@param setting Setting
+---@param eventData table an event registers on the setting
+---@param value any
+function VehicleSettingEvent.sendEvent(vehicle,setting,eventData,value)
+ if g_server ~= nil then
+ VehicleSettingEvent.debug(vehicle,"Send VehicleSettingEvent to clients")
+ VehicleSettingEvent.debug(vehicle,"Setting name: %s",setting:getName())
+ g_server:broadcastEvent(VehicleSettingEvent:new(vehicle,setting,eventData,value), nil, nil, vehicle)
+ else
+ VehicleSettingEvent.debug(vehicle,"Send VehicleSettingEvent to server")
+ VehicleSettingEvent.debug(vehicle,"Setting name: %s",setting:getName())
+ g_client:getServerConnection():sendEvent(VehicleSettingEvent:new(vehicle,setting,eventData,value))
+ end;
+end
+function VehicleSettingEvent.debug(vehicle,...)
courseplay.debugVehicle(courseplay.DBG_MULTIPLAYER,vehicle,...)
end
--- Gets all relevant values from the setting event to send the event.
---@param setting Setting
---@param eventData table
-function SettingEvent.decodeEventData(setting,eventData)
+function VehicleSettingEvent.decodeEventData(setting,eventData)
local parentName = setting:getParentName()
local settingName = setting:getName()
local eventIx = eventData.ix
@@ -131,12 +225,7 @@ end
---@param parentName table Name of the setting container
---@param settingName string Name of the setting
---@param eventIx number Event number received
-function SettingEvent.encodeEventData(vehicle,parentName,settingName,eventIx)
- local setting
- if vehicle ~= nil then
- setting = vehicle.cp[parentName][settingName]
- else
- setting = courseplay[parentName][settingName]
- end
+function VehicleSettingEvent.encodeEventData(vehicle,parentName,settingName,eventIx)
+ local setting = vehicle.cp[parentName][settingName]
return setting,setting:getEvent(eventIx)
end
diff --git a/FieldworkAIDriver.lua b/FieldworkAIDriver.lua
index e1a37bb4a..3332741cf 100644
--- a/FieldworkAIDriver.lua
+++ b/FieldworkAIDriver.lua
@@ -323,6 +323,14 @@ function FieldworkAIDriver:writeUpdateStream(streamId, connection, dirtyMask)
else
streamWriteBool(streamId,false)
end
+ if self.timeRemaining ~= nil and self.timeRemaining ~= self.timeRemainingSend then
+ streamWriteBool(streamId,true)
+ streamWriteFloat32(streamId,self.timeRemaining)
+ self.timeRemainingSend = self.timeRemaining
+ else
+ streamWriteBool(streamId,false)
+ end
+
AIDriver.writeUpdateStream(self,streamId, connection, dirtyMask)
end
@@ -336,6 +344,10 @@ function FieldworkAIDriver:readUpdateStream(streamId, timestamp, connection)
local nameState = streamReadString(streamId)
self.fieldworkState = self.states[nameState]
end
+ if streamReadBool(streamId) then
+ self.timeRemaining = streamReadFloat32(streamId)
+ end
+
AIDriver.readUpdateStream(self,streamId, timestamp, connection)
end
@@ -346,6 +358,12 @@ function FieldworkAIDriver:onWriteStream(streamId)
else
streamWriteBool(streamId,false)
end
+ if self.timeRemaining ~= nil then
+ streamWriteBool(streamId,true)
+ streamWriteFloat32(streamId,self.timeRemaining)
+ else
+ streamWriteBool(streamId,false)
+ end
AIDriver.onWriteStream(self,streamId)
end
@@ -354,6 +372,9 @@ function FieldworkAIDriver:onReadStream(streamId)
local nameState = streamReadString(streamId)
self.fieldworkState = self.states[nameState]
end
+ if streamReadBool(streamId) then
+ self.timeRemaining = streamReadFloat32(streamId)
+ end
AIDriver.onReadStream(self,streamId)
end
@@ -815,11 +836,12 @@ function FieldworkAIDriver:setUpCourses()
self.fieldworkCourse:setOffset(self.vehicle.cp.settings.toolOffsetX:get(), self.vehicle.cp.settings.toolOffsetZ:get())
-- TODO: consolidate the working width calculation and usage, this is currently an ugly mess
self.fieldworkCourse:setWorkWidth(self.vehicle.cp.courseWorkWidth or self.courseGeneratorSettings.workWidth:getAutoWorkWidth())
+ local laneNumber = self.courseGeneratorSettings.multiTools.laneNumber:get()
if self.vehicle.cp.courseGeneratorSettings.multiTools:get() > 1 then
- self:debug('Calculating offset course for position %d of %d', self.vehicle.cp.laneNumber,
+ self:debug('Calculating offset course for position %d of %d', laneNumber,
self.vehicle.cp.courseGeneratorSettings.multiTools:get())
self.fieldworkCourse = self.fieldworkCourse:calculateOffsetCourse(
- self.vehicle.cp.courseGeneratorSettings.multiTools:get(), self.vehicle.cp.laneNumber,
+ self.vehicle.cp.courseGeneratorSettings.multiTools:get(), laneNumber,
self.fieldworkCourse.workWidth / self.vehicle.cp.courseGeneratorSettings.multiTools:get(),
self.vehicle.cp.settings.symmetricLaneChange:is(true))
end
@@ -939,15 +961,19 @@ function FieldworkAIDriver:foldImplements()
end
function FieldworkAIDriver:clearRemainingTime()
- self.vehicle.cp.timeRemaining = nil
+ self.timeRemaining = nil
+end
+
+function FieldworkAIDriver:getRemainingTime()
+ return self.timeRemaining
end
function FieldworkAIDriver:updateRemainingTime(ix)
if self.state == self.states.ON_FIELDWORK_COURSE then
local dist, turns = self.course:getRemainingDistanceAndTurnsFrom(ix)
local turnTime = turns * self.turnDurationMs / 1000
- self.vehicle.cp.timeRemaining = math.max(0, dist / (self:getWorkSpeed() / 3.6) + turnTime)
- self:debug('Distance to go: %.1f; Turns left: %d; Time left: %ds', dist, turns, self.vehicle.cp.timeRemaining)
+ self.timeRemaining = math.max(0, dist / (self:getWorkSpeed() / 3.6) + turnTime)
+ self:debug('Distance to go: %.1f; Turns left: %d; Time left: %ds', dist, turns, self.timeRemaining)
else
self:clearRemainingTime()
end
@@ -1123,7 +1149,7 @@ end
function FieldworkAIDriver:finishRow(ix)
self:setMarkers()
- self.turnContext = RowFinishingContext(self.course, ix, self.aiDriverData, self.vehicle.cp.workWidth,
+ self.turnContext = RowFinishingContext(self.course, ix, self.aiDriverData, self:getWorkWidth(),
self.frontMarkerDistance, self.backMarkerDistance,
self:getTurnEndSideOffset(), self:getTurnEndForwardOffset())
self.aiTurn = FinishRowOnly(self.vehicle, self, self.turnContext)
@@ -1136,7 +1162,7 @@ function FieldworkAIDriver:startTurn(ix)
-- this should help returning to the course faster.
self.ppc:setShortLookaheadDistance()
self:setMarkers()
- self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self.vehicle.cp.workWidth,
+ self.turnContext = TurnContext(self.course, ix, self.aiDriverData, self:getWorkWidth(),
self.frontMarkerDistance, self.backMarkerDistance,
self:getTurnEndSideOffset(), self:getTurnEndForwardOffset())
if self.vehicle.cp.settings.useAITurns:is(true) then
@@ -1204,27 +1230,7 @@ function FieldworkAIDriver:getMarkers()
end
function FieldworkAIDriver:getAIMarkers(object, suppressLog)
- local aiLeftMarker, aiRightMarker, aiBackMarker
- if object.getAIMarkers then
- aiLeftMarker, aiRightMarker, aiBackMarker = object:getAIMarkers()
- end
- if not aiLeftMarker or not aiRightMarker or not aiBackMarker then
- -- use the root node if there are no AI markers
- if not suppressLog then
- self:debug('%s has no AI markers, try work areas', nameNum(object))
- end
- aiLeftMarker, aiRightMarker, aiBackMarker = self:getAIMarkersFromWorkAreas(object)
- if not aiLeftMarker or not aiRightMarker or not aiLeftMarker then
- if not suppressLog then
- self:debug('%s has no work areas, giving up', nameNum(object))
- end
- return nil, nil, nil
- else
- return aiLeftMarker, aiRightMarker, aiBackMarker
- end
- else
- return aiLeftMarker, aiRightMarker, aiBackMarker
- end
+ return WorkWidthUtil.getAIMarkers(object)
end
--- When finishing a turn, is it time to lower all implements here?
@@ -1379,27 +1385,6 @@ function FieldworkAIDriver:onDraw()
AIDriver.onDraw(self)
end
-function FieldworkAIDriver:isValidWorkArea(area)
- return area.start and area.height and area.width and
- area.type ~= WorkAreaType.RIDGEMARKER and
- area.type ~= WorkAreaType.COMBINESWATH and
- area.type ~= WorkAreaType.COMBINECHOPPER
-end
-
---- Calculate the front and back marker nodes of a work area
-function FieldworkAIDriver:getAIMarkersFromWorkAreas(object)
- -- work areas are defined by three nodes: start, width and height. These nodes
- -- define a rectangular work area which you can make visible with the
- -- gsVehicleDebugAttributes console command and then pressing F5
- for _, area in courseplay:workAreaIterator(object) do
- if self:isValidWorkArea(area) then
- -- for now, just use the first valid work area we find
- self:debug('%s: Using %s work area markers as AIMarkers', nameNum(object), g_workAreaTypeManager.workAreaTypes[area.type].name)
- return area.start, area.width, area.height
- end
- end
-end
-
function FieldworkAIDriver:getAllAIImplements(object, implements)
if not implements then implements = {} end
for _, implement in ipairs(object:getAttachedImplements()) do
@@ -1414,7 +1399,7 @@ end
-- Is this and implement we should consider when deciding when to lift/raise implements at the end/start of a row?
function FieldworkAIDriver:isValidAIImplement(object)
- if courseplay:hasWorkAreas(object) then
+ if WorkWidthUtil.hasWorkAreas(object) then
-- has work areas, good.
return true
else
@@ -1503,3 +1488,7 @@ end
function FieldworkAIDriver:getAllFillLevels(object, fillLevelInfo)
AIDriverUtil.getAllFillLevels(object, fillLevelInfo, self)
end
+
+function FieldworkAIDriver:getWorkWidth()
+ return self.courseGeneratorSettings.workWidth:get()
+end
\ No newline at end of file
diff --git a/FillableFieldworkAIDriver.lua b/FillableFieldworkAIDriver.lua
index 7c3b5d509..843a4ddd9 100644
--- a/FillableFieldworkAIDriver.lua
+++ b/FillableFieldworkAIDriver.lua
@@ -293,14 +293,15 @@ end
function FillableFieldworkAIDriver:getTurnEndForwardOffset()
-- TODO: do other implements need this?
+ local workWidth = self:getWorkWidth()
if SpecializationUtil.hasSpecialization(Sprayer, self.vehicle.specializations)
- and self.vehicle.cp.workWidth > self.settings.turnDiameter:get() then
+ and workWidth> self.settings.turnDiameter:get() then
-- compensate for very wide implements like sprayer booms where the tip of the implement
-- on the inner side of the turn may be very far forward of the vehicle's root and miss
-- parts of the inside corner.
- local forwardOffset = - (self.vehicle.cp.workWidth - self.settings.turnDiameter:get()) / 2.5
+ local forwardOffset = - (workWidth - self.settings.turnDiameter:get()) / 2.5
self:debug('sprayer working width %.1f > turn diameter %.1f, applying forward offset %.1f to turn end',
- self.vehicle.cp.workWidth, self.settings.turnDiameter:get(), forwardOffset)
+ workWidth, self.settings.turnDiameter:get(), forwardOffset)
return forwardOffset
else
return 0
diff --git a/GlobalSettings.lua b/GlobalSettings.lua
index 6e6f1ea1c..244e63e74 100644
--- a/GlobalSettings.lua
+++ b/GlobalSettings.lua
@@ -183,6 +183,7 @@ function DebugChannelsSetting:load(xmlFilePath)
self.numChannels = 0
self.toolTips = {}
if xmlFile and hasXMLProperty(xmlFile, baseKey) then
+ print("Loading debug channel setup!")
local i = 0
while true do
local key = string.format("%s.%s(%d)",baseKey,"DebugChannel",i)
@@ -211,19 +212,23 @@ function DebugChannelsSetting:load(xmlFilePath)
end
self.numChannels = i
delete(xmlFile)
+ else
+ print("Couldn't load debug channel setup!")
end
courseplay.debugChannels = self.channels --- Old code
end
function DebugChannelsSetting:onWriteStream(streamID)
+ streamWriteUInt8(streamID,self.numChannels)
for ix,value in ipairs(self.channels) do
streamWriteBool(streamID,value)
end
end
function DebugChannelsSetting:onReadStream(streamID)
- for ix,_ in ipairs(self.channels) do
- self.channels[ix] = streamReadBool(streamID)
+ self.numChannels = streamReadUInt8(streamID)
+ for i= 1, self.numChannels do
+ self.channels[i] = streamReadBool(streamID)
end
end
diff --git a/PlowAIDriver.lua b/PlowAIDriver.lua
index b3c32fd7a..34bde084e 100644
--- a/PlowAIDriver.lua
+++ b/PlowAIDriver.lua
@@ -172,3 +172,11 @@ function PlowAIDriver:getTurnEndSideOffset()
return 0
end
end
+
+function PlowAIDriver:stop(msg)
+ --- Make sure after the driver has finished.
+ --- Clients and server values are synced,
+ --- as the server updates the value locally during driving.
+ self.settings.toolOffsetX:set(self.settings.toolOffsetX:get())
+ FieldworkAIDriver.stop(self,msg)
+end
\ No newline at end of file
diff --git a/ShieldAIDriver.lua b/ShieldAIDriver.lua
index e0198bedb..08fa9e913 100644
--- a/ShieldAIDriver.lua
+++ b/ShieldAIDriver.lua
@@ -167,7 +167,7 @@ function ShieldAIDriver:getTargetNode()
end
function ShieldAIDriver:getWorkWidth()
- return math.max(self.vehicle.cp.workWidth,3)
+ return math.max(CompactingAIDriver.getWorkWidth(self),3)
end
--- Overrides the player shield controls, while a cp driver is driving.
diff --git a/base.lua b/base.lua
index d9495560f..87eb28534 100644
--- a/base.lua
+++ b/base.lua
@@ -224,37 +224,17 @@ function courseplay:onLoad(savegame)
--Offset
- self.cp.laneOffset = 0;
self.cp.totalOffsetX = 0;
self.cp.skipOffsetX = false;
- self.cp.workWidth = 3
-
--Copy course
self.cp.hasFoundCopyDriver = false;
self.cp.copyCourseFromDriver = nil;
self.cp.selectedDriverNumber = 0;
- self.cp.laneNumber = 0;
-
--Course generation
self.cp.hasGeneratedCourse = false;
- self.cp.fieldEdge = {
- selectedField = {
- fieldNum = 0;
- numPoints = 0;
- buttonsCreated = false;
- };
- customField = {
- points = nil;
- numPoints = 0;
- isCreated = false;
- show = false;
- fieldNum = 0;
- selectedFieldNumExists = false;
- };
- };
self.cp.mouseCursorActive = false;
@@ -268,7 +248,7 @@ function courseplay:onLoad(savegame)
---@type SettingsContainer
self.cp.settings = SettingsContainer.createVehicleSpecificSettings(self)
- ---@type SettingsContainer
+ ---@type CourseGeneratorSettingsContainer
self.cp.courseGeneratorSettings = SettingsContainer.createCourseGeneratorSettings(self)
-- HUD
@@ -399,38 +379,11 @@ function courseplay:renderHud(isDriving)
end
function courseplay:showWorkWidth(vehicle)
- local offsX, offsZ = vehicle.cp.settings.toolOffsetX:get() or 0, vehicle.cp.settings.toolOffsetZ:get() or 0;
-
- local left = (vehicle.cp.workWidth * 0.5) + offsX;
- local right = (vehicle.cp.workWidth * -0.5) + offsX;
-
- -- TODO: refactor this, move showWorkWidth into the AIDriver?
- if vehicle.cp.directionNode and vehicle.cp.driver.getMarkers then
- local f, b = vehicle.cp.driver:getMarkers()
- local p1x, p1y, p1z = localToWorld(vehicle.cp.directionNode, left, 1.6, b - offsZ);
- local p2x, p2y, p2z = localToWorld(vehicle.cp.directionNode, right, 1.6, b - offsZ);
- local p3x, p3y, p3z = localToWorld(vehicle.cp.directionNode, right, 1.6, f - offsZ);
- local p4x, p4y, p4z = localToWorld(vehicle.cp.directionNode, left, 1.6, f - offsZ);
-
- cpDebug:drawPoint(p1x, p1y, p1z, 1, 1, 0);
- cpDebug:drawPoint(p2x, p2y, p2z, 1, 1, 0);
- cpDebug:drawPoint(p3x, p3y, p3z, 1, 1, 0);
- cpDebug:drawPoint(p4x, p4y, p4z, 1, 1, 0);
-
- cpDebug:drawLine(p1x, p1y, p1z, 1, 0, 0, p2x, p2y, p2z);
- cpDebug:drawLine(p2x, p2y, p2z, 1, 0, 0, p3x, p3y, p3z);
- cpDebug:drawLine(p3x, p3y, p3z, 1, 0, 0, p4x, p4y, p4z);
- cpDebug:drawLine(p4x, p4y, p4z, 1, 0, 0, p1x, p1y, p1z);
- else
- local lX, lY, lZ = localToWorld(vehicle.rootNode, left, 1.6, -6 - offsZ);
- local rX, rY, rZ = localToWorld(vehicle.rootNode, right, 1.6, -6 - offsZ);
-
- cpDebug:drawPoint(lX, lY, lZ, 1, 1, 0);
- cpDebug:drawPoint(rX, rY, rZ, 1, 1, 0);
-
- cpDebug:drawLine(lX, lY, lZ, 1, 0, 0, rX, rY, rZ);
- end;
-end;
+ local offsX, offsZ = vehicle.cp.settings.toolOffsetX:get() or 0, vehicle.cp.settings.toolOffsetZ:get() or 0
+ local workWidth = vehicle.cp.courseGeneratorSettings.workWidth:get()
+
+ WorkWidthUtil.showWorkWidth(vehicle,workWidth,offsX,offsZ)
+end
function courseplay:onUpdate(dt)
--if the vehicle is attached to another vehicle, disable cp
@@ -487,11 +440,6 @@ function courseplay:onUpdate(dt)
if self.cp.collidingVehicleId ~= nil and g_currentMission.nodeToObject[self.cp.collidingVehicleId] ~= nil and g_currentMission.nodeToObject[self.cp.collidingVehicleId].isCpPathvehicle then
courseplay:setPathVehiclesSpeed(self,dt)
end
-
- --reset selected field num, when field doesn't exist anymone (contracts)
- if courseplay.fields.fieldData[self.cp.fieldEdge.selectedField.fieldNum] == nil then
- self.cp.fieldEdge.selectedField.fieldNum = 0;
- end
-- this really should be only done in one place.
self.cp.curSpeed = self.lastSpeedReal * 3600;
@@ -512,9 +460,6 @@ function courseplay:onUpdateTick(dt)
if not courseplay.isEnabled(self) then
return
end
- if not self.cp.fieldEdge.selectedField.buttonsCreated and courseplay.fields.numAvailableFields > 0 then
- courseplay:createFieldEdgeButtons(self);
- end;
if self.cp.toolsDirty then
courseplay:updateOnAttachOrDetach(self)
@@ -712,9 +657,6 @@ function courseplay:onReadStream(streamId, connection)
self.cp.course2dUpdateDrawData = true;
- if streamReadBool(streamId) then
- self.cp.timeRemaining = streamReadFloat32(streamId)
- end
if streamReadBool(streamId) then
self.cp.infoText = streamReadString(streamId)
@@ -765,13 +707,6 @@ function courseplay:onWriteStream(streamId, connection)
end
- if self.cp.timeRemaining then
- streamWriteBool(streamId,true)
- streamWriteFloat32(streamId,self.cp.timeRemaining)
- else
- streamWriteBool(streamId,false)
- end
-
if self.cp.infoText then
streamWriteBool(streamId,true)
streamWriteString(streamId,self.cp.infoText)
@@ -810,13 +745,6 @@ function courseplay:onReadUpdateStream(streamId, timestamp, connection)
else
self.cp.currentCourseName = nil
end
- if streamReadBool(streamId) then -- is timeRemaining~=nil ?
- if streamReadBool(streamId) then -- has timeRemaining changed
- self.cp.timeRemaining = streamReadFloat32(streamId)
- end
- else
- self.cp.timeRemaining = nil
- end
--gitAdditionalText ?
end
end
@@ -857,19 +785,6 @@ function courseplay:onWriteUpdateStream(streamId, connection, dirtyMask)
else
streamWriteBool(streamId,false)
end
- if self.cp.timeRemaining then -- is timeRemaining~=nil ?
- streamWriteBool(streamId,true)
- if self.cp.timeRemaining~=self.cp.timeRemainingSend then -- has timeRemaining changed
- streamWriteBool(streamId,true)
- streamWriteFloat32(streamId,self.cp.timeRemaining)
- self.cp.timeRemainingSend = self.cp.timeRemaining
- else
- streamWriteBool(streamId,false)
- end
- else
- streamWriteBool(streamId,false)
- end
- --gitAdditionalText ?
end
end
end
@@ -889,21 +804,6 @@ function courseplay:loadVehicleCPSettings(xmlFile, key, resetVehicles)
self.cp.hud.show = Utils.getNoNil( getXMLBool(xmlFile, curKey .. '#showHud'), false);
- -- MODES 4 / 6
- curKey = key .. '.courseplay.fieldWork';
- self.cp.workWidth = Utils.getNoNil(getXMLFloat(xmlFile, curKey .. '#workWidth'), 3);
- self.cp.manualWorkWidth = Utils.getNoNil(getXMLFloat(xmlFile, curKey .. '#manualWorkWidth'), 0);
- if self.cp.manualWorkWidth ~= 0 then
- self.cp.workWidth = self.cp.manualWorkWidth
- else
- self.cp.manualWorkWidth = nil
- end;
-
- local offsetData = Utils.getNoNil(getXMLString(xmlFile, curKey .. '#offsetData'), '0;0;0;false;0;0;0'); -- 1=laneOffset, 2=toolOffsetX, 3=toolOffsetZ, 4=symmetricalLaneChange
- offsetData = StringUtil.splitString(';', offsetData);
- courseplay:changeLaneOffset(self, nil, tonumber(offsetData[1]));
-
- if offsetData[7] ~= nil then self.cp.laneNumber = tonumber(offsetData[7]) end;
self.cp.settings:loadFromXML(xmlFile, key .. '.courseplay')
@@ -943,14 +843,6 @@ function courseplay:saveToXMLFile(xmlFile, key, usedModNames)
setXMLBool(xmlFile, newKey..".HUD #showHud", self.cp.hud.show)
-
- --field work settings
- local offsetData = string.format('%.1f;%.1f;%.1f;%s;%.1f;%.1f;%d', self.cp.laneOffset, 0, 0, 0, 0, 0, self.cp.laneNumber);
- setXMLString(xmlFile, newKey..".fieldWork #workWidth", string.format("%.1f",self.cp.workWidth))
- setXMLString(xmlFile, newKey..".fieldWork #offsetData", offsetData)
- setXMLString(xmlFile, newKey..".fieldWork #manualWorkWidth", string.format("%.1f",Utils.getNoNil(self.cp.manualWorkWidth,0)))
-
-
self.cp.settings:saveToXML(xmlFile, newKey)
self.cp.courseGeneratorSettings:saveToXML(xmlFile, newKey)
diff --git a/config/VehicleConfigurations.xml b/config/VehicleConfigurations.xml
index 8ea2c4366..a9f039ead 100644
--- a/config/VehicleConfigurations.xml
+++ b/config/VehicleConfigurations.xml
@@ -101,6 +101,10 @@ You can define the following custom settings:
+
+
0 then
+ self:setNext()
+ else
+ self:setPrevious()
+ end
+ self:onChange()
+end
+
+function WorkWidthSetting:onChange()
+ courseplay:setCustomTimer(self.vehicle, 'showWorkWidth', 2);
+end
+
--- Course gen center mode setting
---@class CenterModeSetting : SettingList
CenterModeSetting = CpObject(SettingList)
@@ -377,28 +392,96 @@ end
---@class MultiToolsSetting : SettingList
MultiToolsSetting = CpObject(SettingList)
-
+MultiToolsSetting.MAX_DRIVERS = 8
function MultiToolsSetting:init(vehicle)
self.values = {}
self.texts = {}
- for i = 1, 8 do
+ for i = 1, self.MAX_DRIVERS do
table.insert(self.values, i)
table.insert(self.texts, i)
end
SettingList.init(self, 'multiTools', 'COURSEPLAY_MULTI_TOOLS', 'COURSEPLAY_MULTI_TOOLS',
vehicle, self.values, self.texts)
+ self.laneNumber = LaneNumberSetting(vehicle,self)
+ self.LANE_NUMBER_EVENT = self:registerIntEvent(self.setLaneNumberFromNetwork)
+end
+
+function MultiToolsSetting:loadFromXml(xml,parentKey)
+ SettingList.loadFromXml(self,xml,parentKey)
+ self.laneNumber:loadFromXml(xml,parentKey)
+end
+function MultiToolsSetting:saveToXml(xml,parentKey)
+ SettingList.saveToXml(self,xml,parentKey)
+ self.laneNumber:saveToXml(xml,parentKey)
+end
+
+function MultiToolsSetting:onReadStream(streamId)
+ SettingList.onReadStream(self,streamId)
+ self.laneNumber:onReadStream(streamId)
+
+end
+function MultiToolsSetting:onWriteStream(streamId)
+ SettingList.onWriteStream(self,streamId)
+ self.laneNumber:onWriteStream(streamId)
+end
+
+function MultiToolsSetting:setLaneNumberFromNetwork(value)
+ self.laneNumber:set(value)
+end
+
+function MultiToolsSetting:changeLaneNumber(changeBy,noEventSend)
+ self.laneNumber:changeByX(changeBy)
+ if noEventSend == nil or noEventSend == false then
+ self:raiseEvent(self.LANE_NUMBER_EVENT,self.laneNumber:get())
+ end
end
function MultiToolsSetting:onChange()
+ self.laneNumber:refresh(self:get())
-- TODO: consolidate the (poorly named) laneNumber and laneOffset and this into a single setting as they
-- can only change together (instead of having logic all over the place according to the good old CP practices)
if self:get() % 2 == 0 then
- courseplay:changeLaneNumber(self.vehicle, 1)
+ self.laneNumber:set(1)
else
- courseplay:changeLaneNumber(self.vehicle, 0, true)
+ self.laneNumber:set(0)
+ end
+end
+
+---@class LaneNumberSetting : SettingList
+LaneNumberSetting = CpObject(SettingList)
+function LaneNumberSetting:init(vehicle,multiToolsSetting)
+ self.multiToolsSetting = multiToolsSetting
+ self:refresh(multiToolsSetting:get())
+ SettingList.init(self, 'laneNumber', 'COURSEPLAY_LANE_OFFSET', 'COURSEPLAY_LANE_OFFSET',
+ vehicle, self.values, self.texts)
+end
+
+function LaneNumberSetting:refresh(numDrivers)
+ local evenNumOfDrivers = numDrivers % 2 == 0
+ local maxLeft,maxRight = self:getMaxValues(numDrivers)
+ self.texts = {}
+ self.values = {}
+ for i = maxLeft, maxRight do
+ if not evenNumOfDrivers or i ~= 0 and evenNumOfDrivers then
+ table.insert(self.values, i)
+ local text = ""
+ if i>0 then
+ text = string.format("%d %s",i,courseplay:loc('COURSEPLAY_RIGHT'))
+ elseif i<0 then
+ text = string.format("%d %s",i,courseplay:loc('COURSEPLAY_LEFT'))
+ else
+ text = courseplay:loc('COURSEPLAY_CENTER')
+ end
+ table.insert(self.texts, text)
+ end
end
end
+function LaneNumberSetting:getMaxValues(numDrivers)
+ local maxRight = math.floor(numDrivers/2)
+ return -maxRight,maxRight
+end
+
---@class IslandBypassModeSetting : SettingList
IslandBypassModeSetting = CpObject(SettingList)
@@ -507,6 +590,138 @@ function HeadlandPassesSetting:init(vehicle)
vehicle, self.values, self.texts)
end
+---@class FieldEdgePathSetting : IntSetting
+FieldEdgePathSetting = CpObject(IntSetting)
+FieldEdgePathSetting.EMPTY = 0
+FieldEdgePathSetting.MAX_FIELDS = 250
+function FieldEdgePathSetting:init(vehicle,selectedFieldSetting)
+ self.selectedFieldSetting = selectedFieldSetting
+ IntSetting.init(self,"fieldEdgePath","","",vehicle,0,self.MAX_FIELDS,self.EMPTY)
+ self.visible = false
+ self.customFieldPath = nil
+ self.currentFieldNumExists = false
+ -- All the labels, tooltips.
+ self.labels = {
+ ["create"] = courseplay:loc('COURSEPLAY_SCAN_CURRENT_FIELD_EDGES'),
+ ["currentNr"] = courseplay:loc('COURSEPLAY_CURRENT_FIELD_EDGE_PATH_NUMBER'),
+ ["overwritePath"] = courseplay:loc('COURSEPLAY_OVERWRITE_CUSTOM_FIELD_EDGE_PATH_IN_LIST'),
+ ["addNewPath"] = courseplay:loc('COURSEPLAY_ADD_CUSTOM_FIELD_EDGE_PATH_TO_LIST'),
+ ["clear"] = courseplay:loc('COURSEPLAY_CLEAR_CUSTOM_FIELD_EDGE_PATH'),
+ ["visibility"] = courseplay:loc('COURSEPLAY_CHANGE_VISIBILITY_CUSTOM_FIELD_EDGE_PATH'),
+ }
+end
+
+--- After a change, check if the current selected field is already existing.
+function FieldEdgePathSetting:onChange()
+ local value = self:get()
+ if value > 0 then
+ self.currentFieldNumExists = CpFieldUtil.isFieldNumberValid(value)
+ end
+end
+
+--- Generates a new field edge path.
+function FieldEdgePathSetting:createPath()
+ if not self.customFieldPath then
+ local customField = CpFieldUtil.generateCustomFieldEdgePath(self.vehicle)
+ if customField.numPoints > 0 then
+ self.customFieldPath = customField
+ self.visible = true
+ end
+ end
+end
+
+--- Converts a generated field edge path to a field and saves it.
+function FieldEdgePathSetting:savePath()
+ if self.customFieldPath ~= nil then
+ local field = CpFieldUtil.saveCustomFieldEdgePathAsField(self.customFieldPath,self:get())
+ self:sendEvent(field)
+ self:clearPath()
+ end
+end
+
+---@param field table
+function FieldEdgePathSetting:sendEvent(field)
+ CustomFieldEvent.sendEvent(field)
+end
+
+--- Clears the generated field edge path-
+function FieldEdgePathSetting:clearPath()
+ self.customFieldPath = nil
+ self.visible = false
+ self.currentFieldNumExists = false
+ self:set(self.EMPTY,true)
+end
+
+--- Changes the visibility of a generated field edge path.
+function FieldEdgePathSetting:toggleVisibility()
+ if self.customFieldPath then
+ self.visible = not self.visible
+ end
+end
+
+--- Is a field edge path generated ?
+function FieldEdgePathSetting:hasUnsavedCustomFieldPath()
+ return self.customFieldPath~=nil
+end
+
+--- Is the field number ~=0 ?
+function FieldEdgePathSetting:isValidNumber()
+ return self:get() ~= self.EMPTY
+end
+
+--- Is the generated field edge visible ?
+function FieldEdgePathSetting:isUnsavedCustomFieldPathVisible()
+ return self:hasUnsavedCustomFieldPath() and self.visible
+end
+
+--- Displays the generated field edge path.
+function FieldEdgePathSetting:showCustomFieldEdgePath()
+ if self:isUnsavedCustomFieldPathVisible() then
+ CpFieldUtil.showFieldEdgePath(self.vehicle,self.customFieldPath)
+ end
+end
+
+function FieldEdgePathSetting:isDisabled()
+ return self.vehicle:getIsCourseplayDriving()
+ or self.vehicle.cp.canDrive
+ or self.vehicle.cp.isRecording
+ or self.vehicle.cp.recordingIsPaused
+end
+
+function FieldEdgePathSetting:getCreateLabel()
+ return self.labels.create
+end
+
+function FieldEdgePathSetting:getCurrentNrLabel()
+ return self.labels.currentNr
+end
+
+function FieldEdgePathSetting:getClearLabel()
+ return self.labels.clear
+end
+
+function FieldEdgePathSetting:getVisibilityLabel()
+ return self.labels.visibility
+end
+
+--- This is a tooltip for the save button.
+--- The text changes if the field is already generated or not.
+function FieldEdgePathSetting:getSaveLabel()
+ local str = self.currentFieldNumExists and self.labels.overwritePath or self.labels.addNewPath
+ return self:isValidNumber() and string.format(str,self:get()) or ""
+end
+
+function FieldEdgePathSetting:isSyncAllowed()
+ return false
+end
+
+function FieldEdgePathSetting:getText()
+ if not self:isValidNumber() then
+ return "---"
+ end
+ return CpFieldUtil.getFieldName(self:get()) or IntSetting.getText(self)
+end
+
--- Global course generator settings (read from the XML, may be added to the UI later when needed):
---
--- Minimum radius in meters where a lane change on the headland is allowed. This is to ensure that
@@ -573,6 +788,7 @@ function SettingsContainer.createCourseGeneratorSettings(vehicle)
container:addSetting(IslandBypassModeSetting, vehicle)
container:addSetting(HeadlandOverlapPercent, vehicle)
container:addSetting(ShowSeedCalculatorSetting, vehicle)
+ container:addSetting(FieldEdgePathSetting,vehicle,container.selectedField)
return container
end
diff --git a/course-generator/cp.lua b/course-generator/cp.lua
index 8294e3893..5a0ded1ea 100644
--- a/course-generator/cp.lua
+++ b/course-generator/cp.lua
@@ -218,8 +218,6 @@ function courseGenerator.generate(vehicle)
if CpManager.isMP then
CourseEvent.sendEvent(vehicle, vehicle.Waypoints)
CourseplayEvent.sendEvent(vehicle, "self.cp.courseWorkWidth", vehicle.cp.courseWorkWidth) -- need a setting for this one
- CourseplayEvent.sendEvent(vehicle, "self.cp.workWidth", vehicle.cp.workWidth) -- need a setting for this one
- CourseplayEvent.sendEvent(vehicle, "self.cp.laneNumber", vehicle.cp.laneNumber) -- need a setting for this one
--setMultiTools
end
diff --git a/course_management.lua b/course_management.lua
index 273fd607c..0082161c5 100644
--- a/course_management.lua
+++ b/course_management.lua
@@ -352,9 +352,7 @@ function courseplay:copyCourse(vehicle)
--MultiTools
if src.cp.courseGeneratorSettings.multiTools:get() > 1 then
- vehicle.cp.workWidth = src.cp.workWidth
vehicle.cp.courseWorkWidth = src.cp.courseWorkWidth
- vehicle.cp.manualWorkWidth = src.cp.manualWorkWidth
vehicle.cp.courseGeneratorSettings.multiTools:set(src.cp.courseGeneratorSettings.multiTools:get())
else
vehicle.cp.courseGeneratorSettings.multiTools:set(1)
diff --git a/courseplay.lua b/courseplay.lua
index d3c4ae2f2..119823477 100644
--- a/courseplay.lua
+++ b/courseplay.lua
@@ -32,6 +32,7 @@ courseplay.courses = {};
courseplay.settings = {};
courseplay.hud = {};
courseplay.buttons = {};
+---@class courseplay.fields
courseplay.fields = {};
courseplay.generation = {};
courseplay.lights = {};
@@ -138,6 +139,7 @@ local function initialize()
'Events/CourseEvent',
'Events/InfoTextEvent',
'Events/CommandEvents',
+ 'Events/CustomFieldEvent',
'Generic/LinkedList'
};
@@ -229,23 +231,19 @@ local function setGlobalData()
[1]={name='self.cp.canDrive',dataFormat='Bool'},
[2]={name='self.cp.drivingDirReverse',dataFormat='Bool'},
- [3]={name='self.cp.fieldEdge.customField.isCreated',dataFormat='Bool'},
- [4]={name='self.cp.fieldEdge.customField.fieldNum',dataFormat='Int'},
- [5]={name='self.cp.fieldEdge.customField.selectedFieldNumExists',dataFormat='Bool'},
- [6]={name='self.cp.fieldEdge.selectedField.fieldNum',dataFormat='Int'},
- [7]={name='self.cp.isDriving',dataFormat='Bool'},
- [8]={name='self.cp.hud.openWithMouse',dataFormat='Bool'},
- [9]={name='self.cp.workWidth',dataFormat='Float'},
- [10]={name='self.cp.coursePlayerNum',dataFormat='Int'}, --??
- [11]={name='self.cp.laneOffset',dataFormat='Float'},
- [12]={name='self.cp.hud.currentPage',dataFormat='Int'},
- [13]={name='self.cp.waypointIndex',dataFormat='Int'},
- [14]={name='self.cp.isRecording',dataFormat='Bool'},
- [15]={name='self.cp.recordingIsPaused',dataFormat='Bool'},
+ [3]={name='self.cp.isDriving',dataFormat='Bool'},
+ [4]={name='self.cp.hud.openWithMouse',dataFormat='Bool'},
+ [5]={name='self.cp.coursePlayerNum',dataFormat='Int'}, --??
+ [6]={name='self.cp.hud.currentPage',dataFormat='Int'},
+ [7]={name='self.cp.waypointIndex',dataFormat='Int'},
+ [8]={name='self.cp.isRecording',dataFormat='Bool'},
+ [9]={name='self.cp.recordingIsPaused',dataFormat='Bool'},
}
-
+ ---@type SettingsContainer
courseplay.globalSettings = SettingsContainer.createGlobalSettings()
+ ---@type SettingsContainer
courseplay.globalCourseGeneratorSettings = SettingsContainer.createGlobalCourseGeneratorSettings()
+ ---@type SettingsContainer
courseplay.globalPathfinderSettings = SettingsContainer.createGlobalPathfinderSettings()
end;
diff --git a/courseplay_event.lua b/courseplay_event.lua
index 240c7dc84..0eaa36e85 100644
--- a/courseplay_event.lua
+++ b/courseplay_event.lua
@@ -235,32 +235,7 @@ function CourseplayJoinFixEvent:writeStream(streamId, connection)
print(string.format("\t### CourseplayMultiplayer: writing %d custom fields ", fieldsCount))
for id, field in pairs(courseplay.fields.fieldData) do
if field.isCustom then
- streamDebugWriteString(streamId, field.name)
- self:debugWrite(field.name,"field name")
- streamDebugWriteInt32(streamId, field.numPoints)
- self:debugWrite(field.numPoints,"field numPoints")
- streamDebugWriteBool(streamId, field.isCustom)
- self:debugWrite(field.isCustom,"field isCustom")
- streamDebugWriteInt32(streamId, field.fieldNum)
- self:debugWrite(field.fieldNum,"field fieldNum")
- streamDebugWriteInt32(streamId, field.dimensions.minX)
- self:debugWrite(field.dimensions.min,"field minX")
- streamDebugWriteInt32(streamId, field.dimensions.maxX)
- self:debugWrite(field.dimensions.maxX,"field maxX")
- streamDebugWriteInt32(streamId, field.dimensions.minZ)
- self:debugWrite(field.dimensions.minZ,"field minZ")
- streamDebugWriteInt32(streamId, field.dimensions.maxZ)
- self:debugWrite(field.dimensions.maxZ,"field maxZ")
- streamDebugWriteInt32(streamId, #(field.points))
- self:debugWrite(field.points,"field points")
- for p = 1, #(field.points) do
- streamDebugWriteFloat32(streamId, field.points[p].cx)
- self:debugWrite(field.points[p].cx,"field cx")
- streamDebugWriteFloat32(streamId, field.points[p].cy)
- self:debugWrite(field.points[p].cy,"field cy")
- streamDebugWriteFloat32(streamId, field.points[p].cz)
- self:debugWrite(field.points[p].cz,"field cz")
- end
+ CustomFieldEvent.writeField(field,streamId)
end
end
end;
@@ -327,37 +302,8 @@ function CourseplayJoinFixEvent:readStream(streamId, connection)
print(string.format("\t### CourseplayMultiplayer: reading %d custom fields ", fieldsCount))
courseplay.fields.fieldData = {}
for i = 1, fieldsCount do
- local name = streamReadString(streamId)
- self:debugRead(name,"field name")
- local numPoints = streamReadInt32(streamId)
- self:debugRead(numPoints,"field numPoints")
- local isCustom = streamReadBool(streamId)
- self:debugRead(isCustom,"field isCustom")
- local fieldNum = streamReadInt32(streamId)
- self:debugRead(fieldNum,"field fieldNum")
- local minX = streamReadInt32(streamId)
- self:debugRead(minX,"field minX")
- local maxX = streamReadInt32(streamId)
- self:debugRead(maxX,"field maxX")
- local minZ = streamReadInt32(streamId)
- self:debugRead(minZ,"field minZ")
- local maxZ = streamReadInt32(streamId)
- self:debugRead(maxZ,"field maxZ")
- local ammountPoints = streamReadInt32(streamId)
- self:debugRead(ammountPoints,"field numPoints")
- local waypoints = {}
- for w = 1, ammountPoints do
- local cx = streamReadFloat32(streamId)
- self:debugRead(cx,"field cx")
- local cy = streamReadFloat32(streamId)
- self:debugRead(cy,"field cy")
- local cz = streamReadFloat32(streamId)
- self:debugRead(cz,"field cz")
- local wp = { cx = cx, cy = cy, cz = cz}
- table.insert(waypoints, wp)
- end
- local field = { name = name, numPoints = numPoints, isCustom = isCustom, fieldNum = fieldNum, points = waypoints, dimensions = {minX = minX, maxX = maxX, minZ = minZ, maxZ = maxZ}}
- courseplay.fields.fieldData[fieldNum] = field
+ local field = CustomFieldEvent.readField(streamId)
+ courseplay.fields.fieldData[field.fieldNum] = field
end
print("\t### CourseplayMultiplayer: courses/folders reading end")
end;
diff --git a/fields.lua b/fields.lua
index b3dbd1c09..34f4fad1c 100644
--- a/fields.lua
+++ b/fields.lua
@@ -614,3 +614,94 @@ function courseplay.fields:getClosestDistanceToFieldEdge(fieldNum, x, z)
end
return closestDistance
end
+
+
+CpFieldUtil = {}
+
+--- Generates a custom field edge path, relative to the current vehicle position.
+---@param vehicle table
+---@return table data custom field edge path.
+function CpFieldUtil.generateCustomFieldEdgePath(vehicle)
+ local field = {}
+ local x,y,z = getWorldTranslation(vehicle.rootNode)
+ local isField = x and z and courseplay:isField(x, z, 0, 0) --TODO: use width/height of 0.1 ?
+ courseplay.fields:dbg(string.format("Custom field scan: x,z=%.1f,%.1f, isField=%s", x, z, tostring(isField)), 'customLoad')
+ if isField then
+ local edgePoints = courseplay.fields:setSingleFieldEdgePath(vehicle.rootNode, x, z, courseplay.fields.scanStep, 2000, 10, nil, true, 'customLoad')
+ field.points = edgePoints
+ field.numPoints = edgePoints ~= nil and #edgePoints or 0
+ end;
+ return field
+end;
+
+--- Saves a custom field edge path as custom field.
+---@param data table field edge path data.
+---@param ix number field number to save as.
+---@return table customField that was added.
+function CpFieldUtil.saveCustomFieldEdgePathAsField(data,ix)
+ data.fieldNum = ix
+ data.name = string.format("%s %d (%s)", courseplay:loc('COURSEPLAY_FIELD'), data.fieldNum, courseplay:loc('COURSEPLAY_USER'))
+ data.isCustom = true
+ local area, _, dimensions = courseplay.fields:getPolygonData(data.points, nil, nil, true)
+ data.areaSqm = area
+ data.areaHa = area / 10000
+ data.dimensions = dimensions
+ courseplay.fields.fieldData[data.fieldNum] = data
+ courseplay.fields.numAvailableFields = table.maxn(courseplay.fields.fieldData)
+ return data
+end
+
+--- Displays a custom field edge path.
+---@param vehicle table
+---@param fieldData table field edge path data.
+function CpFieldUtil.showFieldEdgePath(vehicle,fieldData)
+
+ local function show(points,numPoints)
+ if numPoints > 0 then
+ local pointHeight = 3;
+ for i,point in pairs(points) do
+ if i < numPoints then
+ local nextPoint = points[i + 1]
+ cpDebug:drawLine(point.cx,point.cy+pointHeight,point.cz, 0,0,1, nextPoint.cx,nextPoint.cy+pointHeight,nextPoint.cz)
+
+ if i == 1 then
+ cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 0,1,0)
+ else
+ cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 1,1,0)
+ end
+ else
+ cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 1,0,0)
+ end
+ end
+ end
+ end
+
+ if fieldData then
+ show(fieldData.points,fieldData.numPoints)
+ else
+ local points,numPoints = nil,0
+ points = courseplay.fields.fieldData[vehicle.cp.courseGeneratorSettings.selectedField:get()].points
+ numPoints = courseplay.fields.fieldData[vehicle.cp.courseGeneratorSettings.selectedField:get()].numPoints
+ show(points,numPoints)
+ end
+end
+
+--- Does a field with the number exist ?
+---@param num number
+---@return boolean
+function CpFieldUtil.isFieldNumberValid(num)
+ return courseplay.fields.fieldData[num] ~= nil
+end
+
+--- Saves a field send from the server.
+---@param field courseplay.fields.fieldData
+function CpFieldUtil.saveFieldFromNetwork(field)
+ courseplay.fields.fieldData[field.fieldNum] = field
+end
+
+function CpFieldUtil.getFieldName(ix)
+ local field = courseplay.fields.fieldData[ix]
+ if field then
+ return field.isCustom and string.format("%d(%s)",ix,courseplay:loc("COURSEPLAY_USER")) or string.format("%d",ix)
+ end
+end
\ No newline at end of file
diff --git a/gui/CourseGeneratorScreen.lua b/gui/CourseGeneratorScreen.lua
index d7c31c7a4..c1289ea9d 100644
--- a/gui/CourseGeneratorScreen.lua
+++ b/gui/CourseGeneratorScreen.lua
@@ -33,6 +33,7 @@ function CourseGeneratorScreen:new(vehicle)
self.returnScreenName = "";
self.state = CourseGeneratorScreen.SHOW_NOTHING
self.vehicle = vehicle
+ ---@type CourseGeneratorSettingsContainer
self.settings = vehicle.cp.courseGeneratorSettings
self.directions = {}
-- map to look up gui element state from angle
@@ -88,7 +89,6 @@ end
function CourseGeneratorScreen:onOpen()
g_currentMission.isPlayerFrozen = true
-
self.settings.selectedField:refresh()
-- work width not set
if self.settings.workWidth:is(0) then
@@ -447,6 +447,8 @@ end
function courseplay:openAdvancedCourseGeneratorSettings( vehicle )
--- Prevent Dialog from locking up mouse and keyboard when closing it.
courseplay:lockContext(false);
+ --- This settings needs to be refreshed here, as there are problems in MP otherwise.
+ vehicle.cp.courseGeneratorSettings.selectedField:refresh()
g_courseGeneratorScreen = CourseGeneratorScreen:new(vehicle)
g_gui:loadProfiles( self.path .. "gui/guiProfiles.xml" )
g_gui:loadGui( self.path .. "gui/CourseGeneratorScreen.xml", "CourseGeneratorScreen", g_courseGeneratorScreen)
diff --git a/hud.lua b/hud.lua
index 82c69908e..3a6182dba 100644
--- a/hud.lua
+++ b/hud.lua
@@ -464,8 +464,9 @@ function courseplay.hud:setContent(vehicle)
end;
--setup bottomInfo texts
- if vehicle.cp.timeRemaining ~= nil then
- local timeRemaining = courseplay:sekToTimeFormat(vehicle.cp.timeRemaining)
+ local timeRemaining = vehicle.cp.driver.getRemainingTime and vehicle.cp.driver:getRemainingTime()
+ if timeRemaining ~= nil then
+ timeRemaining = courseplay:sekToTimeFormat(timeRemaining)
vehicle.cp.hud.content.bottomInfo.timeRemainingText = ('%02.f:%02.f:%02.f'):format(timeRemaining.nHours,timeRemaining.nMins,timeRemaining.nSecs)
else
vehicle.cp.hud.content.bottomInfo.timeRemainingText = nil
@@ -606,10 +607,11 @@ function courseplay.hud:renderHud(vehicle)
courseplay:setFontSettings('white', false);
end;
- --field edge path
- if vehicle:getIsActive() and not vehicle.cp.canDrive and vehicle.cp.fieldEdge.customField.show and vehicle.cp.fieldEdge.customField.points ~= nil then
- courseplay:showFieldEdgePath(vehicle, "customField");
- end;
+ --- Displays a custom field path, if it was generated.
+ local setting = vehicle.cp.courseGeneratorSettings.fieldEdgePath
+ if vehicle:getIsActive() and setting:isUnsavedCustomFieldPathVisible() then
+ setting:showCustomFieldEdgePath()
+ end
end;
@@ -714,14 +716,11 @@ function courseplay.hud:updatePageContent(vehicle, page)
--ReturnToFirstPointSetting
if not vehicle.cp.settings.returnToFirstPoint:isDisabled() then
self:enableButtonWithFunction(vehicle,page, 'changeByX',vehicle.cp.settings.returnToFirstPoint)
- self:disableButtonWithFunction(vehicle,page, 'setCustomSingleFieldEdge')
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.returnToFirstPoint:getLabel()
vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.returnToFirstPoint:getText()
else
self:disableButtonWithFunction(vehicle,page, 'changeByX',vehicle.cp.settings.returnToFirstPoint)
forceUpdate = true -- force reload of this page if functionToCall changed
- entry.functionToCall = 'setCustomSingleFieldEdge'
- self:enableButtonWithFunction(vehicle,page, 'setCustomSingleFieldEdge')
courseplay.hud:setReloadPageOrder(vehicle, page, true);
end
@@ -828,96 +827,32 @@ function courseplay.hud:updatePageContent(vehicle, page)
local setting = vehicle.cp.settings.turnDiameter
vehicle.cp.hud.content.pages[page][line][1].text = setting:getLabel()
vehicle.cp.hud.content.pages[page][line][2].text = setting:getText()
- elseif entry.functionToCall == 'changeWorkWidth' then
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_WORK_WIDTH');
- if vehicle.cp.manualWorkWidth then
- vehicle.cp.hud.content.pages[page][line][2].text = string.format('%.1fm (mnl)', vehicle.cp.workWidth);
- else
- vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.workWidth ~= nil and string.format('%.1fm', vehicle.cp.workWidth) or '---';
- end
-
- elseif entry.functionToCall == 'changeLaneNumber' then
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_LANE_OFFSET');
- if vehicle.cp.courseGeneratorSettings.multiTools:get() > 1 then
- self:enableButtonWithFunction(vehicle,page, 'changeLaneNumber')
- if vehicle.cp.laneNumber == 0 then
- vehicle.cp.hud.content.pages[page][line][2].text = ('%s'):format(courseplay:loc('COURSEPLAY_CENTER'));
- else
- vehicle.cp.hud.content.pages[page][line][2].text = ('%d %s'):format(abs(vehicle.cp.laneNumber), courseplay:loc(vehicle.cp.laneNumber > 0 and 'COURSEPLAY_RIGHT' or 'COURSEPLAY_LEFT'));
- end
- else
- self:disableButtonWithFunction(vehicle,page, 'changeLaneNumber')
- end
-
- elseif entry.functionToCall == 'changeLaneOffset' then
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_LANE_OFFSET');
- if vehicle.cp.courseGeneratorSettings.multiTools:get() == 1 then
- self:enableButtonWithFunction(vehicle,page, 'changeLaneOffset')
- if vehicle.cp.laneOffset and vehicle.cp.laneOffset ~= 0 then
- vehicle.cp.hud.content.pages[page][line][2].text = ('%.1f%s (%s)'):format(abs(vehicle.cp.laneOffset), courseplay:loc('COURSEPLAY_UNIT_METER'), courseplay:loc(vehicle.cp.laneOffset > 0 and 'COURSEPLAY_RIGHT' or 'COURSEPLAY_LEFT'));
- else
- vehicle.cp.hud.content.pages[page][line][2].text = '---';
- end
+ elseif entry.functionToCall == 'workWidth:changeByX' then
+ local setting = vehicle.cp.courseGeneratorSettings.workWidth
+ vehicle.cp.hud.content.pages[page][line][1].text = setting:getLabel()
+ vehicle.cp.hud.content.pages[page][line][2].text = setting:getText()
+ elseif entry.functionToCall == 'multiTools:changeLaneNumber' then
+ local setting = vehicle.cp.courseGeneratorSettings.multiTools
+ vehicle.cp.hud.content.pages[page][line][1].text = setting.laneNumber:getLabel()
+ if setting:get() > 1 then
+ self:enableButtonWithFunction(vehicle,page, 'changeLaneNumber',setting)
+ vehicle.cp.hud.content.pages[page][line][2].text = setting.laneNumber:getText()
else
- self:disableButtonWithFunction(vehicle,page, 'changeLaneOffset')
+ self:disableButtonWithFunction(vehicle,page, 'changeLaneNumber',setting)
end
+
elseif entry.functionToCall == 'turnOnField:toggle' then
--TurnOnFieldSetting
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.turnOnField:getLabel()
vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.turnOnField:getText()
- elseif entry.functionToCall == 'setCustomSingleFieldEdge' then
- if not vehicle.cp.fieldEdge.customField.isCreated
- and not vehicle:getIsCourseplayDriving()
- and not vehicle.cp.canDrive
- and not vehicle.cp.isRecording
- and not vehicle.cp.recordingIsPaused then
- self:enableButtonWithFunction(vehicle,page, 'setCustomSingleFieldEdge')
- self:disableButtonWithFunction(vehicle,page, 'changeByX',vehicle.cp.settings.returnToFirstPoint)
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_SCAN_CURRENT_FIELD_EDGES');
- else
- self:disableButtonWithFunction(vehicle,page, 'setCustomSingleFieldEdge')
- forceUpdate = true -- force reload of this page if functionToCall changed
- entry.functionToCall = 'returnToFirstPoint:changeByX'
- self:enableButtonWithFunction(vehicle, page, 'changeByX',vehicle.cp.settings.returnToFirstPoint)
- courseplay.hud:setReloadPageOrder(vehicle, page, true);
- end
- elseif entry.functionToCall == 'setCustomFieldEdgePathNumber' then
- if vehicle.cp.fieldEdge.customField.isCreated and not vehicle.cp.canDrive then
- self:enableButtonWithFunction(vehicle,page, 'setCustomFieldEdgePathNumber')
- vehicle.cp.hud.clearCustomFieldEdgeButton:setShow(true)
- vehicle.cp.hud.toggleCustomFieldEdgePathShowButton:setShow(true)
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_CURRENT_FIELD_EDGE_PATH_NUMBER');
- if vehicle.cp.fieldEdge.customField.fieldNum > 0 then
- vehicle.cp.hud.content.pages[page][line][2].text = tostring(vehicle.cp.fieldEdge.customField.fieldNum);
- else
- vehicle.cp.hud.content.pages[page][line][2].text = '---';
- end;
- else
- self:disableButtonWithFunction(vehicle,page, 'setCustomFieldEdgePathNumber')
- vehicle.cp.hud.clearCustomFieldEdgeButton:setShow(false)
- vehicle.cp.hud.toggleCustomFieldEdgePathShowButton:setShow(false)
- end
-
-
- elseif entry.functionToCall == 'addCustomSingleFieldEdgeToList' then
- if vehicle.cp.fieldEdge.customField.isCreated and vehicle.cp.fieldEdge.customField.fieldNum > 0 and not vehicle.cp.canDrive then
- self:enableButtonWithFunction(vehicle,page, 'addCustomSingleFieldEdgeToList')
- if vehicle.cp.fieldEdge.customField.selectedFieldNumExists then
- vehicle.cp.hud.content.pages[page][line][1].text = string.format(courseplay:loc('COURSEPLAY_OVERWRITE_CUSTOM_FIELD_EDGE_PATH_IN_LIST'), vehicle.cp.fieldEdge.customField.fieldNum);
- else
- vehicle.cp.hud.content.pages[page][line][1].text = string.format(courseplay:loc('COURSEPLAY_ADD_CUSTOM_FIELD_EDGE_PATH_TO_LIST'), vehicle.cp.fieldEdge.customField.fieldNum);
- end;
- else
- self:disableButtonWithFunction(vehicle,page, 'addCustomSingleFieldEdgeToList')
- end
+ elseif entry.functionToCall == 'fieldEdgePath:changeByX' then
+ --- Updates the custom field edge buttons and the texts.
+ self:updateFieldEdgePathTextsAndButtons(vehicle,page,line)
elseif entry.functionToCall == 'symmetricLaneChange:toggle' then
--SymmetricLaneChangeSetting
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.symmetricLaneChange:getLabel()
- if vehicle.cp.laneOffset ~= 0 then
- vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.symmetricLaneChange:getText()
- else
- vehicle.cp.hud.content.pages[page][line][2].text = '---';
- end;
+ vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.symmetricLaneChange:getText()
+
elseif entry.functionToCall == 'changeToolOffsetX' then
--Tool horizontal offset
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.toolOffsetX:getLabel()
@@ -1141,9 +1076,6 @@ function courseplay.hud:updatePageContent(vehicle, page)
--ShovelModeAIDriverTriggerHandlerIsActiveSetting
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.shovelModeAIDriverTriggerHandlerIsActive:getLabel()
vehicle.cp.hud.content.pages[page][line][2].text = vehicle.cp.settings.shovelModeAIDriverTriggerHandlerIsActive:getText()
- elseif entry.functionToCall == 'changeBladeWorkWidth' then
- vehicle.cp.hud.content.pages[page][line][1].text = courseplay:loc('COURSEPLAY_MODE10_BLADE_WIDTH');
- vehicle.cp.hud.content.pages[page][line][2].text = ('%.1f%s'):format(vehicle.cp.workWidth, courseplay:loc('COURSEPLAY_UNIT_METER'));
elseif entry.functionToCall == 'bunkerSpeed:changeByX' then
--BunkerSpeedSetting
vehicle.cp.hud.content.pages[page][line][1].text = vehicle.cp.settings.bunkerSpeed:getLabel()
@@ -1251,6 +1183,68 @@ function courseplay.hud:updateWorkingToolPositionTexts(vehicle,page,line,setting
end
--------------------------------------
+--- Updates the custom field edge buttons and the texts.
+---@param vehicle table
+---@param page number
+---@param line number
+function courseplay.hud:updateFieldEdgePathTextsAndButtons(vehicle,page,line)
+
+ local function disableBtn(btn)
+ btn:setDisabled(true)
+ btn:setShow(false)
+
+ end
+
+ local function enableBtn(btn)
+ btn:setDisabled(false)
+ btn:setShow(true)
+ end
+
+ local setting = vehicle.cp.courseGeneratorSettings.fieldEdgePath
+ local isDisabled = setting:isDisabled()
+ --- Is a field edge path generated, but not saved yet.
+ local hasCustomFieldPath = setting:hasUnsavedCustomFieldPath()
+ local buttons = vehicle.cp.hud.fieldEdgePathButtons
+ if not isDisabled then
+ if hasCustomFieldPath then
+ disableBtn(buttons.create)
+ enableBtn(buttons.clear)
+ --- Only allow saving when a valid field number is displayed
+ --- and change the tooltip.
+ if setting:isValidNumber() then
+ enableBtn(buttons.save)
+ buttons.save:setToolTip(setting:getSaveLabel())
+ else
+ disableBtn(buttons.save)
+ end
+ enableBtn(buttons.toggleVisibility)
+ for _,btn in pairs(buttons.selectNumber) do
+ enableBtn(btn)
+ end
+ vehicle.cp.hud.content.pages[page][line][1].text = setting:getCurrentNrLabel()
+ vehicle.cp.hud.content.pages[page][line][2].text = setting:getText()
+ else
+ enableBtn(buttons.create)
+ disableBtn(buttons.clear)
+ disableBtn(buttons.save)
+ disableBtn(buttons.toggleVisibility)
+ for _,btn in pairs(buttons.selectNumber) do
+ disableBtn(btn)
+ end
+ vehicle.cp.hud.content.pages[page][line][1].text = setting:getCreateLabel()
+ end
+ else
+ for name,btn in pairs(buttons) do
+ if name == "selectNumber" then
+ for _,b in pairs(btn) do
+ disableBtn(b)
+ end
+ else
+ disableBtn(btn)
+ end
+ end
+ end
+end
function courseplay.hud:setReloadPageOrder(vehicle, page, bool)
-- self = courseplay.hud
@@ -1522,9 +1516,27 @@ function courseplay.hud:setupCopyCourseButton(vehicle,page,line)
vehicle.cp.hud.copyCourseButton = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'copy' }, 'copyCourse', nil, self.buttonPosX[3], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h);
end
+--- Setups the custom field edge buttons.
+---@param vehicle table
+---@param page number
+---@param line number
function courseplay.hud:setupCustomFieldEdgeButtons(vehicle,page,line)
- vehicle.cp.hud.clearCustomFieldEdgeButton = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'cancel' }, 'clearCustomFieldEdge', nil, self.buttonPosX[4], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, nil, false);
- vehicle.cp.hud.toggleCustomFieldEdgePathShowButton = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'eye' }, 'toggleCustomFieldEdgePathShow', nil, self.buttonPosX[3], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, nil, false);
+ local setting = vehicle.cp.courseGeneratorSettings.fieldEdgePath
+ vehicle.cp.hud.fieldEdgePathButtons = {
+ clear = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'cancel' }, 'clearPath',
+ nil, self.buttonPosX[4], self.linesButtonPosY[line], self.buttonSize.small.w,
+ self.buttonSize.small.h, line, nil, false,
+ nil,nil,setting:getClearLabel()):setSetting(setting),
+ toggleVisibility = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'eye' }, 'toggleVisibility',
+ nil, self.buttonPosX[3], self.linesButtonPosY[line], self.buttonSize.small.w,
+ self.buttonSize.small.h, line, nil, false,
+ nil,nil,setting:getVisibilityLabel()):setSetting(setting),
+ save = courseplay.button:new(vehicle, page, { 'iconSprite.png', 'save' }, 'savePath',
+ nil, self.buttonPosX[5], self.linesButtonPosY[line], self.buttonSize.small.w,
+ self.buttonSize.small.h, line, nil, false):setSetting(setting),
+ create = self:addRowButton(vehicle,setting,'createPath', page, line, 1 ),
+ selectNumber = self:addSettingsRow(vehicle,setting,'changeByX', page, line, 2 )
+ }
end
function courseplay.hud:setupCombinesListPageButtons(vehicle,page,assignedCombinesSetting)
@@ -1618,7 +1630,7 @@ function courseplay.hud:setupCourseGeneratorButton(vehicle)
end
function courseplay.hud:setupCalculateWorkWidthButton(vehicle,page,line)
- courseplay.button:new(vehicle, page, { 'iconSprite.png', 'calculator' }, 'calculateWorkWidth', nil, self.buttonPosX[3], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, nil, false);
+ courseplay.button:new(vehicle, page, { 'iconSprite.png', 'calculator' }, 'setToDefault', nil, self.buttonPosX[3], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, nil, false):setSetting(vehicle.cp.courseGeneratorSettings.workWidth);
end
function courseplay.hud:setupSetAutoToolOffsetXButton(vehicle,page,line)
@@ -2153,10 +2165,9 @@ function courseplay.hud:setFieldWorkAIDriverContent(vehicle)
--self:setupCourseGeneratorButton(vehicle)
self:addRowButton(vehicle,vehicle.cp.settings.autoDriveMode,'changeByX', 1, 3, 1 )
self:addRowButton(vehicle,nil,'openAdvancedCourseGeneratorSettings', 1, 4, 1 ):setOnlyCallLocal()
- self:addRowButton(vehicle,nil,'setCustomSingleFieldEdge', 1, 5, 1 )
- self:addSettingsRow(vehicle,nil,'setCustomFieldEdgePathNumber', 1, 5, 2 )
- self:setupCustomFieldEdgeButtons(vehicle,1,5)
- self:addRowButton(vehicle,nil,'addCustomSingleFieldEdgeToList', 1, 6, 1 )
+
+ self:setupCustomFieldEdgeButtons(vehicle,1,6)
+
-- shown in place of the custom field row when a course is loaded
self:addRowButton(vehicle,vehicle.cp.settings.returnToFirstPoint,'changeByX', 1, 5, 1 )
self:addRowButton(vehicle,vehicle.cp.settings.foldImplementAtEnd,'toggle', 6, 6, 1 )
@@ -2164,7 +2175,7 @@ function courseplay.hud:setFieldWorkAIDriverContent(vehicle)
--page 3 settings
self:enablePageButton(vehicle, 3)
self:addSettingsRow(vehicle,vehicle.cp.settings.turnDiameter,'changeByX', 3, 1, 1 )
- self:addSettingsRow(vehicle,nil,'changeWorkWidth', 3, 2, 1, 0.1 )
+ self:addSettingsRow(vehicle,vehicle.cp.courseGeneratorSettings.workWidth,'changeByX', 3, 2, 1)
self:setupCalculateWorkWidthButton(vehicle,3, 2)
self:addRowButton(vehicle,vehicle.cp.settings.convoyActive,'toggle', 3, 3, 1 )
self:addSettingsRow(vehicle,vehicle.cp.settings.convoyMinDistance,'changeByX', 3, 4, 1 )
@@ -2173,8 +2184,7 @@ function courseplay.hud:setFieldWorkAIDriverContent(vehicle)
--page 8 fieldwork settings
self:enablePageButton(vehicle, 8)
- self:addSettingsRowWithArrows(vehicle,nil,'changeLaneNumber', 8, 1, 1 )
- self:addSettingsRowWithArrows(vehicle,nil,'changeLaneOffset', 8, 1, 2 )
+ self:addSettingsRowWithArrows(vehicle,vehicle.cp.courseGeneratorSettings.multiTools,'changeLaneNumber', 8, 1, 1 )
self:addRowButton(vehicle,vehicle.cp.settings.symmetricLaneChange,'toggle', 8, 2, 1 )
self:addRowButton(vehicle,vehicle.cp.settings.turnOnField,'toggle', 8, 3, 1 )
self:addRowButton(vehicle,vehicle.cp.settings.useRealisticDriving,'toggle', 8, 4, 1 )
@@ -2285,7 +2295,7 @@ function courseplay.hud:setShovelModeAIDriverContent(vehicle)
self:setupToolPositionButtons(vehicle,vehicle.cp.settings.frontloaderToolPositions,9,1)
self:addRowButton(vehicle,vehicle.cp.settings.alwaysWaitForShovelPositions,'toggle', 9, 5, 1 )
self:addRowButton(vehicle,vehicle.cp.settings.shovelModeAIDriverTriggerHandlerIsActive,'toggle', 9, 8, 1 )
- self:addSettingsRow(vehicle,nil,'changeWorkWidth',9,6,1, 0.1)
+ self:addSettingsRow(vehicle,vehicle.cp.courseGeneratorSettings.workWidth,'changeByX', 9, 6, 1)
self:setupCalculateWorkWidthButton(vehicle,9, 6)
self:addSettingsRow(vehicle,vehicle.cp.settings.bunkerSpeed,'changeByX', 9, 7, 1 )
end
@@ -2307,7 +2317,7 @@ function courseplay.hud:setLevelCompactAIDriverContent(vehicle)
self:addRowButton(vehicle,vehicle.cp.settings.levelCompactSearchOnlyAutomatedDriver,'changeByX', 10, 1, 1)
self:addRowButton(vehicle,vehicle.cp.settings.levelCompactSiloTyp,'changeByX', 10, 2, 1)
self:addSettingsRow(vehicle,vehicle.cp.settings.levelCompactSearchRadius,'changeByX', 10, 3, 1 )
- self:addSettingsRow(vehicle,nil,'changeBladeWorkWidth', 10, 4, 1)
+ self:addSettingsRow(vehicle,vehicle.cp.courseGeneratorSettings.workWidth,'changeByX', 10, 4, 1)
self:setupCalculateWorkWidthButton(vehicle,10, 4)
self:addSettingsRow(vehicle,vehicle.cp.settings.bunkerSpeed,'changeByX', 10, 5, 1 )
-- self:addSettingsRow(vehicle,vehicle.cp.settings.levelCompactShieldHeight,'changeByX', 10, 6, 1 )
@@ -2318,10 +2328,9 @@ function courseplay.hud:setBaleCollectorAIDriverContent(vehicle)
self:debug(vehicle,"setBaleCollectorAIDriverContent")
self:addRowButton(vehicle,vehicle.cp.settings.autoDriveMode,'changeByX', 1, 3, 1 )
self:addSettingsRow(vehicle, vehicle.cp.settings.baleCollectionField,'changeByX', 1, 4, 1 )
- self:addRowButton(vehicle,nil,'setCustomSingleFieldEdge', 1, 5, 1 )
- self:addSettingsRow(vehicle,nil,'setCustomFieldEdgePathNumber', 1, 5, 2 )
- self:setupCustomFieldEdgeButtons(vehicle,1,5)
- self:addRowButton(vehicle,nil,'addCustomSingleFieldEdgeToList', 1, 6, 1 )
+
+ self:setupCustomFieldEdgeButtons(vehicle,1,6)
+
-- shown in place of the custom field row when a course is loaded
--page 3 settings
@@ -2352,7 +2361,7 @@ function courseplay.hud:setMixerWagonAIDriverContent(vehicle)
--page 9
self:enablePageButton(vehicle, 9)
self:setupToolPositionButtons(vehicle,vehicle.cp.settings.mixerWagonToolPositions,9,1)
- self:addSettingsRow(vehicle,nil,'changeWorkWidth',9,7,1, 0.1)
+ self:addSettingsRow(vehicle,vehicle.cp.courseGeneratorSettings.workWidth,'changeByX', 9, 7, 1)
self:setupCalculateWorkWidthButton(vehicle,9, 7)
self:addSettingsRow(vehicle,vehicle.cp.settings.bunkerSpeed,'changeByX', 9, 8, 1 )
end
@@ -2360,7 +2369,7 @@ end
function courseplay.hud:setBunkerSiloLoaderAIDriverContent(vehicle)
--page 9
self:enablePageButton(vehicle, 9)
- self:addSettingsRow(vehicle,nil,'changeWorkWidth',9,1,1, 0.1)
+ self:addSettingsRow(vehicle,vehicle.cp.courseGeneratorSettings.workWidth,'changeByX', 9, 1, 1)
self:setupCalculateWorkWidthButton(vehicle,9, 1)
end
@@ -2387,14 +2396,15 @@ end
function courseplay.hud:addSettingsRow(vehicle,setting,funct, hudPage, line, column,parameter )
self:debug(vehicle," addSettingsRow: "..tostring(funct))
local parameter = parameter or 1
- courseplay.button:new(vehicle, hudPage, { 'iconSprite.png', 'navMinus' }, funct, -parameter, self.buttonPosX[2], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, -5, false):setSetting(setting);
- courseplay.button:new(vehicle, hudPage, { 'iconSprite.png', 'navPlus' }, funct, parameter, self.buttonPosX[1], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, 5, false):setSetting(setting);
- courseplay.button:new(vehicle, hudPage, nil, funct, parameter, self.contentMinX, self.linesButtonPosY[line], self.contentMaxWidth, self.lineHeight, line, 5, true, true):setSetting(setting);
+ local b1 = courseplay.button:new(vehicle, hudPage, { 'iconSprite.png', 'navMinus' }, funct, -parameter, self.buttonPosX[2], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, -5, false):setSetting(setting);
+ local b2 = courseplay.button:new(vehicle, hudPage, { 'iconSprite.png', 'navPlus' }, funct, parameter, self.buttonPosX[1], self.linesButtonPosY[line], self.buttonSize.small.w, self.buttonSize.small.h, line, 5, false):setSetting(setting);
+ local b3 = courseplay.button:new(vehicle, hudPage, nil, funct, parameter, self.contentMinX, self.linesButtonPosY[line], self.contentMaxWidth, self.lineHeight, line, 5, true, true):setSetting(setting);
if setting then
vehicle.cp.hud.content.pages[hudPage][line][column].functionToCall = setting:getName()..":"..funct
else
vehicle.cp.hud.content.pages[hudPage][line][column].functionToCall = funct
end
+ return {b1,b2,b3}
end
function courseplay.hud:addSettingsRowWithArrows(vehicle,setting,funct, hudPage, line, column )
diff --git a/settings.lua b/settings.lua
index 805db7813..ccc3bae47 100644
--- a/settings.lua
+++ b/settings.lua
@@ -97,48 +97,6 @@ function courseplay:setHudPage(vehicle, pageNum)
courseplay.hud:setReloadPageOrder(vehicle, vehicle.cp.hud.currentPage, true);
end;
-function courseplay:changeLaneOffset(vehicle, changeBy, force)
- vehicle.cp.laneOffset = force or (courseplay:round(vehicle.cp.laneOffset, 1) + changeBy*0.1);
- if abs(vehicle.cp.laneOffset) < 0.1 then
- vehicle.cp.laneOffset = 0;
- end;
-end;
-
-function courseplay:changeLaneNumber(vehicle, changeBy, reset)
- -- This function takes input from the hud. And calculates laneOffset by dividing tool workwidth and multiplying that
- -- by the lane number counting outwards.
- local toolsIsEven = vehicle.cp.courseGeneratorSettings.multiTools:get() % 2 == 0
-
- if reset then
- vehicle.cp.laneNumber = 0;
- vehicle.cp.laneOffset = 0
- else
- -- skip zero if multiTools is even
- if toolsIsEven then
- if vehicle.cp.laneNumber == -1 and changeBy > 0 then
- changeBy = 2
- elseif vehicle.cp.laneNumber == 1 and changeBy < 0 then
- changeBy = -2
- end
- end
- vehicle.cp.laneNumber = MathUtil.clamp(vehicle.cp.laneNumber + changeBy,
- math.floor(vehicle.cp.courseGeneratorSettings.multiTools:get() / 2) * -1,
- math.floor(vehicle.cp.courseGeneratorSettings.multiTools:get() / 2));
- local newOffset = 0
- if toolsIsEven then
- if vehicle.cp.laneNumber > 0 then
- newOffset = vehicle.cp.workWidth/2 + (vehicle.cp.workWidth*(vehicle.cp.laneNumber-1))
- else
- newOffset = -vehicle.cp.workWidth/2 + (vehicle.cp.workWidth*(vehicle.cp.laneNumber+1))
- end
- else
- newOffset = vehicle.cp.workWidth * vehicle.cp.laneNumber
- end
- courseplay:changeLaneOffset(vehicle, nil , newOffset)
- end;
-
-end;
-
--- These three tool offset function should be handled by the setting.
function courseplay:changeToolOffsetX(vehicle, changeBy)
vehicle.cp.settings.toolOffsetX:changeBy(changeBy * 0.1,true)
@@ -157,70 +115,6 @@ function courseplay:changeToolOffsetZ(vehicle, changeBy, force, noDraw)
courseplay:setCustomTimer(vehicle, 'showWorkWidth', 2);
end;
-function courseplay:calculateWorkWidth(vehicle, noDraw)
-
- if vehicle.cp.manualWorkWidth and noDraw ~= nil then
- --courseplay:changeWorkWidth(vehicle, nil, vehicle.cp.manualWorkWidth, noDraw);
- return
- end
-
- courseplay:changeWorkWidth(vehicle, nil,vehicle.cp.courseGeneratorSettings.workWidth:getAutoWorkWidth(), noDraw);
-
-end;
-
-function courseplay:changeBladeWorkWidth(vehicle, changeBy, force, noDraw)
- courseplay:changeWorkWidth(vehicle, changeBy/10, force, noDraw)
-end
-
-function courseplay:changeWorkWidth(vehicle, changeBy, force, noDraw)
- local isSetManually = false
- if force == nil and noDraw == nil then
- --print("is set manually")
- isSetManually = true
- elseif force ~= nil and noDraw ~= nil then
- --print("is set by script")
- if not vehicle.cp.isDriving and vehicle.cp.manualWorkWidth then
- return
- end
- elseif force ~= nil and noDraw == nil then
- vehicle.cp.manualWorkWidth = nil
- courseplay:changeLaneNumber(vehicle, 0, true)
- vehicle.cp.courseGeneratorSettings.multiTools:set(1)
- --print("is set by calculate button")
- end
- if force then
- if force == 0 then
- return
- end
- local newWidth = max(courseplay:round(abs(force), 1), 0.1)
- --vehicle.cp.workWidth = min(vehicle.cp.workWidth,newWidth); --TODO: check what is better:the smallest or the widest work width to consider
- vehicle.cp.workWidth = newWidth
- else
- if vehicle.cp.workWidth + changeBy > 10 then
- if abs(changeBy) == 0.1 and not (Input.keyPressedState[Input.KEY_lalt]) then -- pressing left Alt key enables to have small 0.1 steps even over 10.0
- changeBy = 0.5 * MathUtil.sign(changeBy);
- elseif abs(changeBy) == 0.5 then
- changeBy = 2 * MathUtil.sign(changeBy);
- end;
- end;
-
- if (vehicle.cp.workWidth < 10 and vehicle.cp.workWidth + changeBy > 10) or (vehicle.cp.workWidth > 10 and vehicle.cp.workWidth + changeBy < 10) then
- vehicle.cp.workWidth = 10;
- else
- vehicle.cp.workWidth = max(vehicle.cp.workWidth + changeBy, 0.1);
- end;
- end;
- if isSetManually then
- vehicle.cp.manualWorkWidth = vehicle.cp.workWidth
- end
- if not noDraw then
- courseplay:setCustomTimer(vehicle, 'showWorkWidth', 2);
- end;
-
- courseplay.hud:setReloadPageOrder(vehicle, vehicle.cp.hud.currentPage, true);
-
-end;
-
--legancy Code in toolManager still using it!
function courseplay:changeReverseSpeed(vehicle, changeBy, force, forceReloadPage)
local speed = force or (vehicle.cp.speeds.reverse + changeBy);
@@ -602,10 +496,10 @@ end
function courseplay:validateCanSwitchMode(vehicle)
vehicle:setCpVar('canSwitchMode', not vehicle:getIsCourseplayDriving() and not vehicle.cp.isRecording and
- not vehicle.cp.recordingIsPaused and not vehicle.cp.fieldEdge.customField.isCreated,courseplay.isClient);
- courseplay:debug(('%s: validateCanSwitchMode(): isDriving=%s, isRecording=%s, recordingIsPaused=%s, customField.isCreated=%s ==> canSwitchMode=%s'):format(
+ not vehicle.cp.recordingIsPaused ,courseplay.isClient);
+ courseplay:debug(('%s: validateCanSwitchMode(): isDriving=%s, isRecording=%s, recordingIsPaused=%s ==> canSwitchMode=%s'):format(
nameNum(vehicle), tostring(vehicle:getIsCourseplayDriving()), tostring(vehicle.cp.isRecording),
- tostring(vehicle.cp.recordingIsPaused), tostring(vehicle.cp.fieldEdge.customField.isCreated), tostring(vehicle.cp.canSwitchMode)), courseplay.DBG_UNCATEGORIZED);
+ tostring(vehicle.cp.recordingIsPaused), tostring(vehicle.cp.canSwitchMode)), courseplay.DBG_UNCATEGORIZED);
end;
function courseplay:reloadCoursesFromXML(vehicle)
@@ -678,135 +572,6 @@ function courseplay:changeDebugChannelSection(vehicle, changeBy)
end
end
-
---FIELD EDGE PATHS
-function courseplay:createFieldEdgeButtons(vehicle)
- if not vehicle.cp.fieldEdge.selectedField.buttonsCreated and courseplay.fields.numAvailableFields > 0 then
- local w, h = courseplay.hud.buttonSize.small.w, courseplay.hud.buttonSize.small.h;
- local mouseWheelArea = {
- x = courseplay.hud.contentMinX,
- w = courseplay.hud.contentMaxWidth,
- h = courseplay.hud.lineHeight
- };
- vehicle.cp.hud.showSelectedFieldEdgePathButton = courseplay.button:new(vehicle, 8, { 'iconSprite.png', 'eye' }, 'toggleSelectedFieldEdgePathShow', nil, courseplay.hud.buttonPosX[3], courseplay.hud.linesButtonPosY[1], w, h, 1, nil, false);
- courseplay.button:new(vehicle, 8, { 'iconSprite.png', 'navUp' }, 'setFieldEdgePath', 1, courseplay.hud.buttonPosX[1], courseplay.hud.linesButtonPosY[1], w, h, 1, 5, false);
- courseplay.button:new(vehicle, 8, { 'iconSprite.png', 'navDown' }, 'setFieldEdgePath', -1, courseplay.hud.buttonPosX[2], courseplay.hud.linesButtonPosY[1], w, h, 1, -5, false);
- courseplay.button:new(vehicle, 8, nil, 'setFieldEdgePath', 1, mouseWheelArea.x, courseplay.hud.linesButtonPosY[1], mouseWheelArea.w, mouseWheelArea.h, 1, 5, true, true);
- vehicle.cp.fieldEdge.selectedField.buttonsCreated = true;
- end;
-end;
-
-function courseplay:setFieldEdgePath(vehicle, changeDir, force)
- vehicle.cp.courseGeneratorSettings.selectedField:changeByX(changeDir)
-
- --courseplay:toggleSelectedFieldEdgePathShow(vehicle, false);
- if vehicle.cp.fieldEdge.customField.show then
- courseplay:toggleCustomFieldEdgePathShow(vehicle, false);
- end;
-end;
-
-function courseplay:toggleSelectedFieldEdgePathShow(vehicle, force)
- vehicle.cp.fieldEdge.selectedField.show = Utils.getNoNil(force, not vehicle.cp.fieldEdge.selectedField.show);
- --print(string.format("%s: selectedField.show=%s", nameNum(vehicle), tostring(vehicle.cp.fieldEdge.selectedField.show)));
- --courseplay.buttons:setActiveEnabled(vehicle, "selectedFieldShow");
-end;
-
---CUSTOM SINGLE FIELD EDGE PATH
-function courseplay:setCustomSingleFieldEdge(vehicle)
- --print(string.format("%s: call setCustomSingleFieldEdge()", nameNum(vehicle)));
-
- local x,y,z = getWorldTranslation(vehicle.rootNode);
- local isField = x and z and courseplay:isField(x, z, 0, 0); --TODO: use width/height of 0.1 ?
- courseplay.fields:dbg(string.format("Custom field scan: x,z=%.1f,%.1f, isField=%s", x, z, tostring(isField)), 'customLoad');
- vehicle.cp.fieldEdge.customField.points = nil;
- if isField then
- local edgePoints = courseplay.fields:setSingleFieldEdgePath(vehicle.rootNode, x, z, courseplay.fields.scanStep, 2000, 10, nil, true, 'customLoad');
- vehicle.cp.fieldEdge.customField.points = edgePoints;
- vehicle.cp.fieldEdge.customField.numPoints = edgePoints ~= nil and #edgePoints or 0;
- end;
-
- --print(tableShow(vehicle.cp.fieldEdge.customField.points, nameNum(vehicle) .. " fieldEdge.customField.points"));
- vehicle.cp.fieldEdge.customField.isCreated = vehicle.cp.fieldEdge.customField.points ~= nil;
- courseplay:toggleCustomFieldEdgePathShow(vehicle, vehicle.cp.fieldEdge.customField.isCreated);
- courseplay:validateCanSwitchMode(vehicle);
-end;
-
-function courseplay:clearCustomFieldEdge(vehicle)
- vehicle.cp.fieldEdge.customField.points = nil;
- vehicle.cp.fieldEdge.customField.numPoints = 0;
- vehicle.cp.fieldEdge.customField.isCreated = false;
- courseplay:setCustomFieldEdgePathNumber(vehicle, nil, 0);
- courseplay:toggleCustomFieldEdgePathShow(vehicle, false);
- courseplay:validateCanSwitchMode(vehicle);
-end;
-
-function courseplay:toggleCustomFieldEdgePathShow(vehicle, force)
- vehicle.cp.fieldEdge.customField.show = Utils.getNoNil(force, not vehicle.cp.fieldEdge.customField.show);
- --print(string.format("%s: customField.show=%s", nameNum(vehicle), tostring(vehicle.cp.fieldEdge.customField.show)));
- --courseplay.buttons:setActiveEnabled(vehicle, "customFieldShow");
-end;
-
-function courseplay:setCustomFieldEdgePathNumber(vehicle, changeBy, force)
- vehicle.cp.fieldEdge.customField.fieldNum = force or MathUtil.clamp(vehicle.cp.fieldEdge.customField.fieldNum + changeBy, 0, courseplay.fields.customFieldMaxNum);
- vehicle.cp.fieldEdge.customField.selectedFieldNumExists = courseplay.fields.fieldData[vehicle.cp.fieldEdge.customField.fieldNum] ~= nil;
- --print(string.format("%s: customField.fieldNum=%d, selectedFieldNumExists=%s", nameNum(vehicle), vehicle.cp.fieldEdge.customField.fieldNum, tostring(vehicle.cp.fieldEdge.customField.selectedFieldNumExists)));
-end;
-
-function courseplay:addCustomSingleFieldEdgeToList(vehicle)
- --print(string.format("%s: call addCustomSingleFieldEdgeToList()", nameNum(vehicle)));
- local data = {
- fieldNum = vehicle.cp.fieldEdge.customField.fieldNum;
- points = vehicle.cp.fieldEdge.customField.points;
- numPoints = vehicle.cp.fieldEdge.customField.numPoints;
- name = string.format("%s %d (%s)", courseplay:loc('COURSEPLAY_FIELD'), vehicle.cp.fieldEdge.customField.fieldNum, courseplay:loc('COURSEPLAY_USER'));
- isCustom = true;
- };
- local area, _, dimensions = courseplay.fields:getPolygonData(data.points, nil, nil, true);
- data.areaSqm = area;
- data.areaHa = area / 10000;
- data.dimensions = dimensions;
-
- courseplay.fields.fieldData[vehicle.cp.fieldEdge.customField.fieldNum] = data;
- courseplay.fields.numAvailableFields = table.maxn(courseplay.fields.fieldData);
-
- --print(string.format("\tfieldNum=%d, name=%s, #points=%d", courseplay.fields.fieldData[vehicle.cp.fieldEdge.customField.fieldNum].fieldNum, courseplay.fields.fieldData[vehicle.cp.fieldEdge.customField.fieldNum].name, #courseplay.fields.fieldData[vehicle.cp.fieldEdge.customField.fieldNum].points));
-
- --RESET
- courseplay:setCustomFieldEdgePathNumber(vehicle, nil, 0);
- courseplay:clearCustomFieldEdge(vehicle);
- courseplay:toggleSelectedFieldEdgePathShow(vehicle, false);
- --print(string.format("\t[AFTER RESET] fieldNum=%d, points=%s, fieldEdge.customField.isCreated=%s", vehicle.cp.fieldEdge.customField.fieldNum, tostring(vehicle.cp.fieldEdge.customField.points), tostring(vehicle.cp.fieldEdge.customField.isCreated)));
-end;
-
-function courseplay:showFieldEdgePath(vehicle, pathType)
- local points, numPoints = nil, 0;
- if pathType == "customField" then
- points = vehicle.cp.fieldEdge.customField.points;
- numPoints = vehicle.cp.fieldEdge.customField.numPoints;
- elseif pathType == "selectedField" then
- points = courseplay.fields.fieldData[vehicle.cp.courseGeneratorSettings.selectedField:get()].points;
- numPoints = courseplay.fields.fieldData[vehicle.cp.courseGeneratorSettings.selectedField:get()].numPoints;
- end;
-
- if numPoints > 0 then
- local pointHeight = 3;
- for i,point in pairs(points) do
- if i < numPoints then
- local nextPoint = points[i + 1];
- cpDebug:drawLine(point.cx,point.cy+pointHeight,point.cz, 0,0,1, nextPoint.cx,nextPoint.cy+pointHeight,nextPoint.cz);
-
- if i == 1 then
- cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 0,1,0);
- else
- cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 1,1,0);
- end;
- else
- cpDebug:drawPoint(point.cx, point.cy + pointHeight, point.cz, 1,0,0);
- end;
- end;
- end;
-end;
-
function courseplay:setEngineState(vehicle, on)
if vehicle == nil or on == nil or vehicle.spec_motorized.isMotorStarted == on then
return;
@@ -1060,7 +825,11 @@ end
function Setting:raiseEvent(eventIx,value)
local event = self:getEvent(eventIx)
value = event.getValueFunc and event.getValueFunc(self) or value
- SettingEvent.sendEvent(self.vehicle,self,event,value)
+ if self.vehicle ~= nil then
+ VehicleSettingEvent.sendEvent(self.vehicle,self,event,value)
+ else
+ GlobalSettingEvent.sendEvent(self,event,value)
+ end
end
--- Setting debug.
@@ -1095,6 +864,11 @@ function Setting:debugReadStream(value,valueName)
end
end
+--- Is synchronizing of this setting allowed.
+function Setting:isSyncAllowed()
+ return self.syncValue
+end
+
---@class FloatSetting :Setting
FloatSetting = CpObject(Setting)
--- @param name string name of this settings, will be used as an identifier in containers and XML
@@ -1139,7 +913,7 @@ end
function FloatSetting:set(value,noEventSend)
self.value = value
if noEventSend == nil or noEventSend == false then
- if self.syncValue then
+ if self:isSyncAllowed() then
self:sendEvent(value)
end
end
@@ -1201,7 +975,7 @@ function IntSetting:set(value,noEventSend)
if minOk and maxOk and value then
self.value = value
if noEventSend == nil or noEventSend == false then
- if self.syncValue then
+ if self:isSyncAllowed() then
self:sendEvent(value)
end
end
@@ -1299,7 +1073,7 @@ function SettingList:setToIx(ix, noEventSend)
self:onChange()
self.lastChangeTimeMilliseconds = g_time
if noEventSend == nil or noEventSend == false then
- if self.syncValue then
+ if self:isSyncAllowed() then
self:sendEvent()
end
end
@@ -2262,7 +2036,7 @@ function FieldNumberSetting:loadFields()
table.sort( values, function( a, b ) return a < b end )
-- then convert to text
for _, fieldNumber in ipairs(values) do
- table.insert(texts, tostring(fieldNumber))
+ table.insert(texts, CpFieldUtil.getFieldName(fieldNumber))
end
return values, texts
end
@@ -3494,24 +3268,30 @@ function ShowVisualWaypointsSetting:init(vehicle)
}
)
self:set(1)
- self.syncValue = false
end
function ShowVisualWaypointsSetting:onChange()
courseplay.signs:setSignsVisibility(self.vehicle)
end
+function ShowVisualWaypointsSetting:isSyncAllowed()
+ return false
+end
+
---@class ShowVisualWaypointsCrossPointSetting : BooleanSetting
ShowVisualWaypointsCrossPointSetting = CpObject(BooleanSetting)
function ShowVisualWaypointsCrossPointSetting:init(vehicle)
BooleanSetting.init(self, 'showVisualWaypointsCrossPoint','-', '-', vehicle)
self:set(false)
- self.syncValue = false
end
function ShowVisualWaypointsCrossPointSetting:onChange()
courseplay.signs:setSignsVisibility(self.vehicle)
end
+function ShowVisualWaypointsCrossPointSetting:isSyncAllowed()
+ return false
+end
+
---@class ConvoyActiveSetting : BooleanSetting
ConvoyActiveSetting = CpObject(BooleanSetting)
function ConvoyActiveSetting:init(vehicle)
@@ -4236,7 +4016,10 @@ function CourseDrawModeSetting:init(vehicle)
}
SettingList.init(self,"courseDrawMode","","",vehicle,values,texts)
self:set(self.COURSE_2D_DISPLAY_OFF)
- self.syncValue = false
+end
+
+function CourseDrawModeSetting:isSyncAllowed()
+ return false
end
function CourseDrawModeSetting:isDeactivated()
@@ -4312,7 +4095,7 @@ end
function SettingsContainer:onReadStream(stream)
for k, setting in pairs(self) do
- if self.validateSetting(setting) and setting.syncValue then
+ if self.validateSetting(setting) and setting:isSyncAllowed() then
setting:onReadStream(stream)
end
end
@@ -4320,7 +4103,7 @@ end
function SettingsContainer:onWriteStream(stream)
for k, setting in pairs(self) do
- if self.validateSetting(setting) and setting.syncValue then
+ if self.validateSetting(setting) and setting:isSyncAllowed() then
setting:onWriteStream(stream)
end
end
diff --git a/start_stop.lua b/start_stop.lua
index b7156b853..2966a84c5 100644
--- a/start_stop.lua
+++ b/start_stop.lua
@@ -135,12 +135,6 @@ function courseplay:stop(self)
courseplay:resetCustomTimer(self, 'slippingStage2');
- if self.cp.manualWorkWidth ~= nil then
- courseplay:changeWorkWidth(self, nil, self.cp.manualWorkWidth, true)
- if self.cp.hud.currentPage == courseplay.hud.PAGE_COURSE_GENERATION then
- courseplay.hud:setReloadPageOrder(self, self.cp.hud.currentPage, true);
- end
- end
self.cp.totalLength, self.cp.totalLengthOffset = 0, 0;
self.cp.numWorkTools = 0;
diff --git a/toolManager.lua b/toolManager.lua
index 54e660dea..ff51dc9b7 100644
--- a/toolManager.lua
+++ b/toolManager.lua
@@ -64,7 +64,6 @@ function courseplay:resetTools(vehicle)
courseplay.hud:setReloadPageOrder(vehicle, -1, true);
- courseplay:calculateWorkWidth(vehicle, true);
end;
function courseplay:isAttachedMixer(workTool)
@@ -399,74 +398,94 @@ function courseplay:resetTipTrigger(vehicle, changeToForward)
end
end;
+WorkWidthUtil = {}
+
--- Iterator for all work areas of an object
-function courseplay:workAreaIterator(object)
+---@param object table
+function WorkWidthUtil.workAreaIterator(object)
local i = 0
return function()
i = i + 1
- local wa = object and object.getWorkAreaByIndex and object:getWorkAreaByIndex(i)
+ local wa = WorkWidthUtil.hasValidWorkArea(object) and object:getWorkAreaByIndex(i)
if wa then return i, wa end
end
end
-function courseplay:hasWorkAreas(object)
- return object and object.getWorkAreaByIndex and object:getWorkAreaByIndex(1)
+--- Gets work areas if possible.
+---@param object table
+function WorkWidthUtil.hasWorkAreas(object)
+ return WorkWidthUtil.hasValidWorkArea(object) and object:getWorkAreaByIndex(1)
+end
+
+function WorkWidthUtil.hasValidWorkArea(object)
+ return object and object.getWorkAreaByIndex and object.spec_workArea.workAreas
end
---- Get the working width of thing. Will return the maximum of the working width of thing and
--- all of its implements
--- TODO: consolidate this with the logic in FieldworkAIDriver:setMarkers()
-function courseplay:getWorkWidth(thing, logPrefix)
+--- Gets an automatic calculated work width or a pre configured in vehicle configurations.
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getAutomaticWorkWidth(object,logPrefix)
logPrefix = logPrefix and logPrefix .. ' ' or ''
- courseplay.debugFormat(courseplay.DBG_IMPLEMENTS,'%s%s: getting working width...', logPrefix, nameNum(thing))
+ WorkWidthUtil.debug(object,logPrefix,'getting working width...')
-- check if we have a manually configured working width
- local width = g_vehicleConfigurations:get(thing, 'workingWidth')
+ local width = g_vehicleConfigurations:get(object, 'workingWidth')
if not width then
--- Gets the work width if the object is a shield.
- width = AIDriverUtil.getShieldWorkWidth(thing,logPrefix)
+ width = WorkWidthUtil.getShieldWorkWidth(object,logPrefix)
+ end
+
+ if not width then
+ --- Gets the work width if the object is a shovel.
+ width = WorkWidthUtil.getShovelWorkWidth(object,logPrefix)
end
if not width then
-- no manual config, check AI markers
- width = courseplay:getAIMarkerWidth(thing, logPrefix)
+ width = WorkWidthUtil.getAIMarkerWidth(object, logPrefix)
end
if not width then
-- no AI markers, check work areas
- width = courseplay:getWorkAreaWidth(thing, logPrefix)
+ width = WorkWidthUtil.getWorkAreaWidth(object, logPrefix)
end
- local implements = thing:getAttachedImplements()
+ local implements = object:getAttachedImplements()
if implements then
-- get width of all implements
for _, implement in ipairs(implements) do
- width = math.max( width, courseplay:getWorkWidth(implement.object, logPrefix))
+ width = math.max( width, WorkWidthUtil.getAutomaticWorkWidth(implement.object, logPrefix))
end
end
- courseplay.debugFormat(courseplay.DBG_IMPLEMENTS, '%s%s: working width is %.1f', logPrefix, nameNum(thing), width)
+ WorkWidthUtil.debug(object,logPrefix,'working width is %.1f.',width)
return width
end
-function courseplay:getWorkAreaWidth(object, logPrefix)
+
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getWorkAreaWidth(object, logPrefix)
logPrefix = logPrefix or ''
-- TODO: check if there's a better way to find out if the implement has a work area
local width = 0
- for i, wa in courseplay:workAreaIterator(object) do
+ for i, wa in WorkWidthUtil.workAreaIterator(object) do
-- work areas are defined by three nodes: start, width and height. These nodes
-- define a rectangular work area which you can make visible with the
-- gsVehicleDebugAttributes console command and then pressing F5
local x, _, _ = localToLocal(wa.width, wa.start, 0, 0, 0)
width = math.max(width, math.abs(x))
local _, _, z = localToLocal(wa.height, wa.start, 0, 0, 0)
- courseplay.debugFormat(courseplay.DBG_IMPLEMENTS, '%s%s: work area %d is %s, %.1f by %.1f m',
- logPrefix, nameNum(object), i, g_workAreaTypeManager.workAreaTypes[wa.type].name, math.abs(x), math.abs(z))
+ WorkWidthUtil.debug(object,logPrefix,'work area %d is %s, %.1f by %.1f m',
+ i, g_workAreaTypeManager.workAreaTypes[wa.type].name, math.abs(x), math.abs(z)
+ )
end
if width == 0 then
- courseplay.debugFormat(courseplay.DBG_IMPLEMENTS, '%s%s: has NO work area', logPrefix, nameNum(object))
+ WorkWidthUtil.debug(object,logPrefix,'has NO work area.')
end
return width
end
-function courseplay:getAIMarkerWidth(object, logPrefix)
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getAIMarkerWidth(object, logPrefix)
logPrefix = logPrefix or ''
if object.getAIMarkers then
local aiLeftMarker, aiRightMarker = object:getAIMarkers()
@@ -474,17 +493,137 @@ function courseplay:getAIMarkerWidth(object, logPrefix)
local left, _, _ = localToLocal(aiLeftMarker, object.cp.directionNode or object.rootNode, 0, 0, 0);
local right, _, _ = localToLocal(aiRightMarker, object.cp.directionNode or object.rootNode, 0, 0, 0);
local width, _, _ = localToLocal(aiLeftMarker, aiRightMarker, 0, 0, 0)
- courseplay.debugFormat( courseplay.DBG_IMPLEMENTS, '%s%s aiMarkers: left=%.2f, right=%.2f (width %.2f)', logPrefix, nameNum(object), left, right, width)
-
+ WorkWidthUtil.debug(object,logPrefix,'aiMarkers: left=%.2f, right=%.2f (width %.2f)',left, right,width)
if left < right then
left, right = right, left -- yes, lua can do this!
- courseplay.debugFormat(courseplay.DBG_IMPLEMENTS, '%s%s left < right -> switch -> left=%.2f, right=%.2f', logPrefix, nameNum(object), left, right)
+ WorkWidthUtil.debug(object,logPrefix,'left < right -> switch -> left=%.2f, right=%.2f',left, right)
end
return left - right;
end
end
end
+--- Gets ai markers for an object.
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getAIMarkers(object,logPrefix,suppressLog)
+ local aiLeftMarker, aiRightMarker, aiBackMarker
+ if object.getAIMarkers then
+ aiLeftMarker, aiRightMarker, aiBackMarker = object:getAIMarkers()
+ end
+ if not aiLeftMarker or not aiRightMarker or not aiBackMarker then
+ -- use the root node if there are no AI markers
+ if not suppressLog then
+ WorkWidthUtil.debug(object,logPrefix,'has no AI markers, try work areas')
+ end
+ aiLeftMarker, aiRightMarker, aiBackMarker = WorkWidthUtil.getAIMarkersFromWorkAreas(object)
+ if not aiLeftMarker or not aiRightMarker or not aiLeftMarker then
+ if not suppressLog then
+ WorkWidthUtil.debug(object,logPrefix,'has no work areas, giving up')
+ end
+ return nil, nil, nil
+ else
+ return aiLeftMarker, aiRightMarker, aiBackMarker
+ end
+ else
+ return aiLeftMarker, aiRightMarker, aiBackMarker
+ end
+end
+
+--- Calculate the front and back marker nodes of a work area
+---@param object table
+function WorkWidthUtil.getAIMarkersFromWorkAreas(object)
+ -- work areas are defined by three nodes: start, width and height. These nodes
+ -- define a rectangular work area which you can make visible with the
+ -- gsVehicleDebugAttributes console command and then pressing F5
+ for _, area in WorkWidthUtil.workAreaIterator(object) do
+ if WorkWidthUtil.isValidWorkArea(area) then
+ -- for now, just use the first valid work area we find
+ WorkWidthUtil.debug(object,nil,'%s: Using %s work area markers as AIMarkers', g_workAreaTypeManager.workAreaTypes[area.type].name)
+ return area.start, area.width, area.height
+ end
+ end
+end
+
+---@param area table
+function WorkWidthUtil.isValidWorkArea(area)
+ return area.start and area.height and area.width and
+ area.type ~= WorkAreaType.RIDGEMARKER and
+ area.type ~= WorkAreaType.COMBINESWATH and
+ area.type ~= WorkAreaType.COMBINECHOPPER
+end
+
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getShieldWorkWidth(object,logPrefix)
+ if object.spec_leveler then
+ local width = object.spec_leveler.nodes[1].maxDropWidth * 2
+ WorkWidthUtil.debug(object,logPrefix,'is a shield with work width: %.1f',width)
+ return width
+ end
+end
+
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.getShovelWorkWidth(object,logPrefix)
+ if object.spec_shovel then
+ local width = object.spec_shovel.shovelNodes[1].width
+ WorkWidthUtil.debug(object,logPrefix,'is a shovel with work width: %.1f',width)
+ return width
+ end
+end
+
+--- Shows the current work width selected with the tool offsets applied.
+---@param vehicle table
+---@param workWidth number
+---@param offsX number
+---@param offsZ number
+function WorkWidthUtil.showWorkWidth(vehicle,workWidth,offsX,offsZ)
+ local firstObject = AIDriverUtil.getFirstAttachedImplement(vehicle)
+ local lastObject = AIDriverUtil.getLastAttachedImplement(vehicle)
+
+ local function show(object,workWidth,offsX,offsZ)
+ if object == nil then
+ return
+ end
+ local f, b = 0,0
+ local aiLeftMarker, _, aiBackMarker = WorkWidthUtil.getAIMarkers(object,nil,true)
+ if aiLeftMarker and aiBackMarker then
+ _,_,b = localToLocal(aiBackMarker, object.rootNode, 0, 0, 0)
+ _,_,f = localToLocal(aiLeftMarker, object.rootNode, 0, 0, 0)
+ end
+
+ local left = (workWidth * 0.5) + offsX
+ local right = (workWidth * -0.5) + offsX
+
+ local p1x, p1y, p1z = localToWorld(object.rootNode, left, 1.6, b - offsZ)
+ local p2x, p2y, p2z = localToWorld(object.rootNode, right, 1.6, b - offsZ)
+ local p3x, p3y, p3z = localToWorld(object.rootNode, right, 1.6, f - offsZ)
+ local p4x, p4y, p4z = localToWorld(object.rootNode, left, 1.6, f - offsZ)
+
+ cpDebug:drawPoint(p1x, p1y, p1z, 1, 1, 0)
+ cpDebug:drawPoint(p2x, p2y, p2z, 1, 1, 0)
+ cpDebug:drawPoint(p3x, p3y, p3z, 1, 1, 0)
+ cpDebug:drawPoint(p4x, p4y, p4z, 1, 1, 0)
+
+ cpDebug:drawLine(p1x, p1y, p1z, 1, 0, 0, p2x, p2y, p2z)
+ cpDebug:drawLine(p2x, p2y, p2z, 1, 0, 0, p3x, p3y, p3z)
+ cpDebug:drawLine(p3x, p3y, p3z, 1, 0, 0, p4x, p4y, p4z)
+ cpDebug:drawLine(p4x, p4y, p4z, 1, 0, 0, p1x, p1y, p1z)
+ end
+ show(firstObject,workWidth,offsX,offsZ)
+ if firstObject ~= lastObject then
+ show(lastObject,workWidth,offsX,offsZ)
+ end
+end
+
+
+---@param object table
+---@param logPrefix string
+function WorkWidthUtil.debug(object,logPrefix,str,...)
+ courseplay.debugFormat(courseplay.DBG_IMPLEMENTS,'%s%s: '..str, logPrefix or "", nameNum(object), ...)
+end
+
--this one enable the buttons and allows the user to change the mode
function courseplay:getIsToolCombiValidForCpMode(vehicle,cpModeToCheck)
--5 is always valid
diff --git a/translations/translation_br.xml b/translations/translation_br.xml
index 31050b1f3..c4fd7ae58 100644
--- a/translations/translation_br.xml
+++ b/translations/translation_br.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml
index 367ebfbe2..e4e8f3ba5 100644
--- a/translations/translation_cs.xml
+++ b/translations/translation_cs.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml
index 826cbf19a..8953ca462 100644
--- a/translations/translation_cz.xml
+++ b/translations/translation_cz.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_de.xml b/translations/translation_de.xml
index 8264a02eb..ffe7dbfbe 100644
--- a/translations/translation_de.xml
+++ b/translations/translation_de.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_en.xml b/translations/translation_en.xml
index b210812a7..ca07d16b3 100644
--- a/translations/translation_en.xml
+++ b/translations/translation_en.xml
@@ -297,6 +297,8 @@
+
+
diff --git a/translations/translation_es.xml b/translations/translation_es.xml
index f7ce5d4f8..90a5123d8 100644
--- a/translations/translation_es.xml
+++ b/translations/translation_es.xml
@@ -297,6 +297,8 @@
+
+
diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml
index 8b64dceb5..2ca0f8ac0 100644
--- a/translations/translation_fr.xml
+++ b/translations/translation_fr.xml
@@ -297,6 +297,8 @@
+
+
diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml
index 11c03cb5d..f21f9f434 100644
--- a/translations/translation_hu.xml
+++ b/translations/translation_hu.xml
@@ -292,6 +292,8 @@
+
+
diff --git a/translations/translation_it.xml b/translations/translation_it.xml
index 19dad63ab..4093bd3e9 100644
--- a/translations/translation_it.xml
+++ b/translations/translation_it.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_jp.xml b/translations/translation_jp.xml
index 6807c63c7..38bc72d29 100644
--- a/translations/translation_jp.xml
+++ b/translations/translation_jp.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml
index 10a02d016..3ea2446c2 100644
--- a/translations/translation_nl.xml
+++ b/translations/translation_nl.xml
@@ -292,6 +292,8 @@
+
+
diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml
index f0374f862..39bcfe13a 100644
--- a/translations/translation_pl.xml
+++ b/translations/translation_pl.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml
index 5607fc26b..224684192 100644
--- a/translations/translation_pt.xml
+++ b/translations/translation_pt.xml
@@ -292,6 +292,8 @@
+
+
diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml
index 52fe8cbfe..8efde873b 100644
--- a/translations/translation_ru.xml
+++ b/translations/translation_ru.xml
@@ -297,6 +297,8 @@
+
+
diff --git a/translations/translation_sl.xml b/translations/translation_sl.xml
index 886ade0ff..67ade7397 100644
--- a/translations/translation_sl.xml
+++ b/translations/translation_sl.xml
@@ -296,6 +296,8 @@
+
+
diff --git a/turn.lua b/turn.lua
index 04c463664..8e211023b 100644
--- a/turn.lua
+++ b/turn.lua
@@ -27,8 +27,7 @@ function courseplay:turn(vehicle, dt, turnContext)
--- This is in case we use manually recorded fieldswork course and not generated.
if not vehicle.cp.courseWorkWidth then
- courseplay:calculateWorkWidth(vehicle, true);
- vehicle.cp.courseWorkWidth = vehicle.cp.workWidth;
+ vehicle.cp.courseWorkWidth = vehicle.cp.courseGeneratorSettings.workWidth:getAutoWorkWidth();
end;
--- Get front and back markers
@@ -1217,7 +1216,8 @@ function courseplay.generateTurnTypeHeadlandCornerReverseStraightTractor(vehicle
-- drive forward until our implement reaches the headland after the turn
fromPoint.x, _, fromPoint.z = localToWorld( helperNode, 0, 0, 0 )
-- drive forward only until our implement reaches the headland area after the turn so we leave an unworked area here at the corner
- toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerStart((vehicle.cp.workWidth / 2) + turnInfo.frontMarker - turnInfo.wpChangeDistance)
+ local workWidth = vehicle.cp.courseGeneratorSettings.workWidth:get()
+ toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerStart((workWidth / 2) + turnInfo.frontMarker - turnInfo.wpChangeDistance)
-- is this now in front of us? We may not need to drive forward
local dx, dy, dz = worldToLocal( helperNode, toPoint.x, toPoint.y, toPoint.z )
-- at which waypoint we have to raise the implement
@@ -1238,7 +1238,7 @@ function courseplay.generateTurnTypeHeadlandCornerReverseStraightTractor(vehicle
_, _, dz = worldToLocal( helperNode, toPoint.x, toPoint.y, toPoint.z )
courseplay.destroyNode(helperNode)
courseplay:debug(("%s:(Turn) courseplay:generateTurnTypeHeadlandCornerReverseStraightTractor(), from ( %.2f %.2f ), to ( %.2f %.2f) workWidth: %.1f, dz = %.1f"):format(
- nameNum(vehicle), fromPoint.x, fromPoint.z, toPoint.x, toPoint.z, vehicle.cp.workWidth, dz ), 14)
+ nameNum(vehicle), fromPoint.x, fromPoint.z, toPoint.x, toPoint.z, workWidth, dz ), 14)
courseplay:generateTurnStraightPoints(vehicle, fromPoint, toPoint, dz < 0);
-- Generate turn circle (Forward)
@@ -1259,9 +1259,9 @@ function courseplay.generateTurnTypeHeadlandCornerReverseStraightTractor(vehicle
if turnInfo.reversingWorkTool and turnInfo.reversingWorkTool.cp.realTurningNode then
-- with towed reversing tools the reference point is the tool, not the tractor so don't care about frontMarker and such
- toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerEnd(-(vehicle.cp.workWidth / 2) - turnInfo.reverseWPChangeDistance - 10)
+ toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerEnd(-(workWidth / 2) - turnInfo.reverseWPChangeDistance - 10)
else
- toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerEnd(-(vehicle.cp.workWidth / 2) - turnInfo.frontMarker - turnInfo.reverseWPChangeDistance - 10)
+ toPoint = vehicle.cp.turnCorner:getPointAtDistanceFromCornerEnd(-(workWidth / 2) - turnInfo.frontMarker - turnInfo.reverseWPChangeDistance - 10)
end
courseplay:generateTurnStraightPoints(vehicle, fromPoint, toPoint, true, true, turnInfo.reverseWPChangeDistance);