LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

Dischargeable

Description
Specialization for discharging fillables from a vehicle
Functions

actionEventToggleDischargeToGround

Description
Definition
actionEventToggleDischargeToGround()
Code
1364function Dischargeable.actionEventToggleDischargeToGround(self, actionName, inputValue, callbackState, isAnalog)
1365 if self:getCanToggleDischargeToGround() then
1366 local spec = self.spec_dischargeable
1367 local currentDischargeNode = spec.currentDischargeNode
1368 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
1369 if self:getCanDischargeToGround(currentDischargeNode) then
1370 self:setDischargeState(Dischargeable.DISCHARGE_STATE_GROUND)
1371 else
1372 if not self:getCanDischargeToLand(currentDischargeNode) then
1373 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 5000)
1374 elseif not self:getCanDischargeAtPosition(currentDischargeNode) then
1375 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_actionNotAllowedHere"), 5000)
1376 end
1377 end
1378 else
1379 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1380 end
1381 end
1382end

actionEventToggleDischarging

Description
Definition
actionEventToggleDischarging()
Code
1386function Dischargeable.actionEventToggleDischarging(self, actionName, inputValue, callbackState, isAnalog)
1387 if self:getCanToggleDischargeToObject() then
1388 local spec = self.spec_dischargeable
1389 local currentDischargeNode = spec.currentDischargeNode
1390
1391 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
1392 if self:getCanDischargeToObject(currentDischargeNode) then
1393 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT)
1394 elseif currentDischargeNode.dischargeHit then
1395 if self:getDischargeFillType(currentDischargeNode) ~= FillType.UNKNOWN then
1396 local warning = self:getDischargeNotAllowedWarning(currentDischargeNode)
1397 g_currentMission:showBlinkingWarning(warning, 5000)
1398 end
1399 end
1400 else
1401 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1402 end
1403 end
1404end

discharge

Description
Definition
discharge()
Code
579function Dischargeable:discharge(dischargeNode, emptyLiters)
580 local spec = self.spec_dischargeable
581 local dischargedLiters = 0
582 local minDropReached = true
583 local hasMinDropFillLevel = true
584 local object, fillUnitIndex = self:getDischargeTargetObject(dischargeNode)
585
586 dischargeNode.currentDischargeObject = nil
587
588 if object ~= nil then
589 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then
590 dischargedLiters = self:dischargeToObject(dischargeNode, emptyLiters, object, fillUnitIndex)
591 end
592 elseif dischargeNode.dischargeHitTerrain then
593 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
594 dischargedLiters, minDropReached, hasMinDropFillLevel = self:dischargeToGround(dischargeNode, emptyLiters)
595 end
596 end
597
598 return dischargedLiters, minDropReached, hasMinDropFillLevel
599end

dischargeActivationTriggerCallback

Description
Definition
dischargeActivationTriggerCallback()
Code
1224function 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
1258end

dischargeToGround

Description
Definition
dischargeToGround()
Code
603function 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
640end

dischargeToObject

Description
Definition
dischargeToObject()
Code
644function Dischargeable:dischargeToObject(dischargeNode, emptyLiters, object, targetFillUnitIndex)
645 local fillType = self:getDischargeFillType(dischargeNode)
646 local supportsFillType = object:getFillUnitSupportsFillType(targetFillUnitIndex, fillType)
647 local dischargedLiters = 0
648
649 if supportsFillType then
650 local allowFillType = object:getFillUnitAllowsFillType(targetFillUnitIndex, fillType)
651 if allowFillType then
652 dischargeNode.currentDischargeObject = object
653
654 local delta = object:addFillUnitFillLevel(self:getActiveFarm(), targetFillUnitIndex, emptyLiters, fillType, ToolType.DISCHARGEABLE, dischargeNode.info)
655 local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex)
656
657 dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -delta, fillType, ToolType.UNDEFINED, unloadInfo)
658 end
659 end
660
661 return dischargedLiters
662end

dischargeTriggerCallback

Description
Definition
dischargeTriggerCallback()
Code
1172function 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
1206end

finishDischargeRaycast

