LUADOC - Farming Simulator 22

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
1775function Dischargeable.actionEventToggleDischargeToGround(self, actionName, inputValue, callbackState, isAnalog)
1776 if self:getCanToggleDischargeToGround() then
1777 local spec = self.spec_dischargeable
1778 local currentDischargeNode = spec.currentDischargeNode
1779 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
1780 if self:getCanDischargeToGround(currentDischargeNode) then
1781 self:setDischargeState(Dischargeable.DISCHARGE_STATE_GROUND)
1782 else
1783 if not self:getCanDischargeToLand(currentDischargeNode) then
1784 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 5000)
1785 elseif not self:getCanDischargeAtPosition(currentDischargeNode) then
1786 g_currentMission:showBlinkingWarning(g_i18n:getText("warning_actionNotAllowedHere"), 5000)
1787 end
1788 end
1789 else
1790 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1791 end
1792 end
1793end

actionEventToggleDischarging

Description
Definition
actionEventToggleDischarging()
Code
1797function Dischargeable.actionEventToggleDischarging(self, actionName, inputValue, callbackState, isAnalog)
1798 if self:getCanToggleDischargeToObject() then
1799 local spec = self.spec_dischargeable
1800 local currentDischargeNode = spec.currentDischargeNode
1801
1802 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
1803 if self:getCanDischargeToObject(currentDischargeNode) then
1804 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT)
1805 elseif currentDischargeNode.dischargeHit then
1806 if self:getDischargeFillType(currentDischargeNode) ~= FillType.UNKNOWN then
1807 local warning = self:getDischargeNotAllowedWarning(currentDischargeNode)
1808 g_currentMission:showBlinkingWarning(warning, 5000)
1809 end
1810 end
1811 else
1812 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1813 end
1814 end
1815end

dashboardCurrentDischargeNodeState

Description
Definition
dashboardCurrentDischargeNodeState()
Code
1872function Dischargeable.dashboardCurrentDischargeNodeState(self, dashboard, newValue, minValue, maxValue, isActive)
1873 if dashboard.dischargeNodeIndex ~= nil then
1874 local state = newValue == dashboard.dischargeNodeIndex
1875 Dashboard.defaultDashboardStateFunc(self, dashboard, state, minValue, maxValue, isActive)
1876 end
1877end

dashboardDischargeActiveState

Description
Definition
dashboardDischargeActiveState()
Code
1881function Dischargeable.dashboardDischargeActiveState(self, dashboard, newValue, minValue, maxValue, isActive)
1882 Dashboard.defaultDashboardStateFunc(self, dashboard, self.spec_dischargeable.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF, minValue, maxValue, isActive)
1883end

dashboardDischargeAttributes

Description
Definition
dashboardDischargeAttributes()
Code
1865function Dischargeable.dashboardDischargeAttributes(self, xmlFile, key, dashboard, isActive)
1866 dashboard.dischargeNodeIndex = xmlFile:getValue(key .. "#dischargeNodeIndex")
1867 return true
1868end

discharge

Description
Definition
discharge()
Code
864function Dischargeable:discharge(dischargeNode, emptyLiters)
865 local spec = self.spec_dischargeable
866 local dischargedLiters = 0
867 local minDropReached = true
868 local hasMinDropFillLevel = true
869 local object, fillUnitIndex = self:getDischargeTargetObject(dischargeNode)
870
871 dischargeNode.currentDischargeObject = nil
872
873 if object ~= nil then
874 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then
875 dischargedLiters = self:dischargeToObject(dischargeNode, emptyLiters, object, fillUnitIndex)
876 end
877 elseif dischargeNode.dischargeHitTerrain then
878 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
879 dischargedLiters, minDropReached, hasMinDropFillLevel = self:dischargeToGround(dischargeNode, emptyLiters)
880 end
881 end
882
883 return dischargedLiters, minDropReached, hasMinDropFillLevel
884end

dischargeActivationTriggerCallback

Description
Definition
dischargeActivationTriggerCallback()
Code
1618function Dischargeable:dischargeActivationTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
1619 local spec = self.spec_dischargeable
1620 if onEnter or onLeave then
1621 local object = g_currentMission:getNodeObject(otherActorId)
1622 if object ~= nil and object ~= self then
1623 if object.getFillUnitIndexFromNode ~= nil then
1624 local fillUnitIndex = object:getFillUnitIndexFromNode(otherShapeId)
1625 local dischargeNode = spec.activationTriggerToDischargeNode[triggerId]
1626
1627 if dischargeNode ~= nil and fillUnitIndex ~= nil then
1628 local trigger = dischargeNode.activationTrigger
1629 if onEnter then
1630 if trigger.objects[object] == nil then
1631 trigger.objects[object] = {count=0, fillUnitIndex=fillUnitIndex, shape=otherShapeId}
1632 trigger.numObjects = trigger.numObjects + 1
1633
1634 object:addDeleteListener(self, "onDeleteActivationTriggerObject")
1635 end
1636 trigger.objects[object].count = trigger.objects[object].count + 1
1637
1638 self:raiseActive()
1639 elseif onLeave then
1640 trigger.objects[object].count = trigger.objects[object].count - 1
1641 if trigger.objects[object].count == 0 then
1642 trigger.objects[object] = nil
1643 trigger.numObjects = trigger.numObjects - 1
1644
1645 object:removeDeleteListener(self, "onDeleteActivationTriggerObject")
1646 end
1647 end
1648 end
1649 end
1650 end
1651 end
1652end

dischargeToGround

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

dischargeToObject

Description
Definition
dischargeToObject()
Code
934function Dischargeable:dischargeToObject(dischargeNode, emptyLiters, object, targetFillUnitIndex)
935 local fillType, factor = self:getDischargeFillType(dischargeNode)
936 local supportsFillType = object:getFillUnitSupportsFillType(targetFillUnitIndex, fillType)
937 local dischargedLiters = 0
938
939 if supportsFillType then
940 local allowFillType = object:getFillUnitAllowsFillType(targetFillUnitIndex, fillType)
941 if allowFillType then
942 dischargeNode.currentDischargeObject = object
943
944 local delta = object:addFillUnitFillLevel(self:getActiveFarm(), targetFillUnitIndex, emptyLiters * factor, fillType, dischargeNode.toolType, dischargeNode.info)
945 delta = delta / factor
946 local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex)
947
948 dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -delta, self:getFillUnitFillType(dischargeNode.fillUnitIndex), ToolType.UNDEFINED, unloadInfo)
949 end
950 end
951
952 return dischargedLiters
953end

dischargeTriggerCallback

Description
Definition
dischargeTriggerCallback()
Code
1567function Dischargeable:dischargeTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
1568 local spec = self.spec_dischargeable
1569 if onEnter or onLeave then
1570 local object = g_currentMission:getNodeObject(otherActorId)
1571 if object ~= nil and object ~= self then
1572 if object.getFillUnitIndexFromNode ~= nil then
1573 local fillUnitIndex = object:getFillUnitIndexFromNode(otherShapeId)
1574 local dischargeNode = spec.triggerToDischargeNode[triggerId]
1575
1576 if dischargeNode ~= nil and fillUnitIndex ~= nil then
1577 local trigger = dischargeNode.trigger
1578 if onEnter then
1579 if trigger.objects[object] == nil then
1580 trigger.objects[object] = {count=0, fillUnitIndex=fillUnitIndex, shape=otherShapeId}
1581 trigger.numObjects = trigger.numObjects + 1
1582
1583 object:addDeleteListener(self, "onDeleteDischargeTriggerObject")
1584 end
1585 trigger.objects[object].count = trigger.objects[object].count + 1
1586 self:raiseActive()
1587 elseif onLeave then
1588 trigger.objects[object].count = trigger.objects[object].count - 1
1589 if trigger.objects[object].count == 0 then
1590 trigger.objects[object] = nil
1591 trigger.numObjects = trigger.numObjects - 1
1592
1593 object:removeDeleteListener(self, "onDeleteDischargeTriggerObject")
1594 end
1595 end
1596 end
1597 end
1598 end
1599 end
1600end

finishDischargeRaycast

Description
Definition
finishDischargeRaycast()
Code
1358function Dischargeable:finishDischargeRaycast()
1359 local spec = self.spec_dischargeable
1360 local dischargeNode = spec.currentRaycastDischargeNode
1361 self:handleDischargeRaycast(dischargeNode, dischargeNode.dischargeObject, dischargeNode.dischargeShape, dischargeNode.dischargeDistance, dischargeNode.dischargeFillUnitIndex, dischargeNode.dischargeHitTerrain)
1362 spec.isAsyncRaycastActive = false
1363
1364 if dischargeNode.lastDischargeObject ~= dischargeNode.dischargeObject then
1365 SpecializationUtil.raiseEvent(self, "onDischargeTargetObjectChanged", dischargeNode.dischargeObject)
1366 end
1367end

getAllowLoadTriggerActivation

Description
Definition
getAllowLoadTriggerActivation()
Code
840function Dischargeable:getAllowLoadTriggerActivation(superFunc, rootVehicle)
841 if superFunc(self, rootVehicle) then
842 return true
843 end
844
845 local spec = self.spec_dischargeable
846 if spec.currentDischargeNode ~= nil then
847 local object = spec.currentDischargeNode.dischargeHitObject
848 if object ~= nil and object.getAllowLoadTriggerActivation ~= nil then
849 -- if we start to hit ourself again we break to avoid the endless looping
850 if object == rootVehicle then
851 return false
852 end
853
854 return object:getAllowLoadTriggerActivation(rootVehicle)
855 end
856 end
857
858 return false
859end

