LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

WoodHarvester

Description
Specialization for wood harvesters including grabbing, felling, delimbing and cutting of certrain types of trees
Functions

actionEventCutTree

Description
Definition
actionEventCutTree()
Code
833function WoodHarvester.actionEventCutTree(self, actionName, inputValue, callbackState, isAnalog)
834 local spec = self.spec_woodHarvester
835
836 if spec.hasAttachedSplitShape then
837 if not spec.isAttachedSplitShapeMoving and self:getAnimationTime(spec.cutAnimation.name) == 1 then
838 self:cutTree(spec.currentCutLength)
839 end
840 elseif spec.curSplitShape ~= nil then
841 self:cutTree(0)
842 end
843end

actionEventSetCutlength

Description
Definition
actionEventSetCutlength()
Code
847function WoodHarvester.actionEventSetCutlength(self, actionName, inputValue, callbackState, isAnalog)
848 local spec = self.spec_woodHarvester
849
850 if not spec.isAttachedSplitShapeMoving then
851 spec.currentCutLength = spec.currentCutLength + spec.cutLengthStep
852 if spec.currentCutLength > spec.cutLengthMax + 0.0001 then
853 spec.currentCutLength = spec.cutLengthMin
854 end
855 end
856end

cutTree

Description
Definition
cutTree()
Code
681function WoodHarvester:cutTree(length, noEventSend)
682 local spec = self.spec_woodHarvester
683 WoodHarvesterCutTreeEvent.sendEvent(self, length, noEventSend)
684 if self.isServer then
685 if length == 0 then
686 if spec.attachedSplitShape ~= nil or spec.curSplitShape ~= nil then
687 spec.cutTimer = 100
688 if spec.cutAnimation.name ~= nil then
689 self:setAnimationTime(spec.cutAnimation.name, 0, true)
690 self:playAnimation(spec.cutAnimation.name, spec.cutAnimation.speedScale, self:getAnimationTime(spec.cutAnimation.name))
691 end
692 end
693 elseif length > 0 and spec.attachedSplitShape ~= nil then
694 spec.attachedSplitShapeTargetY = spec.attachedSplitShapeLastCutY + length
695
696 self:onDelimbTree(true)
697 if g_server ~= nil then
698 g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, true), nil, nil, self)
699 end
700 end
701 end
702end

getCanBeSelected

Description
Definition
getCanBeSelected()
Code
827function WoodHarvester:getCanBeSelected(superFunc)
828 return true
829end

onCutTree

Description
Definition
onCutTree()
Code
706function WoodHarvester:onCutTree(radius)
707 local spec = self.spec_woodHarvester
708 if radius > 0 then
709 if self.isClient then
710 if spec.grabAnimation.name ~= nil then
711 local targetAnimTime = math.min( 1.0, radius / spec.treeSizeMeasure.rotMaxRadius )
712
713 if spec.grabAnimation.speedScale < 0 then
714 targetAnimTime = 1.0 - targetAnimTime
715 end
716 self:setAnimationStopTime(spec.grabAnimation.name, targetAnimTime)
717
718 if targetAnimTime > self:getAnimationTime(spec.grabAnimation.name) then
719 self:playAnimation(spec.grabAnimation.name, spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), true)
720 else
721 self:playAnimation(spec.grabAnimation.name, -spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), true)
722 end
723 end
724
725 self:setLastTreeDiameter(2*radius)
726 end
727
728 spec.hasAttachedSplitShape = true
729 else
730 if spec.grabAnimation.name ~= nil then
731 if spec.grabAnimation.speedScale > 0 then
732 self:setAnimationStopTime(spec.grabAnimation.name, 1)
733 else
734 self:setAnimationStopTime(spec.grabAnimation.name, 0)
735 end
736 self:playAnimation(spec.grabAnimation.name, spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), true)
737 end
738 spec.hasAttachedSplitShape = false
739 spec.cutTimer = -1
740 end
741end

onDeactivate

Description
Definition
onDeactivate()
Code
622function WoodHarvester:onDeactivate()
623 local spec = self.spec_woodHarvester
624 spec.curSplitShape = nil
625 self:setLastTreeDiameter(0)
626end

onDelete

Description
Definition
onDelete()
Code
197function WoodHarvester:onDelete()
198 local spec = self.spec_woodHarvester
199 if spec.attachedSplitShapeJointIndex ~= nil then
200 removeJoint(spec.attachedSplitShapeJointIndex)
201 spec.attachedSplitShapeJointIndex = nil
202 end
203 if spec.cutAttachHelperNode ~= nil then
204 delete(spec.cutAttachHelperNode)
205 end
206
207 if self.isClient then
208 g_effectManager:deleteEffects(spec.cutEffects)
209 g_effectManager:deleteEffects(spec.delimbEffects)
210 g_soundManager:deleteSamples(spec.samples)
211 g_animationManager:deleteAnimations(spec.forwardingNodes)
212 end
213end

