LUADOC - Farming Simulator 17

Printable Version

Script v1.4.4.0

Engine v7.0.0.2

Foundation Reference

WoodHarvester

Description
This is the specialization for wood harvesters
Functions

prerequisitesPresent

Description
Checks if all prerequisite specializations are loaded
Definition
prerequisitesPresent(table specializations)
Arguments
tablespecializationsspecializations
Return Values
booleanhasPrerequisitetrue if all prerequisite specializations are loaded
Code
21function WoodHarvester.prerequisitesPresent(specializations)
22 return SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations);
23end

load

Description
Called on loading
Definition
load(table savegame)
Arguments
tablesavegamesavegame
Code
28function 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
184end

postLoad

Description
Called after loading
Definition
postLoad(table savegame)
Arguments
tablesavegamesavegame
Code
189function WoodHarvester:postLoad(savegame)
190 if savegame ~= nil and not savegame.resetVehicles then
191 self.currentCutLength = Utils.getNoNil(getXMLFloat(savegame.xmlFile, savegame.key.."#currentCutLength"), self.cutLengthMin);
192 end
193end

delete

Description
Called on deleting
Definition
delete()
Code
197function WoodHarvester:delete()
198 if self.attachedSplitShapeJointIndex ~= nil then
199 removeJoint(self.attachedSplitShapeJointIndex);
200 self.attachedSplitShapeJointIndex = nil;
201 end;
202 if self.cutAttachHelperNode ~= nil then
203 delete(self.cutAttachHelperNode);
204 end
205
206 ParticleUtil.deleteParticleSystems(self.cutParticleSystems)
207 ParticleUtil.deleteParticleSystems(self.delimbParticleSystems)
208 SoundUtil.deleteSample(self.harvesterSounds.delimbSample);
209 SoundUtil.deleteSample(self.harvesterSounds.cutSample);
210end

readStream

Description
Called on client side on join
Definition
readStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
216function WoodHarvester:readStream(streamId, connection)
217 self.hasAttachedSplitShape = streamReadBool(streamId);
218 self.isAttachedSplitShapeMoving = streamReadBool(streamId);
219end

writeStream

Description
Called on server side on join
Definition
writeStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
225function WoodHarvester:writeStream(streamId, connection)
226 streamWriteBool(streamId, self.hasAttachedSplitShape);
227 streamWriteBool(streamId, self.isAttachedSplitShapeMoving);
228end

getSaveAttributesAndNodes

Description
Returns attributes and nodes to save
Definition
getSaveAttributesAndNodes(table nodeIdent)
Arguments
tablenodeIdentnode ident
Return Values
stringattributesattributes
stringnodesnodes
Code
241function WoodHarvester:getSaveAttributesAndNodes(nodeIdent)
242 local attributes = 'currentCutLength="'..self.currentCutLength..'"';
243 local nodes = "";
244 return attributes, nodes;
245end;

update

Description
Called on update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
256function 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;
505end

updateTick

Description
Called on update tick
Definition
updateTick(float dt)
Arguments
floatdttime since last call in ms
Code
510function 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
609end

draw

Description
Called on draw
Definition
draw()
Code
613function WoodHarvester:draw()
614 if self:getIsActiveForInput(true) and self:getIsTurnedOn() then
615 if self.cutNode ~= nil then
616 if self.hasAttachedSplitShape then
617 if not self.isAttachedSplitShapeMoving and self:getAnimationTime(self.cutAnimation.name) == 1 then
618 g_currentMission:addHelpButtonText(g_i18n:getText("action_woodHarvesterCut"), InputBinding.IMPLEMENT_EXTRA2, nil, GS_PRIO_HIGH);
619 end
620 elseif self.curSplitShape ~= nil then
621 g_currentMission:addHelpButtonText(g_i18n:getText("action_woodHarvesterCut"), InputBinding.IMPLEMENT_EXTRA2, nil, GS_PRIO_HIGH);
622 end
623 if not self.isAttachedSplitShapeMoving then
624 g_currentMission:addHelpButtonText(string.format(g_i18n:getText("action_woodHarvesterChangeCutLength"), string.format("%.1f",self.currentCutLength)), InputBinding.IMPLEMENT_EXTRA3, nil, GS_PRIO_NORMAL);
625 end;
626 if self.warnInvalidTreeRadius then
627 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_treeTooThick"), 1000);
628 elseif self.warnInvalidTree then
629 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_treeTypeNotSupported"), 1000);
630 end
631 end;
632 end
633end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
637function WoodHarvester:onDeactivate()
638 self.curSplitShape = nil;
639 self:setLastTreeDiameter(0);
640end

