LUADOC - Farming Simulator 17

Printable Version

AnimatedObject

Description
Class for animated map objects
Functions

onCreate

Description
Creating animated object
Definition
onCreate(integer id)
Arguments
integeridnode id
Code
19function AnimatedObject:onCreate(id)
20 local object = AnimatedObject:new(g_server ~= nil, g_client ~= nil)
21 if object:load(id) then
22 g_currentMission:addOnCreateLoadedObject(object)
23 g_currentMission:addOnCreateLoadedObjectToSave(object)
24 object:register(true)
25 else
26 object:delete()
27 end
28end

new

Description
Creating new instance of animated object class
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustom metatable
Return Values
tableselfnew instance of object
Code
36function AnimatedObject:new(isServer, isClient, customMt)
37 local mt = customMt
38 if mt == nil then
39 mt = AnimatedObject_mt
40 end
41
42 local self = Object:new(isServer, isClient, mt)
43 self.nodeId = 0
44 self.isMoving = false
45 self.sendIsMoving = false
46 self.wasPressed = false
47 self.baseDirectory = nil
48 self.customEnvironment = g_currentMission.loadingMapModName;
49
50 return self
51end

load

Description
Load animated object attributes from object
Definition
load(integer nodeId)
Arguments
integernodeIdid of object to load from
Return Values
booleansuccesssuccess
Code
57function AnimatedObject:load(nodeId)
58 self.nodeId = nodeId
59
60 local xmlFilename = getUserAttribute(nodeId, "xmlFilename")
61
62 if xmlFilename == nil then
63 print("Error: Missing 'xmlFilename' user attribute for AnimatedObject node '"..getName(nodeId).."'!")
64 return false
65 end
66
67 local baseDir = g_currentMission.loadingMapBaseDirectory
68 if baseDir == "" then
69 baseDir = Utils.getNoNil(self.baseDirectory, baseDir)
70 end
71
72 local i18n = g_i18n;
73 if self.customEnvironment ~= nil then
74 i18n = _G[self.customEnvironment].g_i18n;
75 end
76
77 self.xmlFilename = Utils.getFilename(xmlFilename, baseDir)
78 local success = true
79 local xmlFile = loadXMLFile("AnimatedObject", self.xmlFilename)
80 if xmlFile ~= 0 then
81 local index = getUserAttribute(nodeId, "index")
82 if index ~= nil then
83 local key = nil
84 local i = 0
85 while true do
86 local objectKey = string.format("animatedObjects.animatedObject(%d)", i)
87 if not hasXMLProperty(xmlFile, objectKey) then
88 break
89 end
90
91 local configIndex = getXMLString(xmlFile, objectKey.."#index")
92 if configIndex == index then
93 key = objectKey
94 break
95 end
96 i = i + 1
97 end
98
99 if key ~= nil then
100 self.saveId = getXMLString(xmlFile, key.."#saveId");
101 if self.saveId == nil then
102 self.saveId = "AnimatedObject_"..getName(nodeId)
103 end
104
105 local animKey = key .. ".animation"
106
107 self.animation = {}
108 self.animation.parts = {}
109 self.animation.duration = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#duration"), 3) * 1000
110 if self.animation.duration == 0 then
111 self.animation.duration = 1000
112 end
113 self.animation.time = 0
114 self.animation.direction = 0
115
116 local i = 0
117 while true do
118 local partKey = string.format("%s.part(%d)", animKey, i)
119 if not hasXMLProperty(xmlFile, partKey) then
120 break
121 end
122
123 local node = Utils.indexToObject(self.nodeId, getXMLString(xmlFile, partKey.."#node"))
124 if node ~= nil then
125 local part = {}
126 part.node = node
127 part.animCurve = AnimCurve:new(linearInterpolatorN)
128 local hasFrames = false
129 local j = 0
130 while true do
131 local frameKey = string.format("%s.keyFrame(%d)", partKey, j)
132 if not hasXMLProperty(xmlFile, frameKey) then
133 break
134 end
135
136 local keyTime = getXMLFloat(xmlFile, frameKey.."#time")
137 local values = {self:loadFrameValues(xmlFile, frameKey, node)}
138 part.animCurve:addKeyframe({ v=values, time = keyTime})
139 hasFrames = true
140
141 j = j + 1
142 end
143
144 if hasFrames then
145 table.insert(self.animation.parts, part)
146 end
147 end
148 i = i + 1
149 end
150
151 local initialTime = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#initialTime"), 0)*1000
152 self:setAnimTime(initialTime/self.animation.duration)
153
154 local startTime = getXMLFloat(xmlFile, key..".openingHours#startTime")
155 local endTime = getXMLFloat(xmlFile, key..".openingHours#endTime")
156 if startTime ~= nil and endTime ~= nil then
157 local disableIfClosed = Utils.getNoNil(getXMLBool(xmlFile, key..".openingHours#disableIfClosed"), false)
158 local closedText = getXMLString(xmlFile, key..".openingHours#closedText")
159 if closedText ~= nil then
160 if i18n:hasText(closedText) then
161 closedText = i18n:getText(closedText)
162 end
163 end
164 self.openingHours = {startTime=startTime, endTime=endTime, disableIfClosed=disableIfClosed, closedText=closedText}
165 g_currentMission.environment:addHourChangeListener(self)
166 end
167
168 self.isEnabled = true
169
170
171 local triggerId = Utils.indexToObject(self.nodeId, getXMLString(xmlFile, key..".controls#triggerNode"))
172 if triggerId ~= nil then
173 self.controls = {}
174 self.controls.triggerId = triggerId
175
176 addTrigger(self.controls.triggerId, "triggerCallback", self)
177 for i=0, getNumOfChildren(self.controls.triggerId)-1 do
178 addTrigger(getChildAt(self.controls.triggerId, i), "triggerCallback", self)
179 end
180
181 local posKey = getXMLString(xmlFile, key..".controls#posKey")
182 if posKey ~= nil then
183 if InputBinding[posKey] ~= nil then
184 self.controls.posKey = InputBinding[posKey]
185 local posText = getXMLString(xmlFile, key..".controls#posText")
186 if posText ~= nil then
187 if i18n:hasText(posText) then
188 posText = i18n:getText(posText)
189 end
190 self.controls.posText = posText
191 end
192 local negText = getXMLString(xmlFile, key..".controls#negText")
193 if negText ~= nil then
194 if i18n:hasText(negText) then
195 negText = i18n:getText(negText)
196 end
197 self.controls.negText = negText
198 end
199
200 local negKey = getXMLString(xmlFile, key..".controls#negKey")
201 if negKey ~= nil then
202 if InputBinding[negKey] ~= nil then
203 self.controls.negKey = InputBinding[negKey]
204 else
205 print("Warning: Negative direction key '"..negKey.."' not defined!")
206 end
207 end
208 else
209 print("Warning: Positive direction key '"..posKey.."' not defined!")
210 end
211 end
212 end
213
214 if g_client ~= nil then
215 self.sampleMoving = SoundUtil.loadSample(xmlFile, {}, key..".sound", nil, baseDir, self.nodeId)
216 end
217 else
218 success = false
219 print("Error: index '"..index.."' not found in AnimatedObject xml '"..self.xmlFilename.."'!")
220 end
221 else
222 success = false
223 print("Error: Missing 'index' user attribute for AnimatedObject node '"..getName(nodeId).."'!")
224 end
225
226 delete(xmlFile)
227 end
228
229 self.animatedObjectDirtyFlag = self:getNextDirtyFlag();
230
231
232 return success
233end

