(Keeps Hive Alive)
Go Back   The Hive Workshop - A Warcraft III Modding Site > Warcraft III Resources > JASS Functions

JASS Functions Approved JASS functions will be located here.
Remember to submit your own resources to the submission forum.

Reply
 
LinkBack Thread Tools Display Modes
Old 02-22-2008, 10:33 PM   #1 (permalink)

User
 
Join Date: Mar 2005
Posts: 230

DiscipleOfLife is on a distinguished road (71)DiscipleOfLife is on a distinguished road (71)



GetClosestUnit(s) v1.3.1
finds the unit, or group of units closest to a point faster than any existing alternative

Changelog

v1.3.1
  • removed unnecessary GroupClear calls
v1.3
  • GetClosestUnit function is now smart
  • GetClosestUnitAlt: a new function that resembles the old and simple GetClosestUnit function (since it can be faster than the smart version in some rare cases)
  • GetClosestUnit and GetClosestUnitInRange now use ForGroup() instead of And() making them a lot faster
v1.2b
  • apparently boolexprs created with And() leak every time they are created, these leaks have now been removed from GCU
v1.2
  • added a description and comments
  • replaced bj_globals with own private globals (again from HINDYhat's request)
v1.1
  • fixed a few bugs in GetClosestUnit(s)InGroup and also replaced a few global groups with local ones (thx HINDYhat)
  • changed library name from GetClosestUnit to GCU


System Code

//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------

// GCU // GetClosestUnit(s) v1.3.1

//------------------------------------------------------------------------------
//
// This library contains the following functions, each of which return the
// unit that is closest to given coordinates and passes a specified filter:
//
// - GetClosestUnit(x, y, filter)
// - GetClosestUnitAlt(x, y, filter)
// - GetClosestUnitInRange(x, y, radius, filter)
// - GetClosestUnitInGroup(x, y, g)
//
// Included are also the following functions, each of which return a
// group consisting of the units closest to given coordinates that
// pass a specified filter:
//
// - GetClosestUnits(x, y, n, filter)
// - GetClosestUnitsInRange(x, y, radius, n, filter)
// - GetClosestUnitsInGroup(x, y, g, n)
//
// Important:
//
// - flying heights and the height of the ground aren't taken into account,
// only the distance in 2D
//
// - these functions can't be used in the boolexpr's passed to them
//
// - all of the functions in this library go through many units to find the
// closest one, meaning that the functions' performance is highly dependant
// on the amount of units they have to go through
//
//------------------------------------------------------------------------------
library GCU

    globals
        private unit CurrentPick
        private real CenterX
        private real CenterY
        private real CurrentDistance
        private group AnyGroup = CreateGroup()
    endglobals

    private function Enum takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real dx = GetUnitX(u) - CenterX
        local real dy = GetUnitY(u) - CenterY
local real d = (dx*dx + dy*dy) / 10000.
        if d < CurrentDistance then
            set CurrentDistance = d
            set CurrentPick = u
        endif
        set u = null
    endfunction

    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units on the map that
    // pass the filter and do not have the locust ability.
    //
    function GetClosestUnit takes real x, real y, boolexpr filter returns unit
        local real r = 800.
        loop
            call GroupEnumUnitsInRange(AnyGroup, x, y, r, filter)
            exitwhen FirstOfGroup(AnyGroup) != null
            if r >= 3200. then
                call GroupEnumUnitsInRect(AnyGroup, bj_mapInitialPlayableArea, filter)
                exitwhen true
            endif
            set r = 2.00 * r
        endloop
        set CurrentPick = null
        set CenterX = x
set CenterY = y
        set CurrentDistance = 100000
        call ForGroup(AnyGroup, function Enum)
        return CurrentPick
    endfunction

    //==========================================================================
    // Does the same as above. Faster when there are no units that pass the
    // filter in a 3200 radius, but at other times slower, and most likely a
    // lot slower. How much faster is somewhat directly proportional to the
    // amount of units that do not pass the filter inside that 3200 radius.
    //
    function GetClosestUnitAlt takes real x, real y, boolexpr filter returns unit
        set CurrentPick = null
        set CenterX = x
set CenterY = y
        set CurrentDistance = 100000
        call GroupEnumUnitsInRect(AnyGroup, bj_mapInitialPlayableArea, filter)
        call ForGroup(AnyGroup, function Enum)
        return CurrentPick
    endfunction


    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units in the specified
    // radius, that pass the filter and do not have the locust ability.
    //
    function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
        set CurrentPick = null
set CenterX = x
        set CenterY = y
        set CurrentDistance = 100000
        call GroupEnumUnitsInRange(AnyGroup, x, y, radius, filter)
        call ForGroup(AnyGroup, function Enum)
        return CurrentPick
    endfunction

    //==========================================================================
    // Finds the unit that is closest to (x, y) from whichGroup. Unlike the
    // other versions this one considers locusted units also.
    //
    function GetClosestUnitInGroup takes real x, real y, group whichGroup returns unit
        set CurrentPick = null
        set CenterX = x
        set CenterY = y
        set CurrentDistance = 100000
        call ForGroup(whichGroup, function Enum)
        return CurrentPick
endfunction

    //==========================================================================
    // The following three functions do the same as the preceding ones, with //
    // the exception that they return groups consisting of the n closest //
    // units instead of the closest unit //
    //==========================================================================

    globals
        private group ResultGroup
    endglobals

    function GetClosestUnits takes real x, real y, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRect(AnyGroup, bj_mapInitialPlayableArea, filter)
        set ResultGroup = CreateGroup()
        set CenterX = x
        set CenterY = y
        loop
exitwhen n == 0
            set CurrentPick = null
            set CurrentDistance = 100000
            call ForGroup(AnyGroup, function Enum)
            exitwhen CurrentPick == null
            call GroupRemoveUnit(AnyGroup, CurrentPick)
            call GroupAddUnit(ResultGroup, CurrentPick)
            set n = n - 1
        endloop
        return ResultGroup
    endfunction

    function GetClosestUnitsInRange takes real x, real y, real radius, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRange(AnyGroup, x, y, radius, filter)
        set ResultGroup = CreateGroup()
        set CenterX = x
        set CenterY = y
        loop
            exitwhen n == 0
            set CurrentPick = null
            set CurrentDistance = 100000
            call ForGroup(AnyGroup, function Enum)
exitwhen CurrentPick == null
            call GroupRemoveUnit(AnyGroup, CurrentPick)
            call GroupAddUnit(ResultGroup, CurrentPick)
            set n = n - 1
        endloop
        return ResultGroup
    endfunction

    private function AnyGroupAddGroupEnum takes nothing returns nothing
        call GroupAddUnit(AnyGroup, GetEnumUnit())
    endfunction

    function GetClosestUnitsInGroup takes real x, real y, group whichGroup, integer n returns group
        call GroupClear(AnyGroup)
        call ForGroup(whichGroup, function AnyGroupAddGroupEnum)
        set ResultGroup = CreateGroup()
        set CenterX = x
        set CenterY = y
        loop
            exitwhen n == 0
            set CurrentPick = null
            set CurrentDistance = 100000
            call ForGroup(AnyGroup, function Enum)
            exitwhen CurrentPick == null
            call GroupRemoveUnit(AnyGroup, CurrentPick)
            call GroupAddUnit(ResultGroup, CurrentPick)
            set n = n - 1
        endloop
        return ResultGroup
    endfunction

endlibrary // End of GetClosestUnit
//==============================================================================a

The code above requires JassHelper to work, whereas the code below doesn't


vJassless System Code

//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------

// GCU // GetClosestUnit(s) v1.3.1 without vJass

//------------------------------------------------------------------------------
//
// Contents:
//
// This library contains the following functions, each of which return the
// unit that is closest to given coordinates and passes a specified filter:
//
// - GetClosestUnit(x, y, filter)
// - GetClosestUnitAlt(x, y, filter)
// - GetClosestUnitInRange(x, y, radius, filter)
// - GetClosestUnitInGroup(x, y, g)
//
// Included are also the following functions, each of which return a
// group consisting of the units closest to given coordinates that
// pass a specified filter:
//
// - GetClosestUnits(x, y, n, filter)
// - GetClosestUnitsInRange(x, y, radius, n, filter)
// - GetClosestUnitsInGroup(x, y, g, n)
//
// Notes:
//
// - flying heights and the height of the ground aren't taken into
// account, only the distance in 2D
//
// - these functions can't be used in the boolexpr's passed to them
//
// - all of the functions in this library go through many units to find
// the closest one, meaning that the functions' performance is highly
// dependant on the amount of units they have to go through
//
// - this without vJass version is as efficient as the vJass version
//
// Implementing Instructions:
//
// - either paste this system into your map's header or inside any
// trigger that has been converted to custom text, that has been
// created before any other triggers in which you wish to use these
// functions!
//
// - press Ctrl+B to open the variable editor and create the following
// variables leaving their initial values to the default ones
//
// Type Name
// Unit GCU_CurrentPick
// Real GCU_CurrentX
// Real GCU_CurrentY
// Real GCU_CurrentDistance
// Unit Group GCU_AnyGroup
// Unit Group GCU_ResultingGroup
//
// - congrats, you are done!
//
//------------------------------------------------------------------------------

    function GCU_Enum takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real dx = GetUnitX(u) - udg_GCU_CurrentX
        local real dy = GetUnitY(u) - udg_GCU_CurrentY
local real d = (dx*dx + dy*dy) / 10000.
        if d < udg_GCU_CurrentDistance then
            set udg_GCU_CurrentDistance = d
            set udg_GCU_CurrentPick = u
        endif
        set u = null
    endfunction

    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units on the map that
    // pass the filter and do not have the locust ability.
    //
    function GetClosestUnit takes real x, real y, boolexpr filter returns unit
        local real r = 800.
        loop
            call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, r, filter)
            exitwhen FirstOfGroup(udg_GCU_AnyGroup) != null
            if r >= 3200. then
                call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
                exitwhen true
            endif
            set r = 2.00 * r
        endloop
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction

    //==========================================================================
    // Does the same as above. Faster when there are no units that pass the
    // filter in a 3200 radius, but at other times slower, and most likely a
    // lot slower. How much faster is somewhat directly proportional to the
    // amount of units that do not pass the filter inside that 3200 radius.
    //
    function GetClosestUnitAlt takes real x, real y, boolexpr filter returns unit
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction


    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units in the specified
    // radius, that pass the filter and do not have the locust ability.
    //
    function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
        set udg_GCU_CurrentPick = null