onDelimbTree

Description
Definition
onDelimbTree()
Code
745function WoodHarvester:onDelimbTree(state)
746 local spec = self.spec_woodHarvester
747 if state then
748 spec.isAttachedSplitShapeMoving = true
749 else
750 spec.isAttachedSplitShapeMoving = false
751 self:cutTree(0)
752 end
753end

onDraw

Description
Definition
onDraw()
Code
586function WoodHarvester:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
587 local spec = self.spec_woodHarvester
588 if isActiveForInputIgnoreSelection and isSelected and self:getIsTurnedOn() then
589 if spec.cutNode ~= nil then
590 if spec.warnInvalidTreeRadius then
591 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_treeTooThick"), 1000)
592 elseif spec.warnInvalidTree then
593 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_treeTypeNotSupported"), 1000)
594 elseif spec.warnTreeNotOwned then
595 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 1000)
596 end
597 end
598 end
599end

onLoad

Description
Definition
onLoad()
Code
64function WoodHarvester:onLoad(savegame)
65 local spec = self.spec_woodHarvester
66
67 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.delimbSound", "vehicle.woodHarvester.sounds.delimb") --FS17 to FS19
68 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.cutSound", "vehicle.woodHarvester.sounds.cut") --FS17 to FS19
69 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.treeSizeMeasure#index", "vehicle.woodHarvester.treeSizeMeasure#node") --FS17 to FS19
70 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.forwardingWheels.wheel(0)", "vehicle.woodHarvester.forwardingNodes.animationNode") --FS17 to FS19
71 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.cutParticleSystems", "vehicle.woodHarvester.cutEffects") --FS17 to FS19
72 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.woodHarvester.delimbParticleSystems", "vehicle.woodHarvester.delimbEffects") --FS17 to FS19
73
74 spec.curSplitShape = nil
75 spec.attachedSplitShape = nil
76 spec.hasAttachedSplitShape = false
77 spec.isAttachedSplitShapeMoving = false
78 spec.attachedSplitShapeX = 0
79 spec.attachedSplitShapeY = 0
80 spec.attachedSplitShapeZ = 0
81 spec.attachedSplitShapeTargetY = 0
82 spec.attachedSplitShapeLastCutY = 0
83 spec.attachedSplitShapeStartY = 0
84 spec.cutTimer = -1
85
86 spec.cutNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#node"), self.i3dMappings)
87 spec.cutMaxRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#maxRadius"), 1)
88 spec.cutSizeY = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#sizeY"), 1)
89 spec.cutSizeZ = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#sizeZ"), 1)
90 spec.cutAttachNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#attachNode"), self.i3dMappings)
91 spec.cutAttachReferenceNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.cutNode#attachReferenceNode"), self.i3dMappings)
92 spec.cutAttachMoveSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#attachMoveSpeed"), 3)*0.001
93 local cutReleasedComponentJointIndex = getXMLInt(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointIndex")
94 if cutReleasedComponentJointIndex ~= nil then
95 spec.cutReleasedComponentJoint = self.componentJoints[cutReleasedComponentJointIndex]
96 spec.cutReleasedComponentJointRotLimitX = 0
97 spec.cutReleasedComponentJointRotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001)
98 end
99 local cutReleasedComponentJoint2Index = getXMLInt(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJoint2Index")
100 if cutReleasedComponentJoint2Index ~= nil then
101 spec.cutReleasedComponentJoint2 = self.componentJoints[cutReleasedComponentJoint2Index]
102 spec.cutReleasedComponentJoint2RotLimitX = 0
103 spec.cutReleasedComponentJoint2RotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001)
104 end
105
106 if spec.cutAttachReferenceNode ~= nil and spec.cutAttachNode ~= nil then
107 spec.cutAttachHelperNode = createTransformGroup("helper")
108 link(spec.cutAttachReferenceNode, spec.cutAttachHelperNode)
109 setTranslation(spec.cutAttachHelperNode, 0,0,0)
110 setRotation(spec.cutAttachHelperNode, 0,0,0)
111 end
112
113 spec.delimbNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.delimbNode#node"), self.i3dMappings)
114 spec.delimbSizeX = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeX"), 0.1)
115 spec.delimbSizeY = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeY"), 1)
116 spec.delimbSizeZ = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.delimbNode#sizeZ"), 1)
117 spec.delimbOnCut = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.woodHarvester.delimbNode#delimbOnCut"), false)
118
119 spec.cutLengthMin = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#min"), 1)
120 spec.cutLengthMax = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#max"), 5)
121 spec.cutLengthStep = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutLengths#step"), 0.5)
122
123 if self.isClient then
124 spec.cutEffects = g_effectManager:loadEffect(self.xmlFile, "vehicle.woodHarvester.cutEffects", self.components, self, self.i3dMappings)
125 spec.delimbEffects = g_effectManager:loadEffect(self.xmlFile, "vehicle.woodHarvester.delimbEffects", self.components, self, self.i3dMappings)
126 spec.forwardingNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.woodHarvester.forwardingNodes", self.components, self, self.i3dMappings)
127
128 spec.samples = {}
129 spec.samples.cut = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.woodHarvester.sounds", "cut", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
130 spec.samples.delimb = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.woodHarvester.sounds", "delimb", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
131 spec.isCutSamplePlaying = false
132 spec.isDelimbSamplePlaying = false
133 end
134
135 spec.cutAnimation = {}
136 spec.cutAnimation.name = getXMLString(self.xmlFile, "vehicle.woodHarvester.cutAnimation#name")
137 spec.cutAnimation.speedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutAnimation#speedScale"), 1)
138 spec.cutAnimation.cutTime = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.cutAnimation#cutTime"), 1)
139
140 spec.grabAnimation = {}
141 spec.grabAnimation.name = getXMLString(self.xmlFile, "vehicle.woodHarvester.grabAnimation#name")
142 spec.grabAnimation.speedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.grabAnimation#speedScale"), 1)
143 if spec.grabAnimation.name ~= nil then
144 local speedScale = -spec.grabAnimation.speedScale
145 local stopTime = 0
146 if spec.grabAnimation.speedScale < 0 then
147 stopTime = 1
148 end
149 self:playAnimation(spec.grabAnimation.name, speedScale, nil, true)
150 self:setAnimationStopTime(spec.grabAnimation.name, stopTime)
151 AnimatedVehicle.updateAnimationByName(self, spec.grabAnimation.name, 99999999)
152 end
153
154 spec.treeSizeMeasure = {}
155 spec.treeSizeMeasure.node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.woodHarvester.treeSizeMeasure#node"), self.i3dMappings)
156 spec.treeSizeMeasure.rotMaxRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.woodHarvester.treeSizeMeasure#rotMaxRadius"), 1)
157
158 spec.warnInvalidTree = false
159 spec.warnInvalidTreeRadius = false
160 spec.warnTreeNotOwned = false
161
162 spec.currentCutLength = spec.cutLengthMin
163 spec.lastDiameter = 0
164
165 if self.loadDashboardsFromXML ~= nil then
166 self:loadDashboardsFromXML(self.xmlFile, "vehicle.woodHarvester.dashboards", {valueTypeToLoad = "cutLength",
167 valueObject = spec,
168 valueFunc = function(spec)
169 return spec.currentCutLength * 100
170 end})
171
172 self:loadDashboardsFromXML(self.xmlFile, "vehicle.woodHarvester.dashboards", {valueTypeToLoad = "curCutLength",
173 valueObject = spec,
174 valueFunc = function(spec)
175 return math.abs(spec.currentCutLength - (spec.attachedSplitShapeTargetY - spec.attachedSplitShapeY)) * 100
176 end})
177
178 self:loadDashboardsFromXML(self.xmlFile, "vehicle.woodHarvester.dashboards", {valueTypeToLoad = "diameter",
179 valueObject = spec,
180 valueFunc = function(spec)
181 return spec.lastDiameter * 1000
182 end})
183 end
184end

