• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Get Shortest Path Unit 1.0

This bundle is marked as pending. It has not been reviewed by a staff member yet.
What does it do?
This library takes advantage of some of Wc3's own pathfinding from the ability Call to Arms to detect the shortest path unit in a group from xy co-ordinates which has two limitations:
1. Can only detect the shortest path unit for ground units with less than 32 collision size
2. Has limited "range" to find each shortest path unit
NOTE: Requires xy co-ordinates to have terrain walkability

How does it works?
It's based around the instant ability Call to Arms. When a peasant issues Call to Arms, it will try to find the shortest path to a Town Hall.

How to use and recommendations
GetShortestPathUnit takes group g, real x, real y returns unit

Example usage:
  • Custom script: set udg_ShortestPathUnit = GetShortestPathUnit(udg_YourGroup, GetUnitX(udg_YourUnit), GetUnitY(udg_YourUnit))
  • Custom script: set udg_ShortestPathUnit = GetShortestPathUnit(udg_YourGroup, GetLocationX(udg_YourPoint), GetLocationY(udg_YourPoint))
  • Custom script: set udg_ShortestPathUnit = GetShortestPathUnit(udg_YourGroup, udg_X, udg_Y)
The function can be resource intensive so use it with care.
1. Pick units for your group in a certain range around the main location
2. Filter your group properly and dont forget to filter out units on unpathable terrain
3. Dont run the function too many times in the same frame
Finally, a higher Reciever amount results in better performance for larger groups and longer distances

JASS
JASS:
library GetShortestPathUnit requires optional IsPointReachable//Duckfarter

/*    Configuration:
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯    */
    globals
        private constant integer TRANSMITTER = 'nipt'
        private constant integer RECIEVER = 'nipr'
        private constant integer C1 = 'Aipt'
        private constant integer C2 = 'Aipr'

    /*    Reciever amount. 4 low, 8 medium, 12 high, 16 very high    */
        private constant integer RA = 4

    /*    Closest distance. I dont recommend changing this    */
        private constant real DC = 16
    endglobals