loadFrameValues

Description
Load frame values from xml
Definition
loadFrameValues(integer fileId, string key, integer node)
Arguments
integerfileIdxml file id
stringkeykey
integernodenode id
Return Values
floatxx translation
floatyy translation
floatzz translation
floatrxx rotation
floatryy rotation
floatrzz rotation
floatsxx scale
floatsyy scale
floatszz scale
integervisibilityvisibility
Code
250function AnimatedObject:loadFrameValues(xmlFile, key, node)
251 local rx,ry,rz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rotation"))
252 local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#translation"))
253 local sx,sy,sz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#scale"))
254 local isVisible = Utils.getNoNil(getXMLBool(xmlFile, key.."#visibility"), true)
255
256 local drx,dry,drz = getRotation(node)
257 rx = Utils.getNoNilRad(rx, drx)
258 ry = Utils.getNoNilRad(ry, dry)
259 rz = Utils.getNoNilRad(rz, drz)
260 local dx,dy,dz = getTranslation(node)
261 x = Utils.getNoNil(x, dx)
262 y = Utils.getNoNil(y, dy)
263 z = Utils.getNoNil(z, dz)
264 local dsx,dsy,dsz = getScale(node)
265 sx = Utils.getNoNil(sx, dsx)
266 sy = Utils.getNoNil(sy, dsy)
267 sz = Utils.getNoNil(sz, dsz)
268
269 local visibility = 1
270 if not isVisible then
271 visibility = 0
272 end
273
274 return x, y, z, rx, ry, rz, sx, sy, sz, visibility
275end

delete