onPostLoad

Description
Definition
onPostLoad()
Code
188function WoodHarvester:onPostLoad(savegame)
189 local spec = self.spec_woodHarvester
190 if savegame ~= nil and not savegame.resetVehicles then
191 spec.currentCutLength = Utils.getNoNil(getXMLFloat(savegame.xmlFile, savegame.key..".woodHarvester#currentCutLength"), spec.cutLengthMin)
192 end
193end

onReadStream

Description
Definition
onReadStream()
Code
225function WoodHarvester:onReadStream(streamId, connection)
226 local spec = self.spec_woodHarvester
227 spec.hasAttachedSplitShape = streamReadBool(streamId)
228 spec.isAttachedSplitShapeMoving = streamReadBool(streamId)
229end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
604function WoodHarvester:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
605 if self.isClient then
606 local spec = self.spec_woodHarvester
607 self:clearActionEventsTable(spec.actionEvents)
608
609 if isActiveForInputIgnoreSelection then
610 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.IMPLEMENT_EXTRA2, self, WoodHarvester.actionEventCutTree, false, true, false, true, nil)
611 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_HIGH)
612 g_inputBinding:setActionEventText(actionEventId, g_i18n:getText("action_woodHarvesterCut"))
613
614 _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.IMPLEMENT_EXTRA3, self, WoodHarvester.actionEventSetCutlength, false, true, false, true, nil)
615 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW)
616 end
617 end
618end

onTurnedOff

