28 | function WoodHarvester:load(savegame) |
29 | |
30 | self.woodHarvesterSplitShapeCallback = WoodHarvester.woodHarvesterSplitShapeCallback; |
31 | self.setLastTreeDiameter = WoodHarvester.setLastTreeDiameter; |
32 | |
33 | self.cutTree = SpecializationUtil.callSpecializationsFunction("cutTree"); |
34 | self.onCutTree = SpecializationUtil.callSpecializationsFunction("onCutTree"); |
35 | self.onDelimbTree = SpecializationUtil.callSpecializationsFunction("onDelimbTree"); |
36 | |
37 | self.curSplitShape = nil; |
38 | self.attachedSplitShape = nil; |
39 | self.hasAttachedSplitShape = false; |
40 | self.isAttachedSplitShapeMoving = false; |
41 | self.attachedSplitShapeX = 0; |
42 | self.attachedSplitShapeY = 0; |
43 | self.attachedSplitShapeZ = 0; |
44 | self.attachedSplitShapeTargetY = 0; |
45 | self.attachedSplitShapeLastCutY = 0; |
46 | self.attachedSplitShapeStartY = 0; |
47 | self.cutTimer = -1; |
48 | self.cutTimer = -1; |
49 | |
50 | self.cutNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#node")); |
51 | self.cutMaxRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#maxRadius"), 1); |
52 | self.cutSizeY = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#sizeY"), 1); |
53 | self.cutSizeZ = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#sizeZ"), 1); |
54 | self.cutAttachNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#attachNode")); |
55 | self.cutAttachReferenceNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#attachReferenceNode")); |
56 | self.cutAttachMoveSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#attachMoveSpeed"), 3)*0.001; |
57 | local cutReleasedComponentJointIndex = getXMLInt(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointIndex"); |
58 | if cutReleasedComponentJointIndex ~= nil then |
59 | self.cutReleasedComponentJoint = self.componentJoints[cutReleasedComponentJointIndex+1]; |
60 | self.cutReleasedComponentJointRotLimitX = 0; |
61 | self.cutReleasedComponentJointRotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001); |
62 | end |
63 | local cutReleasedComponentJoint2Index = getXMLInt(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJoint2Index"); |
64 | if cutReleasedComponentJoint2Index ~= nil then |
65 | self.cutReleasedComponentJoint2 = self.componentJoints[cutReleasedComponentJoint2Index+1]; |
66 | self.cutReleasedComponentJoint2RotLimitX = 0; |
67 | self.cutReleasedComponentJoint2RotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001); |
68 | end |
69 | |
70 | if self.cutAttachReferenceNode ~= nil and self.cutAttachNode ~= nil then |
71 | self.cutAttachHelperNode = createTransformGroup("helper"); |
72 | link(self.cutAttachReferenceNode, self.cutAttachHelperNode); |
73 | setTranslation(self.cutAttachHelperNode, 0,0,0); |
74 | setRotation(self.cutAttachHelperNode, 0,0,0); |
75 | end |
76 | |
77 | self.delimbNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.delimbNode#node")); |
78 | self.delimbSizeX = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeX"), 0.1); |
79 | self.delimbSizeY = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeY"), 1); |
80 | self.delimbSizeZ = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeZ"), 1); |
81 | self.delimbOnCut = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.woodHarvester.delimbNode#delimbOnCut"), false); |
82 | |
83 | self.cutLengthMin = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#min"), 1); |
84 | self.cutLengthMax = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#max"), 5); |
85 | self.cutLengthStep = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#step"), 0.5); |
86 | |
87 | self.cutParticleSystems = {}; |
88 | local i = 0; |
89 | while true do |
90 | local keyPS = string.format("vehicle.woodHarvester.cutParticleSystems.emitterShape(%d)", i); |
91 | if not hasXMLProperty(self.xmlFile, keyPS) then |
92 | break; |
93 | end; |
94 | local emitterShape = Utils.indexToObject(self.components, getXMLString(self.xmlFile, keyPS.."#node")); |
95 | local particleType = getXMLString(self.xmlFile, keyPS.."#particleType") |
96 | if emitterShape ~= nil then |
97 | local fillType = FillUtil.FILLTYPE_WOODCHIPS; |
98 | local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType) |
99 | if particleSystem ~= nil then |
100 | table.insert(self.cutParticleSystems, ParticleUtil.copyParticleSystem(self.xmlFile, keyPS, particleSystem, emitterShape)) |
101 | end |
102 | end |
103 | i = i + 1; |
104 | end; |
105 | |
106 | self.delimbParticleSystems = {}; |
107 | local i = 0; |
108 | while true do |
109 | local keyPS = string.format("vehicle.woodHarvester.delimbParticleSystems.emitterShape(%d)", i); |
110 | if not hasXMLProperty(self.xmlFile, keyPS) then |
111 | break; |
112 | end; |
113 | local emitterShape = Utils.indexToObject(self.components, getXMLString(self.xmlFile, keyPS.."#node")); |
114 | local particleType = getXMLString(self.xmlFile, keyPS.."#particleType") |
115 | if emitterShape ~= nil then |
116 | local fillType = FillUtil.FILLTYPE_WOODCHIPS; |
117 | local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType) |
118 | if particleSystem ~= nil then |
119 | table.insert(self.delimbParticleSystems, ParticleUtil.copyParticleSystem(self.xmlFile, keyPS, particleSystem, emitterShape)) |
120 | end |
121 | end |
122 | i = i + 1; |
123 | end; |
124 | |
125 | self.cutAnimation = {}; |
126 | self.cutAnimation.name = getXMLString(self.xmlFile, "vehicle.woodHarvester.cutAnimation#name"); |
127 | self.cutAnimation.speedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutAnimation#speedScale"), 1); |
128 | self.cutAnimation.cutTime = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutAnimation#cutTime"), 1); |
129 | |
130 | self.grabAnimation = {}; |
131 | self.grabAnimation.name = getXMLString(self.xmlFile, "vehicle.woodHarvester.grabAnimation#name"); |
132 | self.grabAnimation.speedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.grabAnimation#speedScale"), 1); |
133 | if self.grabAnimation.name ~= nil then |
134 | local speedScale = -self.grabAnimation.speedScale; |
135 | local stopTime = 0; |
136 | if self.grabAnimation.speedScale < 0 then |
137 | stopTime = 1; |
138 | end |
139 | self:playAnimation(self.grabAnimation.name, speedScale, nil, true); |
140 | self:setAnimationStopTime(self.grabAnimation.name, stopTime); |
141 | AnimatedVehicle.updateAnimationByName(self, self.grabAnimation.name, 99999999); |
142 | end |
143 | |
144 | self.forwardingWheels = {}; |
145 | local i = 0; |
146 | while true do |
147 | local wheelString = string.format("vehicle.woodHarvester.forwardingWheels.wheel(%d)",i); |
148 | if not hasXMLProperty(self.xmlFile, wheelString) then |
149 | break; |
150 | end; |
151 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, wheelString.."#index")); |
152 | local rotSpeed = Utils.getRadiansFromString(getXMLString(self.xmlFile, wheelString.."#rotSpeed"), 3); |
153 | local wheel = {node=node, rotSpeed=rotSpeed}; |
154 | table.insert(self.forwardingWheels, wheel); |
155 | i = i + 1; |
156 | end; |
157 | |
158 | self.treeSizeMeasure = {}; |
159 | self.treeSizeMeasure.node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.treeSizeMeasure#index")); |
160 | self.treeSizeMeasure.rotMaxRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.treeSizeMeasure#rotMaxRadius"), 1); |
161 | |
162 | self.harvesterSounds = {}; |
163 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.harvesterSounds.delimbSound#node")); |
164 | self.harvesterSounds.delimbSample = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.woodHarvester.harvesterSounds.delimbSound", nil, self.baseDirectory, node); |
165 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.harvesterSounds.delimbSound#node")); |
166 | self.harvesterSounds.cutSample = SoundUtil.loadSample(self.xmlFile, {}, "vehicle.woodHarvester.harvesterSounds.cutSound", nil, self.baseDirectory, node); |
167 | |
168 | self.motorSoundPitchOffsetFactor = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.harvesterSounds#motorSoundPitchOffset"), 1.0); |
169 | self.motorRunSoundPitchOffsetFactor = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.harvesterSounds#motorRunSoundPitchOffset"), 1.0); |
170 | |
171 | self.sampleMotor.pitchOffsetBackup = self.sampleMotor.pitchOffset; |
172 | self.sampleMotorRun.pitchOffsetBackup = self.sampleMotorRun.pitchOffset; |
173 | |
174 | self.warnInvalidTree = false; |
175 | self.warnInvalidTreeRadius = false; |
176 | |
177 | self.currentCutLength = self.cutLengthMin; |
178 | self.lastDiameter = 0; |
179 | |
180 | if self.isClient then |
181 | self.treeCutLengthHud = VehicleHudUtils.loadHud(self, self.xmlFile, "cutLength"); |
182 | self.treeDiameterHud = VehicleHudUtils.loadHud(self, self.xmlFile, "diameter"); |
183 | end |
184 | end |
256 | function WoodHarvester:update(dt) |
257 | if self:getIsActive() then |
258 | -- Verify that the split shapes still exist (possible that someone has cut them) |
259 | if self.isServer then |
260 | local lostShape = false; |
261 | if self.attachedSplitShape ~= nil then |
262 | if not entityExists(self.attachedSplitShape) then |
263 | self.attachedSplitShape = nil; |
264 | self.attachedSplitShapeJointIndex = nil; |
265 | self.isAttachedSplitShapeMoving = false; |
266 | self.cutTimer = -1; |
267 | lostShape = true; |
268 | end |
269 | elseif self.curSplitShape ~= nil then |
270 | if not entityExists(self.curSplitShape) then |
271 | self.curSplitShape = nil; |
272 | lostShape = true; |
273 | end |
274 | end |
275 | if lostShape then |
276 | self:onCutTree(0); |
277 | if g_server ~= nil then |
278 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
279 | end; |
280 | end; |
281 | end; |
282 | |
283 | -- Check for input |
284 | if self:getIsActiveForInput(true) and self:getIsTurnedOn() then |
285 | if self.cutNode ~= nil then |
286 | if self.hasAttachedSplitShape then |
287 | if not self.isAttachedSplitShapeMoving and self:getAnimationTime(self.cutAnimation.name) == 1 then |
288 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then |
289 | self:cutTree(self.currentCutLength); |
290 | end |
291 | end |
292 | elseif self.curSplitShape ~= nil then |
293 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then |
294 | self:cutTree(0); |
295 | end |
296 | end |
297 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then |
298 | if not self.isAttachedSplitShapeMoving then |
299 | self.currentCutLength = self.currentCutLength + self.cutLengthStep; |
300 | if self.currentCutLength > self.cutLengthMax+0.0001 then |
301 | self.currentCutLength = self.cutLengthMin; |
302 | end |
303 | if self.treeCutLengthHud ~= nil then |
304 | VehicleHudUtils.setHudValue(self, self.treeCutLengthHud, self.currentCutLength*100, 9999); |
305 | end; |
306 | end; |
307 | end |
308 | end; |
309 | end |
310 | |
311 | -- |
312 | if self.isServer and (self.attachedSplitShape ~= nil or self.curSplitShape ~= nil) then |
313 | |
314 | if self.cutTimer > 0 then |
315 | if self.cutAnimation.name ~= nil then |
316 | if self:getAnimationTime(self.cutAnimation.name) > self.cutAnimation.cutTime then |
317 | self.cutTimer = 0; |
318 | end; |
319 | else |
320 | self.cutTimer = math.max(self.cutTimer - dt, 0); |
321 | end; |
322 | end; |
323 | local readyToCut = self.cutTimer == 0; |
324 | |
325 | -- cut |
326 | if readyToCut then |
327 | self.cutTimer = -1; |
328 | |
329 | local x,y,z = getWorldTranslation(self.cutNode); |
330 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
331 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
332 | local newTreeCut = false; |
333 | |
334 | local currentSplitShape; |
335 | if self.attachedSplitShapeJointIndex ~= nil then |
336 | removeJoint(self.attachedSplitShapeJointIndex); |
337 | self.attachedSplitShapeJointIndex = nil; |
338 | currentSplitShape = self.attachedSplitShape; |
339 | self.attachedSplitShape = nil; |
340 | else |
341 | currentSplitShape = self.curSplitShape; |
342 | self.curSplitShape = nil; |
343 | newTreeCut = true; |
344 | end |
345 | |
346 | -- remember split type name for later (achievement) |
347 | local splitTypeName = ""; |
348 | local splitType = SplitUtil.splitTypes[getSplitType(currentSplitShape)]; |
349 | if splitType ~= nil then |
350 | splitTypeName = splitType.name; |
351 | end; |
352 | |
353 | if self.delimbOnCut then |
354 | local xD,yD,zD = getWorldTranslation(self.delimbNode); |
355 | local nxD,nyD,nzD = localDirectionToWorld(self.delimbNode, 1,0,0); |
356 | local yxD,yyD,yzD = localDirectionToWorld(self.delimbNode, 0,1,0); |
357 | local vx,vy,vz = x-xD,y-yD,z-zD; |
358 | local sizeX = Utils.vector3Length(vx,vy,vz); |
359 | removeSplitShapeAttachments(currentSplitShape, xD+vx*0.5,yD+vy*0.5,zD+vz*0.5, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*0.7+self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
360 | end; |
361 | |
362 | self.attachedSplitShape = nil; |
363 | self.curSplitShape = nil; |
364 | splitShape(currentSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ, "woodHarvesterSplitShapeCallback", self); |
365 | |
366 | if self.attachedSplitShape == nil then |
367 | self:onCutTree(0); |
368 | if g_server ~= nil then |
369 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
370 | end; |
371 | else |
372 | if self.delimbOnCut then |
373 | local xD,yD,zD = getWorldTranslation(self.delimbNode); |
374 | local nxD,nyD,nzD = localDirectionToWorld(self.delimbNode, 1,0,0); |
375 | local yxD,yyD,yzD = localDirectionToWorld(self.delimbNode, 0,1,0); |
376 | local vx,vy,vz = x-xD,y-yD,z-zD; |
377 | local sizeX = Utils.vector3Length(vx,vy,vz); |
378 | removeSplitShapeAttachments(self.attachedSplitShape, xD+vx*3,yD+vy*3,zD+vz*3, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*3+self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
379 | end; |
380 | end; |
381 | |
382 | if newTreeCut then |
383 | -- increase tree cut counter for achievements |
384 | g_currentMission.missionStats:updateStats("cutTreeCount", 1); |
385 | |
386 | -- update the types of trees cut so far (achievement) |
387 | if splitTypeName ~= "" then |
388 | g_currentMission.missionStats:updateTreeTypesCut(splitTypeName); |
389 | end; |
390 | end; |
391 | |
392 | end; |
393 | |
394 | -- delimb |
395 | if self.attachedSplitShape ~= nil and self.isAttachedSplitShapeMoving then |
396 | if self.delimbNode ~= nil then |
397 | local x,y,z = getWorldTranslation(self.delimbNode); |
398 | local nx,ny,nz = localDirectionToWorld(self.delimbNode, 1,0,0); |
399 | local yx,yy,yz = localDirectionToWorld(self.delimbNode, 0,1,0); |
400 | |
401 | removeSplitShapeAttachments(self.attachedSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
402 | end |
403 | |
404 | if self.cutNode ~= nil and self.attachedSplitShapeJointIndex ~= nil then |
405 | local x,y,z = getWorldTranslation(self.cutAttachReferenceNode); |
406 | local nx,ny,nz = localDirectionToWorld(self.cutAttachReferenceNode, 0,1,0); |
407 | local _, lengthRem = getSplitShapePlaneExtents(self.attachedSplitShape, x,y,z, nx,ny,nz); |
408 | |
409 | if lengthRem == nil or lengthRem <= 0.1 then |
410 | |
411 | -- end of tree |
412 | removeJoint(self.attachedSplitShapeJointIndex); |
413 | self.attachedSplitShapeJointIndex = nil; |
414 | self.attachedSplitShape = nil; |
415 | |
416 | self:onDelimbTree(false); |
417 | if g_server ~= nil then |
418 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self); |
419 | end; |
420 | |
421 | self:onCutTree(0); |
422 | if g_server ~= nil then |
423 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
424 | end; |
425 | else |
426 | self.attachedSplitShapeY = self.attachedSplitShapeY + self.cutAttachMoveSpeed*dt; |
427 | |
428 | if self.attachedSplitShapeY >= self.attachedSplitShapeTargetY then |
429 | self.attachedSplitShapeY = self.attachedSplitShapeTargetY; |
430 | self:onDelimbTree(false); |
431 | if g_server ~= nil then |
432 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self); |
433 | end; |
434 | end |
435 | if self.attachedSplitShapeJointIndex ~= nil then |
436 | local x,y,z = localToWorld(self.cutNode, 0.3,0,0); |
437 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
438 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
439 | local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
440 | if shape == self.attachedSplitShape then |
441 | local treeCenterX,treeCenterY,treeCenterZ = localToWorld(self.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5); |
442 | self.attachedSplitShapeX, _, self.attachedSplitShapeZ = worldToLocal(self.attachedSplitShape, treeCenterX,treeCenterY,treeCenterZ); |
443 | self:setLastTreeDiameter((maxY-minY + maxZ-minZ)*0.5); |
444 | end; |
445 | local x,y,z = localToWorld(self.attachedSplitShape, self.attachedSplitShapeX, self.attachedSplitShapeY, self.attachedSplitShapeZ); |
446 | setJointPosition(self.attachedSplitShapeJointIndex, 1, x,y,z); |
447 | end; |
448 | end |
449 | end |
450 | end; |
451 | end; |
452 | end; |
453 | |
454 | -- PS and sound for cut and delimb |
455 | if self.isClient then |
456 | if self:getIsActive() then |
457 | -- cut |
458 | if self.cutAnimation.name ~= nil then |
459 | if self:getIsAnimationPlaying(self.cutAnimation.name) and self:getAnimationTime(self.cutAnimation.name) < self.cutAnimation.cutTime then |
460 | self.cutParticleSystemsActive = true; |
461 | for _,ps in pairs(self.cutParticleSystems) do |
462 | ParticleUtil.setEmittingState(ps, true); |
463 | end; |
464 | if self.harvesterSounds.cutSample ~= nil then |
465 | SoundUtil.play3DSample(self.harvesterSounds.cutSample); |
466 | end; |
467 | else |
468 | self.cutParticleSystemsActive = false; |
469 | for _,ps in pairs(self.cutParticleSystems) do |
470 | ParticleUtil.setEmittingState(ps, false); |
471 | end; |
472 | if self.harvesterSounds.cutSample ~= nil then |
473 | SoundUtil.stop3DSample(self.harvesterSounds.cutSample); |
474 | end; |
475 | end; |
476 | end; |
477 | -- delimb |
478 | if self.isAttachedSplitShapeMoving then |
479 | if #self.forwardingWheels > 0 then |
480 | local f = dt/1000; |
481 | for _,wheel in pairs(self.forwardingWheels) do |
482 | if wheel.node ~= nil then |
483 | rotate(wheel.node, wheel.rotSpeed[1]*f, wheel.rotSpeed[2]*f, wheel.rotSpeed[3]*f); |
484 | end; |
485 | end; |
486 | end; |
487 | if self.harvesterSounds.delimbSample ~= nil then |
488 | SoundUtil.play3DSample(self.harvesterSounds.delimbSample); |
489 | end; |
490 | for _,ps in pairs(self.delimbParticleSystems) do |
491 | self.delimbParticleSystemsActive = true; |
492 | ParticleUtil.setEmittingState(ps, true); |
493 | end; |
494 | else |
495 | if self.harvesterSounds.delimbSample then |
496 | SoundUtil.stop3DSample(self.harvesterSounds.delimbSample); |
497 | end; |
498 | for _,ps in pairs(self.delimbParticleSystems) do |
499 | ParticleUtil.setEmittingState(ps, false); |
500 | end; |
501 | end; |
502 | else -- turn all off, done in onDeactivateSounds() |
503 | end |
504 | end; |
505 | end |
510 | function WoodHarvester:updateTick(dt) |
511 | if self:getIsActive() then |
512 | self.warnInvalidTree = false; |
513 | self.warnInvalidTreeRadius = false; |
514 | |
515 | if self:getIsTurnedOn() then |
516 | if self.attachedSplitShape == nil and self.cutNode ~= nil then |
517 | local x,y,z = getWorldTranslation(self.cutNode); |
518 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
519 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
520 | |
521 | |
522 | if self.curSplitShape == nil and (self.cutReleasedComponentJoint == nil or self.cutReleasedComponentJointRotLimitX == 0) then |
523 | |
524 | local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
525 | if shape ~= 0 then |
526 | local splitType = SplitUtil.splitTypes[getSplitType(shape)]; |
527 | if splitType == nil or not splitType.allowsWoodHarvester then |
528 | self.warnInvalidTree = true; |
529 | else |
530 | local treeDx,treeDy,treeDz = localDirectionToWorld(shape, 0,1,0); -- wood harvester trees always grow in the y direction |
531 | local cosTreeAngle = Utils.dotProduct(nx,ny,nz, treeDx,treeDy,treeDz); |
532 | -- Only allow cutting if the cut header is approximately parallel to the tree (70° offset) |
533 | if cosTreeAngle >= 0.35 then |
534 | local radius = math.max(maxY-minY, maxZ-minZ)*0.5 * cosTreeAngle; |
535 | if radius > self.cutMaxRadius then |
536 | self.warnInvalidTreeRadius = true; |
537 | else |
538 | self:setLastTreeDiameter(math.max(maxY-minY, maxZ-minZ)); |
539 | self.curSplitShape = shape; |
540 | end |
541 | end |
542 | end |
543 | end |
544 | end |
545 | |
546 | if self.curSplitShape ~= nil then |
547 | local minY,maxY, minZ,maxZ = testSplitShape(self.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ) |
548 | if minY == nil then |
549 | self.curSplitShape = nil; |
550 | else |
551 | -- check if cut would be below y=0 (tree CoSy) |
552 | local cutTooLow = false; |
553 | local _,y,_ = localToLocal(self.cutNode, self.curSplitShape, 0,minY,minZ); |
554 | cutTooLow = cutTooLow or y < 0.01; |
555 | local _,y,_ = localToLocal(self.cutNode, self.curSplitShape, 0,minY,maxZ); |
556 | cutTooLow = cutTooLow or y < 0.01; |
557 | local _,y,_ = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,minZ); |
558 | cutTooLow = cutTooLow or y < 0.01; |
559 | local _,y,_ = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,maxZ); |
560 | cutTooLow = cutTooLow or y < 0.01; |
561 | if cutTooLow then |
562 | self.curSplitShape = nil; |
563 | end; |
564 | end |
565 | end |
566 | |
567 | if self.curSplitShape == nil and self.cutTimer > -1 then |
568 | self:onCutTree(0); |
569 | if g_server ~= nil then |
570 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
571 | end; |
572 | end; |
573 | |
574 | end |
575 | end |
576 | |
577 | if self.isServer then |
578 | if self.attachedSplitShape == nil then |
579 | if self.cutReleasedComponentJoint ~= nil and self.cutReleasedComponentJointRotLimitX ~= 0 then |
580 | self.cutReleasedComponentJointRotLimitX = math.max(0, self.cutReleasedComponentJointRotLimitX - self.cutReleasedComponentJointRotLimitXSpeed*dt); |
581 | setJointRotationLimit(self.cutReleasedComponentJoint.jointIndex, 0, true, 0, self.cutReleasedComponentJointRotLimitX); |
582 | end; |
583 | if self.cutReleasedComponentJoint2 ~= nil and self.cutReleasedComponentJoint2RotLimitX ~= 0 then |
584 | self.cutReleasedComponentJoint2RotLimitX = math.max(self.cutReleasedComponentJoint2RotLimitX-self.cutReleasedComponentJoint2RotLimitXSpeed*dt, 0); |
585 | setJointRotationLimit(self.cutReleasedComponentJoint2.jointIndex, 0, true, -self.cutReleasedComponentJoint2RotLimitX, self.cutReleasedComponentJoint2RotLimitX); |
586 | end; |
587 | end |
588 | end; |
589 | end |
590 | |
591 | if self.isServer then |
592 | if self.playDelayedGrabAnimationTime ~= nil then |
593 | if self.playDelayedGrabAnimationTime < g_currentMission.time then |
594 | self.playDelayedGrabAnimationTime = nil; |
595 | if self:getAnimationTime(self.grabAnimation.name) > 0 then |
596 | if self.grabAnimation.name ~= nil and self.attachedSplitShape == nil then |
597 | if self.grabAnimation.speedScale > 0 then |
598 | self:setAnimationStopTime(self.grabAnimation.name, 0); |
599 | else |
600 | self:setAnimationStopTime(self.grabAnimation.name, 1); |
601 | end |
602 | self:playAnimation(self.grabAnimation.name, -self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), false); |
603 | end; |
604 | end; |
605 | end; |
606 | end; |
607 | end; |
608 | |
609 | end |
810 | function WoodHarvester:woodHarvesterSplitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ) |
811 | |
812 | if self.attachedSplitShape == nil and isAbove and not isBelow and self.cutAttachNode ~= nil and self.cutAttachReferenceNode ~= nil then |
813 | self.attachedSplitShape = shape; |
814 | |
815 | -- Current tree center (mid of cut area) |
816 | local treeCenterX,treeCenterY,treeCenterZ = localToWorld(self.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5); |
817 | |
818 | -- Target tree center (half tree size in front of the reference node) |
819 | local x,y,z = localToWorld(self.cutAttachReferenceNode, 0, 0, (maxZ-minZ)*0.5); |
820 | |
821 | local dx,dy,dz = localDirectionToWorld(shape, 0,0,1); |
822 | |
823 | local upx,upy,upz = localDirectionToWorld(self.cutAttachReferenceNode, 0,1,0); |
824 | local sideX,sideY,sizeZ = Utils.crossProduct(upx,upy,upz, dx,dy,dz); |
825 | dx,dy,dz = Utils.crossProduct(sideX,sideY,sizeZ, upx,upy,upz); -- Note: we want the up axis to be exact, thus orthogonalize the direction here |
826 | Utils.setWorldDirection(self.cutAttachHelperNode, dx,dy,dz, upx,upy,upz, 2); |
827 | |
828 | local constr = JointConstructor:new(); |
829 | constr:setActors(self.cutAttachNode, shape); |
830 | -- Note: we assume that the direction of the tree is equal to the y axis |
831 | constr:setJointTransforms(self.cutAttachHelperNode, shape); |
832 | constr:setJointWorldPositions(x,y,z, treeCenterX,treeCenterY,treeCenterZ); |
833 | |
834 | constr:setRotationLimit(0, 0, 0); |
835 | constr:setRotationLimit(1, 0, 0); |
836 | constr:setRotationLimit(2, 0, 0); |
837 | |
838 | constr:setEnableCollision(false); |
839 | |
840 | self.attachedSplitShapeJointIndex = constr:finalize(); |
841 | |
842 | if self.cutReleasedComponentJoint ~= nil then |
843 | self.cutReleasedComponentJointRotLimitX = math.pi*0.9; |
844 | if self.cutReleasedComponentJoint.jointIndex ~= 0 then |
845 | setJointRotationLimit(self.cutReleasedComponentJoint.jointIndex, 0, true, 0, self.cutReleasedComponentJointRotLimitX); |
846 | end |
847 | end |
848 | if self.cutReleasedComponentJoint2 ~= nil then |
849 | self.cutReleasedComponentJoint2RotLimitX = math.pi*0.9; |
850 | if self.cutReleasedComponentJoint2.jointIndex ~= 0 then |
851 | setJointRotationLimit(self.cutReleasedComponentJoint2.jointIndex, 0, true, -self.cutReleasedComponentJoint2RotLimitX, self.cutReleasedComponentJoint2RotLimitX); |
852 | end |
853 | end |
854 | |
855 | self.attachedSplitShapeX, self.attachedSplitShapeY, self.attachedSplitShapeZ = worldToLocal(shape, treeCenterX,treeCenterY,treeCenterZ); |
856 | self.attachedSplitShapeLastCutY = self.attachedSplitShapeY; |
857 | self.attachedSplitShapeStartY = self.attachedSplitShapeY; |
858 | |
859 | local radius = ((maxY - minY) + (maxZ - minZ)) / 4; |
860 | self:onCutTree(radius); |
861 | if g_server ~= nil then |
862 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, radius), nil, nil, self); |
863 | end; |
864 | else |
865 | end |
866 | end |