64 | function 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 |
184 | end |
241 | function 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 |
451 | end |
455 | function 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 |
582 | end |
757 | function 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 |
816 | end |