LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

IngameMapElement

Description
In-game map element. Controls input on the map in the in-game menu with objectives, vehicles, etc. The actual map rendering is deferred to the map component of the current mission. The map reference and terrain size must be set during mission initialization via the setIngameMap() and setTerrainSize() methods.
Parent
GuiElement
XML Configuration Parameters
GuiElement#cursorIdstring ID of element to use as a cursor on the map.
GuiElement#mapAlphafloat Transparency value of the map display, defaults to 1. Range is [0=invisible, 1=opaque].
GuiElement#onDrawPreIngameMapcallback [optional] onDrawPreIngameMap(element) Called before the map is drawn. Receives this element.
GuiElement#onDrawPostIngameMapcallback [optional] onDrawPostIngameMap(element) Called after the map is drawn. Receives this element.
GuiElement#onDrawPostIngameMapHotspotscallback [optional] onDrawPostIngameMapHotspots(element) Called after map markers are drawn. Receives this element.
GuiElement#onClickHotspotcallback [optional] onClickHotspot(element, hotspot) Called when a marker is clicked / activated. Receives this element and the clicked hotspot (as defined in missions).
GuiElement#onClickMapcallback [optional] onClickMap(element, worldX, worldY) Called when the map itself is clicked / activated. Receives this element and the world space X and Z coordinates of the clicked point.

Functions

addCursorDeadzone

Description
Add a dead zone wherein the map will not react to cursor inputs. Used this to designate areas where other controls should receive cursor input which would otherwise be used up by the map (e.g. in full-screen mode in the map overview screen in-game). The deadzones will also restrict cursor movement.
Definition
addCursorDeadzone()
Code
152function IngameMapElement:addCursorDeadzone(screenX, screenY, width, height)
153 table.insert(self.cursorDeadzones, {screenX, screenY, width, height})
154end

checkAndResetMouse

Description
Check if mouse input was active before a bound input was triggered and queue a reset of the mouse state for the next frame. Mouse input continuously sets the mouse input flag (self.useMouse) but does not receive any events when the mouse is inert. Therefore we need to set and reset the state each frame to make sure we can seamlessly switch between mouse and gamepad input on the map element while at the same time preventing any player bindings from interfering with the custom mouse input logic of this class.
Definition
checkAndResetMouse()
Code
667function IngameMapElement:checkAndResetMouse()
668 local useMouse = self.useMouse
669 if useMouse then
670 self.resetMouseNextFrame = true
671 end
672
673 return useMouse
674end

clearCursorDeadzones

Description
Clear cursor dead zones.
Definition
clearCursorDeadzones()
Code
158function IngameMapElement:clearCursorDeadzones()
159 self.cursorDeadzones = {}
160end

copyAttributes

Description
Definition
copyAttributes()
Code
116function IngameMapElement:copyAttributes(src)
117 IngameMapElement:superClass().copyAttributes(self, src)
118
119 self.mapZoom = src.mapZoom
120 self.mapAlpha = src.mapAlpha
121 self.cursorId = src.cursorId
122 self.onDrawPreIngameMapCallback = src.onDrawPreIngameMapCallback
123 self.onDrawPostIngameMapCallback = src.onDrawPostIngameMapCallback
124 self.onDrawPostIngameMapHotspotsCallback = src.onDrawPostIngameMapHotspotsCallback
125 self.onClickHotspotCallback = src.onClickHotspotCallback
126 self.onClickMapCallback = src.onClickMapCallback
127end

delete

Description
Definition
delete()
Code
84function IngameMapElement:delete()
85 GuiOverlay.deleteOverlay(self.overlay)
86 self.ingameMap = nil
87
88 IngameMapElement:superClass().delete(self)
89end

draw

Description
Definition
draw()
Code
391function IngameMapElement:draw(clipX1, clipY1, clipX2, clipY2)
392 self:raiseCallback("onDrawPreIngameMapCallback", self, self.ingameMap)
393 self.ingameMap:drawMapOnly()
394 self:raiseCallback("onDrawPostIngameMapCallback", self, self.ingameMap)
395
396 self.ingameMap:drawHotspotsOnly()
397
398 self:raiseCallback("onDrawPostIngameMapHotspotsCallback", self, self.ingameMap)
399end

getLocalPointerTarget

