--[[
	Author: 	derelky
	Date:		06.03.2019
	Version:	1.0
	
	History:
				v1.0 @ 06.03.2019 - initial implementation in FS 19
]]

beetMill = {}

function beetMill.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(FillUnit, specializations) and SpecializationUtil.hasSpecialization(Dischargeable, specializations) and SpecializationUtil.hasSpecialization(beetMill, specializations)
end

function beetMill.registerFunctions(vehicleType)
end

function beetMill.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "dischargeToObject", beetMill.dischargeToObject)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "dischargeToGround", beetMill.dischargeToGround)
	
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanDischargeToObject", beetMill.getCanDischargeToObject)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanDischargeToGround", beetMill.getCanDischargeToGround)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanDischargeAtPosition", beetMill.getCanDischargeAtPosition)
    SpecializationUtil.registerOverwrittenFunction(vehicleType, "updateNearestObjectInTriggers",      beetMill.updateNearestObjectInTriggers)
    SpecializationUtil.registerOverwrittenFunction(vehicleType, "onDischargeStateChanged",        beetMill.onDischargeStateChanged)
    SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischargeRaycast",        beetMill.handleDischargeRaycast)
    SpecializationUtil.registerOverwrittenFunction(vehicleType, "raycastCallbackDischargeNode",        beetMill.raycastCallbackDischargeNode)
    SpecializationUtil.registerOverwrittenFunction(vehicleType, "setDischargeEffectActive",        beetMill.setDischargeEffectActive)
	
	

end

function beetMill.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", beetMill)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", beetMill)
   -- SpecializationUtil.registerEventListener(vehicleType, "onDischargeStateChanged", beetMill)
end




function beetMill:onLoad(savegame)
		self.spec_beetMill = {}
    local spec = self.spec_beetMill
	--print("spec: "..tostring(spec))
   	if self.isClient then
        spec.beetMillconvert = {}
        spec.beetMillconvertbonus = {}
		spec.beetMillconvertbonus.use = false
        local i=0
		while true do
            local key = string.format("vehicle.beetMill.beetMillconvert(%d)", i)
            if not hasXMLProperty(self.xmlFile, key) then
                break
            end
			
			
			local inputstring = getXMLString(self.xmlFile, key.."#input")
			local input= g_fillTypeManager:getFillTypeIndexByName(inputstring)
			local outputstring =	getXMLString(self.xmlFile, key.."#output")
			local outputfilltype=  g_fillTypeManager:getFillTypeIndexByName(outputstring)
			--print("Input: "..tostring(input))
			--print("output: "..tostring(outputfilltype))
			
			local ratefactor= Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#rate"), 1) 
			
			
			if spec.beetMillconvert[input] == nil then
				spec.beetMillconvert[input] = {}
			end
			spec.beetMillconvert[input].output= outputfilltype
			spec.beetMillconvert[input].rate= ratefactor
			
			for fillUnitIndex, _ in ipairs(self.spec_fillUnit.fillUnits) do
				local currentFillType = self:getFillUnitSupportedFillTypes(fillUnitIndex)
				for Filltypes, Filltypes2nd in pairs(currentFillType) do
					fillType=Filltypes
				--print("Filltypes: "..tostring(Filltypes))
				--print("Filltypes2nd: "..tostring(Filltypes2nd))
                        if fillType ~= nil then
							if spec.beetMillconvert[fillType] == nil then
								spec.beetMillconvert[fillType] = {}
							end
							if spec.beetMillconvert[fillType].output== nil then
								spec.beetMillconvert[fillType].output = fillType
								spec.beetMillconvert[fillType].rate=1
							end
							
							if spec.beetMillconvert[fillType].rate== nil then
								spec.beetMillconvert[fillType].rate=1
							end
						end
				--print("Register Output for: "..tostring(fillType).."  and output is: "..tostring(spec.beetMillconvert[fillType].output))
				end
			end			
            i=i+1
		end	
		
        local i=0
		while true do
            local key = string.format("vehicle.beetMill.beetMillbonus(%d)", i)
            if not hasXMLProperty(self.xmlFile, key) then
                break
            end
			
			local bonusfuindex = getXMLInt(self.xmlFile, key.."#bonusfillunitIndex")
			local ratefactorbonus= Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#bonusrate"), 1) 
			local usage= Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#usage"), 1) 
			local inputstringb = getXMLString(self.xmlFile, key.."#fillType")
			local bonusfilltype= g_fillTypeManager:getFillTypeIndexByName(inputstringb)
			
				if bonusfuindex == nil then
					spec.beetMillconvertbonus.use = false
				else
					spec.beetMillconvertbonus.use = true
					spec.beetMillconvertbonus.fillUnitIndex=bonusfuindex
					spec.beetMillconvertbonus.bonusrate=ratefactorbonus
					spec.beetMillconvertbonus.usage=usage/1000
					spec.beetMillconvertbonus.fillType=bonusfilltype
				end
            i=i+1
		
		end	
		
		
		
   end
   
    spec.dirtyFlag = self:getNextDirtyFlag()