getCanBeSelected

Description
Definition
getCanBeSelected()
Code
822function Dischargeable:getCanBeSelected(superFunc)
823 return true
824end

getCanDischargeAtPosition

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

getCanDischargeToGround

Description
Definition
getCanDischargeToGround()
Code
1014function Dischargeable:getCanDischargeToGround(dischargeNode)
1015 if dischargeNode == nil then
1016 return false
1017 end
1018
1019 if not dischargeNode.dischargeHitTerrain then
1020 return false
1021 end
1022
1023 if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then
1024 local fillTypeIndex = self:getDischargeFillType(dischargeNode)
1025 if not DensityMapHeightUtil.getCanTipToGround(fillTypeIndex) then
1026 return false
1027 end
1028 end
1029
1030 if not self:getCanDischargeToLand(dischargeNode) then
1031 return false
1032 end
1033
1034 if not self:getCanDischargeAtPosition(dischargeNode) then
1035 return false
1036 end
1037
1038 return true
1039end

getCanDischargeToLand

Description
Definition
getCanDischargeToLand()
Code
1043function Dischargeable:getCanDischargeToLand(dischargeNode)
1044 if dischargeNode == nil then
1045 return false
1046 end
1047
1048 if dischargeNode.canDischargeToGroundAnywhere then
1049 return true
1050 end
1051
1052 local info = dischargeNode.info
1053 local sx,_,sz = localToWorld(info.node, -info.width, 0, info.zOffset)
1054 local ex,_,ez = localToWorld(info.node, info.width, 0, info.zOffset)
1055 local activeFarm = self:getActiveFarm()
1056
1057 -- is start owned
1058 if not g_currentMission.accessHandler:canFarmAccessLand(activeFarm, sx, sz) then
1059 return false
1060 end
1061
1062 -- is end owned
1063 if not g_currentMission.accessHandler:canFarmAccessLand(activeFarm, ex, ez) then
1064 return false
1065 end
1066
1067 return true
1068end

getCanDischargeToObject

Description
Definition
getCanDischargeToObject()
Code
1107function Dischargeable:getCanDischargeToObject(dischargeNode)
1108 if dischargeNode == nil then
1109 return false
1110 end
1111
1112 local object = dischargeNode.dischargeObject
1113 if object == nil then
1114 return false
1115 end
1116
1117 local fillType = self:getDischargeFillType(dischargeNode)
1118
1119 if not object:getFillUnitSupportsFillType(dischargeNode.dischargeFillUnitIndex, fillType) then
1120 return false
1121 end
1122
1123 local allowFillType = object:getFillUnitAllowsFillType(dischargeNode.dischargeFillUnitIndex, fillType)
1124 if not allowFillType then
1125 return false
1126 end
1127
1128 if object.getFillUnitFreeCapacity ~= nil and object:getFillUnitFreeCapacity(dischargeNode.dischargeFillUnitIndex, fillType, self:getActiveFarm()) <= 0 then
1129 return false
1130 end
1131
1132 if object.getIsFillAllowedFromFarm ~= nil and not object:getIsFillAllowedFromFarm(self:getActiveFarm()) then
1133 return false
1134 end
1135
1136 -- Adding should only be done if removing is allowed (generally adding is always allowed because it is beneficial to the receiver)
1137 -- 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)
1138 if self.getMountObject ~= nil then
1139 local mounter = self:getDynamicMountObject() or self:getMountObject()
1140 if mounter ~= nil then
1141 -- if the active farm of the mounter has NO access to farmId fill unit: disallow
1142 if not g_currentMission.accessHandler:canFarmAccess(mounter:getActiveFarm(), self, true) then
1143 return false
1144 end
1145 end
1146 end
1147
1148 return true
1149end

getCanToggleDischargeToGround

Description
Definition
getCanToggleDischargeToGround()
Code
1175function Dischargeable:getCanToggleDischargeToGround()
1176 local spec = self.spec_dischargeable
1177 local dischargeNode = spec.currentDischargeNode
1178
1179 return dischargeNode ~= nil and dischargeNode.canDischargeToGround and not dischargeNode.canStartGroundDischargeAutomatically
1180end

getCanToggleDischargeToObject

Description
Definition
getCanToggleDischargeToObject()
Code
1166function Dischargeable:getCanToggleDischargeToObject()
1167 local spec = self.spec_dischargeable
1168 local dischargeNode = spec.currentDischargeNode
1169
1170 return dischargeNode ~= nil and dischargeNode.canDischargeToObject
1171end

getCurrentDischargeNode

Description
Definition
getCurrentDischargeNode()
Code
785function Dischargeable:getCurrentDischargeNode()
786 local spec = self.spec_dischargeable
787 return spec.currentDischargeNode
788end

getCurrentDischargeNodeIndex