Description
Definition
finishDischargeRaycast()
Code
1012function Dischargeable:finishDischargeRaycast()
1013 local spec = self.spec_dischargeable
1014 local dischargeNode = spec.currentRaycastDischargeNode
1015 self:handleDischargeRaycast(dischargeNode, dischargeNode.dischargeObject, dischargeNode.dischargeShape, dischargeNode.dischargeDistance, dischargeNode.dischargeFillUnitIndex, dischargeNode.dischargeHitTerrain)
1016 spec.isAsyncRaycastActive = false
1017end

getCanBeSelected

Description
Definition
getCanBeSelected()
Code
561function Dischargeable:getCanBeSelected(superFunc)
562 return true
563end

getCanDischargeAtPosition

Description
Definition
getCanDischargeAtPosition()
Code
751function 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
782end

getCanDischargeToGround

Description
Definition
getCanDischargeToGround()
Code
697function Dischargeable:getCanDischargeToGround(dischargeNode)
698 if dischargeNode == nil then
699 return false
700 end
701
702 if not dischargeNode.dischargeHitTerrain then
703 return false
704 end
705
706 if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then
707 local fillTypeIndex = self:getDischargeFillType(dischargeNode)
708 if not DensityMapHeightUtil.getCanTipToGround(fillTypeIndex) then
709 return false
710 end
711 end
712
713 if not self:getCanDischargeToLand(dischargeNode) then
714 return false
715 end
716
717 if not self:getCanDischargeAtPosition(dischargeNode) then
718 return false
719 end
720
721 return true
722end

getCanDischargeToLand

Description
Definition
getCanDischargeToLand()
Code
726function Dischargeable:getCanDischargeToLand(dischargeNode)
727 if dischargeNode == nil then
728 return false
729 end
730
731 local info = dischargeNode.info
732 local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset)
733 local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset)
734 local activeFarm = self:getActiveFarm()
735
736 -- is start owned
737 if not g_currentMission.accessHandler:canFarmAccessLand(activeFarm, sx, sz) then
738 return false
739 end
740
741 -- is end owned
742 if not g_currentMission.accessHandler:canFarmAccessLand(activeFarm, ex, ez) then
743 return false
744 end
745
746 return true
747end

getCanDischargeToObject

Description
Definition
getCanDischargeToObject()
Code
786function Dischargeable:getCanDischargeToObject(dischargeNode)
787 if dischargeNode == nil then
788 return false
789 end
790
791 local object = dischargeNode.dischargeObject
792 if object == nil then
793 return false
794 end
795
796 local fillType = self:getDischargeFillType(dischargeNode)
797
798 if not object:getFillUnitSupportsFillType(dischargeNode.dischargeFillUnitIndex, fillType) then
799 return false
800 end
801
802 local allowFillType = object:getFillUnitAllowsFillType(dischargeNode.dischargeFillUnitIndex, fillType)
803 if not allowFillType then
804 return false
805 end
806
807 if object.getFillUnitFreeCapacity ~= nil and object:getFillUnitFreeCapacity(dischargeNode.dischargeFillUnitIndex, fillType, self:getActiveFarm()) <= 0 then
808 return false
809 end
810
811 if object.getIsFillAllowedFromFarm ~= nil and not object:getIsFillAllowedFromFarm(self:getActiveFarm()) then
812 return false
813 end
814
815 -- Adding should only be done if removing is allowed (generally adding is always allowed because it is beneficial to the receiver)
816 -- A case where this is not allowed is when this is a pallet where the the controller does not own it (or can access it)
817 if self.getMountObject ~= nil then
818 local mounter = self:getDynamicMountObject() or self:getMountObject()
819 if mounter ~= nil then
820 -- if the active farm of the mounter has NO access to farmId fill unit: disallow
821 if not g_currentMission.accessHandler:canFarmAccess(mounter:getActiveFarm(), self, true) then
822 return false
823 end
824 end
825 end
826
827 return true
828end

getCanToggleDischargeToGround

Description
Definition
getCanToggleDischargeToGround()
Code
854function Dischargeable:getCanToggleDischargeToGround()
855 local spec = self.spec_dischargeable
856 local dischargeNode = spec.currentDischargeNode
857
858 return dischargeNode ~= nil and dischargeNode.canDischargeToGround
859end

getCanToggleDischargeToObject

Description
Definition
getCanToggleDischargeToObject()
Code
845function Dischargeable:getCanToggleDischargeToObject()
846 local spec = self.spec_dischargeable
847 local dischargeNode = spec.currentDischargeNode
848
849 return dischargeNode ~= nil and dischargeNode.canDischargeToObject
850end