end



function beetMill:dischargeToGround(superFunc,dischargeNode, emptyLiters)
    local spec = self.spec_beetMill
	local endrate = 1
    local fillType = self:getDischargeFillType(dischargeNode)
    local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex)
    local fillLevelBonus = Utils.getNoNil(self:getFillUnitFillLevel(spec.beetMillconvertbonus.fillUnitIndex),0)
    local minLiterToDrop = g_densityMapHeightManager:getMinValidLiterValue(spec.beetMillconvert[fillType].output)
	local Bonusused = false 
	
	if fillLevelBonus > 0 and spec.beetMillconvertbonus.use then	
		Bonusused =true 
		endrate = spec.beetMillconvert[fillType].rate * spec.beetMillconvertbonus.bonusrate
	else
		endrate = spec.beetMillconvert[fillType].rate
	end
	
    local origlitersToDrop = math.min(dischargeNode.litersToDrop + (emptyLiters), math.max(dischargeNode.emptySpeed*250, minLiterToDrop))
    dischargeNode.litersToDrop = origlitersToDrop*endrate
    local minDropReached = dischargeNode.litersToDrop > minLiterToDrop
    local hasMinDropFillLevel = fillLevel > minLiterToDrop
    local info = dischargeNode.info
    local dischargedLiters = 0
    local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset)
    local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset)
    sy = sy + info.yOffset
    ey = ey + info.yOffset
    if info.limitToGround then
        sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy)
        ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey)
    end
    local droppedorrig, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(self, dischargeNode.litersToDrop, spec.beetMillconvert[fillType].output, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true)
	dropped= droppedorrig/endrate
    dischargeNode.lineOffset = lineOffset
    dischargeNode.litersToDrop = dischargeNode.litersToDrop - dropped
    if dropped > 0 then
			if Bonusused then
				dischargedLitersBonus = self:addFillUnitFillLevel(self:getOwnerFarmId(), spec.beetMillconvertbonus.fillUnitIndex, -(dropped*spec.beetMillconvertbonus.usage), spec.beetMillconvertbonus.fillType, ToolType.UNDEFINED)
			end
		
        local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex)
        dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -(dropped), fillType, ToolType.UNDEFINED, unloadInfo)
    end
    fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex)
    if fillLevel > 0 and fillLevel <= minLiterToDrop then
        dischargeNode.litersToDrop = minLiterToDrop
    end
    return dischargedLiters, minDropReached, hasMinDropFillLevel
end

function beetMill:dischargeToObject(superFunc,dischargeNode, emptyLiters, object, targetFillUnitIndex)
    local spec = self.spec_beetMill
	local endrate = 1
    local fillType = self:getDischargeFillType(dischargeNode)
    local supportsFillType = object:getFillUnitSupportsFillType(targetFillUnitIndex, spec.beetMillconvert[fillType].output)
    local fillLevelBonus = Utils.getNoNil(self:getFillUnitFillLevel(spec.beetMillconvertbonus.fillUnitIndex),0)
    local dischargedLiters = 0
    if supportsFillType then
        local allowFillType = object:getFillUnitAllowsFillType(targetFillUnitIndex, spec.beetMillconvert[fillType].output)
        if allowFillType then
				--print("fillLevelBonus: "..tostring(fillLevelBonus).." Use Bonus: "..tostring(spec.beetMillconvertbonus.use).." EmptyLiters: "..tostring(emptyLiters))
				if fillLevelBonus > 0 and spec.beetMillconvertbonus.use then
					dischargedLitersBonus = self:addFillUnitFillLevel(self:getOwnerFarmId(), spec.beetMillconvertbonus.fillUnitIndex, -(emptyLiters*spec.beetMillconvertbonus.usage), spec.beetMillconvertbonus.fillType, ToolType.UNDEFINED)
					endrate = spec.beetMillconvert[fillType].rate * spec.beetMillconvertbonus.bonusrate
				else
					endrate = spec.beetMillconvert[fillType].rate
				end
            dischargeNode.currentDischargeObject = object
            local delta = object:addFillUnitFillLevel(self:getActiveFarm(), targetFillUnitIndex, (emptyLiters*endrate), spec.beetMillconvert[fillType].output, ToolType.DISCHARGEABLE, dischargeNode.info)
            local unloadInfo = self:getFillVolumeUnloadInfo(dischargeNode.unloadInfoIndex)
            dischargedLiters = self:addFillUnitFillLevel(self:getOwnerFarmId(), dischargeNode.fillUnitIndex, -emptyLiters, fillType, ToolType.UNDEFINED, unloadInfo)
        end
    end
    return dischargedLiters
end

function beetMill:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
    local spec = self.spec_dischargeable
    local specm = self.spec_beetMill
    local dischargeNode = spec.currentDischargeNode
    if dischargeNode ~= nil then
        if self.isClient then
            Dischargeable.updateActionEvents(self)
        end
        if self:getIsDischargeNodeActive(dischargeNode) then
            local trigger = dischargeNode.trigger
            if trigger.numObjects > 0 then
                dischargeNode.dischargeObject = nil
                dischargeNode.dischargeHitTerrain = false
                dischargeNode.dischargeShape = nil
                dischargeNode.dischargeDistance = 0
                dischargeNode.dischargeFillUnitIndex = nil
                dischargeNode.dischargeHit = false -- any (also unsupported) object hit
                local nearestDistance = math.huge
                for object, data in pairs(trigger.objects) do
                    local fillType = spec.forcedFillTypeIndex
                    if fillType == nil then
                        fillType = self:getDischargeFillType(dischargeNode)
                    end
					--print("beetMill Script Line 175 fillType is : "..tostring(fillType).." forced is: "..tostring(spec.forcedFillTypeIndex))
                    dischargeNode.dischargeFailedReason = nil
                    dischargeNode.customNotAllowedWarning = nil
                    if object:getFillUnitSupportsFillType(data.fillUnitIndex, specm.beetMillconvert[fillType].output) then
                        local allowFillType = object:getFillUnitAllowsFillType(data.fillUnitIndex, specm.beetMillconvert[fillType].output)
                        local allowToolType = object:getFillUnitSupportsToolType(data.fillUnitIndex, ToolType.TRIGGER)
                        local freeSpace = object:getFillUnitFreeCapacity(data.fillUnitIndex, specm.beetMillconvert[fillType].output, self:getActiveFarm()) > 0
						--print("beetMill Script Line 181 allowFillType is : "..tostring(allowFillType).." allowToolType is : "..tostring(allowToolType).." freeSpace is : "..tostring(freeSpace))
                        if allowFillType and allowToolType and freeSpace then
                            local distance = calcDistanceFrom(dischargeNode.node, object:getFillUnitExactFillRootNode(data.fillUnitIndex))
                            if distance < nearestDistance then
                                dischargeNode.dischargeObject = object
                                dischargeNode.dischargeHitTerrain = false
                                dischargeNode.dischargeShape = data.shape
                                dischargeNode.dischargeDistance = distance
                                dischargeNode.dischargeFillUnitIndex = data.fillUnitIndex
                                nearestDistance = distance
                            end
                        elseif not allowFillType then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED
                        elseif not allowToolType then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED
                        elseif not freeSpace then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY
                        end
                    else
                        dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED
                    end
                    if dischargeNode.dischargeFailedReason ~= nil then
                        if object.getCustomDischargeNotAllowedWarning ~= nil then
                            dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning()
                        end
                    end
                    dischargeNode.dischargeHit = true -- any (also unsupported) object has been hit
                end
            else
                if not spec.isAsyncRaycastActive then
                    self:updateRaycast(dischargeNode)
                end
            end
        else
            if spec.currentDischargeState ~= Dischargeable.DISCHARGE_STATE_OFF then
                self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
            end
        end
        self:updateDischargeSound(dischargeNode, dt)
        if self.isServer then
            if VehicleDebug.state == VehicleDebug.DEBUG then
                local info = dischargeNode.info
                local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset)
                local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset)
                drawDebugLine(sx, sy+info.yOffset, sz, 1, 0, 0, ex, ey+info.yOffset, ez, 1, 0, 0)
            end
            if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
                if dischargeNode.dischargeObject ~= nil then
                    self:handleFoundDischargeObject(dischargeNode)
                end
            else
                local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex)
                local emptySpeed = self:getDischargeNodeEmptyFactor(dischargeNode)
                -- Only allow discharge into a node, or if the land is owned
                local canDischargeToObject = self:getCanDischargeToObject(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT
                local canDischargeToGround = self:getCanDischargeToGround(dischargeNode) and spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND
                local canDischarge = canDischargeToObject or canDischargeToGround
                local allowedToDischarge = dischargeNode.dischargeObject ~= nil or (self:getCanDischargeToLand(dischargeNode) and self:getCanDischargeAtPosition(dischargeNode))
                local isReadyToStartDischarge = fillLevel > 0.0001 and emptySpeed > 0 and allowedToDischarge and canDischarge
                self:setDischargeEffectActive(dischargeNode, isReadyToStartDischarge)
                self:setDischargeEffectDistance(dischargeNode, dischargeNode.dischargeDistance)
                local isReadyForDischarge = dischargeNode.lastEffect == nil or dischargeNode.lastEffect:getIsFullyVisible()
                if isReadyForDischarge and allowedToDischarge and canDischarge then
                    local emptyLiters = math.min(fillLevel, dischargeNode.emptySpeed * emptySpeed * dt)
                    local dischargedLiters, minDropReached, hasMinDropFillLevel = self:discharge(dischargeNode, emptyLiters)
                    spec.dischargedLiters = dischargedLiters
                    self:handleDischarge(dischargeNode, dischargedLiters, minDropReached, hasMinDropFillLevel)
                end
            end
            if dischargeNode.isEffectActive ~= dischargeNode.isEffectActiveSent or  math.abs(dischargeNode.dischargeDistanceSent - dischargeNode.dischargeDistance) > 0.05 then
                self:raiseDirtyFlags(spec.dirtyFlag)
                dischargeNode.dischargeDistanceSent = dischargeNode.dischargeDistance
                dischargeNode.isEffectActiveSent = dischargeNode.isEffectActive
            end
        end
    end
    if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF then
        local currentDischargeNode = spec.currentDischargeNode
        if self:getIsActiveForInput() and self:getCanDischargeToObject(currentDischargeNode) and self:getCanToggleDischargeToObject() then
            g_currentMission:showTipContext(self:getFillUnitFillType(dischargeNode.fillUnitIndex))
        end
    end
    for _, dischargeNode in ipairs(spec.dischargeNodes) do
        if dischargeNode.stopEffectTime ~= nil and dischargeNode.stopEffectTime < g_time then
            self:setDischargeEffectActive(dischargeNode, false, true)
            dischargeNode.stopEffectTime = nil
        end
    end
end

function beetMill:getCanDischargeAtPosition(superFunc,dischargeNode)
    local specm = self.spec_beetMill
    if dischargeNode == nil then
        return false
    end
    if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then
        local info = dischargeNode.info
        local sx,sy,sz = localToWorld(info.node, -info.width, 0, info.zOffset)
        local ex,ey,ez = localToWorld(info.node, info.width, 0, info.zOffset)
        -- check if at the ground position is still space for some more
        -- check this only if we wan't to discharge to the ground (farmland check is also done while discharging to objects)
        local spec = self.spec_dischargeable
        if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OFF or spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_GROUND then
            sy = sy + info.yOffset
            ey = ey + info.yOffset
            if info.limitToGround then
                sy = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz)+0.1, sy)
                ey = math.max(getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0, ez)+0.1, ey)
            end
            local fillType = self:getDischargeFillType(dischargeNode)
            local testDrop = g_densityMapHeightManager:getMinValidLiterValue(specm.beetMillconvert[fillType].output)
            if not DensityMapHeightUtil.getCanTipToGroundAroundLine(self, testDrop, specm.beetMillconvert[fillType].output, sx,sy,sz, ex,ey,ez, info.length, nil, dischargeNode.lineOffset, true, nil, true) then
                return false
            end
        end
    end
    return true
end

function beetMill:getCanDischargeToGround(superFunc,dischargeNode)
    local spec = self.spec_beetMill
    if dischargeNode == nil then
        return false
    end
    if not dischargeNode.dischargeHitTerrain then
        return false
    end
    if self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) > 0 then
        local fillTypeIndex = self:getDischargeFillType(dischargeNode)
        if not DensityMapHeightUtil.getCanTipToGround(spec.beetMillconvert[fillTypeIndex].output) then
            return false
        end
    end
    if not self:getCanDischargeToLand(dischargeNode) then
        return false
    end
    if not self:getCanDischargeAtPosition(dischargeNode) then
        return false
    end
    return true
end

function beetMill:getCanDischargeToObject(superFunc,dischargeNode)
    local spec = self.spec_beetMill
    if dischargeNode == nil then
	--print("beetMill Script Line 325 is false")
        return false
    end
    local object = dischargeNode.dischargeObject
    if object == nil then
	--print("beetMill Script Line 331 is false: "..tostring(object))
        return false
    end	
	--print("self:getDischargeFillType(dischargeNode): "..tostring(g_fillTypeManager:getFillTypeNameByIndex(self:getDischargeFillType(dischargeNode))).." And real Filltype is: "..tostring(fillType))

	local fillType = self:getDischargeFillType(dischargeNode)
	if g_fillTypeManager:getFillTypeNameByIndex(fillType)=="UNKNOWN" then
		--print("Nil Check is True")
		fillType = self:getDischargeFillType(dischargeNode)
	else
		fillType = spec.beetMillconvert[fillType].output
		----print("Nil Check is False and fillType is: "..tostring(fillType).." Formel Ergebniss: "..tostring(spec.beetMillconvert[fillType].output))
	end
	--print("self:getDischargeFillType(dischargeNode): "..tostring(g_fillTypeManager:getFillTypeNameByIndex(self:getDischargeFillType(dischargeNode))).." And real Filltype is: "..tostring(fillType))
	
		if not object:getFillUnitSupportsFillType(dischargeNode.dischargeFillUnitIndex, fillType) then
		--print("beetMill Script Line 335 is false")
			return false
		end
		local allowFillType = object:getFillUnitAllowsFillType(dischargeNode.dischargeFillUnitIndex, fillType)
		if not allowFillType then
		--print("beetMill Script Line 340 is false")
			return false
		end
		if object.getFillUnitFreeCapacity ~= nil and object:getFillUnitFreeCapacity(dischargeNode.dischargeFillUnitIndex, fillType, self:getActiveFarm()) <= 0 then
		--print("beetMill Script Line 344 is false")
			return false
		end
    if object.getIsFillAllowedFromFarm ~= nil and not object:getIsFillAllowedFromFarm(self:getActiveFarm()) then
	--print("beetMill Script Line 348 is false")
        return false
    end
    -- Adding should only be done if removing is allowed (generally adding is always allowed because it is beneficial to the receiver)
    -- 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)
    if self.getMountObject ~= nil then
        local mounter = self:getDynamicMountObject() or self:getMountObject()
        if mounter ~= nil then
            -- if the active farm of the mounter has NO access to farmId fill unit: disallow
            if not g_currentMission.accessHandler:canFarmAccess(mounter:getActiveFarm(), self, true) then
	----print("beetMill Script Line 358 is false")
                return false
            end
        end
    end
	----print("beetMill Script Line 363 is true")
    return true
end


