LUADOC - Farming Simulator 22

Chainsaw

Description
Class for chainsaws
Parent
HandTool
Functions

calculateCutDuration

Description
Definition
calculateCutDuration(float dt, bool true)
Arguments
floatdtin milliseconds
booltrueif the player is cutting
Code
401function Chainsaw:calculateCutDuration()
402 local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd()
403 local trunkWidthSq = MathUtil.vector3LengthSq(endX - startX, endY - startY, endZ - startZ)
404 trunkWidthSq = MathUtil.clamp(trunkWidthSq, 0.0, self.maxTrunkWidthSq)
405 local cutDuration = trunkWidthSq * self.defaultCutDuration / self.maxTrunkWidthSq
406 return cutDuration
407end

cutRaycastCallback

Description
Saves raycast information
Definition
cutRaycastCallback(integer hitObjectId, float x, float y, float z, float distance)
Arguments
integerhitObjectId
floatx
floaty
floatz
floatdistance
Code
295function Chainsaw:cutRaycastCallback(hitObjectId, x, y, z, distance)
296 setWorldTranslation(self.chainsawCameraFocus, x, y, z)
297 self.cutFocusDistance = distance
298end

delete

Description
Deleting chainsaw
Definition
delete()
Code
255function Chainsaw:delete()
256 if self.isClient then
257 ParticleUtil.deleteParticleSystems(self.particleSystems)
258
259 g_soundManager:deleteSamples(self.samplesTree)
260 g_soundManager:deleteSamples(self.samplesBranch)
261 g_soundManager:deleteSamples(self.samples)
262 g_soundManager:deleteSamples(self.samplesQuicktap)
263
264 g_animationManager:deleteAnimations(self.chains)
265
266 if self.ringSelector ~= nil then
267 delete(self.ringSelector)
268 self.ringSelector = nil
269 end
270
271 if self.chainsawCameraFocus ~= nil then
272 delete(self.chainsawCameraFocus)
273 end
274
275 if self.sharedLoadRequestIdRingSelector ~= nil then
276 g_i3DManager:releaseSharedI3DFile(self.sharedLoadRequestIdRingSelector)
277 self.sharedLoadRequestIdRingSelector = nil
278 end
279 end
280
281 Chainsaw:superClass().delete(self)
282
283 if self.rotationNode ~= nil then
284 delete(self.rotationNode)
285 end
286end

getChainSpeedFactor

Description
Definition
getChainSpeedFactor()
Code
921function Chainsaw:getChainSpeedFactor()
922 return self.speedFactor
923end

getCutShapeInformation

Description
Preparing information for the splitShape methods
Definition
getCutShapeInformation(float x, float y, float z, float nx, float ny, float nz, float yx, float yy, float yz)
Arguments
floatx
floaty
floatz
floatnx
floatny
floatnz
floatyx
floatyy
floatyz
Code
584function Chainsaw:getCutShapeInformation()
585 local x,y,z = getWorldTranslation(self.chainsawSplitShapeFocus)
586 local nx,ny,nz = localDirectionToWorld(self.chainsawSplitShapeFocus, 1,0,0)
587 local yx,yy,yz = localDirectionToWorld(self.chainsawSplitShapeFocus, 0,1,0)
588 return x, y, z, nx, ny, nz, yx, yy, yz
589end

getCutStartEnd

Description
Function to calculate the start and the end of the cut by using the ringSelector
Definition
getCutStartEnd()
Code
384function Chainsaw:getCutStartEnd()
385 local selectorPosition = {getWorldTranslation(self.ringSelector)}
386 local selectorScale = {getScale(self.ringSelector)}
387 local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 1, 0)}
388 local cutStartposition = { selectorPosition[1] - 0.5 * selectorScale[1] * cutDirection[1],
389 selectorPosition[2] - 0.5 * selectorScale[2] * cutDirection[2],
390 selectorPosition[3] - 0.5 * selectorScale[3] * cutDirection[3] }
391 local cutEndposition = { selectorPosition[1] + 0.5 * selectorScale[1] * cutDirection[1],
392 selectorPosition[2] + 0.5 * selectorScale[2] * cutDirection[2],
393 selectorPosition[3] + 0.5 * selectorScale[3] * cutDirection[3] }
394 return cutStartposition[1], cutStartposition[2], cutStartposition[3], cutEndposition[1], cutEndposition[2], cutEndposition[3]
395end

getLookAt

Description
Given a target position we calculate a up and towards direction (result is in world reference)
Definition
getLookAt(integer camera, float target, float target, float target)
Arguments
integercameraidentifier
floattargetposition x
floattargetposition y
floattargetposition z
Return Values
floatyx
floatyy
floatyz
floatzx
floatzy
floatzz
Code
363function Chainsaw:getLookAt(cameraNode, targetX, targetY, targetZ)
364 local xx, xy, xz
365 local yx, yy, yz
366 local zx, zy, zz
367 local nodePosition = {getWorldTranslation(cameraNode)}
368 local nodeUpDirection = {localDirectionToWorld(getParent(cameraNode), 0, -1, 0)}
369
370 zx = nodePosition[1] - targetX
371 zy = nodePosition[2] - targetY
372 zz = nodePosition[3] - targetZ
373 zx, zy, zz = MathUtil.vector3Normalize(zx, zy, zz)
374 xx, xy, xz = MathUtil.crossProduct(zx, zy, zz, nodeUpDirection[1], nodeUpDirection[2], nodeUpDirection[3])
375 xx, xy, xz = MathUtil.vector3Normalize(xx, xy, xz)
376 yx, yy, yz = MathUtil.crossProduct(zx, zy, zz, xx, xy, xz)
377 yx, yy, yz = MathUtil.vector3Normalize(yx, yy, yz)
378
379 return yx, yy, yz, zx, zy, zz
380end