getCurrentDischargeNode

Description
Definition
getCurrentDischargeNode()
Code
535function Dischargeable:getCurrentDischargeNode()
536 local spec = self.spec_dischargeable
537 return spec.currentDischargeNode
538end

getCurrentDischargeObject

Description
Object being discharged to
Definition
getCurrentDischargeObject()
Code
548function Dischargeable:getCurrentDischargeObject(dischargeNode)
549 return dischargeNode.currentDischargeObject
550end

getDischargeFillType

Description
Definition
getDischargeFillType()
Code
691function Dischargeable:getDischargeFillType(dischargeNode)
692 return self:getFillUnitFillType(dischargeNode.fillUnitIndex)
693end

getDischargeNodeByIndex

Description
Definition
getDischargeNodeByIndex()
Code
1021function Dischargeable:getDischargeNodeByIndex(index)
1022 local spec = self.spec_dischargeable
1023 return spec.dischargeNodes[index]
1024end

getDischargeNodeByNode

Description
Definition
getDischargeNodeByNode()
Code
875function Dischargeable:getDischargeNodeByNode(node)
876 return self.spec_dischargeable.dischargNodeMapping[node]
877end

getDischargeNodeEmptyFactor

Description
Definition
getDischargeNodeEmptyFactor()
Code
869function Dischargeable:getDischargeNodeEmptyFactor(dischargeNode)
870 return 1.0
871end

getDischargeNotAllowedWarning

Description
Definition
getDischargeNotAllowedWarning()
Code
832function Dischargeable:getDischargeNotAllowedWarning(dischargeNode)
833 local text = g_i18n:getText(Dischargeable.DISCHARGE_WARNINGS[dischargeNode.dischargeFailedReason or 1] or "warning_notAcceptedHere")
834 if dischargeNode.customNotAllowedWarning ~= nil then
835 text = dischargeNode.customNotAllowedWarning
836 end
837
838 local fillType = self:getDischargeFillType(dischargeNode)
839 local fillTypeDesc = g_fillTypeManager:getFillTypeByIndex(fillType)
840 return string.format(text, fillTypeDesc.title)
841end

getDischargeState

Description
Definition
getDischargeState()
Code
685function Dischargeable:getDischargeState()
686 return self.spec_dischargeable.currentDischargeState
687end

getDischargeTargetObject

Description
Object found for possible discharging
Definition
getDischargeTargetObject()
Code
542function Dischargeable:getDischargeTargetObject(dischargeNode)
543 return dischargeNode.dischargeObject, dischargeNode.dischargeFillUnitIndex
544end

getDoConsumePtoPower

Description
Definition
getDoConsumePtoPower()
Code
567function Dischargeable:getDoConsumePtoPower(superFunc)
568 return self:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF or superFunc(self)
569end

getIsDischargeNodeActive

Description
Definition
getIsDischargeNodeActive()
Code
863function Dischargeable:getIsDischargeNodeActive(dischargeNode)
864 return true
865end

getIsPowerTakeOffActive

Description
Definition
getIsPowerTakeOffActive()
Code
573function Dischargeable:getIsPowerTakeOffActive(superFunc)
574 return self:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF or superFunc(self)
575end

getRequiresTipOcclusionArea

Description
Definition
getRequiresTipOcclusionArea()
Code
554function Dischargeable:getRequiresTipOcclusionArea()
555 local spec = self.spec_dischargeable
556 return spec.requiresTipOcclusionArea
557end

handleDischarge

Description
Definition
handleDischarge()
Code
1042function Dischargeable:handleDischarge(dischargeNode, dischargedLiters, minDropReached, hasMinDropFillLevel)
1043 local spec = self.spec_dischargeable
1044
1045 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
1046 local canDrop = not minDropReached and hasMinDropFillLevel
1047
1048 if dischargeNode.stopDischargeIfNotPossible then
1049 if dischargedLiters == 0 and not canDrop then
1050 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1051 end
1052 end
1053 end
1054end

handleDischargeNodeChanged

Description
Definition
handleDischargeNodeChanged()
Code
1037function Dischargeable:handleDischargeNodeChanged()
1038end

handleDischargeOnEmpty