Description
Definition
getCurrentDischargeNodeIndex()
Code
792function Dischargeable:getCurrentDischargeNodeIndex()
793 local spec = self.spec_dischargeable
794 if spec.currentDischargeNode ~= nil then
795 return spec.currentDischargeNode.index
796 end
797
798 return 0
799end

getCurrentDischargeObject

Description
Object being discharged to
Definition
getCurrentDischargeObject()
Code
809function Dischargeable:getCurrentDischargeObject(dischargeNode)
810 return dischargeNode.currentDischargeObject
811end

getDischargeFillType

Description
Definition
getDischargeFillType()
Code
997function Dischargeable:getDischargeFillType(dischargeNode)
998 local fillType = self:getFillUnitFillType(dischargeNode.fillUnitIndex)
999 local conversionFactor = 1
1000
1001 if dischargeNode.fillTypeConverter ~= nil then
1002 local conversion = dischargeNode.fillTypeConverter[fillType]
1003 if conversion ~= nil then
1004 fillType = conversion.targetFillTypeIndex
1005 conversionFactor = conversion.conversionFactor
1006 end
1007 end
1008
1009 return fillType, conversionFactor
1010end

getDischargeNodeByIndex

Description
Definition
getDischargeNodeByIndex()
Code
1371function Dischargeable:getDischargeNodeByIndex(index)
1372 local spec = self.spec_dischargeable
1373 return spec.dischargeNodes[index]
1374end

getDischargeNodeByNode

Description
Definition
getDischargeNodeByNode()
Code
1196function Dischargeable:getDischargeNodeByNode(node)
1197 return self.spec_dischargeable.dischargNodeMapping[node]
1198end

getDischargeNodeEmptyFactor

Description
Definition
getDischargeNodeEmptyFactor()
Code
1190function Dischargeable:getDischargeNodeEmptyFactor(dischargeNode)
1191 return 1.0
1192end

getDischargeNotAllowedWarning

Description
Definition
getDischargeNotAllowedWarning()
Code
1153function Dischargeable:getDischargeNotAllowedWarning(dischargeNode)
1154 local text = g_i18n:getText(Dischargeable.DISCHARGE_WARNINGS[dischargeNode.dischargeFailedReason or 1] or "warning_notAcceptedHere")
1155 if dischargeNode.customNotAllowedWarning ~= nil then
1156 text = dischargeNode.customNotAllowedWarning
1157 end
1158
1159 local fillType = self:getDischargeFillType(dischargeNode)
1160 local fillTypeDesc = g_fillTypeManager:getFillTypeByIndex(fillType)
1161 return string.format(text, fillTypeDesc.title)
1162end

getDischargeState

Description
Definition
getDischargeState()
Code
991function Dischargeable:getDischargeState()
992 return self.spec_dischargeable.currentDischargeState
993end

getDischargeTargetObject

Description
Object found for possible discharging
Definition
getDischargeTargetObject()
Code
803function Dischargeable:getDischargeTargetObject(dischargeNode)
804 return dischargeNode.dischargeObject, dischargeNode.dischargeFillUnitIndex
805end

getDoConsumePtoPower

Description
Definition
getDoConsumePtoPower()
Code
828function Dischargeable:getDoConsumePtoPower(superFunc)
829 return self:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF or superFunc(self)
830end

getIsDischargeNodeActive

Description
Definition
getIsDischargeNodeActive()
Code
1184function Dischargeable:getIsDischargeNodeActive(dischargeNode)
1185 return true
1186end

getIsPowerTakeOffActive

Description
Definition
getIsPowerTakeOffActive()
Code
834function Dischargeable:getIsPowerTakeOffActive(superFunc)
835 return self:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF or superFunc(self)
836end

getRequiresTipOcclusionArea

Description
Definition
getRequiresTipOcclusionArea()
Code
815function Dischargeable:getRequiresTipOcclusionArea()
816 local spec = self.spec_dischargeable
817 return spec.requiresTipOcclusionArea
818end

handleDischarge

Description
Definition
handleDischarge()
Code
1392function Dischargeable:handleDischarge(dischargeNode, dischargedLiters, minDropReached, hasMinDropFillLevel)
1393 local spec = self.spec_dischargeable
1394
1395 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
1396 local canDrop = not minDropReached and hasMinDropFillLevel
1397
1398 if dischargeNode.stopDischargeIfNotPossible then
1399 if dischargedLiters == 0 and not canDrop then
1400 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1401 end
1402 end
1403 else
1404 if dischargeNode.stopDischargeIfNotPossible then
1405 if dischargedLiters == 0 then
1406 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1407 end
1408 end
1409 end
1410end

handleDischargeNodeChanged

Description
Definition
handleDischargeNodeChanged()
Code
1387function Dischargeable:handleDischargeNodeChanged()
1388end

handleDischargeOnEmpty

Description
Definition
handleDischargeOnEmpty()
Code
1378function Dischargeable:handleDischargeOnEmpty(dischargeNode)
1379 local spec = self.spec_dischargeable
1380 if spec.currentDischargeNode.stopDischargeOnEmpty then
1381 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
1382 end
1383end

handleDischargeRaycast

Description
Definition
handleDischargeRaycast()
Code
1414function Dischargeable:handleDischargeRaycast(dischargeNode, object, shape, distance, illUnitIndex, hitTerrain)
1415 local spec = self.spec_dischargeable
1416 -- stop tipping if discharge to object is active but object is not hit anymore
1417 if object == nil and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then
1418 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF)
1419 end
1420
1421 if object == nil and dischargeNode.canStartGroundDischargeAutomatically then
1422 if self:getCanDischargeToGround(dischargeNode) then
1423 self:setDischargeState(Dischargeable.DISCHARGE_STATE_GROUND)
1424 end
1425 end
1426
1427 local currentDischargeNode = spec.currentDischargeNode
1428 if currentDischargeNode.distanceObjectChanges ~= nil then
1429 ObjectChangeUtil.setObjectChanges(currentDischargeNode.distanceObjectChanges, distance > currentDischargeNode.distanceObjectChangeThreshold or spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF, self, self.setMovingToolDirty)
1430 end
1431end

handleFoundDischargeObject

Description
Definition
handleFoundDischargeObject()
Code
1435function Dischargeable:handleFoundDischargeObject(dischargeNode)
1436 if dischargeNode.canStartDischargeAutomatically then
1437 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT)
1438 end
1439end

initSpecialization

Description
Definition
initSpecialization()
Code
43function Dischargeable.initSpecialization()
44 g_configurationManager:addConfigurationType("dischargeable", g_i18n:getText("configuration_dischargeable"), "dischargeable", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION)
45
46 local schema = Vehicle.xmlSchema
47 schema:setXMLSpecializationType("Dischargeable")
48
49 Dischargeable.registerXMLPaths(schema, "vehicle.dischargeable")
50 Dischargeable.registerXMLPaths(schema, "vehicle.dischargeable.dischargeableConfigurations.dischargeableConfiguration(?)")
51
52 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.dischargeable.dischargeableConfigurations.dischargeableConfiguration(?)")
53
54 Dashboard.registerDashboardXMLPaths(schema, "vehicle.dischargeable.dashboards", "activeDischargeNode | dischargeState")
55 schema:register(XMLValueType.INT, "vehicle.dischargeable.dashboards.dashboard(?)#dischargeNodeIndex", "Index of discharge node")
56
57 schema:setXMLSpecializationType()
58end

loadDischargeNode

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

onDeactivate

Description
Definition
onDeactivate()
Code
1712function Dischargeable:onDeactivate()
1713 local spec = self.spec_dischargeable
1714 if spec.stopDischargeOnDeactivate then
1715 if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
1716 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
1717 end
1718 end
1719end

onDelete

Description
Definition
onDelete()
Code
302function Dischargeable:onDelete()
303 local spec = self.spec_dischargeable
304 if spec.dischargeNodes ~= nil then
305 for _, dischargeNode in ipairs(spec.dischargeNodes) do
306 g_effectManager:deleteEffects(dischargeNode.effects)
307 g_soundManager:deleteSample(dischargeNode.sample)
308 g_soundManager:deleteSample(dischargeNode.dischargeSample)
309 g_soundManager:deleteSamples(dischargeNode.dischargeStateSamples)
310 g_animationManager:deleteAnimations(dischargeNode.animationNodes)
311
312 if dischargeNode.trigger.node ~= nil then
313 removeTrigger(dischargeNode.trigger.node)
314 end
315
316 if dischargeNode.activationTrigger.node ~= nil then
317 removeTrigger(dischargeNode.activationTrigger.node)
318 end
319 end
320 end
321 spec.dischargeNodes = nil
322end

onDeleteActivationTriggerObject

Description
Definition
onDeleteActivationTriggerObject()
Code
1656function Dischargeable:onDeleteActivationTriggerObject(object)
1657 local spec = self.spec_dischargeable
1658 for _, dischargeNode in pairs(spec.activationTriggerToDischargeNode) do
1659 local trigger = dischargeNode.activationTrigger
1660
1661 if trigger.objects[object] ~= nil then
1662 trigger.objects[object] = nil
1663 trigger.numObjects = trigger.numObjects - 1
1664 end
1665 end
1666end

onDeleteDischargeTriggerObject

Description
Definition
onDeleteDischargeTriggerObject()
Code
1604function Dischargeable:onDeleteDischargeTriggerObject(object)
1605 local spec = self.spec_dischargeable
1606 for _, dischargeNode in pairs(spec.triggerToDischargeNode) do
1607 local trigger = dischargeNode.trigger
1608
1609 if trigger.objects[object] ~= nil then
1610 trigger.objects[object] = nil
1611 trigger.numObjects = trigger.numObjects - 1
1612 end
1613 end
1614end

onFillUnitFillLevelChanged

Description
Definition
onFillUnitFillLevelChanged()
Code
1698function Dischargeable:onFillUnitFillLevelChanged(fillUnitIndex, fillLevelDelta, fillType, toolType, fillPositionData, appliedDelta)
1699 local spec = self.spec_dischargeable
1700
1701 local dischargeNode = spec.fillUnitDischargeNodeMapping[fillUnitIndex]
1702 if dischargeNode ~= nil then
1703 local fillLevel = self:getFillUnitFillLevel(fillUnitIndex)
1704 if fillLevel == 0 then
1705 self:handleDischargeOnEmpty(dischargeNode)
1706 end
1707 end
1708end

onLoad

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

onReadStream

Description
Definition
onReadStream()
Code
326function Dischargeable:onReadStream(streamId, connection)
327 if connection:getIsServer() then
328 local spec = self.spec_dischargeable
329
330 for _, dischargeNode in ipairs(spec.dischargeNodes) do
331 if streamReadBool(streamId) then
332 local distance = streamReadUIntN(streamId, 8)*dischargeNode.maxDistance / 255
333 dischargeNode.dischargeDistance = distance
334 local fillTypeIndex = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS)
335 self:setDischargeEffectActive(dischargeNode, true, false, fillTypeIndex)
336 self:setDischargeEffectDistance(dischargeNode, distance)
337 else
338 self:setDischargeEffectActive(dischargeNode, false)
339 end
340 end
341
342 self:setDischargeState(streamReadUIntN(streamId, Dischargeable.SEND_NUM_BITS_DISCHARGE_STATE), true)
343 end
344end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
365function Dischargeable:onReadUpdateStream(streamId, timestamp, connection)
366 if connection:getIsServer() then
367 local spec = self.spec_dischargeable
368
369 if streamReadBool(streamId) then
370 for _, dischargeNode in ipairs(spec.dischargeNodes) do
371 if streamReadBool(streamId) then
372 local distance = streamReadUIntN(streamId, 8)*dischargeNode.maxDistance / 255
373 dischargeNode.dischargeDistance = distance
374 local fillTypeIndex = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS)
375 self:setDischargeEffectActive(dischargeNode, true, false, fillTypeIndex)
376 self:setDischargeEffectDistance(dischargeNode, distance)
377 else
378 self:setDischargeEffectActive(dischargeNode, false)
379 end
380 end
381 end
382 end
383end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
1676function Dischargeable:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
1677 if self.isClient then
1678 local spec = self.spec_dischargeable
1679 self:clearActionEventsTable(spec.actionEvents)
1680
1681 if isActiveForInputIgnoreSelection then
1682 if self:getCanToggleDischargeToGround() then
1683 local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.TOGGLE_TIPSTATE_GROUND, self, Dischargeable.actionEventToggleDischargeToGround, false, true, false, true, nil)
1684 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL)
1685 end
1686 if self:getCanToggleDischargeToObject() then
1687 local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, InputAction.TOGGLE_TIPSTATE, self, Dischargeable.actionEventToggleDischarging, false, true, false, true, nil)
1688 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)
1689 end
1690
1691 Dischargeable.updateActionEvents(self)
1692 end
1693 end
1694end

onStateChange

Description
Definition
onStateChange()
Code
1723function Dischargeable:onStateChange(state, data)
1724 if state == Vehicle.STATE_CHANGE_MOTOR_TURN_OFF then
1725 if not self:getIsPowered() then
1726 local spec = self.spec_dischargeable
1727 if spec.stopDischargeOnDeactivate then
1728 if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
1729 self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
1730 end
1731 end
1732 end
1733 end
1734end

onUpdate

Description
Definition
onUpdate()
Code
404function Dischargeable:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
405 local spec = self.spec_dischargeable
406
407 local dischargeNode = spec.currentDischargeNode
408 if dischargeNode ~= nil then
409 if dischargeNode.activationTrigger.numObjects > 0 or spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
410 self:raiseActive()
411 end
412 end
413end

onUpdateTick

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

onWriteStream