set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, radius, filter)
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction

    //==========================================================================
    // Finds the unit that is closest to (x, y) from whichGroup. Unlike the
    // other versions this one considers locusted units also.
    //
    function GetClosestUnitInGroup takes real x, real y, group whichGroup returns unit
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call ForGroup(whichGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction

    //==========================================================================
    // The following three functions do the same as the preceding ones, with //
    // the exception that they return groups consisting of the n closest //
    // units instead of the closest unit //
    //==========================================================================

    function GetClosestUnits takes real x, real y, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
            exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction

    function GetClosestUnitsInRange takes real x, real y, real radius, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, radius, filter)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction

    function GCU_AnyGroupAddGroupEnum takes nothing returns nothing
        call GroupAddUnit(udg_GCU_AnyGroup, GetEnumUnit())
    endfunction

    function GetClosestUnitsInGroup takes real x, real y, group whichGroup, integer n returns group
        call GroupClear(udg_GCU_AnyGroup)
        call ForGroup(whichGroup, function GCU_AnyGroupAddGroupEnum)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
            exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
            exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction

           // End of GetClosestUnit
//==============================================================================


Last edited by DiscipleOfLife; 06-28-2008 at 11:34 AM..
DiscipleOfLife is offline   Reply With Quote
Old 02-29-2008, 02:36 PM   #2 (permalink)
 