Description
Definition
handleDischargeOnEmpty()
Code
1028function Dischargeable:handleDischargeOnEmpty(dischargeNode)
1029 local spec = self.spec_dischargeable
1030 if spec.currentDischargeNode.stopDischargeOnEmpty then
1031 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
1032 end
1033end

handleDischargeRaycast

Description
Definition
handleDischargeRaycast()
Code
1058function Dischargeable:handleDischargeRaycast(dischargeNode, object, shape, distance, illUnitIndex, hitTerrain)
1059 local spec = self.spec_dischargeable
1060 -- stop tipping if discharge to object is active but object is not hit anymore
1061 if object == nil and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then
1062 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1063 end
1064end

handleFoundDischargeObject

Description
Definition
handleFoundDischargeObject()
Code
1068function Dischargeable:handleFoundDischargeObject(dischargeNode)
1069 if dischargeNode.canStartDischargeAutomatically then
1070 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT)
1071 end
1072end

loadDischargeNode

Description
Definition
loadDischargeNode()
Code
424function 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
515end

onDeactivate

Description
Definition
onDeactivate()
Code
1316function Dischargeable:onDeactivate()
1317 local spec = self.spec_dischargeable
1318 if spec.stopDischargeOnDeactivate then
1319 if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
1320 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
1321 end
1322 end
1323end

onDelete

Description
Definition
onDelete()
Code
180function Dischargeable:onDelete()
181 local spec = self.spec_dischargeable
182 for _, dischargeNode in ipairs(spec.dischargeNodes) do
183 g_effectManager:deleteEffects(dischargeNode.effects)
184
185 if self.isClient then
186 g_soundManager:deleteSample(dischargeNode.sample)
187 end
188
189 if dischargeNode.trigger.node ~= nil then
190 removeTrigger(dischargeNode.trigger.node)
191 end
192
193 if dischargeNode.activationTrigger.node ~= nil then
194 removeTrigger(dischargeNode.activationTrigger.node)
195 end
196 end
197end

onDeleteActivationTriggerObject

Description
Definition
onDeleteActivationTriggerObject()
Code
1262function Dischargeable:onDeleteActivationTriggerObject(object)
1263 local spec = self.spec_dischargeable
1264 for _, dischargeNode in pairs(spec.activationTriggerToDischargeNode) do
1265 local trigger = dischargeNode.activationTrigger
1266
1267 if trigger.objects[object] ~= nil then
1268 trigger.objects[object] = nil
1269 trigger.numObjects = trigger.numObjects - 1
1270 end
1271 end
1272end

onDeleteDischargeTriggerObject

Description
Definition
onDeleteDischargeTriggerObject()
Code
1210function Dischargeable:onDeleteDischargeTriggerObject(object)
1211 local spec = self.spec_dischargeable
1212 for _, dischargeNode in pairs(spec.triggerToDischargeNode) do
1213 local trigger = dischargeNode.trigger
1214
1215 if trigger.objects[object] ~= nil then
1216 trigger.objects[object] = nil
1217 trigger.numObjects = trigger.numObjects - 1
1218 end
1219 end
1220end

onFillUnitFillLevelChanged

Description
Definition
onFillUnitFillLevelChanged()
Code
1302function Dischargeable:onFillUnitFillLevelChanged(fillUnitIndex, fillLevelDelta, fillType, toolType, fillPositionData, appliedDelta)
1303 local spec = self.spec_dischargeable
1304
1305 local dischargeNode = spec.fillUnitDischargeNodeMapping[fillUnitIndex]
1306 if dischargeNode ~= nil then
1307 local fillLevel = self:getFillUnitFillLevel(fillUnitIndex)
1308 if fillLevel == 0 then
1309 self:handleDischargeOnEmpty(dischargeNode)
1310 end
1311 end
1312end

onLoad

Description
Definition
onLoad()
Code
111function 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()
176end

onReadStream

