LUADOC - Farming Simulator 19

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
336function Chainsaw:calculateCutDuration()
337 local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd()
338 local trunkWidthSq = MathUtil.vector3LengthSq(endX - startX, endY - startY, endZ - startZ)
339 trunkWidthSq = MathUtil.clamp(trunkWidthSq, 0.0, self.maxTrunkWidthSq)
340 local cutDuration = trunkWidthSq * self.defaultCutDuration / self.maxTrunkWidthSq
341 return cutDuration
342end

cutRaycastCallback

Description
Saves raycast information
Definition
cutRaycastCallback(integer hitObjectId, float x, float y, float z, float distance)
Arguments
integerhitObjectId
floatx
floaty
floatz
floatdistance
Code
234function Chainsaw:cutRaycastCallback(hitObjectId, x, y, z, distance)
235 setWorldTranslation(self.player.chainsawCameraFocus, x, y, z)
236 self.cutFocusDistance = distance
237end

delete

Description
Deleting chainsaw
Definition
delete()
Code
205function Chainsaw:delete()
206 if self.isClient then
207 ParticleUtil.deleteParticleSystems(self.particleSystems)
208
209 if self.ringSelector ~= nil then
210 delete(self.ringSelector)
211 end
212
213 g_soundManager:deleteSamples(self.samplesTree)
214 g_soundManager:deleteSamples(self.samplesBranch)
215 g_soundManager:deleteSamples(self.samples)
216 g_soundManager:deleteSamples(self.samplesQuicktap)
217
218 if self.ringSelectorFilename ~= nil then
219 g_i3DManager:releaseSharedI3DFile(self.ringSelectorFilename, self.baseDirectory, false)
220 end
221 g_animationManager:deleteAnimations(self.chains)
222 end
223
224 Chainsaw:superClass().delete(self)
225end

getChainSpeedFactor

Description
Definition
getChainSpeedFactor()
Code
831function Chainsaw:getChainSpeedFactor()
832 return self.speedFactor
833end

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
521function Chainsaw:getCutShapeInformation()
522 local x,y,z = getWorldTranslation(self.player.chainsawSplitShapeFocus)
523 local nx,ny,nz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 1,0,0)
524 local yx,yy,yz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 0,1,0)
525 return x, y, z, nx, ny, nz, yx, yy, yz
526end

getCutStartEnd

Description
Function to calculate the start and the end of the cut by using the ringSelector
Definition
getCutStartEnd()
Code
319function Chainsaw:getCutStartEnd()
320 local selectorPosition = {getWorldTranslation(self.ringSelector)}
321 local selectorScale = {getScale(self.ringSelector)}
322 local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 1, 0)}
323 local cutStartposition = { selectorPosition[1] - 0.5 * selectorScale[1] * cutDirection[1],
324 selectorPosition[2] - 0.5 * selectorScale[2] * cutDirection[2],
325 selectorPosition[3] - 0.5 * selectorScale[3] * cutDirection[3] }
326 local cutEndposition = { selectorPosition[1] + 0.5 * selectorScale[1] * cutDirection[1],
327 selectorPosition[2] + 0.5 * selectorScale[2] * cutDirection[2],
328 selectorPosition[3] + 0.5 * selectorScale[3] * cutDirection[3] }
329 return cutStartposition[1], cutStartposition[2], cutStartposition[3], cutEndposition[1], cutEndposition[2], cutEndposition[3]
330end

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
298function Chainsaw:getLookAt(cameraNode, targetX, targetY, targetZ)
299 local xx, xy, xz = 0, 0, 0
300 local yx, yy, yz = 0, 0, 0
301 local zx, zy, zz = 0, 0, 0
302 local nodePosition = {getWorldTranslation(cameraNode)}
303 local nodeUpDirection = {localDirectionToWorld(getParent(cameraNode), 0, -1, 0)}
304
305 zx = nodePosition[1] - targetX
306 zy = nodePosition[2] - targetY
307 zz = nodePosition[3] - targetZ
308 zx, zy, zz = MathUtil.vector3Normalize(zx, zy, zz)
309 xx, xy, xz = MathUtil.crossProduct(zx, zy, zz, nodeUpDirection[1], nodeUpDirection[2], nodeUpDirection[3])
310 xx, xy, xz = MathUtil.vector3Normalize(xx, xy, xz)
311 yx, yy, yz = MathUtil.crossProduct(zx, zy, zz, xx, xy, xz)
312 yx, yy, yz = MathUtil.vector3Normalize(yx, yy, yz)
313
314 return yx, yy, yz, zx, zy, zz
315end

isBeingUsed

Description
Definition
isBeingUsed()
Code
909function Chainsaw:isBeingUsed()
910 return self.isCutting
911end

load