Description
Definition
onTurnedOff()
Code
647function WoodHarvester:onTurnedOff()
648 local spec = self.spec_woodHarvester
649 spec.curSplitShape = nil
650 self:setLastTreeDiameter(0)
651 spec.attachedSplitShape = nil
652 spec.hasAttachedSplitShape = false
653 spec.isAttachedSplitShapeMoving = false
654 if spec.attachedSplitShapeJointIndex ~= nil then
655 removeJoint(spec.attachedSplitShapeJointIndex)
656 spec.attachedSplitShapeJointIndex = nil
657 end
658
659 -- first open the cutter completely
660 self.playDelayedGrabAnimationTime = g_currentMission.time + 500
661 if spec.grabAnimation.name ~= nil and spec.attachedSplitShape == nil then
662 if spec.grabAnimation.speedScale > 0 then
663 self:setAnimationStopTime(spec.grabAnimation.name, 1)
664 else
665 self:setAnimationStopTime(spec.grabAnimation.name, 0)
666 end
667 self:playAnimation(spec.grabAnimation.name, spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), true)
668 end
669
670 if self.isClient then
671 g_effectManager:stopEffects(spec.delimbEffects)
672 g_effectManager:stopEffects(spec.cutEffects)
673 g_soundManager:stopSamples(spec.samples)
674 spec.isCutSamplePlaying = false
675 spec.isDelimbSamplePlaying = false
676 end
677end

onTurnedOn

Description
Definition
onTurnedOn()
Code
630function WoodHarvester:onTurnedOn()
631 local spec = self.spec_woodHarvester
632 self.playDelayedGrabAnimationTime = nil
633 if spec.grabAnimation.name ~= nil then
634 if spec.grabAnimation.speedScale > 0 then
635 self:setAnimationStopTime(spec.grabAnimation.name, 1)
636 else
637 self:setAnimationStopTime(spec.grabAnimation.name, 0)
638 end
639 self:playAnimation(spec.grabAnimation.name, spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), true)
640 end
641
642 self:setLastTreeDiameter(0)
643end

onUpdate