getNeedCustomWorkStyle

Description
Definition
getNeedCustomWorkStyle()
Code
1000function Chainsaw:getNeedCustomWorkStyle()
1001 local style = self.player.model:getStyle()
1002 if style == nil then
1003 return false
1004 end
1005
1006 local glovesConfig = style.glovesConfig
1007 local gloveIndex = glovesConfig.selection
1008 if gloveIndex == 0 then
1009 return true
1010 end
1011
1012 if glovesConfig.items[gloveIndex] == nil or not glovesConfig.items[gloveIndex].isForestryItem then
1013 return true
1014 end
1015
1016 local headgearConfig = style.headgearConfig
1017 local headgearIndex = headgearConfig.selection
1018 if headgearIndex == 0 then
1019 return true
1020 end
1021
1022 if headgearConfig.items[headgearIndex] == nil or not headgearConfig.items[headgearIndex].isForestryItem then
1023 return true
1024 end
1025
1026 return false
1027end

isBeingUsed

Description
Definition
isBeingUsed()
Code
994function Chainsaw:isBeingUsed()
995 return self.isCutting
996end

isCuttingAllowed

Description
Definition
isCuttingAllowed()
Code
774function Chainsaw:isCuttingAllowed(x, y, z, shape)
775 return g_currentMission.accessHandler:canFarmAccessLand(self.player.farmId, x, z) and g_currentMission:getHasPlayerPermission("cutTrees")
776end

new

Description
Creating chainsaw object
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustom metatable
Return Values
tableinstanceInstance of object
Code
71function Chainsaw.new(isServer, isClient, customMt)
72 local self = HandTool.new(isServer, isClient, customMt or Chainsaw_mt)
73 return self
74end

onActivate

Description
On activate
Definition
onActivate(boolean allowInput)
Arguments
booleanallowInputallow input
Code
928function Chainsaw:onActivate(allowInput)
929 Chainsaw:superClass().onActivate(self)
930
931 self.rotationZ = 0.0
932 setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3])
933 setRotation(self.chainsawCameraFocus, 0, 0, self.rotationZ)
934
935 self.startTime = g_currentMission.time
936
937 if self.isClient then
938 g_animationManager:startAnimations(self.chains)
939 end
940
941 self.cutTimer = 0.0
942 setTranslation(self.graphicsNode, 0, 0, 0)
943
944 self.soundFSM:changeState(Chainsaw.SOUND_STATES.START)
945end

onDeactivate

Description
On deactivate
Definition
onDeactivate(boolean allowInput)
Arguments
booleanallowInputallow input
Code
950function Chainsaw:onDeactivate(allowInput)
951 Chainsaw:superClass().onDeactivate(self)
952
953 self.speedFactor = 0
954 self.curSplitShape = nil
955 self.player:lockInput(false)
956 self.player.walkingIsLocked = false
957 if self.isClient then
958 g_animationManager:stopAnimations(self.chains)
959 self.cutTimer = 0.0
960 setTranslation(self.graphicsNode, 0, 0, 0)
961 if self.particleSystems ~= nil then
962 for _, ps in pairs(self.particleSystems) do
963 ParticleUtil.resetNumOfEmittedParticles(ps)
964 ParticleUtil.setEmittingState(ps, false)
965 end
966 end
967 if self.ringSelector ~= nil then
968 setVisibility(self.ringSelector, false)
969 end
970 end
971 self.soundFSM:changeState(Chainsaw.SOUND_STATES.STOP)
972end

onInputRotate

Description
Event function for chainsaw rotation bound to InputAction.AXIS_ROTATE_HANDTOOL.
Definition
onInputRotate()
Code
988function Chainsaw:onInputRotate(_, inputValue)
989 self.rotateInput = self.rotateInput + inputValue
990end

onRingSelectorLoaded

Description
Ring selector i3d loaded
Definition
onRingSelectorLoaded()
Code
810function Chainsaw:onRingSelectorLoaded(node, failedReason, args)
811 if node ~= 0 then
812 if not self.isDeleted then
813 self.ringSelector = getChildAt(node, 0)
814
815 setVisibility(self.ringSelector, false)
816
817 link(self.chainsawSplitShapeFocus, self.ringSelector)
818 end
819
820 delete(node)
821 end
822end

postLoad