Description
Load chainsaw from xml file
Definition
load(string xmlFilename, table player)
Arguments
stringxmlFilenamexml file name
tableplayerplayer
Return Values
booleansuccesssuccess
Code
43function Chainsaw:load(xmlFilename, player)
44 if not Chainsaw:superClass().load(self, xmlFilename, player) then
45 return false
46 end
47
48 local xmlFile = loadXMLFile("TempXML", xmlFilename)
49
50 self.rotateInput = 0
51 self.activatePressed = false
52 self.eventIdRotateHandtool = ""
53
54 self.rotationZ = 0
55 self.rotationSpeedZ = 0.003
56 self.cutSizeY = 1.1
57 self.cutSizeZ = 1.0
58 self.isCutting = false
59 self.waitingForResetAfterCut = false
60 self.cutNode = getChildAt(self.rootNode, 0)
61 self.graphicsNode = getChildAt(self.cutNode, 0)
62 self.chainNode = getChildAt(self.graphicsNode, 0)
63 self.psNode = getChildAt(self.graphicsNode, 1)
64 self.cutPositionNode = getChildAt(self.graphicsNode, 5)
65
66 self.pricePerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw.pricePerMinute"), 50) / 1000
67 self.quicktapThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw#quicktapThreshold"), 0.0) * 1000
68 if self.isClient then
69 self.particleSystems = {}
70
71 local i = 0
72 while true do
73 local keyPS = string.format("handTool.chainsaw.particleSystems.emitterShape(%d)", i)
74 if not hasXMLProperty(xmlFile, keyPS) then
75 break
76 end
77 local emitterShape = I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, keyPS.."#node"))
78 local particleType = getXMLString(xmlFile, keyPS.."#particleType")
79 if emitterShape ~= nil then
80 local fillType = FillType.WOODCHIPS
81 local particleSystem = g_particleSystemManager:getParticleSystem(fillType, particleType)
82 if particleSystem ~= nil then
83 table.insert(self.particleSystems, ParticleUtil.copyParticleSystem(xmlFile, keyPS, particleSystem, emitterShape))
84 end
85 end
86 i = i + 1
87 end
88
89 if #self.particleSystems == 0 then
90 self.particleSystems = nil
91 end
92
93 self.equipmentUVs = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.chainsaw.equipment#uvs"), "0 0"), 2)
94
95 self.chains = g_animationManager:loadAnimations(xmlFile, "handTool.chainsaw.chain", self.rootNode, self, nil)
96 self.samples = {}
97 self.samples.start = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "start", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
98 self.samples.idle = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "idle", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
99 self.samples.cutStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
100 self.samples.cutStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
101 self.samples.cutLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
102 self.samples.activeStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
103 self.samples.activeStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
104 self.samples.activeLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil)
105 player:loadHandToolStopSample(xmlFile, "handTool.chainsaw.sounds", "stop")
106 self.samplesQuicktap = {}
107 local j = 0
108 while true do
109 local sampleQuicktap = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds.quickTapSounds", string.format("quickTap(%d)", j), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
110 if sampleQuicktap == nil then
111 break
112 end
113 table.insert(self.samplesQuicktap, sampleQuicktap)
114 j = j + 1
115 end
116 self.samplesQuicktapCount = j
117
118 self.samplesTree = {}
119 self.samplesTree.cut = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "cut", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
120 --self.samplesTree.falling = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "falling", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
121 self.samplesBranch = {}
122 local k = 0
123 while true do
124 local sampleBranch = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.branchSounds", string.format("branch(%d)", k), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil)
125 if sampleBranch == nil then
126 break
127 end
128 table.insert(self.samplesBranch, sampleBranch)
129 k = k + 1
130 end
131 self.samplesBranchCount = k
132 self.samplesBranchActiveTimer = 0
133
134 self.samplesTreeLinkNode = createTransformGroup("cutSoundLinkNode")
135 link(self.cutNode, self.samplesTreeLinkNode)
136
137 if self.samplesTree.cut ~= nil and self.samplesTree.cut.soundNode ~= nil then
138 link(self.samplesTreeLinkNode, self.samplesTree.cut.soundNode)
139 --link(self.samplesTreeLinkNode, self.samplesTree.falling.soundNode)
140 end
141
142 self.soundFSM = FSMUtil.create()
143 self.soundFSM:addState(Chainsaw.SOUND_STATES.START, ChainsawSoundStateStart:new(Chainsaw.SOUND_STATES.START, self, self.soundFSM))
144 self.soundFSM:addState(Chainsaw.SOUND_STATES.STOP, ChainsawSoundStateStop:new(Chainsaw.SOUND_STATES.STOP, self, self.soundFSM))
145 self.soundFSM:addState(Chainsaw.SOUND_STATES.IDLE, ChainsawSoundStateIdle:new(Chainsaw.SOUND_STATES.IDLE, self, self.soundFSM))
146 self.soundFSM:addState(Chainsaw.SOUND_STATES.ACTIVE, ChainsawSoundStateActive:new(Chainsaw.SOUND_STATES.ACTIVE, self, self.soundFSM))
147 self.soundFSM:addState(Chainsaw.SOUND_STATES.CUT, ChainsawSoundStateCut:new(Chainsaw.SOUND_STATES.CUT, self, self.soundFSM))
148 self.soundFSM:addState(Chainsaw.SOUND_STATES.QUICKTAP, ChainsawSoundStateQuicktap:new(Chainsaw.SOUND_STATES.QUICKTAP, self, self.soundFSM))
149
150 local filename = getXMLString(xmlFile, "handTool.chainsaw.ringSelector#file")
151 if filename ~= nil then
152 local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false)
153 if i3dNode ~= 0 then
154 self.ringSelectorFilename = filename
155 self.ringSelector = getChildAt(i3dNode, 0)
156 self.ringSelectorScaleOffset = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw.ringSelector#scaleOffset"), 0.3)
157 setVisibility(self.ringSelector, false)
158 link(player.chainsawSplitShapeFocus, self.ringSelector)
159 delete(i3dNode)
160 end
161 end
162 end
163
164 if self.player ~= g_currentMission.player then
165 self.handNodePositionInCutting = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.handNode.thirdPersonCutting#position"), "0 0 0"), 3)
166 self.handNodeRotationInCutting = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.handNode.thirdPersonCutting#rotation"), "0 0 0"), 3)
167 self.referenceNodeInCutting = I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, "handTool.handNode.thirdPersonCutting#referenceNode"))
168 end
169
170 self.lastWorkTime = 0
171 self.maxWorkTime = 300
172
173 self.moveSpeedY = 0.0001
174 self.speedFactor = 0
175 self.defaultCutDuration = 8.0 -- in s
176 self.maxTrunkWidthSq = 1.0 -- in m
177 self.outDuration = 0.15 -- in s
178 self.inDuration = 0.15 -- in s
179 self.cutTimer = 0.0 -- in s
180 self.outTimer = self.outDuration -- in s
181 self.transitionAlpha = 0.0 -- [0,1]
182 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE -- 0=in 1=cut 2=out
183 self.minRotationZ = math.rad(90) -- in rad
184 self.maxRotationZ = math.rad(-90) -- in rad
185 self.maxModelTranslation = 0.0 -- in m
186 self.cutFocusDistance = -1.0
187 self.startCameraDirectionY = { 0, 1, 0 }
188 self.startCameraDirectionZ = { 0, 0, 1 }
189 self.endCameraDirectionY = { 0, 1, 0 }
190 self.endCameraDirectionZ = { 0, 0, 1 }
191 self.startChainsawPosition = { 0, 0, 0 }
192 self.endChainsawPosition = { 0, 0, 0 }
193 self.showNotOwnedWarning = false
194
195 self.isCutting = false
196 self.isHorizontalCut = false
197
198 delete(xmlFile)
199
200 return true
201end

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
33function Chainsaw:new(isServer, isClient, customMt)
34 local self = HandTool:new(isServer, isClient, customMt or Chainsaw_mt)
35 return self
36end

onActivate

Description
On activate
Definition
onActivate(boolean allowInput)
Arguments
booleanallowInputallow input
Code
838function Chainsaw:onActivate(allowInput)
839 Chainsaw:superClass().onActivate(self)
840
841 self.rotationZ = 0.0
842 setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3])
843 setRotation(self.player.chainsawCameraFocus, 0, 0, self.rotationZ)
844
845 self.startTime = g_currentMission.time
846 if not self.player.isOwner then
847 self.player.visualInformation:setProtectiveUV(self.equipmentUVs)
848 self.player:setWoodWorkVisibility(true)
849 end
850
851 if self.isClient then
852 g_animationManager:startAnimations(self.chains)
853 end
854
855 self.cutTimer = 0.0
856 setTranslation(self.graphicsNode, 0, 0, 0)
857
858 self.soundFSM:changeState(Chainsaw.SOUND_STATES.START)
859end

onDeactivate

Description
On deactivate
Definition
onDeactivate(boolean allowInput)
Arguments
booleanallowInputallow input
Code
864function Chainsaw:onDeactivate(allowInput)
865 Chainsaw:superClass().onDeactivate(self)
866
867 self.speedFactor = 0
868 self.curSplitShape = nil
869 self.player:lockInput(false)
870 self.player.walkingIsLocked = false
871 self.player:setWoodWorkVisibility(false)
872 if self.isClient then
873 g_animationManager:stopAnimations(self.chains)
874 self.cutTimer = 0.0
875 setTranslation(self.graphicsNode, 0, 0, 0)
876 if self.particleSystems ~= nil then
877 for _, ps in pairs(self.particleSystems) do
878 ParticleUtil.resetNumOfEmittedParticles(ps)
879 ParticleUtil.setEmittingState(ps, false)
880 end
881 end
882 if getVisibility(self.ringSelector) then
883 setVisibility(self.ringSelector, false)
884 end
885 end
886 self.soundFSM:changeState(Chainsaw.SOUND_STATES.STOP)
887end

onInputRotate

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

registerActionEvents

Description
Definition
registerActionEvents()
Code
891function Chainsaw:registerActionEvents()
892 Chainsaw:superClass().registerActionEvents(self, allowInput)
893 g_inputBinding:beginActionEventsModification(Player.INPUT_CONTEXT_NAME)
894 local eventId = ""
895 _, eventId = g_inputBinding:registerActionEvent(InputAction.AXIS_ROTATE_HANDTOOL, self, self.onInputRotate, false, false, true, true)
896 g_inputBinding:setActionEventText(eventId, g_i18n:getText("action_rotate"))
897 self.eventIdRotateHandtool = eventId
898 g_inputBinding:endActionEventsModification()
899end

resetTransitionState

Description
Definition
resetTransitionState()
Code
388function Chainsaw:resetTransitionState()
389 if (self.cameraTransitionState ~= Chainsaw.CAMERA_TRANSITION_STATES.NONE) then
390 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE
391 end
392end

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
791function Chainsaw:setCutting(isCutting, isHorizontalCut, hasBeenCut, noEventSend)
792 ChainsawStateEvent.sendEvent(self.player, isCutting, isHorizontalCut, hasBeenCut, noEventSend)
793
794 if not self.player.isOwner then
795 self.player:setCuttingAnim(isCutting, isHorizontalCut)
796
797 if self.isCutting ~= isCutting then
798 if isCutting then
799 -- switch to cutting node
800 setTranslation(self.handNode, unpack(self.handNodePositionInCutting))
801 setRotation(self.handNode, unpack(self.handNodeRotationInCutting))
802
803 if self.referenceNodeInCutting ~= nil then
804 local x, y, z = getWorldTranslation(self.referenceNodeInCutting)
805 x, y, z = worldToLocal(getParent(self.handNode), x, y, z)
806 local a, b, c = getTranslation(self.handNode)
807 setTranslation(self.handNode, a - x, b - y, c - z)
808 end
809 else
810 -- switch to normal node
811 setTranslation(self.handNode, unpack(self.handNodePosition))
812 setRotation(self.handNode, unpack(self.handNodeRotation))
813
814 if self.referenceNode ~= nil then
815 local x, y, z = getWorldTranslation(self.referenceNode)
816 x, y, z = worldToLocal(getParent(self.handNode), x, y, z)
817 local a, b, c = getTranslation(self.handNode)
818 setTranslation(self.handNode, a - x, b - y, c - z)
819 end
820 end
821 end
822 end
823
824 self.isCutting = isCutting
825 self.isHorizontalCut = isHorizontalCut
826 self.hasBeenCut = hasBeenCut
827end

testTooLow

Description
Definition
testTooLow(integer shape, float minY, float maxY, float minZ, float maxZ)
Arguments
integershape
floatminY
floatmaxY
floatminZ
floatmaxZ
Return Values
bool
Code
262function Chainsaw:testTooLow(shape, minY, maxY, minZ, maxZ)
263 local cutTooLow = false
264 local _,y1,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,minY,minZ)
265 local _,y3,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,maxY,minZ)
266 local _,y4,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,maxY,maxZ)
267
268 cutTooLow = y1 < 0.01 or y1 < 0.01 or y3 < 0.03 or y4 < 0.01
269 if not cutTooLow then
270 local x1,y1,z1 = localToWorld(self.player.chainsawSplitShapeFocus, 0,minY,minZ)
271 local x2,y2,z2 = localToWorld(self.player.chainsawSplitShapeFocus, 0,minY,maxZ)
272 local x3,y3,z3 = localToWorld(self.player.chainsawSplitShapeFocus, 0,maxY,minZ)
273 local x4,y4,z4 = localToWorld(self.player.chainsawSplitShapeFocus, 0,maxY,maxZ)
274 local h1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1,y1,z1)
275 local h2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2,y2,z2)
276 local h3 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x3,y3,z3)
277 local h4 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x4,y4,z4)
278 cutTooLow = h1 < 0.01 or h2 < 0.01 or h3 < 0.03 or h4 < 0.01
279 end
280 if cutTooLow then
281 return true
282 end
283 return false
284end

update

