From 006fcea22f0b40d67623861f7bed6d6644571c5b Mon Sep 17 00:00:00 2001 From: schwiti6190 <58079399+schwiti6190@users.noreply.github.com> Date: Fri, 25 Jun 2021 12:56:51 +0200 Subject: [PATCH] Small fixes and improved global info texts - fixes #7312 - created CourseDrawModeSetting - global info texts improvements --- AIDriver.lua | 15 +- BalerAIDriver.lua | 2 +- CpManager.lua | 353 +------------------------------- Events/InfoTextEvent.lua | 7 +- FieldworkAIDriver.lua | 6 + GlobalInfoTextHandler.lua | 410 ++++++++++++++++++++++++++++++++++++++ TriggerHandler.lua | 10 +- base.lua | 54 ++--- button.lua | 7 +- config/InfoTexts.xml | 64 ++++++ courseplay.lua | 40 ++-- hud.lua | 16 +- input.lua | 8 +- settings.lua | 48 ++++- start_stop.lua | 5 +- 15 files changed, 590 insertions(+), 455 deletions(-) create mode 100644 GlobalInfoTextHandler.lua create mode 100644 config/InfoTexts.xml diff --git a/AIDriver.lua b/AIDriver.lua index ee86c00f3..9db3784f0 100644 --- a/AIDriver.lua +++ b/AIDriver.lua @@ -416,14 +416,15 @@ end -- This has to be called in each update cycle to show messages function AIDriver:updateInfoText() for msg, _ in pairs(self.activeMsgReferences) do - CpManager:setGlobalInfoText(self.vehicle, msg) + g_globalInfoTextHandler:setInfoText(self.vehicle, msg) end end --- Displays an one time ingame warning. ---@param msgReference string this can be a global info text or just a translation text. function AIDriver:displayWarning(msgReference) - local msg = CpManager.globalInfoText.msgReference[msgReference] or msgReference + local infoTexts = g_globalInfoTextHandler:getInfoTexts() + local msg = infoTexts[msgReference] and infoTexts[msgReference].text or msgReference local text = string.format("%s: %s",nameNum(self.vehicle),courseplay:loc(msg)) g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_CRITICAL, text) end @@ -1725,7 +1726,7 @@ end --- without restarting the game. function AIDriver:onDraw() if CpManager.isDeveloper and self.course and - (self.vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_DBGONLY or self.vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_BOTH) then + self.settings.courseDrawMode:isCourseVisible() then self.course:draw() end if CpManager.isDeveloper and self.pathfinder then @@ -2189,13 +2190,13 @@ end function AIDriver:isFuelLevelOk() local currentFuelPercentage = self:getFuelLevelPercentage() if currentFuelPercentage < 5 then - CpManager:setGlobalInfoText(self.vehicle, 'FUEL_MUST'); + self:setInfoText('FUEL_MUST') return false elseif currentFuelPercentage < 20 then - CpManager:setGlobalInfoText(self.vehicle, 'FUEL_SHOULD'); + self:setInfoText('FUEL_SHOULD') elseif currentFuelPercentage < 99.99 then - -- CpManager:setGlobalInfoText(vehicle, 'FUEL_IS'); - end; + -- self:setInfoText('FUEL_IS') + end return true end diff --git a/BalerAIDriver.lua b/BalerAIDriver.lua index e97dbd51d..9e41423ab 100644 --- a/BalerAIDriver.lua +++ b/BalerAIDriver.lua @@ -120,7 +120,7 @@ function BalerAIDriver:handleBaler() self:setSpeed(0) --baler needs refilling of some sort (net,...) if self.balerSpec.unloadingState == Baler.UNLOADING_CLOSED then - CpManager:setGlobalInfoText(self.vehicle, 'NEEDS_REFILLING'); + self:setInfoText('NEEDS_REFILLING') end end end diff --git a/CpManager.lua b/CpManager.lua index bce4cbb3a..358eed1b9 100644 --- a/CpManager.lua +++ b/CpManager.lua @@ -55,7 +55,7 @@ function CpManager:loadMap(name) -- SETUP (continued) courseplay.hud:setup(); -- NOTE: hud has to be set up after the xml settings have been loaded, as almost all its values are based on basePosX/Y self:setUpDebugChannels(); -- NOTE: debugChannels have to be set up after the hud, as they rely on some hud values [positioning] - self:setupGlobalInfoText(); -- NOTE: globalInfoText has to be set up after the hud, as they rely on some hud values [colors, function] + g_globalInfoTextHandler:setup() -- NOTE: globalInfoText has to be set up after the hud, as they rely on some hud values [colors, function] courseplay.courses:setup(); -- NOTE: load the courses and folders from the XML self:setup2dCourseData(true); -- NOTE: setup2dCourseData is called a second time, now we actually create the data and overlays courseplay:register(true)-- NOTE: running here again to check whether there were mods loaded after courseplay @@ -72,8 +72,6 @@ function CpManager:loadMap(name) -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- INPUT - self.playerOnFootMouseEnabled = false; - self.wasPlayerFrozen = false; local ovl = courseplay.inputBindings.mouse.overlaySecondary; if ovl then local h = (2.5 * g_currentMission.helpBoxTextSize); @@ -173,19 +171,7 @@ function CpManager:deleteMap() end; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - if self.globalInfoText and self.globalInfoText.buttons then - --delete globalInfoText overlays - for i,button in pairs(self.globalInfoText.buttons) do - button:deleteOverlay(); - - if self.globalInfoText.overlays[i] then - local ovl = self.globalInfoText.overlays[i]; - if ovl.overlayId ~= nil and ovl.delete ~= nil then - ovl:delete(); - end; - end; - end; - end + g_globalInfoTextHandler:delete() -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- delete waypoint signs and protoTypes for section,signDatas in pairs(courseplay.signs.buffer) do @@ -315,20 +301,8 @@ function CpManager:draw() -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- DISPLAY GLOBALINFOTEXTS - local git = self.globalInfoText; - git.hasContent = false; - local numLinesRendered = 0; - local basePosY = git.posY; - if not (g_currentMission.hud.ingameMap.isVisible and g_currentMission.hud.ingameMap:getIsFullSize()) and next(git.content) ~= nil then - git.hasContent = true; - if g_currentMission.hud.ingameMap.isVisible then - basePosY = git.posYAboveMap; - end; - numLinesRendered = self:renderGlobalInfoTexts(basePosY); - end; - git.buttonsClickArea.y1 = basePosY; - git.buttonsClickArea.y2 = basePosY + (numLinesRendered * (git.lineHeight + git.lineMargin)); - + g_globalInfoTextHandler:render() + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- DISPLAY FIELD SCAN MSG if courseplay.fields.fieldDefinitionBase and courseplay.globalSettings.autoFieldScan:is(true) and not courseplay.fields.allFieldsScanned and self.startFieldScanAfter <= 0 then @@ -347,110 +321,11 @@ function CpManager:mouseEvent(posX, posY, isDown, isUp, mouseKey) courseplay:onMouseEvent(posX, posY, isDown, isUp, mouseKey) - - local area = self.globalInfoText.buttonsClickArea; - if area == nil then - return; - end; + g_globalInfoTextHandler:mouseEvent(posX, posY, isDown, isUp, mouseKey) - -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- LEFT CLICK to click the button shown in globalInfoText - if (isDown or isUp) and mouseKey == courseplay.inputBindings.mouse.primaryButtonId and courseplay:mouseIsInArea(posX, posY, area.x1, area.x2, area.y1, area.y2) then - self:onPrimaryMouseClick(posX, posY, isDown, isUp, mouseKey) - -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- RIGHT CLICK to activate the mouse cursor when I'm not in a vehicle and a globalInfoText is shown - elseif isUp and mouseKey == courseplay.inputBindings.mouse.secondaryButtonId and g_currentMission.controlledVehicle == nil then - self:onSecondaryMouseClick(posX, posY, isDown, isUp, mouseKey) - -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- HOVER - elseif not isDown and not isUp and self:getHasGlobalInfoText() then - for _,button in pairs(self.globalInfoText.buttons) do - button:setClicked(false); - if button.show and not button.isHidden then - button:setHovered(button:getHasMouse(posX, posY)); - end; - end; - end; g_devHelper:mouseEvent(posX, posY, isDown, isUp, mouseKey) end; ----Secondary mouse button pressed ----@param float posX, mouse x position ----@param float posY, mouse y position ----@param boolean isDown, is the mouse button down ? ----@param boolean isUp, is the mouse button up ? ----@param int mouseKey, which mouse button was pressed ? -function CpManager:onSecondaryMouseClick(posX, posY, isDown, isUp, mouseKey) - if self:getHasGlobalInfoText() and not self.playerOnFootMouseEnabled and not g_currentMission.player.currentTool then - self.playerOnFootMouseEnabled = true - self.wasPlayerFrozen = g_currentMission.isPlayerFrozen - g_currentMission.isPlayerFrozen = true - elseif self.playerOnFootMouseEnabled then - self.playerOnFootMouseEnabled = false - if self:getHasGlobalInfoText() then --if a button was hovered when deactivating the cursor, deactivate hover state - self:resetGlobalInfoTextButtons() - end; - if not self.wasPlayerFrozen then - g_currentMission.isPlayerFrozen = false - end; - end; - g_inputBinding:setShowMouseCursor(self.playerOnFootMouseEnabled) - CpManager:updateMouseInputText() -end - ----Primary mouse button pressed ----@param float posX, mouse x position ----@param float posY, mouse y position ----@param boolean isDown, is the mouse button down ? ----@param boolean isUp, is the mouse button up ? ----@param int mouseKey, which mouse button was pressed ? -function CpManager:onPrimaryMouseClick(posX, posY, isDown, isUp, mouseKey) - if self:getHasGlobalInfoText() then - for i,button in pairs(self.globalInfoText.buttons) do - if button.show and button:getHasMouse(posX, posY) then - button:setClicked(isDown) - if isUp then - local sourceVehicle = g_currentMission.controlledVehicle or button.parameter - button:handleMouseClick(sourceVehicle) - end - break - end - end - CpManager:updateMouseInputText() - end -end - ----Is the second mouse button allowed, when the player isn't in a vehicle ? ----@return boolean allowed? -function CpManager:isSecondaryMouseClickAllowed() - return self.playerOnFootMouseEnabled or self:getHasGlobalInfoText() and not self.playerOnFootMouseEnabled and not g_currentMission.player.currentTool -end - ----Is the first mouse button allowed, when the player isn't in a vehicle ? ----@return boolean allowed? -function CpManager:isPrimaryMouseClickAllowed() - return self:getHasGlobalInfoText() and self.playerOnFootMouseEnabled -end - ----Updates mouse input texts ----@param boolean forceText should the texts forced to display? -function CpManager:updateMouseInputText() - -- HELP MENU - ---TODO: For now always display the mouse action text, - --- as this one would probably be broken in MP, - --- should probably rework global info texts. - --- if g_gui.currentGui == nil and not g_currentMission.player.currentTool then --- g_currentMission.hud.inputHelp:clearCustomEntries() --- if self:isPrimaryMouseClickAllowed() or g_currentMission.controlledVehicle ~= nil then --- g_currentMission.hud.inputHelp:addCustomEntry('COURSEPLAY_MOUSEACTION_PRIMARY', '', courseplay:loc("input_COURSEPLAY_MOUSEACTION_PRIMARY"), false) --- end --- if self:isSecondaryMouseClickAllowed() or g_currentMission.controlledVehicle ~= nil then --- g_currentMission.hud.inputHelp:addCustomEntry('COURSEPLAY_MOUSEACTION_SECONDARY', '', courseplay:loc("input_COURSEPLAY_MOUSEACTION_SECONDARY"), false) --- end --- end -end - function CpManager:keyEvent(unicode, sym, modifier, isDown) courseplay:onKeyEvent(unicode, sym, modifier, isDown) g_devHelper:keyEvent(unicode, sym, modifier, isDown) @@ -945,224 +820,6 @@ function CpManager:fieldScanDialogueCallback(setActive) self.showFieldScanYesNoDialogue = false end; --- #################################################################################################### --- GLOBALINFOTEXT -function CpManager:setupGlobalInfoText() - print('## Courseplay: setting up globalInfoText'); - - self.globalInfoText = {}; - - self.globalInfoText.posY = 0.01238; -- = ingameMap posY - self.globalInfoText.posYAboveMap = self.globalInfoText.posY + 0.027777777777778 + 0.20833333333333; - self.globalInfoText.fontSize = courseplay.hud:pxToNormal(18, 'y'); - self.globalInfoText.lineHeight = self.globalInfoText.fontSize * 1.2; - self.globalInfoText.lineMargin = self.globalInfoText.lineHeight * 0.2; - self.globalInfoText.buttonHeight = self.globalInfoText.lineHeight; - self.globalInfoText.buttonWidth = self.globalInfoText.buttonHeight / g_screenAspectRatio; - self.globalInfoText.buttonPosX = 0.015625; -- = ingameMap posX - self.globalInfoText.buttonMargin = self.globalInfoText.buttonWidth * 0.4; - self.globalInfoText.backgroundPadding = self.globalInfoText.buttonWidth * 0.2; - self.globalInfoText.backgroundImg = 'dataS2/menu/white.png'; - self.globalInfoText.backgroundPosX = self.globalInfoText.buttonPosX + self.globalInfoText.buttonWidth + self.globalInfoText.buttonMargin; - self.globalInfoText.backgroundPosY = self.globalInfoText.posY; - self.globalInfoText.textPosX = self.globalInfoText.backgroundPosX + self.globalInfoText.backgroundPadding; - self.globalInfoText.content = {}; - self.globalInfoText.vehicleHasText = {}; - self.globalInfoText.levelColors = { - [-2] = courseplay.hud.colors.closeRed; - [-1] = courseplay.hud.colors.activeRed; - [0] = courseplay.hud.colors.hover; - [1] = courseplay.hud.colors.activeGreen; - }; - - self.globalInfoText.maxNum = 20; - self.globalInfoText.overlays = {}; - self.globalInfoText.buttons = {}; - for i=1, self.globalInfoText.maxNum do - local posY = self.globalInfoText.backgroundPosY + (i - 1) * self.globalInfoText.lineHeight; - self.globalInfoText.overlays[i] = Overlay:new(self.globalInfoText.backgroundImg, self.globalInfoText.backgroundPosX, posY, 0.1, self.globalInfoText.buttonHeight); - courseplay.button:new(self, 'globalInfoText', 'iconSprite.png', 'goToVehicle', i, self.globalInfoText.buttonPosX, posY, self.globalInfoText.buttonWidth, self.globalInfoText.buttonHeight); - end; - self.globalInfoText.buttonsClickArea = { - x1 = self.globalInfoText.buttonPosX; - x2 = self.globalInfoText.buttonPosX + self.globalInfoText.buttonWidth; - y1 = self.globalInfoText.backgroundPosY, - y2 = self.globalInfoText.backgroundPosY + (self.globalInfoText.maxNum * (self.globalInfoText.lineHeight + self.globalInfoText.lineMargin)); - }; - self.globalInfoText.hasContent = false; - - - self.globalInfoText.msgReference = { - BGA_IS_FULL = { level = -1, text = 'COURSEPLAY_BGA_IS_FULL'}; - DAMAGE_IS = { level = 0, text = 'COURSEPLAY_DAMAGE_IS_BEING_REPAIRED' }; - DAMAGE_MUST = { level = -2, text = 'COURSEPLAY_DAMAGE_MUST_BE_REPAIRED' }; - DAMAGE_SHOULD = { level = -1, text = 'COURSEPLAY_DAMAGE_SHOULD_BE_REPAIRED' }; - END_POINT = { level = 0, text = 'COURSEPLAY_REACHED_END_POINT' }; - END_POINT_MODE_1 = { level = 0, text = 'COURSEPLAY_REACHED_END_POINT_MODE_1' }; - END_POINT_MODE_8 = { level = 0, text = 'COURSEPLAY_REACHED_END_POINT_MODE_8' }; - FARM_SILO_NO_FILLTYPE = { level = -2, text = 'COURSEPLAY_FARM_SILO_NO_FILLTYPE'}; - FARM_SILO_IS_EMPTY = { level = 0, text = 'COURSEPLAY_FARM_SILO_IS_EMPTY'}; - FARM_SILO_IS_FULL = { level = 0, text = 'COURSEPLAY_FARM_SILO_IS_FULL'}; - FUEL_IS = { level = 0, text = 'COURSEPLAY_IS_BEING_REFUELED' }; - FUEL_MUST = { level = -2, text = 'COURSEPLAY_MUST_BE_REFUELED' }; - FUEL_SHOULD = { level = -1, text = 'COURSEPLAY_SHOULD_BE_REFUELED' }; - HOSE_MISSING = { level = -2, text = 'COURSEPLAY_HOSEMISSING' }; - NEEDS_REFILLING = { level = -1, text = 'COURSEPLAY_NEEDS_REFILLING' }; - NEEDS_UNLOADING = { level = -1, text = 'COURSEPLAY_NEEDS_UNLOADING' }; - OVERLOADING_POINT = { level = 0, text = 'COURSEPLAY_REACHED_OVERLOADING_POINT' }; - PICKUP_JAMMED = { level = -2, text = 'COURSEPLAY_PICKUP_JAMMED' }; - SLIPPING_1 = { level = -1, text = 'COURSEPLAY_SLIPPING_WARNING' }; - SLIPPING_2 = { level = -2, text = 'COURSEPLAY_SLIPPING_WARNING' }; - TRAFFIC = { level = -1, text = 'COURSEPLAY_IS_IN_TRAFFIC' }; - SLOWING_DOWN_FOR_TRAFFIC = { level = 0, text = 'COURSEPLAY_SLOWING_DOWN_FOR_TRAFFIC' }; - UNLOADING_BALE = { level = 0, text = 'COURSEPLAY_UNLOADING_BALES' }; - WAIT_POINT = { level = 0, text = 'COURSEPLAY_REACHED_WAITING_POINT' }; - WATER = { level = -2, text = 'COURSEPLAY_WATER_WARNING' }; - WEATHER = { level = 0, text = 'COURSEPLAY_WEATHER_WARNING' }; - WEIGHING_VEHICLE = { level = 0, text = 'COURSEPLAY_IS_BEING_WEIGHED' }; - WORK_END = { level = 1, text = 'COURSEPLAY_WORK_END' }; - REACHED_OVERLOADING_POINT = { level = 0, text = 'COURSEPLAY_REACHED_OVERLOADING_POINT' }; - NO_SELECTED_FILLTYPE = { level = 0, text = 'COURSEPLAY_NO_SELECTED_FILLTYPE' }; - REACHED_REFILLING_POINT = { level = 0, text = 'COURSEPLAY_REACHED_REFILL_POINT' }; - WRONG_FILLTYPE_FOR_TRIGGER = { level = 0, text = 'COURSEPLAY_WRONG_FILLTYPE_FOR_TRIGGER' }; - RUNCOUNTER_ERROR_FOR_TRIGGER = { level = 0, text = 'COURSEPLAY_RUNCOUNTER_ERROR_FOR_TRIGGER' }; - WAITING_FOR_UNLOADERS = { level = 0, text = 'COURSEPLAY_WAITING_FOR_UNLOADERS' }; - WAITING_FOR_LEVELCOMPACTAIDRIVER = { level = 0, text = 'COURSEPLAY_WAITING_FOR_LEVELCOMPACTAIDRIVER' }; - NO_FIELD_SELECTED = { level = -1, text = 'COURSEPLAY_NO_FIELD_SELECTED' }; - }; -end; - -function CpManager:setGlobalInfoText(vehicle, refIdx, forceRemove,additionalString) - local git = self.globalInfoText; - - --TODO: test in Multiplayer if it works fine - if vehicle.cp.activeGlobalInfoTexts[refIdx] ~= nil and (additionalString ~= vehicle.cp.gitAdditionalText and g_server~= nil) then - vehicle:setCpVar('gitAdditionalText',additionalString,courseplay.isClient) - forceRemove = true - end - - - --print(string.format('setGlobalInfoText(vehicle, %s, %s)', tostring(refIdx), tostring(forceRemove))); - if forceRemove then - if g_server ~= nil then - -- CourseplayEvent.sendEvent(vehicle, "setMPGlobalInfoText", refIdx, false, forceRemove) - InfoTextEvent.sendEvent(vehicle,refIdx,forceRemove) - end - if git.content[vehicle.rootNode][refIdx] then - git.content[vehicle.rootNode][refIdx] = nil; - end; - vehicle.cp.activeGlobalInfoTexts[refIdx] = nil; - vehicle.cp.numActiveGlobalInfoTexts = vehicle.cp.numActiveGlobalInfoTexts - 1; - --print(string.format('\t%s: remove globalInfoText[%s] from global table, numActiveGlobalInfoTexts=%d', nameNum(vehicle), refIdx, vehicle.cp.numActiveGlobalInfoTexts)); - if vehicle.cp.numActiveGlobalInfoTexts == 0 then - git.content[vehicle.rootNode] = nil; - --print(string.format('\t\tset globalInfoText.content[rootNode] to nil')); - end; - return; - end; - - vehicle.cp.hasSetGlobalInfoTextThisLoop[refIdx] = true; - local data = git.msgReference[refIdx]; - --print(string.format('refIdx=%q, level=%s, text=%q, textLoc=%q', tostring(refIdx), tostring(data.level), tostring(data.text), tostring(courseplay:loc(data.text)))); - if vehicle.cp.activeGlobalInfoTexts[refIdx] == nil or vehicle.cp.activeGlobalInfoTexts[refIdx] ~= data.level then - if g_server ~= nil then - -- CourseplayEvent.sendEvent(vehicle, "setMPGlobalInfoText", refIdx, false, forceRemove) - InfoTextEvent.sendEvent(vehicle,refIdx,forceRemove) - end - if vehicle.cp.activeGlobalInfoTexts[refIdx] == nil then - vehicle.cp.numActiveGlobalInfoTexts = vehicle.cp.numActiveGlobalInfoTexts + 1; - end; - local text = nameNum(vehicle) .. " " .. courseplay:loc(data.text); - if vehicle.cp.gitAdditionalText ~= nil then - text = text..": "..vehicle.cp.gitAdditionalText - end - --print(string.format('\t%s: setGlobalInfoText [%q] numActiveGlobalInfoTexts=%d, lvl %d, text=%q', nameNum(vehicle), refIdx, vehicle.cp.numActiveGlobalInfoTexts, data.level, tostring(text))); - vehicle.cp.activeGlobalInfoTexts[refIdx] = data.level; - - if git.content[vehicle.rootNode] == nil then - git.content[vehicle.rootNode] = {}; - end; - git.content[vehicle.rootNode][refIdx] = { - level = data.level, - text = text, - backgroundWidth = getTextWidth(git.fontSize, text) + git.backgroundPadding * 2.5, - vehicle = vehicle - }; - end; -end; - -function CpManager:renderGlobalInfoTexts(basePosY) - local git = self.globalInfoText; - local line = 0; - courseplay:setFontSettings('white', false, 'left'); - for _,refIndexes in pairs(git.content) do - if line >= self.globalInfoText.maxNum then - break; - end; - - for refIdx,data in pairs(refIndexes) do - line = line + 1; - - -- background - local bg = git.overlays[line]; - bg:setColor(unpack(git.levelColors[data.level])); - local gfxPosY = basePosY + (line - 1) * (git.lineHeight + git.lineMargin); - bg:setPosition(bg.x, gfxPosY); - bg:setDimension(data.backgroundWidth, bg.height); - bg:render(); - - -- text - local textPosY = gfxPosY + (git.lineHeight - git.fontSize) * 1.2; -- should be (lineHeight-fontSize)*0.5, but there seems to be some pixel/sub-pixel rendering error - renderText(git.textPosX, textPosY, git.fontSize, data.text); - -- button - local button = self.globalInfoText.buttons[line]; - if button ~= nil then - button:setPosition(button.overlay.x, gfxPosY) - - local currentColor = button.curColor; - local targetColor = currentColor; - - button:setCanBeClicked(true); - button:setDisabled(data.vehicle.isBroken or data.vehicle.isControlled); - button:setParameter(data.vehicle); - if g_currentMission.controlledVehicle and g_currentMission.controlledVehicle == data.vehicle then - targetColor = 'activeGreen'; - button:setCanBeClicked(false); - elseif button.isDisabled then - targetColor = 'whiteDisabled'; - elseif button.isClicked then - targetColor = 'activeRed'; - elseif button.isHovered then - targetColor = 'hover'; - else - targetColor = 'white'; - end; - - -- set color - if currentColor ~= targetColor then - button:setColor(targetColor); - end; - - -- NOTE: do not use button:render() here, as we neither need the button.show check, nor the hoveredButton var, nor the color setting. Simply rendering the overlay suffices - button.overlay:render(); - end; - end; - end; - - return line; -end; - -function CpManager:resetGlobalInfoTextButtons() - for _,button in pairs(self.globalInfoText.buttons) do - button:setClicked(false); - button:setHovered(false); - end; -end - -function CpManager:getHasGlobalInfoText() - return self.globalInfoText.hasContent -end - -- #################################################################################################### -- 2D COURSE DRAW SETUP function CpManager:setup2dCourseData(createOverlays) diff --git a/Events/InfoTextEvent.lua b/Events/InfoTextEvent.lua index 3c7df036c..3e18da41e 100644 --- a/Events/InfoTextEvent.lua +++ b/Events/InfoTextEvent.lua @@ -44,9 +44,12 @@ end function InfoTextEvent:run(connection) -- wir fuehren das empfangene event aus courseplay.debugVehicle(courseplay.DBG_MULTIPLAYER,vehicle,"run infoText event") if self.vehicle then - CpManager:setGlobalInfoText(self.vehicle, self.refIdx, self.forceRemove) + if self.forceRemove then + g_globalInfoTextHandler:resetInfoText(self.vehicle,self.refIdx) + else + g_globalInfoTextHandler:setInfoText(self.vehicle,self.refIdx) + end end - end function InfoTextEvent.sendEvent(vehicle,refIdx,forceRemove) diff --git a/FieldworkAIDriver.lua b/FieldworkAIDriver.lua index bed4f39d9..dbe3cc0ee 100644 --- a/FieldworkAIDriver.lua +++ b/FieldworkAIDriver.lua @@ -304,6 +304,12 @@ function FieldworkAIDriver:stop(msgReference) AIDriver.stop(self, msgReference) end + +function FieldworkAIDriver:setWorkFinished(msgReference) + self:stopWork() + AIDriver.setWorkFinished(self, msgReference) +end + function FieldworkAIDriver:writeUpdateStream(streamId, connection, dirtyMask) if self.vehicle.cp.settings.convoyActive:is(true) then streamWriteInt32(streamId,self.convoyCurrentDistance) diff --git a/GlobalInfoTextHandler.lua b/GlobalInfoTextHandler.lua new file mode 100644 index 000000000..879da56e6 --- /dev/null +++ b/GlobalInfoTextHandler.lua @@ -0,0 +1,410 @@ +--[[ +This file is part of Courseplay (https://github.com/Courseplay/courseplay) +Copyright (C) 2021 courseplay dev team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +--[[ + The global info texts handler controls all info texts at the left side of the screen relative to the vehicles. + It enables quick vehicle swapping by pressing the button which has an info texts by the vehicle. +]]-- + + +---@class GlobalInfoTextHandler +GlobalInfoTextHandler = CpObject() + +GlobalInfoTextHandler.attributes = { + {name = 'level', getXmlFunction = getXMLInt}, + {name = 'text', getXmlFunction = getXMLString}, +} + +function GlobalInfoTextHandler:init() + self.playerOnFootMouseEnabled = false; + self.wasPlayerFrozen = false; + self.infoTexts = {} + self.vehicles = {} +end + +--- Load the info text xml File. +function GlobalInfoTextHandler:loadFromXml() + self.xmlFileName = Utils.getFilename('config/InfoTexts.xml', courseplay.path) + self.xmlFile = self:loadXmlFile(self.xmlFileName) +end + +--- Loads the info texts. +---@param fileName string +function GlobalInfoTextHandler:loadXmlFile(fileName) + courseplay.info('Loading Info texts from %s ...', fileName) + if fileExists(fileName) then + local xmlFile = loadXMLFile('infoTexts', fileName); + local rootElement = 'InfoTexts' + if xmlFile and hasXMLProperty(xmlFile, rootElement) then + local i = 0 + while true do + local infoTextElement = string.format('%s.InfoText(%d)', rootElement, i) + if hasXMLProperty(xmlFile, infoTextElement) then + self:loadInfoText(xmlFile, infoTextElement) + else + break + end + i = i + 1 + end + return xmlFile + end + else + courseplay.info('Info text file %s does not exist.', fileName) + end +end + +--- Load a single info text. +---@param xmlFile number id of the loaded xml file. +---@param infoTextElement string xml key to the info text element. +function GlobalInfoTextHandler:loadInfoText(xmlFile, infoTextElement) + local infoText = {} + local name = getXMLString(xmlFile,string.format("%s#name",infoTextElement)) + for _,attribute in pairs(self.attributes) do + infoText[attribute.name] = attribute.getXmlFunction(xmlFile,string.format("%s#%s",infoTextElement,attribute.name)) + end + self.infoTexts[name] = infoText +end + +--- Setup all the basic global info text information. +--- Needs to be done after the hud is initialized. +function GlobalInfoTextHandler:setup() + --- Load info texts + self:loadFromXml() + + --- All visual/button variables + self.posY = 0.01238; -- = ingameMap posY + self.posYAboveMap = self.posY + 0.027777777777778 + 0.20833333333333; + self.fontSize = courseplay.hud:pxToNormal(18, 'y'); + self.lineHeight = self.fontSize * 1.2; + self.lineMargin = self.lineHeight * 0.2; + self.buttonHeight = self.lineHeight; + self.buttonWidth = self.buttonHeight / g_screenAspectRatio; + self.buttonPosX = 0.015625; -- = ingameMap posX + self.buttonMargin = self.buttonWidth * 0.4; + self.backgroundPadding = self.buttonWidth * 0.2; + self.backgroundImg = 'dataS2/menu/white.png'; + self.backgroundPosX = self.buttonPosX + self.buttonWidth + self.buttonMargin; + self.backgroundPosY = self.posY; + self.textPosX = self.backgroundPosX + self.backgroundPadding; + self.content = {}; + self.vehicleHasText = {}; + self.levelColors = { + [-2] = courseplay.hud.colors.closeRed; + [-1] = courseplay.hud.colors.activeRed; + [0] = courseplay.hud.colors.hover; + [1] = courseplay.hud.colors.activeGreen; + }; + + self.maxNum = 20; + self.overlays = {}; + self.buttons = {}; + for i=1, self.maxNum do + local posY = self.backgroundPosY + (i - 1) * self.lineHeight; + self.overlays[i] = Overlay:new(self.backgroundImg, self.backgroundPosX, posY, 0.1, self.buttonHeight); + courseplay.button:new(self, 'globalInfoText', 'iconSprite.png', 'goToVehicle', i, self.buttonPosX, posY, self.buttonWidth, self.buttonHeight); + end; + self.buttonsClickArea = { + x1 = self.buttonPosX; + x2 = self.buttonPosX + self.buttonWidth; + y1 = self.backgroundPosY, + y2 = self.backgroundPosY + (self.maxNum * (self.lineHeight + self.lineMargin)); + }; + self.hasContent = false; +end + +--- TODO: Find a better solution then to saves these in the vehicle. +--- Adds a new vehicle. +---@param vehicle table +function GlobalInfoTextHandler:addVehicle(vehicle) + self.vehicles[vehicle] = { + currentInfoTexts = {}, + lastInfoTexts = {} + } +end + +--- Removes a vehicle. +---@param vehicle table +function GlobalInfoTextHandler:removeVehicle(vehicle) + self:resetAllInfoTextsForVehicle(vehicle) + self.vehicles[vehicle] = nil +end + + +--- Reset all not used info texts at the end of the vehicle update loop. +---@param vehicle table +function GlobalInfoTextHandler:resetInactiveInfoTextsForVehicle(vehicle) + for refIdx,_ in pairs(self.infoTexts) do + if not self.vehicles[vehicle].currentInfoTexts[refIdx] then + if self.vehicles[vehicle].lastInfoTexts[refIdx] then + self:resetInfoText(vehicle,refIdx) + end + end + end + self.vehicles[vehicle].lastInfoTexts = self.vehicles[vehicle].currentInfoTexts + self.vehicles[vehicle].currentInfoTexts = {} +end + +--- Completely resets all info texts related to the vehicle. +---@param vehicle table +function GlobalInfoTextHandler:resetAllInfoTextsForVehicle(vehicle) + for refIdx,_ in pairs(self.infoTexts) do + if self.vehicles[vehicle].currentInfoTexts[refIdx] + or self.vehicles[vehicle].lastInfoTexts[refIdx] then + self:resetInfoText(vehicle,refIdx) + end + end +end + +--- Sets a global info text for the given vehicle. +---@param vehicle table +---@param refIdx string index for the info text. +function GlobalInfoTextHandler:setInfoText(vehicle, refIdx) + + local data = self.infoTexts[refIdx] + if self.vehicles[vehicle].currentInfoTexts[refIdx] == nil or self.vehicles[vehicle].currentInfoTexts[refIdx] ~= data.level then + if g_server ~= nil then + InfoTextEvent.sendEvent(vehicle,refIdx,false) + end + + local text = nameNum(vehicle) .. " " .. courseplay:loc(data.text); + + self.vehicles[vehicle].currentInfoTexts[refIdx] = data.level; + + if self.content[vehicle.rootNode] == nil then + self.content[vehicle.rootNode] = {}; + end; + self.content[vehicle.rootNode][refIdx] = { + level = data.level, + text = text, + backgroundWidth = getTextWidth(self.fontSize, text) + self.backgroundPadding * 2.5, + vehicle = vehicle + }; + end; +end + +--- Resets a global info text for the given vehicle. +---@param vehicle table +---@param refIdx string index for the info text. +function GlobalInfoTextHandler:resetInfoText(vehicle, refIdx) + + if g_server ~= nil then + InfoTextEvent.sendEvent(vehicle,refIdx,true) + end + if self.content[vehicle.rootNode] and self.content[vehicle.rootNode][refIdx] then + self.content[vehicle.rootNode][refIdx] = nil; + end; + self.vehicles[vehicle][refIdx] = nil + if #self.vehicles[vehicle].currentInfoTexts == 0 then + self.content[vehicle.rootNode] = nil; + end; +end + + +--- Renders the info texts buttons. +function GlobalInfoTextHandler:render() + self.hasContent = false; + local numLinesRendered = 0; + local basePosY = self.posY; + if not (g_currentMission.hud.ingameMap.isVisible and g_currentMission.hud.ingameMap:getIsFullSize()) and next(self.content) ~= nil then + self.hasContent = true; + if g_currentMission.hud.ingameMap.isVisible then + basePosY = self.posYAboveMap; + end; + end; + local line = 0; + courseplay:setFontSettings('white', false, 'left'); + for _,refIndexes in pairs(self.content) do + if line >= self.maxNum then + break; + end; + + for refIdx,data in pairs(refIndexes) do + line = line + 1; + + -- background + local bg = self.overlays[line]; + bg:setColor(unpack(self.levelColors[data.level])); + local gfxPosY = basePosY + (line - 1) * (self.lineHeight + self.lineMargin); + bg:setPosition(bg.x, gfxPosY); + bg:setDimension(data.backgroundWidth, bg.height); + bg:render(); + + -- text + local textPosY = gfxPosY + (self.lineHeight - self.fontSize) * 1.2; -- should be (lineHeight-fontSize)*0.5, but there seems to be some pixel/sub-pixel rendering error + renderText(self.textPosX, textPosY, self.fontSize, data.text); + -- button + local button = self.buttons[line]; + if button ~= nil then + button:setPosition(button.overlay.x, gfxPosY) + + local currentColor = button.curColor; + local targetColor = currentColor; + + button:setCanBeClicked(true); + button:setDisabled(data.vehicle.isBroken or data.vehicle.isControlled); + button:setParameter(data.vehicle); + if g_currentMission.controlledVehicle and g_currentMission.controlledVehicle == data.vehicle then + targetColor = 'activeGreen'; + button:setCanBeClicked(false); + elseif button.isDisabled then + targetColor = 'whiteDisabled'; + elseif button.isClicked then + targetColor = 'activeRed'; + elseif button.isHovered then + targetColor = 'hover'; + else + targetColor = 'white'; + end; + + -- set color + if currentColor ~= targetColor then + button:setColor(targetColor); + end; + + -- NOTE: do not use button:render() here, as we neither need the button.show check, nor the hoveredButton var, nor the color setting. Simply rendering the overlay suffices + button.overlay:render(); + end; + end; + end; + self.buttonsClickArea.y1 = basePosY; + self.buttonsClickArea.y2 = basePosY + (line * (self.lineHeight + self.lineMargin)); +end; + +--- Resets all buttons. +function GlobalInfoTextHandler:resetButtons() + for _,button in pairs(self.buttons) do + button:setClicked(false); + button:setHovered(false); + end; +end + +--- Delete all button overlays. +function GlobalInfoTextHandler:delete() + --delete globalInfoText overlays + for i,button in pairs(self.buttons) do + button:deleteOverlay(); + + if self.overlays[i] then + local ovl = self.overlays[i]; + if ovl.overlayId ~= nil and ovl.delete ~= nil then + ovl:delete(); + end; + end; + end; +end + +--- Mouse event to interact with the info text buttons. +---@param posX number mouse x position +---@param posY number mouse y position +---@param isDown boolean the mouse button down ? +---@param isUp boolean the mouse button up ? +---@param mouseKey number mouse button was pressed ? +function GlobalInfoTextHandler:mouseEvent(posX, posY, isDown, isUp, mouseKey) + local area = self.buttonsClickArea; + + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + -- LEFT CLICK to click the button shown in globalInfoText + if (isDown or isUp) and mouseKey == courseplay.inputBindings.mouse.primaryButtonId and courseplay:mouseIsInArea(posX, posY, area.x1, area.x2, area.y1, area.y2) then + self:onPrimaryMouseClick(posX, posY, isDown, isUp, mouseKey) + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + -- RIGHT CLICK to activate the mouse cursor when I'm not in a vehicle and a globalInfoText is shown + elseif isUp and mouseKey == courseplay.inputBindings.mouse.secondaryButtonId and g_currentMission.controlledVehicle == nil then + self:onSecondaryMouseClick(posX, posY, isDown, isUp, mouseKey) + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + -- HOVER + elseif not isDown and not isUp and self.hasContent then + for _,button in pairs(self.buttons) do + button:setClicked(false) + if button.show and not button.isHidden then + button:setHovered(button:getHasMouse(posX, posY)) + end + end + end +end + +---Secondary mouse button pressed +---@param posX number mouse x position +---@param posY number mouse y position +---@param isDown boolean the mouse button down ? +---@param isUp boolean the mouse button up ? +---@param mouseKey number mouse button was pressed ? +function GlobalInfoTextHandler:onSecondaryMouseClick(posX, posY, isDown, isUp, mouseKey) + if self.hasContent and not self.playerOnFootMouseEnabled and not g_currentMission.player.currentTool then + self.playerOnFootMouseEnabled = true + self.wasPlayerFrozen = g_currentMission.isPlayerFrozen + g_currentMission.isPlayerFrozen = true + elseif self.playerOnFootMouseEnabled then + self.playerOnFootMouseEnabled = false + if self.hasContent then --if a button was hovered when deactivating the cursor, deactivate hover state + self:resetButtons() + end; + if not self.wasPlayerFrozen then + g_currentMission.isPlayerFrozen = false + end; + end; + g_inputBinding:setShowMouseCursor(self.playerOnFootMouseEnabled) +end + +---Primary mouse button pressed +---@param posX number mouse x position +---@param posY number mouse y position +---@param isDown boolean the mouse button down ? +---@param isUp boolean the mouse button up ? +---@param mouseKey number mouse button was pressed ? +function GlobalInfoTextHandler:onPrimaryMouseClick(posX, posY, isDown, isUp, mouseKey) + if self.hasContent then + for i,button in pairs(self.buttons) do + if button.show and button:getHasMouse(posX, posY) then + button:setClicked(isDown) + if isUp then + local sourceVehicle = g_currentMission.controlledVehicle or button.parameter + button:handleMouseClick(sourceVehicle) + end + break + end + end + end +end + +---Is the second mouse button allowed, when the player isn't in a vehicle ? +---@return boolean allowed? +function GlobalInfoTextHandler:isSecondaryMouseClickAllowed() + return self.playerOnFootMouseEnabled or self.hasContent and not self.playerOnFootMouseEnabled and not g_currentMission.player.currentTool +end + +---Is the first mouse button allowed, when the player isn't in a vehicle ? +---@return boolean allowed? +function GlobalInfoTextHandler:isPrimaryMouseClickAllowed() + return self.hasContent and self.playerOnFootMouseEnabled +end + +--- Gets all info texts +function GlobalInfoTextHandler:getInfoTexts() + return self.infoTexts +end + +--- Switch to a vehicle, which info text button was pressed. +function GlobalInfoTextHandler:goToVehicle(vehicle) + g_client:getServerConnection():sendEvent(VehicleEnterRequestEvent:new(vehicle, g_currentMission.missionInfo.playerStyle, g_currentMission.player.farmId)); + g_currentMission.isPlayerFrozen = false; + self.playerOnFootMouseEnabled = false; + g_inputBinding:setShowMouseCursor(vehicle.cp.mouseCursorActive); +end + + +g_globalInfoTextHandler = GlobalInfoTextHandler() \ No newline at end of file diff --git a/TriggerHandler.lua b/TriggerHandler.lua index 6a25f331e..377055fb6 100644 --- a/TriggerHandler.lua +++ b/TriggerHandler.lua @@ -738,7 +738,7 @@ function TriggerHandler:onActivateObject(superFunc,vehicle) return else triggerHandler:setFuelLoadingState() - CpManager:setGlobalInfoText(vehicle, 'FARM_SILO_IS_EMPTY') + g_globalInfoTextHandler:setInfoText(vehicle, 'FARM_SILO_IS_EMPTY') courseplay.debugFormat(courseplay.DBG_LOAD_UNLOAD, 'No Diesel at this trigger.') end else @@ -791,7 +791,7 @@ function TriggerHandler:handleLoadingCallback(trigger,object,fillUnitIndex,loadi -- min FillLevel not reached to start filling elseif callbackData.callback == TriggerHandler.CALLBACK.MIN_NOT_REACHED then self:setLoadingState() - CpManager:setGlobalInfoText(self.vehicle, 'FARM_SILO_IS_EMPTY'); + g_globalInfoTextHandler:setInfoText(self.vehicle, 'FARM_SILO_IS_EMPTY'); self:debugSparse(object, 'LoadingTrigger: minLevel not reached, fillType: '..g_fillTypeManager:getFillTypeByIndex(fillType).title) return --min is reached so continue.. @@ -805,14 +805,14 @@ function TriggerHandler:handleLoadingCallback(trigger,object,fillUnitIndex,loadi --runCounter = 0 if lastCallbackData and lastCallbackData.callback == TriggerHandler.CALLBACK.RUN_COUNTER_NOT_REACHED then self:setLoadingState() - CpManager:setGlobalInfoText(self.vehicle, 'RUNCOUNTER_ERROR_FOR_TRIGGER'); + g_globalInfoTextHandler:setInfoText(self.vehicle, 'RUNCOUNTER_ERROR_FOR_TRIGGER'); self:debugSparse(object, 'last runCounter=0, fillType: '..g_fillTypeManager:getFillTypeByIndex(lastCallbackData.data.fillType).title) return end if lastCallbackData and (lastCallbackData.callback == TriggerHandler.CALLBACK.SKIP_LOADING or lastCallbackData.callback == TriggerHandler.CALLBACK.SEPARATE_FILLTYPE_NOT_ALLOWED) then --not enough fillTypes loaded!! self:setLoadingState() - CpManager:setGlobalInfoText(self.vehicle, 'FARM_SILO_IS_EMPTY'); + g_globalInfoTextHandler:setInfoText(self.vehicle, 'FARM_SILO_IS_EMPTY'); self:debugSparse(object, 'last FillType minLevel not reached, fillType: '..g_fillTypeManager:getFillTypeByIndex(lastCallbackData.data.fillType).title) return end @@ -1179,7 +1179,7 @@ function TriggerHandler:onUpdateDischargeable(dt, isActiveForInput, isActiveForI spec:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT) end if currentDischargeNode.dischargeFailedReason and currentDischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY then - CpManager:setGlobalInfoText(rootVehicle, 'FARM_SILO_IS_FULL') -- not working for now, might have to double check + g_globalInfoTextHandler:setInfoText(rootVehicle, 'FARM_SILO_IS_FULL') -- not working for now, might have to double check end if currentDischargeNode.dischargeObject then if triggerHandler.objectsInTrigger[self] == nil then diff --git a/base.lua b/base.lua index b405973fc..bc60245ed 100644 --- a/base.lua +++ b/base.lua @@ -69,11 +69,8 @@ function courseplay:onLoad(savegame) self.cp.infoText = nil; -- info text in tractor self.cp.toolTip = nil; - -- global info text - also displayed when not in vehicle - self.cp.hasSetGlobalInfoTextThisLoop = {}; - self.cp.activeGlobalInfoTexts = {}; - self.cp.numActiveGlobalInfoTexts = 0; - + --- Adds the vehicle to the global info texts handler + g_globalInfoTextHandler:addVehicle(self) -- CP mode @@ -268,18 +265,12 @@ function courseplay:onLoad(savegame) self.cp.mouseCursorActive = false; - -- 2D course - self.cp.drawCourseMode = courseplay.COURSE_2D_DISPLAY_OFF; -- 2D pda map background -- TODO: MP? if g_currentMission.hud.ingameMap and g_currentMission.hud.ingameMap.mapOverlay and g_currentMission.hud.ingameMap.mapOverlay.filename then self.cp.course2dPdaMapOverlay = Overlay:new(g_currentMission.hud.ingameMap.mapOverlay.filename, 0, 0, 1, 1); self.cp.course2dPdaMapOverlay:setColor(1, 1, 1, CpManager.course2dPdaMapOpacity); end; - -- HUD - courseplay.hud:setupVehicleHud(self); - - courseplay:validateCanSwitchMode(self); ---@type SettingsContainer self.cp.settings = SettingsContainer.createVehicleSpecificSettings(self) @@ -287,6 +278,12 @@ function courseplay:onLoad(savegame) ---@type SettingsContainer self.cp.courseGeneratorSettings = SettingsContainer.createCourseGeneratorSettings(self) + -- HUD + courseplay.hud:setupVehicleHud(self); + + courseplay:validateCanSwitchMode(self); + + courseplay.signs:updateWaypointSigns(self); courseplay:setAIDriver(self, self.cp.mode) @@ -303,8 +300,6 @@ function courseplay:onLeaveVehicle() courseplay:setMouseCursor(self, false); courseEditor:reset() end - ---Update mouse action event texts - CpManager:updateMouseInputText() --hide visual i3D waypoint signs when not in vehicle courseplay.signs:setSignsVisibility(self, true); end @@ -319,8 +314,6 @@ function courseplay:onEnterVehicle() if self.cp.mouseCursorActive then courseplay:setMouseCursor(self, true); end; - ---Update mouse action event texts - CpManager:updateMouseInputText() --show visual i3D waypoint signs only when in vehicle courseplay.signs:setSignsVisibility(self); end @@ -372,7 +365,7 @@ function courseplay:onDraw() self.cp.hudBroken = true end - if self.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_2DONLY or self.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_BOTH then + if self.cp.settings.courseDrawMode:isCourseMapVisible() then courseplay:drawCourse2D(self, false); end; end; --END draw() @@ -507,7 +500,7 @@ function courseplay:onUpdate(dt) end - if self.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_DBGONLY or self.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_BOTH then + if self.cp.settings.courseDrawMode:isCourseVisible() then courseplay:drawWaypointsLines(self); end; @@ -518,17 +511,11 @@ function courseplay:onUpdate(dt) -- we are in drive mode and single player /MP server if self.cp.isDriving and g_server ~= nil then - for refIdx,_ in pairs(CpManager.globalInfoText.msgReference) do - self.cp.hasSetGlobalInfoTextThisLoop[refIdx] = false; - end; - + local status, err = xpcall(self.cp.driver.update, function(err) printCallstack(); return err end, self.cp.driver, dt) - for refIdx,_ in pairs(self.cp.activeGlobalInfoTexts) do - if not self.cp.hasSetGlobalInfoTextThisLoop[refIdx] then - CpManager:setGlobalInfoText(self, refIdx, true); --force remove - end; - end; + --- Resets all global info texts that were not called in this update loop. + g_globalInfoTextHandler:resetInactiveInfoTextsForVehicle(self) if not status then courseplay.infoVehicle(self, 'Exception, stopping Courseplay driver, %s', tostring(err)) @@ -605,13 +592,10 @@ end; function courseplay:onPreDelete() ---Delete map hotspot and all global info texts leftovers. - CpMapHotSpot.deleteMapHotSpot(self) + CpMapHotSpot.deleteMapHotSpot(self) if g_server ~= nil then - for refIdx,_ in pairs(CpManager.globalInfoText.msgReference) do - if self.cp.activeGlobalInfoTexts[refIdx] ~= nil then - CpManager:setGlobalInfoText(self, refIdx, true) - end - end + --- Removes this vehicle form the global info texts handler. + g_globalInfoTextHandler:removeVehicle(self) end end @@ -1318,11 +1302,7 @@ function courseplay.onStopCpAIDriver(vehicle, reason, noEventSend) --remove any global info texts if g_server ~= nil then - for refIdx,_ in pairs(CpManager.globalInfoText.msgReference) do - if vehicle.cp.activeGlobalInfoTexts[refIdx] ~= nil then - CpManager:setGlobalInfoText(vehicle, refIdx, true); - end; - end; + g_globalInfoTextHandler:resetAllInfoTextsForVehicle(vehicle) end --remove from activeCoursePlayers diff --git a/button.lua b/button.lua index d03cfa4aa..499c57080 100644 --- a/button.lua +++ b/button.lua @@ -75,8 +75,8 @@ function courseplay.button:new(vehicle, hudPage, img, functionToCall, parameter, self:setSpecialButtonUVs(); end; - if vehicle.isCourseplayManager then - table.insert(vehicle[hudPage].buttons, self); + if not vehicle.cp then + table.insert(vehicle.buttons, self); else table.insert(vehicle.cp.buttons[hudPage], self); end; @@ -238,8 +238,9 @@ function courseplay.button:handleMouseClick(vehicle) if self.functionToCall == "showSaveCourseForm" then vehicle.cp.imWriting = true end + --- Global info text button was pressed, switch to vehicle. if self.functionToCall == "goToVehicle" then - courseplay:executeFunction(vehicle, "goToVehicle", parameter) + g_globalInfoTextHandler:goToVehicle(parameter) else courseplay:debug(string.format("%s: MOUSE_BUTTON_ClICKED: %s(%s)", nameNum(vehicle), tostring(self.functionToCall), tostring(parameter)), courseplay.DBG_HUD); self:handleInput(vehicle,parameter) diff --git a/config/InfoTexts.xml b/config/InfoTexts.xml new file mode 100644 index 000000000..746d79346 --- /dev/null +++ b/config/InfoTexts.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/courseplay.lua b/courseplay.lua index 3d2a75b83..72390fe3b 100644 --- a/courseplay.lua +++ b/courseplay.lua @@ -112,6 +112,7 @@ local function initialize() 'AITurn', 'VehicleConfigurations', 'ActionEventsLoader', + 'GlobalInfoTextHandler', 'course-generator/geo', 'course-generator/Island', 'course-generator/courseGenerator', @@ -222,12 +223,6 @@ local function setGlobalData() courseplay.lights.HEADLIGHT_STREET = 1; courseplay.lights.HEADLIGHT_FULL = 7; - -- 2D/debug lines display options - courseplay.COURSE_2D_DISPLAY_OFF = 0; - courseplay.COURSE_2D_DISPLAY_2DONLY = 1; - courseplay.COURSE_2D_DISPLAY_DBGONLY = 2; - courseplay.COURSE_2D_DISPLAY_BOTH = 3; - -- number separators local langNumData = { br = { '.', ',' }, @@ -259,23 +254,22 @@ local function setGlobalData() [1]={name='self.cp.mode',dataFormat='Int'}, [2]={name='self.cp.turnDiameterAuto',dataFormat='Float'}, [3]={name='self.cp.canDrive',dataFormat='Bool'}, - [6]={name='self.cp.drivingDirReverse',dataFormat='Bool'}, - [7]={name='self.cp.fieldEdge.customField.isCreated',dataFormat='Bool'}, - [8]={name='self.cp.fieldEdge.customField.fieldNum',dataFormat='Int'}, - [9]={name='self.cp.fieldEdge.customField.selectedFieldNumExists',dataFormat='Bool'}, - [10]={name='self.cp.fieldEdge.selectedField.fieldNum',dataFormat='Int'}, - [11]={name='self.cp.globalInfoTextLevel',dataFormat='Int'}, - [15]={name='self.cp.isDriving',dataFormat='Bool'}, - [26]={name='self.cp.hud.openWithMouse',dataFormat='Bool'}, - [29]={name='self.cp.workWidth',dataFormat='Float'}, - [20]={name='self.cp.turnDiameterAutoMode',dataFormat='Bool'}, - [21]={name='self.cp.turnDiameter',dataFormat='Float'}, - [22]={name='self.cp.coursePlayerNum',dataFormat='Int'}, --?? - [23]={name='self.cp.laneOffset',dataFormat='Float'}, - [36]={name='self.cp.hud.currentPage',dataFormat='Int'}, - [37]={name='self.cp.waypointIndex',dataFormat='Int'}, - [38]={name='self.cp.isRecording',dataFormat='Bool'}, - [39]={name='self.cp.recordingIsPaused',dataFormat='Bool'}, + [4]={name='self.cp.drivingDirReverse',dataFormat='Bool'}, + [5]={name='self.cp.fieldEdge.customField.isCreated',dataFormat='Bool'}, + [6]={name='self.cp.fieldEdge.customField.fieldNum',dataFormat='Int'}, + [7]={name='self.cp.fieldEdge.customField.selectedFieldNumExists',dataFormat='Bool'}, + [8]={name='self.cp.fieldEdge.selectedField.fieldNum',dataFormat='Int'}, + [9]={name='self.cp.isDriving',dataFormat='Bool'}, + [10]={name='self.cp.hud.openWithMouse',dataFormat='Bool'}, + [11]={name='self.cp.workWidth',dataFormat='Float'}, + [12]={name='self.cp.turnDiameterAutoMode',dataFormat='Bool'}, + [13]={name='self.cp.turnDiameter',dataFormat='Float'}, + [14]={name='self.cp.coursePlayerNum',dataFormat='Int'}, --?? + [15]={name='self.cp.laneOffset',dataFormat='Float'}, + [16]={name='self.cp.hud.currentPage',dataFormat='Int'}, + [17]={name='self.cp.waypointIndex',dataFormat='Int'}, + [18]={name='self.cp.isRecording',dataFormat='Bool'}, + [19]={name='self.cp.recordingIsPaused',dataFormat='Bool'}, } courseplay.globalSettings = SettingsContainer.createGlobalSettings() diff --git a/hud.lua b/hud.lua index 785f64ee0..efef6b83d 100644 --- a/hud.lua +++ b/hud.lua @@ -563,15 +563,9 @@ function courseplay.hud:renderHud(vehicle) -- 2D/DEBUG LINE BUTTON MODE - if vehicle.cp.drawCourseMode ~= courseplay.COURSE_2D_DISPLAY_OFF then - local txt; - if vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_2DONLY then - txt = '2D'; - elseif vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_DBGONLY then - txt = '\nDBG'; - else - txt = '2D\nDBG'; - end; + local courseDrawModeSetting = vehicle.cp.settings.courseDrawMode + if not courseDrawModeSetting:isDeactivated() then + local txt = courseDrawModeSetting:getText() courseplay:setFontSettings('white', true); renderText(vehicle.cp.hud.changeDrawCourseModeButton.x + vehicle.cp.hud.changeDrawCourseModeButton.width * 0.5, self.topIconsY + self.fontSizes.version * 1.25, self.fontSizes.version, txt); courseplay:setFontSettings('white', false); @@ -1215,7 +1209,7 @@ function courseplay.hud:updatePageContent(vehicle, page) else self:showShowWaypointsButtons(vehicle, false) end - + vehicle.cp.hud.changeDrawCourseModeButton:setActive(not vehicle.cp.settings.courseDrawMode:isDeactivated()) -- make sure AutoDrive mode has all options currently available for the vehicle vehicle.cp.settings.autoDriveMode:update() self:setReloadPageOrder(vehicle, page, forceUpdate); @@ -1376,7 +1370,7 @@ function courseplay.hud:setupVehicleHud(vehicle) vehicle.cp.hud.saveCourseButton = courseplay.button:new(vehicle, 'global', { 'iconSprite.png', 'save' }, 'showSaveCourseForm', 'course', topIconsX[3], self.topIconsY, wMiddle, hMiddle, nil, nil, false, false, false, courseplay:loc('COURSEPLAY_SAVE_CURRENT_COURSE')); vehicle.cp.hud.clearCurrentCourseButton = courseplay.button:new(vehicle, 'global', { 'iconSprite.png', 'courseClear' }, 'clearCurrentLoadedCourse', nil, topIconsX[0], self.topIconsY, wMiddle, hMiddle, nil, nil, false, false, false, courseplay:loc('COURSEPLAY_CLEAR_COURSE')); - vehicle.cp.hud.changeDrawCourseModeButton = courseplay.button:new(vehicle, 'global', { 'iconSprite.png', 'eye' }, 'changeDrawCourseMode', 1, self.col1posX, self.topIconsY, wMiddle, hMiddle, nil, -1, false, false, true); + vehicle.cp.hud.changeDrawCourseModeButton = courseplay.button:new(vehicle, 'global', { 'iconSprite.png', 'eye' }, 'changeByX', 1, self.col1posX, self.topIconsY, wMiddle, hMiddle, nil, -1, false, false, true):setSetting(vehicle.cp.settings.courseDrawMode) self:setupCpModeButtons(vehicle) self:setupRecordingButtons(vehicle) self:setupCoursePageButtons(vehicle,2) diff --git a/input.lua b/input.lua index 5d19e2a80..a673dc0ac 100644 --- a/input.lua +++ b/input.lua @@ -124,7 +124,7 @@ function courseplay:onMouseEvent(posX, posY, isDown, isUp, mouseButton) -- ################################################## -- 2D COURSE WINDOW: DRAG + DROP MOVE - if vehicle.cp.course2dDrawData and (vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_2DONLY or vehicle.cp.drawCourseMode == courseplay.COURSE_2D_DISPLAY_BOTH) then + if vehicle.cp.course2dDrawData and vehicle.cp.settings.courseDrawMode:isCourseVisible() then local plot = CpManager.course2dPlotField; if isDown and mouseButton == courseplay.inputBindings.mouse.primaryButtonId and vehicle.cp.mouseCursorActive and vehicle:getIsEntered() and courseplay:mouseIsInArea(posX, posY, plot.x, plot.x + plot.width, plot.y, plot.y + plot.height) then CpManager.course2dDragDropMouseDown = { posX, posY }; @@ -162,11 +162,7 @@ end function courseplay:executeFunction(self, func, value, page) courseplay:debug("executeFunction: function: " .. func .. " value: " .. tostring(value) .. " page: " .. tostring(page), courseplay.DBG_MULTIPLAYER) --legancy code - if func == "setMPGlobalInfoText" then - CpManager:setGlobalInfoText(self, value, page) - courseplay:debug(" setting infoText: "..value..", force remove: "..tostring(page),courseplay.DBG_MULTIPLAYER) - return - elseif StringUtil.startsWith(func,"self") or StringUtil.startsWith(func,"courseplay") then + if StringUtil.startsWith(func,"self") or StringUtil.startsWith(func,"courseplay") then courseplay:debug(" setting value",courseplay.DBG_MULTIPLAYER) courseplay:setVarValueFromString(self, func, value) --courseplay:debug(" "..tostring(func)..": "..tostring(value),courseplay.DBG_MULTIPLAYER) diff --git a/settings.lua b/settings.lua index f63449090..05d961d63 100644 --- a/settings.lua +++ b/settings.lua @@ -722,13 +722,7 @@ function courseplay:changeDebugChannelSection(vehicle, changeBy) --courseplay.buttons:setActiveEnabled(vehicle, 'debug'); end; -function courseplay:goToVehicle(curVehicle, targetVehicle) - -- print(string.format("%s: goToVehicle(): targetVehicle=%q", nameNum(curVehicle), nameNum(targetVehicle))); - g_client:getServerConnection():sendEvent(VehicleEnterRequestEvent:new(targetVehicle, g_currentMission.missionInfo.playerStyle, g_currentMission.player.farmId)); - g_currentMission.isPlayerFrozen = false; - CpManager.playerOnFootMouseEnabled = false; - g_inputBinding:setShowMouseCursor(targetVehicle.cp.mouseCursorActive); -end; + --FIELD EDGE PATHS function courseplay:createFieldEdgeButtons(vehicle) @@ -3806,7 +3800,7 @@ end --- The wait time setting can be incremented by 15 seconds up to max: 60min. --- ----@class WaitTimeSetting +---@class WaitTimeSetting : IntSetting WaitTimeSetting = CpObject(IntSetting) function WaitTimeSetting:init(vehicle) local max = 3600 --- 60 min @@ -3840,6 +3834,43 @@ function WaitTimeSetting:changeByX(x) IntSetting.changeByX(self,x) end +--- @class CourseDrawModeSetting : SettingList +CourseDrawModeSetting = CpObject(SettingList) +-- 2D/debug lines display options +CourseDrawModeSetting.COURSE_2D_DISPLAY_OFF = 0; +CourseDrawModeSetting.COURSE_2D_DISPLAY_2DONLY = 1; +CourseDrawModeSetting.COURSE_2D_DISPLAY_DBGONLY = 2; +CourseDrawModeSetting.COURSE_2D_DISPLAY_BOTH = 3; +function CourseDrawModeSetting:init(vehicle) + local values = { + self.COURSE_2D_DISPLAY_OFF, + self.COURSE_2D_DISPLAY_2DONLY, + self.COURSE_2D_DISPLAY_DBGONLY, + self.COURSE_2D_DISPLAY_BOTH + } + local texts = { + "", + "2D", + "\nDBG", + "2D\nDBG" + } + SettingList.init(self,"courseDrawMode","","",vehicle,values,texts) + self:set(self.COURSE_2D_DISPLAY_OFF) + self.syncValue = false +end + +function CourseDrawModeSetting:isDeactivated() + return self:get() == self.COURSE_2D_DISPLAY_OFF +end + +function CourseDrawModeSetting:isCourseMapVisible() + return self:get() == self.COURSE_2D_DISPLAY_2DONLY or self:get() == self.COURSE_2D_DISPLAY_BOTH +end + +function CourseDrawModeSetting:isCourseVisible() + return self:get() == self.COURSE_2D_DISPLAY_DBGONLY or self:get() == self.COURSE_2D_DISPLAY_BOTH +end + --[[ ---@class SearchCombineAutomaticallySetting : BooleanSetting SearchCombineAutomaticallySetting = CpObject(BooleanSetting) @@ -4035,6 +4066,7 @@ function SettingsContainer.createVehicleSpecificSettings(vehicle) container:addSetting(WaitTimeSetting, vehicle) container:addSetting(MixerWagonAIDriver_SiloSelectedFillTypeSetting, vehicle) container:addSetting(MixerWagonToolPositionsSetting, vehicle) + container:addSetting(CourseDrawModeSetting,vehicle) return container end diff --git a/start_stop.lua b/start_stop.lua index 405c837f3..9af570967 100644 --- a/start_stop.lua +++ b/start_stop.lua @@ -120,10 +120,7 @@ function courseplay:stop(self) courseplay:setEngineState(self, true); self.cp.saveFuel = false; end - if courseplay:getCustomTimerExists(self,'fuelSaveTimer') then - --print("reset existing timer") - courseplay:resetCustomTimer(self,'fuelSaveTimer',true) - end + if self.cp.directionNodeToTurnNodeLength ~= nil then self.cp.directionNodeToTurnNodeLength = nil