Description
Definition
onWriteStream()
Code
348function Dischargeable:onWriteStream(streamId, connection)
349 if not connection:getIsServer() then
350 local spec = self.spec_dischargeable
351
352 for _, dischargeNode in ipairs(spec.dischargeNodes) do
353 if streamWriteBool(streamId, dischargeNode.isEffectActiveSent) then
354 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(dischargeNode.dischargeDistanceSent/dischargeNode.maxDistance*255), 1, 255), 8)
355 streamWriteUIntN(streamId, self:getDischargeFillType(dischargeNode), FillTypeManager.SEND_NUM_BITS)
356 end
357 end
358
359 streamWriteUIntN(streamId, spec.currentDischargeState, Dischargeable.SEND_NUM_BITS_DISCHARGE_STATE)
360 end
361end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
387function Dischargeable:onWriteUpdateStream(streamId, connection, dirtyMask)
388 if not connection:getIsServer() then
389 local spec = self.spec_dischargeable
390
391 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
392 for _, dischargeNode in ipairs(spec.dischargeNodes) do
393 if streamWriteBool(streamId, dischargeNode.isEffectActiveSent) then
394 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(dischargeNode.dischargeDistanceSent/dischargeNode.maxDistance*255), 1, 255), 8)
395 streamWriteUIntN(streamId, self:getDischargeFillType(dischargeNode), FillTypeManager.SEND_NUM_BITS)
396 end
397 end
398 end
399 end
400end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
37function Dischargeable.prerequisitesPresent(specializations)
38 return SpecializationUtil.hasSpecialization(FillUnit, specializations) and SpecializationUtil.hasSpecialization(FillVolume, specializations)
39end

raycastCallbackDischargeNode

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

registerEventListeners

Description
Definition
registerEventListeners()
Code
182function Dischargeable.registerEventListeners(vehicleType)
183 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Dischargeable)
184 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Dischargeable)
185 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Dischargeable)
186 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Dischargeable)
187 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Dischargeable)
188 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Dischargeable)
189 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Dischargeable)
190 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Dischargeable)
191 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Dischargeable)
192 SpecializationUtil.registerEventListener(vehicleType, "onFillUnitFillLevelChanged", Dischargeable)
193 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Dischargeable)
194 SpecializationUtil.registerEventListener(vehicleType, "onStateChange", Dischargeable)
195end

registerEvents

Description
Definition
registerEvents()
Code
175function Dischargeable.registerEvents(vehicleType)
176 SpecializationUtil.registerEvent(vehicleType, "onDischargeStateChanged")
177 SpecializationUtil.registerEvent(vehicleType, "onDischargeTargetObjectChanged")
178end

registerFunctions

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

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
165function Dischargeable.registerOverwrittenFunctions(vehicleType)
166 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getRequiresTipOcclusionArea", Dischargeable.getRequiresTipOcclusionArea)
167 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", Dischargeable.getCanBeSelected)
168 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDoConsumePtoPower", Dischargeable.getDoConsumePtoPower)
169 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsPowerTakeOffActive", Dischargeable.getIsPowerTakeOffActive)
170 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAllowLoadTriggerActivation", Dischargeable.getAllowLoadTriggerActivation)
171end

registerXMLPaths

Description
Definition
registerXMLPaths()
Code
62function 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")
116end

setCurrentDischargeNodeIndex

Description
Definition
setCurrentDischargeNodeIndex()
Code
752function Dischargeable:setCurrentDischargeNodeIndex(dischargeNodeIndex)
753 local spec = self.spec_dischargeable
754
755 -- deactivate effects on old discharge node
756 if spec.currentDischargeNode ~= nil then
757 self:setDischargeEffectActive(spec.currentDischargeNode, false, true)
758 self:updateDischargeSound(spec.currentDischargeNode, 99999)
759
760 if spec.dischargeNodes[dischargeNodeIndex] ~= spec.currentDischargeNode then
761 g_animationManager:stopAnimations(spec.currentDischargeNode.animationNodes)
762 end
763
764 g_soundManager:stopSamples(spec.currentDischargeNode.dischargeStateSamples)
765 end
766
767 spec.currentDischargeNode = spec.dischargeNodes[dischargeNodeIndex]
768
769 for i=1, #spec.dischargeNodes do
770 local node = spec.dischargeNodes[i]
771 if node.nodeActiveObjectChanges ~= nil then
772 ObjectChangeUtil.setObjectChanges(node.nodeActiveObjectChanges, i == dischargeNodeIndex, self, self.setMovingToolDirty)
773 end
774 end
775
776 if self.setDashboardsDirty ~= nil then
777 self:setDashboardsDirty()
778 end
779
780 self:handleDischargeNodeChanged()
781end

setDischargeEffectActive

Description
Definition
setDischargeEffectActive()
Code
1457function Dischargeable:setDischargeEffectActive(dischargeNode, isActive, force, fillTypeIndex)
1458 if isActive then
1459 if not dischargeNode.isEffectActive then
1460 if fillTypeIndex == nil then
1461 fillTypeIndex = self:getDischargeFillType(dischargeNode)
1462 end
1463
1464 g_effectManager:setFillType(dischargeNode.effects, fillTypeIndex)
1465 g_effectManager:startEffects(dischargeNode.effects)
1466
1467 dischargeNode.isEffectActive = true
1468 end
1469
1470 dischargeNode.stopEffectTime = nil
1471 else
1472 if force == nil or not force then
1473 if dischargeNode.stopEffectTime == nil then
1474 dischargeNode.stopEffectTime = g_time + dischargeNode.effectTurnOffThreshold
1475 self:raiseActive()
1476 end
1477 else
1478 if dischargeNode.isEffectActive then
1479 g_effectManager:stopEffects(dischargeNode.effects)
1480 dischargeNode.isEffectActive = false
1481 end
1482 end
1483 end
1484end