Description
Called after hand tool i3d file was loaded
Definition
postLoad(table xmlFile)
Arguments
tablexmlFilexmlFile
Code
79function Chainsaw:postLoad(xmlFile)
80 if not Chainsaw:superClass().postLoad(self, xmlFile) then
81 return false
82 end
83
84 self.rotateInput = 0
85 self.activatePressed = false
86 self.eventIdRotateHandtool = ""
87
88 self.rotationZ = 0
89 self.rotationSpeedZ = 0.003
90 self.cutSizeY = xmlFile:getValue("handTool.chainsaw#cutSizeY", 1.1)
91 self.cutSizeZ = xmlFile:getValue("handTool.chainsaw#cutSizeZ", 1.0)
92 self.isCutting = false
93 self.waitingForResetAfterCut = false
94 self.cutNode = getChildAt(self.rootNode, 0)
95 self.graphicsNode = getChildAt(self.cutNode, 0)
96 self.chainNode = getChildAt(self.graphicsNode, 0)
97 self.psNode = getChildAt(self.graphicsNode, 1)
98 self.cutPositionNode = getChildAt(self.graphicsNode, 5)
99
100 self.pricePerSecond = xmlFile:getValue("handTool.chainsaw.pricePerMinute", 50) / 1000
101 self.quicktapThreshold = xmlFile:getValue("handTool.chainsaw#quicktapThreshold", 0.0) * 1000
102
103 self.minCutDistance = xmlFile:getValue("handTool.chainsaw.targetSelection#minCutDistance", 0.5)
104 self.maxCutDistance = xmlFile:getValue("handTool.chainsaw.targetSelection#maxCutDistance", 1.0)
105 self.cutDetectionDistance = xmlFile:getValue("handTool.chainsaw.targetSelection#cutDetectionDistance", 10.0)
106
107 if self.isClient then
108 self.particleSystems = {}
109
110 xmlFile:iterate("handTool.chainsaw.particleSystems.emitterShape", function(_, key)
111 local emitterShape = xmlFile:getValue(key.."#node", nil, self.rootNode)
112 local particleType = xmlFile:getValue(key.."#particleType")
113
114 if emitterShape ~= nil then
115 local fillType = FillType.WOODCHIPS
116 local particleSystem = g_particleSystemManager:getParticleSystem(particleType)
117
118 if particleSystem ~= nil then
119 local material = g_materialManager:getParticleMaterial(fillType, particleType, 1)
120 if material ~= nil then
121 ParticleUtil.setMaterial(particleSystem, material)
122 end
123
124 table.insert(self.particleSystems, ParticleUtil.copyParticleSystem(xmlFile, key, particleSystem, emitterShape))
125 end
126 end
127 end)
128
129
130 if #self.particleSystems == 0 then
131 self.particleSystems = nil
132 end
133
134 self.equipmentUVs = xmlFile:getValue("handTool.chainsaw.equipment#uvs", "0 0", true)
135
136 self.chains = g_animationManager:loadAnimations(xmlFile, "handTool.chainsaw.chain", self.rootNode, self, nil)
137 self.samples = {}
138 self.samples.start = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "start", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
139 self.samples.idle = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "idle", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
140 self.samples.cutStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
141 self.samples.cutStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
142 self.samples.cutLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
143 self.samples.activeStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
144 self.samples.activeStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
145 self.samples.activeLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
146 self.samples.stop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "stop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
147
148 self.samplesQuicktap = {}
149 local j = 0
150 while true do
151 local sampleQuicktap = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds.quickTapSounds", string.format("quickTap(%d)", j), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
152 if sampleQuicktap == nil then
153 break
154 end
155 table.insert(self.samplesQuicktap, sampleQuicktap)
156 j = j + 1
157 end
158 self.samplesQuicktapCount = j
159
160 self.samplesTree = {}
161 -- TODO: load more than one sample
162 self.samplesTree.cut = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "cut", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
163 --self.samplesTree.falling = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "falling", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
164 self.samplesBranch = {}
165 local k = 0
166 while true do
167 local sampleBranch = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.branchSounds", string.format("branch(%d)", k), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
168 if sampleBranch == nil then
169 break
170 end
171 table.insert(self.samplesBranch, sampleBranch)
172 k = k + 1
173 end
174 self.samplesBranchCount = k
175 self.samplesBranchActiveTimer = 0
176
177 self.samplesTreeLinkNode = createTransformGroup("cutSoundLinkNode")
178 link(self.cutNode, self.samplesTreeLinkNode)
179
180 if self.samplesTree.cut ~= nil and self.samplesTree.cut.soundNode ~= nil then
181 link(self.samplesTreeLinkNode, self.samplesTree.cut.soundNode)
182 --link(self.samplesTreeLinkNode, self.samplesTree.falling.soundNode)
183 end
184
185 self.soundFSM = FSMUtil.create()
186 self.soundFSM:addState(Chainsaw.SOUND_STATES.START, ChainsawSoundStateStart.new(Chainsaw.SOUND_STATES.START, self, self.soundFSM))
187 self.soundFSM:addState(Chainsaw.SOUND_STATES.STOP, ChainsawSoundStateStop.new(Chainsaw.SOUND_STATES.STOP, self, self.soundFSM))
188 self.soundFSM:addState(Chainsaw.SOUND_STATES.IDLE, ChainsawSoundStateIdle.new(Chainsaw.SOUND_STATES.IDLE, self, self.soundFSM))
189 self.soundFSM:addState(Chainsaw.SOUND_STATES.ACTIVE, ChainsawSoundStateActive.new(Chainsaw.SOUND_STATES.ACTIVE, self, self.soundFSM))
190 self.soundFSM:addState(Chainsaw.SOUND_STATES.CUT, ChainsawSoundStateCut.new(Chainsaw.SOUND_STATES.CUT, self, self.soundFSM))
191 self.soundFSM:addState(Chainsaw.SOUND_STATES.QUICKTAP, ChainsawSoundStateQuicktap.new(Chainsaw.SOUND_STATES.QUICKTAP, self, self.soundFSM))
192
193 self.ringSelectorScaleOffset = xmlFile:getValue("handTool.chainsaw.ringSelector#scaleOffset", 0.3)
194
195 self.rotationNode = createTransformGroup("chainsaw_rotationNode")
196 link(getRootNode(), self.rotationNode)
197 self.chainsawCameraFocus = createTransformGroup("chainsaw_cameraFocus")
198 link(self.rotationNode, self.chainsawCameraFocus)
199 self.chainsawSplitShapeFocus = createTransformGroup("chainsaw_splitShapeFocus")
200 link(self.chainsawCameraFocus, self.chainsawSplitShapeFocus)
201
202 local cutFocusOffset = xmlFile:getVector("handTool.chainsaw.cutAnimation#cutFocusOffset", "0 0 -1", 3)
203 setTranslation(self.chainsawSplitShapeFocus, unpack(cutFocusOffset))
204 local cutFocusRotation = xmlFile:getVector("handTool.chainsaw.cutAnimation#cutFocusRotation", "0 0 0", 3)
205 local rotX, rotY, rotZ = unpack(cutFocusRotation)
206 setRotation(self.chainsawSplitShapeFocus, math.rad(rotX), math.rad(rotY), math.rad(rotZ))
207
208 local filename = xmlFile:getValue("handTool.chainsaw.ringSelector#file")
209 if filename ~= nil then
210 filename = Utils.getFilename(filename, self.baseDirectory)
211 g_i3DManager:pinSharedI3DFileInCache(filename)
212 self.sharedLoadRequestIdRingSelector = g_i3DManager:loadSharedI3DFileAsync(filename, false, false, self.onRingSelectorLoaded, self, self.player)
213 end
214 end
215
216 if self.player ~= g_currentMission.player then
217 self.handNodePositionInCutting = xmlFile:getValue("handTool.handNode.thirdPersonCutting#position", "0 0 0", true)
218 self.handNodeRotationInCutting = xmlFile:getValue("handTool.handNode.thirdPersonCutting#rotation", "0 0 0", true)
219 self.referenceNodeInCutting = xmlFile:getValue("handTool.handNode.thirdPersonCutting#referenceNode", nil, self.rootNode)
220 end
221
222 self.lastWorkTime = 0
223 self.maxWorkTime = 300
224
225 self.moveSpeedY = 0.0001
226 self.speedFactor = 0
227 self.defaultCutDuration = 8.0 -- in s
228 self.maxTrunkWidthSq = 1.0 -- in m
229 self.outDuration = 0.15 -- in s
230 self.inDuration = 0.15 -- in s
231 self.cutTimer = 0.0 -- in s
232 self.outTimer = self.outDuration -- in s
233 self.transitionAlpha = 0.0 -- [0,1]
234 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE -- 0=in 1=cut 2=out
235 self.minRotationZ = math.rad(90) -- in rad
236 self.maxRotationZ = math.rad(-90) -- in rad
237 self.maxModelTranslation = 0.0 -- in m
238 self.cutFocusDistance = -1.0
239 self.startCameraDirectionY = { 0, 1, 0 }
240 self.startCameraDirectionZ = { 0, 0, 1 }
241 self.endCameraDirectionY = { 0, 1, 0 }
242 self.endCameraDirectionZ = { 0, 0, 1 }
243 self.startChainsawPosition = { 0, 0, 0 }
244 self.endChainsawPosition = { 0, 0, 0 }
245 self.showNotOwnedWarning = false
246
247 self.isCutting = false
248 self.isHorizontalCut = false
249
250 return true
251end