Description
Update
Definition
update(float dt, boolean allowInput)
Arguments
floatdttime since last call in ms
booleanallowInputallow input
Code
532function Chainsaw:update(dt, allowInput)
533 Chainsaw:superClass().update(self, dt, allowInput)
534
535 if self.isServer then
536 local price = self.pricePerSecond * (dt / 1000)
537 g_farmManager:getFarmById(self.player.farmId).stats:updateStats("expenses", price)
538 g_currentMission:addMoney(-price, self.player.farmId, MoneyType.VEHICLE_RUNNING_COSTS)
539 end
540
541 if self.isClient then
542 if not self.isCutting then
543 self:updateCutRaycast()
544 end
545
546 if self.showNotOwnedWarning then
547 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 2000)
548 self.showNotOwnedWarning = false -- reset so it can be set to true later
549 end
550 end
551
552 self.shouldDelimb = false
553
554 local lockPlayerInput = false
555
556 if allowInput then
557 local isCutting = false
558 local hasBeenCut = false
559
560 setRotation(self.graphicsNode, math.rad(math.random(-1, 1)) * 0.1, math.rad(math.random(-1, 1)) * 0.1, math.rad(-180))
561
562 if self.curSplitShape == nil then
563 lockPlayerInput = self.rotateInput ~= 0
564
565 if self.rotateInput ~= 0 then
566 self.rotationZ = MathUtil.clamp(self.rotationZ + self.rotationSpeedZ * self.rotateInput * dt, self.maxRotationZ, self.minRotationZ )
567 setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3] -self.rotationZ)
568 setRotation(self.player.chainsawCameraFocus, 0, 0, -self.rotationZ)
569 end
570 end
571
572 local shape = 0
573 if not self.waitingForResetAfterCut then
574 if self.curSplitShape ~= nil or self.cutTimer == 0 then
575 local minY,maxY, minZ,maxZ
576 if self.curSplitShape == nil or not entityExists(self.curSplitShape) then
577 self.curSplitShape = nil
578
579 local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation()
580 shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ)
581
582 if shape ~= nil and shape ~= 0 then
583 if self:isCuttingAllowed(x, y, z) then
584 self.showNotOwnedWarning = false
585
586 local cutTooLow = self:testTooLow(shape, minY, maxY, minZ, maxZ)
587 local outsideRange = (self.cutFocusDistance < 0.0 or self.cutFocusDistance >= self.player.cutDetectionDistance)
588 if cutTooLow or outsideRange then
589 self.player.walkingIsLocked = false
590 self.curSplitShape = nil
591 shape, minY,maxY, minZ,maxZ = 0, nil,nil, nil,nil
592 end
593 else
594 self.showNotOwnedWarning = true
595 end
596 end
597 self.curSplitShapeMinY = minY
598 self.curSplitShapeMaxY = maxY
599 self.curSplitShapeMinZ = minZ
600 self.curSplitShapeMaxZ = maxZ
601 else
602 shape = self.curSplitShape
603 end
604
605 self:updateRingSelector(shape)
606 end
607 end
608
609 if self.activatePressed then
610 self.speedFactor = math.min(self.speedFactor + dt/self.maxWorkTime, 1)
611
612 if not self.waitingForResetAfterCut then
613 local inRange = (self.cutFocusDistance >= self.player.minCutDistance and self.cutFocusDistance < self.player.maxCutDistance)
614 self.shouldDelimb = inRange
615
616 if (self.curSplitShape ~= nil or self.cutTimer == 0) and inRange then
617 if self.curSplitShape ~= nil and entityExists(self.curSplitShape) then
618 lockPlayerInput = true
619 local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation()
620 local minY, maxY, minZ, maxZ = testSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ)
621
622 if minY == nil then
623 -- cancel cutting if shape can't be cut anymore from current position
624 self.player.walkingIsLocked = false
625 self.curSplitShape = nil
626 else
627 local cutTooLow = self:testTooLow(self.curSplitShape, minY,maxY, minZ,maxZ)
628 if cutTooLow then
629 self.player.walkingIsLocked = false
630 self.curSplitShape = nil
631 end
632 end
633
634 self.curSplitShapeMinY = minY
635 self.curSplitShapeMaxY = maxY
636 self.curSplitShapeMinZ = minZ
637 self.curSplitShapeMaxZ = maxZ
638 else
639 if shape ~= 0 then
640 self.player.walkingIsLocked = true
641 self.curSplitShape = shape
642 end
643 end
644
645 if self.curSplitShape ~= nil then
646 local x,y,z, nx,ny,nz, yx,yy,yz = self:getCutShapeInformation()
647
648 if self:isCuttingAllowed(x, y, z) then
649 isCutting = true
650 end
651
652 if self.cutTimer > 0 then
653 self.lastWorkTime = math.min(self.lastWorkTime, self.maxWorkTime*0.7)
654 end
655
656 local cutDuration = self:calculateCutDuration()
657 if self.cutTimer >= cutDuration then
658 if g_currentMission:getIsServer() then
659 ChainsawUtil.cutSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId)
660 else
661 g_client:getServerConnection():sendEvent(ChainsawCutEvent:new(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId))
662 end
663
664 hasBeenCut = true
665 self.waitingForResetAfterCut = true
666 self.player.walkingIsLocked = false
667 self.curSplitShape = nil
668 self.curSplitShapeMinY = nil
669 self:updateRingSelector(0)
670 end
671 end
672 end
673 end
674 else
675 self.speedFactor = math.max(self.speedFactor - dt / self.maxWorkTime, 0)
676 self.waitingForResetAfterCut = false
677 self.player.walkingIsLocked = false
678 self.curSplitShape = nil
679 self.curSplitShapeMinY = nil
680 self.lastWorkTime = math.max(self.lastWorkTime - dt, 0)
681 self.workUpPlayed = false
682 end
683
684 self.player:lockInput(lockPlayerInput)
685
686 self:updateCuttingTimers(dt, isCutting)
687 self:updateCuttingCamera(isCutting)
688 self:updateChainsawModel(isCutting)
689 self:updateDelimb()
690 self:setCutting(isCutting, self.rotationZ > 0.7, hasBeenCut)
691 end
692 self.soundFSM:update(dt)
693 self:updateParticles()
694
695 self.rotateInput = 0
696 self.activatePressed = false
697end