Description
Definition
onUpdate()
Code
241function WoodHarvester:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
242 local spec = self.spec_woodHarvester
243
244 -- Verify that the split shapes still exist (possible that someone has cut them)
245 if self.isServer then
246 local lostShape = false
247 if spec.attachedSplitShape ~= nil then
248 if not entityExists(spec.attachedSplitShape) then
249 spec.attachedSplitShape = nil
250 spec.attachedSplitShapeJointIndex = nil
251 spec.isAttachedSplitShapeMoving = false
252 spec.cutTimer = -1
253 lostShape = true
254 end
255 elseif spec.curSplitShape ~= nil then
256 if not entityExists(spec.curSplitShape) then
257 spec.curSplitShape = nil
258 lostShape = true
259 end
260 end
261 if lostShape then
262 SpecializationUtil.raiseEvent(self, "onCutTree", 0)
263 if g_server ~= nil then
264 g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self)
265 end
266 end
267 end
268
269 if self.isServer and (spec.attachedSplitShape ~= nil or spec.curSplitShape ~= nil) then
270 if spec.cutTimer > 0 then
271 if spec.cutAnimation.name ~= nil then
272 if self:getAnimationTime(spec.cutAnimation.name) > spec.cutAnimation.cutTime then
273 spec.cutTimer = 0
274 end
275 else
276 spec.cutTimer = math.max(spec.cutTimer - dt, 0)
277 end
278 end
279 local readyToCut = spec.cutTimer == 0
280
281 -- cut
282 if readyToCut then
283 spec.cutTimer = -1
284
285 local x,y,z = getWorldTranslation(spec.cutNode)
286 local nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0)
287 local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0)
288 local newTreeCut = false
289
290 local currentSplitShape
291 if spec.attachedSplitShapeJointIndex ~= nil then
292 removeJoint(spec.attachedSplitShapeJointIndex)
293 spec.attachedSplitShapeJointIndex = nil
294 currentSplitShape = spec.attachedSplitShape
295 spec.attachedSplitShape = nil
296 else
297 currentSplitShape = spec.curSplitShape
298 spec.curSplitShape = nil
299 newTreeCut = true
300 end
301
302 -- remember split type name for later (achievement)
303 local splitTypeName = ""
304 local splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(currentSplitShape))
305 if splitType ~= nil then
306 splitTypeName = splitType.name
307 end
308
309 if spec.delimbOnCut then
310 local xD,yD,zD = getWorldTranslation(spec.delimbNode)
311 local nxD,nyD,nzD = localDirectionToWorld(spec.delimbNode, 1,0,0)
312 local yxD,yyD,yzD = localDirectionToWorld(spec.delimbNode, 0,1,0)
313 local vx,vy,vz = x-xD,y-yD,z-zD
314 local sizeX = MathUtil.vector3Length(vx,vy,vz)
315 removeSplitShapeAttachments(currentSplitShape, xD+vx*0.5,yD+vy*0.5,zD+vz*0.5, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*0.7+spec.delimbSizeX, spec.delimbSizeY, spec.delimbSizeZ)
316 end
317
318 spec.attachedSplitShape = nil
319 spec.curSplitShape = nil
320 spec.prevSplitShape = currentSplitShape
321
322 g_currentMission:removeKnownSplitShape(currentSplitShape)
323 splitShape(currentSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ, "woodHarvesterSplitShapeCallback", self)
324
325 if spec.attachedSplitShape == nil then
326 SpecializationUtil.raiseEvent(self, "onCutTree", 0)
327 if g_server ~= nil then
328 g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self)
329 end
330 else
331 if spec.delimbOnCut then
332 local xD,yD,zD = getWorldTranslation(spec.delimbNode)
333 local nxD,nyD,nzD = localDirectionToWorld(spec.delimbNode, 1,0,0)
334 local yxD,yyD,yzD = localDirectionToWorld(spec.delimbNode, 0,1,0)
335 local vx,vy,vz = x-xD,y-yD,z-zD
336 local sizeX = MathUtil.vector3Length(vx,vy,vz)
337 removeSplitShapeAttachments(spec.attachedSplitShape, xD+vx*3,yD+vy*3,zD+vz*3, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*3+spec.delimbSizeX, spec.delimbSizeY, spec.delimbSizeZ)
338 end
339 end
340
341 if newTreeCut then
342 local stats = g_currentMission:farmStats(self:getActiveFarm())
343
344 -- increase tree cut counter for achievements
345 stats:updateStats("cutTreeCount", 1)
346
347 -- update the types of trees cut so far (achievement)
348 if splitTypeName ~= "" then
349 stats:updateTreeTypesCut(splitTypeName)
350 end
351 end
352 end
353
354 -- delimb
355 if spec.attachedSplitShape ~= nil and spec.isAttachedSplitShapeMoving then
356 if spec.delimbNode ~= nil then
357 local x,y,z = getWorldTranslation(spec.delimbNode)
358 local nx,ny,nz = localDirectionToWorld(spec.delimbNode, 1,0,0)
359 local yx,yy,yz = localDirectionToWorld(spec.delimbNode, 0,1,0)
360
361 removeSplitShapeAttachments(spec.attachedSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.delimbSizeX, spec.delimbSizeY, spec.delimbSizeZ)
362 end
363
364 if spec.cutNode ~= nil and spec.attachedSplitShapeJointIndex ~= nil then
365 local x,y,z = getWorldTranslation(spec.cutAttachReferenceNode)
366 local nx,ny,nz = localDirectionToWorld(spec.cutAttachReferenceNode, 0,1,0)
367 local _, lengthRem = getSplitShapePlaneExtents(spec.attachedSplitShape, x,y,z, nx,ny,nz)
368
369 if lengthRem == nil or lengthRem <= 0.1 then
370
371 -- end of tree
372 removeJoint(spec.attachedSplitShapeJointIndex)
373 spec.attachedSplitShapeJointIndex = nil
374 spec.attachedSplitShape = nil
375
376 self:onDelimbTree(false)
377 if g_server ~= nil then
378 g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self)
379 end
380
381 SpecializationUtil.raiseEvent(self, "onCutTree", 0)
382 if g_server ~= nil then
383 g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self)
384 end
385 else
386 spec.attachedSplitShapeY = spec.attachedSplitShapeY + spec.cutAttachMoveSpeed*dt
387
388 if spec.attachedSplitShapeY >= spec.attachedSplitShapeTargetY then
389 spec.attachedSplitShapeY = spec.attachedSplitShapeTargetY
390 self:onDelimbTree(false)
391 if g_server ~= nil then
392 g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self)
393 end
394 end
395 if spec.attachedSplitShapeJointIndex ~= nil then
396 x,y,z = localToWorld(spec.cutNode, 0.3,0,0)
397 nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0)
398 local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0)
399 local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
400 if shape == spec.attachedSplitShape then
401 local treeCenterX,treeCenterY,treeCenterZ = localToWorld(spec.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5)
402 spec.attachedSplitShapeX, _, spec.attachedSplitShapeZ = worldToLocal(spec.attachedSplitShape, treeCenterX,treeCenterY,treeCenterZ)
403 self:setLastTreeDiameter((maxY-minY + maxZ-minZ)*0.5)
404 end
405 x,y,z = localToWorld(spec.attachedSplitShape, spec.attachedSplitShapeX, spec.attachedSplitShapeY, spec.attachedSplitShapeZ)
406 setJointPosition(spec.attachedSplitShapeJointIndex, 1, x,y,z)
407 end
408 end
409 end
410 end
411 end
412
413 -- effect and sound for cut and delimb
414 if self.isClient then
415 -- cut
416 if spec.cutAnimation.name ~= nil then
417 if self:getIsAnimationPlaying(spec.cutAnimation.name) and self:getAnimationTime(spec.cutAnimation.name) < spec.cutAnimation.cutTime then
418 if not spec.isCutSamplePlaying then
419 g_soundManager:playSample(spec.samples.cut)
420 spec.isCutSamplePlaying = true
421 end
422 g_effectManager:setFillType(spec.cutEffects, FillType.WOODCHIPS)
423 g_effectManager:startEffects(spec.cutEffects)
424 else
425 if spec.isCutSamplePlaying then
426 g_soundManager:stopSample(spec.samples.cut)
427 spec.isCutSamplePlaying = false
428 end
429 g_effectManager:stopEffects(spec.cutEffects)
430 end
431 end
432
433 -- delimb
434 if spec.isAttachedSplitShapeMoving then
435 if not spec.isDelimbSamplePlaying then
436 g_soundManager:playSample(spec.samples.delimb)
437 spec.isDelimbSamplePlaying = true
438 end
439 g_effectManager:setFillType(spec.delimbEffects, FillType.WOODCHIPS)
440 g_effectManager:startEffects(spec.delimbEffects)
441 g_animationManager:startAnimations(spec.forwardingNodes)
442 else
443 if spec.isDelimbSamplePlaying then
444 g_soundManager:stopSample(spec.samples.delimb)
445 spec.isDelimbSamplePlaying = false
446 end
447 g_effectManager:stopEffects(spec.delimbEffects)
448 g_animationManager:stopAnimations(spec.forwardingNodes)
449 end
450 end
451end