Description
Definition
getLocalPointerTarget()
Code
506function IngameMapElement:getLocalPointerTarget()
507 if self.useMouse then
508 return self:getLocalPosition(self.lastMousePosX, self.lastMousePosY)
509 elseif self.cursorElement then
510 local posX = self.cursorElement.absPosition[1] + self.cursorElement.size[1] * 0.5
511 local posY = self.cursorElement.absPosition[2] + self.cursorElement.size[2] * 0.5
512
513 return self:getLocalPosition(posX, posY)
514 end
515
516 return 0, 0
517end

getLocalPosition

Description
Definition
getLocalPosition()
Code
492function IngameMapElement:getLocalPosition(posX, posY)
493 local width, height = self.ingameMap.fullScreenLayout:getMapSize()
494 local offX, offY = self.ingameMap.fullScreenLayout:getMapPosition()
495
496 -- offset with map poisition, then conver to 0-1 and adjust for minimap being doubled in size
497 -- from actual map.
498 local x = ((posX - offX) / width - 0.25) * 2
499 local y = ((posY - offY) / height - 0.25) * 2
500
501 return x, y
502end

isCursorInDeadzones

Description
Check if a cursor position is within one of the stored deadzones.
Definition
isCursorInDeadzones()
Code
164function IngameMapElement:isCursorInDeadzones(cursorScreenX, cursorScreenY)
165 for _, zone in pairs(self.cursorDeadzones) do
166 if GuiUtils.checkOverlayOverlap(cursorScreenX, cursorScreenY, zone[1], zone[2], zone[3], zone[4]) then
167 return true
168 end
169 end
170
171 return false
172end

isPointVisible

Description
Definition
isPointVisible()
Code
576function IngameMapElement:isPointVisible(x, z)
577end

loadFromXML

Description
Definition
loadFromXML()
Code
93function IngameMapElement:loadFromXML(xmlFile, key)
94 IngameMapElement:superClass().loadFromXML(self, xmlFile, key)
95
96 self.cursorId = getXMLString(xmlFile, key.."#cursorId")
97 self.mapAlpha = getXMLFloat(xmlFile, key .. "#mapAlpha") or self.mapAlpha
98
99 self:addCallback(xmlFile, key.."#onDrawPreIngameMap", "onDrawPreIngameMapCallback")
100 self:addCallback(xmlFile, key.."#onDrawPostIngameMap", "onDrawPostIngameMapCallback")
101 self:addCallback(xmlFile, key.."#onDrawPostIngameMapHotspots", "onDrawPostIngameMapHotspotsCallback")
102 self:addCallback(xmlFile, key.."#onClickHotspot", "onClickHotspotCallback")
103 self:addCallback(xmlFile, key.."#onClickMap", "onClickMapCallback")
104end

loadProfile

Description
Definition
loadProfile()
Code
108function IngameMapElement:loadProfile(profile, applyProfile)
109 IngameMapElement:superClass().loadProfile(self, profile, applyProfile)
110
111 self.mapAlpha = profile:getNumber("mapAlpha", self.mapAlpha)
112end

localToWorldPos

Description
Definition
localToWorldPos()
Code
529function IngameMapElement:localToWorldPos(localPosX, localPosY)
530 local worldPosX = localPosX * self.terrainSize
531 local worldPosZ = -localPosY * self.terrainSize
532
533 -- move world positions to range -1024 to 1024 on a 2k map
534 worldPosX = worldPosX - self.terrainSize * 0.5
535 worldPosZ = worldPosZ + self.terrainSize * 0.5
536
537 return worldPosX, worldPosZ
538end

mouseEvent

