HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Library: Spawn Movement

09-16-2009, 11:02 AM#1
Quillraven
i hope for your feedback to make the library better and to improve my skills :)

Collapse JASS:
//*
//* Library:        Spawn Movement
//*
//* Version:        0.3a
//*
//* Description:    library for the movement of spawning units.
//*                 allows to create lanes and to set waypoints.
//*                 also includes an automatically thing to disallow unit's flee.
//*
//* User functions: 1) function RegisterLane takes Lane WhichLane, integer Key returns nothing
//*                 2) function GetLane takes integer Key returns Lane
//*                 3) RegisterUnit takes unit WhichUnit, integer LaneKey returns nothing
//*                 4) GetUnitLane takes unit WhichUnit returns integer
//*                 5) GetUnitLaneKey takes unit WhichUnit returns integer
//*                 6) GetUnitWaypointIndex takes unit WhichUnit returns integer
//*                 7) SetUnitWaypointIndex takes unit WhichUnit, integer NewIndex returns nothing
//*                 8) GetUnitWaypointX takes unit WhichUnit returns real
//*                 9) GetUnitWaypointY takes unit WhichUnit returns real
//*
//*

library SpawnMovement initializer init

globals
    private constant integer MAXWAYPOINTS           = 16         // max number of waypoints a lane contains
    private key INDEXLANE                           // index for the hashtable
    private key INDEXLANEKEY                        // index for the hashtable
    private key INDEXWAYPOINTX                      // index for the hashtable
    private key INDEXWAYPOINTY                      // index for the hashtable
    private key INDEXWAYPOINTID                     // index for the hashtable
    
    // MAP SPECIFIC VARIABLES
    public key TOPLANEWEST
    public key MIDDLELANEWEST
    public key BOTTOMLANEWEST
    public key TOPLANEEAST
    public key MIDDLELANEEAST
    public key BOTTOMLANEEAST
    
    private constant real REFRESHINTERVAL   = 20 * 60   // every 20 minutes refresh the trigger
                                                        // if u got problems with this, set the
                                                        // interval to 0 to avoid refreshing
    private constant real EPSILON           = 300       // tolerance for checking if unit is near waypoint
    
    private region REGION                   = null      // region to store all waypointrects
    
    private boolexpr ENTERFILTER           = null
    
    private trigger TRIG                    = null      // trigger to catch "flee" orders of registered unit
    private trigger TRIG2                   = null      // trigger for region entering
    
    private timer TIM                       = null      // timer to refresh the dynamic trigger
    
    private group REGISTEREDUNITS            = null      // group to add units after refreshing the trigger
    
    private hashtable TABLE                 = null      // table to store all the data
endglobals



// struct for creating lanes
struct Lane
    real array CoordX[MAXWAYPOINTS]
    real array CoordY[MAXWAYPOINTS]
    
    integer MaxCoords
    
    static method create takes real StartX, real StartY returns Lane
        local Lane data = Lane.allocate()
        
        set data.MaxCoords = 1
        set data.CoordX[0] = StartX
        set data.CoordY[0] = StartY
        
        return data
    endmethod
    
    method AddCoord takes real X, real Y returns nothing
        if( this.MaxCoords > MAXWAYPOINTS ) then
            call BJDebugMsg( "SpawnMovement - Maximum Waypoints reached!" )
            return
        endif
        
        set this.CoordX[ this.MaxCoords ] = X
        set this.CoordY[ this.MaxCoords ] = Y
        set this.MaxCoords = this.MaxCoords + 1
    endmethod
    
    // returns the reversal of a lane
    // could be useful in aos maps
    method Reverse takes nothing returns Lane
        local integer i = this.MaxCoords-1
        local Lane rev = Lane.create( 0, 0 )
        
        set rev.MaxCoords = this.MaxCoords
        loop
            exitwhen i < 0
            set rev.CoordX[this.MaxCoords-1-i] = this.CoordX[i]
            set rev.CoordY[this.MaxCoords-1-i] = this.CoordY[i]
            set i = i - 1
        endloop
        
        return rev
    endmethod
    
    // returns the closest waypoint index to a given mapcoordinate
    method GetClosestWaypointID takes real X, real Y returns integer
        local integer i = this.MaxCoords-1
        local integer id = i
        local real dx = GetRectMaxX( bj_mapInitialPlayableArea )
        local real dy = 0
        local real diff = 0
        local real diff2 = dx * dx  // store maximum possible maprange
        
        loop
            exitwhen i < 0
            set dx = X - this.CoordX[i]
            set dy = Y - this.CoordY[i]
            set diff = dx * dx + dy * dy
            if( diff < diff2 ) then
                set diff2 = diff
                set id = i
            endif
            set i = i - 1
        endloop
        
        return id
    endmethod