JoHnyW's Avatar

User
 
Join Date: Oct 2007
Posts: 130

JoHnyW has little to show at this moment (26)JoHnyW has little to show at this moment (26)JoHnyW has little to show at this moment (26)


sorry for such stupid question delete this post if i shouldn ask but i want to know what are closest units ?
__________________
Wanna Play my Footy Frenzy?
Its 4MB because of models retc.
Still working at better balancing and fixing bugs

Working on totally new ORPG

JoHnyW is offline   Reply With Quote
Old 02-29-2008, 07:13 PM   #3 (permalink)

iRawr
 
Join Date: Dec 2005
Posts: 8,349

PurplePoot is a splendid one to behold (807)PurplePoot is a splendid one to behold (807)PurplePoot is a splendid one to behold (807)

Paired Mapping Contest #4 Winner: Fallen Angel - Lucifer's Keep Respected User: This user has been given the respected user award. Map Development Mini-Contest #1 Winner: Stand of the Elements 

Basically, if you give them a radius and coordinates, it finds the unit closest to the coordinates matching certain conditions (including being in that radius), although some assume radius is infinite.

I'll give these the standard checkup once I return from vacation, Monday night or Tuesday afternoon.
PurplePoot is offline   Reply With Quote
Old 03-02-2008, 11:20 AM   #4 (permalink)
 
HINDYhat's Avatar

WRONG HOLE!
 
Join Date: Apr 2007
Posts: 1,022

HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)

Super Donor: This user has donated at least $100 to The Hive. 

Found a bug. In the GetClosestUnitInGroup function:
call ForGroup( bj_groupAddGroupDest, function Enum2)
Should be:
call ForGroup( whichGroup, function Enum2)

Other than that, why are you using global groups everywhere?
__________________
HINDYhat is offline   Reply With Quote
Old 03-02-2008, 03:09 PM   #5 (permalink)

User
 
Join Date: Mar 2005
Posts: 230