registerActionEvents

Description
Definition
registerActionEvents()
Code
976function Chainsaw:registerActionEvents()
977 g_inputBinding:beginActionEventsModification(Player.INPUT_CONTEXT_NAME)
978
979 local _, eventId = g_inputBinding:registerActionEvent(InputAction.AXIS_ROTATE_HANDTOOL, self, self.onInputRotate, false, false, true, true)
980 g_inputBinding:setActionEventText(eventId, g_i18n:getText("action_rotate"))
981 self.eventIdRotateHandtool = eventId
982
983 g_inputBinding:endActionEventsModification()
984end

resetTransitionState

Description
Definition
resetTransitionState()
Code
452function Chainsaw:resetTransitionState()
453 if (self.cameraTransitionState ~= Chainsaw.CAMERA_TRANSITION_STATES.NONE) then
454 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE
455 end
456end

setCutting

Description
Set cutting
Definition
setCutting(boolean isCutting, boolean isHorizontalCut, boolean hasBeenCut, boolean noEventSend)
Arguments
booleanisCuttingis cutting
booleanisHorizontalCutis horizontal cut
booleanhasBeenCut
booleannoEventSendno event send
Code
881function Chainsaw:setCutting(isCutting, isHorizontalCut, hasBeenCut, noEventSend)
882 ChainsawStateEvent.sendEvent(self.player, isCutting, isHorizontalCut, hasBeenCut, noEventSend)
883
884 if not self.player.isOwner then
885 self.player:setCuttingAnim(isCutting, isHorizontalCut)
886
887 if self.isCutting ~= isCutting then
888 if isCutting then
889 -- switch to cutting node
890 setTranslation(self.handNode, unpack(self.handNodePositionInCutting))
891 setRotation(self.handNode, unpack(self.handNodeRotationInCutting))
892
893 if self.referenceNodeInCutting ~= nil then
894 local x, y, z = getWorldTranslation(self.referenceNodeInCutting)
895 x, y, z = worldToLocal(getParent(self.handNode), x, y, z)
896 local a, b, c = getTranslation(self.handNode)
897 setTranslation(self.handNode, a - x, b - y, c - z)
898 end
899 else
900 -- switch to normal node
901 setTranslation(self.handNode, unpack(self.handNodePosition))
902 setRotation(self.handNode, unpack(self.handNodeRotation))
903
904 if self.referenceNode ~= nil then
905 local x, y, z = getWorldTranslation(self.referenceNode)
906 x, y, z = worldToLocal(getParent(self.handNode), x, y, z)
907 local a, b, c = getTranslation(self.handNode)
908 setTranslation(self.handNode, a - x, b - y, c - z)
909 end
910 end
911 end
912 end
913
914 self.isCutting = isCutting
915 self.isHorizontalCut = isHorizontalCut
916 self.hasBeenCut = hasBeenCut
917end