Description
Custom mouse event handling for the in-game map. Directly handles zoom, click and drag events on the map. See input events and IngameMapElement:checkAndResetMouse() for the state checking code required to bypass player mouse input bindings.
Definition
mouseEvent()
Code
178function IngameMapElement:mouseEvent(posX, posY, isDown, isUp, button, eventUsed)
179 if self:getIsActive() then
180 eventUsed = IngameMapElement:superClass().mouseEvent(self, posX, posY, isDown, isUp, button, eventUsed)
181
182 if not GS_IS_CONSOLE_VERSION and (isDown or isUp or posX ~= self.lastMousePosX or posY ~= self.lastMousePosY) then
183 self.useMouse = true
184
185 if self.cursorElement then
186 self.cursorElement:setVisible(false)
187 end
188 self.isCursorActive = false
189 end
190
191 -- On mobile we have touch input. Touch does not give us a position until there is a touch.
192 -- This means on the first touch-begin, the lastMousePos is wrong and has a big offset.
193 -- We set it when the touch begins so it becomes a drag action
194 if GS_IS_MOBILE_VERSION and self.useMouse then
195 if isDown then
196 self.lastMousePosY = posY
197 end
198 end
199
200 if not eventUsed then
201 if isDown and button == Input.MOUSE_BUTTON_LEFT and not self:isCursorInDeadzones(posX, posY) then
202 eventUsed = true
203 if not self.mouseDown then
204 self.mouseDown = true
205 end
206 end
207 end
208
209 if self.mouseDown and self.lastMousePosX ~= nil then
210 local distX = self.lastMousePosX - posX
211 local distY = posY - self.lastMousePosY
212
213 if self.isFixedHorizontal then
214 distX = 0
215 end
216
217 if math.abs(distX) > self.minDragDistanceX or math.abs(distY) > self.minDragDistanceY then
218 local factorX = -distX
219 local factorY = distY
220
221 self:moveCenter(factorX, factorY)
222
223 self.hasDragged = true
224 end
225 end
226
227 if isUp and button == Input.MOUSE_BUTTON_LEFT then
228 if not eventUsed and self.mouseDown and not self.hasDragged then
229 local localX, localY = self:getLocalPosition(posX, posY)
230
231 -- save state locally to avoid issues if activating/deactivating selection in the onClickMap callback
232 local isHotspotSelectionActive = self.isHotspotSelectionActive
233
234 self:onClickMap(localX, localY)
235
236 if isHotspotSelectionActive then
237 -- Trigger hot spot selection after map clicking because it's the more specific event
238 self:selectHotspotAt(posX, posY)
239 end
240
241 eventUsed = true
242 end
243
244 self.mouseDown = false
245 self.hasDragged = false
246 end
247
248 self.lastMousePosX = posX
249 self.lastMousePosY = posY
250 end
251
252 return eventUsed
253end

moveCenter

Description
Move center of the map
Definition
moveCenter()
Code
257function IngameMapElement:moveCenter(x, y)
258 local width, height = self.ingameMap.fullScreenLayout:getMapSize()
259
260 local centerX = self.cursorElement.absPosition[1] + self.cursorElement.absSize[1] * 0.5
261 local centerY = self.cursorElement.absPosition[2] + self.cursorElement.absSize[2] * 0.5
262
263 self.mapCenterX = MathUtil.clamp(self.mapCenterX + x, width * -0.25 + centerX, width * 0.25 + centerX)
264 self.mapCenterY = MathUtil.clamp(self.mapCenterY + y, height * -0.25 + centerY, height * 0.25 + centerY)
265
266 self.ingameMap.fullScreenLayout:setMapCenter(self.mapCenterX, self.mapCenterY)
267end

new

Description
Definition
new()
Code
35function IngameMapElement.new(target, custom_mt)
36 local self = GuiElement.new(target, custom_mt or IngameMapElement_mt)
37
38 self.ingameMap = nil
39
40 -- cursor
41 self.cursorId = nil
42
43 self.inputMode = GS_INPUT_HELP_MODE_GAMEPAD
44
45 -- map attributes
46 self.terrainSize = 0
47 self.mapAlpha = 1
48 self.zoomMin = 1
49 self.zoomMax = 5
50 self.zoomDefault = 2
51
52 self.mapCenterX = 0.5
53 self.mapCenterY = 0.5
54
55 self.mapZoom = self.zoomDefault
56
57 -- horizontal cursor input since last frame
58 self.accumHorizontalInput = 0
59 -- vertical cursor input since last frame
60 self.accumVerticalInput = 0
61 -- zoom input since last frame
62 self.accumZoomInput = 0
63 -- mouse input flag to override potential double binding on cursor movement
64 self.useMouse = false
65 -- reset flag for mouse input flag to avoid catching input in the current frame
66 self.resetMouseNextFrame = false
67 -- screen space rectangle definitions {x, y, w, h} where the cursor/mouse should not go and react to input
68 self.cursorDeadzones = {}
69
70 self.minDragDistanceX = IngameMapElement.DRAG_START_DISTANCE / g_screenWidth
71 self.minDragDistanceY = IngameMapElement.DRAG_START_DISTANCE / g_screenHeight
72 self.hasDragged = false -- drag state flag to avoid triggering a click event on a dragging mouse up
73
74 self.minimalHotspotSize = getNormalizedScreenValues(9, 1)
75
76 self.isHotspotSelectionActive = true
77 self.isCursorAvailable = true
78
79 return self
80end