DiscipleOfLife is on a distinguished road (71)DiscipleOfLife is on a distinguished road (71)


Quote:
Originally Posted by HINDYhat View Post
Found a bug. In the GetClosestUnitInGroup function:
call ForGroup( bj_groupAddGroupDest, function Enum2)
Should be:
call ForGroup( whichGroup, function Enum2)

Other than that, why are you using global groups everywhere?
Damn I feel stupid right now. Fixed that bug now and removed 2 unneccessary globals uses. Thx & +rep.
Right now the bj_groupRemoveGroupDest global is used because it is returned in the functions it is in. The G global is just an empty group that is never filled with anything since the filter I use with it returns always false. (Using a null group instead doesn't work.)
In GetClosestUnitsInGroup I use bj_groupAddGroupDest because the GroupAddGroupEnum function fills it automaticly.

Now I have actually tested all of them -.-... I thought I had but seems like I skipped the Group versions :(. Pls forgive me.
__________________
DiscipleOfLife is offline   Reply With Quote
Old 03-03-2008, 01:52 AM   #6 (permalink)
 
HINDYhat's Avatar

WRONG HOLE!
 
Join Date: Apr 2007
Posts: 1,022

HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)

Super Donor: This user has donated at least $100 to The Hive. 

I understand the use of bj_groupAddGroupDest, but I don't understand the use of bj_groupRemoveGroupDest... You should use a local to avoid double frees or just randomness with globals.

Maybe you should add some documentation (mostly considering locusted units and enums) or something about each function : some people don't understand very easily. Extra documentation is always nice.

If you fix those two issues, I think this is good for approval. Might ask another mod though first.
__________________
HINDYhat is offline   Reply With Quote
Old 03-03-2008, 01:50 PM   #7 (permalink)

User
 
Join Date: Mar 2005
Posts: 230

DiscipleOfLife is on a distinguished road (71)DiscipleOfLife is on a distinguished road (71)


Quote:
Originally Posted by HINDYhat View Post
Maybe you should add some documentation (mostly considering locusted units and enums) or something about each function : some people don't understand very easily. Extra documentation is always nice.
Updated to 1.2 which now includes documentation.

Quote:
Originally Posted by HINDYhat View Post
I understand the use of bj_groupAddGroupDest, but I don't understand the use of bj_groupRemoveGroupDest... You should use a local to avoid double frees or just randomness with globals.
bj_groupRemoveGroupDest, which I know replaced with a private global 'resultingGroup' in v1.2, is a global because if it was local I would have to null it, but I cant null and return it at the same time.
Example failure:
function A takes nothing returns group
    local group g = CreateGroup()
    // ...actions...
    set g = null
    return g // == return null
endfunction


The new version uses 'G' instead of bj_groupAddGroupDest, so there shouldn't be any possible conflicts regarding bj_globals anymore.
__________________
DiscipleOfLife is offline   Reply With Quote
Old 03-03-2008, 03:37 PM   #8 (permalink)
 
HINDYhat's Avatar

WRONG HOLE!
 
Join Date: Apr 2007
Posts: 1,022

HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)

Super Donor: This user has donated at least $100 to The Hive. 

Why would you have to null it...? If the user is calling that function, then the local is being passed to another use, so the nulling falls in the hands of the user (like it always should). Really, use of a global group here is completely unnecessary, but I'll try to get a second opinion to strengthen my cause.
__________________
HINDYhat is offline   Reply With Quote
Old 03-03-2008, 04:37 PM   #9 (permalink)

User
 
Join Date: Mar 2005
Posts: 230

DiscipleOfLife is on a distinguished road (71)DiscipleOfLife is on a distinguished road (71)


Quote:
Originally Posted by HINDYhat View Post
Why would you have to null it...?
Because local handle variables that are destroyed but not nulled leak.
Source: Local Var Leaks
__________________
DiscipleOfLife is offline   Reply With Quote
Old 03-03-2008, 06:23 PM   #10 (permalink)
 
HINDYhat's Avatar

WRONG HOLE!
 
Join Date: Apr 2007
Posts: 1,022

HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)HINDYhat is a glorious beacon of light (587)

Super Donor: This user has donated at least $100 to The Hive. 

Ahhh, I asked Earth-Fury for help and he said that you were right. It seems that you DO have to null local handles, even if they are the returned value. Sounds great.

~Approved~
__________________
HINDYhat is offline   Reply With Quote
Old 03-10-2008, 10:14 PM   #11 (permalink)
 
Silvenon's Avatar

BBoy Silv
 
Join Date: Nov 2006
Posts: 866

Silvenon is on a distinguished road (81)Silvenon is on a distinguished road (81)