43 | function HighPressureWasher:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
44 | if not HighPressureWasher:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
45 | return false; |
46 | end; |
47 | |
48 | local xmlFile = loadXMLFile("TempXML", xmlFilename); |
49 | |
50 | self.playerInRangeDistance = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.playerInRangeDistance"), 3); |
51 | |
52 | self.washDistance = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.washDistance"), 10); |
53 | self.actionRadius = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.actionRadius#distance"), 15); |
54 | self.washMultiplier = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.washMultiplier"), 1); |
55 | self.pricePerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.pricePerMinute"), 10) / 1000; |
56 | self.lanceNode = Utils.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.highPressureWasher.lance#index")); |
57 | self.linkPosition = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "placeable.highPressureWasher.lance#position"), "0 0 0"), 3); |
58 | self.linkRotation = Utils.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "placeable.highPressureWasher.lance#rotation"), "0 0 0"), 3); |
59 | self.lanceNodeParent = getParent(self.lanceNode); |
60 | |
61 | self.lanceRaycastNode = Utils.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.highPressureWasher.lance#raycastNode")); |
62 | |
63 | if self.isClient then |
64 | self.washerParticleSystems = {}; |
65 | |
66 | local i = 0 |
67 | while true do |
68 | local key = string.format("placeable.highPressureWasher.emitterShape(%d)", i); |
69 | if not hasXMLProperty(xmlFile, key) then |
70 | break |
71 | end |
72 | |
73 | local emitterShape = Utils.indexToObject(self.nodeId, getXMLString(xmlFile, key.."#node")); |
74 | local particleType = getXMLString(xmlFile, key.."#particleType") |
75 | if emitterShape ~= nil then |
76 | local fillType = FillUtil.FILLTYPE_WATER; |
77 | local particleSystem = MaterialUtil.getParticleSystem(fillType, particleType) |
78 | if particleSystem ~= nil then |
79 | table.insert(self.washerParticleSystems, ParticleUtil.copyParticleSystem(xmlFile, key, particleSystem, emitterShape)) |
80 | end |
81 | end |
82 | i = i + 1 |
83 | end |
84 | |
85 | self.waterEffects = EffectManager:loadEffect(xmlFile, "placeable.highPressureWasher.waterEffect", self.nodeId, self); |
86 | |
87 | |
88 | self.sampleCompressor = SoundUtil.loadSample(xmlFile, {}, "placeable.highPressureWasher.compressorSound", nil, self.baseDirectory, self.nodeId); |
89 | self.compressorPitchMin = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.compressorSound#pitchMin"), 0.5); |
90 | self.sampleWashing = SoundUtil.loadSample(xmlFile, {}, "placeable.highPressureWasher.washingSound", nil, self.baseDirectory); |
91 | self.sampleSwitch = SoundUtil.loadSample(xmlFile, {}, "placeable.highPressureWasher.switchSound", nil, self.baseDirectory); |
92 | |
93 | local filename = getXMLString(xmlFile, "placeable.highPressureWasher.exhaust#filename"); |
94 | if filename ~= nil then |
95 | local i3dNode = Utils.loadSharedI3DFile(filename, self.baseDirectory, false, false, false); |
96 | if i3dNode ~= 0 then |
97 | local linkNode = Utils.getNoNil(Utils.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.highPressureWasher.exhaust#index")), self.nodeId); |
98 | self.exhaustFilename = filename; |
99 | self.exhaustNode = getChildAt(i3dNode, 0); |
100 | link(linkNode, self.exhaustNode); |
101 | setVisibility(self.exhaustNode, false); |
102 | delete(i3dNode); |
103 | end; |
104 | end; |
105 | |
106 | self.targets = {}; |
107 | IKUtil.loadIKChainTargets(xmlFile, "placeable.highPressureWasher.targets", self.nodeId, self.targets); |
108 | end; |
109 | |
110 | delete(xmlFile); |
111 | |
112 | self.isPlayerInRange = false; |
113 | self.isTurnedOn = false; |
114 | self.activatable = HighPressureWasherActivatable:new(self); |
115 | self.doWashing = false; |
116 | self.lastInRangePosition = {0,0,0}; |
117 | self.isTurningOff = false; |
118 | self.turnOffTime = 0; |
119 | self.turnOffDuration = 500; |
120 | |
121 | return true; |
122 | end; |
126 | function HighPressureWasher:delete() |
127 | self:setIsTurnedOn(false, nil, false); |
128 | if self.isClient then |
129 | EffectManager:deleteEffects(self.waterEffects); |
130 | if self.exhaustFilename ~= nil then |
131 | Utils.releaseSharedI3DFile(self.exhaustFilename, self.baseDirectory, true); |
132 | end; |
133 | ParticleUtil.deleteParticleSystems(self.washerParticleSystems) |
134 | if self.sampleCompressor ~= nil then |
135 | SoundUtil.deleteSample(self.sampleCompressor); |
136 | end; |
137 | if self.sampleWashing ~= nil then |
138 | SoundUtil.deleteSample(self.sampleWashing); |
139 | end; |
140 | if self.sampleSwitch ~= nil then |
141 | SoundUtil.deleteSample(self.sampleSwitch); |
142 | end; |
143 | end; |
144 | |
145 | unregisterObjectClassName(self); |
146 | g_currentMission:removeActivatableObject(self.activatable); |
147 | HighPressureWasher:superClass().delete(self); |
148 | end; |
191 | function HighPressureWasher:update(dt) |
192 | HighPressureWasher:superClass().update(self, dt); |
193 | |
194 | if self.currentPlayer ~= nil then |
195 | local isPlayerInRange = self:getIsPlayerInRange(self.actionRadius, self.currentPlayer); |
196 | if isPlayerInRange then |
197 | self.lastInRangePosition = {getTranslation(self.currentPlayer.rootNode)}; |
198 | else |
199 | local kx, _, kz = getWorldTranslation(self.nodeId); |
200 | local px, py, pz = getWorldTranslation(self.currentPlayer.rootNode); |
201 | local len = Utils.vector2Length(px-kx, pz-kz); |
202 | |
203 | local x,y,z = unpack(self.lastInRangePosition); |
204 | x = kx + ((px-kx) / len) * (self.actionRadius-0.00001*dt); |
205 | z = kz + ((pz-kz) / len) * (self.actionRadius-0.00001*dt); |
206 | self.currentPlayer:moveToAbsoluteInternal(x, py, z); |
207 | self.lastInRangePosition = {x,y,z}; |
208 | |
209 | if not self.messageShown and self.currentPlayer == g_currentMission.player then |
210 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hpwRangeRestriction"), 6000); |
211 | self.messageShown = true; |
212 | end; |
213 | end; |
214 | end; |
215 | |
216 | if self.isServer then |
217 | if self.isTurnedOn and self.doWashing then |
218 | self.foundVehicle = nil; |
219 | self:cleanVehicle(self.currentPlayer.cameraNode, dt); |
220 | if self.lanceRaycastNode ~= nil then |
221 | self:cleanVehicle(self.lanceRaycastNode, dt); |
222 | end; |
223 | |
224 | local price = self.pricePerSecond * (dt / 1000); |
225 | g_currentMission.missionStats:updateStats("expenses", price); |
226 | g_currentMission:addSharedMoney(-price, "vehicleRunningCost"); |
227 | end; |
228 | end; |
229 | |
230 | if self.isTurningOff then |
231 | if g_currentMission.time < self.turnOffTime then |
232 | if self.sampleCompressor ~= nil then |
233 | local pitch = Utils.lerp(self.compressorPitchMin, self.sampleCompressor.pitchOffset, Utils.clamp((self.turnOffTime - g_currentMission.time) / self.turnOffDuration, 0, 1)); |
234 | local volume = Utils.lerp(0, self.sampleCompressor.volume, Utils.clamp((self.turnOffTime - g_currentMission.time) / self.turnOffDuration, 0, 1)); |
235 | SoundUtil.setSamplePitch(self.sampleCompressor, pitch); |
236 | SoundUtil.setSampleVolume(self.sampleCompressor, volume); |
237 | end; |
238 | else |
239 | self.isTurningOff = false; |
240 | if self.sampleCompressor ~= nil then |
241 | SoundUtil.stop3DSample(self.sampleCompressor); |
242 | end; |
243 | end; |
244 | end; |
245 | end; |
284 | function HighPressureWasher:setIsWashing(doWashing, force, noEventSend) |
285 | HPWPlaceableStateEvent.sendEvent(self, doWashing, noEventSend); |
286 | if self.doWashing ~= doWashing then |
287 | if self.isClient then |
288 | if self.washerParticleSystems ~= nil then |
289 | for _,ps in pairs(self.washerParticleSystems) do |
290 | ParticleUtil.setEmittingState(ps, doWashing and self:getIsActiveForInput()); |
291 | end |
292 | end; |
293 | |
294 | if doWashing then |
295 | EffectManager:setFillType(self.waterEffects, FillUtil.FILLTYPE_WATER) |
296 | EffectManager:startEffects(self.waterEffects) |
297 | if self.sampleWashing ~= nil and self.currentPlayer == g_currentMission.player then |
298 | if self:getIsActiveForSound() then |
299 | SoundUtil.playSample(self.sampleWashing, 0, 0, 1); |
300 | end; |
301 | end; |
302 | else |
303 | if force then |
304 | EffectManager:resetEffects(self.waterEffects); |
305 | else |
306 | EffectManager:stopEffects(self.waterEffects); |
307 | end; |
308 | if self.sampleWashing ~= nil then |
309 | SoundUtil.stopSample(self.sampleWashing, true); |
310 | end; |
311 | end; |
312 | end; |
313 | self.doWashing = doWashing; |
314 | end; |
315 | end; |
322 | function HighPressureWasher:setIsTurnedOn(isTurnedOn, player, noEventSend) |
323 | HPWPlaceableTurnOnEvent.sendEvent(self, isTurnedOn, player, noEventSend); |
324 | |
325 | if self.isTurnedOn ~= isTurnedOn then |
326 | if isTurnedOn then |
327 | self.isTurnedOn = isTurnedOn; |
328 | self.currentPlayer = player; |
329 | |
330 | -- player stuff |
331 | local tool = {}; |
332 | tool.node = self.lanceNode; |
333 | link(player.toolsRootNode, tool.node); |
334 | setVisibility(tool.node, false); |
335 | setTranslation(tool.node, unpack(self.linkPosition)); |
336 | setRotation(tool.node, unpack(self.linkRotation)); |
337 | tool.update = HighPressureWasher.updateLance; |
338 | tool.updateTick = HighPressureWasher.updateTickLance; |
339 | tool.delete = HighPressureWasher.deleteLance; |
340 | tool.draw = HighPressureWasher.drawLance; |
341 | tool.onActivate = HighPressureWasher.activateLance; |
342 | tool.onDeactivate = HighPressureWasher.deactivateLance; |
343 | tool.targets = self.targets; |
344 | tool.owner = self; |
345 | tool.static = false; |
346 | self.tool = tool; |
347 | self.currentPlayer:setTool(tool); |
348 | self.currentPlayer.hasHPWLance = true; |
349 | |
350 | if self.isClient then |
351 | if self.sampleSwitch ~= nil and self:getIsActiveForSound() then |
352 | SoundUtil.playSample(self.sampleSwitch, 1, 0, nil); |
353 | end; |
354 | if self.sampleCompressor ~= nil then |
355 | SoundUtil.setSamplePitch(self.sampleCompressor, self.sampleCompressor.pitchOffset); |
356 | SoundUtil.setSampleVolume(self.sampleCompressor, self.sampleCompressor.volume); |
357 | SoundUtil.play3DSample(self.sampleCompressor); |
358 | end; |
359 | if self.isTurningOff then |
360 | self.isTurningOff = false; |
361 | end; |
362 | setVisibility(self.lanceNode, g_currentMission.player == player); |
363 | end; |
364 | else |
365 | self:onDeactivate(); |
366 | end; |
367 | if self.exhaustNode ~= nil then |
368 | setVisibility(self.exhaustNode, isTurnedOn); |
369 | end; |
370 | end; |
371 | end; |
375 | function HighPressureWasher:onDeactivate() |
376 | if self.isClient then |
377 | if self.sampleSwitch ~= nil and self:getIsActiveForSound() then |
378 | SoundUtil.playSample(self.sampleSwitch, 1, 0, nil); |
379 | end; |
380 | end; |
381 | self.isTurnedOn = false; |
382 | setVisibility(self.lanceNode, true); |
383 | self:setIsWashing(false, true, true); |
384 | if self.currentPlayer ~= nil then |
385 | self.currentPlayer:setToolById(0, true); |
386 | self.currentPlayer.hasHPWLance = false; |
387 | end; |
388 | if self.isClient then |
389 | if self.sampleWashing ~= nil then |
390 | SoundUtil.stopSample(self.sampleWashing, true); |
391 | end; |
392 | self.isTurningOff = true; |
393 | self.turnOffTime = g_currentMission.time + self.turnOffDuration; |
394 | link(self.lanceNodeParent, self.lanceNode); |
395 | setTranslation(self.lanceNode, 0,0,0); |
396 | setRotation(self.lanceNode, 0,0,0); |
397 | end; |
398 | self.currentPlayer = nil; |
399 | end; |
442 | function HighPressureWasher:washRaycastCallback(hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId) |
443 | local vehicle = g_currentMission.nodeToVehicle[hitActorId]; |
444 | if hitActorId ~= hitShapeId then |
445 | -- object is a compoundChild. Try to find the compound |
446 | local parentId = hitShapeId |
447 | while parentId ~= 0 do |
448 | if g_currentMission.nodeToVehicle[parentId] ~= nil then |
449 | -- found valid compound |
450 | vehicle = g_currentMission.nodeToVehicle[parentId] |
451 | break |
452 | end |
453 | parentId = getParent(parentId) |
454 | end |
455 | end |
456 | |
457 | if vehicle ~= nil and vehicle.getDirtAmount ~= nil and vehicle.setDirtAmount ~= nil and vehicle.washDuration ~= nil then |
458 | self.foundCoords = {x,y,z}; |
459 | self.foundVehicle = vehicle; |
460 | return false; |
461 | end; |
462 | |
463 | return true; |
464 | end; |