24 | function WoodCrusher:load(savegame) |
25 | self.getIsTurnedOnAllowed = Utils.overwrittenFunction(self.getIsTurnedOnAllowed, WoodCrusher.getIsTurnedOnAllowed); |
26 | self.getDirtMultiplier = Utils.overwrittenFunction(self.getDirtMultiplier, WoodCrusher.getDirtMultiplier); |
27 | |
28 | self.onCrushedSplitShape = WoodCrusher.onCrushedSplitShape; |
29 | |
30 | WoodCrusher.loadWoodCrusher(self, self.xmlFile, self.components) |
31 | |
32 | local moveColDisableCollisionPairs = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.woodCrusher#moveColDisableCollisionPairs"), true); |
33 | if moveColDisableCollisionPairs then |
34 | for _, component in pairs(self.components) do |
35 | for _, node in pairs(self.woodCrusherMoveColNodes) do |
36 | setPairCollision(component.node, node, false); |
37 | end |
38 | end |
39 | end |
40 | |
41 | self.woodCrusher = {}; |
42 | self.woodCrusher.fillUnitIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.woodCrusher#fillUnitIndex"), 1); |
43 | self.woodCrusher.unloadInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.woodCrusher#unloadInfoIndex"), 1); |
44 | self.woodCrusher.loadInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.woodCrusher#loadInfoIndex"), 1); |
45 | self.woodCrusher.dischargeInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.woodCrusher#dischargeInfoIndex"), 1); |
46 | end |
115 | function WoodCrusher:updateTick(dt) |
116 | if self:getIsTurnedOn() then |
117 | if self:getCapacity() <= 0 then |
118 | self.lastLostFillLevel = self:getUnitFillLevel(self.woodCrusher.fillUnitIndex); |
119 | if self:getUnitFillLevel(self.woodCrusher.fillUnitIndex) > 0 then |
120 | self:setUnitFillLevel(self.woodCrusher.fillUnitIndex, 0, FillUtil.FILLTYPE_WOODCHIPS, true, self.fillVolumeUnloadInfos[self.woodCrusher.unloadInfoIndex]) |
121 | end |
122 | end |
123 | |
124 | WoodCrusher.updateTickWoodCrusher(self, dt); |
125 | end |
126 | |
127 | -- turn on/off automatically |
128 | if self.isServer then |
129 | if g_currentMission.missionInfo.automaticMotorStartEnabled then |
130 | if self.woodCrusherTurnOnAutomatically and self.setIsTurnedOn ~= nil then |
131 | if next(self.woodCrusherMoveTriggerNodes) ~= nil then |
132 | if self.getIsMotorStarted ~= nil then |
133 | if not self:getIsMotorStarted() then |
134 | self:startMotor() |
135 | end; |
136 | else |
137 | if self.attacherVehicle ~= nil then |
138 | if self.attacherVehicle.getIsMotorStarted ~= nil then |
139 | if not self.attacherVehicle:getIsMotorStarted() then |
140 | self.attacherVehicle:startMotor() |
141 | end; |
142 | end; |
143 | end; |
144 | end; |
145 | |
146 | local isTurnedOnAllowed = self:getIsTurnedOnAllowed(true); |
147 | |
148 | if self.attacherVehicle == nil and self.getIsMotorStarted == nil then |
149 | isTurnedOnAllowed = false; |
150 | end; |
151 | |
152 | if not self.isControlled and not self:getIsTurnedOn() and isTurnedOnAllowed then |
153 | self:setIsTurnedOn(true); |
154 | end |
155 | self.turnOffTimer = 3000; |
156 | else |
157 | if self:getIsTurnedOn() then |
158 | if self.turnOffTimer == nil then |
159 | self.turnOffTimer = 3000; |
160 | end |
161 | self.turnOffTimer = self.turnOffTimer - dt; |
162 | |
163 | if self.getIsMotorStarted ~= nil then |
164 | self.motor:updateMotorRpm(dt); |
165 | end |
166 | |
167 | if self.turnOffTimer < 0 then |
168 | local rootAttacherVehicle = self:getRootAttacherVehicle(); |
169 | |
170 | if not rootAttacherVehicle.isControlled then |
171 | if self.getIsMotorStarted ~= nil then |
172 | if self:getIsMotorStarted() then |
173 | self:stopMotor() |
174 | end; |
175 | end; |
176 | |
177 | self:setIsTurnedOn(false); |
178 | end |
179 | end |
180 | end |
181 | end |
182 | end |
183 | end |
184 | end; |
185 | end |
277 | function WoodCrusher.loadWoodCrusher(self, xmlFile, rootNode) |
278 | self.woodCrusherSplitShapeCallback = WoodCrusher.woodCrusherSplitShapeCallback; |
279 | self.woodCrusherMoveTriggerCallback = WoodCrusher.woodCrusherMoveTriggerCallback; |
280 | self.crushSplitShape = WoodCrusher.crushSplitShape; |
281 | |
282 | local xmlRootName = getXMLRootName(xmlFile) |
283 | self.woodCrusherCutNode = Utils.indexToObject(rootNode, getXMLString(xmlFile, xmlRootName..".woodCrusher#cutNode")); |
284 | self.woodCrusherMainDrumRefNode = Utils.indexToObject(rootNode, getXMLString(xmlFile, xmlRootName..".woodCrusher#mainDrumRefNode")); |
285 | |
286 | self.woodCrusherMoveTriggers = {}; |
287 | local i=0; |
288 | while true do |
289 | local key = xmlRootName .. string.format(".woodCrusher.moveTrigger(%d)", i); |
290 | if not hasXMLProperty(xmlFile, key) then |
291 | break; |
292 | end |
293 | local node = Utils.indexToObject(rootNode, getXMLString(xmlFile, key .. "#index")); |
294 | if node == nil then |
295 | break; |
296 | end |
297 | table.insert(self.woodCrusherMoveTriggers, node); |
298 | i=i+1; |
299 | end |
300 | |
301 | self.woodCrusherMoveColNodes = {}; |
302 | local i=0; |
303 | while true do |
304 | local key = xmlRootName .. string.format(".woodCrusher.moveCollision(%d)", i); |
305 | if not hasXMLProperty(xmlFile, key) then |
306 | break; |
307 | end |
308 | local node = Utils.indexToObject(rootNode, getXMLString(xmlFile, key .. "#index")); |
309 | if node == nil then |
310 | break; |
311 | end |
312 | table.insert(self.woodCrusherMoveColNodes, node); |
313 | i=i+1; |
314 | end |
315 | |
316 | self.woodCrusherMoveVelocityZ = Utils.getNoNil(getXMLFloat(xmlFile, xmlRootName..".woodCrusher#moveVelocityZ"), 0.8); -- m/s |
317 | self.woodCrusherMoveMaxForce = Utils.getNoNil(getXMLFloat(xmlFile, xmlRootName..".woodCrusher#moveMaxForce"), 7); -- input is kN |
318 | self.woodCrusherDownForceNode = Utils.indexToObject(rootNode, getXMLString(xmlFile, xmlRootName..".woodCrusher#downForceNode")); |
319 | self.woodCrusherDownForce = Utils.getNoNil(getXMLFloat(xmlFile, xmlRootName..".woodCrusher#downForce"), 2); |
320 | self.woodCrusherCutSizeY = Utils.getNoNil(getXMLFloat(xmlFile, xmlRootName..".woodCrusher#cutSizeY"), 1); |
321 | self.woodCrusherCutSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, xmlRootName..".woodCrusher#cutSizeZ"), 1); |
322 | |
323 | self.woodCrusherMoveTriggerNodes = {}; |
324 | if self.isServer and self.woodCrusherMoveTriggers ~= nil then |
325 | for _,node in pairs(self.woodCrusherMoveTriggers) do |
326 | addTrigger(node, "woodCrusherMoveTriggerCallback", self); |
327 | end |
328 | end |
329 | |
330 | self.woodCrusherCrushNodes = {}; |
331 | |
332 | self.crushingTime = 0; |
333 | |
334 | self.woodCrusherTurnOnAutomatically = Utils.getNoNil(getXMLBool(xmlFile, xmlRootName .. ".woodCrusher#automaticallyTurnOn"), false); |
335 | |
336 | if self.isClient then |
337 | self.woodCrusherParticleSystems = {}; |
338 | |
339 | local i = 0 |
340 | while true do |
341 | local key = string.format(xmlRootName..".woodCrusher.emitterShape(%d)", i); |
342 | if not hasXMLProperty(xmlFile, key) then |
343 | break |
344 | end |
345 | |
346 | local emitterShape = Utils.indexToObject(rootNode, getXMLString(xmlFile, key.."#node")); |
347 | local particleType = getXMLString(xmlFile, key.."#particleType") |
348 | if emitterShape ~= nil then |
349 | local fillType = FillUtil.FILLTYPE_WOODCHIPS; |
350 | local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType) |
351 | if particleSystem ~= nil then |
352 | table.insert(self.woodCrusherParticleSystems, ParticleUtil.copyParticleSystem(xmlFile, key, particleSystem, emitterShape)) |
353 | end |
354 | end |
355 | i = i + 1 |
356 | end |
357 | |
358 | self.woodCrusherTurnedOnRotationNode = Utils.loadRotationNodes(xmlFile, {}, xmlRootName..".turnedOnRotationNodes.turnedOnRotationNode", "woodCrusher", rootNode); |
359 | self.woodCrusherTurnedOnScrollers = Utils.loadScrollers(rootNode, xmlFile, xmlRootName..".turnedOnScrollers.turnedOnScroller", {}, false); |
360 | |
361 | local linkNode = nil |
362 | if type(rootNode) == "table" then |
363 | linkNode = rootNode[1].node; |
364 | elseif rootNode ~= nil then |
365 | linkNode = rootNode |
366 | end |
367 | |
368 | self.sampleWoodCrusherStart = SoundUtil.loadSample(xmlFile, {}, xmlRootName..".woodCrusherStartSound", nil, self.baseDirectory); |
369 | self.sampleWoodCrusherStop = SoundUtil.loadSample(xmlFile, {}, xmlRootName..".woodCrusherStopSound", nil, self.baseDirectory); |
370 | self.sampleWoodCrusherIdle = SoundUtil.loadSample(xmlFile, {}, xmlRootName..".woodCrusherIdleSound", nil, self.baseDirectory, linkNode); |
371 | self.sampleWoodCrusherWork = SoundUtil.loadSample(xmlFile, {}, xmlRootName..".woodCrusherWorkSound", nil, self.baseDirectory, linkNode); |
372 | |
373 | self.maxWorkFadeTime = 300; |
374 | self.workFadeTime = 0; |
375 | end; |
376 | end |
398 | function WoodCrusher.updateWoodCrusher(self, dt) |
399 | if self.isServer then |
400 | for node in pairs(self.woodCrusherCrushNodes) do |
401 | self:crushSplitShape(node); |
402 | self.woodCrusherCrushNodes[node] = nil; |
403 | self.woodCrusherMoveTriggerNodes[node] = nil; |
404 | end |
405 | |
406 | local x,y,z = getTranslation(self.woodCrusherMainDrumRefNode); |
407 | local ty = 0; |
408 | local maxTreeSizeY = 0; |
409 | for id in pairs(self.woodCrusherMoveTriggerNodes) do |
410 | if not entityExists(id) then |
411 | self.woodCrusherMoveTriggerNodes[id] = nil; |
412 | else |
413 | if self.woodCrusherMoveColNodes == nil then |
414 | local vx,vy,vz = getLinearVelocity(id); |
415 | local _,_,lvz = worldDirectionToLocal(self.woodCrusherMoveTrigger, vx,vy,vz); |
416 | if lvz < self.woodCrusherMoveVelocityZ then |
417 | local dvz = math.min(self.woodCrusherMoveVelocityZ - lvz, self.woodCrusherMoveMaxForce*dt*0.001/getMass(id)); |
418 | local dvx,dvy,dvz = localDirectionToWorld(self.woodCrusherMoveTrigger, 0,0,dvz); |
419 | setLinearVelocity(id, vx+dvx, vy+dvy, vz+dvz); |
420 | end |
421 | end |
422 | if self.woodCrusherDownForceNode ~= nil then |
423 | local x,y,z = getWorldTranslation(self.woodCrusherDownForceNode); |
424 | local nx,ny,nz = localDirectionToWorld(self.woodCrusherDownForceNode, 1,0,0); |
425 | local yx,yy,yz = localDirectionToWorld(self.woodCrusherDownForceNode, 0,1,0); |
426 | |
427 | local minY,maxY, minZ,maxZ = testSplitShape(id, x,y,z, nx,ny,nz, yx,yy,yz, self.woodCrusherCutSizeY, self.woodCrusherCutSizeZ); |
428 | if minY ~= nil then |
429 | local cx,cy,cz = localToWorld(self.woodCrusherDownForceNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5); |
430 | local downX,downY,downZ = localDirectionToWorld(self.woodCrusherDownForceNode, 0,-self.woodCrusherDownForce,0); |
431 | addForce(id, downX, downY, downZ, cx,cy,cz, false); |
432 | if self.woodCrusherMainDrumRefNode ~= nil then |
433 | maxTreeSizeY = math.max(maxTreeSizeY, maxY); |
434 | end; |
435 | end |
436 | |
437 | end |
438 | end |
439 | end |
440 | if self.woodCrusherMainDrumRefNode ~= nil then |
441 | if maxTreeSizeY > 0 then |
442 | local a,b,c = localToWorld(self.woodCrusherDownForceNode, 0, maxTreeSizeY, 0); |
443 | _, ty, _ = worldToLocal(getParent(self.woodCrusherMainDrumRefNode), a,b,c); |
444 | end; |
445 | if ty > y then |
446 | y = math.min(y + 0.0003*dt, ty); |
447 | else |
448 | y = math.max(y - 0.0003*dt, ty); |
449 | end; |
450 | setTranslation(self.woodCrusherMainDrumRefNode, x,y,z); |
451 | end; |
452 | end |
453 | end |
458 | function WoodCrusher.updateTickWoodCrusher(self, dt) |
459 | if self.isServer then |
460 | if self.woodCrusherCutNode ~= nil and next(self.woodCrusherMoveTriggerNodes) ~= nil then |
461 | local x,y,z = getWorldTranslation(self.woodCrusherCutNode); |
462 | local nx,ny,nz = localDirectionToWorld(self.woodCrusherCutNode, 1,0,0); |
463 | local yx,yy,yz = localDirectionToWorld(self.woodCrusherCutNode, 0,1,0); |
464 | for id in pairs(self.woodCrusherMoveTriggerNodes) do |
465 | local lenBelow, lenAbove = getSplitShapePlaneExtents(id, x,y,z, nx,ny,nz); |
466 | if lenAbove ~= nil and lenBelow ~= nil then |
467 | if lenBelow <= 0.4 then |
468 | self.woodCrusherMoveTriggerNodes[id] = nil; |
469 | self:crushSplitShape(id); |
470 | elseif lenAbove >= 0.2 then |
471 | local minY = splitShape(id, x,y,z, nx,ny,nz, yx,yy,yz, self.woodCrusherCutSizeY, self.woodCrusherCutSizeZ, "woodCrusherSplitShapeCallback", self); |
472 | if minY ~= nil then |
473 | self.woodCrusherMoveTriggerNodes[id] = nil; |
474 | end |
475 | end |
476 | end; |
477 | end |
478 | end |
479 | end |
480 | |
481 | if self.crushingTime > 0 then |
482 | self.crushingTime = self.crushingTime - dt; |
483 | self.workFadeTime = math.min(self.maxWorkFadeTime, self.workFadeTime + dt); |
484 | else |
485 | self.workFadeTime = math.max(0, self.workFadeTime - dt); |
486 | end; |
487 | |
488 | if self.isClient and self.woodCrusherParticleSystems ~= nil then |
489 | for _,ps in pairs(self.woodCrusherParticleSystems) do |
490 | ParticleUtil.setEmittingState(ps, self.crushingTime > 0 ); |
491 | end |
492 | end; |
493 | |
494 | if self.isClient then |
495 | |
496 | if self.sampleWoodCrusherWork ~= nil then |
497 | local volume = self.sampleWoodCrusherWork.volume * self.workFadeTime/self.maxWorkFadeTime; |
498 | SoundUtil.setSampleVolume(self.sampleWoodCrusherWork, volume); |
499 | end |
500 | |
501 | if self.getIsActiveForSound ~= nil and self:getIsActiveForSound() then |
502 | if not SoundUtil.isSamplePlaying(self.sampleWoodCrusherStart, 1.8*dt) then --and not self.use3DSound then |
503 | SoundUtil.playSample(self.sampleWoodCrusherIdle, 0, 0, nil); |
504 | SoundUtil.playSample(self.sampleWoodCrusherWork, 0, 0, nil); |
505 | end |
506 | SoundUtil.stop3DSample(self.sampleWoodCrusherIdle) |
507 | SoundUtil.stop3DSample(self.sampleWoodCrusherWork) |
508 | else |
509 | SoundUtil.play3DSample(self.sampleWoodCrusherIdle) |
510 | SoundUtil.play3DSample(self.sampleWoodCrusherWork) |
511 | end |
512 | end; |
513 | end |
536 | function WoodCrusher.turnOffWoodCrusher(self) |
537 | if self.isServer then |
538 | for node in pairs(self.woodCrusherCrushNodes) do |
539 | self:crushSplitShape(node); |
540 | self.woodCrusherCrushNodes[node] = nil; |
541 | end |
542 | if self.woodCrusherMoveColNodes ~= nil then |
543 | for _,node in pairs(self.woodCrusherMoveColNodes) do |
544 | setFrictionVelocity(node, 0.0); |
545 | end |
546 | end |
547 | end |
548 | |
549 | if self.isClient then |
550 | self.workFadeTime = 0; |
551 | if self.woodCrusherParticleSystems ~= nil then |
552 | for _,ps in pairs(self.woodCrusherParticleSystems) do |
553 | ParticleUtil.setEmittingState(ps, false); |
554 | end |
555 | end; |
556 | WoodCrusher.onDeactivateSounds(self); |
557 | |
558 | SoundUtil.stopSample(self.sampleWoodCrusherStart); |
559 | SoundUtil.stopSample(self.sampleWoodCrusherIdle); |
560 | SoundUtil.stopSample(self.sampleWoodCrusherWork); |
561 | if self.getIsActiveForSound ~= nil and self:getIsActiveForSound() then |
562 | SoundUtil.playSample(self.sampleWoodCrusherStop, 1, 0, nil); |
563 | end |
564 | |
565 | SoundUtil.stop3DSample(self.sampleWoodCrusherIdle) |
566 | SoundUtil.stop3DSample(self.sampleWoodCrusherWork) |
567 | end; |
568 | end |