endstruct



// functions for the user
// should be self explaning? :)
public function RegisterLane takes Lane WhichLane, integer Key returns nothing
    local integer i = 0
    local rect r = null
    debug local texttag t = null

    if( WhichLane == 0 ) then
        call BJDebugMsg( "SpawnMovement - Trying to register a null Lane!" )
        return
    endif
    
    call SaveInteger( TABLE, Key, 0, WhichLane )
    
    loop
        exitwhen i >= WhichLane.MaxCoords
        if( not IsPointInRegion( REGION, WhichLane.CoordX[i], WhichLane.CoordY[i] ) and i < WhichLane.MaxCoords - 1 ) then
            set r = Rect( WhichLane.CoordX[i]-EPSILON, WhichLane.CoordY[i]-EPSILON, WhichLane.CoordX[i]+EPSILON, WhichLane.CoordY[i]+EPSILON )
            call RegionAddRect( REGION, r )
            call RemoveRect( r )
            set r = null
        endif
        debug   set t = CreateTextTag()
        debug   call SetTextTagText( t, "Lane:"+I2S(Key)+" --- X:"+I2S(R2I(WhichLane.CoordX[i]))+" --- Y:"+I2S(R2I(WhichLane.CoordY[i])), 0.03 )
        debug   call SetTextTagPos( t, WhichLane.CoordX[i], WhichLane.CoordY[i], 30.00 )
        debug   call SetTextTagPermanent( t, true )
        debug   call SetTextTagVisibility( t, true )
        set i = i + 1
    endloop
endfunction

public function GetLane takes integer Key returns Lane
    return LoadInteger( TABLE, Key, 0 )
endfunction

public function RegisterUnit takes unit WhichUnit, integer LaneKey returns nothing
    local Lane l = 0
    local integer uid = 0
    
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to register a null unit!" )
        return
    endif
    if( IsUnitInGroup( WhichUnit, REGISTEREDUNITS ) ) then
        call BJDebugMsg( "SpawnMovement - Trying to register a unit which is already registered!" )
    endif
    
    set l = GetLane( LaneKey )
    if( l == 0 ) then
        call BJDebugMsg( "SpawnMovement - Trying to register a unit for a wrong lane!" )
        return
    endif
    
    set uid = GetHandleId( WhichUnit )
    call SaveInteger( TABLE, uid, INDEXLANE, l )
    call SaveInteger( TABLE, uid, INDEXLANEKEY, LaneKey )
    call SaveReal( TABLE, uid, INDEXWAYPOINTX, l.CoordX[0] )
    call SaveReal( TABLE, uid, INDEXWAYPOINTY, l.CoordY[0] )
    call SaveInteger( TABLE, uid, INDEXWAYPOINTID, 0 )
    
    call GroupAddUnit( REGISTEREDUNITS, WhichUnit )
    
    call TriggerRegisterUnitEvent( TRIG, WhichUnit, EVENT_UNIT_ISSUED_ORDER )
    call TriggerRegisterUnitEvent( TRIG, WhichUnit, EVENT_UNIT_ISSUED_POINT_ORDER )
    call IssuePointOrder( WhichUnit, "attack", l.CoordX[0], l.CoordY[0] )
