Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
2 | |
3 | Chainsaw = {} |
4 | Chainsaw_mt = Class(Chainsaw, HandTool) |
5 | |
6 | InitStaticObjectClass(Chainsaw, "Chainsaw", ObjectIds.OBJECT_CHAINSAW); |
7 | |
8 | function Chainsaw:new(isServer, isClient, customMt) |
9 | local mt = customMt; |
10 | if mt == nil then |
11 | mt = Chainsaw_mt; |
12 | end; |
13 | |
14 | local self = HandTool:new(isServer, isClient, mt); |
15 | return self; |
16 | end; |
17 | |
18 | |
19 | function Chainsaw:load(xmlFilename, player) |
20 | if not Chainsaw:superClass().load(self, xmlFilename, player) then |
21 | return false; |
22 | end; |
23 | |
24 | local xmlFile = loadXMLFile("TempXML", xmlFilename); |
25 | |
26 | self.rotationZ = 0; |
27 | self.rotationSpeedZ = 0.003; |
28 | self.cutSizeY = 1.1; |
29 | self.cutSizeZ = 1; |
30 | self.isCutting = false; |
31 | self.waitingForResetAfterCut = false; |
32 | self.cutNode = getChildAt(self.rootNode, 0); |
33 | self.graphicsNode = getChildAt(self.cutNode, 0); |
34 | self.chainNode = getChildAt(self.graphicsNode, 0); |
35 | self.psNode = getChildAt(self.graphicsNode, 1); |
36 | |
37 | self.pricePerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.pricePerMinute"), 50) / 1000; |
38 | |
39 | if self.isClient then |
40 | self.particleSystems = {}; |
41 | Utils.loadParticleSystem(xmlFile, self.particleSystems, "handTool.particleSystem", self.psNode, false, "data/vehicles/particleAnimations/shared/chainsaw.i3d", self.baseDirectory); |
42 | |
43 | self.handNode = Utils.getNoNil(Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "handTool.handNode#index")), self.rootNode); |
44 | self.handNodeRotation = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.handNode#rotation"), "0 0 0"), 3); |
45 | |
46 | self.equipmentUVs = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.equipment#uvs"), "0 0"), 2); |
47 | |
48 | self.chain = Utils.loadScrollers(self.rootNode, xmlFile, "handTool.chain", {}, false); |
49 | table.getn(self.chain); |
50 | |
51 | self.sampleStart = Utils.loadSample(xmlFile, {}, "handTool.startSound", nil, self.baseDirectory); |
52 | self.sampleIdle = Utils.loadSample(xmlFile, {}, "handTool.idleSound", nil, self.baseDirectory); |
53 | self.sampleWorkUp = Utils.loadSample(xmlFile, {}, "handTool.workUpSound", nil, self.baseDirectory); |
54 | self.sampleWork = Utils.loadSample(xmlFile, {}, "handTool.workSound", nil, self.baseDirectory); |
55 | self.sampleWorkDown = Utils.loadSample(xmlFile, {}, "handTool.workDownSound", nil, self.baseDirectory); |
56 | self.sampleStop = Utils.loadSample(xmlFile, {}, "handTool.stopSound", nil, self.baseDirectory); |
57 | self.sampleQuickTap = Utils.loadSample(xmlFile, {}, "handTool.quickTapSound", nil, self.baseDirectory); |
58 | |
59 | self.soundIdlePitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.idleSound#pitchMax"), 2.0); |
60 | |
61 | self.samplesBranch = {}; |
62 | local i=0; |
63 | while true do |
64 | if not hasXMLProperty(xmlFile, string.format("handTool.branchSounds.branchSound(%d)#file",i)) then |
65 | break; |
66 | end; |
67 | sampleBranch = Utils.loadSample(xmlFile, {}, string.format("handTool.branchSounds.branchSound(%d)",i), nil, self.baseDirectory); |
68 | table.insert(self.samplesBranch, sampleBranch); |
69 | i = i + 1; |
70 | end; |
71 | self.samplesBranchCount = i; |
72 | self.samplesBranchActiveTimer = 0; |
73 | |
74 | local filename = getXMLString(xmlFile, "handTool.ringSelector#file"); |
75 | if filename ~= nil then |
76 | local i3dNode = Utils.loadSharedI3DFile(filename, self.baseDirectory, false, false, false); |
77 | if i3dNode ~= 0 then |
78 | self.ringSelectorFilename = filename; |
79 | self.ringSelector = getChildAt(i3dNode, 0); |
80 | self.ringSelectorScaleOffset = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.ringSelector#scaleOffset"), 0.3); |
81 | setVisibility(self.ringSelector, false); |
82 | link(self.cutNode, self.ringSelector); |
83 | delete(i3dNode); |
84 | end; |
85 | end; |
86 | end; |
87 | |
88 | self.needGloves = true; |
89 | self.needHelmet = true; |
90 | self.lastWorkTime = 0; |
91 | self.maxWorkTime = 300; |
92 | |
93 | self.speedFactor = 0; |
94 | |
95 | self.offsetY = 0; |
96 | self.moveSpeedY = 0.0001; |
97 | self.speedFactor = 0; |
98 | |
99 | self.startDuration = self.sampleStart.duration * 0.5; -- during the first half of the start sample the idle sample is faded in |
100 | self.startTime = 0; |
101 | |
102 | self.quickTapDuration = 0; |
103 | self.quickTapDurationTime = 250; -- mouse/button presses shorter than this will play the quick tap sound |
104 | self.lastQuickTapTime = 0; |
105 | self.quickTapMinDelay = 250; -- quick tap sound won't play again until after this amount of time |
106 | |
107 | self.workUpStartTime = 0; |
108 | self.workUpDuration = self.sampleWorkUp.duration - 20; -- shorter duration for transition between WorkUp & Work |
109 | self.workUpPlayed = false; |
110 | self.workPlaying = false; |
111 | |
112 | self.isCutting = false; |
113 | self.isHorizontalCut = false; |
114 | |
115 | self.currentHandNode = nil; |
116 | |
117 | delete(xmlFile); |
118 | |
119 | return true; |
120 | end; |
121 | |
122 | function Chainsaw:delete() |
123 | Chainsaw:superClass().delete(self); |
124 | if self.isClient then |
125 | if self.particleSystems ~= nil then |
126 | Utils.deleteParticleSystem(self.particleSystems); |
127 | end; |
128 | Utils.deleteSample(self.sampleStart); |
129 | Utils.deleteSample(self.sampleIdle); |
130 | Utils.deleteSample(self.sampleWorkUp); |
131 | Utils.deleteSample(self.sampleWork); |
132 | Utils.deleteSample(self.sampleWorkDown); |
133 | Utils.deleteSample(self.sampleStop); |
134 | Utils.deleteSample(self.sampleQuickTap); |
135 | for _,v in pairs(self.samplesBranch) do |
136 | Utils.deleteSample(v); |
137 | end; |
138 | if self.ringSelectorFilename ~= nil then |
139 | Utils.releaseSharedI3DFile(self.ringSelectorFilename, self.baseDirectory, true); |
140 | end; |
141 | end; |
142 | end; |
143 | |
144 | function Chainsaw:update(dt, allowInput) |
145 | Chainsaw:superClass().update(self, dt, allowInput); |
146 | |
147 | Utils.updateScrollers(self.chain, dt*self.speedFactor, true); |
148 | |
149 | if self.isServer then |
150 | local price = self.pricePerSecond * (dt / 1000); |
151 | g_currentMission.missionStats:updateStats("expenses", price); |
152 | g_currentMission:addSharedMoney(-price, "vehicleRunningCost"); |
153 | end; |
154 | |
155 | self.shouldDelimb = false; |
156 | |
157 | if allowInput then |
158 | local isCutting = false; |
159 | |
160 | if not Utils.isSamplePlaying(self.sampleStart, 1.5*dt) then |
161 | Utils.playSample(self.sampleIdle, 0, 0, nil); |
162 | Utils.playSample(self.sampleWork, 0, 0, 0); |
163 | Utils.setSampleVolume(self.sampleIdle, self.sampleIdle.volume); |
164 | else |
165 | local idleVolume = Utils.clamp((g_currentMission.time - self.startTime) / self.startDuration, 0, self.sampleIdle.volume); |
166 | Utils.setSampleVolume(self.sampleIdle, idleVolume); -- idle sound fades in during start sound |
167 | end; |
168 | |
169 | setRotation(self.graphicsNode, math.rad(math.random(-1, 1))*0.1, math.rad(math.random(-1, 1))*0.1, math.rad(-180)); |
170 | |
171 | if self.curSplitShape == nil then |
172 | local input = InputBinding.getDigitalInputAxis(InputBinding.AXIS_ROTATE_HANDTOOL); |
173 | if InputBinding.isAxisZero(input) then |
174 | input = InputBinding.getAnalogInputAxis(InputBinding.AXIS_ROTATE_HANDTOOL); |
175 | end; |
176 | self.player:lockInput(input ~= 0); |
177 | if input ~= 0 then |
178 | self.rotationZ = Utils.clamp(self.rotationZ + self.rotationSpeedZ*input*dt, -0.1, 1.57); |
179 | setRotation(self.rootNode, 0, 0, self.rotationZ); |
180 | self.player:setIKDirty(); |
181 | end |
182 | |
183 | self.offsetY = math.max(self.offsetY - 5*self.moveSpeedY*dt, 0); |
184 | setTranslation(self.graphicsNode, 0, self.offsetY, 0); |
185 | if self.offsetY ~= 0 then |
186 | self.player:setIKDirty(); |
187 | end; |
188 | end; |
189 | |
190 | local shape = 0; |
191 | if not self.waitingForResetAfterCut then |
192 | local x,y,z = getWorldTranslation(self.cutNode); |
193 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
194 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
195 | if self.curSplitShape ~= nil or self.offsetY == 0 then |
196 | local minY,maxY, minZ,maxZ; |
197 | if self.curSplitShape == nil or not entityExists(self.curSplitShape) then |
198 | self.curSplitShape = nil; |
199 | shape, minY,maxY, minZ,maxZ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
200 | |
201 | if shape ~= nil and shape ~= 0 then |
202 | local cutTooLow = false; |
203 | local x,y,z = localToLocal(self.cutNode, shape, 0,minY,minZ); |
204 | cutTooLow = cutTooLow or y < 0.01; |
205 | local x,y,z = localToLocal(self.cutNode, shape, 0,minY,maxZ); |
206 | cutTooLow = cutTooLow or y < 0.01; |
207 | local x,y,z = localToLocal(self.cutNode, shape, 0,maxY,minZ); |
208 | cutTooLow = cutTooLow or y < 0.01; |
209 | local x,y,z = localToLocal(self.cutNode, shape, 0,maxY,maxZ); |
210 | cutTooLow = cutTooLow or y < 0.01; |
211 | if cutTooLow then |
212 | self.player.walkingIsLocked = false; |
213 | self.curSplitShape = nil; |
214 | shape, minY,maxY, minZ,maxZ = 0, nil,nil, nil,nil; |
215 | end; |
216 | end; |
217 | end; |
218 | |
219 | self.curSplitShapeMinY = minY; |
220 | self.curSplitShapeMaxY = maxY; |
221 | self.curSplitShapeMinZ = minZ; |
222 | self.curSplitShapeMaxZ = maxZ; |
223 | end; |
224 | end; |
225 | |
226 | if self.ringSelector ~= nil then |
227 | setVisibility(self.ringSelector, (self.curSplitShapeMinY ~= nil or self.curSplitShape ~= nil) and g_woodCuttingMarkerEnabled); |
228 | if g_woodCuttingMarkerEnabled then |
229 | if self.curSplitShape ~= nil then |
230 | setShaderParameter(self.ringSelector, "colorScale", 0.395, 0.925, 0.115, 1, false); |
231 | else |
232 | setShaderParameter(self.ringSelector, "colorScale", 0.098, 0.450, 0.960, 1, false); |
233 | end; |
234 | if self.curSplitShapeMinY ~= nil then |
235 | local a,b,c = localToWorld(self.cutNode, 0, (self.curSplitShapeMinY+self.curSplitShapeMaxY)*0.5, (self.curSplitShapeMinZ+self.curSplitShapeMaxZ)*0.5); |
236 | x, y, z = worldToLocal(getParent(self.ringSelector), a,b,c); |
237 | setTranslation(self.ringSelector, x,y,z); |
238 | local scale = math.max(self.curSplitShapeMaxY-self.curSplitShapeMinY + self.ringSelectorScaleOffset, self.curSplitShapeMaxZ-self.curSplitShapeMinZ + self.ringSelectorScaleOffset); |
239 | setScale(self.ringSelector, 1, scale, scale); |
240 | end; |
241 | end; |
242 | end; |
243 | |
244 | if InputBinding.isPressed(InputBinding.ACTIVATE_HANDTOOL) then |
245 | self.quickTapDuration = self.quickTapDuration + dt; |
246 | |
247 | self.speedFactor = math.min(self.speedFactor + dt/self.maxWorkTime, 1); |
248 | |
249 | if self.quickTapDuration > self.quickTapDurationTime then |
250 | if not self.workUpPlayed then |
251 | self.workUpStartTime = g_currentMission.time; |
252 | Utils.playSample(self.sampleWorkUp, 1, 0, nil); |
253 | self.workUpPlayed = true; |
254 | end; |
255 | |
256 | self.lastWorkTime = math.min(self.lastWorkTime+dt, self.maxWorkTime); |
257 | |
258 | if g_currentMission.time - self.workUpStartTime >= self.workUpDuration then |
259 | if not Utils.isSamplePlaying(self.sampleWorkUp, 1.5*dt) -- TEST THIS |
260 | and not self.workPlaying then |
261 | Utils.setSampleVolume(self.sampleWork, 1); |
262 | self.workPlaying = true; |
263 | end; |
264 | end; |
265 | end; |
266 | |
267 | if not self.waitingForResetAfterCut then |
268 | self.shouldDelimb = true; |
269 | |
270 | local x,y,z = getWorldTranslation(self.cutNode); |
271 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
272 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
273 | |
274 | if self.curSplitShape ~= nil or self.offsetY == 0 then |
275 | if self.curSplitShape ~= nil and entityExists(self.curSplitShape) then |
276 | local minY,maxY, minZ,maxZ = testSplitShape(self.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
277 | if minY == nil then |
278 | -- cancel cutting if shape can't be cut anymore from current position |
279 | self.player.walkingIsLocked = false; |
280 | self.curSplitShape = nil; |
281 | Utils.setSamplePitch(self.sampleWork, 1); |
282 | else |
283 | local cutTooLow = false; |
284 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,minY,minZ); |
285 | cutTooLow = cutTooLow or y < 0.01; |
286 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,minY,maxZ); |
287 | cutTooLow = cutTooLow or y < 0.01; |
288 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,minZ); |
289 | cutTooLow = cutTooLow or y < 0.01; |
290 | local x,y,z = localToLocal(self.cutNode, self.curSplitShape, 0,maxY,maxZ); |
291 | cutTooLow = cutTooLow or y < 0.01; |
292 | if cutTooLow then |
293 | self.player.walkingIsLocked = false; |
294 | self.curSplitShape = nil; |
295 | end; |
296 | end |
297 | self.curSplitShapeMinY = minY; |
298 | self.curSplitShapeMaxY = maxY; |
299 | self.curSplitShapeMinZ = minZ; |
300 | self.curSplitShapeMaxZ = maxZ; |
301 | else |
302 | if shape ~= 0 then |
303 | self.player.walkingIsLocked = true; |
304 | self.curSplitShape = shape; |
305 | end |
306 | end |
307 | |
308 | if self.curSplitShape ~= nil then |
309 | isCutting = true; |
310 | self.offsetY = self.offsetY + self.moveSpeedY*dt; |
311 | setTranslation(self.graphicsNode, 0, self.offsetY, 0); |
312 | self.player:setIKDirty(); |
313 | |
314 | if self.offsetY > self.curSplitShapeMinY then |
315 | self.lastWorkTime = math.min(self.lastWorkTime, self.maxWorkTime*0.7); |
316 | Utils.setSamplePitch(self.sampleWork, 0.96); |
317 | end; |
318 | |
319 | if self.offsetY > self.curSplitShapeMaxY then |
320 | if g_currentMission:getIsServer() then |
321 | ChainsawUtil.cutSplitShape(self.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ); |
322 | else |
323 | g_client:getServerConnection():sendEvent(ChainsawCutEvent:new(self.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, self.cutSizeY, self.cutSizeZ)); |
324 | end |
325 | self.player.walkingIsLocked = false; |
326 | self.waitingForResetAfterCut = true; |
327 | self.curSplitShape = nil; |
328 | Utils.setSamplePitch(self.sampleWork, 1); |
329 | end |
330 | end |
331 | end |
332 | end |
333 | else |
334 | self.speedFactor = math.max(self.speedFactor - dt/self.maxWorkTime, 0); |
335 | self.waitingForResetAfterCut = false; |
336 | self.player.walkingIsLocked = false; |
337 | self.curSplitShape = nil; |
338 | self.lastWorkTime = math.max(self.lastWorkTime - dt, 0); |
339 | |
340 | if self.quickTapDuration > 0 then |
341 | if self.quickTapDuration < self.quickTapDurationTime then |
342 | if g_currentMission.time - self.lastQuickTapTime > self.quickTapMinDelay then |
343 | Utils.playSample(self.sampleQuickTap, 1, 0, nil); |
344 | self.lastQuickTapTime = g_currentMission.time; |
345 | end; |
346 | else |
347 | Utils.stopSample(self.sampleWorkUp, true); |
348 | Utils.setSampleVolume(self.sampleWork, 0); |
349 | self.workPlaying = false; |
350 | Utils.setSamplePitch(self.sampleWork, 1); |
351 | Utils.playSample(self.sampleWorkDown, 1, 0, nil); |
352 | end; |
353 | end; |
354 | |
355 | self.workUpPlayed = false; |
356 | self.quickTapDuration = 0; |
357 | end |
358 | |
359 | if (self.samplesBranchActiveTimer > g_currentMission.time) or |
360 | (self.curSplitShapeMinY ~= nil and self.curSplitShapeMaxY ~= nil and self.offsetY > self.curSplitShapeMinY and self.offsetY < self.curSplitShapeMaxY) |
361 | then |
362 | Utils.setEmittingState(self.particleSystems, true); |
363 | else |
364 | Utils.setEmittingState(self.particleSystems, false); |
365 | end; |
366 | |
367 | local idlePitch = Utils.lerp(self.sampleIdle.pitchOffset, self.soundIdlePitchMax, Utils.clamp(self.lastWorkTime/self.maxWorkTime, 0, 1)); |
368 | Utils.setSamplePitch(self.sampleIdle, idlePitch); |
369 | |
370 | self:setCutting(isCutting, self.rotationZ > 0.7); |
371 | end |
372 | end; |
373 | |
374 | function Chainsaw:updateTick(dt, allowInput) |
375 | Chainsaw:superClass().updateTick(self, dt, allowInput); |
376 | if self.isClient then |
377 | if self.shouldDelimb then |
378 | local x,y,z = getWorldTranslation(self.cutNode); |
379 | local nx,ny,nz = localDirectionToWorld(self.cutNode, 1,0,0); |
380 | local yx,yy,yz = localDirectionToWorld(self.cutNode, 0,1,0); |
381 | if g_server == nil then |
382 | g_client:getServerConnection():sendEvent(ChainsawDelimbEvent:new(self.player, x,y,z, nx,ny,nz, yx,yy,yz, false)); |
383 | else |
384 | local ret = findAndRemoveSplitShapeAttachments(x,y,z, nx,ny,nz, yx,yy,yz, 0.7, self.cutSizeY, self.cutSizeZ); |
385 | if ret then |
386 | self:setOnDelimb(true); |
387 | end; |
388 | end; |
389 | end; |
390 | end; |
391 | end; |
392 | |
393 | function Chainsaw:setCutting(isCutting, isHorizontalCut, noEventSend) |
394 | ChainsawStateEvent.sendEvent(self.player, isCutting, isHorizontalCut, noEventSend); |
395 | self.isCutting = isCutting; |
396 | self.isHorizontalCut = isHorizontalCut; |
397 | if g_currentMission.player ~= self.player then |
398 | self.player:setCuttingAnim(isCutting, isHorizontalCut); |
399 | end; |
400 | end; |
401 | |
402 | function Chainsaw:setOnDelimb(state) |
403 | if state == true then |
404 | if not (self.samplesBranchActiveTimer > g_currentMission.time) then |
405 | local idx = math.floor(math.random(1, self.samplesBranchCount)); |
406 | Utils.playSample(self.samplesBranch[idx], 1, 0, nil); |
407 | self.samplesBranchActiveTimer = g_currentMission.time + self.samplesBranch[idx].duration; |
408 | |
409 | self.lastWorkTime = math.max(0, self.lastWorkTime - 160); --10*dt); |
410 | end; |
411 | end; |
412 | end; |
413 | |
414 | function Chainsaw:draw() |
415 | Chainsaw:superClass().draw(self); |
416 | g_currentMission:addHelpButtonText(g_i18n:getText("ACTIVATE_HANDTOOL"), InputBinding.ACTIVATE_HANDTOOL); |
417 | if GS_IS_CONSOLE_VERSION then |
418 | g_currentMission:addHelpButtonText(g_i18n:getText("rotatePlaceable"), nil, InputBinding.AXIS_ROTATE_HANDTOOL); |
419 | end; |
420 | end; |
421 | |
422 | function Chainsaw:setHandNode(handNode) |
423 | Chainsaw:superClass().setHandNode(self, handNode); |
424 | if self.currentHandNode ~= handNode then |
425 | if g_currentMission.player ~= self.player then |
426 | link(handNode, self.rootNode); |
427 | self.currentHandNode = handNode; |
428 | setRotation(self.rootNode, unpack(self.handNodeRotation)); |
429 | local x,y,z = getWorldTranslation(self.handNode); |
430 | x,y,z = worldToLocal(getParent(self.rootNode), x,y,z); |
431 | local a,b,c = getTranslation(self.rootNode); |
432 | setTranslation(self.rootNode, a-x,b-y,c-z); |
433 | end; |
434 | end; |
435 | end; |
436 | |
437 | function Chainsaw:onActivate(allowInput) |
438 | Chainsaw:superClass().onActivate(self); |
439 | self.startTime = g_currentMission.time; |
440 | self.player:setEquipmentUVs(self.equipmentUVs); |
441 | if self.isClient and allowInput then |
442 | Utils.playSample(self.sampleStart, 1, 0, nil); |
443 | Utils.playSample(self.sampleIdle, 0, 0, 0); |
444 | Utils.playSample(self.sampleWork, 0, 0, 0); |
445 | end; |
446 | end; |
447 | |
448 | function Chainsaw:onDeactivate(allowInput) |
449 | Chainsaw:superClass().onDeactivate(self); |
450 | self.speedFactor = 0; |
451 | self.curSplitShape = nil; |
452 | self.player.walkingIsLocked = false; |
453 | if self.isClient then |
454 | if allowInput then |
455 | Utils.playSample(self.sampleStop, 1, 0, nil); |
456 | end; |
457 | Utils.setEmittingState(self.particleSystems, false); |
458 | Utils.stopSample(self.sampleStart, true); |
459 | Utils.stopSample(self.sampleIdle, true); |
460 | Utils.stopSample(self.sampleWorkUp, true); |
461 | Utils.stopSample(self.sampleWork, true); |
462 | Utils.stopSample(self.sampleWorkDown, true); |
463 | Utils.stopSample(self.sampleQuickTap, true); |
464 | end; |
465 | end; |
466 | |
467 | ChainsawUtil = {} |
468 | |
469 | function ChainsawUtil.cutSplitShape(shape, x,y,z, nx,ny,nz, yx,yy,yz, cutSizeY, cutSizeZ) |
470 | -- only add a cut joint if we do a horizontal cut |
471 | if math.abs(ny) < 0.96 then |
472 | ChainsawUtil.curSplitShapes = {}; |
473 | splitShape(shape, x,y,z, nx,ny,nz, yx,yy,yz, cutSizeY, cutSizeZ, "ChainsawUtil.cutSplitShapeCallback", nil); |
474 | else |
475 | ChainsawUtil.curSplitShapes = {}; |
476 | splitShape(shape, x,y,z, nx,ny,nz, yx,yy,yz, cutSizeY, cutSizeZ, "ChainsawUtil.cutSplitShapeCallback", nil); |
477 | |
478 | if table.getn(ChainsawUtil.curSplitShapes) == 2 then |
479 | local split0 = ChainsawUtil.curSplitShapes[1]; |
480 | local split1 = ChainsawUtil.curSplitShapes[2]; |
481 | local type0 = getRigidBodyType(split0.shape); |
482 | local type1 = getRigidBodyType(split1.shape); |
483 | local dynamicSplit = nil; |
484 | local staticSplit = nil; |
485 | if type0 == "Static" and type1 == "Dynamic" then |
486 | staticSplit = split0; |
487 | dynamicSplit = split1; |
488 | elseif type1 == "Static" and type0 == "Dynamic" then |
489 | staticSplit = split1; |
490 | dynamicSplit = split0; |
491 | end |
492 | if dynamicSplit ~= nil then |
493 | local distY = dynamicSplit.minY + (dynamicSplit.maxY-dynamicSplit.minY)*0.75; -- create hinge at 75% of the tree |
494 | local distZ = (dynamicSplit.minZ+dynamicSplit.maxZ)*0.5; |
495 | |
496 | local zx,zy,zz = Utils.crossProduct(nx,ny,nz, yx,yy,yz); |
497 | |
498 | |
499 | -- Create notch, Humboldt style |
500 | -- Calculate new normal as slerp between normal and direction (those are 90° apart) |
501 | local angle = math.rad(40); |
502 | local scale0 = math.sin(1.5707-angle); |
503 | local scale1 = math.sin(angle); |
504 | if dynamicSplit.isBelow then |
505 | -- rotate towards -y direction if the normal points downwards |
506 | scale1 = -scale1; |
507 | end |
508 | local nx2 = nx*scale0 + yx*scale1; |
509 | local ny2 = ny*scale0 + yy*scale1; |
510 | local nz2 = nz*scale0 + yz*scale1; |
511 | local yx2,yy2,yz2 = Utils.crossProduct(zx,zy,zz, nx2,ny2,nz2); |
512 | -- the cut point is at the hinge, slightly moved out of the tree |
513 | local cx = x + yx*distY - yx2*cutSizeY*0.1; |
514 | local cy = y + yy*distY - yy2*cutSizeY*0.1; |
515 | local cz = z + yz*distY - yz2*cutSizeY*0.1; |
516 | splitShape(staticSplit.shape, cx,cy,cz, nx2,ny2,nz2, yx2,yy2,yz2, cutSizeY*1.1, cutSizeZ); |
517 | |
518 | |
519 | -- create a joint in the middle of the hinge |
520 | local jx = x + yx*distY + zx*distZ; |
521 | local jy = y + yy*distY + zy*distZ; |
522 | local jz = z + yz*distY + zz*distZ; |
523 | |
524 | local constr = JointConstructor:new(); |
525 | constr:setActors(0, dynamicSplit.shape); |
526 | constr:setJointWorldAxes(nx,ny,nz, nx,ny,nz); |
527 | constr:setJointWorldNormals(yx,yy,yz, yx,yy,yz); |
528 | constr:setJointWorldPositions(jx, jy, jz, jx, jy, jz); |
529 | |
530 | constr:setRotationLimit(0, 0, 0); |
531 | constr:setTranslationLimit(0, false, 0, 0); |
532 | constr:setEnableCollision(true); |
533 | local jointIndex = constr:finalize(); |
534 | |
535 | --local torqueVal = 10; |
536 | --local tx,ty,tz = Utils.crossProduct(0,torqueVal,0, yx,yy, yz); |
537 | --addTorque(dynamicSplit.shape, tx,ty,tz); |
538 | |
539 | local ax,ay,az = Utils.crossProduct(0,0.8,0, yx,yy, yz); |
540 | setAngularVelocity(dynamicSplit.shape, ax,ay,az); |
541 | |
542 | --local f = 20; |
543 | --addForce(dynamicSplit.shape, yx*f,yy*f,yz*f, jx,jy+10,jz, false); |
544 | |
545 | TreePlantUtil.addTreeCutJoint(g_currentMission.plantedTrees, jointIndex, dynamicSplit.shape, nx,ny,nz, math.rad(45), 2000); |
546 | end |
547 | end |
548 | end |
549 | end |
550 | |
551 | function ChainsawUtil.cutSplitShapeCallback(unused, shape, isBelow, isAbove, minY, maxY, minZ, maxZ) |
552 | table.insert(ChainsawUtil.curSplitShapes, {shape=shape, isBelow=isBelow, isAbove=isAbove, minY=minY, maxY=maxY, minZ=minZ, maxZ=maxZ}); |
553 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de