testTooLow

Description
Definition
testTooLow(integer shape, float minY, float maxY, float minZ, float maxZ)
Arguments
integershape
floatminY
floatmaxY
floatminZ
floatmaxZ
Return Values
bool
Code
325function Chainsaw:testTooLow(shape, minY, maxY, minZ, maxZ)
326 local _,a,_ = localToLocal(self.chainsawSplitShapeFocus, shape, 0,minY,minZ)
327 local _,b,_ = localToLocal(self.chainsawSplitShapeFocus, shape, 0,maxY,minZ)
328 local _,c,_ = localToLocal(self.chainsawSplitShapeFocus, shape, 0,maxY,maxZ)
329
330 local cutTooLow = a < 0.01 or b < 0.03 or c < 0.01
331 if not cutTooLow then
332 local x1,y1,z1 = localToWorld(self.chainsawSplitShapeFocus, 0,minY,minZ)
333 local x2,y2,z2 = localToWorld(self.chainsawSplitShapeFocus, 0,minY,maxZ)
334 local x3,y3,z3 = localToWorld(self.chainsawSplitShapeFocus, 0,maxY,minZ)
335 local x4,y4,z4 = localToWorld(self.chainsawSplitShapeFocus, 0,maxY,maxZ)
336 local h1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1,y1,z1)
337 local h2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2,y2,z2)
338 local h3 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x3,y3,z3)
339 local h4 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x4,y4,z4)
340
341 cutTooLow = h1 < 0.01 or h2 < 0.01 or h3 < 0.03 or h4 < 0.01
342 end
343
344 if cutTooLow then
345 return true
346 end
347
348 return false
349end

update

Description
Update
Definition
update(float dt, boolean allowInput)
Arguments
floatdttime since last call in ms
booleanallowInputallow input
Code
595function Chainsaw:update(dt, allowInput)
596 Chainsaw:superClass().update(self, dt, allowInput)
597
598 if self.isServer then
599 local price = self.pricePerSecond * (dt / 1000)
600 g_farmManager:getFarmById(self.player.farmId).stats:updateStats("expenses", price)
601 g_currentMission:addMoney(-price, self.player.farmId, MoneyType.VEHICLE_RUNNING_COSTS)
602 end
603
604 -- Update camera changes to our chainsaw focus so rotation of the player affects the focus ring
605 local wrx, wry, wrz = getWorldRotation(self.player.cameraNode)
606 setWorldRotation(self.rotationNode, wrx, wry, wrz)
607
608 if self.isClient then
609 if not self.isCutting then
610 self:updateCutRaycast()
611 end
612
613 if self.showNotOwnedWarning then
614 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youAreNotAllowedToCutThisTree"), 2000)
615 self.showNotOwnedWarning = false -- reset so it can be set to true later
616 end
617 end
618
619 self.shouldDelimb = false
620
621 local lockPlayerInput = false
622
623 if allowInput then
624 local isCutting = false
625 local hasBeenCut = false
626
627 setRotation(self.graphicsNode, math.rad(math.random(-1, 1)) * 0.1, math.rad(math.random(-1, 1)) * 0.1, math.rad(-180))
628
629 if self.curSplitShape == nil then
630 lockPlayerInput = self.rotateInput ~= 0
631
632 -- Rotate over Z axis chainsaw based on input
633 if self.rotateInput ~= 0 then
634 self.rotationZ = MathUtil.clamp(self.rotationZ + self.rotationSpeedZ * self.rotateInput * dt, self.maxRotationZ, self.minRotationZ )
635 -- Visual
636 setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3] -self.rotationZ)
637
638 -- Cutting
639 setRotation(self.chainsawCameraFocus, 0, 0, -self.rotationZ)
640 end
641 end
642
643 local shape = 0
644 if not self.waitingForResetAfterCut then
645 if self.curSplitShape ~= nil or self.cutTimer == 0 then
646 if self.curSplitShape == nil or not entityExists(self.curSplitShape) then
647 self.curSplitShape = nil
648
649 local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation()
650 local minY,maxY, minZ,maxZ
651 shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ)
652
653 if shape ~= nil and shape ~= 0 then
654 if self:isCuttingAllowed(x, y, z, shape) then
655 self.showNotOwnedWarning = false
656
657 local cutTooLow = self:testTooLow(shape, minY, maxY, minZ, maxZ)
658 local outsideRange = (self.cutFocusDistance < 0.0 or self.cutFocusDistance >= self.cutDetectionDistance)
659
660 if cutTooLow or outsideRange then
661 self.player.walkingIsLocked = false
662 self.curSplitShape = nil
663 shape, minY,maxY, minZ,maxZ = 0, nil,nil, nil,nil
664 end
665 else
666 self.showNotOwnedWarning = true
667 end
668 end
669 self.curSplitShapeMinY = minY
670 self.curSplitShapeMaxY = maxY
671 self.curSplitShapeMinZ = minZ
672 self.curSplitShapeMaxZ = maxZ
673 else
674 shape = self.curSplitShape
675 end
676
677 self:updateRingSelector(shape)
678 end
679 end
680
681 if self.activatePressed then
682 self.speedFactor = math.min(self.speedFactor + dt/self.maxWorkTime, 1)
683
684 if not self.waitingForResetAfterCut then
685 local inRange = (self.cutFocusDistance >= self.minCutDistance and self.cutFocusDistance < self.maxCutDistance)
686 self.shouldDelimb = inRange
687
688 if (self.curSplitShape ~= nil or self.cutTimer == 0) and inRange then
689 if self.curSplitShape ~= nil and entityExists(self.curSplitShape) then
690 lockPlayerInput = true
691 local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation()
692 local minY, maxY, minZ, maxZ = testSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ)
693
694 if minY == nil then
695 -- cancel cutting if shape can't be cut anymore from current position
696 self.player.walkingIsLocked = false
697 self.curSplitShape = nil
698 else
699 local cutTooLow = self:testTooLow(self.curSplitShape, minY,maxY, minZ,maxZ)
700 if cutTooLow then
701 self.player.walkingIsLocked = false
702 self.curSplitShape = nil
703 end
704 end
705
706 self.curSplitShapeMinY = minY
707 self.curSplitShapeMaxY = maxY
708 self.curSplitShapeMinZ = minZ
709 self.curSplitShapeMaxZ = maxZ
710 else
711 -- log("FIND SHAPE", shape)
712 if shape ~= 0 then
713 self.player.walkingIsLocked = true
714 self.curSplitShape = shape
715 end
716 end
717
718 if self.curSplitShape ~= nil then
719 local x,y,z, nx,ny,nz, yx,yy,yz = self:getCutShapeInformation()
720
721 if self:isCuttingAllowed(x, y, z, self.curSplitShape) then
722 isCutting = true
723 end
724
725 if self.cutTimer > 0 then
726 self.lastWorkTime = math.min(self.lastWorkTime, self.maxWorkTime*0.7)
727 end
728
729 local cutDuration = self:calculateCutDuration()
730 if self.cutTimer >= cutDuration then
731 if g_currentMission:getIsServer() then
732 ChainsawUtil.cutSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId)
733 else
734 g_client:getServerConnection():sendEvent(ChainsawCutEvent.new(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId))
735 end
736
737 hasBeenCut = true
738 self.waitingForResetAfterCut = true
739 self.player.walkingIsLocked = false
740 self.curSplitShape = nil
741 self.curSplitShapeMinY = nil
742 self:updateRingSelector(0)
743 end
744 end
745 end
746 end
747 else
748 self.speedFactor = math.max(self.speedFactor - dt / self.maxWorkTime, 0)
749 self.waitingForResetAfterCut = false
750 self.player.walkingIsLocked = false
751 self.curSplitShape = nil
752 self.curSplitShapeMinY = nil
753 self.lastWorkTime = math.max(self.lastWorkTime - dt, 0)
754 self.workUpPlayed = false
755 end
756
757 self.player:lockInput(lockPlayerInput)
758
759 self:updateCuttingTimers(dt, isCutting)
760 self:updateCuttingCamera(isCutting)
761 self:updateChainsawModel(isCutting)
762 self:updateDelimb()
763 self:setCutting(isCutting, self.rotationZ > 0.7, hasBeenCut)
764 end
765 self.soundFSM:update(dt)
766 self:updateParticles()
767
768 self.rotateInput = 0
769 self.activatePressed = false
770end