Description
Delete animated object
Definition
delete()
Code
279function AnimatedObject:delete()
280 g_currentMission:removeOnCreateLoadedObjectToSave(self)
281 if self.controls.triggerId ~= nil then
282 removeTrigger(self.controls.triggerId)
283 for i=0, getNumOfChildren(self.controls.triggerId)-1 do
284 removeTrigger(getChildAt(self.controls.triggerId, i))
285 end
286 end
287 if self.sampleMoving ~= nil then
288 SoundUtil.deleteSample(self.sampleMoving)
289 end
290
291 g_currentMission.environment:removeHourChangeListener(self)
292
293 AnimatedObject:superClass().delete(self)
294end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
300function AnimatedObject:readStream(streamId, connection)
301 AnimatedObject:superClass().readStream(self, streamId, connection)
302 if connection:getIsServer() then
303 local animTime = streamReadFloat32(streamId)
304 self:setAnimTime(animTime)
305 end
306end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, table connection)
Arguments
integerstreamIdstream ID
tableconnectionconnection
Code
312function AnimatedObject:writeStream(streamId, connection)
313 AnimatedObject:superClass().writeStream(self, streamId, connection)
314 if not connection:getIsServer() then
315 streamWriteFloat32(streamId, self.animation.time)
316 end
317end

readUpdateStream

Description
Called on client side on update
Definition
readUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
324function AnimatedObject:readUpdateStream(streamId, timestamp, connection)
325 AnimatedObject:superClass().readUpdateStream(self, streamId, timestamp, connection)
326 if connection:getIsServer() then
327 self.isMoving = streamReadBool(streamId)
328 if self.isMoving then
329 local animTime = streamReadFloat32(streamId)
330 self:setAnimTime(animTime)
331 end
332 end
333end

writeUpdateStream

Description
Called on server side on update
Definition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
340function AnimatedObject:writeUpdateStream(streamId, connection, dirtyMask)
341 AnimatedObject:superClass().writeUpdateStream(self, streamId, connection, dirtyMask)
342 if not connection:getIsServer() then
343 streamWriteBool(streamId, self.isMoving)
344
345 if self.isMoving then
346 streamWriteFloat32(streamId, self.animation.time)
347 end
348
349 self.sendIsMoving = self.isMoving;
350 end
351end

loadFromAttributesAndNodes

Description
Loading from attributes and nodes
Definition
loadFromAttributesAndNodes(integer xmlFile, string key)
Arguments
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
358function AnimatedObject:loadFromAttributesAndNodes(xmlFile, key)
359 local animTime = getXMLFloat(xmlFile, key .. "#animTime")
360 if animTime ~= nil then
361 self.animation.direction = Utils.getNoNil(getXMLInt(xmlFile, key.."#direction"), 0)
362 self:setAnimTime(animTime)
363 end
364
365 AnimatedObject.hourChanged(self)
366
367 return true
368end

getSaveAttributesAndNodes

Description
Get save attributes and nodes
Definition
getSaveAttributesAndNodes()
Return Values
stringattributesattributes
Code
373function AnimatedObject:getSaveAttributesAndNodes()
374 local attributes = 'animTime="' .. tostring(self.animation.time) .. '" direction="'..tostring(self.animation.direction)..'"'
375 return attributes
376end

update

Description
Called on update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
381function AnimatedObject:update(dt)
382 AnimatedObject:superClass().update(self, dt)
383
384 if self.playerInRange then
385 if self.isEnabled then
386 if self.controls.posKey ~= nil and g_gui.currentGui == nil and not g_currentMission.isPlayerFrozen then
387 if self.controls.negKey == nil then
388 -- event based action
389 if InputBinding.hasEvent(self.controls.posKey) then
390 self.animation.direction = self.animation.direction * -1
391 if self.animation.direction == 0 then
392 if self.animation.time > 0 then
393 self.animation.direction = -1
394 else
395 self.animation.direction = 1
396 end
397 end
398 if g_server == nil then
399 g_client:getServerConnection():sendEvent(AnimatedObjectEvent:new(self, self.animation.direction))
400 end
401 end
402
403 local text = nil
404 if self.animation.direction == 0 then
405 if self.animation.time > 0 then
406 text = self.controls.negText
407 else
408 text = self.controls.posText
409 end
410 else
411 if self.animation.direction == 1 then
412 text = self.controls.negText
413 else
414 text = self.controls.posText
415 end
416 end
417
418 if text ~= nil then
419 g_currentMission:addHelpButtonText(text, self.controls.posKey, nil, GS_PRIO_VERY_HIGH);
420 end
421 else
422 -- move if key is pressed action
423 local changed = false
424 if InputBinding.isPressed(self.controls.posKey) then
425 self.wasPressed = true
426 if self.animation.direction ~= 1 and self.animation.time ~= 1 then
427 self.animation.direction = 1
428 changed = true
429 end
430 elseif InputBinding.isPressed(self.controls.negKey) then
431 self.wasPressed = true
432 if self.animation.direction ~= -1 and self.animation.time ~= 0 then
433 self.animation.direction = -1
434 changed = true
435 end
436 else
437 if self.animation.direction ~= 0 and self.wasPressed then
438 self.animation.direction = 0
439 changed = true
440 end
441 end;
442
443 if g_server == nil and changed then
444 g_client:getServerConnection():sendEvent(AnimatedObjectEvent:new(self, self.animation.direction))
445 end
446
447 if self.controls.posText ~= nil then
448 g_currentMission:addHelpButtonText(self.controls.posText, self.controls.posKey, nil, GS_PRIO_VERY_HIGH);
449 end;
450 if self.controls.negText ~= nil then
451 g_currentMission:addHelpButtonText(self.controls.negText, self.controls.negKey, nil, GS_PRIO_VERY_HIGH);
452 end;
453 end
454 end
455 else
456 if self.openingHours ~= nil and self.openingHours.closedText ~= nil then
457 g_currentMission:addExtraPrintText(self.openingHours.closedText)
458 end
459 end
460 end
461end