onDeactivateSounds

Description
Called on deactivating sounds
Definition
onDeactivateSounds()
Code
644function WoodHarvester:onDeactivateSounds()
645 if self.isClient then
646 if self.delimbParticleSystemsActive then
647 self.delimbParticleSystemsActive = false;
648 for _,ps in pairs(self.delimbParticleSystems) do
649 ParticleUtil.setEmittingState(ps, false);
650 end;
651 end;
652 if self.cutParticleSystemsActive then
653 self.cutParticleSystemsActive = false;
654 for _,ps in pairs(self.cutParticleSystems) do
655 ParticleUtil.setEmittingState(ps, false);
656 end;
657 end;
658 if self.harvesterSounds.cutSample.sound3D ~= nil then
659 setVisibility(self.harvesterSounds.cutSample.sound3D, false);
660 end;
661 if self.harvesterSounds.delimbSample.sound3D ~= nil then
662 setVisibility(self.harvesterSounds.delimbSample.sound3D, false);
663 end;
664 end;
665end

onTurnedOn

Description
Called on turn on
Definition
onTurnedOn(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
670function WoodHarvester:onTurnedOn(noEventSend)
671 self.playDelayedGrabAnimationTime = nil;
672 if self.grabAnimation.name ~= nil then
673 if self.grabAnimation.speedScale > 0 then
674 self:setAnimationStopTime(self.grabAnimation.name, 1);
675 else
676 self:setAnimationStopTime(self.grabAnimation.name, 0);
677 end
678 self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true);
679 end;
680 self.sampleMotor.pitchOffset = self.sampleMotor.pitchOffset * self.motorSoundPitchOffsetFactor;
681 self.sampleMotorRun.pitchOffset = self.sampleMotorRun.pitchOffset * self.motorRunSoundPitchOffsetFactor;
682
683 self:setLastTreeDiameter(0);
684 if self.treeCutLengthHud ~= nil then
685 VehicleHudUtils.setHudValue(self, self.treeCutLengthHud, self.currentCutLength*100, 9999);
686 end;
687end;

onTurnedOff

Description
Called on turn off
Definition
onTurnedOff(boolean noEventSend)
Arguments
booleannoEventSendno event send
Code
692function WoodHarvester:onTurnedOff(noEventSend)
693 self.curSplitShape = nil;
694 self:setLastTreeDiameter(0);
695 self.attachedSplitShape = nil;
696 self.hasAttachedSplitShape = false;
697 self.isAttachedSplitShapeMoving = false;
698 if self.attachedSplitShapeJointIndex ~= nil then
699 removeJoint(self.attachedSplitShapeJointIndex);
700 self.attachedSplitShapeJointIndex = nil;
701 end;
702 -- first open the cutter completely
703 self.playDelayedGrabAnimationTime = g_currentMission.time + 500;
704 if self.grabAnimation.name ~= nil and self.attachedSplitShape == nil then
705 if self.grabAnimation.speedScale > 0 then
706 self:setAnimationStopTime(self.grabAnimation.name, 1);
707 else
708 self:setAnimationStopTime(self.grabAnimation.name, 0);
709 end
710 self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true);
711 end;
712 self.sampleMotor.pitchOffset = self.sampleMotor.pitchOffsetBackup;
713 self.sampleMotorRun.pitchOffset = self.sampleMotorRun.pitchOffsetBackup;
714end

