Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- WoodHarvester |
3 | -- This is the specialization for wood harvesters |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 14/09/14 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | source("dataS/scripts/vehicles/specializations/WoodHarvesterCutTreeEvent.lua") |
11 | source("dataS/scripts/vehicles/specializations/WoodHarvesterOnCutTreeEvent.lua") |
12 | source("dataS/scripts/vehicles/specializations/WoodHarvesterOnDelimbTreeEvent.lua") |
13 | |
14 | WoodHarvester = {}; |
15 | |
16 | function WoodHarvester.prerequisitesPresent(specializations) |
17 | return SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations); |
18 | end |
19 | |
20 | function WoodHarvester:load(xmlFile) |
21 | |
22 | self.woodHarvesterSplitShapeCallback = WoodHarvester.woodHarvesterSplitShapeCallback; |
23 | |
24 | self.cutTree = SpecializationUtil.callSpecializationsFunction("cutTree"); |
25 | self.onCutTree = SpecializationUtil.callSpecializationsFunction("onCutTree"); |
26 | self.onDelimbTree = SpecializationUtil.callSpecializationsFunction("onDelimbTree"); |
27 | |
28 | self.curSplitShape = nil; |
29 | self.attachedSplitShape = nil; |
30 | self.hasAttachedSplitShape = false; |
31 | self.isAttachedSplitShapeMoving = false; |
32 | self.attachedSplitShapeX = 0; |
33 | self.attachedSplitShapeY = 0; |
34 | self.attachedSplitShapeZ = 0; |
35 | self.attachedSplitShapeTargetY = 0; |
36 | self.attachedSplitShapeLastCutY = 0; |
37 | self.attachedSplitShapeStartY = 0; |
38 | self.cutTimer = -1; |
39 | self.cutTimer = -1; |
40 | |
41 | self.cutNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.cutNode#node")); |
42 | self.cutMaxRadius = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#maxRadius"), 1); |
43 | self.cutSizeY = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#sizeY"), 1); |
44 | self.cutSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#sizeZ"), 1); |
45 | self.cutAttachNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.cutNode#attachNode")); |
46 | self.cutAttachReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.cutNode#attachReferenceNode")); |
47 | self.cutAttachMoveSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#attachMoveSpeed"), 3)*0.001; |
48 | local cutReleasedComponentJointIndex = getXMLInt(xmlFile, "vehicle.cutNode#releasedComponentJointIndex"); |
49 | if cutReleasedComponentJointIndex ~= nil then |
50 | self.cutReleasedComponentJoint = self.componentJoints[cutReleasedComponentJointIndex+1]; |
51 | self.cutReleasedComponentJointRotLimitX = 0; |
52 | self.cutReleasedComponentJointRotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001); |
53 | end |
54 | local cutReleasedComponentJoint2Index = getXMLInt(xmlFile, "vehicle.cutNode#releasedComponentJoint2Index"); |
55 | if cutReleasedComponentJoint2Index ~= nil then |
56 | self.cutReleasedComponentJoint2 = self.componentJoints[cutReleasedComponentJoint2Index+1]; |
57 | self.cutReleasedComponentJoint2RotLimitX = 0; |
58 | self.cutReleasedComponentJoint2RotLimitXSpeed = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutNode#releasedComponentJointRotLimitXSpeed"), 100)*0.001); |
59 | end |
60 | |
61 | if self.cutAttachReferenceNode ~= nil and self.cutAttachNode ~= nil then |
62 | self.cutAttachHelperNode = createTransformGroup("helper"); |
63 | link(self.cutAttachReferenceNode, self.cutAttachHelperNode); |
64 | setTranslation(self.cutAttachHelperNode, 0,0,0); |
65 | setRotation(self.cutAttachHelperNode, 0,0,0); |
66 | end |
67 | |
68 | self.delimbNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.delimbNode#node")); |
69 | self.delimbSizeX = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.delimbNode#sizeX"), 0.1); |
70 | self.delimbSizeY = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.delimbNode#sizeY"), 1); |
71 | self.delimbSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.delimbNode#sizeZ"), 1); |
72 | self.delimbOnCut = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.delimbNode#delimbOnCut"), false); |
73 | |
74 | self.cutLengthMin = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutLengths#min"), 1); |
75 | self.cutLengthMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutLengths#max"), 5); |
76 | self.cutLengthStep = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutLengths#step"), 0.5); |
77 | |
78 | self.cutParticleSystems = {}; |
79 | local i = 0; |
80 | while true do |
81 | local keyPS = string.format("vehicle.cutParticleSystems.cutParticleSystem(%d)", i); |
82 | if not hasXMLProperty(xmlFile, keyPS) then |
83 | break; |
84 | end; |
85 | local currentPS = {}; |
86 | local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, keyPS, self.components, false, nil, self.baseDirectory); |
87 | table.insert(self.cutParticleSystems, currentPS); |
88 | i = i + 1; |
89 | end; |
90 | |
91 | self.delimbParticleSystems = {}; |
92 | local i = 0; |
93 | while true do |
94 | local keyPS = string.format("vehicle.delimbParticleSystems.delimbParticleSystem(%d)", i); |
95 | if not hasXMLProperty(xmlFile, keyPS) then |
96 | break; |
97 | end; |
98 | local currentPS = {}; |
99 | local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, keyPS, self.components, false, nil, self.baseDirectory); |
100 | table.insert(self.delimbParticleSystems, currentPS); |
101 | i = i + 1; |
102 | end; |
103 | |
104 | self.cutAnimation = {}; |
105 | self.cutAnimation.name = getXMLString(xmlFile, "vehicle.cutAnimation#name"); |
106 | self.cutAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutAnimation#speedScale"), 1); |
107 | self.cutAnimation.cutTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cutAnimation#cutTime"), 1); |
108 | |
109 | self.grabAnimation = {}; |
110 | self.grabAnimation.name = getXMLString(xmlFile, "vehicle.grabAnimation#name"); |
111 | self.grabAnimation.speedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grabAnimation#speedScale"), 1); |
112 | |
113 | self.forwardingWheels = {}; |
114 | local i = 0; |
115 | while true do |
116 | local wheelString = string.format("vehicle.forwardingWheels.wheel(%d)",i); |
117 | if not hasXMLProperty(xmlFile, wheelString) then |
118 | break; |
119 | end; |
120 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, wheelString.."#index")); |
121 | local rotSpeed = Utils.getRadiansFromString(getXMLString(xmlFile, wheelString.."#rotSpeed"), 3); |
122 | local wheel = {node=node, rotSpeed=rotSpeed}; |
123 | table.insert(self.forwardingWheels, wheel); |
124 | i = i + 1; |
125 | end; |
126 | |
127 | self.treeSizeMeasure = {}; |
128 | self.treeSizeMeasure.node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.treeSizeMeasure#index")); |
129 | self.treeSizeMeasure.rotMaxRadius = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.treeSizeMeasure#rotMaxRadius"), 1); |
130 | |
131 | self.harvesterSounds = {}; |
132 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.harvesterSounds.delimbSound#node")); |
133 | self.harvesterSounds.delimbSample = Utils.loadSample(xmlFile, {}, "vehicle.harvesterSounds.delimbSound", nil, self.baseDirectory, node); |
134 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.harvesterSounds.delimbSound#node")); |
135 | self.harvesterSounds.cutSample = Utils.loadSample(xmlFile, {}, "vehicle.harvesterSounds.cutSound", nil, self.baseDirectory, node); |
136 | |
137 | self.motorSoundPitchOffsetFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.harvesterSounds#motorSoundPitchOffset"), 1.0); |
138 | self.motorRunSoundPitchOffsetFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.harvesterSounds#motorRunSoundPitchOffset"), 1.0); |
139 | |
140 | self.sampleMotor.pitchOffsetBackup = self.sampleMotor.pitchOffset; |
141 | self.sampleMotorRun.pitchOffsetBackup = self.sampleMotorRun.pitchOffset; |
142 | |
143 | self.warnInvalidTree = false; |
144 | self.warnInvalidTreeRadius = false; |
145 | |
146 | self.currentCutLength = self.cutLengthMin; |
147 | self.lastDiameter = 0; |
148 | end |
149 | |
150 | function WoodHarvester:delete() |
151 | if self.attachedSplitShapeJointIndex ~= nil then |
152 | removeJoint(self.attachedSplitShapeJointIndex); |
153 | self.attachedSplitShapeJointIndex = nil; |
154 | end; |
155 | if self.cutAttachHelperNode ~= nil then |
156 | delete(self.cutAttachHelperNode); |
157 | end |
158 | for _, particleSystem in pairs(self.cutParticleSystems) do |
159 | Utils.deleteParticleSystem(particleSystem); |
160 | end |
161 | for _, particleSystem in pairs(self.delimbParticleSystems) do |
162 | Utils.deleteParticleSystem(particleSystem); |
163 | end |
164 | Utils.deleteSample(self.harvesterSounds.delimbSample); |
165 | Utils.deleteSample(self.harvesterSounds.cutSample); |
166 | end |
167 | |
168 | function WoodHarvester:readStream(streamId, connection) |
169 | self.hasAttachedSplitShape = streamReadBool(streamId); |
170 | self.isAttachedSplitShapeMoving = streamReadBool(streamId); |
171 | end |
172 | |
173 | function WoodHarvester:writeStream(streamId, connection) |
174 | streamWriteBool(streamId, self.hasAttachedSplitShape); |
175 | streamWriteBool(streamId, self.isAttachedSplitShapeMoving); |
176 | end |
177 | |
178 | function WoodHarvester:readUpdateStream(streamId, timestamp, connection) |
179 | end |
180 | |
181 | function WoodHarvester:writeUpdateStream(streamId, connection, dirtyMask) |
182 | end |
183 | |
184 | function WoodHarvester:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) |
185 | return BaseMission.VEHICLE_LOAD_OK; |
186 | end; |
187 | |
188 | function WoodHarvester:getSaveAttributesAndNodes(nodeIdent) |
189 | local attributes = ''; |
190 | local nodes = ""; |
191 | return attributes, nodes; |
192 | end; |
193 | |
194 | function WoodHarvester:mouseEvent(posX, posY, isDown, isUp, button) |
195 | end |
196 | |
197 | function WoodHarvester:keyEvent(unicode, sym, modifier, isDown) |
198 | end |
199 | |
200 | function WoodHarvester:update(dt) |
201 | if self:getIsActive() then |
202 | -- Verify that the split shapes still exist (possible that someone has cut them) |
203 | if self.isServer then |
204 | local lostShape = false; |
205 | if self.attachedSplitShape ~= nil then |
206 | if not entityExists(self.attachedSplitShape) then |
207 | self.attachedSplitShape = nil; |
208 | self.attachedSplitShapeJointIndex = nil; |
209 | self.isAttachedSplitShapeMoving = false; |
210 | self.cutTimer = -1; |
211 | lostShape = true; |
212 | end |
213 | elseif self.curSplitShape ~= nil then |
214 | if not entityExists(self.curSplitShape) then |
215 | self.curSplitShape = nil; |
216 | lostShape = true; |
217 | end |
218 | end |
219 | if lostShape then |
220 | self:onCutTree(0); |
221 | if g_server ~= nil then |
222 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
223 | end; |
224 | end; |
225 | end; |
226 | |
227 | -- Check for input |
228 | if self:getIsActiveForInput(true) and self:getIsTurnedOn() then |
229 | if self.cutNode ~= nil then |
230 | if self.hasAttachedSplitShape then |
231 | if not self.isAttachedSplitShapeMoving and self:getAnimationTime(self.cutAnimation.name) == 1 then |
232 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then |
233 | self:cutTree(self.currentCutLength); |
234 | end |
235 | end |
236 | elseif self.curSplitShape ~= nil then |
237 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then |
238 | self:cutTree(0); |
239 | end |
240 | end |
241 | if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then |
242 | if not self.isAttachedSplitShapeMoving then |
243 | self.currentCutLength = self.currentCutLength + self.cutLengthStep; |
244 | if self.currentCutLength > self.cutLengthMax+0.0001 then |
245 | self.currentCutLength = self.cutLengthMin; |
246 | end |
247 | end; |
248 | end |
249 | end; |
250 | end |
251 | |
252 | -- |
253 | if self.isServer and (self.attachedSplitShape ~= nil or self.curSplitShape ~= nil) then |
254 | |
255 | local readyToCut = false; |
256 | if self.cutTimer > 0 then |
257 | if self.cutAnimation.name ~= nil then |
258 | if self:getAnimationTime(self.cutAnimation.name) > self.cutAnimation.cutTime then |
259 | self.cutTimer = 0; |
260 | end; |
261 | else |
262 | self.cutTimer = math.max(self.cutTimer - dt, 0); |
263 | end; |
264 | end; |
265 | local readyToCut = self.cutTimer == 0; |
266 | |
267 | -- cut |
268 | if readyToCut then |
269 | self.cutTimer = -1; |
270 | |
271 | local x,y,z = getWorldTranslation(self.cutNode); |
272 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
273 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
274 | |
275 | local currentSplitShape; |
276 | if self.attachedSplitShapeJointIndex ~= nil then |
277 | removeJoint(self.attachedSplitShapeJointIndex); |
278 | self.attachedSplitShapeJointIndex = nil; |
279 | currentSplitShape = self.attachedSplitShape; |
280 | self.attachedSplitShape = nil; |
281 | else |
282 | currentSplitShape = self.curSplitShape; |
283 | self.curSplitShape = nil; |
284 | end |
285 | |
286 | if self.delimbOnCut then |
287 | local xD,yD,zD = getWorldTranslation(self.delimbNode); |
288 | local nxD,nyD,nzD = localDirectionToWorld(self.delimbNode, 1,0,0); |
289 | local yxD,yyD,yzD = localDirectionToWorld(self.delimbNode, 0,1,0); |
290 | local vx,vy,vz = x-xD,y-yD,z-zD; |
291 | local sizeX = Utils.vector3Length(vx,vy,vz); |
292 | removeSplitShapeAttachments(currentSplitShape, xD+vx*0.5,yD+vy*0.5,zD+vz*0.5, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*0.7+self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
293 | end; |
294 | |
295 | self.attachedSplitShape = nil; |
296 | self.curSplitShape = nil; |
297 | splitShape(currentSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ, "woodHarvesterSplitShapeCallback", self); |
298 | |
299 | if self.attachedSplitShape == nil then |
300 | self:onCutTree(0); |
301 | if g_server ~= nil then |
302 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
303 | end; |
304 | else |
305 | if self.delimbOnCut then |
306 | local xD,yD,zD = getWorldTranslation(self.delimbNode); |
307 | local nxD,nyD,nzD = localDirectionToWorld(self.delimbNode, 1,0,0); |
308 | local yxD,yyD,yzD = localDirectionToWorld(self.delimbNode, 0,1,0); |
309 | local vx,vy,vz = x-xD,y-yD,z-zD; |
310 | local sizeX = Utils.vector3Length(vx,vy,vz); |
311 | removeSplitShapeAttachments(self.attachedSplitShape, xD+vx*3,yD+vy*3,zD+vz*3, nxD,nyD,nzD, yxD,yyD,yzD, sizeX*3+self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
312 | end; |
313 | end; |
314 | |
315 | end; |
316 | |
317 | -- delimb |
318 | if self.attachedSplitShape ~= nil and self.isAttachedSplitShapeMoving then |
319 | if self.delimbNode ~= nil then |
320 | local x,y,z = getWorldTranslation(self.delimbNode); |
321 | local nx,ny,nz = localDirectionToWorld(self.delimbNode, 1,0,0); |
322 | local yx,yy,yz = localDirectionToWorld(self.delimbNode, 0,1,0); |
323 | |
324 | removeSplitShapeAttachments(self.attachedSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.delimbSizeX, self.delimbSizeY, self.delimbSizeZ); |
325 | end |
326 | |
327 | if self.cutNode ~= nil and self.attachedSplitShapeJointIndex ~= nil then |
328 | local x,y,z = getWorldTranslation(self.cutAttachReferenceNode); |
329 | local nx,ny,nz = localDirectionToWorld(self.cutAttachReferenceNode, 0,1,0); |
330 | local _, lengthRem = getSplitShapePlaneExtents(self.attachedSplitShape, x,y,z, nx,ny,nz); |
331 | |
332 | if lengthRem == nil or lengthRem <= 0.1 then |
333 | |
334 | -- end of tree |
335 | removeJoint(self.attachedSplitShapeJointIndex); |
336 | self.attachedSplitShapeJointIndex = nil; |
337 | self.attachedSplitShape = nil; |
338 | |
339 | self:onDelimbTree(false); |
340 | if g_server ~= nil then |
341 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self); |
342 | end; |
343 | |
344 | self:onCutTree(0); |
345 | if g_server ~= nil then |
346 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
347 | end; |
348 | else |
349 | self.attachedSplitShapeY = self.attachedSplitShapeY + self.cutAttachMoveSpeed*dt; |
350 | |
351 | if self.attachedSplitShapeY >= self.attachedSplitShapeTargetY then |
352 | self.attachedSplitShapeY = self.attachedSplitShapeTargetY; |
353 | self:onDelimbTree(false); |
354 | if g_server ~= nil then |
355 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, false), nil, nil, self); |
356 | end; |
357 | end |
358 | if self.attachedSplitShapeJointIndex ~= nil then |
359 | --local x,y,z = localToWorld(self.attachedSplitShape, self.attachedSplitShapeX, self.attachedSplitShapeY, self.attachedSplitShapeZ); |
360 | --setJointPosition(self.attachedSplitShapeJointIndex, 1, x,y,z); |
361 | |
362 | local x,y,z = localToWorld(self.cutNode, 0.3,0,0); |
363 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
364 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
365 | local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
366 | if shape == self.attachedSplitShape then |
367 | local treeCenterX,treeCenterY,treeCenterZ = localToWorld(self.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5); |
368 | self.attachedSplitShapeX, _, self.attachedSplitShapeZ = worldToLocal(self.attachedSplitShape, treeCenterX,treeCenterY,treeCenterZ); |
369 | self.lastDiameter = (maxY-minY + maxZ-minZ)*0.5; |
370 | end; |
371 | local x,y,z = localToWorld(self.attachedSplitShape, self.attachedSplitShapeX, self.attachedSplitShapeY, self.attachedSplitShapeZ); |
372 | setJointPosition(self.attachedSplitShapeJointIndex, 1, x,y,z); |
373 | end; |
374 | end |
375 | end |
376 | end; |
377 | end; |
378 | end; |
379 | |
380 | -- PS and sound for cut and delimb |
381 | if self.isClient then |
382 | if self:getIsActive() then |
383 | -- cut |
384 | if self.cutAnimation.name ~= nil then |
385 | if self:getIsAnimationPlaying(self.cutAnimation.name) and self:getAnimationTime(self.cutAnimation.name) < self.cutAnimation.cutTime then |
386 | self.cutParticleSystemsActive = true; |
387 | for _,ps in pairs(self.cutParticleSystems) do |
388 | Utils.setEmittingState(ps, true); |
389 | end; |
390 | if self.harvesterSounds.cutSample.sound3D ~= nil then |
391 | setVisibility(self.harvesterSounds.cutSample.sound3D, true); |
392 | end; |
393 | else |
394 | self.cutParticleSystemsActive = false; |
395 | for _,ps in pairs(self.cutParticleSystems) do |
396 | Utils.setEmittingState(ps, false); |
397 | end; |
398 | if self.harvesterSounds.cutSample.sound3D ~= nil then |
399 | setVisibility(self.harvesterSounds.cutSample.sound3D, false); |
400 | end; |
401 | end; |
402 | end; |
403 | -- delimb |
404 | if self.isAttachedSplitShapeMoving then |
405 | if #self.forwardingWheels > 0 then |
406 | local f = dt/1000; |
407 | for _,wheel in pairs(self.forwardingWheels) do |
408 | if wheel.node ~= nil then |
409 | rotate(wheel.node, wheel.rotSpeed[1]*f, wheel.rotSpeed[2]*f, wheel.rotSpeed[3]*f); |
410 | end; |
411 | end; |
412 | end; |
413 | if self.harvesterSounds.delimbSample ~= nil and self.harvesterSounds.delimbSample.sound3D ~= nil then |
414 | setVisibility(self.harvesterSounds.delimbSample.sound3D, true); |
415 | end; |
416 | for _,ps in pairs(self.delimbParticleSystems) do |
417 | self.delimbParticleSystemsActive = true; |
418 | Utils.setEmittingState(ps, true); |
419 | end; |
420 | else |
421 | if self.harvesterSounds.delimbSample ~= nil and self.harvesterSounds.delimbSample.sound3D ~= nil then |
422 | setVisibility(self.harvesterSounds.delimbSample.sound3D, false); |
423 | end; |
424 | for _,ps in pairs(self.delimbParticleSystems) do |
425 | Utils.setEmittingState(ps, false); |
426 | end; |
427 | end; |
428 | else -- turn all off, done in onDeactivateSounds() |
429 | end |
430 | end; |
431 | end |
432 | |
433 | function WoodHarvester:updateTick(dt) |
434 | if self:getIsActive() then |
435 | self.warnInvalidTree = false; |
436 | self.warnInvalidTreeRadius = false; |
437 | |
438 | if self:getIsTurnedOn() then |
439 | if self.attachedSplitShape == nil and self.cutNode ~= nil then |
440 | local x,y,z = getWorldTranslation(self.cutNode); |
441 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
442 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
443 | |
444 | |
445 | if self.curSplitShape == nil and (self.cutReleasedComponentJoint == nil or self.cutReleasedComponentJointRotLimitX == 0) then |
446 | |
447 | local shape, minY, maxY, minZ, maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
448 | if shape ~= 0 then |
449 | local splitType = SplitUtil.splitTypes[getSplitType(shape)]; |
450 | if splitType == nil or not splitType.allowsWoodHarvester then |
451 | self.warnInvalidTree = true; |
452 | else |
453 | local treeDx,treeDy,treeDz = localDirectionToWorld(shape, 0,1,0); -- wood harvester trees always grow in the y direction |
454 | local cosTreeAngle = Utils.dotProduct(nx,ny,nz, treeDx,treeDy,treeDz); |
455 | -- Only allow cutting if the cut header is approximately parallel to the tree (70° offset) |
456 | if cosTreeAngle >= 0.35 then |
457 | local radius = math.max(maxY-minY, maxZ-minZ)*0.5 * cosTreeAngle; |
458 | if radius > self.cutMaxRadius then |
459 | self.warnInvalidTreeRadius = true; |
460 | else |
461 | self.lastDiameter = math.max(maxY-minY, maxZ-minZ); |
462 | self.curSplitShape = shape; |
463 | end |
464 | end |
465 | end |
466 | end |
467 | end |
468 | |
469 | if self.curSplitShape ~= nil then |
470 | local minY,maxY, minZ,maxZ = testSplitShape(self.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ) |
471 | if minY == nil then |
472 | self.curSplitShape = nil; |
473 | else |
474 | -- check if cut would be below y=0 (tree CoSy) |
475 | local cutTooLow = false; |
476 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,minY,minZ); |
477 | cutTooLow = cutTooLow or y < 0.01; |
478 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,minY,maxZ); |
479 | cutTooLow = cutTooLow or y < 0.01; |
480 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,minZ); |
481 | cutTooLow = cutTooLow or y < 0.01; |
482 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,maxZ); |
483 | cutTooLow = cutTooLow or y < 0.01; |
484 | if cutTooLow then |
485 | self.curSplitShape = nil; |
486 | end; |
487 | end |
488 | end |
489 | |
490 | if self.curSplitShape == nil and self.cutTimer > -1 then |
491 | self:onCutTree(0); |
492 | if g_server ~= nil then |
493 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, 0), nil, nil, self); |
494 | end; |
495 | end; |
496 | |
497 | end |
498 | end |
499 | |
500 | if self.isServer then |
501 | if self.attachedSplitShape == nil then |
502 | if self.cutReleasedComponentJoint ~= nil and self.cutReleasedComponentJointRotLimitX ~= 0 then |
503 | self.cutReleasedComponentJointRotLimitX = math.min(self.cutReleasedComponentJointRotLimitX+self.cutReleasedComponentJointRotLimitXSpeed*dt, 0); |
504 | setJointRotationLimit(self.cutReleasedComponentJoint.jointIndex, 0, true, self.cutReleasedComponentJointRotLimitX, 0); |
505 | end; |
506 | if self.cutReleasedComponentJoint2 ~= nil and self.cutReleasedComponentJoint2RotLimitX ~= 0 then |
507 | self.cutReleasedComponentJoint2RotLimitX = math.max(self.cutReleasedComponentJoint2RotLimitX-self.cutReleasedComponentJoint2RotLimitXSpeed*dt, 0); |
508 | setJointRotationLimit(self.cutReleasedComponentJoint2.jointIndex, 0, true, -self.cutReleasedComponentJoint2RotLimitX, self.cutReleasedComponentJoint2RotLimitX); |
509 | end; |
510 | end |
511 | end; |
512 | end |
513 | |
514 | if self.isServer then |
515 | if self.playDelayedGrabAnimationTime ~= nil then |
516 | if self.playDelayedGrabAnimationTime < g_currentMission.time then |
517 | self.playDelayedGrabAnimationTime = nil; |
518 | if self:getAnimationTime(self.grabAnimation.name) > 0 then |
519 | if self.grabAnimation.name ~= nil and self.attachedSplitShape == nil then |
520 | self:setAnimationStopTime(self.grabAnimation.name, 0); |
521 | self:playAnimation(self.grabAnimation.name, -self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), false); |
522 | end; |
523 | end; |
524 | end; |
525 | end; |
526 | end; |
527 | |
528 | end |
529 | |
530 | function WoodHarvester:draw() |
531 | if self:getIsActiveForInput(true) and self:getIsTurnedOn() then |
532 | if self.cutNode ~= nil then |
533 | if self.hasAttachedSplitShape then |
534 | if not self.isAttachedSplitShapeMoving and self:getAnimationTime(self.cutAnimation.name) == 1 then |
535 | g_currentMission:addHelpButtonText(g_i18n:getText("woodHarvester_cut"), InputBinding.IMPLEMENT_EXTRA2); |
536 | end |
537 | elseif self.curSplitShape ~= nil then |
538 | g_currentMission:addHelpButtonText(g_i18n:getText("woodHarvester_cut"), InputBinding.IMPLEMENT_EXTRA2); |
539 | end |
540 | if not self.isAttachedSplitShapeMoving then |
541 | g_currentMission:addHelpButtonText(string.format(g_i18n:getText("woodHarvester_cutLength"), string.format("%.1f",self.currentCutLength)), InputBinding.IMPLEMENT_EXTRA3); |
542 | end; |
543 | if self.warnInvalidTreeRadius then |
544 | g_currentMission:showBlinkingWarning(g_i18n:getText("InvalidTreeDiameterWarning"), 1000); |
545 | elseif self.warnInvalidTree then |
546 | g_currentMission:showBlinkingWarning(g_i18n:getText("InvalidTreeTypeWarning"), 1000); |
547 | end |
548 | end; |
549 | end |
550 | end |
551 | |
552 | function WoodHarvester:onDeactivate() |
553 | self.curSplitShape = nil; |
554 | self.lastDiameter = 0; |
555 | WoodHarvester.onDeactivateSounds(self); |
556 | end |
557 | |
558 | function WoodHarvester:onDeactivateSounds() |
559 | if self.isClient then |
560 | if self.delimbParticleSystemsActive then |
561 | self.delimbParticleSystemsActive = false; |
562 | for _,ps in pairs(self.delimbParticleSystems) do |
563 | Utils.setEmittingState(ps, false); |
564 | end; |
565 | end; |
566 | if self.cutParticleSystemsActive then |
567 | self.cutParticleSystemsActive = false; |
568 | for _,ps in pairs(self.cutParticleSystems) do |
569 | Utils.setEmittingState(ps, false); |
570 | end; |
571 | end; |
572 | if self.harvesterSounds.cutSample.sound3D ~= nil then |
573 | setVisibility(self.harvesterSounds.cutSample.sound3D, false); |
574 | end; |
575 | if self.harvesterSounds.delimbSample.sound3D ~= nil then |
576 | setVisibility(self.harvesterSounds.delimbSample.sound3D, false); |
577 | end; |
578 | end; |
579 | end |
580 | |
581 | function WoodHarvester:onTurnedOn(noEventSend) |
582 | self.playDelayedGrabAnimationTime = nil; |
583 | if self.grabAnimation.name ~= nil then |
584 | self:setAnimationStopTime(self.grabAnimation.name, 1); |
585 | self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true); |
586 | end; |
587 | self.sampleMotor.pitchOffset = self.sampleMotor.pitchOffset * self.motorSoundPitchOffsetFactor; |
588 | self.sampleMotorRun.pitchOffset = self.sampleMotorRun.pitchOffset * self.motorRunSoundPitchOffsetFactor; |
589 | end; |
590 | |
591 | function WoodHarvester:onTurnedOff(noEventSend) |
592 | self.curSplitShape = nil; |
593 | self.lastDiameter = 0; |
594 | self.attachedSplitShape = nil; |
595 | self.hasAttachedSplitShape = false; |
596 | self.isAttachedSplitShapeMoving = false; |
597 | if self.attachedSplitShapeJointIndex ~= nil then |
598 | removeJoint(self.attachedSplitShapeJointIndex); |
599 | self.attachedSplitShapeJointIndex = nil; |
600 | end; |
601 | -- first open the cutter |
602 | self.playDelayedGrabAnimationTime = g_currentMission.time + 500; |
603 | if self.grabAnimation.name ~= nil and self.attachedSplitShape == nil then |
604 | self:setAnimationStopTime(self.grabAnimation.name, 1); |
605 | self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true); |
606 | end; |
607 | self.sampleMotor.pitchOffset = self.sampleMotor.pitchOffsetBackup; |
608 | self.sampleMotorRun.pitchOffset = self.sampleMotorRun.pitchOffsetBackup; |
609 | end |
610 | |
611 | function WoodHarvester:cutTree(length, noEventSend) |
612 | WoodHarvesterCutTreeEvent.sendEvent(self, length, noEventSend); |
613 | if self.isServer then |
614 | if length == 0 then |
615 | |
616 | if self.attachedSplitShape ~= nil or self.curSplitShape ~= nil then |
617 | self.cutTimer = 100; |
618 | if self.cutAnimation.name ~= nil then |
619 | self:setAnimationTime(self.cutAnimation.name, 0, true); |
620 | self:playAnimation(self.cutAnimation.name, self.cutAnimation.speedScale, self:getAnimationTime(self.cutAnimation.name)); |
621 | end; |
622 | end; |
623 | |
624 | elseif length > 0 and self.attachedSplitShape ~= nil then |
625 | |
626 | self.attachedSplitShapeTargetY = self.attachedSplitShapeLastCutY + length; |
627 | self.delimbDistance = length; |
628 | |
629 | self:onDelimbTree(true); |
630 | if g_server ~= nil then |
631 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self, true), nil, nil, self); |
632 | end; |
633 | |
634 | end; |
635 | end; |
636 | |
637 | end; |
638 | |
639 | function WoodHarvester:onCutTree(radius) |
640 | if radius > 0 then |
641 | |
642 | if self.isClient then |
643 | if self.grabAnimation.name ~= nil then |
644 | local targetAnimTime = math.min( 1.0, radius / self.treeSizeMeasure.rotMaxRadius ); |
645 | |
646 | self:setAnimationStopTime(self.grabAnimation.name, targetAnimTime); |
647 | if targetAnimTime > self:getAnimationTime(self.grabAnimation.name) then |
648 | self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true); |
649 | else |
650 | self:playAnimation(self.grabAnimation.name, -self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true); |
651 | end; |
652 | end; |
653 | |
654 | self.lastDiameter = 2*radius; |
655 | end; |
656 | |
657 | self.hasAttachedSplitShape = true; |
658 | |
659 | else |
660 | if self.grabAnimation.name ~= nil then |
661 | self:setAnimationStopTime(self.grabAnimation.name, 1); |
662 | self:playAnimation(self.grabAnimation.name, self.grabAnimation.speedScale, self:getAnimationTime(self.grabAnimation.name), true); |
663 | end; |
664 | self.hasAttachedSplitShape = false; |
665 | self.cutTimer = -1; |
666 | end; |
667 | end; |
668 | |
669 | function WoodHarvester:onDelimbTree(state) |
670 | if state then |
671 | self.isAttachedSplitShapeMoving = true; |
672 | else |
673 | self.isAttachedSplitShapeMoving = false; |
674 | self:cutTree(0); |
675 | end; |
676 | end; |
677 | |
678 | function WoodHarvester:woodHarvesterSplitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ) |
679 | |
680 | if self.attachedSplitShape == nil and isAbove and not isBelow and self.cutAttachNode ~= nil and self.cutAttachReferenceNode ~= nil then |
681 | self.attachedSplitShape = shape; |
682 | |
683 | -- Current tree center (mid of cut area) |
684 | local treeCenterX,treeCenterY,treeCenterZ = localToWorld(self.cutNode, 0, (minY+maxY)*0.5, (minZ+maxZ)*0.5); |
685 | |
686 | -- Target tree center (half tree size in front of the reference node) |
687 | local x,y,z = localToWorld(self.cutAttachReferenceNode, 0, 0, (maxZ-minZ)*0.5); |
688 | |
689 | local dx,dy,dz = localDirectionToWorld(shape, 0,0,1); |
690 | |
691 | local upx,upy,upz = localDirectionToWorld(self.cutAttachReferenceNode, 0,1,0); |
692 | local sideX,sideY,sizeZ = Utils.crossProduct(upx,upy,upz, dx,dy,dz); |
693 | dx,dy,dz = Utils.crossProduct(sideX,sideY,sizeZ, upx,upy,upz); -- Note: we want the up axis to be exact, thus orthogonalize the direction here |
694 | Utils.setWorldDirection(self.cutAttachHelperNode, dx,dy,dz, upx,upy,upz, 2); |
695 | |
696 | local constr = JointConstructor:new(); |
697 | constr:setActors(self.cutAttachNode, shape); |
698 | -- Note: we assume that the direction of the tree is equal to the y axis |
699 | constr:setJointTransforms(self.cutAttachHelperNode, shape); |
700 | constr:setJointWorldPositions(x,y,z, treeCenterX,treeCenterY,treeCenterZ); |
701 | |
702 | constr:setRotationLimit(0, 0, 0); |
703 | constr:setRotationLimit(1, 0, 0); |
704 | constr:setRotationLimit(2, 0, 0); |
705 | |
706 | constr:setEnableCollision(false); |
707 | |
708 | self.attachedSplitShapeJointIndex = constr:finalize(); |
709 | |
710 | if self.cutReleasedComponentJoint ~= nil then |
711 | self.cutReleasedComponentJointRotLimitX = -math.pi*0.8; |
712 | setJointRotationLimit(self.cutReleasedComponentJoint.jointIndex, 0, true, self.cutReleasedComponentJointRotLimitX, 0); |
713 | end |
714 | if self.cutReleasedComponentJoint2 ~= nil then |
715 | self.cutReleasedComponentJoint2RotLimitX = math.pi*0.9; |
716 | setJointRotationLimit(self.cutReleasedComponentJoint2.jointIndex, 0, true, -self.cutReleasedComponentJoint2RotLimitX, self.cutReleasedComponentJoint2RotLimitX); |
717 | end |
718 | |
719 | self.attachedSplitShapeX, self.attachedSplitShapeY, self.attachedSplitShapeZ = worldToLocal(shape, treeCenterX,treeCenterY,treeCenterZ); |
720 | self.attachedSplitShapeLastCutY = self.attachedSplitShapeY; |
721 | self.attachedSplitShapeStartY = self.attachedSplitShapeY; |
722 | |
723 | local radius = ((maxY - minY) + (maxZ - minZ)) / 4; |
724 | self:onCutTree(radius); |
725 | if g_server ~= nil then |
726 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self, radius), nil, nil, self); |
727 | end; |
728 | else |
729 | end |
730 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de