165 | function VehicleSchemaDisplay:animateDocking(startX, startY, targetX, targetY, isDocking) |
166 | local sequence = TweenSequence.new(self) |
167 | local lateDockInstant = HUDDisplayElement.MOVE_ANIMATION_DURATION * 0.5 |
168 | if not isDocking then |
169 | sequence:addInterval(HUDDisplayElement.MOVE_ANIMATION_DURATION) -- synchronize with input help element |
170 | lateDockInstant = lateDockInstant + HUDDisplayElement.MOVE_ANIMATION_DURATION |
171 | end |
172 | |
173 | sequence:addTween(MultiValueTween:new(self.setPosition, {startX, startY}, {targetX, targetY}, HUDDisplayElement.MOVE_ANIMATION_DURATION)) |
174 | -- set docked state in the middle of the animation: |
175 | sequence:insertCallback(self.lateSetDocked, isDocking, lateDockInstant) |
176 | |
177 | sequence:start() |
178 | self.animation = sequence |
179 | end |
183 | function VehicleSchemaDisplay:collectVehicleSchemaDisplayOverlays(overlays, depth, vehicle, rootVehicle, parentOverlay, x, y, rotation, invertX) |
184 | if vehicle.getAttachedImplements == nil then |
185 | return |
186 | end |
187 | |
188 | local attachedImplements = vehicle:getAttachedImplements() |
189 | for _, implement in pairs(attachedImplements) do |
190 | local object = implement.object |
191 | if object ~= nil and object.schemaOverlay ~= nil then |
192 | local selected = object:getIsSelected() |
193 | local turnedOn = object.getIsTurnedOn ~= nil and object:getIsTurnedOn() |
194 | local jointDesc = vehicle.schemaOverlay.attacherJoints[implement.jointDescIndex] |
195 | |
196 | if jointDesc ~= nil then |
197 | local invertX = invertX ~= jointDesc.invertX |
198 | local overlay = self:getSchemaOverlayForState(object.schemaOverlay, turnedOn, selected, true) |
199 | |
200 | local baseY = y + jointDesc.y * parentOverlay.height |
201 | local baseX |
202 | if invertX then |
203 | baseX = x - overlay.width + (1 - jointDesc.x) * parentOverlay.width |
204 | else |
205 | baseX = x + jointDesc.x * parentOverlay.width |
206 | end |
207 | |
208 | local rotation = rotation + jointDesc.rotation |
209 | |
210 | local offsetX, offsetY |
211 | if invertX then |
212 | offsetX = -object.schemaOverlay.offsetX * overlay.width |
213 | else |
214 | offsetX = object.schemaOverlay.offsetX * overlay.width |
215 | end |
216 | |
217 | offsetY = object.schemaOverlay.offsetY * overlay.height |
218 | local rotatedX = offsetX * math.cos(rotation) - offsetY * math.sin(rotation) |
219 | local rotatedY = offsetX * math.sin(rotation) + offsetY * math.cos(rotation) |
220 | baseX = baseX - rotatedX |
221 | baseY = baseY - rotatedY |
222 | |
223 | if not (object.getIsLowered ~= nil and object:getIsLowered(true)) then |
224 | local widthOffset, heightOffset = getNormalizedScreenValues(jointDesc.liftedOffsetX, jointDesc.liftedOffsetY) |
225 | baseX = baseX + widthOffset |
226 | baseY = baseY + heightOffset |
227 | end |
228 | |
229 | local additionalText = object:getAdditionalSchemaText() |
230 | |
231 | table.insert(overlays, {overlay=overlay, additionalText=additionalText, x=baseX, y=baseY, rotation=rotation, invertX=invertX, invisibleBorderRight=object.schemaOverlay.invisibleBorderRight, invisibleBorderLeft=object.schemaOverlay.invisibleBorderLeft}) |
232 | |
233 | if depth <= VehicleSchemaDisplay.MAX_SCHEMA_COLLECTION_DEPTH then |
234 | self:collectVehicleSchemaDisplayOverlays(overlays, depth + 1, object, rootVehicle, overlay, baseX, baseY, rotation, invertX) |
235 | end |
236 | end |
237 | end |
238 | end |
239 | end |
291 | function VehicleSchemaDisplay:drawVehicleSchemaOverlays(vehicle) |
292 | vehicle = vehicle:getRootVehicle() |
293 | |
294 | if vehicle.schemaOverlay ~= nil then |
295 | local overlays, overlayHeight = self:getVehicleSchemaOverlays(vehicle) |
296 | |
297 | local baseX, baseY = self:getPosition() |
298 | baseY = baseY + (self:getHeight() - overlayHeight) * 0.5 -- vertically center icon base in panel |
299 | if self.isDocked then |
300 | baseX = baseX + self:getWidth() -- right-align when docked to input help |
301 | end |
302 | |
303 | local minX, maxX = self:getSchemaDelimiters(overlays) |
304 | |
305 | -- dynamically scale schemas if going over size limit: |
306 | local scale = 1 |
307 | local sizeX = maxX - minX |
308 | if sizeX > self.maxSchemaWidth then |
309 | scale = self.maxSchemaWidth / sizeX |
310 | end |
311 | |
312 | local newPosX = baseX |
313 | if self.isDocked then |
314 | newPosX = newPosX - maxX * scale |
315 | else |
316 | newPosX = newPosX - minX * scale |
317 | end |
318 | |
319 | for _, overlayDesc in pairs(overlays) do |
320 | local overlay = overlayDesc.overlay |
321 | local width, height = overlay.width, overlay.height |
322 | |
323 | overlay:setInvertX(overlayDesc.invertX) |
324 | overlay:setPosition(newPosX + overlayDesc.x, baseY + overlayDesc.y) |
325 | overlay:setRotation(overlayDesc.rotation, 0, 0) |
326 | overlay:setDimension(width * scale, height * scale) |
327 | overlay:render() |
328 | |
329 | if overlayDesc.additionalText ~= nil then |
330 | local posX = newPosX + overlayDesc.x + (width * scale) * 0.5 |
331 | local posY = baseY + overlayDesc.y + (height * scale) |
332 | setTextBold(false) |
333 | setTextColor(1, 1, 1, 1) |
334 | setTextAlignment(RenderText.ALIGN_CENTER) |
335 | renderText(posX, posY, getCorrectTextSize(0.009), overlayDesc.additionalText) |
336 | setTextAlignment(RenderText.ALIGN_LEFT) |
337 | setTextColor(1, 1, 1, 1) |
338 | end |
339 | |
340 | -- reset dimension |
341 | overlay:setDimension(width, height) |
342 | end |
343 | end |
344 | end |
263 | function VehicleSchemaDisplay:getSchemaDelimiters(overlayDescriptions) |
264 | local minX = math.huge |
265 | local maxX = -math.huge |
266 | for _, overlayDesc in pairs(overlayDescriptions) do |
267 | local overlay = overlayDesc.overlay |
268 | |
269 | local cosRot = math.cos(overlayDesc.rotation) |
270 | local sinRot = math.sin(overlayDesc.rotation) |
271 | |
272 | local offX = overlayDesc.invisibleBorderLeft * overlay.width |
273 | local dx = overlay.width + (overlayDesc.invisibleBorderRight + overlayDesc.invisibleBorderLeft) * overlay.width |
274 | |
275 | local dy = overlay.height |
276 | local x = overlayDesc.x + offX * cosRot |
277 | local dx2 = dx * cosRot |
278 | local dx3 = -dy * sinRot |
279 | local dx4 = dx2 + dx3 |
280 | |
281 | maxX = math.max(maxX, x, x + dx2, x + dx3, x + dx4) |
282 | minX = math.min(minX, x, x + dx2, x + dx3, x + dx4) |
283 | end |
284 | |
285 | return minX, maxX |
286 | end |
353 | function VehicleSchemaDisplay:getSchemaOverlayForState(schemaOverlayData, isTurnedOn, isSelected, isImplement) |
354 | local overlay = nil |
355 | local schemaName = nil |
356 | |
357 | if isSelected then |
358 | schemaName = schemaOverlayData.schemaNameSelected |
359 | if not schemaName or schemaName == "" then |
360 | schemaName = isImplement and VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_IMPLEMENT_SELECTED or VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_VEHICLE_SELECTED |
361 | end |
362 | end |
363 | |
364 | if isTurnedOn then |
365 | schemaName = schemaOverlayData.schemaNameTurnedOn |
366 | if not schemaName or schemaName == "" then |
367 | schemaName = isImplement and VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_IMPLEMENT_ON or VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_VEHICLE_ON |
368 | end |
369 | end |
370 | |
371 | if isTurnedOn and isSelected then |
372 | schemaName = schemaOverlayData.schemaNameSelectedOn |
373 | if not schemaName or schemaName == "" then |
374 | schemaName = isImplement and VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_IMPLEMENT_SELECTED_ON or VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_VEHICLE_SELECTED_ON |
375 | end |
376 | end |
377 | |
378 | if not schemaName or schemaName == "" then |
379 | schemaName = isImplement and VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_IMPLEMENT or VehicleSchemaOverlayData.SCHEMA_OVERLAY.DEFAULT_VEHICLE |
380 | end |
381 | |
382 | return self.vehicleSchemaOverlays[schemaName] |
383 | end |
245 | function VehicleSchemaDisplay:getVehicleSchemaOverlays(vehicle) |
246 | local turnedOn = vehicle.getIsTurnedOn ~= nil and vehicle:getIsTurnedOn() |
247 | local selected = vehicle:getIsSelected() |
248 | local overlay = self:getSchemaOverlayForState(vehicle.schemaOverlay, turnedOn, selected, false) |
249 | local additionalText = vehicle:getAdditionalSchemaText() |
250 | local overlays = {} |
251 | table.insert(overlays, {overlay=overlay, additionalText=additionalText, x=0, y=0, rotation=0, invertX=false, invisibleBorderRight=vehicle.schemaOverlay.invisibleBorderRight, invisibleBorderLeft=vehicle.schemaOverlay.invisibleBorderLeft}) |
252 | self:collectVehicleSchemaDisplayOverlays(overlays, 1, vehicle, vehicle, overlay, 0, 0, 0, false) |
253 | |
254 | return overlays, overlay.height |
255 | end |
70 | function VehicleSchemaDisplay:loadVehicleSchemaOverlaysFromXML(xmlFile, modPath) |
71 | local rootPath = "vehicleSchemaOverlays" |
72 | local baseDirectory = "" |
73 | local prefix = "" |
74 | if modPath then |
75 | rootPath = "modDesc.vehicleSchemaOverlays" |
76 | local modName, dir = Utils.getModNameAndBaseDirectory(modPath) |
77 | baseDirectory = dir |
78 | prefix = modName |
79 | end |
80 | |
81 | local atlasPath = getXMLString(xmlFile, rootPath .. "#filename") |
82 | local imageSize = GuiUtils.get2DArray(getXMLString(xmlFile, rootPath .. "#imageSize"), {1024, 1024}) |
83 | |
84 | local i = 0 |
85 | while true do |
86 | local baseName = string.format("%s.overlay(%d)", rootPath, i) |
87 | if not hasXMLProperty(xmlFile, baseName) then |
88 | break -- no more overlay definitions |
89 | end |
90 | |
91 | local baseOverlayName = getXMLString(xmlFile, baseName .. "#name") |
92 | local uvString = getXMLString(xmlFile, baseName .. "#uvs") or string.format("0px 0px %ipx %ipx", imageSize[1], imageSize[2]) |
93 | local uvs = GuiUtils.getUVs(uvString, imageSize) |
94 | |
95 | local sizeString = getXMLString(xmlFile, baseName .. "#size") or string.format("%ipx %ipx", |
96 | VehicleSchemaDisplay.SIZE.ICON[1], VehicleSchemaDisplay.SIZE.ICON[1]) |
97 | local size = GuiUtils.getNormalizedValues(sizeString, {1, 1}) -- remove pixel units but do not change numbers |
98 | |
99 | if baseOverlayName then |
100 | local overlayName = prefix .. baseOverlayName |
101 | |
102 | local atlasFileName = Utils.getFilename(atlasPath, baseDirectory) |
103 | local schemaOverlay = Overlay:new(atlasFileName, 0, 0, size[1], size[2]) -- store pixel size to be scaled later |
104 | schemaOverlay:setUVs(uvs) |
105 | |
106 | self.vehicleSchemaOverlays[overlayName] = schemaOverlay |
107 | end |
108 | |
109 | i = i + 1 |
110 | end |
111 | end |
22 | function VehicleSchemaDisplay.new(modManager) |
23 | local backgroundOverlay = VehicleSchemaDisplay.createBackground() |
24 | local self = VehicleSchemaDisplay:superClass().new(VehicleSchemaDisplay_mt, backgroundOverlay, nil) |
25 | |
26 | self.modManager = modManager |
27 | |
28 | self.vehicle = nil -- currently controlled vehicle |
29 | self.isDocked = false -- If true, the schema display is docked to the input help display |
30 | self.vehicleSchemaOverlays = {} -- schema name -> overlay |
31 | |
32 | self.iconSizeX, self.iconSizeY = 0, 0 -- schema overlay icon size |
33 | self.maxSchemaWidth = 0 -- maximum width of vehicle configuration schema |
34 | |
35 | return self |
36 | end |
131 | function VehicleSchemaDisplay:setDocked(isDocked, animate) |
132 | local targetX, targetY = VehicleSchemaDisplay.getBackgroundPosition(isDocked, self:getScale()) |
133 | if animate and self.animation:getFinished() then |
134 | local startX, startY = self:getPosition() |
135 | |
136 | self:animateDocking(startX, startY, targetX, targetY, isDocked) |
137 | else |
138 | self.animation:stop() |
139 | self.isDocked = isDocked |
140 | self:setPosition(targetX, targetY) |
141 | end |
142 | end |
403 | function VehicleSchemaDisplay:storeScaledValues() |
404 | self.iconSizeX, self.iconSizeY = self:scalePixelToScreenVector(VehicleSchemaDisplay.SIZE.ICON) |
405 | self.maxSchemaWidth = self:scalePixelToScreenWidth(VehicleSchemaDisplay.MAX_SCHEMA_WIDTH) |
406 | |
407 | for _, overlay in pairs(self.vehicleSchemaOverlays) do |
408 | overlay:resetDimensions() |
409 | |
410 | local pixelSize = {overlay.defaultWidth, overlay.defaultHeight} |
411 | local width, height = self:scalePixelToScreenVector(pixelSize) |
412 | overlay:setDimension(width, height) |
413 | end |
414 | end |