updateChainsawModel

Description
Definition
updateChainsawModel(bool isCutting)
Arguments
boolisCutting
Code
522function Chainsaw:updateChainsawModel(isCutting)
523 local currentPos = {getWorldTranslation(self.graphicsNode)}
524
525 if isCutting then
526 local startPos = {}
527 local endPos = {}
528 startPos[1], startPos[2], startPos[3], endPos[1], endPos[2], endPos[3] = self:getCutStartEnd()
529
530 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then
531 self.startChainsawPosition = currentPos
532 self.endChainsawPosition = startPos
533 elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then
534 self.startChainsawPosition = startPos
535 self.endChainsawPosition = endPos
536 end
537 else
538 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then
539 self.startChainsawPosition = currentPos
540 setTranslation(self.graphicsNode, 0, 0, 0)
541 self.endChainsawPosition = {getWorldTranslation(self.graphicsNode)}
542 end
543 end
544
545 if isCutting or self.outTimer < self.outDuration then
546 local smoothPosition = { MathUtil.lerp( self.startChainsawPosition[1], self.endChainsawPosition[1], self.transitionAlpha ),
547 MathUtil.lerp( self.startChainsawPosition[2], self.endChainsawPosition[2], self.transitionAlpha ),
548 MathUtil.lerp( self.startChainsawPosition[3], self.endChainsawPosition[3], self.transitionAlpha )}
549 local offset = {localToLocal(self.cutPositionNode, self.graphicsNode, 0,0,0)}
550 local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 0, offset[3])}
551 local destination = {smoothPosition[1] - cutDirection[1], smoothPosition[2] - cutDirection[2], smoothPosition[3] - cutDirection[3]}
552 local modelTranslation = {worldToLocal(getParent(self.graphicsNode), destination[1], destination[2], destination[3])}
553 local distance = MathUtil.vector3Length(modelTranslation[1], modelTranslation[2], modelTranslation[3])
554
555 if distance > self.maxModelTranslation then
556 modelTranslation = {MathUtil.vector3Normalize(modelTranslation[1], modelTranslation[2], modelTranslation[3])}
557 modelTranslation = {modelTranslation[1]*self.maxModelTranslation, modelTranslation[2]*self.maxModelTranslation, modelTranslation[3]*self.maxModelTranslation}
558 local screen = { project(destination[1], destination[2], destination[3]) }
559 setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3])
560 local graph = {getWorldTranslation(self.graphicsNode)}
561 local screen2 = { project(graph[1], graph[2], graph[3]) }
562 local world2 = { unProject(screen[1], screen[2], screen2[3]) }
563
564 setWorldTranslation(self.graphicsNode, world2[1], world2[2], world2[3])
565 else
566 setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3])
567 end
568 else
569 setTranslation(self.graphicsNode, 0, 0, 0)
570 end
571end

