43 | function 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 |
201 | end |
791 | function 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 |
827 | end |
262 | function 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 |
284 | end |
532 | function 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 |
697 | end |
459 | function 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 |
508 | end |
397 | function 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 |
454 | end |
738 | function 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 |
783 | end |