endfunction

public function DeregisterUnit takes unit WhichUnit returns nothing
    if( WhichUnit != null ) then
        call GroupRemoveUnit( REGISTEREDUNITS, WhichUnit )
    endif
endfunction

public function GetUnitLane takes unit WhichUnit returns integer
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to get the Lane of a null unit!" )
        return 0
    endif
    
    return LoadInteger( TABLE, GetHandleId( WhichUnit ), INDEXLANE )
endfunction

public function GetUnitLaneKey takes unit WhichUnit returns integer
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to get the LaneKey of a null unit!" )
        return 0
    endif
    
    return LoadInteger( TABLE, GetHandleId( WhichUnit ), INDEXLANEKEY )
endfunction

public function GetUnitWaypointIndex takes unit WhichUnit returns integer
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to get the Waypoint index of a null unit!" )
        return 0
    endif
    
    return LoadInteger( TABLE, GetHandleId( WhichUnit ), INDEXWAYPOINTID )
endfunction

public function GetUnitWaypointX takes unit WhichUnit returns real
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to get the X coordination of a null unit!" )
        return 0
    endif
    
    return LoadReal( TABLE, GetHandleId( WhichUnit ), INDEXWAYPOINTX )
endfunction

public function GetUnitWaypointY takes unit WhichUnit returns real
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to get the Y coordination of a null unit!" )
        return 0
    endif
    
    return LoadReal( TABLE, GetHandleId( WhichUnit ), INDEXWAYPOINTY )
endfunction

public function SetUnitWaypointIndex takes unit WhichUnit, integer NewIndex returns nothing
    local Lane l = 0
    local integer uid = 0
    
    if( WhichUnit == null ) then
        call BJDebugMsg( "SpawnMovement - Trying to set the Waypoint index of a null unit!" )
        return
    endif
    set l = GetUnitLane( WhichUnit )
    if( NewIndex >= l.MaxCoords or NewIndex < 0 ) then
        call BJDebugMsg( "SpawnMovement - Setting Waypoint index out of bounds!" )
        return
    endif
    
    set uid = GetHandleId( WhichUnit )
    call SaveInteger( TABLE, uid, INDEXWAYPOINTID, NewIndex )
    call SaveReal( TABLE, uid, INDEXWAYPOINTX, l.CoordX[NewIndex] )
    call SaveReal( TABLE, uid, INDEXWAYPOINTY, l.CoordY[NewIndex] )
    call IssuePointOrder( WhichUnit, "attack", l.CoordX[NewIndex], l.CoordY[NewIndex] )
endfunction


// trigger fires, when registered units are given orders
// if the order is a "flee" order, reorder the unit
// to move to the last waypoint
//
// if the unit is already in range of the waypoint
// set the next waypoint
private function Trig_Conditions takes nothing returns boolean
    local integer OID = GetIssuedOrderId()
    local integer wid = 0
    local integer uid = 0
    local unit u = null
    local real x = 0
    local real y = 0
    local Lane l = 0
    
    if( OID == 851976 or OID == 851973 or OID == 851974 or OID == 851972 or OID == 851993 or OID == 0 ) then
        set u = GetOrderedUnit()
        set x = GetUnitWaypointX( u )
        set y = GetUnitWaypointY( u )
        set l = GetUnitLane( u )
        set wid = GetUnitWaypointIndex( u )
        set uid = GetHandleId( u )
        
        if( IsUnitInRangeXY( u, x, y, EPSILON ) and wid+1 < l.MaxCoords ) then
            set wid = wid + 1
            call SaveReal( TABLE, uid, INDEXWAYPOINTX, l.CoordX[wid] )
            call SaveReal( TABLE, uid, INDEXWAYPOINTY, l.CoordY[wid] )
            call SaveInteger( TABLE, uid, INDEXWAYPOINTID, wid )
        endif
        call IssuePointOrder( u, "attack", l.CoordX[wid], l.CoordY[wid] )
        
        set u = null
    endif
    
    return false