updateCutRaycast

Description
Cast ray from the player camera
Definition
updateCutRaycast()
Code
302function Chainsaw:updateCutRaycast()
303 self.cutFocusDistance = -1.0
304
305 -- Reset
306 setTranslation(self.chainsawCameraFocus, 0, 0, 0)
307
308 local x, y, z = getWorldTranslation(self.player.cameraNode)
309
310 local dx, dy, dz = unProject(0.52, 0.4, 1) -- Transform vector from screen space into world space
311 dx, dy, dz = MathUtil.vector3Normalize(dx, dy, dz)
312
313 local treeCollisionMask = 16789504 -- bits: 12,13,24
314 raycastClosest(x, y, z, dx, dy, dz, "cutRaycastCallback", self.cutDetectionDistance, self, treeCollisionMask)
315end

updateCuttingCamera

Description
This function updates the timers: cuttimer and the outTimer, depending if the player is cutting or not: we switch from player camera to cutting camera as to not lose focus. We rotate the cutting camera towards the cut target. We the player has finished cutting we move the cutting camera towards the player camera and switch back to the player camera.
Definition
updateCuttingCamera(bool isCutting)
Arguments
boolisCuttingtrue if the player is cutting
Code
461function Chainsaw:updateCuttingCamera(isCutting)
462 -- if isCutting then
463 -- if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then
464 -- setRotation(self.player.model.cuttingCameraNode, 0, 0, 0)
465 -- local yx, yy, yz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 1, 0)
466 -- local zx, zy, zz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 0, 1)
467 -- local startX, startY, startZ, _, _, _ = self:getCutStartEnd()
468
469 -- self.startCameraDirectionY = {yx, yy, yz}
470 -- self.startCameraDirectionZ = {zx, zy, zz}
471 -- self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3],
472 -- self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.model.cuttingCameraNode, startX, startY, startZ)
473 -- elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then
474 -- local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd()
475
476 -- self.startCameraDirectionY[1], self.startCameraDirectionY[2], self.startCameraDirectionY[3],
477 -- self.startCameraDirectionZ[1], self.startCameraDirectionZ[2], self.startCameraDirectionZ[3] = self:getLookAt(self.player.model.cuttingCameraNode, startX, startY, startZ)
478 -- self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3],
479 -- self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.model.cuttingCameraNode, endX, endY, endZ)
480 -- end
481 -- else
482 -- if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then
483 -- local yx, yy, yz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 1, 0)
484 -- local zx, zy, zz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 0, 1)
485 -- self.startCameraDirectionY = {yx, yy, yz}
486 -- self.startCameraDirectionZ = {zx, zy, zz}
487 -- setRotation(self.player.cuttingCameraNode, 0, 0, 0)
488 -- yx, yy, yz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 1, 0)
489 -- zx, zy, zz = localDirectionToWorld(self.player.model.cuttingCameraNode, 0, 0, 1)
490 -- self.endCameraDirectionY = {yx, yy, yz}
491 -- self.endCameraDirectionZ = {zx, zy, zz}
492 -- end
493 -- end
494
495 -- local currentCamera = getCamera()
496 -- if isCutting or self.outTimer < self.outDuration then
497 -- if (currentCamera ~= self.player.model.cuttingCameraNode) then
498 -- setCamera(self.player.model.cuttingCameraNode)
499 -- end
500
501 -- local smoothDirY = { MathUtil.lerp(self.startCameraDirectionY[1], self.endCameraDirectionY[1], self.transitionAlpha),
502 -- MathUtil.lerp(self.startCameraDirectionY[2], self.endCameraDirectionY[2], self.transitionAlpha),
503 -- MathUtil.lerp(self.startCameraDirectionY[3], self.endCameraDirectionY[3], self.transitionAlpha) }
504 -- local smoothDirZ = { MathUtil.lerp(self.startCameraDirectionZ[1], self.endCameraDirectionZ[1], self.transitionAlpha),
505 -- MathUtil.lerp(self.startCameraDirectionZ[2], self.endCameraDirectionZ[2], self.transitionAlpha),
506 -- MathUtil.lerp(self.startCameraDirectionZ[3], self.endCameraDirectionZ[3], self.transitionAlpha) }
507
508 -- smoothDirY = { worldDirectionToLocal(getParent(self.player.model.cuttingCameraNode), smoothDirY[1], smoothDirY[2], smoothDirY[3]) }
509 -- smoothDirZ = { worldDirectionToLocal(getParent(self.player.model.cuttingCameraNode), smoothDirZ[1], smoothDirZ[2], smoothDirZ[3]) }
510 -- setDirection(self.player.model.cuttingCameraNode, smoothDirZ[1], smoothDirZ[2], smoothDirZ[3], smoothDirY[1], smoothDirY[2], smoothDirY[3])
511 -- else
512 -- if (currentCamera ~= self.player.cameraNode) then
513 -- setRotation(self.player.model.cuttingCameraNode, 0, 0, 0)
514 -- setCamera(self.player.cameraNode)
515 -- end
516 -- end
517end

updateCuttingTimers