updateChainsawModel

Description
Definition
updateChainsawModel(bool isCutting)
Arguments
boolisCutting
Code
459function Chainsaw:updateChainsawModel(isCutting)
460 local currentPos = {getWorldTranslation(self.graphicsNode)}
461
462 if isCutting then
463 local startPos = {}
464 local endPos = {}
465 startPos[1], startPos[2], startPos[3], endPos[1], endPos[2], endPos[3] = self:getCutStartEnd()
466
467 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then
468 self.startChainsawPosition = currentPos
469 self.endChainsawPosition = startPos
470 elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then
471 self.startChainsawPosition = startPos
472 self.endChainsawPosition = endPos
473 end
474 else
475 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then
476 self.startChainsawPosition = currentPos
477 setTranslation(self.graphicsNode, 0, 0, 0)
478 self.endChainsawPosition = {getWorldTranslation(self.graphicsNode)}
479 end
480 end
481
482 if isCutting or self.outTimer < self.outDuration then
483 local smoothPosition = { MathUtil.lerp( self.startChainsawPosition[1], self.endChainsawPosition[1], self.transitionAlpha ),
484 MathUtil.lerp( self.startChainsawPosition[2], self.endChainsawPosition[2], self.transitionAlpha ),
485 MathUtil.lerp( self.startChainsawPosition[3], self.endChainsawPosition[3], self.transitionAlpha )}
486 local offset = {localToLocal(self.cutPositionNode, self.graphicsNode, 0,0,0)}
487 local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 0, offset[3])}
488 local destination = {smoothPosition[1] - cutDirection[1], smoothPosition[2] - cutDirection[2], smoothPosition[3] - cutDirection[3]}
489 local modelTranslation = {worldToLocal(getParent(self.graphicsNode), destination[1], destination[2], destination[3])}
490 local distance = MathUtil.vector3Length(modelTranslation[1], modelTranslation[2], modelTranslation[3])
491
492 if distance > self.maxModelTranslation then
493 modelTranslation = {MathUtil.vector3Normalize(modelTranslation[1], modelTranslation[2], modelTranslation[3])}
494 modelTranslation = {modelTranslation[1]*self.maxModelTranslation, modelTranslation[2]*self.maxModelTranslation, modelTranslation[3]*self.maxModelTranslation}
495 local screen = { project(destination[1], destination[2], destination[3]) }
496 setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3])
497 local graph = {getWorldTranslation(self.graphicsNode)}
498 local screen2 = { project(graph[1], graph[2], graph[3]) }
499 local world2 = { unProject(screen[1], screen[2], screen2[3]) }
500
501 setWorldTranslation(self.graphicsNode, world2[1], world2[2], world2[3])
502 else
503 setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3])
504 end
505 else
506 setTranslation(self.graphicsNode, 0, 0, 0)
507 end
508end

updateCutRaycast