setDischargeEffectDistance

Description
Definition
setDischargeEffectDistance()
Code
1443function Dischargeable:setDischargeEffectDistance(dischargeNode, distance)
1444 if dischargeNode.isEffectActive then
1445 if dischargeNode.effects ~= nil and distance ~= math.huge then
1446 for _, effect in pairs(dischargeNode.effects) do
1447 if effect.setDistance ~= nil then
1448 effect:setDistance(distance, g_currentMission.terrainRootNode)
1449 end
1450 end
1451 end
1452 end
1453end

setDischargeState

Description
Definition
setDischargeState()
Code
957function Dischargeable:setDischargeState(state, noEventSend)
958 local spec = self.spec_dischargeable
959 if state ~= spec.currentDischargeState then
960 SetDischargeStateEvent.sendEvent(self, state, noEventSend)
961
962 spec.currentDischargeState = state
963
964 local dischargeNode = spec.currentDischargeNode
965 if state == Dischargeable.DISCHARGE_STATE_OFF then
966 self:setDischargeEffectActive(dischargeNode, false)
967 dischargeNode.isEffectActiveSent = false
968
969 g_animationManager:stopAnimations(dischargeNode.animationNodes)
970 else
971 g_animationManager:startAnimations(dischargeNode.animationNodes)
972 end
973
974 for i=1, #spec.dischargeNodes do
975 local node = spec.dischargeNodes[i]
976 if node.stateObjectChanges ~= nil then
977 ObjectChangeUtil.setObjectChanges(node.stateObjectChanges, state ~= Dischargeable.DISCHARGE_STATE_OFF and node == dischargeNode, self, self.setMovingToolDirty)
978 end
979 end
980
981 if self.setDashboardsDirty ~= nil then
982 self:setDashboardsDirty()
983 end
984
985 SpecializationUtil.raiseEvent(self, "onDischargeStateChanged", state)
986 end
987end

setForcedFillTypeIndex

Description
Definition
setForcedFillTypeIndex()
Code
1670function Dischargeable:setForcedFillTypeIndex(fillTypeIndex)
1671 self.spec_dischargeable.forcedFillTypeIndex = fillTypeIndex
1672end

updateActionEvents

Description
Definition
updateActionEvents()
Code
1819function Dischargeable.updateActionEvents(self)
1820 local spec = self.spec_dischargeable
1821
1822 local actionEventTip = spec.actionEvents[InputAction.TOGGLE_TIPSTATE]
1823 local actionEventTipGround = spec.actionEvents[InputAction.TOGGLE_TIPSTATE_GROUND]
1824 local showTip = false
1825 local showTipGround = false
1826
1827 if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
1828 local currentDischargeNode = spec.currentDischargeNode
1829 if self:getIsDischargeNodeActive(currentDischargeNode) then
1830 if self:getCanDischargeToObject(currentDischargeNode) and self:getCanToggleDischargeToObject() then
1831 if actionEventTip ~= nil then
1832 g_inputBinding:setActionEventText(actionEventTip.actionEventId, g_i18n:getText("action_startOverloading"))
1833 showTip = true
1834 end
1835 elseif self:getCanDischargeToGround(currentDischargeNode) and self:getCanToggleDischargeToGround() then
1836 if actionEventTipGround ~= nil then
1837 g_inputBinding:setActionEventText(actionEventTipGround.actionEventId, g_i18n:getText("action_startTipToGround"))
1838 showTipGround = true
1839 end
1840 end
1841 end
1842 elseif spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
1843 if actionEventTipGround ~= nil then
1844 g_inputBinding:setActionEventText(actionEventTipGround.actionEventId, g_i18n:getText("action_stopTipToGround"))
1845 showTipGround = true
1846 end
1847 else
1848 if actionEventTip ~= nil then
1849 g_inputBinding:setActionEventText(actionEventTip.actionEventId, g_i18n:getText("action_stopOverloading"))
1850 showTip = true
1851 end
1852 end
1853
1854 if actionEventTip ~= nil then
1855 g_inputBinding:setActionEventTextVisibility(actionEventTip.actionEventId, showTip)
1856 end
1857 if actionEventTipGround ~= nil then
1858 g_inputBinding:setActionEventTextVisibility(actionEventTipGround.actionEventId, showTipGround)
1859 end
1860end

updateDebugValues

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

updateDischargeInfo

Description
Definition
updateDischargeInfo()
Code
1240function Dischargeable:updateDischargeInfo(dischargeNode, x, y, z)
1241 if dischargeNode.info.useRaycastHitPosition then
1242 setWorldTranslation(dischargeNode.info.node, x, y, z)
1243 end
1244end

updateDischargeSound

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

updateRaycast

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