/*
                GetShortestPathUnit 1.0
                ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    Description:
    ¯¯¯¯¯¯¯¯¯¯¯¯
        This library takes advantage of some of Wc3's own pathfinding from the ability Call to Arms
        to detect the shortest path unit in a group from xy co-ordinates which has two limitations:

        1. Can only detect the shortest path unit for ground units with less than 32 collision size
        2. Has limited "range" to find each shortest path unit

        NOTE: Requires xy co-ordinates to have terrain walkability

    API:
    ¯¯¯¯
        | function GetShortestPathUnit takes group g, real x, real y returns unit

    How to import:
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        1. Copy abilities Call to Arms (Transmitter) ID: Aipt, and Call To Arms (Reciever) ID: Aipr
        2. Copy units Transmitter (1) ID: nipt, and Reciever (2) ID: nipr
        3. Copy trigger GetClosestPathUnit

    Link:
    ¯¯¯¯¯

*/
    globals
        private unit uT
        private unit array uR
        private unit array uF
        private unit uC
        private unit uSP
        private integer i
        private integer j
        private integer gSize
        private real xF
        private real yF
        private real d
        private real dC
        private boolean array disabled
        private group swap = CreateGroup()
    endglobals

    private function FilterShortestPathUnit takes real x, real y returns unit

        set uC = null
    
        set i = 1
        loop
            exitwhen uF[i] == null
            set xF = GetUnitX(uF[i])
            set yF = GetUnitY(uF[i])
            //Stops false negatives with ~ equal XY
            set d = SquareRoot((xF-x)*(xF-x) + (yF-y)*(yF-y))
            if d <= dC then
                set dC = d
                set uC = uF[i]
            elseif dC == DC then
                call SetUnitX(uR[i], xF)
                call SetUnitY(uR[i], yF)
            endif
            set i = i + 1
        endloop
        set i = i - 1

        //If a shortest path unit exists within 16 range it skips the Call to Arms check
        if dC < DC then
            if uC != null then
                set gSize = gSize + 1
            endif
            return uC
        endif

        //Disables Reciever abilities when gSize < DA
        set j = RA
        loop
            exitwhen i >= j
            if disabled[j] == false then
                set disabled[j] = true
                call BlzUnitDisableAbility(uR[j], C2, true, false)
                set uF[j] = null
            endif
            set j = j - 1
        endloop

        //Initiate Call to Arms check
        call IssueImmediateOrderById(uT, 852072)
        //Initial check to see if there is a shortest path unit in the batch
        if GetUnitCurrentOrder(uT) == 0 then
            return null
        endif
        //Unhides and hides Recievers to interrupt the Call to Arms order
        set j = 1
        loop
            exitwhen j > i
            call ShowUnit(uR[j], true)
            call ShowUnit(uR[j], false)
            if GetUnitCurrentOrder(uT) == 0 then
                set gSize = gSize + 1
                return uF[j]
            endif
            set j = j + 1
        endloop

        return null

    endfunction

    function GetShortestPathUnit takes group g, real x, real y returns unit

        //Aborts if gSize is 0
        set gSize = BlzGroupAddGroupFast(g, swap)
        if gSize == 0 then
            return null
        endif

        set dC = DC
        set uSP = null
        call SetUnitX(uT, x)
        call SetUnitY(uT, y)

        //Enables abilities
        call BlzUnitDisableAbility(uT, C1, false, false)
        call BlzUnitDisableAbility(uT, 'Amov', false, false)
        set i = 1
        loop
            exitwhen i > RA or i > gSize
            set disabled[i] = false
            call BlzUnitDisableAbility(uR[i], C2, false, false)
            set i = i + 1
        endloop

        //Loops through g in batches of DA to find the shortest path unit
        set i = 1
        loop
            exitwhen gSize == 0
            set uF[i] = FirstOfGroup(swap)
            call GroupRemoveUnit(swap, uF[i])
            set gSize = gSize - 1
            if i == RA or gSize == 0 then
                set uSP = FilterShortestPathUnit(x, y)
                call GroupAddUnit(swap, uSP)
                exitwhen gSize == 1 and uSP != null
                set i = 0
            endif
            set i = i + 1
        endloop
        call GroupRemoveUnit(swap, uSP)
        set uC = null

        //Disables abilities
        call BlzUnitDisableAbility(uT, C1, true, false)
        call BlzUnitDisableAbility(uT, 'Amov', true, false)
        set i = 1
        loop
            exitwhen i > RA
            if disabled[i] == false then
                set disabled[i] = true
                call BlzUnitDisableAbility(uR[i], C2, true, false)
                set uF[i] = null
            endif
            set i = i + 1
        endloop

        return uSP

    endfunction

    //For future use
    function GetRecieverAmount takes nothing returns integer
        return RA
    endfunction

    function GetRecieverUnitByIndex takes integer i returns unit
        return uR[i]
    endfunction

    private module Init
        private static method onInit takes nothing returns nothing
            call init()
        endmethod
    endmodule

    private struct InitStruct extends array
        private static method init takes nothing returns nothing
            static if LIBRARY_IsPointReachable then
                set uT = GetTransmitterUnit()
                set uR[1] = GetRecieverUnit()
                set disabled[1] = true
                set i = 2
            else
                set uT = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), TRANSMITTER, 0, 0, 0)
                call BlzUnitDisableAbility(uT, C1, true, false)
                call BlzUnitDisableAbility(uT, 'Amov', true, false)
                call ShowUnit(uT, false)
                set i = 1
            endif
            loop
                exitwhen i > RA
                set uR[i] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), RECIEVER, 0, 0, 0)
                set disabled[i] = true
                call BlzUnitDisableAbility(uR[i], C2, true, false)
                call ShowUnit(uR[i], false)
                set i = i + 1
            endloop
        endmethod
        implement Init
    endstruct

endlibrary
11-05-2025
1.0 initial release
Previews
Contents

Get Shortest Path Unit 1.0 (Map)

Top