Description
Cast ray from the player camera
Definition
updateCutRaycast()
Code
241function Chainsaw:updateCutRaycast()
242 self.cutFocusDistance = -1.0
243 setTranslation(self.player.chainsawCameraFocus, 0, 0, 0)
244 local cameraPosition = {getWorldTranslation(self.player.cameraNode)}
245 local worldDirection = {unProject(0.52, 0.4, 1)} -- Transform vector from screen space into world space
246 local treeCollisionMask = 16789504 -- bits: 12,13,24
247
248 worldDirection[1],
249 worldDirection[2],
250 worldDirection[3] = MathUtil.vector3Normalize(worldDirection[1], worldDirection[2], worldDirection[3])
251 raycastClosest(cameraPosition[1], cameraPosition[2], cameraPosition[3], worldDirection[1], worldDirection[2], worldDirection[3], "cutRaycastCallback", self.player.cutDetectionDistance, self, treeCollisionMask)
252end

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
397function Chainsaw:updateCuttingCamera(isCutting)
398 if isCutting then
399 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then
400 setRotation(self.player.cuttingCameraNode, 0, 0, 0)
401 local yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0)
402 local zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1)
403 local startX, startY, startZ, _, _, _ = self:getCutStartEnd()
404
405 self.startCameraDirectionY = {yx, yy, yz}
406 self.startCameraDirectionZ = {zx, zy, zz}
407 self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3],
408 self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, startX, startY, startZ)
409 elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then
410 local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd()
411
412 self.startCameraDirectionY[1], self.startCameraDirectionY[2], self.startCameraDirectionY[3],
413 self.startCameraDirectionZ[1], self.startCameraDirectionZ[2], self.startCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, startX, startY, startZ)
414 self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3],
415 self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, endX, endY, endZ)
416 end
417 else
418 if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then
419 local yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0)
420 local zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1)
421 self.startCameraDirectionY = {yx, yy, yz}
422 self.startCameraDirectionZ = {zx, zy, zz}
423 setRotation(self.player.cuttingCameraNode, 0, 0, 0)
424 yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0)
425 zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1)
426 self.endCameraDirectionY = {yx, yy, yz}
427 self.endCameraDirectionZ = {zx, zy, zz}
428 end
429 end
430
431 local currentCamera = getCamera()
432 if isCutting or self.outTimer < self.outDuration then
433 if (currentCamera ~= self.player.cuttingCameraNode) then
434 setCamera(self.player.cuttingCameraNode)
435 end
436
437 local smoothDirY = { MathUtil.lerp(self.startCameraDirectionY[1], self.endCameraDirectionY[1], self.transitionAlpha),
438 MathUtil.lerp(self.startCameraDirectionY[2], self.endCameraDirectionY[2], self.transitionAlpha),
439 MathUtil.lerp(self.startCameraDirectionY[3], self.endCameraDirectionY[3], self.transitionAlpha) }
440 local smoothDirZ = { MathUtil.lerp(self.startCameraDirectionZ[1], self.endCameraDirectionZ[1], self.transitionAlpha),
441 MathUtil.lerp(self.startCameraDirectionZ[2], self.endCameraDirectionZ[2], self.transitionAlpha),
442 MathUtil.lerp(self.startCameraDirectionZ[3], self.endCameraDirectionZ[3], self.transitionAlpha) }
443
444 smoothDirY = { worldDirectionToLocal(getParent(self.player.cuttingCameraNode), smoothDirY[1], smoothDirY[2], smoothDirY[3]) }
445 smoothDirZ = { worldDirectionToLocal(getParent(self.player.cuttingCameraNode), smoothDirZ[1], smoothDirZ[2], smoothDirZ[3]) }
446 local d,e,f = getWorldTranslation(self.player.chainsawSplitShapeFocus)
447 setDirection(self.player.cuttingCameraNode, smoothDirZ[1], smoothDirZ[2], smoothDirZ[3], smoothDirY[1], smoothDirY[2], smoothDirY[3])
448 else
449 if (currentCamera ~= self.player.cameraNode) then
450 setRotation(self.player.cuttingCameraNode, 0, 0, 0)
451 setCamera(self.player.cameraNode)
452 end
453 end
454end

updateCuttingTimers

Description
Definition
updateCuttingTimers(float dt, bool true)
Arguments
floatdtin milliseconds
booltrueif the player is cutting
Code
348function Chainsaw:updateCuttingTimers(dt, isCutting)
349 local dtInSec = dt * 0.001
350 self.transitionAlpha = 0.0
351
352 if isCutting then
353 local cutDuration = self:calculateCutDuration()
354 if (self.cutTimer == 0.0) then
355 self.outTimer = 0.0
356 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.IN
357 elseif self.cutTimer == self.inDuration then
358 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.CUT
359 end
360
361 if (self.cutTimer >= 0.0) and (self.cutTimer < self.inDuration) then
362 self.cutTimer = math.min(self.cutTimer + dtInSec, self.inDuration)
363 self.transitionAlpha = MathUtil.clamp(self.cutTimer, 0.0, self.inDuration) / self.inDuration
364 elseif (self.cutTimer >= self.inDuration) and (self.cutTimer < cutDuration) then
365 local restCutDuration = math.max(cutDuration - self.inDuration, 0)
366
367 self.cutTimer = math.min(self.cutTimer + dtInSec, cutDuration)
368 self.transitionAlpha = MathUtil.clamp(self.cutTimer - self.inDuration, 0, restCutDuration) / restCutDuration
369 else
370 self.transitionAlpha = 1.0
371 end
372 else
373 cutDuration = self.defaultCutDuration
374 if self.outTimer == 0.0 then
375 self.cutTimer = 0.0
376 self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.OUT
377 end
378
379 if (self.outTimer >= 0.0) and (self.outTimer < self.outDuration) then
380 self.outTimer = math.min(self.outTimer + dtInSec, self.outDuration)
381 self.transitionAlpha = MathUtil.clamp(self.outTimer, 0.0, self.outDuration) / self.outDuration
382 end
383 end
384end