updateTick

Description
Called on update tick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
466function AnimatedObject:updateTick(dt)
467 AnimatedObject:superClass().updateTick(self, dt)
468
469 if self.isServer then
470 if self.animation.direction ~= 0 then
471 local newAnimTime = Utils.clamp(self.animation.time + (self.animation.direction*dt)/self.animation.duration, 0, 1)
472 self:setAnimTime(newAnimTime)
473 if newAnimTime == 0 or newAnimTime == 1 then
474 self.animation.direction = 0
475 end
476
477 self:raiseDirtyFlags(self.animatedObjectDirtyFlag);
478 end
479
480 self.isMoving = self.animation.direction ~= 0;
481
482 if self.sendIsMoving ~= self.isMoving then
483 self:raiseDirtyFlags(self.animatedObjectDirtyFlag);
484 end;
485 end
486
487 if self.isClient and self.sampleMoving ~= nil then
488 if self.isMoving then
489 if not self.sampleMoving.isPlaying then
490 SoundUtil.play3DSample(self.sampleMoving)
491 self.sampleMoving.isPlaying = true
492 end
493 else
494 if self.sampleMoving.isPlaying then
495 SoundUtil.stop3DSample(self.sampleMoving)
496 self.sampleMoving.isPlaying = false
497 end
498 end
499 end
500end

setAnimTime

Description
Set animation time
Definition
setAnimTime(float t)
Arguments
floatttime
Code
505function AnimatedObject:setAnimTime(t)
506 t = Utils.clamp(t, 0, 1)
507 for _, part in pairs(self.animation.parts) do
508 local v = part.animCurve:get(t)
509 self:setFrameValues(part.node, v)
510 end
511 self.animation.time = t
512end

setFrameValues

Description
Set frame values
Definition
setFrameValues(integer node, table v)
Arguments
integernodenode id
tablevvalues
Code
518function AnimatedObject:setFrameValues(node, v)
519 setTranslation(node, v[1], v[2], v[3])
520 setRotation(node, v[4], v[5], v[6])
521 setScale(node, v[7], v[8], v[9])
522 setVisibility(node, v[10] == 1)
523end

hourChanged

Description
Called on hour change
Definition
hourChanged()
Code
527function AnimatedObject:hourChanged()
528 if self.isServer then
529 local currentHour = g_currentMission.environment.currentHour
530 if self.openingHours ~= nil then
531 if currentHour >= self.openingHours.startTime and currentHour < self.openingHours.endTime then
532 if not self.openingHours.isOpen then
533 if self.isServer then
534 self.animation.direction = 1
535 end
536 self.openingHours.isOpen = true
537 end
538 if self.openingHours.disableIfClosed then
539 self.isEnabled = true
540 end
541 else
542 if self.openingHours.isOpen then
543 if self.isServer then
544 self.animation.direction = -1
545 end
546 self.openingHours.isOpen = false
547 end
548 if self.openingHours.disableIfClosed then
549 self.isEnabled = false
550 end
551 end
552 end;
553 end
554end

triggerCallback

Description
Trigger callback
Definition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)
Arguments
integertriggerIdid of trigger
integerotherIdid of object that calls callback
booleanonEntercalled on enter
booleanonLeavecalled on leave
booleanonStaycalled on stay
Code
563function AnimatedObject:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
564 if g_currentMission.missionInfo:isa(FSCareerMissionInfo) then
565 if onEnter or onLeave then
566 if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
567 if onEnter then
568 self.playerInRange = g_currentMission.player
569 else
570 self.playerInRange = nil
571 end
572 end
573 end
574 end
575end