Script v1.7.1.0
- AI
- Animals
- Contracts
- Debug
- Economy
- Effects
- Events
- Farms
- GUI
- Handtools
- I3d
- Materials
- Misc
- Objects
- AnimatedMapObject
- AnimatedObject
- AnimatedObjectEvent
- Bale
- Basketball
- Bga
- BgaSellStation
- BunkerSilo
- BuyingStation
- DigitalDisplay
- DogBall
- HelpIcons
- LoadingStation
- NightIllumination
- Nightlight2
- NightlightFlicker
- PhysicsObject
- Rotator
- SellingStation
- SimParticleSystem
- Storage
- StorageSystem
- SunAdmirer
- TourIcons
- UnloadingStation
- VehicleSellingPoint
- WildlifeSpawner
- Placeables
- Player
- Shop
- Sounds
- Specializations
- Triggers
- Utils
- Vehicles
- Weather
Engine v1.7.1.0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- XML
- general
Foundation Reference
AnimatedObject
DescriptionClass for animated objectsParent
ObjectFunctions
- delete
- hourChanged
- load
- loadFrameValues
- loadFromXMLFile
- new
- readStream
- readUpdateStream
- setAnimTime
- setFrameValues
- triggerCallback
- update
- writeStream
- writeUpdateStream
delete
DescriptionDelete animated objectDefinition
delete()Code
230 | function AnimatedObject:delete() |
231 | self:removeActionEvents() |
232 | |
233 | if self.controls.triggerId ~= nil then |
234 | removeTrigger(self.controls.triggerId) |
235 | for i=0, getNumOfChildren(self.controls.triggerId)-1 do |
236 | removeTrigger(getChildAt(self.controls.triggerId, i)) |
237 | end |
238 | self.controls.triggerId = nil |
239 | end |
240 | |
241 | if self.sampleMoving ~= nil then |
242 | g_soundManager:deleteSample(self.sampleMoving) |
243 | self.sampleMoving = nil |
244 | end |
245 | if self.samplePosEnd ~= nil then |
246 | g_soundManager:deleteSample(self.samplePosEnd) |
247 | self.samplePosEnd = nil |
248 | end |
249 | if self.sampleNegEnd ~= nil then |
250 | g_soundManager:deleteSample(self.sampleNegEnd) |
251 | self.sampleNegEnd = nil |
252 | end |
253 | |
254 | g_currentMission.environment:removeHourChangeListener(self) |
255 | |
256 | AnimatedObject:superClass().delete(self) |
257 | end |
hourChanged
DescriptionCalled on hour changeDefinition
hourChanged()Code
586 | function AnimatedObject:hourChanged() |
587 | if self.isServer then |
588 | local currentHour = g_currentMission.environment.currentHour |
589 | if self.openingHours ~= nil then |
590 | if currentHour >= self.openingHours.startTime and currentHour < self.openingHours.endTime then |
591 | if not self.openingHours.isOpen then |
592 | if self.isServer then |
593 | self.animation.direction = 1 |
594 | self:raiseActive() |
595 | end |
596 | self.openingHours.isOpen = true |
597 | end |
598 | if self.openingHours.disableIfClosed then |
599 | self.isEnabled = true |
600 | end |
601 | else |
602 | if self.openingHours.isOpen then |
603 | if self.isServer then |
604 | self.animation.direction = -1 |
605 | self:raiseActive() |
606 | end |
607 | self.openingHours.isOpen = false |
608 | end |
609 | if self.openingHours.disableIfClosed then |
610 | self.isEnabled = false |
611 | end |
612 | end |
613 | end |
614 | end |
615 | end |
load
DescriptionLoad animated object from object with given configuration fileDefinition
load(integer nodeId, xmlFilename string, index integer)Arguments
integer | nodeId | id of object |
xmlFilename | string | Path of the xml configuration |
index | integer | Configuration index within the xml file |
boolean | success | success |
48 | function AnimatedObject:load(nodeId, xmlFile, key, xmlFilename) |
49 | local modName, baseDirectory = Utils.getModNameAndBaseDirectory(xmlFilename) |
50 | self.baseDirectory = baseDirectory |
51 | self.customEnvironment = modName |
52 | |
53 | self.nodeId = nodeId |
54 | |
55 | self.samples = {} |
56 | |
57 | local success = true |
58 | self.saveId = getXMLString(xmlFile, key.."#saveId") |
59 | if self.saveId == nil then |
60 | self.saveId = "AnimatedObject_"..getName(nodeId) |
61 | end |
62 | |
63 | local animKey = key .. ".animation" |
64 | |
65 | self.animation = {} |
66 | self.animation.parts = {} |
67 | self.animation.duration = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#duration"), 3) * 1000 |
68 | if self.animation.duration == 0 then |
69 | self.animation.duration = 1000 |
70 | end |
71 | self.animation.time = 0 |
72 | self.animation.direction = 0 |
73 | |
74 | local i = 0 |
75 | while true do |
76 | local partKey = string.format("%s.part(%d)", animKey, i) |
77 | if not hasXMLProperty(xmlFile, partKey) then |
78 | break |
79 | end |
80 | |
81 | local node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, partKey.."#node")) |
82 | if node ~= nil then |
83 | local part = {} |
84 | part.node = node |
85 | part.animCurve = AnimCurve:new(linearInterpolatorN) |
86 | local hasFrames = false |
87 | local j = 0 |
88 | while true do |
89 | local frameKey = string.format("%s.keyFrame(%d)", partKey, j) |
90 | if not hasXMLProperty(xmlFile, frameKey) then |
91 | break |
92 | end |
93 | |
94 | local keyTime = getXMLFloat(xmlFile, frameKey.."#time") |
95 | local keyframe = {self:loadFrameValues(xmlFile, frameKey, node)} |
96 | keyframe.time = keyTime |
97 | part.animCurve:addKeyframe(keyframe) |
98 | hasFrames = true |
99 | |
100 | j = j + 1 |
101 | end |
102 | |
103 | if hasFrames then |
104 | table.insert(self.animation.parts, part) |
105 | end |
106 | end |
107 | i = i + 1 |
108 | end |
109 | |
110 | local initialTime = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#initialTime"), 0)*1000 |
111 | self:setAnimTime(initialTime / self.animation.duration, true) |
112 | |
113 | local startTime = getXMLFloat(xmlFile, key..".openingHours#startTime") |
114 | local endTime = getXMLFloat(xmlFile, key..".openingHours#endTime") |
115 | if startTime ~= nil and endTime ~= nil then |
116 | local disableIfClosed = Utils.getNoNil(getXMLBool(xmlFile, key..".openingHours#disableIfClosed"), false) |
117 | local closedText = getXMLString(xmlFile, key..".openingHours#closedText") |
118 | if closedText ~= nil then |
119 | if g_i18n:hasText(closedText, self.customEnvironment) then |
120 | closedText = g_i18n:getText(closedText, self.customEnvironment) |
121 | end |
122 | end |
123 | self.openingHours = {startTime=startTime, endTime=endTime, disableIfClosed=disableIfClosed, closedText=closedText} |
124 | g_currentMission.environment:addHourChangeListener(self) |
125 | end |
126 | |
127 | self.isEnabled = true |
128 | |
129 | |
130 | local triggerId = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key..".controls#triggerNode")) |
131 | if triggerId ~= nil then |
132 | self.controls.triggerId = triggerId |
133 | |
134 | addTrigger(self.controls.triggerId, "triggerCallback", self) |
135 | for i=0, getNumOfChildren(self.controls.triggerId)-1 do |
136 | addTrigger(getChildAt(self.controls.triggerId, i), "triggerCallback", self) |
137 | end |
138 | |
139 | local posAction = getXMLString(xmlFile, key..".controls#posAction") |
140 | if posAction ~= nil then |
141 | if InputAction[posAction] then |
142 | self.controls.posAction = posAction |
143 | |
144 | local posText = getXMLString(xmlFile, key..".controls#posText") |
145 | if posText ~= nil then |
146 | if g_i18n:hasText(posText, self.customEnvironment) then |
147 | posText = g_i18n:getText(posText, self.customEnvironment) |
148 | end |
149 | self.controls.posActionText = posText |
150 | end |
151 | |
152 | local negText = getXMLString(xmlFile, key..".controls#negText") |
153 | if negText ~= nil then |
154 | if g_i18n:hasText(negText, self.customEnvironment) then |
155 | negText = g_i18n:getText(negText, self.customEnvironment) |
156 | end |
157 | self.controls.negActionText = negText |
158 | end |
159 | |
160 | local negAction = getXMLString(xmlFile, key..".controls#negAction") |
161 | if negAction ~= nil then |
162 | if InputAction[negAction] then |
163 | self.controls.negAction = negAction |
164 | else |
165 | print("Warning: Negative direction action '"..negAction.."' not defined!") |
166 | end |
167 | end |
168 | else |
169 | print("Warning: Positive direction action '"..posAction.."' not defined!") |
170 | end |
171 | end |
172 | end |
173 | |
174 | if g_client ~= nil then |
175 | local soundsKey = key .. ".sounds" |
176 | self.sampleMoving = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "moving", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
177 | self.samplePosEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "posEnd", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
178 | self.sampleNegEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "negEnd", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
179 | end |
180 | |
181 | self.animatedObjectDirtyFlag = self:getNextDirtyFlag() |
182 | |
183 | return success |
184 | end |
loadFrameValues
DescriptionLoad frame values from xmlDefinition
loadFrameValues(integer fileId, string key, integer node)Arguments
integer | fileId | xml file id |
string | key | key |
integer | node | node id |
float | x | x translation |
float | y | y translation |
float | z | z translation |
float | rx | x rotation |
float | ry | y rotation |
float | rz | z rotation |
float | sx | x scale |
float | sy | y scale |
float | sz | z scale |
integer | visibility | visibility |
201 | function AnimatedObject:loadFrameValues(xmlFile, key, node) |
202 | local rx,ry,rz = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotation")) |
203 | local x,y,z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#translation")) |
204 | local sx,sy,sz = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#scale")) |
205 | local isVisible = Utils.getNoNil(getXMLBool(xmlFile, key.."#visibility"), true) |
206 | |
207 | local drx,dry,drz = getRotation(node) |
208 | rx = Utils.getNoNilRad(rx, drx) |
209 | ry = Utils.getNoNilRad(ry, dry) |
210 | rz = Utils.getNoNilRad(rz, drz) |
211 | local dx,dy,dz = getTranslation(node) |
212 | x = Utils.getNoNil(x, dx) |
213 | y = Utils.getNoNil(y, dy) |
214 | z = Utils.getNoNil(z, dz) |
215 | local dsx,dsy,dsz = getScale(node) |
216 | sx = Utils.getNoNil(sx, dsx) |
217 | sy = Utils.getNoNil(sy, dsy) |
218 | sz = Utils.getNoNil(sz, dsz) |
219 | |
220 | local visibility = 1 |
221 | if not isVisible then |
222 | visibility = 0 |
223 | end |
224 | |
225 | return x, y, z, rx, ry, rz, sx, sy, sz, visibility |
226 | end |
loadFromXMLFile
DescriptionLoading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key)Arguments
integer | xmlFile | id of xml object |
string | key | key |
boolean | success | success |
321 | function AnimatedObject:loadFromXMLFile(xmlFile, key) |
322 | local animTime = getXMLFloat(xmlFile, key .. "#time") |
323 | if animTime ~= nil then |
324 | self.animation.direction = Utils.getNoNil(getXMLInt(xmlFile, key.."#direction"), 0) |
325 | self:setAnimTime(animTime, true) |
326 | end |
327 | |
328 | AnimatedObject.hourChanged(self) |
329 | |
330 | return true |
331 | end |
new
DescriptionCreating new instance of animated object classDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
boolean | isServer | is server |
boolean | isClient | is client |
table | customMt | custom metatable |
table | self | new instance of object |
20 | function AnimatedObject:new(isServer, isClient, customMt) |
21 | local self = Object:new(isServer, isClient, customMt or AnimatedObject_mt) |
22 | self.nodeId = 0 |
23 | self.isMoving = false |
24 | self.wasPressed = false |
25 | |
26 | -- input controls fields: |
27 | self.controls = {} |
28 | self.controls.active = false |
29 | self.controls.posAction = nil |
30 | self.controls.negAction = nil |
31 | self.controls.posText = nil |
32 | self.controls.negText = nil |
33 | self.controls.posActionEventId = nil |
34 | self.controls.negActionEventId = nil |
35 | |
36 | self.networkTimeInterpolator = InterpolationTime:new(1.2) |
37 | self.networkAnimTimeInterpolator = InterpolatorValue:new(0) |
38 | |
39 | return self |
40 | end |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
263 | function AnimatedObject:readStream(streamId, connection) |
264 | AnimatedObject:superClass().readStream(self, streamId, connection) |
265 | if connection:getIsServer() then |
266 | local animTime = streamReadFloat32(streamId) |
267 | self:setAnimTime(animTime, true) |
268 | |
269 | self.networkAnimTimeInterpolator:setValue(animTime) |
270 | |
271 | self.networkTimeInterpolator:reset() |
272 | end |
273 | end |
readUpdateStream
DescriptionCalled on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
integer | streamId | stream ID |
integer | timestamp | timestamp |
table | connection | connection |
291 | function AnimatedObject:readUpdateStream(streamId, timestamp, connection) |
292 | AnimatedObject:superClass().readUpdateStream(self, streamId, timestamp, connection) |
293 | if connection:getIsServer() then |
294 | if streamReadBool(streamId) then |
295 | self.networkTimeInterpolator:startNewPhaseNetwork() |
296 | local animTime = streamReadFloat32(streamId) |
297 | self.networkAnimTimeInterpolator:setTargetValue(animTime) |
298 | end |
299 | end |
300 | end |
setAnimTime
DescriptionSet animation timeDefinition
setAnimTime(float t)Arguments
float | t | time |
559 | function AnimatedObject:setAnimTime(t, omitSound) |
560 | t = MathUtil.clamp(t, 0, 1) |
561 | |
562 | for _, part in pairs(self.animation.parts) do |
563 | local v = part.animCurve:get(t) |
564 | self:setFrameValues(part.node, v) |
565 | end |
566 | |
567 | self.animation.time = t |
568 | self.isMoving = true |
569 | |
570 | return t |
571 | end |
setFrameValues
DescriptionSet frame valuesDefinition
setFrameValues(integer node, table v)Arguments
integer | node | node id |
table | v | values |
577 | function AnimatedObject:setFrameValues(node, v) |
578 | setTranslation(node, v[1], v[2], v[3]) |
579 | setRotation(node, v[4], v[5], v[6]) |
580 | setScale(node, v[7], v[8], v[9]) |
581 | setVisibility(node, v[10] == 1) |
582 | end |
triggerCallback
DescriptionTrigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
integer | triggerId | id of trigger |
integer | otherId | id of object that calls callback |
boolean | onEnter | called on enter |
boolean | onLeave | called on leave |
boolean | onStay | called on stay |
624 | function AnimatedObject:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
625 | if g_currentMission.missionInfo:isa(FSCareerMissionInfo) then |
626 | if onEnter or onLeave then |
627 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
628 | if onEnter then |
629 | if self.ownerFarmId == nil or self.ownerFarmId == AccessHandler.EVERYONE or g_currentMission.accessHandler:canFarmAccessOtherId(g_currentMission:getFarmId(), self.ownerFarmId) then |
630 | self.playerInRange = g_currentMission.player |
631 | end |
632 | else |
633 | self.playerInRange = nil |
634 | end |
635 | self:raiseActive() |
636 | end |
637 | end |
638 | end |
639 | end |
update
DescriptionCalled on updateDefinition
update(float dt)Arguments
float | dt | time since last call in ms |
459 | function AnimatedObject:update(dt) |
460 | AnimatedObject:superClass().update(self, dt) |
461 | |
462 | -- Update availability of the input actions |
463 | local deactivateInput = false |
464 | if self.playerInRange then |
465 | if self.isEnabled then |
466 | if not self.controls.active then |
467 | self:registerActionEventsWhenInRange() |
468 | end |
469 | self:updateActionEventTexts() |
470 | else |
471 | deactivateInput = true |
472 | if self.openingHours ~= nil and self.openingHours.closedText ~= nil then |
473 | g_currentMission:addExtraPrintText(self.openingHours.closedText) |
474 | end |
475 | end |
476 | else |
477 | deactivateInput = true |
478 | end |
479 | |
480 | if deactivateInput and self.controls.active then |
481 | self:removeActionEvents() |
482 | end |
483 | |
484 | local finishedAnimation = false |
485 | |
486 | -- former updateTick() |
487 | if self.isServer then |
488 | if self.animation.direction ~= 0 then |
489 | local newAnimTime = MathUtil.clamp(self.animation.time + (self.animation.direction*dt)/self.animation.duration, 0, 1) |
490 | |
491 | self:setAnimTime(newAnimTime) |
492 | if newAnimTime == 0 or newAnimTime == 1 then |
493 | self.animation.direction = 0 |
494 | finishedAnimation = true |
495 | end |
496 | end |
497 | |
498 | if self.animation.time ~= self.animation.timeSend then |
499 | self.animation.timeSend = self.animation.time |
500 | self:raiseDirtyFlags(self.animatedObjectDirtyFlag) |
501 | end |
502 | else |
503 | self.networkTimeInterpolator:update(dt) |
504 | local interpolationAlpha = self.networkTimeInterpolator:getAlpha() |
505 | local animTime = self.networkAnimTimeInterpolator:getInterpolatedValue(interpolationAlpha) |
506 | local newAnimTime = self:setAnimTime(animTime) |
507 | |
508 | if self.animation.direction ~= 0 then |
509 | if self.animation.direction > 0 then |
510 | if newAnimTime == 1 then |
511 | self.animation.direction = 0 |
512 | finishedAnimation = true |
513 | end |
514 | else |
515 | if newAnimTime == 0 then |
516 | self.animation.direction = 0 |
517 | finishedAnimation = true |
518 | end |
519 | end |
520 | end |
521 | |
522 | if self.networkTimeInterpolator:isInterpolating() then |
523 | self:raiseActive() |
524 | end |
525 | end |
526 | |
527 | if self.sampleMoving ~= nil then |
528 | if self.isMoving and self.animation.direction ~= 0 then |
529 | if not self.sampleMoving.isPlaying then |
530 | g_soundManager:playSample(self.sampleMoving) |
531 | self.sampleMoving.isPlaying = true |
532 | end |
533 | else |
534 | if self.sampleMoving.isPlaying then |
535 | g_soundManager:stopSample(self.sampleMoving) |
536 | self.sampleMoving.isPlaying = false |
537 | end |
538 | end |
539 | end |
540 | |
541 | if finishedAnimation and self.animation.direction == 0 then |
542 | if self.samplePosEnd ~= nil and self.animation.time == 1 then |
543 | g_soundManager:playSample(self.samplePosEnd) |
544 | elseif self.sampleNegEnd ~= nil and self.animation.time == 0 then |
545 | g_soundManager:playSample(self.sampleNegEnd) |
546 | end |
547 | end |
548 | |
549 | self.isMoving = false |
550 | |
551 | if self.animation.direction ~= 0 then |
552 | self:raiseActive() |
553 | end |
554 | end |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
279 | function AnimatedObject:writeStream(streamId, connection) |
280 | AnimatedObject:superClass().writeStream(self, streamId, connection) |
281 | if not connection:getIsServer() then |
282 | streamWriteFloat32(streamId, self.animation.time) |
283 | end |
284 | end |
writeUpdateStream
DescriptionCalled on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
integer | streamId | stream ID |
table | connection | connection |
integer | dirtyMask | dirty mask |
307 | function AnimatedObject:writeUpdateStream(streamId, connection, dirtyMask) |
308 | AnimatedObject:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
309 | if not connection:getIsServer() then |
310 | if streamWriteBool(streamId, bitAND(dirtyMask, self.animatedObjectDirtyFlag) ~= 0) then |
311 | streamWriteFloat32(streamId, self.animation.timeSend) |
312 | end |
313 | end |
314 | end |