updateDelimb

Description
Update delimb mechanic (removing leaves, ...)
Definition
updateDelimb()
Code
705function Chainsaw:updateDelimb()
706 if self.shouldDelimb then
707 local x,y,z = getWorldTranslation(self.player.chainsawSplitShapeFocus)
708 local nx,ny,nz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 1,0,0)
709 local yx,yy,yz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 0,1,0)
710 if g_currentMission:getIsServer() then
711 findAndRemoveSplitShapeAttachments(x, y, z, nx, ny, nz, yx, yy, yz, 0.7, self.cutSizeY, self.cutSizeZ)
712 else
713 g_client:getServerConnection():sendEvent(ChainsawDelimbEvent:new(self.player, x, y, z, nx, ny, nz, yx, yy, yz, false))
714 end
715 end
716end

updateParticles

Description
Update particle system
Definition
updateParticles()
Code
720function Chainsaw:updateParticles()
721 if self.particleSystems ~= nil then
722 local active = false
723 if self.isCutting and ((self.samplesBranchActiveTimer > g_currentMission.time) or (self.curSplitShapeMinY ~= nil and self.curSplitShapeMaxY ~= nil and self.cutTimer > self.inDuration)) then
724 active = true
725 end
726 if self.isCutting and self.player.isOwner then
727 active = true
728 end
729 for _, ps in pairs(self.particleSystems) do
730 ParticleUtil.setEmittingState(ps, active)
731 end
732 end
733end

updateRingSelector

Description
Update ring selector
Definition
updateRingSelector(integer shape)
Arguments
integershape
Code
738function Chainsaw:updateRingSelector(shape)
739 if self.ringSelector ~= nil then
740 local hasShape = (shape ~= nil) and (shape ~= 0)
741
742 if g_woodCuttingMarkerEnabled and hasShape then
743 local inDetectionRange = false
744 local inCutRange = false
745
746 if self.cutFocusDistance ~= nil and (self.cutFocusDistance >= 0.0 and self.cutFocusDistance < self.player.cutDetectionDistance) then
747 inDetectionRange = true
748 inCutRange = (self.cutFocusDistance >= self.player.minCutDistance and self.cutFocusDistance < self.player.maxCutDistance)
749 end
750 if not getVisibility(self.ringSelector) and inDetectionRange then
751 local x, y, z = getWorldTranslation(self.ringSelector)
752 if self:isCuttingAllowed(x, y, z) then
753 setVisibility(self.ringSelector, true)
754 else
755 setVisibility(self.ringSelector, false)
756 end
757 elseif getVisibility(self.ringSelector) and not inDetectionRange then
758 setVisibility(self.ringSelector, false)
759 end
760
761 if getVisibility(self.ringSelector) then
762 if inCutRange then
763 setShaderParameter(self.ringSelector, "colorScale", 0.395, 0.925, 0.115, 1, false)
764 else
765 setShaderParameter(self.ringSelector, "colorScale", 0.098, 0.450, 0.960, 1, false)
766 end
767
768 if self.curSplitShapeMinY ~= nil then
769 local scale = math.max(self.curSplitShapeMaxY - self.curSplitShapeMinY + self.ringSelectorScaleOffset, self.curSplitShapeMaxZ-self.curSplitShapeMinZ + self.ringSelectorScaleOffset)
770 setScale(self.ringSelector, 1, scale, scale)
771
772 local a,b,c = localToWorld(self.player.chainsawSplitShapeFocus, 0, (self.curSplitShapeMinY+self.curSplitShapeMaxY)*0.5, (self.curSplitShapeMinZ+self.curSplitShapeMaxZ)*0.5)
773 local x, y, z = worldToLocal(getParent(self.ringSelector), a,b,c)
774 setTranslation(self.ringSelector, x,y,z)
775 else
776 setScale(self.ringSelector, 1, 1, 1)
777 end
778 end
779 elseif getVisibility(self.ringSelector) then
780 setVisibility(self.ringSelector, false)
781 end
782 end
783end