Warcraft III resources & community, 2003–2006 · archived

Waypoint System

not rated
Submitted by KaTTaNaAI0 downloads
Lets you setup waypoints around the map, and let units follow them.
This can be useful in an AoS-style map with computer controlled heroes, so they can find their way around.
Use NewWaypoint to create waypoints, then ConnectWaypoints to connect them using the integers returned by NewWaypoint.
Use UnitGotoWaypoint to make a unit find the quickest way to that waypoint while following the waypointed path.

Source

//************************************************************
//
// For more JASS scripts, visit us at http://www.wc3JASS.com
//
//============================================================

// *********************************
//        Handle Converters
// *********************************

function HandleToInt takes handle h returns integer
    return h
    return 0
endfunction
function IntToHandle takes integer i returns handle
    return i
    return null
endfunction

// *********************************
//        Local Vars Utility
// *********************************

function LocalVars takes nothing returns gamecache
    return InitGameCache("jasslocalvars.w3v")
endfunction

function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(HandleToInt(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(HandleToInt(subject)), name, HandleToInt(value))
    endif
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(HandleToInt(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(HandleToInt(subject)), name, value)
    endif
endfunction

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(HandleToInt(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(HandleToInt(subject)), name, value)
    endif
endfunction

function SetHandleString takes handle subject, string name, string value returns nothing
    if value == "" or value == null then
        call FlushStoredString(LocalVars(), I2S(HandleToInt(subject)), name)
    else
        call StoreString(LocalVars(), I2S(HandleToInt(subject)), name, value)
    endif
endfunction

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(HandleToInt(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(HandleToInt(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(HandleToInt(subject)), name)
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(HandleToInt(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(HandleToInt(subject)), name)
    return null
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(HandleToInt(subject)), name)
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(HandleToInt(subject)) )
endfunction




// *********************************
//           List Utility
// *********************************



// The cache storing the lists
function ListCache takes nothing returns gamecache
    return InitGameCache("listcache")
endfunction

// Returns a pointer to a new list. Old pointers are not recycled!
function ListNew takes nothing returns integer
    local gamecache gc = ListCache()
    local integer n = GetStoredInteger(gc, "lists", "count")
    call StoreInteger(gc, "lists", "count", n+1)
    return n
endfunction

// Returns the length of the list
function ListLength takes integer list returns integer
    return GetStoredInteger(ListCache(), "list"+I2S(list), "length")
endfunction

// Returns the value of an item
function ListGet takes integer list, integer index returns integer
    return GetStoredInteger(ListCache(), "list"+I2S(list), "item"+I2S(index))
endfunction

// Changes the value of a list item, and increases the length of the list if necessary
function ListSetValue takes integer list, integer index, integer value returns nothing
    local gamecache gc = ListCache()
    call StoreInteger(gc, "list"+I2S(list),  "item"+I2S(index), value)
    if ( index >= ListLength(list) ) then
        call StoreInteger(gc, "list"+I2S(list), "length", index+1)
    endif
endfunction

// Adds an item at the end of a list
function ListAdd takes integer list, integer value returns nothing
    call ListSetValue(list, ListLength(list), value)
endfunction

// Removes the last item in a list
function ListRemoveLast takes integer list returns nothing
    local gamecache gc = ListCache()
    local integer index = ListLength(list)-1
    call FlushStoredInteger(gc, "list"+I2S(list), "item"+I2S(index))
    if ( index >= 0 ) then
        call StoreInteger(gc, "list"+I2S(list), "length", index)    
    endif
endfunction

// Inserts an item in a list
function ListInsert takes integer list, integer index, integer value returns nothing
    local integer i = ListLength(list)
    loop // Push all items beyond index
        exitwhen i <= index
        call ListSetValue(list, i, ListGet(list, i-1))
        set i = i - 1
    endloop
    call ListSetValue(list, index, value)
endfunction

// Adds listB to the end of listA
function ListAddList takes integer listA, integer listB returns nothing
    local integer i = 0
    local integer max = ListLength(listB)
    local integer index = ListLength(listA)
    loop
        exitwhen i >= max
        call ListSetValue(listA, index+i, ListGet(listB, i))
        set i = i + 1
    endloop
endfunction

// Deletes the list
function ListClear takes integer list returns nothing
    call FlushStoredMission(ListCache(), "list"+I2S(list))
endfunction

// Removes an item from the list. All items beyond this will be pulled one index down
function ListRemove takes integer list, integer index returns nothing
    local integer i = index
    local integer max = ListLength(list)
    loop
        exitwhen i >= max
        call ListSetValue(list, i, ListGet(list, i+1))
        set i = i + 1
    endloop
    call ListRemoveLast(list)
endfunction

// Finds an item with the value, beginning the search at start. (Use 0 for a complete search)
// Returns the index or -1 if not found
function ListFind takes integer list, integer value, integer start returns integer
    local integer i = start
    local integer max = ListLength(list)
    loop
        exitwhen i >= max
        if ( ListGet(list, i) == value ) then
            return i
        endif
        set i = i + 1
    endloop
    return -1
endfunction

// Returns a copy of the list
function ListClone takes integer list returns integer
    local integer new = ListNew()
    call ListAddList(new, list)
    return new
endfunction

// Returns the value of the item in question, used in ListFilter's filter parameter
function ListFilterValue takes nothing returns integer
    return GetStoredInteger(ListCache(), "lists", "filtervalue")
endfunction
// Returns the value of the item in question, used in ListFilter's filter parameter
function ListFilterIndex takes nothing returns integer
    return GetStoredInteger(ListCache(), "lists", "filterindex")
endfunction

// Removes all items NOT matching filter. Returns the amount of items removed
function ListFilter takes integer list, boolexpr filter returns integer
    local integer n = ListLength(list)
    local integer i = 0
    local integer o = 0
    local trigger trg = CreateTrigger()
    local triggercondition c = TriggerAddCondition(trg, filter)
    local gamecache gc = ListCache()
    local integer removed = 0
    local boolean array remove
    loop
        exitwhen i >= n
        call StoreInteger(gc, "lists", "filtervalue", ListGet(list, i))
        call StoreInteger(gc, "lists", "filterindex", i)
        if ( not TriggerEvaluate(trg) ) then
            set remove[i] = true
            set removed = removed + 1
        endif
        set i = i + 1
    endloop
    call TriggerRemoveCondition(trg, c)
    call DestroyTrigger(trg)
    if ( removed == 0 ) then
        return 0
    endif
    set i = 0
    loop
        exitwhen i >= n-removed
        loop
            exitwhen not remove[i+o]
            set o = o + 1
        endloop
        if ( o != 0 ) then
            call ListSetValue(list, i, ListGet(list, i+o))
        endif
        set i = i + 1
    endloop
    loop
        exitwhen i >= n
        call FlushStoredInteger(gc, "list"+I2S(list), "item"+I2S(i))
        set i = i + 1
    endloop
    call StoreInteger(gc, "list"+I2S(list), "length", n-removed)
    return removed
endfunction

// Will display the whole list to all players.
// For example: list0(12, 1, 68, 42, 1, 3, 8)
function ListDebug takes integer list returns nothing
    local integer n = ListLength(list)
    local integer i = 0
    local string s = "list"+I2S(list)+"("
    loop
        exitwhen i >= n
        set s = s + I2S(ListGet(list, i))
        if ( i != n-1 ) then
            set s = s + ", "
        endif
        set i = i + 1
    endloop
    set s = s + ")"
    call DisplayTextToForce(GetPlayersAll(), s)
endfunction







// **************************************
//            Waypoint Utility
// **************************************

function WPCache takes nothing returns gamecache
    return InitGameCache("wpsystem")
endfunction
function GetWPConnectCount takes integer wp returns integer
    return GetStoredInteger(InitGameCache("wpsystem"), "wp"+I2S(wp), "connects")
endfunction

function WPListClear takes nothing returns nothing
    call FlushStoredMission(WPCache(), "wplist")
endfunction
function WPSetTarget takes integer wp returns nothing
    call StoreInteger(WPCache(), "wp", "target", wp)
endfunction
function WPGetTarget takes nothing returns integer
    return GetStoredInteger(WPCache(), "wp", "target")
endfunction
function WPSetDist takes real dist returns nothing
    call StoreReal(WPCache(), "wp", "dist", dist)
endfunction
function WPGetDist takes nothing returns real
    return GetStoredReal(WPCache(), "wp", "dist")
endfunction
function WPListPost takes integer wp, real dist returns boolean
    local real d = GetStoredReal(WPCache(), "wplist", "wp"+I2S(wp))
    if ( d <= dist and d != 0 ) then
        return false
    endif
    call StoreReal(WPCache(), "wplist", "wp"+I2S(wp), dist)
    return true
endfunction

function WPX takes integer wp returns real
    return GetStoredReal(WPCache(), "wp"+I2S(wp), "x")
endfunction
function WPY takes integer wp returns real
    return GetStoredReal(WPCache(), "wp"+I2S(wp), "y")
endfunction

function WPDist takes integer wp1, integer wp2 returns real
    local gamecache gc = WPCache()
    local real dx = GetStoredReal(gc, "wp"+I2S(wp1), "x") - GetStoredReal(gc, "wp"+I2S(wp2), "x")
    local real dy = GetStoredReal(gc, "wp"+I2S(wp1), "y") - GetStoredReal(gc, "wp"+I2S(wp2), "y")
    return dx*dx + dy*dy
endfunction

function NewWaypoint takes real x, real y returns integer
    local gamecache gc = WPCache()
    local integer id = GetStoredInteger(gc, "wp", "wpcounter") // Get unique id
    call StoreInteger(gc, "wp", "wpcounter", id+1) // Increase reference
    call StoreReal(gc, "wp"+I2S(id), "x", x)
    call StoreReal(gc, "wp"+I2S(id), "y", y)
    return id
endfunction
// Only tests if they are directly connected.
function AreWaypointsConnected takes integer wp1, integer wp2 returns boolean
    local gamecache gc = WPCache()
    local integer i
    local integer max = GetWPConnectCount(wp1)
    if ( GetWPConnectCount(wp2) < max ) then
        set max = GetWPConnectCount(wp2)
        set i = wp1
        set wp1 = wp2
        set wp2 = wp1
    endif
    set i = 0
    loop
        exitwhen i >= max
        if ( GetStoredInteger(gc, "wp"+I2S(wp1), "connect"+I2S(i)) == wp2 ) then
            return true
        endif
        set i = i + 1
    endloop
    return false
endfunction
function ConnectWaypoints takes integer wp1, integer wp2 returns nothing
    local gamecache gc = WPCache()
    local integer n = GetStoredInteger(gc, "wp"+I2S(wp1), "connects")
    if ( AreWaypointsConnected(wp1, wp2) ) then
        return
    endif
    call StoreInteger(gc, "wp"+I2S(wp1), "connect"+I2S(n), wp2)
    call StoreInteger(gc, "wp"+I2S(wp1), "connects", n+1)
    set n = GetStoredInteger(gc, "wp"+I2S(wp2), "connects")
    call StoreInteger(gc, "wp"+I2S(wp2), "connect"+I2S(n), wp1)
    call StoreInteger(gc, "wp"+I2S(wp2), "connects", n+1)
endfunction
function WPIterateConnections takes integer wp, real dist, integer list, integer level returns boolean
    local integer i = 0
    local integer m = GetWPConnectCount(wp)
    local integer id
    local real d
    local boolean r = false
    if ( wp == WPGetTarget() ) then
        call ListSetValue(list, level, wp)
        return true
    endif
    loop
        exitwhen i >= m
        set id = GetStoredInteger(WPCache(), "wp"+I2S(wp), "connect"+I2S(i))
        set d = WPDist(wp, id) + dist
        if ( id != wp and WPListPost(id, d) ) then
            if ( WPIterateConnections(id, d, list, level+1) ) then
                call ListSetValue(list, level, wp)
                set r = true
            endif
        endif
        set i = i + 1
    endloop
    return r
endfunction

function GetWaypointPathList takes integer wpstart, integer wpend returns integer
    local integer list = ListNew()
    call WPListClear()
    call WPSetTarget(wpend)
    if ( not WPIterateConnections(wpstart, 1, list, 0) ) then
        call ListClear(list)
        return -1
    endif
    return list
endfunction

function WPIterateSmall takes integer wp, real x, real y returns nothing
    local gamecache gc = WPCache()
    local real dx = WPX(wp) - x
    local real dy = WPY(wp) - y
    local real dist = dx*dx + dy*dy
    local integer i = 0
    local integer n = GetWPConnectCount(wp)
    local integer id
    if ( not WPListPost(wp, 1)) then
        return
    endif
    if ( dist < GetStoredReal(gc, "wp", "mindist") ) then
        call StoreReal(gc, "wp", "mindist", dist)
        call StoreInteger(gc, "wp", "mindistwp", wp)
    endif
    loop
        exitwhen i >= n
        set id = GetStoredInteger(gc, "wp"+I2S(wp), "connect"+I2S(i))
        call WPIterateSmall(id, x, y)
        set i = i + 1
    endloop
endfunction

// Gets the waypoint closest to (x,y) that is somehow connected to wp
function GetClosestWaypoint takes real x, real y, integer wp returns integer
    local gamecache gc = WPCache()
    local real dx = WPX(wp) - x
    local real dy = WPY(wp) - y
    local integer i = 0
    call WPListClear()
    call StoreReal(gc, "wp", "mindist", dx*dx+dy*dy)
    call StoreInteger(gc, "wp", "mindistwp", wp)
    call WPIterateSmall(wp, x, y)
    return GetStoredInteger(gc, "wp", "mindistwp")
endfunction

function UnitStopFollowWaypointPath takes unit u returns nothing
    local timer t = GetHandleTimer(u, "wptimer")
    local integer list = GetHandleInt(t, "list")
    call ListClear(list)
    call FlushHandleLocals(t)
    call SetHandleHandle(u, "wptimer", null)
    call DestroyTimer(t)
endfunction

function IsOrderFromWP takes nothing returns boolean
    return GetStoredBoolean(WPCache(), "wp", "wp"+I2S(HandleToInt(GetOrderedUnit()))+"order")
endfunction
function SetOrderFromWP takes unit u, boolean b returns nothing
    call StoreBoolean(WPCache(), "wp", "wp"+I2S(HandleToInt(u))+"order", b)
endfunction

function UnitGotoWaypoint_Loop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit(t, "u")
    local string order = GetHandleString(t, "order")
    local integer list = GetHandleInt(t, "list")
    local integer level = GetHandleInt(t, "level")
    local integer wp = ListGet(list, level)
    local real dx = GetUnitX(u) - WPX(wp)
    local real dy = GetUnitY(u) - WPY(wp)
    local real dist = dx*dx + dy*dy
    if ( dist <= 400*400 ) then
        set level = level + 1
        set wp = ListGet(list, level)
        if ( wp == 0 ) then // Target waypoint reached
            call UnitStopFollowWaypointPath(u)
        else // Goto next waypoint
            call SetOrderFromWP(u, true)
            call IssuePointOrder(u, order, WPX(wp), WPY(wp))
            call SetOrderFromWP(u, false)
            call SetHandleInt(t, "level", level)
        endif
    endif
    if ( GetUnitCurrentOrder(u) != OrderId(order) ) then
        call UnitStopFollowWaypointPath(u)
    endif
endfunction

// Lets unit u move to waypoint wp through the shortest waypointed path.
// If attack=true he will be ordered to attack-move instead of just move
function UnitGotoWaypoint takes unit u, integer wp, boolean attack returns nothing
    local integer wpstart = GetClosestWaypoint(GetUnitX(u), GetUnitY(u), wp)
    local integer list = GetWaypointPathList(wpstart, wp)
    local timer t = CreateTimer()
    local string order = "move"
    if ( attack ) then
        set order = "attack"
    endif
    call SetHandleHandle(t, "u", u)
    call SetHandleInt(t, "list", list)
    call SetHandleString(t, "order", order)
    call SetHandleHandle(u, "wptimer", t)
    call SetOrderFromWP(u, true)
    call IssuePointOrder(u, order, WPX(ListGet(list, 0)), WPY(ListGet(list, 0)))
    call SetOrderFromWP(u, false)
    call TimerStart(t, 1, true, function UnitGotoWaypoint_Loop)
endfunction

Comments

7
It seems that people are having a hard time using the waypoint system.

If you know nothing of Jass yourself, it will be pretty much impossible to make it work. Get a Jass person to join your project if you are desperate.

For those who know Jass: Copy the script to the map header - but make sure that you don't end up with duplicate handle converters and handle var functions.
Placing the waypoints can be a long process since you have to read the coordinates manually from the world editor and type them in your initialization script.
Also be sure to replace InitGameCache("wpsystem"), InitGameCache("listcache"), and InitGameCache("jasslocalvars.w3v") with global variables to prevent leaks.

Here is an example of a waypoint initialization script:

function InitWaypoints takes nothing returns nothing
    local integer array p
    // Make a square of waypoints
    set p[0] = NewWaypoint(1000, 1000)
    set udg_WPTopRightCorner = p[0]
    set p[1] = NewWaypoint(-1000, 1000)
    set p[2] = NewWaypoint(-1000, -1000)
    set p[3] = NewWaypoint(1000, -1000)
    call ConnectWaypoints(p[0], p[1])
    call ConnectWaypoints(p[1], p[2])
    call ConnectWaypoints(p[2], p[3])
    call ConnectWaypoints(p[3], p[0])
endfunction

function UnitGotoTopRightCorner takes unit u returns nothing
    call UnitGotoWaypoint(u, udg_WPTopRightCorner, false)
endfunction

As a rather crude example, say that the waypoints surrounded a lava pit. The unit would go to the corner of the pit without actually entering the pit (if that made any sense).

In an AoS map you'd probably be placing a waypoint at every turn on the path and one at the shop and one at the healing pool etc.
:oops:

Wo lernt man sowas cooles^^
How do i Dl it???!!!????!!!?
*HELP PLESE!!!*
Yes, the code is quite optimized and it shouldn't leak a thing. But that has little to do with me using the return bug.
The return bug has been known for ages, and it is piece of cake using it.
Personally I think the WP and List utility is way more interesting, but then again it would take a long time to read through my uncommented code.
WTF? I was logged when I wrote that comment...

Im Raz.
I don't see why using the return bug will mean that it is optimized, it would just mean it is multi instaceable, but yes, the code doesn't leak
This rox :)

And its optimized (and doesnt leaks) i think, though i havent read the code yet. But you are using Return Bug, so thats why i thought so :rolleyes: