Finding the closest unit

edited November 2015 in Questions

Hello

I want to pull my unit to the closest cog-unit and leash it to it, and ideally I would like to leash it to the closest cog unit if they move closer to another. Somehow my unit however gets transferred to the centre. Could it be the reason that there are two units on that spot?

This function creates the metal_rod

function Metal_Rod_creation (keys)
    local caster = keys.caster
    local ability = keys.ability
    local target_point = keys.target_points[1]
    local rodspacing = ability:GetSpecialValueFor("Rod_Spacing")
    local rodelements = ability:GetSpecialValueFor("Rod_Elements")
    local rodsize = ability:GetSpecialValueFor("Metal_Rod_Size")
    local direction_to_target_point = caster:GetForwardVector()
    local leash_duration = ability:GetLevelSpecialValueFor("leash_duration", ( ability:GetLevel() - 1 ))
    local duration =ability:GetDuration()

    local direction_to_target_point_normal = Vector(-direction_to_target_point.y, direction_to_target_point.x, direction_to_target_point.z)
    local vector_distance_from_center = (direction_to_target_point_normal * (rodelements * rodspacing))
    local one_end_point = target_point - vector_distance_from_center
    local other_end_point = target_point + vector_distance_from_center

    --Display the Ice Wall particles in a line.
    --local metal_rod_particle_effect = ParticleManager:CreateParticle("particles/borus/metal_rod_metalblock.vpcf", PATTACH_ABSORIGIN, keys.caster)
    --ParticleManager:SetParticleControl(metal_rod_particle_effect, 0, target_point - vector_distance_from_center)
    --ParticleManager:SetParticleControl(metal_rod_particle_effect, 1, target_point + vector_distance_from_center)

    --Timers:CreateTimer({
    --  endTime = duration,
    --  callback = function()
    --      ParticleManager:DestroyParticle(metal_rod_particle_effect, false)
    --  end
    --})

    for i=0, rodelements, 1 do
        local metal_rod_unit = CreateUnitByName("npc_magnet_unit", one_end_point + direction_to_target_point_normal * (rodspacing * i), false, nil, nil, caster:GetTeam())

        --We give the ice wall dummy unit its own instance of Ice Wall both to more easily make it apply the correct intensity of slow (based on Quas' level)
        --and because if Invoker uninvokes Ice Wall and the spell is removed from his toolbar, existing modifiers originating from that ability can start to error out.
        metal_rod_unit:AddAbility("Metal_Rod")
        local metal_rod_unit_ability = metal_rod_unit:FindAbilityByName("Metal_Rod")
        if metal_rod_unit_ability ~= nil then
            metal_rod_unit_ability:ApplyDataDrivenModifier(metal_rod_unit, metal_rod_unit, "metal_rod_modifier_dummy", {duration = -1})
        end
                metal_rod_unit.parent_caster = keys.caster  --Store the reference to the Invoker that spawned this Ice Wall unit.

        --Remove the Ice Wall auras after the duration ends, and the dummy units themselves after their aura slow modifiers will have all expired.
        Timers:CreateTimer({
            endTime = duration,
            callback = function()
                metal_rod_unit:RemoveModifierByName("metal_rod_modifier_dummy")
                metal_rod_unit:RemoveSelf()

            end
        })
    end

    for i=0, rodelements, 1 do
        local metal_rod_unit = CreateUnitByName("npc_magnet_unit", other_end_point - direction_to_target_point_normal * (rodspacing * i), false, nil, nil, caster:GetTeam())

        --We give the ice wall dummy unit its own instance of Ice Wall both to more easily make it apply the correct intensity of slow (based on Quas' level)
        --and because if Invoker uninvokes Ice Wall and the spell is removed from his toolbar, existing modifiers originating from that ability can start to error out.
        metal_rod_unit:AddAbility("Metal_Rod")
        local metal_rod_unit_ability = metal_rod_unit:FindAbilityByName("Metal_Rod")
        if metal_rod_unit_ability ~= nil then
            metal_rod_unit_ability:ApplyDataDrivenModifier(metal_rod_unit, metal_rod_unit, "metal_rod_modifier_dummy", {duration = -1})
        end
                metal_rod_unit.parent_caster = keys.caster  --Store the reference to the Invoker that spawned this Ice Wall unit.

        --Remove the Ice Wall auras after the duration ends, and the dummy units themselves after their aura slow modifiers will have all expired.
        Timers:CreateTimer({
            endTime = duration,
            callback = function()
                metal_rod_unit:RemoveModifierByName("metal_rod_modifier_dummy")
                metal_rod_unit:RemoveSelf()     
            end
        })
    end
end

This function pulls the units towards the cog units and applies the modifier to check for the leash distance.

function metal_rod( keys )

    local target = keys.target
    local caster = caster_global
    local ability = keys.ability
    local pull_speed = ability:GetSpecialValueFor("pull_speed") * 0.01 -- Interval = 0.01
    local metal_rod = ability:GetSpecialValueFor("metal_rod")
    local metal_rod_leash_duration = ability:GetLevelSpecialValueFor("metal_rod_leash_duration", ( ability:GetLevel() - 1 ))
    local metal_rod_aoe = ability:GetSpecialValueFor("magnetic_aoe")
    local silence_duration = ability:GetLevelSpecialValueFor("Silence_duration", ( ability:GetLevel() - 1 ))
    local disarm_duration = ability:GetLevelSpecialValueFor("Disarm_duration", ( ability:GetLevel() - 1 ))
    local targets = FindUnitsInRadius(DOTA_TEAM_BADGUYS + DOTA_TEAM_NEUTRALS + DOTA_TEAM_GOODGUYS, target:GetAbsOrigin(), nil, metal_rod_aoe, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_ALL, DOTA_UNIT_TARGET_FLAG_NONE, FIND_ANY_ORDER, false)
    for _,unit in pairs(targets) do
        if unit:GetUnitName() == target:GetUnitName() then
        else
            if target:GetTeamNumber() == caster:GetTeamNumber() then
            else
                if unit:GetUnitName() == "npc_magnet_unit" then
                    local targets = FindUnitsInRadius(DOTA_TEAM_BADGUYS + DOTA_TEAM_NEUTRALS + DOTA_TEAM_GOODGUYS, target:GetAbsOrigin(), nil, metal_rod_aoe, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_ALL, DOTA_UNIT_TARGET_FLAG_NONE, FIND_CLOSEST, false)
                    for _,unit in pairs(targets) do
                        local unitlocation = unit:GetAbsOrigin()
                        local targetlocation = target:GetAbsOrigin()
                        local vector_distance = targetlocation - unitlocation
                        local distance = (vector_distance):Length2D()
                        local direction = (vector_distance):Normalized()
                        if target:HasAbility("Positive_Charge_Magnetic") then
                            ability:ApplyDataDrivenModifier(caster, target, "rod_disarm", {duration = silence_duration})
                        elseif target:HasAbility("Negative_Charge_Magnetic") then
                            ability:ApplyDataDrivenModifier(caster, target, "rod_silence", {duration = disarm_duration})
                        end
                        if distance < 50 then
                            target:RemoveModifierByName("Negative_Charge_Magnetic")
                            target:RemoveModifierByName("Positive_Charge_Magnetic")
                            ability:ApplyDataDrivenModifier(target, target, "metal_rod_leash", {duration = metal_rod_leash_duration})
                            ability:ApplyDataDrivenModifier(target, target, "no_collision", {duration = 2})
                            FindClearSpaceForUnit(target, target:GetAbsOrigin(), false)
                        end
                        if distance < metal_rod_aoe then
                            target:SetAbsOrigin(targetlocation - direction * pull_speed)
                        end
                    end
                end
            end
        end
    end
end

This checks the leash distance and keeps units inside

function metal_rod_leash( keys )

    local target = keys.target
    local caster = caster_global
    local ability = caster:FindAbilityByName("Positive_Charge")
    local leash_range = ability:GetSpecialValueFor("leash_range")
    local metal_rod_leash_duration = ability:GetLevelSpecialValueFor("metal_rod_leash_duration",  ( ability:GetLevel() - 1 ))
    local targets = FindUnitsInRadius(DOTA_TEAM_BADGUYS + DOTA_TEAM_NEUTRALS + DOTA_TEAM_GOODGUYS, target:GetAbsOrigin(), nil, 600, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_ALL, DOTA_UNIT_TARGET_FLAG_NONE, FIND_ANY_ORDER, false)
    for _,unit in pairs(targets) do
        if unit:GetUnitName() == "npc_magnet_unit" then
            local targets = FindUnitsInRadius(DOTA_TEAM_BADGUYS + DOTA_TEAM_NEUTRALS + DOTA_TEAM_GOODGUYS, target:GetAbsOrigin(), nil, 600, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_ALL, DOTA_UNIT_TARGET_FLAG_NONE, FIND_ANY_ORDER, false)
            for _,unit in pairs(targets) do
                local targetloc = target:GetAbsOrigin()
                local unitloc = unit:GetAbsOrigin()
                if oldlocation == nil then
                    oldlocation = target:GetAbsOrigin()
                end
                if centre == nil then
                    centre = target:GetAbsOrigin()
                end
                if leash_range < (target:GetAbsOrigin() - centre):Length2D() then
                    if leash_range > 1500 then
                    else
                        target:SetAbsOrigin(oldlocation)
                    end
                end
                oldlocation = target:GetAbsOrigin()
            break           
            end
        end
    end
end

Resets the values.

function metal_rod_leash_reset_values (keys)
    oldlocation = nil
    centre = nil
end

This is what happens