function beetMill:updateNearestObjectInTriggers(superFunc)
    local specm = self.spec_beetMill
    local spec = self.spec_pipe
    spec.nearestObjectInTriggers.objectId = nil
    spec.nearestObjectInTriggers.fillUnitIndex = 0
    local minDistance = math.huge
    local dischargeNode = self:getDischargeNodeByIndex(self:getPipeDischargeNodeIndex())
    if dischargeNode ~= nil then
        local checkNode = Utils.getNoNil(dischargeNode.node, self.components[1].node)
        for object, _ in pairs(spec.objectsInTriggers) do
			local outputFillType = self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex)
			if specm.beetMillconvert[self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex)] == nil then
			else
				outputFillType = specm.beetMillconvert[self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex)].output
			end
            for fillUnitIndex, _ in ipairs(object.spec_fillUnit.fillUnits) do
                local allowedToFillByPipe = object:getFillUnitSupportsToolType(fillUnitIndex, ToolType.DISCHARGEABLE)
                local supportsFillType = object:getFillUnitSupportsFillType(fillUnitIndex, outputFillType) or outputFillType == FillType.UNKNOWN
                local fillLevel = object:getFillUnitFreeCapacity(fillUnitIndex, outputFillType, self:getOwnerFarmId())
                if allowedToFillByPipe and supportsFillType and fillLevel > 0 then
                    local targetPoint = object:getFillUnitAutoAimTargetNode(fillUnitIndex)
                    local exactFillRootNode = object:getFillUnitExactFillRootNode(fillUnitIndex)
                    if targetPoint == nil then
                        targetPoint = exactFillRootNode
                    end
                    if targetPoint ~= nil then
                        local distance = calcDistanceFrom(checkNode, targetPoint)
                        if distance < minDistance then
                            minDistance = distance
                            spec.nearestObjectInTriggers.objectId = NetworkUtil.getObjectId(object)
                            spec.nearestObjectInTriggers.fillUnitIndex = fillUnitIndex
                            break
                        end
                    end
                end
            end
        end
    else
        g_logManager:xmlWarning(self.configFileName, "Unable to find discharge node index '%d' for Pipe", self:getPipeDischargeNodeIndex())
    end
end

function beetMill:handleDischargeRaycast(superFunc, dischargeNode, hitObject, hitShape, hitDistance, hitFillUnitIndex, hitTerrain)
    local specm = self.spec_beetMill
    local stopDischarge = false
    if hitObject ~= nil then
        local fillType = specm.beetMillconvert[self:getDischargeFillType(dischargeNode)].output
        local allowFillType = hitObject:getFillUnitAllowsFillType(hitFillUnitIndex, fillType)
        if allowFillType and hitObject:getFillUnitFreeCapacity(hitFillUnitIndex, fillType, self:getOwnerFarmId()) > 0 then
            self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT, true)
        else
            stopDischarge = true
        end
    else
        stopDischarge = true
    end
    if stopDischarge and self:getDischargeState() == Dischargeable.DISCHARGE_STATE_OBJECT then
        self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true)
    end
end

function beetMill:onDischargeStateChanged(superFunc,state)
    local specm = self.spec_beetMill
    if self.isClient then
        local spec = self.spec_pipe
        local dischargeNode = self:getCurrentDischargeNode()
        local dischargeNodeIndex = nil
        if dischargeNode ~= nil then
            dischargeNodeIndex = dischargeNode.index
        end
        if dischargeNodeIndex == spec.dischargeNodeIndex then
            if state == Dischargeable.DISCHARGE_STATE_OFF then
                g_animationManager:stopAnimations(spec.animationNodes)
            else
                g_animationManager:startAnimations(spec.animationNodes)
                g_animationManager:setFillType(spec.animationNodes, specm.beetMillconvert[self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex)].output)
            end
        end
    end
end

function beetMill:setDischargeEffectActive(superFunc,dischargeNode, isActive, force)
    if isActive then
		local specm = self.spec_beetMill
        if not dischargeNode.isEffectActive then
            g_effectManager:setFillType(dischargeNode.effects, specm.beetMillconvert[self:getFillUnitLastValidFillType(dischargeNode.fillUnitIndex)].output)
            g_effectManager:startEffects(dischargeNode.effects)
            dischargeNode.isEffectActive = true
        end
        dischargeNode.stopEffectTime = nil
    else
        if force == nil or not force then
            if dischargeNode.stopEffectTime == nil then
                dischargeNode.stopEffectTime = g_time + 500
            end
        else
            if dischargeNode.isEffectActive then
                g_effectManager:stopEffects(dischargeNode.effects)
                dischargeNode.isEffectActive = false
            end
        end
    end
end

function beetMill:raycastCallbackDischargeNode(superFunc,hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId)
    if hitActorId ~= nil then
		local specm = self.spec_beetMill
        local spec = self.spec_dischargeable
        local dischargeNode = spec.currentRaycastDischargeNode
        local object = g_currentMission:getNodeObject(hitActorId)
        distance = distance - dischargeNode.raycast.yOffset
        if VehicleDebug.state == VehicleDebug.DEBUG then
            DebugUtil.drawDebugGizmoAtWorldPos(x,y,z, 0, 0, 1, 0, 1, 0, nil)
        end
        local validObject = object ~= nil and object ~= self
        -- if we hit a object because of the yOffset it has to be a exact fill root node, otherwise we ignore it
        -- is used to get exactFillRootNodes if the dischargeNode of the shovel is already below it
        if validObject and distance < 0 then
            if object.getFillUnitIndexFromNode ~= nil then
                validObject = validObject and object:getFillUnitIndexFromNode(hitShapeId) ~= nil
            end
        end
        if validObject then
            if object.getFillUnitIndexFromNode ~= nil then
                local fillUnitIndex = object:getFillUnitIndexFromNode(hitShapeId)
                if fillUnitIndex ~= nil then
                    local fillType = spec.forcedFillTypeIndex
                    if fillType == nil then
						if g_fillTypeManager:getFillTypeNameByIndex(self:getDischargeFillType(dischargeNode))=="UNKNOWN" then
							fillType = self:getDischargeFillType(dischargeNode)
						else
							fillType = specm.beetMillconvert[self:getDischargeFillType(dischargeNode)].output
						end
                    end
                    dischargeNode.dischargeFailedReason = nil
                    dischargeNode.customNotAllowedWarning = nil
                    if object:getFillUnitSupportsFillType(fillUnitIndex, fillType) then
                        local allowFillType = object:getFillUnitAllowsFillType(fillUnitIndex, fillType)
                        local allowToolType = object:getFillUnitSupportsToolType(fillUnitIndex, ToolType.DISCHARGEABLE)
                        local freeSpace = object:getFillUnitFreeCapacity(fillUnitIndex, fillType, self:getActiveFarm()) > 0
                        if allowFillType and allowToolType and freeSpace then
                            dischargeNode.dischargeObject = object
                            dischargeNode.dischargeShape = hitShapeId
                            dischargeNode.dischargeDistance = distance
                            dischargeNode.dischargeFillUnitIndex = fillUnitIndex
                            if object.getFillUnitExtraDistanceFromNode ~= nil then
                                dischargeNode.dischargeExtraDistance = object:getFillUnitExtraDistanceFromNode(hitShapeId)
                            end
                        elseif not allowFillType then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED
                        elseif not allowToolType then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_TOOLTYPE_NOT_SUPPORTED
                        elseif not freeSpace then
                            dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_NO_FREE_CAPACITY
                        end
                    else
                        dischargeNode.dischargeFailedReason = Dischargeable.DISCHARGE_REASON_FILLTYPE_NOT_SUPPORTED
                    end
                    if dischargeNode.dischargeFailedReason ~= nil then
                        if object.getCustomDischargeNotAllowedWarning ~= nil then
                            dischargeNode.customNotAllowedWarning = object:getCustomDischargeNotAllowedWarning()
                        end
                    end
                    dischargeNode.dischargeHit = true -- any, even unsupported, object has been hit.
                else
                    -- raycast until we hit the object underneath the exact fill root node
                    dischargeNode.dischargeDistance = distance + (dischargeNode.dischargeExtraDistance or 0)
                    dischargeNode.dischargeExtraDistance = nil
                    self:updateDischargeInfo(dischargeNode, x, y, z)
                    return false
                end
            end
        elseif hitActorId == g_currentMission.terrainRootNode then
            dischargeNode.dischargeDistance = math.min(dischargeNode.dischargeDistance, distance)
            dischargeNode.dischargeHitTerrain = true
            self:updateDischargeInfo(dischargeNode, x, y, z)
            return false
        end
        return true
    else
        self:finishDischargeRaycast()
    end
end