endfunction

// trigger fires, when registered units come within range
// of a waypoint dummy unit -> set next waypoint
private function Trig2_Conditions takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local integer wid = GetUnitWaypointIndex( u )
    local integer uid = GetHandleId( u )
    local real x = GetUnitWaypointX( u )
    local real y = GetUnitWaypointY( u )
    local Lane l = GetUnitLane( u )
    
    if( IsUnitInRangeXY( u, x, y, EPSILON ) and wid+1 < l.MaxCoords ) then
        set wid = wid + 1
        call SaveReal( TABLE, uid, INDEXWAYPOINTX, l.CoordX[wid] )
        call SaveReal( TABLE, uid, INDEXWAYPOINTY, l.CoordY[wid] )
        call SaveInteger( TABLE, uid, INDEXWAYPOINTID, wid )
    endif
    call IssuePointOrder( u, "attack", l.CoordX[wid], l.CoordY[wid] )
    set u = null
    
    return false
endfunction


// functions for refreshing the dynamic trigger
private function group_callback takes nothing returns nothing
    call TriggerRegisterUnitEvent( TRIG, GetEnumUnit(), EVENT_UNIT_ISSUED_ORDER )
    call TriggerRegisterUnitEvent( TRIG, GetEnumUnit(), EVENT_UNIT_ISSUED_POINT_ORDER )
endfunction

private function RefreshTrigger takes nothing returns nothing
    call TriggerClearConditions( TRIG )
    call DestroyTrigger( TRIG )
    
    set TRIG = CreateTrigger()    
    call TriggerAddCondition( TRIG, Condition( function Trig_Conditions ) )
    call ForGroup( REGISTEREDUNITS, function group_callback )
endfunction 
// endfunctions

private function EnterFilter takes nothing returns boolean
    return IsUnitInGroup( GetFilterUnit(), REGISTEREDUNITS )
endfunction

// init function to initialize the globals
// 
// also starts the refreshing of the dynamic trigger
// if the refreshinterval is greater zero
private function init takes nothing returns nothing
    local Lane l = 0
    local Lane l2 = 0
    
    set TABLE = InitHashtable()
    set TIM = CreateTimer()
    set REGISTEREDUNITS = CreateGroup()
    set REGION = CreateRegion()
    set TRIG = CreateTrigger()
    set TRIG2 = CreateTrigger()
    set ENTERFILTER = Condition( function EnterFilter )
    call TriggerAddCondition( TRIG, Condition( function Trig_Conditions ) )
    call TriggerAddCondition( TRIG2, Condition( function Trig2_Conditions ) )
    
    if( REFRESHINTERVAL > 0 ) then
        call TimerStart( TIM, REFRESHINTERVAL, true, function RefreshTrigger )
    endif
    
    // init toplane
    set l = l.create( -6400, 0 )
    call l.AddCoord( -5000, 1100 )
    call l.AddCoord( -2000, 4500 )
    call l.AddCoord( 0, 4400 )
    call l.AddCoord( 2300, 4300 )
    call l.AddCoord( 3700, 3700 )
    call l.AddCoord( 5200, 1700 )
    call l.AddCoord( 6400, 0 )
    set l2 = l.Reverse()
    call RegisterLane( l, TOPLANEWEST )
    call RegisterLane( l2, TOPLANEEAST )
      
    // init midlane
    set l = l.create( -6400, 0 )
    call l.AddCoord( -4400, 0 )
    call l.AddCoord( -3300, 0 )
    call l.AddCoord( -2600, 1800 )
    call l.AddCoord( -1800, 1800 )
    call l.AddCoord( -1800, 0 )
    call l.AddCoord( 1800, 0 )
    call l.AddCoord( 2000, -1700 )
    call l.AddCoord( 2700, -1700 )
    call l.AddCoord( 3400, 0 )
    call l.AddCoord( 4500, 0 )
    call l.AddCoord( 6400, 0 )
    set l2 = l.Reverse()
    call RegisterLane( l, MIDDLELANEWEST )
    call RegisterLane( l2, MIDDLELANEEAST )
    
    // init botlane
    set l = l.create( -6400, 0 )
    call l.AddCoord( -5200, -1500 )
    call l.AddCoord( -1700, -4900 )
    call l.AddCoord( -700, -4800 )
    call l.AddCoord( 2900, -4500 )
    call l.AddCoord( 5400, -1400 )
    call l.AddCoord( 6400, 0 )
    set l2 = l.Reverse()
    call RegisterLane( l, BOTTOMLANEWEST )
    call RegisterLane( l2, BOTTOMLANEEAST )
    
    // register enter region event to TRIG2
    call TriggerRegisterEnterRegion( TRIG2, REGION, ENTERFILTER )