onUpdateTick

Description
Definition
onUpdateTick()
Code
455function WoodHarvester:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
456 local spec = self.spec_woodHarvester
457
458 spec.warnInvalidTree = false
459 spec.warnInvalidTreeRadius = false
460 spec.warnTreeNotOwned = false
461
462 if self:getIsTurnedOn() then
463 if spec.attachedSplitShape == nil and spec.cutNode ~= nil then
464 local x,y,z = getWorldTranslation(spec.cutNode)
465 local nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0)
466 local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0)
467
468 if spec.curSplitShape == nil and (spec.cutReleasedComponentJoint == nil or spec.cutReleasedComponentJointRotLimitX == 0) then
469
470 local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
471 if shape ~= 0 then
472 local splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(shape))
473 if splitType == nil or not splitType.allowsWoodHarvester then
474 spec.warnInvalidTree = true
475 else
476 if g_currentMission.accessHandler:canFarmAccessLand(self:getActiveFarm(), x, z) then
477 local treeDx,treeDy,treeDz = localDirectionToWorld(shape, 0,1,0) -- wood harvester trees always grow in the y direction
478 local cosTreeAngle = MathUtil.dotProduct(nx,ny,nz, treeDx,treeDy,treeDz)
479 -- Only allow cutting if the cut header is approximately parallel to the tree (70° offset)
480 if cosTreeAngle >= 0.35 then
481 local radius = math.max(maxY-minY, maxZ-minZ)*0.5 * cosTreeAngle
482 if radius > spec.cutMaxRadius then
483 spec.warnInvalidTreeRadius = true
484 else
485 self:setLastTreeDiameter(math.max(maxY-minY, maxZ-minZ))
486 spec.curSplitShape = shape
487 end
488 end
489 else
490 spec.warnTreeNotOwned = true
491 end
492 end
493 end
494 end
495
496 if spec.curSplitShape ~= nil then
497 local minY,maxY, minZ,maxZ = testSplitShape(spec.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ)
498 if minY == nil then
499 spec.curSplitShape = nil
500 else
501 -- check if cut would be below y=0 (tree CoSy)
502 local cutTooLow = false
503 _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,minY,minZ)
504 cutTooLow = cutTooLow or y < 0.01
505 _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,minY,maxZ)
506 cutTooLow = cutTooLow or y < 0.01
507 _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,maxY,minZ)
508 cutTooLow = cutTooLow or y < 0.01
509 _,y,_ = localToLocal(spec.cutNode, spec.curSplitShape, 0,maxY,maxZ)
510 cutTooLow = cutTooLow or y < 0.01
511 if cutTooLow then
512 spec.curSplitShape = nil
513 end
514 end
515 end
516
517 if spec.curSplitShape == nil and spec.cutTimer > -1 then
518 SpecializationUtil.raiseEvent(self, "onCutTree", 0)
519 if g_server ~= nil then
520 g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self)
521 end
522 end
523
524 end
525 end
526
527 if self.isServer then
528 if spec.attachedSplitShape == nil then
529 if spec.cutReleasedComponentJoint ~= nil and spec.cutReleasedComponentJointRotLimitX ~= 0 then
530 spec.cutReleasedComponentJointRotLimitX = math.max(0, spec.cutReleasedComponentJointRotLimitX - spec.cutReleasedComponentJointRotLimitXSpeed*dt)
531 setJointRotationLimit(spec.cutReleasedComponentJoint.jointIndex, 0, true, 0, spec.cutReleasedComponentJointRotLimitX)
532 end
533 if spec.cutReleasedComponentJoint2 ~= nil and spec.cutReleasedComponentJoint2RotLimitX ~= 0 then
534 spec.cutReleasedComponentJoint2RotLimitX = math.max(spec.cutReleasedComponentJoint2RotLimitX-spec.cutReleasedComponentJoint2RotLimitXSpeed*dt, 0)
535 setJointRotationLimit(spec.cutReleasedComponentJoint2.jointIndex, 0, true, -spec.cutReleasedComponentJoint2RotLimitX, spec.cutReleasedComponentJoint2RotLimitX)
536 end
537 end
538 end
539
540 if self.isServer then
541 if self.playDelayedGrabAnimationTime ~= nil then
542 if self.playDelayedGrabAnimationTime < g_currentMission.time then
543 self.playDelayedGrabAnimationTime = nil
544 if self:getAnimationTime(spec.grabAnimation.name) > 0 then
545 if spec.grabAnimation.name ~= nil and spec.attachedSplitShape == nil then
546 if spec.grabAnimation.speedScale > 0 then
547 self:setAnimationStopTime(spec.grabAnimation.name, 0)
548 else
549 self:setAnimationStopTime(spec.grabAnimation.name, 1)
550 end
551 self:playAnimation(spec.grabAnimation.name, -spec.grabAnimation.speedScale, self:getAnimationTime(spec.grabAnimation.name), false)
552 end
553 end
554 end
555 end
556 end
557
558 if self.isClient then
559 local actionEvent = spec.actionEvents[InputAction.IMPLEMENT_EXTRA2]
560 if actionEvent ~= nil then
561 local showAction = false
562 if spec.hasAttachedSplitShape then
563 if not spec.isAttachedSplitShapeMoving and self:getAnimationTime(spec.cutAnimation.name) == 1 then
564 showAction = true
565 end
566 elseif spec.curSplitShape ~= nil then
567 showAction = true
568 end
569
570 g_inputBinding:setActionEventActive(actionEvent.actionEventId, showAction)
571 end
572
573 actionEvent = spec.actionEvents[InputAction.IMPLEMENT_EXTRA3]
574 if actionEvent ~= nil then
575 g_inputBinding:setActionEventActive(actionEvent.actionEventId, not spec.isAttachedSplitShapeMoving)
576 if not spec.isAttachedSplitShapeMoving then
577 g_inputBinding:setActionEventText(actionEvent.actionEventId, string.format(g_i18n:getText("action_woodHarvesterChangeCutLength"), string.format("%.1f", spec.currentCutLength)))
578 end
579 end
580
581 end
582end

onWriteStream

Description
Definition
onWriteStream()
Code
233function WoodHarvester:onWriteStream(streamId, connection)
234 local spec = self.spec_woodHarvester
235 streamWriteBool(streamId, spec.hasAttachedSplitShape)
236 streamWriteBool(streamId, spec.isAttachedSplitShapeMoving)
237end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
19function WoodHarvester.prerequisitesPresent(specializations)
20 return SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations)
21end

registerEventListeners

Description
Definition
registerEventListeners()
Code
46function WoodHarvester.registerEventListeners(vehicleType)
47 SpecializationUtil.registerEventListener(vehicleType, "onLoad", WoodHarvester)
48 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", WoodHarvester)
49 SpecializationUtil.registerEventListener(vehicleType, "onDelete", WoodHarvester)
50 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", WoodHarvester)
51 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", WoodHarvester)
52 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", WoodHarvester)
53 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", WoodHarvester)
54 SpecializationUtil.registerEventListener(vehicleType, "onDraw", WoodHarvester)
55 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", WoodHarvester)
56 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", WoodHarvester)
57 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", WoodHarvester)
58 SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", WoodHarvester)
59 SpecializationUtil.registerEventListener(vehicleType, "onCutTree", WoodHarvester)
60end

registerEvents

Description
Definition
registerEvents()
Code
25function WoodHarvester.registerEvents(vehicleType)
26 SpecializationUtil.registerEvent(vehicleType, "onCutTree")
27end

registerFunctions

