888 | function Dischargeable:dischargeToGround(dischargeNode, emptyLiters) |
889 | if emptyLiters == 0 then |
890 | return 0, false, false |
891 | end |
892 | |
893 | local fillType, factor = self:getDischargeFillType(dischargeNode) |
894 | local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
895 | local minLiterToDrop = g_densityMapHeightManager:getMinValidLiterValue(fillType) |
896 | |
897 | dischargeNode.litersToDrop = math.min(dischargeNode.litersToDrop + emptyLiters, math.max(dischargeNode.emptySpeed*250, minLiterToDrop)) |
898 | |
899 | local minDropReached = dischargeNode.litersToDrop > minLiterToDrop |
900 | local hasMinDropFillLevel = fillLevel > minLiterToDrop |
901 | local info = dischargeNode.info |
902 | local dischargedLiters = 0 |
903 | local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset) |
904 | local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset) |
905 | |
906 | sy = sy + info.yOffset |
907 | ey = ey + info.yOffset |
908 | |
909 | if info.limitToGround then |
910 | sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy) |
911 | ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey) |
912 | end |
913 | |
914 | local dropped, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(self, dischargeNode.litersToDrop * factor, fillType, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true) |
915 | dropped = dropped / factor |
916 | dischargeNode.lineOffset = lineOffset |
917 | dischargeNode.litersToDrop = dischargeNode.litersToDrop - dropped |
918 | |
919 | if dropped > 0 then |
920 | local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex) |
921 | dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -dropped, self:getFillUnitFillType(dischargeNode.fillUnitIndex), ToolType.UNDEFINED, unloadInfo) |
922 | end |
923 | |
924 | fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
925 | if fillLevel > 0 and fillLevel <= minLiterToDrop then |
926 | dischargeNode.litersToDrop = minLiterToDrop |
927 | end |
928 | |
929 | return dischargedLiters, minDropReached, hasMinDropFillLevel |
930 | end |
1072 | function Dischargeable:getCanDischargeAtPosition(dischargeNode) |
1073 | if dischargeNode == nil then |
1074 | return false |
1075 | end |
1076 | |
1077 | if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then |
1078 | local info = dischargeNode.info |
1079 | local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset) |
1080 | local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset) |
1081 | |
1082 | -- check if at the ground position is still space for some more |
1083 | -- check this only if we wan't to discharge to the ground (farmland check is also done while discharging to objects) |
1084 | local spec = self.spec_dischargeable |
1085 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF or spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then |
1086 | sy = sy + info.yOffset |
1087 | ey = ey + info.yOffset |
1088 | |
1089 | if info.limitToGround then |
1090 | sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy) |
1091 | ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey) |
1092 | end |
1093 | |
1094 | local fillType = self:getDischargeFillType(dischargeNode) |
1095 | local testDrop = g_densityMapHeightManager:getMinValidLiterValue(fillType) |
1096 | if not DensityMapHeightUtil.getCanTipToGroundAroundLine(self, testDrop, fillType, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true) then |
1097 | return false |
1098 | end |
1099 | end |
1100 | end |
1101 | |
1102 | return true |
1103 | end |
614 | function Dischargeable:loadDischargeNode(xmlFile, key, entry) |
615 | entry.node = xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
616 | if entry.node == nil then |
617 | Logging.xmlWarning(self.xmlFile, "Missing discharge 'node' for dischargeNode '%s'", key) |
618 | return false |
619 | end |
620 | |
621 | entry.fillUnitIndex = xmlFile:getValue(key .. "#fillUnitIndex") |
622 | if entry.fillUnitIndex == nil then |
623 | Logging.xmlWarning(self.xmlFile, "Missing 'fillUnitIndex' for dischargeNode '%s'", key) |
624 | return false |
625 | end |
626 | |
627 | entry.unloadInfoIndex = xmlFile:getValue(key .. "#unloadInfoIndex", 1) |
628 | entry.stopDischargeOnEmpty = xmlFile:getValue(key .. "#stopDischargeOnEmpty", true) |
629 | entry.canDischargeToGround = xmlFile:getValue(key .. "#canDischargeToGround", true) |
630 | entry.canDischargeToObject = xmlFile:getValue(key .. "#canDischargeToObject", true) |
631 | entry.canStartDischargeAutomatically = xmlFile:getValue(key .. "#canStartDischargeAutomatically", Platform.gameplay.automaticDischarge) |
632 | entry.canStartGroundDischargeAutomatically = xmlFile:getValue(key .. "#canStartGroundDischargeAutomatically", false) |
633 | entry.stopDischargeIfNotPossible = xmlFile:getValue(key .. "#stopDischargeIfNotPossible", xmlFile:hasProperty(key .. ".trigger#node")) |
634 | entry.canDischargeToGroundAnywhere = xmlFile:getValue(key .. "#canDischargeToGroundAnywhere", false) |
635 | entry.emptySpeed = xmlFile:getValue(key .. "#emptySpeed", self:getFillUnitCapacity(entry.fillUnitIndex)) / 1000 |
636 | entry.effectTurnOffThreshold = xmlFile:getValue(key .. "#effectTurnOffThreshold", 0.25) |
637 | |
638 | entry.lineOffset = 0 |
639 | entry.litersToDrop = 0 |
640 | |
641 | local toolTypeStr = xmlFile:getValue(key .. "#toolType", "dischargeable") |
642 | entry.toolType = g_toolTypeManager:getToolTypeIndexByName(toolTypeStr) |
643 | |
644 | entry.info = {} |
645 | entry.info.node = xmlFile:getValue(key .. ".info#node", entry.node, self.components, self.i3dMappings) |
646 | if entry.info.node == entry.node then |
647 | entry.info.node = createTransformGroup("dischargeInfoNode") |
648 | link(entry.node, entry.info.node) |
649 | end |
650 | entry.info.width = xmlFile:getValue(key .. ".info#width", 1.0) / 2 |
651 | entry.info.length = xmlFile:getValue(key .. ".info#length", 1.0) / 2 |
652 | entry.info.zOffset = xmlFile:getValue(key .. ".info#zOffset", 0.0) |
653 | entry.info.yOffset = xmlFile:getValue(key .. ".info#yOffset", 2.0) |
654 | entry.info.limitToGround = xmlFile:getValue(key .. ".info#limitToGround", true) |
655 | entry.info.useRaycastHitPosition = xmlFile:getValue(key .. ".info#useRaycastHitPosition", false) |
656 | |
657 | entry.raycast = {} |
658 | entry.raycast.node = xmlFile:getValue(key .. ".raycast#node", entry.node, self.components, self.i3dMappings) |
659 | entry.raycast.useWorldNegYDirection = xmlFile:getValue(key .. ".raycast#useWorldNegYDirection", false) |
660 | entry.raycast.yOffset = xmlFile:getValue(key .. ".raycast#yOffset", 0) |
661 | |
662 | local raycastMaxDistance = xmlFile:getValue(key .. ".raycast#maxDistance") |
663 | entry.maxDistance = xmlFile:getValue(key .. "#maxDistance", raycastMaxDistance) or 10 |
664 | |
665 | entry.dischargeObject = nil |
666 | entry.dischargeHitObject = nil |
667 | entry.dischargeHitObjectUnitIndex = nil |
668 | entry.dischargeHitTerrain = false |
669 | entry.dischargeShape = nil |
670 | entry.dischargeDistance = 0 |
671 | entry.dischargeDistanceSent = 0 |
672 | entry.dischargeFillUnitIndex = nil |
673 | entry.dischargeHit = false |
674 | |
675 | entry.trigger = {} |
676 | entry.trigger.node = xmlFile:getValue(key .. ".trigger#node", nil, self.components, self.i3dMappings) |
677 | if entry.trigger.node ~= nil then |
678 | addTrigger(entry.trigger.node, "dischargeTriggerCallback", self) |
679 | end |
680 | entry.trigger.objects = {} |
681 | entry.trigger.numObjects = 0 |
682 | |
683 | entry.activationTrigger = {} |
684 | entry.activationTrigger.node = xmlFile:getValue(key .. ".activationTrigger#node", nil, self.components, self.i3dMappings) |
685 | if entry.activationTrigger.node ~= nil then |
686 | addTrigger(entry.activationTrigger.node, "dischargeActivationTriggerCallback", self) |
687 | end |
688 | entry.activationTrigger.objects = {} |
689 | entry.activationTrigger.numObjects = 0 |
690 | |
691 | local fillTypeConverterName = xmlFile:getValue(key .. ".fillType#converterName") |
692 | if fillTypeConverterName ~= nil then |
693 | entry.fillTypeConverter = g_fillTypeManager:getConverterDataByName(fillTypeConverterName) |
694 | end |
695 | |
696 | entry.distanceObjectChanges = {} |
697 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, key..".distanceObjectChanges", entry.distanceObjectChanges, self.components, self) |
698 | if #entry.distanceObjectChanges == 0 then |
699 | entry.distanceObjectChanges = nil |
700 | else |
701 | entry.distanceObjectChangeThreshold = xmlFile:getValue(key .. ".distanceObjectChanges#threshold", 0.5) |
702 | ObjectChangeUtil.setObjectChanges(entry.distanceObjectChanges, false, self, self.setMovingToolDirty) |
703 | end |
704 | |
705 | entry.stateObjectChanges = {} |
706 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, key..".stateObjectChanges", entry.stateObjectChanges, self.components, self) |
707 | if #entry.stateObjectChanges == 0 then |
708 | entry.stateObjectChanges = nil |
709 | else |
710 | ObjectChangeUtil.setObjectChanges(entry.stateObjectChanges, false, self, self.setMovingToolDirty) |
711 | end |
712 | |
713 | entry.nodeActiveObjectChanges = {} |
714 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, key..".nodeActiveObjectChanges", entry.nodeActiveObjectChanges, self.components, self) |
715 | if #entry.nodeActiveObjectChanges == 0 then |
716 | entry.nodeActiveObjectChanges = nil |
717 | else |
718 | ObjectChangeUtil.setObjectChanges(entry.nodeActiveObjectChanges, false, self, self.setMovingToolDirty) |
719 | end |
720 | |
721 | entry.effects = g_effectManager:loadEffect(xmlFile, key..".effects", self.components, self, self.i3dMappings) |
722 | |
723 | if self.isClient then |
724 | entry.playSound = xmlFile:getValue(key.."#playSound", true) |
725 | entry.soundNode = xmlFile:getValue(key .. "#soundNode", nil, self.components, self.i3dMappings) |
726 | |
727 | -- additional discharge sound if the flag overwriteSharedSound is not set |
728 | if entry.playSound then |
729 | entry.dischargeSample = g_soundManager:loadSampleFromXML(self.xmlFile, key, "dischargeSound", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
730 | end |
731 | |
732 | if xmlFile:getValue(key..".dischargeSound#overwriteSharedSound", false) then |
733 | entry.playSound = false |
734 | end |
735 | |
736 | entry.dischargeStateSamples = g_soundManager:loadSamplesFromXML(self.xmlFile, key, "dischargeStateSound", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
737 | |
738 | entry.animationNodes = g_animationManager:loadAnimations(self.xmlFile, key .. ".animationNodes", self.components, self, self.i3dMappings) |
739 | end |
740 | |
741 | entry.sentHitDistance = 0 |
742 | entry.isEffectActive = false |
743 | entry.isEffectActiveSent = false |
744 | |
745 | entry.lastEffect = entry.effects[#entry.effects] |
746 | |
747 | return true |
748 | end |
199 | function Dischargeable:onLoad(savegame) |
200 | local spec = self.spec_dischargeable |
201 | |
202 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.pipeEffect", "vehicle.dischargeable.dischargeNode.effects") --FS17 to FS19 |
203 | |
204 | local coverConfigurationId = Utils.getNoNil(self.configurations["dischargeable"], 1) |
205 | local configKey = string.format("vehicle.dischargeable.dischargeableConfigurations.dischargeableConfiguration(%d)", coverConfigurationId - 1) |
206 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.dischargeable.dischargeableConfigurations.dischargeableConfiguration", coverConfigurationId, self.components, self) |
207 | |
208 | if not self.xmlFile:hasProperty(configKey) then |
209 | configKey = "vehicle.dischargeable" |
210 | end |
211 | |
212 | spec.dischargeNodes = {} |
213 | spec.fillUnitDischargeNodeMapping = {} |
214 | spec.dischargNodeMapping = {} |
215 | spec.triggerToDischargeNode = {} |
216 | spec.activationTriggerToDischargeNode = {} |
217 | |
218 | spec.requiresTipOcclusionArea = self.xmlFile:getValue(configKey .. "#requiresTipOcclusionArea", true) |
219 | spec.stopDischargeOnDeactivate = self.xmlFile:getValue(configKey .. "#stopDischargeOnDeactivate", true) |
220 | |
221 | spec.dischargedLiters = 0 |
222 | |
223 | self.xmlFile:iterate(configKey .. ".dischargeNode", function(i, key) |
224 | local entry = {} |
225 | if self:loadDischargeNode(self.xmlFile, key, entry) then |
226 | local canBeAdded = true |
227 | |
228 | if spec.dischargNodeMapping[entry.node] ~= nil then |
229 | Logging.xmlWarning(self.xmlFile, "DischargeNode '%s' already defined. Discharge nodes need to be unique. Ignoring it!", getName(entry.node)) |
230 | canBeAdded = false |
231 | end |
232 | if entry.trigger.node ~= nil and spec.triggerToDischargeNode[entry.trigger.node] ~= nil then |
233 | Logging.xmlWarning(self.xmlFile, "DischargeNode trigger '%s' already defined. DischargeNode triggers need to be unique. Ignoring it!", getName(entry.trigger.node)) |
234 | canBeAdded = false |
235 | end |
236 | if entry.activationTrigger.node ~= nil and spec.activationTriggerToDischargeNode[entry.activationTrigger.node] ~= nil then |
237 | Logging.xmlWarning(self.xmlFile, "DischargeNode activationTrigger '%s' already defined. DischargeNode activationTriggers need to be unique. Ignoring it!", getName(entry.activationTrigger.node)) |
238 | canBeAdded = false |
239 | end |
240 | if self.getFillUnitExists ~= nil and not self:getFillUnitExists(entry.fillUnitIndex) then |
241 | Logging.xmlWarning(self.xmlFile, "FillUnit with index '%d' does not exist for discharge node '%s'. Ignoring discharge node!", entry.fillUnitIndex, key) |
242 | canBeAdded = false |
243 | end |
244 | |
245 | if canBeAdded then |
246 | table.insert(spec.dischargeNodes, entry) |
247 | entry.index = #spec.dischargeNodes |
248 | spec.fillUnitDischargeNodeMapping[entry.fillUnitIndex] = entry |
249 | spec.dischargNodeMapping[entry.node] = entry |
250 | |
251 | if entry.trigger.node ~= nil then |
252 | spec.triggerToDischargeNode[entry.trigger.node] = entry |
253 | end |
254 | if entry.activationTrigger.node ~= nil then |
255 | spec.activationTriggerToDischargeNode[entry.activationTrigger.node] = entry |
256 | end |
257 | end |
258 | end |
259 | end) |
260 | |
261 | spec.requiresTipOcclusionArea = spec.requiresTipOcclusionArea and #spec.dischargeNodes > 0 |
262 | |
263 | spec.currentDischargeState = Dischargeable.DISCHARGE_STATE_OFF |
264 | spec.currentRaycast = nil |
265 | spec.forcedFillTypeIndex = nil |
266 | spec.raycastCollisionMask = CollisionFlag.FILLABLE + CollisionFlag.VEHICLE + CollisionFlag.TERRAIN |
267 | spec.isAsyncRaycastActive = false |
268 | spec.currentRaycast = {} |
269 | self:setCurrentDischargeNodeIndex(1) |
270 | |
271 | spec.dirtyFlag = self:getNextDirtyFlag() |
272 | |
273 | if self.loadDashboardsFromXML ~= nil then |
274 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.dischargeable.dashboards", {valueTypeToLoad = "activeDischargeNode", |
275 | valueObject = self, |
276 | valueFunc = "getCurrentDischargeNodeIndex", |
277 | additionalAttributesFunc = Dischargeable.dashboardDischargeAttributes, |
278 | stateFunc = Dischargeable.dashboardCurrentDischargeNodeState}) |
279 | |
280 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.dischargeable.dashboards", {valueTypeToLoad = "dischargeState", |
281 | valueObject = spec, |
282 | valueFunc = "currentDischargeState", |
283 | additionalAttributesFunc = Dischargeable.dashboardDischargeAttributes, |
284 | stateFunc = Dischargeable.dashboardDischargeActiveState}) |
285 | end |
286 | |
287 | if #spec.dischargeNodes == 0 then |
288 | SpecializationUtil.removeEventListener(self, "onReadStream", Dischargeable) |
289 | SpecializationUtil.removeEventListener(self, "onWriteStream", Dischargeable) |
290 | SpecializationUtil.removeEventListener(self, "onReadUpdateStream", Dischargeable) |
291 | SpecializationUtil.removeEventListener(self, "onWriteUpdateStream", Dischargeable) |
292 | SpecializationUtil.removeEventListener(self, "onUpdate", Dischargeable) |
293 | SpecializationUtil.removeEventListener(self, "onUpdateTick", Dischargeable) |
294 | SpecializationUtil.removeEventListener(self, "onRegisterActionEvents", Dischargeable) |
295 | SpecializationUtil.removeEventListener(self, "onFillUnitFillLevelChanged", Dischargeable) |
296 | SpecializationUtil.removeEventListener(self, "onDeactivate", Dischargeable) |
297 | end |
298 | end |
417 | function Dischargeable:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
418 | local spec = self.spec_dischargeable |
419 | |
420 | local dischargeNode = spec.currentDischargeNode |
421 | if dischargeNode ~= nil then |
422 | if self.isClient then |
423 | Dischargeable.updateActionEvents(self) |
424 | end |
425 | |
426 | if self:getIsDischargeNodeActive(dischargeNode) then |
427 | local trigger = dischargeNode.trigger |
428 | if trigger.numObjects > 0 then |
429 | local lastDischargeObject = dischargeNode.dischargeObject |
430 | |
431 | dischargeNode.dischargeObject = nil |
432 | dischargeNode.dischargeHitObject = nil |
433 | dischargeNode.dischargeHitObjectUnitIndex = nil |
434 | dischargeNode.dischargeHitTerrain = false |
435 | dischargeNode.dischargeShape = nil |
436 | dischargeNode.dischargeDistance = 0 |
437 | dischargeNode.dischargeFillUnitIndex = nil |
438 | dischargeNode.dischargeHit = false -- any (also unsupported) object hit |
439 | |
440 | local nearestDistance = math.huge |
441 | for object, data in pairs(trigger.objects) do |
442 | local fillType = spec.forcedFillTypeIndex |
443 | if fillType == nil then |
444 | fillType = self:getDischargeFillType(dischargeNode) |
445 | end |
446 | |
447 | dischargeNode.dischargeFailedReason = nil |
448 | dischargeNode.dischargeFailedReasonShowAuto = false |
449 | dischargeNode.customNotAllowedWarning = nil |
450 | |
451 | if object:getFillUnitSupportsFillType(data.fillUnitIndex, fillType) then |
452 | local allowFillType = object:getFillUnitAllowsFillType(data.fillUnitIndex, fillType) |
453 | local allowToolType = object:getFillUnitSupportsToolType(data.fillUnitIndex, ToolType.TRIGGER) |
454 | local freeSpace = object:getFillUnitFreeCapacity(data.fillUnitIndex, fillType, self:getActiveFarm()) > 0 |
455 | local accessible = object:getIsFillAllowedFromFarm(self:getActiveFarm()) |
456 | |
457 | if allowFillType and allowToolType and freeSpace then |
458 | local exactFillRootNode = object:getFillUnitExactFillRootNode(data.fillUnitIndex) |
459 | if exactFillRootNode ~= nil and entityExists(exactFillRootNode) then |
460 | local distance = calcDistanceFrom(dischargeNode.node, exactFillRootNode) |
461 | if distance < nearestDistance then |
462 | dischargeNode.dischargeObject = object |
463 | dischargeNode.dischargeHitTerrain = false |
464 | dischargeNode.dischargeShape = data.shape |
465 | dischargeNode.dischargeDistance = distance |
466 | dischargeNode.dischargeFillUnitIndex = data.fillUnitIndex |
467 | nearestDistance = distance |
468 | |
469 | if object ~= lastDischargeObject then |
470 | SpecializationUtil.raiseEvent(self, "onDischargeTargetObjectChanged", object) |
471 | end |
472 | end |
473 | end |
474 | elseif not allowFillType then |
475 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
476 | elseif not allowToolType then |
477 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED |
478 | elseif not accessible then |
479 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_ACCESS |
480 | elseif not freeSpace then |
481 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
482 | end |
483 | |
484 | dischargeNode.dischargeHitObject = object |
485 | dischargeNode.dischargeHitObjectUnitIndex = data.fillUnitIndex |
486 | else |
487 | if fillType ~= FillType.UNKNOWN then |
488 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
489 | end |
490 | end |
491 | |
492 | if dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
493 | or dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
494 | or dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_ACCESS then |
495 | if object.isa == nil or not object:isa(Vehicle) then |
496 | dischargeNode.dischargeFailedReasonShowAuto = true |
497 | end |
498 | end |
499 | |
500 | if dischargeNode.dischargeFailedReason ~= nil then |
501 | -- only display custom warning if at least the fill type would be supported |
502 | if dischargeNode.dischargeFailedReason ~= Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED then |
503 | if object.getCustomDischargeNotAllowedWarning ~= nil then |
504 | dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning() |
505 | end |
506 | end |
507 | end |
508 | |
509 | dischargeNode.dischargeHit = true -- any (also unsupported) object has been hit |
510 | end |
511 | |
512 | if lastDischargeObject ~= nil and dischargeNode.dischargeObject == nil then |
513 | SpecializationUtil.raiseEvent(self, "onDischargeTargetObjectChanged", nil) |
514 | end |
515 | else |
516 | if not spec.isAsyncRaycastActive then |
517 | self:updateRaycast(dischargeNode) |
518 | end |
519 | end |
520 | else |
521 | if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then |
522 | self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true) |
523 | end |
524 | |
525 | if dischargeNode.dischargeObject ~= nil then |
526 | SpecializationUtil.raiseEvent(self, "onDischargeTargetObjectChanged", nil) |
527 | end |
528 | |
529 | dischargeNode.dischargeObject = nil |
530 | dischargeNode.dischargeHitObject = nil |
531 | dischargeNode.dischargeHitObjectUnitIndex = nil |
532 | dischargeNode.dischargeHitTerrain = false |
533 | dischargeNode.dischargeShape = nil |
534 | dischargeNode.dischargeDistance = 0 |
535 | dischargeNode.dischargeFillUnitIndex = nil |
536 | dischargeNode.dischargeHit = false -- any (also unsupported) object hit |
537 | end |
538 | |
539 | self:updateDischargeSound(dischargeNode, dt) |
540 | |
541 | if self.isServer then |
542 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then |
543 | if dischargeNode.dischargeObject ~= nil then |
544 | self:handleFoundDischargeObject(dischargeNode) |
545 | end |
546 | else |
547 | local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
548 | local emptySpeed = self:getDischargeNodeEmptyFactor(dischargeNode) |
549 | |
550 | -- Only allow discharge into a node, or if the land is owned |
551 | local canDischargeToObject = self:getCanDischargeToObject(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT |
552 | local canDischargeToGround = self:getCanDischargeToGround(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND |
553 | local canDischarge = canDischargeToObject or canDischargeToGround |
554 | local allowedToDischarge = dischargeNode.dischargeObject ~= nil or (self:getCanDischargeToLand(dischargeNode) and self:getCanDischargeAtPosition(dischargeNode)) |
555 | local isReadyToStartDischarge = fillLevel > 0 and emptySpeed > 0 and allowedToDischarge and canDischarge |
556 | |
557 | self:setDischargeEffectActive(dischargeNode, isReadyToStartDischarge) |
558 | self:setDischargeEffectDistance(dischargeNode, dischargeNode.dischargeDistance) |
559 | |
560 | local isReadyForDischarge = dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsFullyVisible() |
561 | if isReadyForDischarge and allowedToDischarge and canDischarge then |
562 | local emptyLiters = math.min(fillLevel, dischargeNode.emptySpeed * emptySpeed * dt) |
563 | local dischargedLiters, minDropReached, hasMinDropFillLevel = self:discharge(dischargeNode, emptyLiters) |
564 | |
565 | spec.dischargedLiters = dischargedLiters |
566 | self:handleDischarge(dischargeNode, dischargedLiters, minDropReached, hasMinDropFillLevel) |
567 | end |
568 | end |
569 | |
570 | if dischargeNode.isEffectActive ~= dischargeNode.isEffectActiveSent or math.abs(dischargeNode.dischargeDistanceSent - dischargeNode.dischargeDistance) > 0.05 then |
571 | self:raiseDirtyFlags(spec.dirtyFlag) |
572 | dischargeNode.dischargeDistanceSent = dischargeNode.dischargeDistance |
573 | dischargeNode.isEffectActiveSent = dischargeNode.isEffectActive |
574 | end |
575 | end |
576 | end |
577 | |
578 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then |
579 | local currentDischargeNode = spec.currentDischargeNode |
580 | if self:getIsActiveForInput() and self:getCanDischargeToObject(currentDischargeNode) and self:getCanToggleDischargeToObject() then |
581 | g_currentMission:showTipContext(self:getFillUnitFillType(dischargeNode.fillUnitIndex)) |
582 | end |
583 | end |
584 | |
585 | -- permanent warning if automatic discharge is enabled, otherwise only on action event |
586 | if isActiveForInputIgnoreSelection then |
587 | if dischargeNode ~= nil and dischargeNode.canStartDischargeAutomatically then |
588 | if dischargeNode.dischargeHit then |
589 | if dischargeNode.dischargeFailedReasonShowAuto then |
590 | if dischargeNode.dischargeFailedReason ~= nil and g_currentMission.time > 10000 then |
591 | local warning = self:getDischargeNotAllowedWarning(dischargeNode) |
592 | g_currentMission:showBlinkingWarning(warning, 5000) |
593 | end |
594 | end |
595 | end |
596 | end |
597 | end |
598 | |
599 | -- Look for non-used discharge node that still have their stop animation running |
600 | for _, inactiveDischargeNode in ipairs(spec.dischargeNodes) do |
601 | if inactiveDischargeNode.stopEffectTime ~= nil then |
602 | if inactiveDischargeNode.stopEffectTime < g_time then |
603 | self:setDischargeEffectActive(inactiveDischargeNode, false, true) |
604 | inactiveDischargeNode.stopEffectTime = nil |
605 | else |
606 | self:raiseActive() |
607 | end |
608 | end |
609 | end |
610 | end |
1248 | function Dischargeable:raycastCallbackDischargeNode(hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId) |
1249 | if hitActorId ~= nil then |
1250 | local spec = self.spec_dischargeable |
1251 | local dischargeNode = spec.currentRaycastDischargeNode |
1252 | local object = g_currentMission:getNodeObject(hitActorId) |
1253 | |
1254 | distance = distance - dischargeNode.raycast.yOffset |
1255 | |
1256 | if VehicleDebug.state == VehicleDebug.DEBUG then |
1257 | DebugUtil.drawDebugGizmoAtWorldPos(x,y,z, 0, 0, 1, 0, 1, 0, string.format("hitActorId %s; hitShape %s; object %s", getName(hitActorId), getName(hitShapeId), tostring(object))) |
1258 | end |
1259 | |
1260 | local validObject = object ~= nil and object ~= self |
1261 | -- if we hit a object because of the yOffset it has to be a exact fill root node, otherwise we ignore it |
1262 | -- is used to get exactFillRootNodes if the dischargeNode of the shovel is already below it |
1263 | if validObject and distance < 0 then |
1264 | if object.getFillUnitIndexFromNode ~= nil then |
1265 | validObject = validObject and object:getFillUnitIndexFromNode(hitShapeId) ~= nil |
1266 | end |
1267 | end |
1268 | |
1269 | if validObject then |
1270 | if object.getFillUnitIndexFromNode ~= nil then |
1271 | local fillUnitIndex = object:getFillUnitIndexFromNode(hitShapeId) |
1272 | if fillUnitIndex ~= nil then |
1273 | local fillType = spec.forcedFillTypeIndex |
1274 | if fillType == nil then |
1275 | fillType = self:getDischargeFillType(dischargeNode) |
1276 | end |
1277 | |
1278 | dischargeNode.dischargeFailedReason = nil |
1279 | dischargeNode.dischargeFailedReasonShowAuto = false |
1280 | dischargeNode.customNotAllowedWarning = nil |
1281 | |
1282 | if object:getFillUnitSupportsFillType(fillUnitIndex, fillType) then |
1283 | local allowFillType = object:getFillUnitAllowsFillType(fillUnitIndex, fillType) |
1284 | local allowToolType = object:getFillUnitSupportsToolType(fillUnitIndex, dischargeNode.toolType) |
1285 | local freeSpace = object:getFillUnitFreeCapacity(fillUnitIndex, fillType, self:getActiveFarm()) > 0 |
1286 | local accessible = object:getIsFillAllowedFromFarm(self:getActiveFarm()) |
1287 | |
1288 | if allowFillType and allowToolType and freeSpace then |
1289 | dischargeNode.dischargeObject = object |
1290 | dischargeNode.dischargeShape = hitShapeId |
1291 | dischargeNode.dischargeDistance = distance |
1292 | dischargeNode.dischargeFillUnitIndex = fillUnitIndex |
1293 | |
1294 | if object.getFillUnitExtraDistanceFromNode ~= nil then |
1295 | dischargeNode.dischargeExtraDistance = object:getFillUnitExtraDistanceFromNode(hitShapeId) |
1296 | end |
1297 | elseif not allowFillType then |
1298 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
1299 | elseif not allowToolType then |
1300 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED |
1301 | elseif not accessible then |
1302 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_ACCESS |
1303 | elseif not freeSpace then |
1304 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
1305 | end |
1306 | else |
1307 | if fillType ~= FillType.UNKNOWN then |
1308 | dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
1309 | end |
1310 | end |
1311 | |
1312 | if dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED |
1313 | or dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY |
1314 | or dischargeNode.dischargeFailedReason == Dischargeable.DISCHARGE_REASON_NO_ACCESS then |
1315 | if object.isa == nil or not object:isa(Vehicle) then |
1316 | dischargeNode.dischargeFailedReasonShowAuto = true |
1317 | end |
1318 | end |
1319 | |
1320 | if dischargeNode.dischargeFailedReason ~= nil then |
1321 | -- only display custom warning if at least the fill type would be supported |
1322 | if dischargeNode.dischargeFailedReason ~= Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED then |
1323 | if object.getCustomDischargeNotAllowedWarning ~= nil then |
1324 | dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning() |
1325 | end |
1326 | end |
1327 | end |
1328 | |
1329 | dischargeNode.dischargeHit = true -- any, even unsupported, object has been hit. |
1330 | dischargeNode.dischargeHitObject = object -- hit object, no matter if it's fillable or not |
1331 | dischargeNode.dischargeHitObjectUnitIndex = fillUnitIndex |
1332 | else |
1333 | if dischargeNode.dischargeHit then |
1334 | -- raycast until we hit the object underneath the exact fill root node |
1335 | dischargeNode.dischargeDistance = distance + (dischargeNode.dischargeExtraDistance or 0) |
1336 | dischargeNode.dischargeExtraDistance = nil |
1337 | self:updateDischargeInfo(dischargeNode, x, y, z) |
1338 | |
1339 | return false |
1340 | end |
1341 | end |
1342 | end |
1343 | elseif hitActorId == g_currentMission.terrainRootNode then |
1344 | dischargeNode.dischargeDistance = math.min(dischargeNode.dischargeDistance, distance) |
1345 | dischargeNode.dischargeHitTerrain = true |
1346 | self:updateDischargeInfo(dischargeNode, x, y, z) |
1347 | return false |
1348 | end |
1349 | |
1350 | return true |
1351 | else |
1352 | self:finishDischargeRaycast() |
1353 | end |
1354 | end |
120 | function Dischargeable.registerFunctions(vehicleType) |
121 | SpecializationUtil.registerFunction(vehicleType, "loadDischargeNode", Dischargeable.loadDischargeNode) |
122 | SpecializationUtil.registerFunction(vehicleType, "setCurrentDischargeNodeIndex", Dischargeable.setCurrentDischargeNodeIndex) |
123 | SpecializationUtil.registerFunction(vehicleType, "getCurrentDischargeNode", Dischargeable.getCurrentDischargeNode) |
124 | SpecializationUtil.registerFunction(vehicleType, "getCurrentDischargeNodeIndex", Dischargeable.getCurrentDischargeNodeIndex) |
125 | SpecializationUtil.registerFunction(vehicleType, "getDischargeTargetObject", Dischargeable.getDischargeTargetObject) |
126 | SpecializationUtil.registerFunction(vehicleType, "getCurrentDischargeObject", Dischargeable.getCurrentDischargeObject) |
127 | SpecializationUtil.registerFunction(vehicleType, "discharge", Dischargeable.discharge) |
128 | SpecializationUtil.registerFunction(vehicleType, "dischargeToGround", Dischargeable.dischargeToGround) |
129 | SpecializationUtil.registerFunction(vehicleType, "dischargeToObject", Dischargeable.dischargeToObject) |
130 | SpecializationUtil.registerFunction(vehicleType, "setDischargeState", Dischargeable.setDischargeState) |
131 | SpecializationUtil.registerFunction(vehicleType, "getDischargeState", Dischargeable.getDischargeState) |
132 | SpecializationUtil.registerFunction(vehicleType, "getDischargeFillType", Dischargeable.getDischargeFillType) |
133 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToGround", Dischargeable.getCanDischargeToGround) |
134 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeAtPosition", Dischargeable.getCanDischargeAtPosition) |
135 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToLand", Dischargeable.getCanDischargeToLand) |
136 | SpecializationUtil.registerFunction(vehicleType, "getCanDischargeToObject", Dischargeable.getCanDischargeToObject) |
137 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNotAllowedWarning", Dischargeable.getDischargeNotAllowedWarning) |
138 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleDischargeToObject", Dischargeable.getCanToggleDischargeToObject) |
139 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleDischargeToGround", Dischargeable.getCanToggleDischargeToGround) |
140 | SpecializationUtil.registerFunction(vehicleType, "getIsDischargeNodeActive", Dischargeable.getIsDischargeNodeActive) |
141 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeEmptyFactor", Dischargeable.getDischargeNodeEmptyFactor) |
142 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeByNode", Dischargeable.getDischargeNodeByNode) |
143 | SpecializationUtil.registerFunction(vehicleType, "updateRaycast", Dischargeable.updateRaycast) |
144 | SpecializationUtil.registerFunction(vehicleType, "updateDischargeInfo", Dischargeable.updateDischargeInfo) |
145 | SpecializationUtil.registerFunction(vehicleType, "raycastCallbackDischargeNode", Dischargeable.raycastCallbackDischargeNode) |
146 | SpecializationUtil.registerFunction(vehicleType, "finishDischargeRaycast", Dischargeable.finishDischargeRaycast) |
147 | SpecializationUtil.registerFunction(vehicleType, "getDischargeNodeByIndex", Dischargeable.getDischargeNodeByIndex) |
148 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeOnEmpty", Dischargeable.handleDischargeOnEmpty) |
149 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeNodeChanged", Dischargeable.handleDischargeNodeChanged) |
150 | SpecializationUtil.registerFunction(vehicleType, "handleDischarge", Dischargeable.handleDischarge) |
151 | SpecializationUtil.registerFunction(vehicleType, "handleDischargeRaycast", Dischargeable.handleDischargeRaycast) |
152 | SpecializationUtil.registerFunction(vehicleType, "handleFoundDischargeObject", Dischargeable.handleFoundDischargeObject) |
153 | SpecializationUtil.registerFunction(vehicleType, "setDischargeEffectDistance", Dischargeable.setDischargeEffectDistance) |
154 | SpecializationUtil.registerFunction(vehicleType, "setDischargeEffectActive", Dischargeable.setDischargeEffectActive) |
155 | SpecializationUtil.registerFunction(vehicleType, "updateDischargeSound", Dischargeable.updateDischargeSound) |
156 | SpecializationUtil.registerFunction(vehicleType, "dischargeTriggerCallback", Dischargeable.dischargeTriggerCallback) |
157 | SpecializationUtil.registerFunction(vehicleType, "onDeleteDischargeTriggerObject", Dischargeable.onDeleteDischargeTriggerObject) |
158 | SpecializationUtil.registerFunction(vehicleType, "dischargeActivationTriggerCallback", Dischargeable.dischargeActivationTriggerCallback) |
159 | SpecializationUtil.registerFunction(vehicleType, "onDeleteActivationTriggerObject", Dischargeable.onDeleteActivationTriggerObject) |
160 | SpecializationUtil.registerFunction(vehicleType, "setForcedFillTypeIndex", Dischargeable.setForcedFillTypeIndex) |
161 | end |
62 | function Dischargeable.registerXMLPaths(schema, basePath) |
63 | schema:register(XMLValueType.BOOL, basePath .. "#requiresTipOcclusionArea", "Requires tip occlusion area", true) |
64 | schema:register(XMLValueType.BOOL, basePath .. "#stopDischargeOnDeactivate", "Stop discharge if the vehicle is deactivated", true) |
65 | |
66 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?)#node", "Discharge node") |
67 | schema:register(XMLValueType.INT, basePath .. ".dischargeNode(?)#fillUnitIndex", "Fill unit index") |
68 | schema:register(XMLValueType.INT, basePath .. ".dischargeNode(?)#unloadInfoIndex", "Unload info index", 1) |
69 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#stopDischargeOnEmpty", "Stop discharge if fill unit empty", true) |
70 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#canDischargeToGround", "Can discharge to ground", true) |
71 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#canDischargeToObject", "Can discharge to object", true) |
72 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#canStartDischargeAutomatically", "Can start discharge automatically", false) |
73 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#canStartGroundDischargeAutomatically", "Can start discharge to ground automatically", false) |
74 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#stopDischargeIfNotPossible", "Stop discharge if not possible", "default 'true' while having discharge trigger") |
75 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#canDischargeToGroundAnywhere", "Can discharge to ground independent of land owned state", false) |
76 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?)#emptySpeed", "Empty speed in l/sec", "fill unit capacity") |
77 | schema:register(XMLValueType.TIME, basePath .. ".dischargeNode(?)#effectTurnOffThreshold", "After this time has passed and nothing has been harvested the effects are turned off", 0.25) |
78 | schema:register(XMLValueType.STRING, basePath .. ".dischargeNode(?)#toolType", "Tool type", "dischargable") |
79 | |
80 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?).info#node", "Discharge info node", "Discharge node") |
81 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).info#width", "Discharge info width", 1) |
82 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).info#length", "Discharge info length", 1) |
83 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).info#zOffset", "Discharge info Z axis offset", 0) |
84 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).info#yOffset", "Discharge info y axis offset", 2) |
85 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?).info#limitToGround", "Discharge info is limited to ground", true) |
86 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?).info#useRaycastHitPosition", "Discharge info uses raycast hit position", false) |
87 | |
88 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?).raycast#node", "Raycast node", "Discharge node") |
89 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?).raycast#useWorldNegYDirection", "Use world negative Y Direction", false) |
90 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).raycast#yOffset", "Y Offset", 0) |
91 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).raycast#maxDistance", "Max. raycast distance", 10) |
92 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?)#maxDistance", "Max. raycast distance", 10) |
93 | |
94 | schema:register(XMLValueType.STRING, basePath .. ".dischargeNode(?).fillType#converterName", "Converter to be used to convert the fill types") |
95 | |
96 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?).trigger#node", "Discharge trigger node") |
97 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?).activationTrigger#node", "Discharge activation trigger node") |
98 | |
99 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath .. ".dischargeNode(?).distanceObjectChanges") |
100 | schema:register(XMLValueType.FLOAT, basePath .. ".dischargeNode(?).distanceObjectChanges#threshold", "Defines at which raycast distance the object changes", 0.5) |
101 | |
102 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath .. ".dischargeNode(?).stateObjectChanges") |
103 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath .. ".dischargeNode(?).nodeActiveObjectChanges") |
104 | |
105 | EffectManager.registerEffectXMLPaths(schema, basePath .. ".dischargeNode(?).effects") |
106 | |
107 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?)#playSound", "Play discharge sound", true) |
108 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".dischargeNode(?)#soundNode", "Sound link node", "Discharge node") |
109 | |
110 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".dischargeNode(?)", "dischargeSound") |
111 | schema:register(XMLValueType.BOOL, basePath .. ".dischargeNode(?).dischargeSound#overwriteSharedSound", "Overwrite shared discharge sound with sound defined in discharge node", false) |
112 | |
113 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".dischargeNode(?)", "dischargeStateSound(?)") |
114 | |
115 | AnimationManager.registerAnimationNodesXMLPaths(schema, basePath .. ".dischargeNode(?).animationNodes") |
116 | end |
1738 | function Dischargeable:updateDebugValues(values) |
1739 | local spec = self.spec_dischargeable |
1740 | local currentDischargeNode = spec.currentDischargeNode |
1741 | |
1742 | local state = "OFF" |
1743 | if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then |
1744 | state = "OBJECT" |
1745 | elseif spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then |
1746 | state = "GROUND" |
1747 | end |
1748 | table.insert(values, {name="state", value=state}) |
1749 | table.insert(values, {name="getCanDischargeToObject", value=tostring(self:getCanDischargeToObject(currentDischargeNode))}) |
1750 | table.insert(values, {name="getCanDischargeToGround", value=tostring(self:getCanDischargeToGround(currentDischargeNode))}) |
1751 | table.insert(values, {name="dischargedLiters", value=tostring(spec.dischargedLiters)}) |
1752 | table.insert(values, {name="currentNode", value=tostring(currentDischargeNode)}) |
1753 | |
1754 | for _, dischargeNode in ipairs(spec.dischargeNodes) do |
1755 | table.insert(values, {name="--->", value=tostring(dischargeNode)}) |
1756 | local object = nil |
1757 | if dischargeNode.dischargeObject ~= nil then |
1758 | object = tostring(dischargeNode.dischargeObject.configFileName) |
1759 | end |
1760 | table.insert(values, {name="object", value=tostring(object)}) |
1761 | table.insert(values, {name="distance", value=dischargeNode.dischargeDistance}) |
1762 | table.insert(values, {name="effect", value=tostring(dischargeNode.isEffectActive)}) |
1763 | table.insert(values, {name="fillLevel", value=tostring(self:getFillUnitFillLevel(dischargeNode.fillUnitIndex))}) |
1764 | table.insert(values, {name="litersToDrop", value=tostring(dischargeNode.litersToDrop)}) |
1765 | table.insert(values, {name="emptyFactor", value=tostring(self:getDischargeNodeEmptyFactor(dischargeNode))}) |
1766 | table.insert(values, {name="emptySpeed", value=tostring(self:getDischargeNodeEmptyFactor(dischargeNode))}) |
1767 | table.insert(values, {name="readyForDischarge", value=tostring(dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsFullyVisible())}) |
1768 | table.insert(values, {name="objectsInTrigger", value=tostring(dischargeNode.trigger.numObjects)}) |
1769 | table.insert(values, {name="objectsInActivationTrigger", value=tostring(dischargeNode.activationTrigger.numObjects)}) |
1770 | end |
1771 | end |
1488 | function Dischargeable:updateDischargeSound(dischargeNode, dt) |
1489 | if self.isClient then |
1490 | local fillType = self:getDischargeFillType(dischargeNode) |
1491 | local isInDischargeState = self.spec_dischargeable.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF |
1492 | local isEffectActive = dischargeNode.isEffectActive and fillType ~= FillType.UNKNOWN |
1493 | local lastEffectVisible = dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsVisible() |
1494 | local effectsStillActive = dischargeNode.lastEffect ~= nil and dischargeNode.lastEffect:getIsVisible() |
1495 | if ((isInDischargeState and isEffectActive) or effectsStillActive) and lastEffectVisible then |
1496 | -- shared sample |
1497 | if dischargeNode.playSound and fillType ~= FillType.UNKNOWN then |
1498 | local sharedSample = g_fillTypeManager:getSampleByFillType(fillType) |
1499 | if sharedSample ~= nil then |
1500 | if sharedSample ~= dischargeNode.sharedSample then |
1501 | if dischargeNode.sample ~= nil then |
1502 | g_soundManager:deleteSample(dischargeNode.sample) |
1503 | end |
1504 | |
1505 | dischargeNode.sample = g_soundManager:cloneSample(sharedSample, dischargeNode.node or dischargeNode.soundNode, self) |
1506 | dischargeNode.sharedSample = sharedSample |
1507 | |
1508 | g_soundManager:playSample(dischargeNode.sample) |
1509 | else |
1510 | if not g_soundManager:getIsSamplePlaying(dischargeNode.sample) then |
1511 | g_soundManager:playSample(dischargeNode.sample) |
1512 | end |
1513 | end |
1514 | end |
1515 | end |
1516 | |
1517 | -- additional sample |
1518 | if dischargeNode.dischargeSample ~= nil then |
1519 | if not g_soundManager:getIsSamplePlaying(dischargeNode.dischargeSample) then |
1520 | g_soundManager:playSample(dischargeNode.dischargeSample) |
1521 | end |
1522 | end |
1523 | dischargeNode.turnOffSoundTimer = 250 |
1524 | else |
1525 | if dischargeNode.turnOffSoundTimer ~= nil and dischargeNode.turnOffSoundTimer > 0 then |
1526 | dischargeNode.turnOffSoundTimer = dischargeNode.turnOffSoundTimer - dt |
1527 | if dischargeNode.turnOffSoundTimer <= 0 then |
1528 | -- shared sample |
1529 | if dischargeNode.playSound then |
1530 | if g_soundManager:getIsSamplePlaying(dischargeNode.sample) then |
1531 | g_soundManager:stopSample(dischargeNode.sample) |
1532 | end |
1533 | end |
1534 | |
1535 | -- additional sample |
1536 | if dischargeNode.dischargeSample ~= nil then |
1537 | if g_soundManager:getIsSamplePlaying(dischargeNode.dischargeSample) then |
1538 | g_soundManager:stopSample(dischargeNode.dischargeSample) |
1539 | end |
1540 | end |
1541 | |
1542 | dischargeNode.turnOffSoundTimer = 0 |
1543 | end |
1544 | end |
1545 | end |
1546 | |
1547 | if dischargeNode.dischargeStateSamples ~= nil and #dischargeNode.dischargeStateSamples > 0 then |
1548 | for i=1, #dischargeNode.dischargeStateSamples do |
1549 | local sample = dischargeNode.dischargeStateSamples[i] |
1550 | |
1551 | if isInDischargeState then |
1552 | if not g_soundManager:getIsSamplePlaying(sample) then |
1553 | g_soundManager:playSample(sample) |
1554 | end |
1555 | else |
1556 | if g_soundManager:getIsSamplePlaying(sample) then |
1557 | g_soundManager:stopSample(sample) |
1558 | end |
1559 | end |
1560 | end |
1561 | end |
1562 | end |
1563 | end |
1202 | function Dischargeable:updateRaycast(dischargeNode) |
1203 | local spec = self.spec_dischargeable |
1204 | local raycast = dischargeNode.raycast |
1205 | |
1206 | if raycast.node == nil then |
1207 | return |
1208 | end |
1209 | |
1210 | dischargeNode.lastDischargeObject = dischargeNode.dischargeObject |
1211 | dischargeNode.dischargeObject = nil |
1212 | dischargeNode.dischargeHitObject = nil |
1213 | dischargeNode.dischargeHitObjectUnitIndex = nil |
1214 | dischargeNode.dischargeHitTerrain = false |
1215 | dischargeNode.dischargeShape = nil |
1216 | dischargeNode.dischargeDistance = math.huge |
1217 | dischargeNode.dischargeFillUnitIndex = nil |
1218 | dischargeNode.dischargeHit = false |
1219 | |
1220 | local x,y,z = getWorldTranslation(raycast.node) |
1221 | local dx,dy,dz = 0, -1, 0 |
1222 | |
1223 | y = y + raycast.yOffset |
1224 | |
1225 | if not raycast.useWorldNegYDirection then |
1226 | dx,dy,dz = localDirectionToWorld(raycast.node, 0,-1,0) |
1227 | end |
1228 | |
1229 | spec.currentRaycastDischargeNode = dischargeNode |
1230 | spec.currentRaycast = raycast |
1231 | spec.isAsyncRaycastActive = true |
1232 | raycastAll(x,y,z, dx,dy,dz, "raycastCallbackDischargeNode", dischargeNode.maxDistance, self, spec.raycastCollisionMask, false, false) |
1233 | |
1234 | -- TODO: remove if async raycast is added |
1235 | self:raycastCallbackDischargeNode(nil) |
1236 | end |