Description
Definition
updateCuttingTimers(float dt, bool true)
Arguments
floatdtin milliseconds
booltrueif the player is cutting
Code
413function Chainsaw:updateCuttingTimers(dt, isCutting)
414 local dtInSec = dt * 0.001
415 self.transitionAlpha = 0.0
416
417 if isCutting then
418 local cutDuration = self:calculateCutDuration()
419 if (self.cutTimer == 0.0) then
420 self.outTimer = 0.0
421 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.IN
422 elseif self.cutTimer == self.inDuration then
423 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.CUT
424 end
425
426 if (self.cutTimer >= 0.0) and (self.cutTimer < self.inDuration) then
427 self.cutTimer = math.min(self.cutTimer + dtInSec, self.inDuration)
428 self.transitionAlpha = MathUtil.clamp(self.cutTimer, 0.0, self.inDuration) / self.inDuration
429 elseif (self.cutTimer >= self.inDuration) and (self.cutTimer < cutDuration) then
430 local restCutDuration = math.max(cutDuration - self.inDuration, 0)
431
432 self.cutTimer = math.min(self.cutTimer + dtInSec, cutDuration)
433 self.transitionAlpha = MathUtil.clamp(self.cutTimer - self.inDuration, 0, restCutDuration) / restCutDuration
434 else
435 self.transitionAlpha = 1.0
436 end
437 else
438 if self.outTimer == 0.0 then
439 self.cutTimer = 0.0
440 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.OUT
441 end
442
443 if (self.outTimer >= 0.0) and (self.outTimer < self.outDuration) then
444 self.outTimer = math.min(self.outTimer + dtInSec, self.outDuration)
445 self.transitionAlpha = MathUtil.clamp(self.outTimer, 0.0, self.outDuration) / self.outDuration
446 end
447 end
448end

updateDelimb

Description
Update delimb mechanic (removing leaves, ...)
Definition
updateDelimb()
Code
780function Chainsaw:updateDelimb()
781 if self.shouldDelimb then
782 local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation()
783 if g_currentMission:getIsServer() then
784 findAndRemoveSplitShapeAttachments(x, y, z, nx, ny, nz, yx, yy, yz, 0.7, self.cutSizeY, self.cutSizeZ)
785 else
786 g_client:getServerConnection():sendEvent(ChainsawDelimbEvent.new(self.player, x, y, z, nx, ny, nz, yx, yy, yz, false))
787 end
788 end
789end

updateParticles

Description
Update particle system
Definition
updateParticles()
Code
793function Chainsaw:updateParticles()
794 if self.particleSystems ~= nil then
795 local active = false
796 if self.isCutting and ((self.samplesBranchActiveTimer > g_currentMission.time) or (self.curSplitShapeMinY ~= nil and self.curSplitShapeMaxY ~= nil and self.cutTimer > self.inDuration)) then
797 active = true
798 end
799 if self.isCutting and self.player.isOwner then
800 active = true
801 end
802 for _, ps in pairs(self.particleSystems) do
803 ParticleUtil.setEmittingState(ps, active)
804 end
805 end
806end

updateRingSelector

Description
Update ring selector
Definition
updateRingSelector(integer shape)
Arguments
integershape
Code
827function Chainsaw:updateRingSelector(shape)
828 if self.ringSelector ~= nil then
829 local hasShape = (shape ~= nil) and (shape ~= 0)
830
831 if g_woodCuttingMarkerEnabled and hasShape then
832 local inDetectionRange = false
833 local inCutRange = false
834
835 if self.cutFocusDistance ~= nil and (self.cutFocusDistance >= 0.0 and self.cutFocusDistance < self.cutDetectionDistance) then
836 inDetectionRange = true
837 inCutRange = (self.cutFocusDistance >= self.minCutDistance and self.cutFocusDistance < self.maxCutDistance)
838 end
839
840 if not getVisibility(self.ringSelector) and inDetectionRange then
841 local x, y, z = getWorldTranslation(self.ringSelector)
842 if self:isCuttingAllowed(x, y, z, shape) then
843 setVisibility(self.ringSelector, true)
844 else
845 setVisibility(self.ringSelector, false)
846 end
847 elseif getVisibility(self.ringSelector) and not inDetectionRange then
848 setVisibility(self.ringSelector, false)
849 end
850
851 if getVisibility(self.ringSelector) then
852 if inCutRange then
853 setShaderParameter(self.ringSelector, "colorScale", 0.395, 0.925, 0.115, 1, false)
854 else
855 setShaderParameter(self.ringSelector, "colorScale", 0.098, 0.450, 0.960, 1, false)
856 end
857
858 if self.curSplitShapeMinY ~= nil then
859 local scale = math.max(self.curSplitShapeMaxY - self.curSplitShapeMinY + self.ringSelectorScaleOffset, self.curSplitShapeMaxZ-self.curSplitShapeMinZ + self.ringSelectorScaleOffset)
860 setScale(self.ringSelector, 1, scale, scale)
861
862 local a,b,c = localToWorld(self.chainsawSplitShapeFocus, 0, (self.curSplitShapeMinY+self.curSplitShapeMaxY)*0.5, (self.curSplitShapeMinZ+self.curSplitShapeMaxZ)*0.5)
863 local x, y, z = worldToLocal(getParent(self.ringSelector), a,b,c)
864 setTranslation(self.ringSelector, x,y,z)
865 else
866 setScale(self.ringSelector, 1, 1, 1)
867 end
868 end
869 else
870 setVisibility(self.ringSelector, false)
871 end
872 end
873end