cutTree

Description
Cut tree
Definition
cutTree(float length, boolean noEventSend)
Arguments
floatlengthlength
booleannoEventSendno event send
Code
720function WoodHarvester:cutTree(length, noEventSend)
721 WoodHarvesterCutTreeEvent.sendEvent(self, length, noEventSend);
722 if self.isServer then
723 if length == 0 then
724
725 if self.attachedSplitShape ~= nil or self.curSplitShape ~= nil then
726 self.cutTimer = 100;
727 if self.cutAnimation.name ~= nil then
728 self:setAnimationTime(self.cutAnimation.name, 0, true);
729 self:playAnimation(self.cutAnimation.name, self.cutAnimation.speedScale, self:getAnimationTime(self.cutAnimation.name));
730 end;
731 end;
732
733 elseif length > 0 and self.attachedSplitShape ~= nil then
734
735 self.attachedSplitShapeTargetY = self.attachedSplitShapeLastCutY + length;
736 self.delimbDistance = length;
737
738 self:onDelimbTree(true);
739 if g_server ~= nil then
740 g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, true), nil, nil, self);
741 end;
742
743 end;
744 end;
745
746end;

onCutTree

Description
Called on tree cut
Definition
onCutTree(float radius)
Arguments
floatradiusradius
Code
751function WoodHarvester:onCutTree(radius)
752 if radius > 0 then
753
754 if self.isClient then
755 if self.grabAnimation.name ~= nil then
756 local targetAnimTime = math.min( 1.0, radius / self.treeSizeMeasure.rotMaxRadius );
757
758 if self.grabAnimation.speedScale < 0 then
759 targetAnimTime = 1.0 - targetAnimTime;
760 end
761 self:setAnimationStopTime(self.grabAnimation.name, targetAnimTime);
762
763 if targetAnimTime > self:getAnimationTime(self.grabAnimation.name) then
764 self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true);
765 else
766 self:playAnimation(self.grabAnimation.name, -self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true);
767 end;
768 end;
769
770 self:setLastTreeDiameter(2*radius);
771 end;
772
773 self.hasAttachedSplitShape = true;
774
775 else
776 if self.grabAnimation.name ~= nil then
777 if self.grabAnimation.speedScale > 0 then
778 self:setAnimationStopTime(self.grabAnimation.name, 1);
779 else
780 self:setAnimationStopTime(self.grabAnimation.name, 0);
781 end
782 self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true);
783 end;
784 self.hasAttachedSplitShape = false;
785 self.cutTimer = -1;
786 end;
787end;

onDelimbTree

Description
Called on delimbing
Definition
onDelimbTree(boolean state)
Arguments
booleanstatestate
Code
792function WoodHarvester:onDelimbTree(state)
793 if state then
794 self.isAttachedSplitShapeMoving = true;
795 else
796 self.isAttachedSplitShapeMoving = false;
797 self:cutTree(0);
798 end;
799end;

woodHarvesterSplitShapeCallback

Description
Split shape callback
Definition
woodHarvesterSplitShapeCallback(integer shape, boolean isBelow, boolean isAbove, float minY, float maxY, float minZ, float maxZ)
Arguments
integershapeshape
booleanisBelowis below
booleanisAboveis above
floatminYmin y split position
floatmaxYmax y split position
floatminZmin z split position
floatmaxZmax z split position
Code
810function 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
866end

setLastTreeDiameter

Description
Set last tree diameter
Definition
setLastTreeDiameter(float diameter)
Arguments
floatdiameterdiameter
Code
871function WoodHarvester:setLastTreeDiameter(diameter)
872 self.lastDiameter = diameter;
873 if self.treeDiameterHud ~= nil then
874 VehicleHudUtils.setHudValue(self, self.treeDiameterHud, self.lastDiameter*1000, 9999);
875 end;
876end;