Description
Definition
onReadStream()
Code
201function Dischargeable:onReadStream(streamId, connection)
202 if connection:getIsServer() then
203 local spec = self.spec_dischargeable
204
205 for _, dischargeNode in ipairs(spec.dischargeNodes) do
206 if streamReadBool(streamId) then
207 local distance = streamReadUIntN(streamId, 8)*dischargeNode.maxDistance / 255
208 dischargeNode.dischargeDistance = distance
209 self:setDischargeEffectActive(dischargeNode, true)
210 self:setDischargeEffectDistance(dischargeNode, distance)
211 else
212 self:setDischargeEffectActive(dischargeNode, false)
213 end
214 end
215 end
216end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
234function Dischargeable:onReadUpdateStream(streamId, timestamp, connection)
235 if connection:getIsServer() then
236 local spec = self.spec_dischargeable
237
238 if streamReadBool(streamId) then
239 for _, dischargeNode in ipairs(spec.dischargeNodes) do
240 if streamReadBool(streamId) then
241 local distance = streamReadUIntN(streamId, 8)*dischargeNode.maxDistance / 255
242 dischargeNode.dischargeDistance = distance
243 self:setDischargeEffectActive(dischargeNode, true)
244 self:setDischargeEffectDistance(dischargeNode, distance)
245 else
246 self:setDischargeEffectActive(dischargeNode, false)
247 end
248 end
249 end
250 end
251end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
1282function Dischargeable:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
1283 if self.isClient then
1284 local spec = self.spec_dischargeable
1285 self:clearActionEventsTable(spec.actionEvents)
1286
1287 if isActiveForInputIgnoreSelection then
1288 if self:getCanToggleDischargeToGround() then
1289 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_TIPSTATE_GROUND, self, Dischargeable.actionEventToggleDischargeToGround, false, true, false, true, nil)
1290 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL)
1291 end
1292 if self:getCanToggleDischargeToObject() then
1293 local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_TIPSTATE, self, Dischargeable.actionEventToggleDischarging, false, true, false, true, nil)
1294 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)
1295 end
1296 end
1297 end
1298end

onUpdate

Description
Definition
onUpdate()
Code
271function Dischargeable:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
272 local spec = self.spec_dischargeable
273
274 local dischargeNode = spec.currentDischargeNode
275 if dischargeNode ~= nil then
276 if dischargeNode.activationTrigger.numObjects > 0 or spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
277 self:raiseActive()
278 end
279 end
280end

onUpdateTick

Description
Definition
onUpdateTick()
Code
284function 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
420end

onWriteStream

Description
Definition
onWriteStream()
Code
220function Dischargeable:onWriteStream(streamId, connection)
221 if not connection:getIsServer() then
222 local spec = self.spec_dischargeable
223
224 for _, dischargeNode in ipairs(spec.dischargeNodes) do
225 if streamWriteBool(streamId, dischargeNode.isEffectActiveSent) then
226 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(dischargeNode.dischargeDistanceSent/dischargeNode.maxDistance*255), 1, 255), 8)
227 end
228 end
229 end
230end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
255function Dischargeable:onWriteUpdateStream(streamId, connection, dirtyMask)
256 if not connection:getIsServer() then
257 local spec = self.spec_dischargeable
258
259 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
260 for _, dischargeNode in ipairs(spec.dischargeNodes) do
261 if streamWriteBool(streamId, dischargeNode.isEffectActiveSent) then
262 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(dischargeNode.dischargeDistanceSent/dischargeNode.maxDistance*255), 1, 255), 8)
263 end
264 end
265 end
266 end
267end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
30function Dischargeable.prerequisitesPresent(specializations)
31 return SpecializationUtil.hasSpecialization(FillUnit, specializations) and SpecializationUtil.hasSpecialization(FillVolume, specializations)
32end

raycastCallbackDischargeNode

Description
Definition
raycastCallbackDischargeNode()
Code
924function 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
1008end

registerEventListeners

Description
Definition
registerEventListeners()
Code
95function Dischargeable.registerEventListeners(vehicleType)
96 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Dischargeable)
97 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Dischargeable)
98 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Dischargeable)
99 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Dischargeable)
100 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Dischargeable)
101 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Dischargeable)
102 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Dischargeable)
103 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Dischargeable)
104 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Dischargeable)
105 SpecializationUtil.registerEventListener(vehicleType, "onFillUnitFillLevelChanged", Dischargeable)
106 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Dischargeable)
107end

registerEvents

Description
Definition
registerEvents()
Code
89function Dischargeable.registerEvents(vehicleType)
90 SpecializationUtil.registerEvent(vehicleType, "onDischargeStateChanged")
91end

