Script v1.4.4.0
- Handtools
- Events
- Objects
- AnimatedObject
- Bale
- Basketball
- Bga
- BunkerSilo
- FieldDefinition
- FillablePallet
- HelpIcons
- MountableObject
- Nightlight2
- NightlightFlicker
- PhysicsObject
- Rotator
- SaplingPallet
- ShovelTarget
- SimParticleSystem
- StationCrane
- Storage
- TourIcons
- Train
- VehicleSellingPoint
- Placeables
- Triggers
- Utils
- Vehicles
- Specializations
Engine v7.0.0.2
- General
- Entity
- Node
- Scenegraph
- Lighting
- Camera
- Shape
- Particle System
- Physics
- Spline
- Animation
- Overlays
- Sound
- Input
- XML
- Network
- Callbacks
- Text Rendering
- Terrain Detail
- Tire Track
- Editor
- Rendering
- String
- Math
- I3D
- Fillplanes
Foundation Reference
StationCrane
DescriptionClass for station cranesFunctions
- onCreate
- new
- load
- delete
- getSaveAttributesAndNodes
- readStream
- writeStream
- readUpdateStream
- writeUpdateStream
- keyEvent
- mouseEvent
- update
- updateTick
- addNodeVehicleMapping
- removeNodeVehicleMapping
- getIsActive
- getIsActiveForInput
- getIsActiveForSound
- getRootAttacherVehicle
- getAttachedTrailersFillLevelAndCapacity
- getOwner
- getIsHired
- getParentComponent
- getDeactivateOnLeave
- getDailyUpKeep
- onActivate
- onDeactivate
- onDeactivateSounds
- createComponentJoint
- hasInputConflictWithSelection
- hasInputConflict
- addConflictCheckedInput
- removeConflictCheckedInput
- setComponentJointRotLimit
- setComponentJointTransLimit
- setWorldPositionQuaternion
- setComponentJointFrame
onCreate
DescriptionCreating station craneDefinition
onCreate(integer transformId)Arguments
integer | transformId | id of node |
15 | function StationCrane:onCreate(transformId) |
16 | |
17 | if g_currentMission.isTutorialMission then |
18 | return; |
19 | end |
20 | |
21 | local xmlFilename = getUserAttribute(transformId, "xmlFile"); |
22 | if xmlFilename ~= nil then |
23 | xmlFilename = Utils.getFilename(xmlFilename, g_currentMission.loadingMapBaseDirectory); |
24 | local xmlFile = loadXMLFile("StationCrane", xmlFilename); |
25 | |
26 | local stationCrane = StationCrane:new(g_server ~= nil, g_client ~= nil); |
27 | if stationCrane ~= nil then |
28 | -- load() will be called by OnCreateLoadedObjectsManager |
29 | |
30 | stationCrane.stationCraneId = transformId; |
31 | stationCrane.xmlFile = xmlFile; |
32 | stationCrane.configFileName = xmlFilename; |
33 | |
34 | g_currentMission:addOnCreateLoadedObject(stationCrane); |
35 | |
36 | stationCrane:register(true); |
37 | |
38 | OnCreateLoadedObjectsManager:registerObject(stationCrane); |
39 | end; |
40 | else |
41 | print("Error: Missing xmlFile user-attribute for station crane system in " .. getName(transformId)); |
42 | end |
43 | end |
new
DescriptionCreating new instance of station craneDefinition
new(boolean isServer, boolean isClient)Arguments
boolean | isServer | is server |
boolean | isClient | is client |
table | self | new instance of object |
50 | function StationCrane:new(isServer, isClient, customMt) |
51 | local mt = customMt; |
52 | if mt == nil then |
53 | mt = StationCrane_mt; |
54 | end |
55 | local self = Object:new(isServer, isClient, mt); |
56 | |
57 | self.isServer = isServer; |
58 | self.isClient = isClient; |
59 | |
60 | -- |
61 | self.specializations = {}; |
62 | |
63 | return self; |
64 | end |
load
DescriptionCalled on loadingDefinition
load(table savegame)Arguments
table | savegame | savegame |
69 | function StationCrane:load(savegame) |
70 | |
71 | local xmlNode = "vehicle"; |
72 | |
73 | self.rootNode = Utils.indexToObject(self.stationCraneId, "0>"); |
74 | |
75 | self.components = {}; |
76 | table.insert(self.components, {node=self.rootNode, centerOfMass={0,0,0}, solverIterationCount=10}); |
77 | |
78 | self.propertyState = 0; |
79 | self.isEntered = false; |
80 | self.isBroken = false; |
81 | self.nonTabbable = false; |
82 | self.isControlled = false; |
83 | |
84 | self.conflictCheckedInputs = {}; |
85 | |
86 | self.attachedImplements = {}; |
87 | self.attacherJoints = {}; |
88 | |
89 | -- load components |
90 | local numComponents = Utils.getNoNil(getXMLInt(self.xmlFile, xmlNode..".components#count"), 0); |
91 | for i=1, numComponents do |
92 | local key = xmlNode .. string.format(".components.component%d", i); |
93 | if not hasXMLProperty(self.xmlFile, key) then |
94 | print("Warning: " .. key .. " not found in '"..self.configFileName.."'"); |
95 | break; |
96 | end; |
97 | |
98 | local component = {}; |
99 | component.node = Utils.indexToObject(self.rootNode, getXMLString(self.xmlFile, key .. "#index")); |
100 | |
101 | local x, y, z = Utils.getVectorFromString(getXMLString(self.xmlFile, key .. "#centerOfMass")); |
102 | if x ~= nil and y ~= nil and z ~= nil then |
103 | setCenterOfMass(component.node, x, y, z); |
104 | component.centerOfMass = { x, y, z }; |
105 | end; |
106 | |
107 | local count = getXMLInt(self.xmlFile, key .. "#solverIterationCount"); |
108 | if count ~= nil then |
109 | setSolverIterationCount(component.node, count); |
110 | component.solverIterationCount = count; |
111 | end; |
112 | |
113 | table.insert(self.components, component); |
114 | end |
115 | |
116 | self.componentJoints = {}; |
117 | if self.isServer then |
118 | local componentJointI = 0; |
119 | while true do |
120 | local key = xmlNode .. string.format(".componentJoints.componentJoint(%d)", componentJointI); |
121 | local index1 = getXMLInt(self.xmlFile, key.."#component1"); |
122 | local index2 = getXMLInt(self.xmlFile, key.."#component2"); |
123 | local jointIndexStr = getXMLString(self.xmlFile, key.."#index"); |
124 | if index1 == nil or index2 == nil or jointIndexStr == nil then |
125 | break; |
126 | end; |
127 | local jointNode = Utils.indexToObject(self.components, jointIndexStr); |
128 | if jointNode ~= nil and jointNode ~= 0 then |
129 | local jointDesc = {}; |
130 | if Vehicle.loadComponentJointFromXML(self, jointDesc, self.xmlFile, key, componentJointI, jointNode, index1, index2) then |
131 | table.insert(self.componentJoints, jointDesc); |
132 | end |
133 | end |
134 | componentJointI = componentJointI + 1; |
135 | end |
136 | end |
137 | |
138 | if self.isServer then |
139 | for _, jointDesc in pairs(self.componentJoints) do |
140 | self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc); |
141 | end |
142 | end |
143 | |
144 | local collisionPairI = 0; |
145 | self.collisionPairs = {} |
146 | while true do |
147 | local key = string.format("vehicle.components.collisionPair(%d)", collisionPairI); |
148 | if not hasXMLProperty(self.xmlFile, key) then |
149 | break; |
150 | end; |
151 | local enabled = getXMLBool(self.xmlFile, key.."#enabled"); |
152 | local index1 = getXMLInt(self.xmlFile, key.."#component1"); |
153 | local index2 = getXMLInt(self.xmlFile, key.."#component2"); |
154 | if index1 ~= nil and index2 ~= nil and enabled ~= nil then |
155 | local component1 = self.components[index1+1]; |
156 | local component2 = self.components[index2+1]; |
157 | if component1 ~= nil and component2 ~= nil then |
158 | if not enabled then |
159 | table.insert(self.collisionPairs, {component1=component1, component2=component2, enabled=enabled}) |
160 | setPairCollision(component1.node, component2.node, false); |
161 | end; |
162 | end; |
163 | end; |
164 | collisionPairI = collisionPairI +1; |
165 | end; |
166 | |
167 | |
168 | if self.isClient then |
169 | self.engineSample = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.stationCrane.engineSound", nil, g_currentMission.loadingMapBaseDirectory, self.components[2].node); |
170 | end |
171 | |
172 | -- |
173 | self.ikChains = {}; |
174 | |
175 | self.specializations = {}; |
176 | local i = 0; |
177 | while true do |
178 | local baseString = xmlNode .. string.format(".specializations.specialization(%d)", i); |
179 | if not hasXMLProperty(self.xmlFile, baseString) then |
180 | break; |
181 | end |
182 | |
183 | local specName = getXMLString(self.xmlFile, baseString .. "#name"); |
184 | local spec = SpecializationUtil.getSpecialization(specName); |
185 | if spec == nil then |
186 | print("Error: Vehicle types unknown specialization " .. specName); |
187 | return false; |
188 | end |
189 | |
190 | table.insert(self.specializations, spec); |
191 | i = i + 1; |
192 | end |
193 | |
194 | for _,spec in pairs(self.specializations) do |
195 | if spec.preLoad ~= nil then |
196 | local ret = spec.preLoad(self, savegame); |
197 | if ret ~= nil and ret ~= BaseMission.VEHICLE_LOAD_OK then |
198 | return ret; |
199 | end |
200 | end |
201 | end |
202 | for _,spec in pairs(self.specializations) do |
203 | if spec.load ~= nil then |
204 | local ret = spec.load(self, savegame); |
205 | if ret ~= nil and ret ~= BaseMission.VEHICLE_LOAD_OK then |
206 | return ret; |
207 | end |
208 | end |
209 | end |
210 | for _,spec in pairs(self.specializations) do |
211 | if spec.postLoad ~= nil then |
212 | -- don't apply saved values of cylindered |
213 | if spec ~= Cylindered then |
214 | local ret = spec.postLoad(self, savegame); |
215 | if ret ~= nil and ret ~= BaseMission.VEHICLE_LOAD_OK then |
216 | return ret; |
217 | end |
218 | end |
219 | end |
220 | end |
221 | |
222 | |
223 | -- link components to rootNode, after all objects have been loaded |
224 | for i=2, table.getn(self.components) do |
225 | local component = self.components[i]; |
226 | |
227 | local x,y,z = getWorldTranslation(component.node); |
228 | local rx,ry,rz = getWorldRotation(component.node); |
229 | local qx,qy,qz,qw = getWorldQuaternion(component.node); |
230 | |
231 | link(getRootNode(), component.node); |
232 | setTranslation(component.node, x,y,z); |
233 | setRotation(component.node, rx,ry,rz); |
234 | |
235 | component.sentTranslation = {x,y,z}; |
236 | component.sentRotation = {rx,ry,rz}; |
237 | component.lastTranslation = {x,y,z}; |
238 | component.lastRotation = {qx,qy,qz,qw}; |
239 | component.targetTranslation = {x,y,z}; |
240 | component.targetRotation = {qx,qy,qz,qw}; |
241 | component.curTranslation = {x,y,z}; |
242 | component.curRotation = {qx,qy,qz,qw}; |
243 | |
244 | if not self.isServer then |
245 | setRigidBodyType(component.node, "Kinematic"); |
246 | end; |
247 | |
248 | end |
249 | |
250 | self.interpolationAlpha = 0; |
251 | self.interpolationDuration = 50+30; |
252 | self.positionIsDirty = false; |
253 | |
254 | -- |
255 | self.stationCraneDirtyFlag = self:getNextDirtyFlag(); |
256 | |
257 | delete(self.xmlFile); |
258 | self.xmlFile = nil; |
259 | |
260 | g_currentMission:addVehicle(self); |
261 | end |
delete
DescriptionDeleting station craneDefinition
delete()Code
265 | function StationCrane:delete() |
266 | |
267 | if self.isClient then |
268 | SoundUtil.deleteSample(self.engineSample); |
269 | end |
270 | |
271 | -- Check if the objects has been loaded |
272 | if self.specializations ~= nil then |
273 | for i=table.getn(self.specializations), 1, -1 do |
274 | if self.specializations[i].preDelete ~= nil then |
275 | self.specializations[i].preDelete(self); |
276 | end |
277 | end |
278 | for i=table.getn(self.specializations), 1, -1 do |
279 | if self.specializations[i].delete ~= nil then |
280 | self.specializations[i].delete(self); |
281 | end |
282 | end |
283 | end |
284 | |
285 | if self.isServer then |
286 | for _,v in pairs(self.componentJoints) do |
287 | if v.jointIndex ~= 0 then |
288 | removeJoint(v.jointIndex); |
289 | end |
290 | end; |
291 | end; |
292 | |
293 | if self.components then |
294 | for _,v in pairs(self.components) do |
295 | delete(v.node); |
296 | end; |
297 | end; |
298 | |
299 | StationCrane:superClass().delete(self); |
300 | |
301 | OnCreateLoadedObjectsManager:removeObject(self); |
302 | |
303 | g_currentMission:removeVehicle(self, false); |
304 | end |
getSaveAttributesAndNodes
DescriptionGet save attributes and nodesDefinition
getSaveAttributesAndNodes(string nodeIdent)Arguments
string | nodeIdent | node ident |
string | attributes | attributes |
string | nodes | nodes |
311 | function StationCrane:getSaveAttributesAndNodes(nodeIdent) |
312 | local attributes = ""; |
313 | local nodes = ""; |
314 | |
315 | for k,v in pairs(self.specializations) do |
316 | if v.getSaveAttributesAndNodes ~= nil then |
317 | if nodes ~= '' then |
318 | nodes = nodes .."\n" |
319 | end |
320 | local specAttributes, specNodes = v.getSaveAttributesAndNodes(self, nodeIdent, usedModNames); |
321 | if specAttributes ~= nil and specAttributes ~= "" then |
322 | attributes = attributes.." "..specAttributes; |
323 | end; |
324 | if specNodes ~= nil and specNodes ~= "" then |
325 | nodes = nodes..specNodes; |
326 | end; |
327 | end; |
328 | end; |
329 | |
330 | return attributes, nodes; |
331 | end |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
337 | function StationCrane:readStream(streamId, connection) |
338 | self:load(nil); |
339 | for i=2, table.getn(self.components) do |
340 | local x=streamReadFloat32(streamId); |
341 | local y=streamReadFloat32(streamId); |
342 | local z=streamReadFloat32(streamId); |
343 | local x_rot=streamReadFloat32(streamId) |
344 | local y_rot=streamReadFloat32(streamId); |
345 | local z_rot=streamReadFloat32(streamId); |
346 | local w_rot=streamReadFloat32(streamId); |
347 | self:setWorldPositionQuaternion(x,y,z, x_rot,y_rot,z_rot,w_rot, i); |
348 | |
349 | self.components[i].lastTranslation = {x,y,z}; |
350 | self.components[i].lastRotation = {x_rot,y_rot,z_rot,w_rot}; |
351 | self.components[i].targetTranslation = {x,y,z}; |
352 | self.components[i].targetRotation = {x_rot,y_rot,z_rot,w_rot}; |
353 | self.components[i].curTranslation = {x,y,z}; |
354 | self.components[i].curRotation = {x_rot,y_rot,z_rot,w_rot}; |
355 | end; |
356 | self.interpolationAlpha = 0; |
357 | self.positionIsDirty = false; |
358 | |
359 | for _,spec in pairs(self.specializations) do |
360 | if spec.readStream ~= nil then |
361 | spec.readStream(self, streamId, connection); |
362 | end |
363 | end |
364 | end |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
370 | function StationCrane:writeStream(streamId, connection) |
371 | for i=2, table.getn(self.components) do |
372 | local x,y,z = getTranslation(self.components[i].node); |
373 | local x_rot,y_rot,z_rot,w_rot = getQuaternion(self.components[i].node); |
374 | |
375 | streamWriteFloat32(streamId, x); |
376 | streamWriteFloat32(streamId, y); |
377 | streamWriteFloat32(streamId, z); |
378 | streamWriteFloat32(streamId, x_rot); |
379 | streamWriteFloat32(streamId, y_rot); |
380 | streamWriteFloat32(streamId, z_rot); |
381 | streamWriteFloat32(streamId, w_rot); |
382 | end; |
383 | |
384 | for _,spec in pairs(self.specializations) do |
385 | if spec.writeStream ~= nil then |
386 | spec.writeStream(self, streamId, connection); |
387 | end |
388 | end |
389 | 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 |
396 | function StationCrane:readUpdateStream(streamId, timestamp, connection) |
397 | if connection.isServer then |
398 | local hasUpdate = streamReadBool(streamId); |
399 | if hasUpdate then |
400 | |
401 | -- We need to use g_packetPhysicsNetworkTime instead of timestamp, because we need to use the exact same time stepping as we used when simulating |
402 | -- Due to the async physics simulation, we always see the results of the dt of one frame behind, so physicsDt ~= dt |
403 | |
404 | -- Interpolate to target from currently interpolated position |
405 | |
406 | local deltaTime = g_client.tickDuration; |
407 | if self.lastPhysicsNetworkTime ~= nil then |
408 | deltaTime = math.min(g_packetPhysicsNetworkTime - self.lastPhysicsNetworkTime, 3*g_client.tickDuration); |
409 | end |
410 | self.lastPhysicsNetworkTime = g_packetPhysicsNetworkTime; |
411 | |
412 | -- interpTimeLeft is the time we would've continued interpolating. This should always be about g_clientInterpDelay. |
413 | -- If we extrapolated, reset to the full delay |
414 | local interpTimeLeft = g_clientInterpDelay; |
415 | if self.positionIsDirty and self.interpolationAlpha < 1 then |
416 | interpTimeLeft = (1-self.interpolationAlpha) * self.interpolationDuration; |
417 | interpTimeLeft = interpTimeLeft * 0.95 + g_clientInterpDelay * 0.05; -- slighly blend towards the expected delay |
418 | interpTimeLeft = math.min(interpTimeLeft, 3*g_clientInterpDelay); -- clamp to some reasonable maximum |
419 | end |
420 | self.interpolationDuration = interpTimeLeft + deltaTime; |
421 | self.interpolationAlpha = 0; |
422 | |
423 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
424 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
425 | for i=2, table.getn(self.components) do -- start at index 2 ! |
426 | local x = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
427 | local y = Utils.readCompressedWorldPosition(streamId, paramsY); |
428 | local z = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
429 | local x_rot = Utils.readCompressedAngle(streamId); |
430 | local y_rot = Utils.readCompressedAngle(streamId); |
431 | local z_rot = Utils.readCompressedAngle(streamId); |
432 | |
433 | local x_rot,y_rot,z_rot,w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
434 | local targetTranslation = self.components[i].targetTranslation; |
435 | local targetRotation = self.components[i].targetRotation; |
436 | targetTranslation[1] = x; |
437 | targetTranslation[2] = y; |
438 | targetTranslation[3] = z; |
439 | targetRotation[1] = x_rot; targetRotation[2] = y_rot; targetRotation[3] = z_rot; targetRotation[4] = w_rot; |
440 | |
441 | local trans = self.components[i].curTranslation; |
442 | local rot = self.components[i].curRotation; |
443 | local lastTranslation = self.components[i].lastTranslation; |
444 | local lastRotation = self.components[i].lastRotation; |
445 | lastTranslation[1] = trans[1]; |
446 | lastTranslation[2] = trans[2]; |
447 | lastTranslation[3] = trans[3]; |
448 | lastRotation[1] = rot[1]; lastRotation[2] = rot[2]; lastRotation[3] = rot[3]; lastRotation[4] = rot[4]; |
449 | end; |
450 | self.positionIsDirty = true; |
451 | end |
452 | end |
453 | |
454 | for _,spec in pairs(self.specializations) do |
455 | if spec.readUpdateStream ~= nil then |
456 | spec.readUpdateStream(self, streamId, timestamp, connection); |
457 | end |
458 | end |
459 | 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 |
466 | function StationCrane:writeUpdateStream(streamId, connection, dirtyMask) |
467 | if not connection.isServer then |
468 | if streamWriteBool(streamId, bitAND(dirtyMask, self.stationCraneDirtyFlag) ~= 0) then |
469 | |
470 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
471 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
472 | for i=2, table.getn(self.components) do |
473 | local x,y,z = getTranslation(self.components[i].node) |
474 | --local x_rot,y_rot,z_rot,w_rot=getQuaternion(self.components[i].node); |
475 | local x_rot,y_rot,z_rot = getRotation(self.components[i].node); |
476 | |
477 | Utils.writeCompressedWorldPosition(streamId, x, paramsXZ); |
478 | Utils.writeCompressedWorldPosition(streamId, y, paramsY); |
479 | Utils.writeCompressedWorldPosition(streamId, z, paramsXZ); |
480 | Utils.writeCompressedAngle(streamId, x_rot); |
481 | Utils.writeCompressedAngle(streamId, y_rot); |
482 | Utils.writeCompressedAngle(streamId, z_rot); |
483 | end; |
484 | end |
485 | end |
486 | |
487 | for _,spec in pairs(self.specializations) do |
488 | if spec.writeUpdateStream ~= nil then |
489 | spec.writeUpdateStream(self, streamId, connection, dirtyMask); |
490 | end |
491 | end |
492 | end |
keyEvent
DescriptionCalled on key eventDefinition
keyEvent(integer unicode, integer sym, integer modifier, boolean isDown)Arguments
integer | unicode | unicode |
integer | sym | sym |
integer | modifier | modifier |
boolean | isDown | is button down |
500 | function StationCrane:keyEvent(unicode, sym, modifier, isDown) |
501 | for _,spec in pairs(self.specializations) do |
502 | if spec.keyEvent ~= nil then |
503 | spec.mouseEvent(self, unicode, sym, modifier, isDown); |
504 | end |
505 | end |
506 | end |
mouseEvent
DescriptionCalled on mouse eventDefinition
mouseEvent(float posX, float posY, boolean isDown, boolean isUp, integer button)Arguments
float | posX | x position |
float | posY | y position |
boolean | isDown | is mouse button down |
boolean | isUp | is mouse button up |
integer | button | mouse button |
515 | function StationCrane:mouseEvent(posX, posY, isDown, isUp, button) |
516 | for _,spec in pairs(self.specializations) do |
517 | if spec.mouseEvent ~= nil then |
518 | spec.mouseEvent(self, posX, posY, isDown, isUp, button); |
519 | end |
520 | end |
521 | end |
update
DescriptionCalled on updateDefinition
update(float dt)Arguments
float | dt | time since last call in ms |
526 | function StationCrane:update(dt) |
527 | StationCrane:superClass().update(self, dt); |
528 | |
529 | if not self.isServer and self.positionIsDirty then |
530 | local maxIntrpAlpha = 1.2; |
531 | local interpolationAlpha = self.interpolationAlpha + g_physicsDtUnclamped / self.interpolationDuration; |
532 | if interpolationAlpha >= maxIntrpAlpha then |
533 | interpolationAlpha = maxIntrpAlpha; |
534 | self.positionIsDirty = false; |
535 | end; |
536 | self.interpolationAlpha = interpolationAlpha; |
537 | for i=2, table.getn(self.components) do |
538 | local lastTranslation = self.components[i].lastTranslation; |
539 | local targetTranslation = self.components[i].targetTranslation; |
540 | local curTranslation = self.components[i].curTranslation; |
541 | curTranslation[1] = lastTranslation[1] + interpolationAlpha * (targetTranslation[1] - lastTranslation[1]); |
542 | curTranslation[2] = lastTranslation[2] + interpolationAlpha * (targetTranslation[2] - lastTranslation[2]); |
543 | curTranslation[3] = lastTranslation[3] + interpolationAlpha * (targetTranslation[3] - lastTranslation[3]); |
544 | local rot1 = self.components[i].lastRotation; |
545 | local rot2 = self.components[i].targetRotation; |
546 | local x,y,z,w = Utils.nlerpQuaternionShortestPath(rot1[1], rot1[2], rot1[3], rot1[4], rot2[1], rot2[2], rot2[3], rot2[4], interpolationAlpha); |
547 | local curRotation = self.components[i].curRotation; |
548 | curRotation[1] = x; curRotation[2] = y; curRotation[3] = z; curRotation[4] = w; |
549 | self:setWorldPositionQuaternion(curTranslation[1], curTranslation[2], curTranslation[3], x,y,z,w, i); |
550 | end; |
551 | end; |
552 | |
553 | if self.specializations ~= nil then |
554 | for _,spec in pairs(self.specializations) do |
555 | if spec.update ~= nil then |
556 | spec.update(self, dt); |
557 | end |
558 | end |
559 | end |
560 | if self:getIsActive() then |
561 | IKUtil.updateIKChains(self.ikChains); |
562 | end |
563 | |
564 | end |
updateTick
DescriptionCalled on update tickDefinition
updateTick(float dt)Arguments
float | dt | time since last call in ms |
569 | function StationCrane:updateTick(dt) |
570 | StationCrane:superClass().updateTick(self, dt); |
571 | |
572 | if self.isServer then |
573 | local hasOwner = self:getOwner() ~= nil; |
574 | for i=2, table.getn(self.components) do |
575 | local x,y,z = getTranslation(self.components[i].node); |
576 | local x_rot,y_rot,z_rot=getRotation(self.components[i].node); |
577 | local sentTranslation = self.components[i].sentTranslation; |
578 | local sentRotation = self.components[i].sentRotation; |
579 | if hasOwner or |
580 | math.abs(x-sentTranslation[1]) > 0.005 or |
581 | math.abs(y-sentTranslation[2]) > 0.005 or |
582 | math.abs(z-sentTranslation[3]) > 0.005 or |
583 | math.abs(x_rot-sentRotation[1]) > 0.1 or |
584 | math.abs(y_rot-sentRotation[2]) > 0.1 or |
585 | math.abs(z_rot-sentRotation[3]) > 0.1 |
586 | then |
587 | self:raiseDirtyFlags(self.stationCraneDirtyFlag); |
588 | sentTranslation[1] = x; sentTranslation[2] = y; sentTranslation[3] = z; |
589 | sentRotation[1] = x_rot; sentRotation[2] = y_rot; sentRotation[3] = z_rot; |
590 | self.lastMoveTime = g_currentMission.time; |
591 | end; |
592 | end; |
593 | end |
594 | |
595 | if self.isClient then |
596 | if self.isControlled then |
597 | if self.isEntered then |
598 | SoundUtil.playSample(self.engineSample, 0, 0, nil); |
599 | SoundUtil.stop3DSample(self.engineSample); |
600 | else |
601 | SoundUtil.stopSample(self.engineSample); |
602 | SoundUtil.play3DSample(self.engineSample); |
603 | end |
604 | else |
605 | SoundUtil.stopSample(self.engineSample); |
606 | SoundUtil.stop3DSample(self.engineSample); |
607 | end |
608 | end; |
609 | |
610 | if self.specializations then |
611 | for _,spec in pairs(self.specializations) do |
612 | if spec.updateTick ~= nil then |
613 | spec.updateTick(self, dt); |
614 | end |
615 | end |
616 | end |
617 | end |
addNodeVehicleMapping
DescriptionAdd node to vehicle mappingDefinition
addNodeVehicleMapping(table list)Arguments
table | list | list |
625 | function StationCrane:addNodeVehicleMapping(list) |
626 | if self.components == nil then |
627 | return; |
628 | end |
629 | |
630 | for k,v in pairs(self.components) do |
631 | list[v.node] = self; |
632 | end |
633 | |
634 | for k,v in pairs(self.specializations) do |
635 | if v.addNodeVehicleMapping ~= nil then |
636 | v.addNodeVehicleMapping(self, list); |
637 | end |
638 | end |
639 | end |
removeNodeVehicleMapping
DescriptionRemove node from vehicle mappingDefinition
removeNodeVehicleMapping(table list)Arguments
table | list | list |
644 | function StationCrane:removeNodeVehicleMapping(list) |
645 | if self.components == nil then |
646 | return; |
647 | end |
648 | |
649 | for k,v in pairs(self.components) do |
650 | list[v.node] = nil; |
651 | end |
652 | |
653 | for k,v in pairs(self.specializations) do |
654 | if v.removeNodeVehicleMapping ~= nil then |
655 | v.removeNodeVehicleMapping(self, list); |
656 | end |
657 | end |
658 | end |
getIsActive
DescriptionReturns if is activeDefinition
getIsActive()Return Values
boolean | isActive | is active |
663 | function StationCrane:getIsActive() |
664 | if self.isEntered or self.isControlled then |
665 | return true; |
666 | end |
667 | if self.attacherVehicle ~= nil then |
668 | return self.attacherVehicle:getIsActive(); |
669 | end |
670 | end |
getIsActiveForInput
DescriptionReturns if is active for inputDefinition
getIsActiveForInput()Return Values
boolean | isActive | is active |
675 | function StationCrane:getIsActiveForInput(onlyTrueIfSelected) |
676 | if g_gui:getIsGuiVisible() or g_currentMission.isPlayerFrozen then |
677 | return false; |
678 | end |
679 | |
680 | if self.isEntered then |
681 | if onlyTrueIfSelected == nil or onlyTrueIfSelected then |
682 | return self.selectedImplement == nil; |
683 | else |
684 | return true; |
685 | end |
686 | end |
687 | |
688 | if self.attacherVehicle ~= nil then |
689 | if onlyTrueIfSelected == nil or onlyTrueIfSelected then |
690 | return self.isSelected and self.attacherVehicle:getIsActiveForInput(false); |
691 | else |
692 | return self.attacherVehicle:getIsActiveForInput(false); |
693 | end |
694 | end |
695 | return false; |
696 | end |
getIsActiveForSound
DescriptionReturns if is active for soundDefinition
getIsActiveForSound()Return Values
boolean | isActive | is active |
701 | function StationCrane:getIsActiveForSound() |
702 | if self.isClient then |
703 | if self.isEntered then |
704 | return true; |
705 | end |
706 | if self.attacherVehicle ~= nil then |
707 | return self.attacherVehicle:getIsActiveForSound(); |
708 | end |
709 | end |
710 | return false; |
711 | end |
getRootAttacherVehicle
DescriptionReturns root attacher vehicleDefinition
getRootAttacherVehicle()Return Values
table | rootAttacherVehicle | root attacher vehicle |
716 | function StationCrane:getRootAttacherVehicle() |
717 | local rootVehicle = self; |
718 | while rootVehicle.attacherVehicle ~= nil do |
719 | rootVehicle = rootVehicle.attacherVehicle; |
720 | end |
721 | return rootVehicle; |
722 | end |
getAttachedTrailersFillLevelAndCapacity
DescriptionReturns fill level and capacity of attached trailersDefinition
getAttachedTrailersFillLevelAndCapacity()Return Values
float | fillLevel | fill level |
float | capacity | capacity |
728 | function StationCrane:getAttachedTrailersFillLevelAndCapacity() |
729 | local fillLevel = 0; |
730 | local capacity = 0; |
731 | local hasTrailer = false; |
732 | |
733 | if self.fillLevel ~= nil and self.getCapacity ~= nil then |
734 | fillLevel = fillLevel + self.fillLevel; |
735 | capacity = capacity + self:getCapacity(); |
736 | hasTrailer = true; |
737 | end |
738 | |
739 | for _, implement in pairs(self.attachedImplements) do |
740 | if implement.object ~= nil then |
741 | local f, c = implement.object:getAttachedTrailersFillLevelAndCapacity(); |
742 | if f ~= nil and c ~= nil then |
743 | fillLevel = fillLevel + f; |
744 | capacity = capacity + c; |
745 | hasTrailer = true; |
746 | end |
747 | end |
748 | end |
749 | if hasTrailer then |
750 | return fillLevel, capacity; |
751 | end |
752 | return nil; |
753 | end |
getOwner
DescriptionReturns ownerDefinition
getOwner()Return Values
table | owner | owner |
758 | function StationCrane:getOwner() |
759 | if self.owner ~= nil then |
760 | return self.owner; |
761 | end |
762 | if self.attacherVehicle ~= nil then |
763 | return self.attacherVehicle:getOwner(); |
764 | end |
765 | return nil; |
766 | end |
getIsHired
DescriptionReturns if is hiredDefinition
getIsHired()Return Values
boolean | isHired | is hired |
771 | function StationCrane:getIsHired() |
772 | return false; |
773 | end |
getParentComponent
DescriptionReturns parent componentDefinition
getParentComponent(integer node)Arguments
integer | node | node |
integer | parentComponent | parent component |
779 | function StationCrane:getParentComponent(node) |
780 | local parent = node; |
781 | while getParent(parent) ~= nil do |
782 | for i,component in pairs(self.components) do |
783 | if component.node == parent then |
784 | return parent; |
785 | end |
786 | end |
787 | parent = getParent(parent); |
788 | end |
789 | return 0; |
790 | end |
getDeactivateOnLeave
DescriptionReturns if deactivate on leaveDefinition
getDeactivateOnLeave()Return Values
boolean | deactivateOnLeave | deactivate on leave |
795 | function StationCrane:getDeactivateOnLeave() |
796 | return true; |
797 | end |
getDailyUpKeep
DescriptionReturns daily up keepDefinition
getDailyUpKeep()Return Values
float | dailyUpKeep | daily up keep |
802 | function StationCrane:getDailyUpKeep() |
803 | return 0; |
804 | end |
onActivate
DescriptionCalled on activationDefinition
onActivate()Code
808 | function StationCrane:onActivate() |
809 | for k,v in pairs(self.specializations) do |
810 | if v.onActivate ~= nil then |
811 | v.onActivate(self); |
812 | end |
813 | end |
814 | end |
onDeactivate
DescriptionCalled on deactivationDefinition
onDeactivate()Code
818 | function StationCrane:onDeactivate() |
819 | for k,v in pairs(self.specializations) do |
820 | if v.onDeactivate ~= nil then |
821 | v.onDeactivate(self); |
822 | end |
823 | end |
824 | |
825 | self:onDeactivateSounds(); |
826 | end |
onDeactivateSounds
DescriptionCalled on sound deactivationDefinition
onDeactivateSounds()Code
830 | function StationCrane:onDeactivateSounds() |
831 | for k,v in pairs(self.specializations) do |
832 | if v.onDeactivateSounds ~= nil then |
833 | v.onDeactivateSounds(self); |
834 | end |
835 | end |
836 | end |
createComponentJoint
DescriptionCreate component joint between two componentsDefinition
createComponentJoint(table component1, table component2, table jointDesc)Arguments
table | component1 | component 1 |
table | component2 | component 2 |
table | jointDesc | joint desc |
boolean | success | success |
844 | function StationCrane:createComponentJoint(component1, component2, jointDesc) |
845 | Vehicle.createComponentJoint(self, component1, component2, jointDesc) |
846 | end |
hasInputConflictWithSelection
DescriptionReturns true if input conflict with selectionDefinition
hasInputConflictWithSelection(table inputs)Arguments
table | inputs | inputs |
boolean | hasConflict | has conflict |
852 | function StationCrane:hasInputConflictWithSelection(inputs) |
853 | if inputs == nil then |
854 | inputs = self.conflictCheckedInputs; |
855 | end |
856 | local rootAttacherVehicle = self:getRootAttacherVehicle(); |
857 | local curVehicle = rootAttacherVehicle; |
858 | if rootAttacherVehicle.selectedImplement ~= nil then |
859 | curVehicle = rootAttacherVehicle.selectedImplement.object; |
860 | end |
861 | -- check selected implement and all of its attachers (up to our vehicle or root) for conflicts |
862 | -- implements have a higher priority if they have a smaller distance to the selected implement in the tree |
863 | while curVehicle ~= nil and curVehicle ~= self do |
864 | if curVehicle:hasInputConflict(inputs, false) then |
865 | return true; |
866 | end |
867 | curVehicle = curVehicle.attacherVehicle; |
868 | end |
869 | -- no conflcit if we are between the selection and the root and have no conflict with implements closer to the selection |
870 | if curVehicle == self then |
871 | return false; |
872 | end |
873 | |
874 | -- not within the selection and the root, test for conflict to all other implements |
875 | return rootAttacherVehicle:hasInputConflict(inputs, true, self); |
876 | end |
hasInputConflict
DescriptionReturns true if has input conflictDefinition
hasInputConflict(table inputs, boolean recursive, table excludedVehicle)Arguments
table | inputs | inputs |
boolean | recursive | check recursive |
table | excludedVehicle | excluded vehicle |
boolean | hasConflict | has conflict |
884 | function StationCrane:hasInputConflict(inputs, recursive, excludedVehicle) |
885 | if next(self.conflictCheckedInputs) ~= nil and self ~= excludedVehicle then |
886 | for input,_ in pairs(inputs) do |
887 | if InputBinding.getHasInputConflict(input, self.conflictCheckedInputs) then |
888 | return true; |
889 | end |
890 | end |
891 | end |
892 | if recursive then |
893 | for _,v in pairs(self.attachedImplements) do |
894 | if v.object ~= nil and v.object:hasInputConflict(inputs, recursive, excludedVehicle) then |
895 | return true; |
896 | end |
897 | end |
898 | end |
899 | return false; |
900 | end |
addConflictCheckedInput
DescriptionAdd conflict checked inputDefinition
addConflictCheckedInput(integer actionIndex)Arguments
integer | actionIndex | index of action |
905 | function StationCrane:addConflictCheckedInput(actionIndex) |
906 | self.conflictCheckedInputs[actionIndex] = true; |
907 | end |
removeConflictCheckedInput
DescriptionRemove conflict checked inputDefinition
removeConflictCheckedInput(integer actionIndex)Arguments
integer | actionIndex | index of action |
912 | function StationCrane:removeConflictCheckedInput(actionIndex) |
913 | self.conflictCheckedInputs[actionIndex] = nil; |
914 | end |
setComponentJointRotLimit
DescriptionSet component joint rot limitDefinition
setComponentJointRotLimit(integer componentJoint, integer axis, float minLimit, float maxLimit)Arguments
integer | componentJoint | index of component joint |
integer | axis | axis |
float | minLimit | min limit |
float | maxLimit | max limit |
922 | function StationCrane:setComponentJointRotLimit(componentJoint, axis, minLimit, maxLimit) |
923 | Vehicle.setComponentJointRotLimit(self, componentJoint, axis, minLimit, maxLimit) |
924 | end |
setComponentJointTransLimit
DescriptionSet component joint trans limitDefinition
setComponentJointTransLimit(integer componentJoint, integer axis, float minLimit, float maxLimit)Arguments
integer | componentJoint | index of component joint |
integer | axis | axis |
float | minLimit | min limit |
float | maxLimit | max limit |
932 | function StationCrane:setComponentJointTransLimit(componentJoint, axis, minLimit, maxLimit) |
933 | Vehicle.setComponentJointTransLimit(self, componentJoint, axis, minLimit, maxLimit) |
934 | end |
setWorldPositionQuaternion
DescriptionSet world position and quaternion rotation of componentDefinition
setWorldPositionQuaternion(float x, float y, float z, float qx, float qy, float qz, float qw, integer i, boolean changeInterp)Arguments
float | x | x position |
float | y | y position |
float | z | z position |
float | qx | x rotation |
float | qy | y rotation |
float | qz | z rotation |
float | qw | w rotation |
integer | i | index if component |
boolean | changeInterp | change interpolation |
947 | function StationCrane:setWorldPositionQuaternion(x,y,z, xRot,yRot,zRot,wRot, i) |
948 | setTranslation(self.components[i].node, x,y,z); |
949 | setQuaternion(self.components[i].node, xRot,yRot,zRot,wRot); |
950 | end; |
setComponentJointFrame
DescriptionSet component joint frameDefinition
setComponentJointFrame(integer jointDesc, integer anchorActor)Arguments
integer | jointDesc | joint desc index |
integer | anchorActor | anchor actor |
956 | function StationCrane:setComponentJointFrame(jointDesc, anchorActor) |
957 | Vehicle.setComponentJointFrame(self, jointDesc, anchorActor) |
958 | end |