1224 | function Dischargeable:dischargeActivationTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
1225 | local spec = self.spec_dischargeable |
1226 | if onEnter or onLeave then |
1227 | local object = g_currentMission:getNodeObject(otherActorId) |
1228 | if object ~= nil and object ~= self then |
1229 | if object.getFillUnitIndexFromNode ~= nil then |
1230 | local fillUnitIndex = object:getFillUnitIndexFromNode(otherShapeId) |
1231 | local dischargeNode = spec.activationTriggerToDischargeNode[triggerId] |
1232 | |
1233 | if dischargeNode ~= nil and fillUnitIndex ~= nil then |
1234 | local trigger = dischargeNode.activationTrigger |
1235 | if onEnter then |
1236 | if trigger.objects[object] == nil then |
1237 | trigger.objects[object] = {count=0, fillUnitIndex=fillUnitIndex, shape=otherShapeId} |
1238 | trigger.numObjects = trigger.numObjects + 1 |
1239 | |
1240 | object:addDeleteListener(self, "onDeleteActivationTriggerObject") |
1241 | end |
1242 | trigger.objects[object].count = trigger.objects[object].count + 1 |
1243 | |
1244 | self:raiseActive() |
1245 | elseif onLeave then |
1246 | trigger.objects[object].count = trigger.objects[object].count - 1 |
1247 | if trigger.objects[object].count == 0 then |
1248 | trigger.objects[object] = nil |
1249 | trigger.numObjects = trigger.numObjects - 1 |
1250 | |
1251 | object:removeDeleteListener(self, "onDeleteActivationTriggerObject") |
1252 | end |
1253 | end |
1254 | end |
1255 | end |
1256 | end |
1257 | end |
1258 | end |
603 | function Dischargeable:dischargeToGround(dischargeNode, emptyLiters) |
604 | local fillType = self:getDischargeFillType(dischargeNode) |
605 | local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
606 | local minLiterToDrop = g_densityMapHeightManager:getMinValidLiterValue(fillType) |
607 | |
608 | dischargeNode.litersToDrop = math.min(dischargeNode.litersToDrop + emptyLiters, math.max(dischargeNode.emptySpeed*250, minLiterToDrop)) |
609 | |
610 | local minDropReached = dischargeNode.litersToDrop > minLiterToDrop |
611 | local hasMinDropFillLevel = fillLevel > minLiterToDrop |
612 | local info = dischargeNode.info |
613 | local dischargedLiters = 0 |
614 | local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset) |
615 | local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset) |
616 | |
617 | sy = sy + info.yOffset |
618 | ey = ey + info.yOffset |
619 | |
620 | if info.limitToGround then |
621 | sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy) |
622 | ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey) |
623 | end |
624 | |
625 | local dropped, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(self, dischargeNode.litersToDrop, fillType, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true) |
626 | dischargeNode.lineOffset = lineOffset |
627 | dischargeNode.litersToDrop = dischargeNode.litersToDrop - dropped |
628 | |
629 | if dropped > 0 then |
630 | local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex) |
631 | dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -dropped, fillType, ToolType.UNDEFINED, unloadInfo) |
632 | end |
633 | |
634 | fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
635 | if fillLevel > 0 and fillLevel <= minLiterToDrop then |
636 | dischargeNode.litersToDrop = minLiterToDrop |
637 | end |
638 | |
639 | return dischargedLiters, minDropReached, hasMinDropFillLevel |
640 | end |
1172 | function Dischargeable:dischargeTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
1173 | local spec = self.spec_dischargeable |
1174 | if onEnter or onLeave then |
1175 | local object = g_currentMission:getNodeObject(otherActorId) |
1176 | if object ~= nil and object ~= self then |
1177 | if object.getFillUnitIndexFromNode ~= nil then |
1178 | local fillUnitIndex = object:getFillUnitIndexFromNode(otherShapeId) |
1179 | local dischargeNode = spec.triggerToDischargeNode[triggerId] |
1180 | |
1181 | if dischargeNode ~= nil and fillUnitIndex ~= nil then |
1182 | local trigger = dischargeNode.trigger |
1183 | if onEnter then |
1184 | if trigger.objects[object] == nil then |
1185 | trigger.objects[object] = {count=0, fillUnitIndex=fillUnitIndex, shape=otherShapeId} |
1186 | trigger.numObjects = trigger.numObjects + 1 |
1187 | |
1188 | object:addDeleteListener(self, "onDeleteDischargeTriggerObject") |
1189 | end |
1190 | trigger.objects[object].count = trigger.objects[object].count + 1 |
1191 | self:raiseActive() |
1192 | |
1193 | elseif onLeave then |
1194 | trigger.objects[object].count = trigger.objects[object].count - 1 |
1195 | if trigger.objects[object].count == 0 then |
1196 | trigger.objects[object] = nil |
1197 | trigger.numObjects = trigger.numObjects - 1 |
1198 | |
1199 | object:removeDeleteListener(self, "onDeleteDischargeTriggerObject") |
1200 | end |
1201 | end |
1202 | end |
1203 | end |
1204 | end |
1205 | end |
1206 | end |
751 | function Dischargeable:getCanDischargeAtPosition(dischargeNode) |
752 | if dischargeNode == nil then |
753 | return false |
754 | end |
755 | |
756 | if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then |
757 | local info = dischargeNode.info |
758 | local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset) |
759 | local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset) |
760 | |
761 | -- check if at the ground position is still space for some more |
762 | -- check this only if we wan't to discharge to the ground (farmland check is also done while discharging to objects) |
763 | local spec = self.spec_dischargeable |
764 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF or spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then |
765 | sy = sy + info.yOffset |
766 | ey = ey + info.yOffset |
767 | |
768 | if info.limitToGround then |
769 | sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy) |
770 | ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey) |
771 | end |
772 | |
773 | local fillType = self:getDischargeFillType(dischargeNode) |
774 | local testDrop = g_densityMapHeightManager:getMinValidLiterValue(fillType) |
775 | if not DensityMapHeightUtil.getCanTipToGroundAroundLine(self, testDrop, fillType, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true) then |
776 | return false |
777 | end |
778 | end |
779 | end |
780 | |
781 | return true |
782 | end |
424 | function Dischargeable:loadDischargeNode(xmlFile, key, entry) |
425 | entry.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#node"), self.i3dMappings) |
426 | if entry.node == nil then |
427 | g_logManager:xmlWarning(self.configFileName, "Missing discharge 'node' for dischargeNode '%s'", key) |
428 | return false |
429 | end |
430 | |
431 | entry.fillUnitIndex = getXMLInt(xmlFile, key .. "#fillUnitIndex") |
432 | if entry.fillUnitIndex == nil then |
433 | g_logManager:xmlWarning(self.configFileName, "Missing 'fillUnitIndex' for dischargeNode '%s'", key) |
434 | return false |
435 | end |
436 | |
437 | entry.unloadInfoIndex = Utils.getNoNil(getXMLInt(xmlFile, key .. "#unloadInfoIndex"), 1) |
438 | entry.stopDischargeOnEmpty = Utils.getNoNil(getXMLBool(xmlFile, key .. "#stopDischargeOnEmpty"), true) |
439 | entry.canDischargeToGround = Utils.getNoNil(getXMLBool(xmlFile, key .. "#canDischargeToGround"), true) |
440 | entry.canDischargeToObject = Utils.getNoNil(getXMLBool(xmlFile, key .. "#canDischargeToObject"), true) |
441 | entry.canStartDischargeAutomatically = Utils.getNoNil(getXMLBool(xmlFile, key .. "#canStartDischargeAutomatically"), false) |
442 | entry.stopDischargeIfNotPossible = Utils.getNoNil(getXMLBool(xmlFile, key .. "#stopDischargeIfNotPossible"), false) |
443 | entry.emptySpeed = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#emptySpeed"), self:getFillUnitCapacity(entry.fillUnitIndex)) / 1000 |
444 | entry.lineOffset = 0 |
445 | entry.litersToDrop = 0 |
446 | |
447 | entry.info = {} |
448 | entry.info.node = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. ".info#node"), self.i3dMappings), entry.node) |
449 | if entry.info.node == entry.node then |
450 | entry.info.node = createTransformGroup("dischargeInfoNode") |
451 | link(entry.node, entry.info.node) |
452 | end |
453 | entry.info.width = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".info#width"), 1.0) / 2 |
454 | entry.info.length = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".info#length"), 1.0) / 2 |
455 | entry.info.zOffset = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".info#zOffset"), 0.0) |
456 | entry.info.yOffset = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".info#yOffset"), 2.0) |
457 | entry.info.limitToGround = Utils.getNoNil(getXMLBool(xmlFile, key .. ".info#limitToGround"), true) |
458 | entry.info.useRaycastHitPosition = Utils.getNoNil(getXMLBool(xmlFile, key .. ".info#useRaycastHitPosition"), false) |
459 | |
460 | entry.raycast = {} |
461 | entry.raycast.node = Utils.getNoNil( I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. ".raycast#node"), self.i3dMappings), entry.node ) |
462 | entry.raycast.useWorldNegYDirection = Utils.getNoNil(getXMLBool(xmlFile, key .. ".raycast#useWorldNegYDirection"), false) |
463 | entry.raycast.yOffset = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".raycast#yOffset"), 0) |
464 | |
465 | local raycastMaxDistance = getXMLFloat(xmlFile, key .. ".raycast#maxDistance") |
466 | entry.maxDistance = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key .. "#maxDistance") , raycastMaxDistance ), 10) |
467 | |
468 | entry.dischargeObject = nil |
469 | entry.dischargeHitTerrain = false |
470 | entry.dischargeShape = nil |
471 | entry.dischargeDistance = 0 |
472 | entry.dischargeDistanceSent = 0 |
473 | entry.dischargeFillUnitIndex = nil |
474 | entry.dischargeHit = false |
475 | |
476 | entry.trigger = {} |
477 | entry.trigger.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. ".trigger#node"), self.i3dMappings) |
478 | if entry.trigger.node ~= nil then |
479 | addTrigger(entry.trigger.node, "dischargeTriggerCallback", self) |
480 | end |
481 | entry.trigger.objects = {} |
482 | entry.trigger.numObjects = 0 |
483 | |
484 | entry.activationTrigger = {} |
485 | entry.activationTrigger.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. ".activationTrigger#node"), self.i3dMappings) |
486 | if entry.activationTrigger.node ~= nil then |
487 | addTrigger(entry.activationTrigger.node, "dischargeActivationTriggerCallback", self) |
488 | end |
489 | entry.activationTrigger.objects = {} |
490 | entry.activationTrigger.numObjects = 0 |
491 | |
492 | entry.effects = g_effectManager:loadEffect(xmlFile, key..".effects", self.components, self, self.i3dMappings) |
493 | |
494 | if self.isClient then |
495 | entry.playSound = Utils.getNoNil(getXMLBool(xmlFile, key.."#playSound"), true) |
496 | entry.soundNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#soundNode"), self.i3dMappings) |
497 | |
498 | -- additional discharge sound if the flag overwriteSharedSound is not set |
499 | if entry.playSound then |
500 | entry.dischargeSample = g_soundManager:loadSampleFromXML(self.xmlFile, key, "dischargeSound", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
501 | end |
502 | |
503 | if Utils.getNoNil(getXMLBool(xmlFile, key..".dischargeSound#overwriteSharedSound"), false) then |
504 | entry.playSound = false |
505 | end |
506 | end |
507 | |
508 | entry.sentHitDistance = 0 |
509 | entry.isEffectActive = false |
510 | entry.isEffectActiveSent = false |
511 | |
512 | entry.lastEffect = entry.effects[#entry.effects] |
513 | |
514 | return true |
515 | end |
111 | function Dischargeable:onLoad(savegame) |
112 | local spec = self.spec_dischargeable |
113 | |
114 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.pipeEffect", "vehicle.dischargeable.dischargeNode.effects") --FS17 to FS19 |
115 | |
116 | spec.dischargeNodes = {} |
117 | spec.fillUnitDischargeNodeMapping = {} |
118 | spec.dischargNodeMapping = {} |
119 | spec.triggerToDischargeNode = {} |
120 | spec.activationTriggerToDischargeNode = {} |
121 | |
122 | spec.requiresTipOcclusionArea = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.dischargeable#requiresTipOcclusionArea"), true) |
123 | spec.stopDischargeOnDeactivate = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.dischargeable#stopDischargeOnDeactivate"), true) |
124 | spec.dischargedLiters = 0 |
125 | |
126 | local i = 0 |
127 | while true do |
128 | local key = string.format("vehicle.dischargeable.dischargeNode(%d)", i) |
129 | if not hasXMLProperty(self.xmlFile, key) then |
130 | break |
131 | end |
132 | |
133 | local entry = {} |
134 | if self:loadDischargeNode(self.xmlFile, key, entry) then |
135 | local canBeAdded = true |
136 | |
137 | if spec.dischargNodeMapping[entry.node] ~= nil then |
138 | g_logManager:xmlWarning(self.configFileName, "DischargeNode '%s' already defined. Discharge nodes need to be unique. Ignoring it!", getName(entry.node)) |
139 | canBeAdded = false |
140 | end |
141 | if entry.trigger.node ~= nil and spec.triggerToDischargeNode[entry.trigger.node] ~= nil then |
142 | g_logManager:xmlWarning(self.configFileName, "DischargeNode trigger '%s' already defined. DischargeNode triggers need to be unique. Ignoring it!", getName(entry.trigger.node)) |
143 | canBeAdded = false |
144 | end |
145 | if entry.activationTrigger.node ~= nil and spec.activationTriggerToDischargeNode[entry.activationTrigger.node] ~= nil then |
146 | g_logManager:xmlWarning(self.configFileName, "DischargeNode activationTrigger '%s' already defined. DischargeNode activationTriggers need to be unique. Ignoring it!", getName(entry.activationTrigger.node)) |
147 | canBeAdded = false |
148 | end |
149 | |
150 | if canBeAdded then |
151 | table.insert(spec.dischargeNodes, entry) |
152 | entry.index = #spec.dischargeNodes |
153 | spec.fillUnitDischargeNodeMapping[entry.fillUnitIndex] = entry |
154 | spec.dischargNodeMapping[entry.node] = entry |
155 | |
156 | if entry.trigger.node ~= nil then |
157 | spec.triggerToDischargeNode[entry.trigger.node] = entry |
158 | end |
159 | if entry.activationTrigger.node ~= nil then |
160 | spec.activationTriggerToDischargeNode[entry.activationTrigger.node] = entry |
161 | end |
162 | end |
163 | end |
164 | |
165 | i = i + 1 |
166 | end |
167 | |
168 | spec.currentDischargeState = Dischargeable.DISCHARGE_STATE_OFF |
169 | spec.currentRaycast = nil |
170 | spec.forcedFillTypeIndex = nil |
171 | spec.isAsyncRaycastActive = false |
172 | spec.currentRaycast = {} |
173 | self:setCurrentDischargeNodeIndex(1) |
174 | |
175 | spec.dirtyFlag = self:getNextDirtyFlag() |
176 | end |
284 | function Dischargeable:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
285 | local spec = self.spec_dischargeable |
286 | |
287 | local dischargeNode = spec.currentDischargeNode |
288 | if dischargeNode ~= nil then |
289 | if self.isClient then |
290 | Dischargeable.updateActionEvents(self) |
291 | end |
292 | |
293 | if self:getIsDischargeNodeActive(dischargeNode) then |
294 | local trigger = dischargeNode.trigger |
295 | if trigger.numObjects > 0 then |
296 | dischargeNode.dischargeObject = nil |
297 | dischargeNode.dischargeHitTerrain = false |
298 | dischargeNode.dischargeShape = nil |
299 | dischargeNode.dischargeDistance = 0 |
300 | dischargeNode.dischargeFillUnitIndex = nil |
301 | dischargeNode.dischargeHit = false -- any (also unsupported) object hit |
302 | |
303 | local nearestDistance = math.huge |
304 | for object, data in pairs(trigger.objects) do |
305 | local fillType = spec.forcedFillTypeIndex |
306 | if fillType == nil then |
307 | fillType = self:getDischargeFillType(dischargeNode) |
308 | end |
309 | |
310 | dischargeNode.dischargeFailedReason = nil |
311 | dischargeNode.customNotAllowedWarning = nil |
312 | |
313 | if object:getFillUnitSupportsFillType(data.fillUnitIndex, fillType) then |
314 | local allowFillType = object:getFillUnitAllowsFillType(data.fillUnitIndex, fillType) |
315 | local allowToolType = object:getFillUnitSupportsToolType(data.fillUnitIndex, ToolType.TRIGGER) |
316 | local freeSpace = object:getFillUnitFreeCapacity(data.fillUnitIndex, fillType, self:getActiveFarm()) > 0 |
317 | |
318 | if allowFillType and allowToolType and freeSpace then |
319 | local exactFillRootNode = object:getFillUnitExactFillRootNode(data.fillUnitIndex) |
320 | if exactFillRootNode ~= nil and entityExists(exactFillRootNode) then |
321 | local distance = calcDistanceFrom(dischargeNode.node, exactFillRootNode) |
322 | if distance < nearestDistance then |
323 | dischargeNode.dischargeObject = object |
324 | dischargeNode.dischargeHitTerrain = false |
325 | dischargeNode.dischargeShape = data.shape |
326 | dischargeNode.dischargeDistance = distance |
327 | dischargeNode.dischargeFillUnitIndex = data.fillUnitIndex |
328 | nearestDistance = distance |
329 | end |
330 | end |
331 | elseif not allowFillType then |
332 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
333 | elseif not allowToolType then |
334 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED |
335 | elseif not freeSpace then |
336 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
337 | end |
338 | else |
339 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
340 | end |
341 | |
342 | if dischargeNode.dischargeFailedReason ~= nil then |
343 | if object.getCustomDischargeNotAllowedWarning ~= nil then |
344 | dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning() |
345 | end |
346 | end |
347 | |
348 | dischargeNode.dischargeHit = true -- any (also unsupported) object has been hit |
349 | end |
350 | else |
351 | if not spec.isAsyncRaycastActive then |
352 | self:updateRaycast(dischargeNode) |
353 | end |
354 | end |
355 | else |
356 | if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then |
357 | self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true) |
358 | end |
359 | end |
360 | |
361 | self:updateDischargeSound(dischargeNode, dt) |
362 | |
363 | if self.isServer then |
364 | if VehicleDebug.state == VehicleDebug.DEBUG then |
365 | local info = dischargeNode.info |
366 | local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset) |
367 | local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset) |
368 | drawDebugLine(sx, sy+info.yOffset, sz, 1, 0, 0, ex, ey+info.yOffset, ez, 1, 0, 0) |
369 | end |
370 | |
371 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then |
372 | if dischargeNode.dischargeObject ~= nil then |
373 | self:handleFoundDischargeObject(dischargeNode) |
374 | end |
375 | else |
376 | local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
377 | local emptySpeed = self:getDischargeNodeEmptyFactor(dischargeNode) |
378 | |
379 | -- Only allow discharge into a node, or if the land is owned |
380 | local canDischargeToObject = self:getCanDischargeToObject(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT |
381 | local canDischargeToGround = self:getCanDischargeToGround(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND |
382 | local canDischarge = canDischargeToObject or canDischargeToGround |
383 | local allowedToDischarge = dischargeNode.dischargeObject ~= nil or (self:getCanDischargeToLand(dischargeNode) and self:getCanDischargeAtPosition(dischargeNode)) |
384 | local isReadyToStartDischarge = fillLevel > 0.0001 and emptySpeed > 0 and allowedToDischarge and canDischarge |
385 | |
386 | self:setDischargeEffectActive(dischargeNode, isReadyToStartDischarge) |
387 | self:setDischargeEffectDistance(dischargeNode, dischargeNode.dischargeDistance) |
388 | |
389 | local isReadyForDischarge = dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsFullyVisible() |
390 | if isReadyForDischarge and allowedToDischarge and canDischarge then |
391 | local emptyLiters = math.min(fillLevel, dischargeNode.emptySpeed * emptySpeed * dt) |
392 | local dischargedLiters, minDropReached, hasMinDropFillLevel = self:discharge(dischargeNode, emptyLiters) |
393 | |
394 | spec.dischargedLiters = dischargedLiters |
395 | self:handleDischarge(dischargeNode, dischargedLiters, minDropReached, hasMinDropFillLevel) |
396 | end |
397 | end |
398 | |
399 | if dischargeNode.isEffectActive ~= dischargeNode.isEffectActiveSent or math.abs(dischargeNode.dischargeDistanceSent - dischargeNode.dischargeDistance) > 0.05 then |
400 | self:raiseDirtyFlags(spec.dirtyFlag) |
401 | dischargeNode.dischargeDistanceSent = dischargeNode.dischargeDistance |
402 | dischargeNode.isEffectActiveSent = dischargeNode.isEffectActive |
403 | end |
404 | end |
405 | end |
406 | |
407 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then |
408 | local currentDischargeNode = spec.currentDischargeNode |
409 | if self:getIsActiveForInput() and self:getCanDischargeToObject(currentDischargeNode) and self:getCanToggleDischargeToObject() then |
410 | g_currentMission:showTipContext(self:getFillUnitFillType(dischargeNode.fillUnitIndex)) |
411 | end |
412 | end |
413 | |
414 | for _, dischargeNode in ipairs(spec.dischargeNodes) do |
415 | if dischargeNode.stopEffectTime ~= nil and dischargeNode.stopEffectTime < g_time then |
416 | self:setDischargeEffectActive(dischargeNode, false, true) |
417 | dischargeNode.stopEffectTime = nil |
418 | end |
419 | end |
420 | end |
924 | function Dischargeable:raycastCallbackDischargeNode(hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId) |
925 | if hitActorId ~= nil then |
926 | local spec = self.spec_dischargeable |
927 | local dischargeNode = spec.currentRaycastDischargeNode |
928 | local object = g_currentMission:getNodeObject(hitActorId) |
929 | |
930 | distance = distance - dischargeNode.raycast.yOffset |
931 | |
932 | if VehicleDebug.state == VehicleDebug.DEBUG then |
933 | DebugUtil.drawDebugGizmoAtWorldPos(x,y,z, 0, 0, 1, 0, 1, 0, nil) |
934 | end |
935 | |
936 | local validObject = object ~= nil and object ~= self |
937 | -- if we hit a object because of the yOffset it has to be a exact fill root node, otherwise we ignore it |
938 | -- is used to get exactFillRootNodes if the dischargeNode of the shovel is already below it |
939 | if validObject and distance < 0 then |
940 | if object.getFillUnitIndexFromNode ~= nil then |
941 | validObject = validObject and object:getFillUnitIndexFromNode(hitShapeId) ~= nil |
942 | end |
943 | end |
944 | |
945 | if validObject then |
946 | if object.getFillUnitIndexFromNode ~= nil then |
947 | local fillUnitIndex = object:getFillUnitIndexFromNode(hitShapeId) |
948 | if fillUnitIndex ~= nil then |
949 | local fillType = spec.forcedFillTypeIndex |
950 | if fillType == nil then |
951 | fillType = self:getDischargeFillType(dischargeNode) |
952 | end |
953 | |
954 | dischargeNode.dischargeFailedReason = nil |
955 | dischargeNode.customNotAllowedWarning = nil |
956 | |
957 | if object:getFillUnitSupportsFillType(fillUnitIndex, fillType) then |
958 | local allowFillType = object:getFillUnitAllowsFillType(fillUnitIndex, fillType) |
959 | local allowToolType = object:getFillUnitSupportsToolType(fillUnitIndex, ToolType.DISCHARGEABLE) |
960 | local freeSpace = object:getFillUnitFreeCapacity(fillUnitIndex, fillType, self:getActiveFarm()) > 0 |
961 | |
962 | if allowFillType and allowToolType and freeSpace then |
963 | dischargeNode.dischargeObject = object |
964 | dischargeNode.dischargeShape = hitShapeId |
965 | dischargeNode.dischargeDistance = distance |
966 | dischargeNode.dischargeFillUnitIndex = fillUnitIndex |
967 | |
968 | if object.getFillUnitExtraDistanceFromNode ~= nil then |
969 | dischargeNode.dischargeExtraDistance = object:getFillUnitExtraDistanceFromNode(hitShapeId) |
970 | end |
971 | elseif not allowFillType then |
972 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
973 | elseif not allowToolType then |
974 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED |
975 | elseif not freeSpace then |
976 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
977 | end |
978 | else |
979 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
980 | end |
981 | |
982 | if dischargeNode.dischargeFailedReason ~= nil then |
983 | if object.getCustomDischargeNotAllowedWarning ~= nil then |
984 | dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning() |
985 | end |
986 | end |
987 | |
988 | dischargeNode.dischargeHit = true -- any, even unsupported, object has been hit. |
989 | else |
990 | -- raycast until we hit the object underneath the exact fill root node |
991 | dischargeNode.dischargeDistance = distance + (dischargeNode.dischargeExtraDistance or 0) |
992 | dischargeNode.dischargeExtraDistance = nil |
993 | self:updateDischargeInfo(dischargeNode, x, y, z) |
994 | return false |
995 | end |
996 | end |
997 | elseif hitActorId == g_currentMission.terrainRootNode then |
998 | dischargeNode.dischargeDistance = math.min(dischargeNode.dischargeDistance, distance) |
999 | dischargeNode.dischargeHitTerrain = true |
1000 | self:updateDischargeInfo(dischargeNode, x, y, z) |
1001 | return false |
1002 | end |
1003 | |
1004 | return true |
1005 | else |
1006 | self:finishDischargeRaycast() |
1007 | end |
1008 | end |
36 | function Dischargeable.registerFunctions(vehicleType) |
37 | SpecializationUtil.registerFunction(vehicleType, "loadDischargeNode", Dischargeable.loadDischargeNode) |
38 | SpecializationUtil.registerFunction(vehicleType, "setCurrentDischargeNodeIndex", Dischargeable.setCurrentDischargeNodeIndex) |
39 | SpecializationUtil.registerFunction(vehicleType, "getCurrentDischargeNode", Dischargeable.getCurrentDischargeNode) |
40 | SpecializationUtil.registerFunction(vehicleType, "getDischargeTargetObject", Dischargeable.getDischargeTargetObject) |
41 | SpecializationUtil.registerFunction(vehicleType, "getCurrentDischargeObject", Dischargeable.getCurrentDischargeObject) |
42 | SpecializationUtil.registerFunction(vehicleType, "discharge", Dischargeable.discharge) |
43 | SpecializationUtil.registerFunction(vehicleType, "dischargeToGround", Dischargeable.dischargeToGround) |
44 | SpecializationUtil.registerFunction(vehicleType, "dischargeToObject", Dischargeable.dischargeToObject) |
45 | SpecializationUtil.registerFunction(vehicleType, "setDischargeState", Dischargeable.setDischargeState) |
46 | SpecializationUtil.registerFunction(vehicleType, "getDischargeState", Dischargeable.getDischargeState) |
47 | SpecializationUtil.registerFunction(vehicleType, "getDischargeFillType", Dischargeable.getDischargeFillType) |
48 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToGround", Dischargeable.getCanDischargeToGround) |
49 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeAtPosition", Dischargeable.getCanDischargeAtPosition) |
50 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToLand", Dischargeable.getCanDischargeToLand) |
51 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToObject", Dischargeable.getCanDischargeToObject) |
52 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNotAllowedWarning", Dischargeable.getDischargeNotAllowedWarning) |
53 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleDischargeToObject", Dischargeable.getCanToggleDischargeToObject) |
54 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleDischargeToGround", Dischargeable.getCanToggleDischargeToGround) |
55 | SpecializationUtil.registerFunction(vehicleType, "getIsDischargeNodeActive", Dischargeable.getIsDischargeNodeActive) |
56 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeEmptyFactor", Dischargeable.getDischargeNodeEmptyFactor) |
57 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeByNode", Dischargeable.getDischargeNodeByNode) |
58 | SpecializationUtil.registerFunction(vehicleType, "updateRaycast", Dischargeable.updateRaycast) |
59 | SpecializationUtil.registerFunction(vehicleType, "updateDischargeInfo", Dischargeable.updateDischargeInfo) |
60 | SpecializationUtil.registerFunction(vehicleType, "raycastCallbackDischargeNode", Dischargeable.raycastCallbackDischargeNode) |
61 | SpecializationUtil.registerFunction(vehicleType, "finishDischargeRaycast", Dischargeable.finishDischargeRaycast) |
62 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeByIndex", Dischargeable.getDischargeNodeByIndex) |
63 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeOnEmpty", Dischargeable.handleDischargeOnEmpty) |
64 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeNodeChanged", Dischargeable.handleDischargeNodeChanged) |
65 | SpecializationUtil.registerFunction(vehicleType, "handleDischarge", Dischargeable.handleDischarge) |
66 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeRaycast", Dischargeable.handleDischargeRaycast) |
67 | SpecializationUtil.registerFunction(vehicleType, "handleFoundDischargeObject", Dischargeable.handleFoundDischargeObject) |
68 | SpecializationUtil.registerFunction(vehicleType, "setDischargeEffectDistance", Dischargeable.setDischargeEffectDistance) |
69 | SpecializationUtil.registerFunction(vehicleType, "setDischargeEffectActive", Dischargeable.setDischargeEffectActive) |
70 | SpecializationUtil.registerFunction(vehicleType, "updateDischargeSound", Dischargeable.updateDischargeSound) |
71 | SpecializationUtil.registerFunction(vehicleType, "dischargeTriggerCallback", Dischargeable.dischargeTriggerCallback) |
72 | SpecializationUtil.registerFunction(vehicleType, "onDeleteDischargeTriggerObject", Dischargeable.onDeleteDischargeTriggerObject) |
73 | SpecializationUtil.registerFunction(vehicleType, "dischargeActivationTriggerCallback", Dischargeable.dischargeActivationTriggerCallback) |
74 | SpecializationUtil.registerFunction(vehicleType, "onDeleteActivationTriggerObject", Dischargeable.onDeleteActivationTriggerObject) |
75 | SpecializationUtil.registerFunction(vehicleType, "setForcedFillTypeIndex", Dischargeable.setForcedFillTypeIndex) |
76 | end |
1408 | function Dischargeable.updateActionEvents(self) |
1409 | local spec = self.spec_dischargeable |
1410 | |
1411 | local actionEventTip = spec.actionEvents[InputAction.TOGGLE_TIPSTATE] |
1412 | local actionEventTipGround = spec.actionEvents[InputAction.TOGGLE_TIPSTATE_GROUND] |
1413 | local showTip = false |
1414 | local showTipGround = false |
1415 | |
1416 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then |
1417 | local currentDischargeNode = spec.currentDischargeNode |
1418 | if self:getIsDischargeNodeActive(currentDischargeNode) then |
1419 | if self:getCanDischargeToObject(currentDischargeNode) and self:getCanToggleDischargeToObject() then |
1420 | if actionEventTip ~= nil then |
1421 | g_inputBinding:setActionEventText(actionEventTip.actionEventId, g_i18n:getText("action_startOverloading")) |
1422 | showTip = true |
1423 | end |
1424 | elseif self:getCanDischargeToGround(currentDischargeNode) and self:getCanToggleDischargeToGround() then |
1425 | if actionEventTipGround ~= nil then |
1426 | g_inputBinding:setActionEventText(actionEventTipGround.actionEventId, g_i18n:getText("action_startTipToGround")) |
1427 | showTipGround = true |
1428 | end |
1429 | end |
1430 | end |
1431 | elseif spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then |
1432 | if actionEventTipGround ~= nil then |
1433 | g_inputBinding:setActionEventText(actionEventTipGround.actionEventId, g_i18n:getText("action_stopTipToGround")) |
1434 | showTipGround = true |
1435 | end |
1436 | else |
1437 | if actionEventTip ~= nil then |
1438 | g_inputBinding:setActionEventText(actionEventTip.actionEventId, g_i18n:getText("action_stopOverloading")) |
1439 | showTip = true |
1440 | end |
1441 | end |
1442 | |
1443 | if actionEventTip ~= nil then |
1444 | g_inputBinding:setActionEventTextVisibility(actionEventTip.actionEventId, showTip) |
1445 | end |
1446 | if actionEventTipGround ~= nil then |
1447 | g_inputBinding:setActionEventTextVisibility(actionEventTipGround.actionEventId, showTipGround) |
1448 | end |
1449 | end |
1327 | function Dischargeable:updateDebugValues(values) |
1328 | local spec = self.spec_dischargeable |
1329 | local currentDischargeNode = spec.currentDischargeNode |
1330 | |
1331 | local state = "OFF" |
1332 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then |
1333 | state = "OBJECT" |
1334 | elseif spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then |
1335 | state = "GROUND" |
1336 | end |
1337 | table.insert(values, {name="state", value=state}) |
1338 | table.insert(values, {name="getCanDischargeToObject", value=tostring(self:getCanDischargeToObject(currentDischargeNode))}) |
1339 | table.insert(values, {name="getCanDischargeToGround", value=tostring(self:getCanDischargeToGround(currentDischargeNode))}) |
1340 | table.insert(values, {name="dischargedLiters", value=tostring(spec.dischargedLiters)}) |
1341 | table.insert(values, {name="currentNode", value=tostring(currentDischargeNode)}) |
1342 | |
1343 | for _, dischargeNode in ipairs(spec.dischargeNodes) do |
1344 | table.insert(values, {name="--->", value=tostring(dischargeNode)}) |
1345 | local object = nil |
1346 | if dischargeNode.dischargeObject ~= nil then |
1347 | object = tostring(dischargeNode.dischargeObject.configFileName) |
1348 | end |
1349 | table.insert(values, {name="object", value=tostring(object)}) |
1350 | table.insert(values, {name="distance", value=dischargeNode.dischargeDistance}) |
1351 | table.insert(values, {name="effect", value=tostring(dischargeNode.isEffectActive)}) |
1352 | table.insert(values, {name="fillLevel", value=tostring(self:getFillUnitFillLevel(dischargeNode.fillUnitIndex))}) |
1353 | table.insert(values, {name="litersToDrop", value=tostring(dischargeNode.litersToDrop)}) |
1354 | table.insert(values, {name="emptyFactor", value=tostring(self:getDischargeNodeEmptyFactor(dischargeNode))}) |
1355 | table.insert(values, {name="emptySpeed", value=tostring(self:getDischargeNodeEmptyFactor(dischargeNode))}) |
1356 | table.insert(values, {name="readyForDischarge", value=tostring(dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsFullyVisible())}) |
1357 | table.insert(values, {name="objectsInTrigger", value=tostring(dischargeNode.trigger.numObjects)}) |
1358 | table.insert(values, {name="objectsInActivationTrigger", value=tostring(dischargeNode.activationTrigger.numObjects)}) |
1359 | end |
1360 | end |
1115 | function Dischargeable:updateDischargeSound(dischargeNode, dt) |
1116 | if self.isClient then |
1117 | local fillType = self:getDischargeFillType(dischargeNode) |
1118 | local isInDischargeState = self.spec_dischargeable.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF |
1119 | local isEmptying = dischargeNode.isEffectActive and fillType ~= FillType.UNKNOWN |
1120 | if isInDischargeState and isEmptying then |
1121 | -- shared sample |
1122 | local sharedSample = g_fillTypeManager:getSampleByFillType(fillType) |
1123 | if sharedSample ~= nil then |
1124 | if sharedSample ~= dischargeNode.sharedSample then |
1125 | if dischargeNode.sample ~= nil then |
1126 | g_soundManager:deleteSample(dischargeNode.sample) |
1127 | end |
1128 | |
1129 | dischargeNode.sample = g_soundManager:cloneSample(sharedSample, dischargeNode.node or dischargeNode.soundNode, self) |
1130 | dischargeNode.sharedSample = sharedSample |
1131 | |
1132 | g_soundManager:playSample(dischargeNode.sample) |
1133 | else |
1134 | if not g_soundManager:getIsSamplePlaying(dischargeNode.sample) then |
1135 | g_soundManager:playSample(dischargeNode.sample) |
1136 | end |
1137 | end |
1138 | end |
1139 | |
1140 | -- additional sample |
1141 | if dischargeNode.dischargeSample ~= nil then |
1142 | if not g_soundManager:getIsSamplePlaying(dischargeNode.dischargeSample) then |
1143 | g_soundManager:playSample(dischargeNode.dischargeSample) |
1144 | end |
1145 | end |
1146 | dischargeNode.turnOffSoundTimer = 500 |
1147 | else |
1148 | if dischargeNode.turnOffSoundTimer ~= nil and dischargeNode.turnOffSoundTimer > 0 then |
1149 | dischargeNode.turnOffSoundTimer = dischargeNode.turnOffSoundTimer - dt |
1150 | if dischargeNode.turnOffSoundTimer <= 0 then |
1151 | -- shared sample |
1152 | if g_soundManager:getIsSamplePlaying(dischargeNode.sample) then |
1153 | g_soundManager:stopSample(dischargeNode.sample) |
1154 | end |
1155 | |
1156 | -- additional sample |
1157 | if dischargeNode.dischargeSample ~= nil then |
1158 | if g_soundManager:getIsSamplePlaying(dischargeNode.dischargeSample) then |
1159 | g_soundManager:stopSample(dischargeNode.dischargeSample) |
1160 | end |
1161 | end |
1162 | |
1163 | dischargeNode.turnOffSoundTimer = 0 |
1164 | end |
1165 | end |
1166 | end |
1167 | end |
1168 | end |
881 | function Dischargeable:updateRaycast(dischargeNode) |
882 | local spec = self.spec_dischargeable |
883 | local raycast = dischargeNode.raycast |
884 | |
885 | if raycast.node == nil then |
886 | return |
887 | end |
888 | |
889 | dischargeNode.dischargeObject = nil |
890 | dischargeNode.dischargeHitTerrain = false |
891 | dischargeNode.dischargeShape = nil |
892 | dischargeNode.dischargeDistance = math.huge |
893 | dischargeNode.dischargeFillUnitIndex = nil |
894 | dischargeNode.dischargeHit = false |
895 | |
896 | local x,y,z = getWorldTranslation(raycast.node) |
897 | local dx,dy,dz = 0, -1, 0 |
898 | |
899 | y = y + raycast.yOffset |
900 | |
901 | if not raycast.useWorldNegYDirection then |
902 | dx,dy,dz = localDirectionToWorld(raycast.node, 0,-1,0) |
903 | end |
904 | |
905 | spec.currentRaycastDischargeNode = dischargeNode |
906 | spec.currentRaycast = raycast |
907 | spec.isAsyncRaycastActive = true |
908 | raycastAll(x,y,z, dx,dy,dz, "raycastCallbackDischargeNode", dischargeNode.maxDistance, self, nil, false) |
909 | |
910 | -- TODO: remove if async raycast is added |
911 | self:raycastCallbackDischargeNode(nil) |
912 | end |