Description
Definition
registerFunctions()
Code
31function WoodHarvester.registerFunctions(vehicleType)
32 SpecializationUtil.registerFunction(vehicleType, "woodHarvesterSplitShapeCallback", WoodHarvester.woodHarvesterSplitShapeCallback)
33 SpecializationUtil.registerFunction(vehicleType, "setLastTreeDiameter", WoodHarvester.setLastTreeDiameter)
34 SpecializationUtil.registerFunction(vehicleType, "cutTree", WoodHarvester.cutTree)
35 SpecializationUtil.registerFunction(vehicleType, "onDelimbTree", WoodHarvester.onDelimbTree)
36end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
40function WoodHarvester.registerOverwrittenFunctions(vehicleType)
41 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", WoodHarvester.getCanBeSelected)
42end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
217function WoodHarvester:saveToXMLFile(xmlFile, key, usedModNames)
218 local spec = self.spec_woodHarvester
219
220 setXMLFloat(xmlFile, key.."#currentCutLength", spec.currentCutLength)
221end

setLastTreeDiameter

Description
Definition
setLastTreeDiameter()
Code
820function WoodHarvester:setLastTreeDiameter(diameter)
821 local spec = self.spec_woodHarvester
822 spec.lastDiameter = diameter
823end

woodHarvesterSplitShapeCallback

Description
Definition
woodHarvesterSplitShapeCallback()
Code
757function WoodHarvester:woodHarvesterSplitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ)
758 local spec = self.spec_woodHarvester
759
760 g_currentMission:addKnownSplitShape(shape)
761
762 if spec.attachedSplitShape == nil and isAbove and not isBelow and spec.cutAttachNode ~= nil and spec.cutAttachReferenceNode ~= nil then
763 spec.attachedSplitShape = shape
764
765 -- Current tree center (mid of cut area)
766 local treeCenterX,treeCenterY,treeCenterZ = localToWorld(spec.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5)
767
768 -- Target tree center (half tree size in front of the reference node)
769 local x,y,z = localToWorld(spec.cutAttachReferenceNode, 0, 0, (maxZ-minZ)*0.5)
770
771 local dx,dy,dz = localDirectionToWorld(shape, 0,0,1)
772
773 local upx,upy,upz = localDirectionToWorld(spec.cutAttachReferenceNode, 0,1,0)
774 local sideX,sideY,sizeZ = MathUtil.crossProduct(upx,upy,upz, dx,dy,dz)
775 dx,dy,dz = MathUtil.crossProduct(sideX,sideY,sizeZ, upx,upy,upz) -- Note: we want the up axis to be exact, thus orthogonalize the direction here
776 I3DUtil.setWorldDirection(spec.cutAttachHelperNode, dx,dy,dz, upx,upy,upz, 2)
777
778 local constr = JointConstructor:new()
779 constr:setActors(spec.cutAttachNode, shape)
780 -- Note: we assume that the direction of the tree is equal to the y axis
781 constr:setJointTransforms(spec.cutAttachHelperNode, shape)
782 constr:setJointWorldPositions(x,y,z, treeCenterX,treeCenterY,treeCenterZ)
783
784 constr:setRotationLimit(0, 0, 0)
785 constr:setRotationLimit(1, 0, 0)
786 constr:setRotationLimit(2, 0, 0)
787
788 constr:setEnableCollision(false)
789
790 spec.attachedSplitShapeJointIndex = constr:finalize()
791
792 if spec.cutReleasedComponentJoint ~= nil then
793 spec.cutReleasedComponentJointRotLimitX = math.pi*0.9
794 if spec.cutReleasedComponentJoint.jointIndex ~= 0 then
795 setJointRotationLimit(spec.cutReleasedComponentJoint.jointIndex, 0, true, 0, spec.cutReleasedComponentJointRotLimitX)
796 end
797 end
798 if spec.cutReleasedComponentJoint2 ~= nil then
799 spec.cutReleasedComponentJoint2RotLimitX = math.pi*0.9
800 if spec.cutReleasedComponentJoint2.jointIndex ~= 0 then
801 setJointRotationLimit(spec.cutReleasedComponentJoint2.jointIndex, 0, true, -spec.cutReleasedComponentJoint2RotLimitX, spec.cutReleasedComponentJoint2RotLimitX)
802 end
803 end
804
805 spec.attachedSplitShapeX, spec.attachedSplitShapeY, spec.attachedSplitShapeZ = worldToLocal(shape, treeCenterX,treeCenterY,treeCenterZ)
806 spec.attachedSplitShapeLastCutY = spec.attachedSplitShapeY
807 spec.attachedSplitShapeStartY = spec.attachedSplitShapeY
808 spec.attachedSplitShapeTargetY = spec.attachedSplitShapeY
809
810 local radius = ((maxY - minY) + (maxZ - minZ)) / 4
811 SpecializationUtil.raiseEvent(self, "onCutTree", radius)
812 if g_server ~= nil then
813 g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, radius), nil, nil, self)
814 end
815 end
816end