endfunction

endlibrary

hoping for your feedback
Quill

example for usage
Code:
local unit u = CreateUnit( Player(0), 'hfoo', 0, 0, 0 )
call RegisterUnit( u, SpawnMovement_TOPLANEWEST )
09-16-2009, 11:30 AM#2
Anachron
Funny, I had the same idea 2 weeks before:
Anachrons AoS Move System.
09-16-2009, 01:39 PM#3
Rising_Dusk
It's not the right forum. Script forum is for resource submissions, you should post in the "Triggers & Scripts" forum. I have moved it there. Furthermore, use [jass][/jass] tags for your code.
09-16-2009, 01:52 PM#4
Anachron
Its funny, our both systems are quite the same somehow, however, I use repeating checks to check whether an unit is on the move, but you only check with commands, which is bugged and can be disabled with my system.
09-16-2009, 07:51 PM#5
Zerzax
Collapse JASS:
set diff = dx*dx + dy*dy
if diff < diff2 then
    set diff2 = diff
endif

Comparison of squares will yield the same result in the if condition.
09-20-2009, 06:22 PM#6
Quillraven
fixed a few things and added some stuff.

i now use invisible permanent immolation dummies instead of rect entering or periodic checking (because the administration of regions was very ugly).
the advantage of immo is, that it damages units when they come within range so you maybe can set the hero/unitduration in the ability to a higher value than 2 imo.

i first thought of tornado slow aura, but this would add a buff to units and i think removing the buff per trigger will not disable the aura to add the buff a few seconds later when the unit is still in range? if so, i could remove immo and use tornado slow aura to avoid the 0.01 damage taken every immolation period.
09-21-2009, 03:50 PM#7
Anachron
I still think my system is better, because its giving you more functionality and less rules to follow.

Anachrons Move System.

What I am missing at your system is the ability to remove the automatic reordering, your system is simply based of that function, which isn't well at all.
09-23-2009, 06:13 PM#8
Quillraven
*update*

i removed the thing with the perma immolation. dunno why i thought, this was a good way? :)
however - included now an automatic rect generation at the given waypointcoordinate, if there isn't already a rect.
so you now have both: order and entering trigger


Quote:
What I am missing at your system is the ability to remove the automatic reordering, your system is simply based of that function, which isn't well at all.
1) it isn't only based on this since 0.2
2) if any1 wants to disabled the reordering (i don't know any reason why?) he can simple comment the triggeraddcondition thing in the init. shouldn't be a problem



edit:
fixed a stupid mistake of mine in version 0.3, where i added the enterregion event für each registering unit :/ also included an own filter to exclude non-registered entering units.
also replaced the stop order in registerunit/setunitwaypointindex with an attack order.