onAccept

Description
Event function for gamepad cursor accept input bound to InputAction.INGAMEMAP_ACCEPT.
Definition
onAccept()
Code
632function IngameMapElement:onAccept()
633 if self.cursorElement then
634 local cursorElement = self.cursorElement
635 local posX, posY = cursorElement.absPosition[1] + cursorElement.size[1]*0.5, cursorElement.absPosition[2] + cursorElement.size[2]*0.5
636 local localX, localY = self:getLocalPointerTarget()
637
638 -- save state locally to avoid issues if activating/deactivating selection in the onClickMap callback
639 local isHotspotSelectionActive = self.isHotspotSelectionActive
640
641 self:onClickMap(localX, localY)
642
643 if isHotspotSelectionActive then
644 -- trigger hot spot selection after map clicking because it's the more specific event
645 self:selectHotspotAt(posX, posY)
646 end
647 end
648end

onClickMap

Description
Definition
onClickMap()
Code
521function IngameMapElement:onClickMap(localPosX, localPosY)
522 local worldPosX, worldPosZ = self:localToWorldPos(localPosX, localPosY)
523
524 self:raiseCallback("onClickMapCallback", self, worldPosX, worldPosZ)
525end

onClose

Description
Definition
onClose()
Code
422function IngameMapElement:onClose()
423 IngameMapElement:superClass().onClose(self)
424
425 self:removeActionEvents()
426
427 self.ingameMap:setFullscreen(false)
428end

onGuiSetupFinished

Description
Definition
onGuiSetupFinished()
Code
131function IngameMapElement:onGuiSetupFinished()
132 IngameMapElement:superClass().onGuiSetupFinished(self)
133
134 if self.cursorId ~= nil then
135 if self.target[self.cursorId] ~= nil then
136 self.cursorElement = self.target[self.cursorId]
137 else
138 print("Warning: CursorId '"..self.cursorId.."' not found for '"..self.target.name.."'!")
139 end
140 end
141end

onHorizontalCursorInput

Description
Event function for horizontal cursor input bound to InputAction.AXIS_LOOK_LEFTRIGHT_VEHICLE.
Definition
onHorizontalCursorInput()
Code
616function IngameMapElement:onHorizontalCursorInput(_, inputValue)
617 if not self:checkAndResetMouse() and not self.isFixedHorizontal then
618 self.accumHorizontalInput = self.accumHorizontalInput + inputValue
619 end
620end

onOpen

Description
Definition
onOpen()
Code
403function IngameMapElement:onOpen()
404 IngameMapElement:superClass().onOpen(self)
405
406 if self.cursorElement ~= nil then
407 self.cursorElement:setVisible(false)
408 end
409 self.isCursorActive = false
410
411 if self.largestSize == nil then
412 self.largestSize = self.size
413 end
414
415 self.ingameMap:setFullscreen(true)
416
417 self:zoom(0)
418end

onVerticalCursorInput

Description
Event function for vertical cursor input bound to InputAction.AXIS_LOOK_UPDOWN_VEHICLE.
Definition
onVerticalCursorInput()
Code
624function IngameMapElement:onVerticalCursorInput(_, inputValue)
625 if not self:checkAndResetMouse() then
626 self.accumVerticalInput = self.accumVerticalInput + inputValue
627 end
628end

onZoomInput

Description
Event function for map zoom input bound to InputAction.AXIS_ACCELERATE_VEHICLE and InputAction.AXIS_BRAKE_VEHICLE.
Definition
onZoomInput(inputValue Zoom, direction Zoom)
Arguments
inputValueZoominput value
directionZoominput sign value, 1 for zoom in, -1 for zoom out
Code
654function IngameMapElement:onZoomInput(_, inputValue, direction)
655 if not self:hasMouseOverlapWithTabHeader() or not self.useMouse then
656 self.accumZoomInput = self.accumZoomInput - direction*inputValue
657 end
658end

registerActionEvents