registerFunctions

Description
Definition
registerFunctions()
Code
36function 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)
76end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
80function Dischargeable.registerOverwrittenFunctions(vehicleType)
81 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getRequiresTipOcclusionArea", Dischargeable.getRequiresTipOcclusionArea)
82 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", Dischargeable.getCanBeSelected)
83 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDoConsumePtoPower", Dischargeable.getDoConsumePtoPower)
84 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsPowerTakeOffActive", Dischargeable.getIsPowerTakeOffActive)
85end

setCurrentDischargeNodeIndex

Description
Definition
setCurrentDischargeNodeIndex()
Code
519function Dischargeable:setCurrentDischargeNodeIndex(dischargeNodeIndex)
520 local spec = self.spec_dischargeable
521
522 -- deactivate effects on old discharge node
523 if spec.currentDischargeNode ~= nil then
524 self:setDischargeEffectActive(spec.currentDischargeNode, false, true)
525 self:updateDischargeSound(spec.currentDischargeNode, 99999)
526 end
527
528 spec.currentDischargeNode = spec.dischargeNodes[dischargeNodeIndex]
529
530 self:handleDischargeNodeChanged()
531end

setDischargeEffectActive

Description
Definition
setDischargeEffectActive()
Code
1090function Dischargeable:setDischargeEffectActive(dischargeNode, isActive, force)
1091 if isActive then
1092 if not dischargeNode.isEffectActive then
1093 g_effectManager:setFillType(dischargeNode.effects, self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex))
1094 g_effectManager:startEffects(dischargeNode.effects)
1095 dischargeNode.isEffectActive = true
1096 end
1097
1098 dischargeNode.stopEffectTime = nil
1099 else
1100 if force == nil or not force then
1101 if dischargeNode.stopEffectTime == nil then
1102 dischargeNode.stopEffectTime = g_time + 500
1103 end
1104 else
1105 if dischargeNode.isEffectActive then
1106 g_effectManager:stopEffects(dischargeNode.effects)
1107 dischargeNode.isEffectActive = false
1108 end
1109 end
1110 end
1111end

setDischargeEffectDistance

Description
Definition
setDischargeEffectDistance()
Code
1076function Dischargeable:setDischargeEffectDistance(dischargeNode, distance)
1077 if dischargeNode.isEffectActive then
1078 if dischargeNode.effects ~= nil and distance ~= math.huge then
1079 for _, effect in pairs(dischargeNode.effects) do
1080 if effect.setDistance ~= nil then
1081 effect:setDistance(distance, g_currentMission.terrainRootNode)
1082 end
1083 end
1084 end
1085 end
1086end

setDischargeState

Description
Definition
setDischargeState()
Code
666function Dischargeable:setDischargeState(state, noEventSend)
667 local spec = self.spec_dischargeable
668 if state ~= spec.currentDischargeState then
669 SetDischargeStateEvent.sendEvent(self, state, noEventSend)
670
671 spec.currentDischargeState = state
672
673 local dischargeNode = spec.currentDischargeNode
674 if state == Dischargeable.DISCHARGE_STATE_OFF then
675 self:setDischargeEffectActive(dischargeNode, false)
676 dischargeNode.isEffectActiveSent = false
677 end
678
679 SpecializationUtil.raiseEvent(self, "onDischargeStateChanged", state)
680 end
681end

setForcedFillTypeIndex

Description
Definition
setForcedFillTypeIndex()
Code
1276function Dischargeable:setForcedFillTypeIndex(fillTypeIndex)
1277 self.spec_dischargeable.forcedFillTypeIndex = fillTypeIndex
1278end

updateActionEvents

Description
Definition
updateActionEvents()
Code
1408function 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
1449end

updateDebugValues

Description
Definition
updateDebugValues()
Code
1327function 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
1360end

updateDischargeInfo

Description
Definition
updateDischargeInfo()
Code
916function Dischargeable:updateDischargeInfo(dischargeNode, x, y, z)
917 if dischargeNode.info.useRaycastHitPosition then
918 setWorldTranslation(dischargeNode.info.node, x, y, z)
919 end
920end

updateDischargeSound

Description
Definition
updateDischargeSound()
Code
1115function 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
1168end

updateRaycast

Description
Definition
updateRaycast()
Code
881function 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)
912end