Description
Register non-GUI input action events.
Definition
registerActionEvents()
Code
600function IngameMapElement:registerActionEvents()
601 g_inputBinding:registerActionEvent(InputAction.AXIS_MAP_SCROLL_LEFT_RIGHT, self, self.onHorizontalCursorInput, false, false, true, true)
602 g_inputBinding:registerActionEvent(InputAction.AXIS_MAP_SCROLL_UP_DOWN, self, self.onVerticalCursorInput, false, false, true, true)
603 g_inputBinding:registerActionEvent(InputAction.INGAMEMAP_ACCEPT, self, self.onAccept, false, true, false, true)
604 g_inputBinding:registerActionEvent(InputAction.AXIS_MAP_ZOOM_OUT, self, self.onZoomInput, false, false, true, true, -1) -- -1 == zoom out
605 g_inputBinding:registerActionEvent(InputAction.AXIS_MAP_ZOOM_IN, self, self.onZoomInput, false, false, true, true, 1) -- 1 == zoom in
606end

removeActionEvents

Description
Remove non-GUI input action events.
Definition
removeActionEvents()
Code
610function IngameMapElement:removeActionEvents()
611 g_inputBinding:removeActionEventsByTarget(self)
612end

reset

Description
Definition
reset()
Code
432function IngameMapElement:reset()
433 IngameMapElement:superClass().reset(self)
434
435 self.mapCenterX = 0.5
436 self.mapCenterY = 0.5
437 self.mapZoom = self.zoomDefault
438
439 -- self.ingameMap:resetSettings()
440end

resetFrameInputState

Description
Definition
resetFrameInputState()
Code
379function IngameMapElement:resetFrameInputState()
380 self.accumZoomInput = 0
381 self.accumHorizontalInput = 0
382 self.accumVerticalInput = 0
383 if self.resetMouseNextFrame then
384 self.useMouse = false
385 self.resetMouseNextFrame = false
386 end
387end

selectHotspotAt

Description
Definition
selectHotspotAt()
Code
458function IngameMapElement:selectHotspotAt(posX, posY)
459 if self.isHotspotSelectionActive then
460 if self.ingameMap.hotspotsSorted ~= nil then
461 if not self:selectHotspotFrom(self.ingameMap.hotspotsSorted[true], posX, posY) then
462 self:selectHotspotFrom(self.ingameMap.hotspotsSorted[false], posX, posY)
463 end
464
465 return
466 end
467
468 self:selectHotspotFrom(self.ingameMap.hotspots, posX, posY)
469 end
470end

selectHotspotFrom

Description
Definition
selectHotspotFrom()
Code
474function IngameMapElement:selectHotspotFrom(hotspots, posX, posY)
475 for i=#hotspots, 1, -1 do
476 local hotspot = hotspots[i]
477
478 if self.ingameMap.filter[hotspot:getCategory()] and hotspot:getIsVisible() and hotspot:getCanBeAccessed() then
479 if hotspot:hasMouseOverlap(posX, posY) then
480 self:raiseCallback("onClickHotspotCallback", self, hotspot)
481
482 return true
483 end
484 end
485 end
486
487 return false
488end

setIngameMap

Description
Set the IngameMap reference to use for display.
Definition
setIngameMap()
Code
581function IngameMapElement:setIngameMap(ingameMap)
582 self.ingameMap = ingameMap
583end

setMapFocusToHotspot

Description
Definition
setMapFocusToHotspot()
Code
553function IngameMapElement:setMapFocusToHotspot(hotspot)
554 -- if hotspot ~= nil then
555 -- local objectX = (hotspot.worldX + self.ingameMap.worldCenterOffsetX) / self.ingameMap.worldSizeX
556 -- local objectZ = (hotspot.worldZ + self.ingameMap.worldCenterOffsetZ) / self.ingameMap.worldSizeZ
557
558 -- -- This function is only used to cycle for visible hotspots. So we only need to check the 'visible' does not overlap deadzones
559 -- if self:isCursorInDeadzones(objectX, objectZ) then
560 -- self.ingameMapCenterX = MathUtil.clamp(objectX, 0 + self.ingameMap.mapVisWidth * 0.5, 1 - self.ingameMap.mapVisWidth * 0.5)
561 -- self.ingameMapCenterY = MathUtil.clamp(objectZ, 0 + self.ingameMap.mapVisHeight * 0.5, 1 - self.ingameMap.mapVisHeight * 0.5)
562 -- end
563
564 -- if self.isFixedHorizontal then
565 -- if objectZ < 0.5 then
566 -- self:moveCenter(0, -1)
567 -- else
568 -- self:moveCenter(0, 1)
569 -- end
570 -- end
571 -- end
572end

setTerrainSize

Description
Set the current map's terrain size for map display.
Definition
setTerrainSize()
Code
587function IngameMapElement:setTerrainSize(terrainSize)
588 self.terrainSize = terrainSize
589end

update

Description
Definition
update()
Code
338function IngameMapElement:update(dt)
339 IngameMapElement:superClass().update(self, dt)
340
341 self.inputMode = g_inputBinding:getLastInputMode()
342
343 if not g_gui:getIsDialogVisible() then
344 if not self.alreadyClosed then
345 local zoomFactor = MathUtil.clamp(self.accumZoomInput, -1, 1)
346
347 if zoomFactor ~= 0 then
348 self:zoom(zoomFactor * -0.015 * dt)
349 end
350
351 if self.cursorElement ~= nil then
352 self.isCursorActive = self.inputMode == GS_INPUT_HELP_MODE_GAMEPAD and not GS_IS_MOBILE_VERSION
353 self.cursorElement:setVisible(self.isCursorAvailable and self.isCursorActive)
354 self:updateCursor(self.accumHorizontalInput, -self.accumVerticalInput, dt)
355 self.useMouse = false
356 end
357
358 self:updateMap()
359 end
360 end
361
362 self:resetFrameInputState()
363end

updateCursor

Description
Definition
updateCursor()
Code
445function IngameMapElement:updateCursor(deltaX, deltaY, dt)
446 if self.cursorElement ~= nil then
447 local speed = IngameMapElement.CURSOR_SPEED_FACTOR
448
449 local diffX = deltaX * speed * dt / g_screenAspectRatio
450 local diffY = deltaY * speed * dt
451
452 self:moveCenter(-diffX, -diffY)
453 end
454end

updateMap

Description
Update our element to match the zoom level and map center
Definition
updateMap()
Code
367function IngameMapElement:updateMap()
368 -- Update the ingame map. The ingame map draws the map background and hotspots
369 -- self.ingameMap:setPosition(self.absPosition[1], self.absPosition[2])
370 -- self.ingameMap:setSize(self.size[1], self.size[2])
371 -- self.ingameMap.iconZoom = 0.3 + (self.zoomMax - self.zoomMin) * self.mapZoom
372 -- self.ingameMap:setZoomScale(self.ingameMap.iconZoom)
373
374 -- self.ingameMap:updatePlayerPosition()
375end

zoom

Description
Definition
zoom()
Code
281function IngameMapElement:zoom(direction)
282 -- No zooming for mobile
283 if GS_IS_MOBILE_VERSION then
284 return
285 end
286
287 -- Find the location pointed at by the cursor so we can zoom towards it
288 local targetX, targetZ = self:localToWorldPos(self:getLocalPointerTarget())
289
290 local width, height = self.ingameMap.fullScreenLayout:getMapSize()
291
292 -- Zoom by a set factor
293 local oldZoom = self.mapZoom
294 local speed = IngameMapElement.ZOOM_SPEED_FACTOR * direction * width -- multiply by size to mimic a constant scroll
295 self.mapZoom = MathUtil.clamp(self.mapZoom + speed, self.zoomMin, self.zoomMax)
296
297 self.ingameMap.fullScreenLayout:setMapZoom(self.mapZoom)
298
299 -- Size depends on zoom, center bounds depend on size. So clamp the center
300 self:moveCenter(0, 0)
301
302 -- -- Do not change focus position if we did not change zoom
303 if oldZoom ~= self.mapZoom then
304 -- Find the location the mouseis pointing at now
305 local newTargetX, newTargetZ = self:localToWorldPos(self:getLocalPointerTarget())
306
307 -- Above location is wrong. We want it to point at the same location as before, so find the different for moving
308 local diffX, diffZ = newTargetX - targetX, newTargetZ - targetZ
309
310 -- The diff is in world coordinates. Transform it to screenspace.
311 local dx, dy = diffX / self.terrainSize * 0.5 * width, -diffZ / self.terrainSize * 0.5 * height
312
313 self:moveCenter(dx, dy)
314 end
315end