1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Patrol System v1.6

Submitted by Chaosy, IcemanBo
This bundle is marked as approved. It works and satisfies the submission rules.
Patrol allows units to walk certain patrol paths with certain behaviour. PatrolPaths can be simple routes, but a unit can also run complex routes.
With "complex" patrols is meant that you can combine multiple patrol paths into one bigger, complex patrol path. See demo example.

Gif

[​IMG]
Footman - Normal Patrol , and in reverse form
Gryphon - Backward Patrol, and in reverse form
Knight - Loop Patrol, and in reverse form
Spellbreaker - Complex Patrol (2 paths combined)
Whisp - Normal Patrol


There is a normal, vJass version, and an addon which is made for GUI usage.
The for GUI made library internaly works with the normal vJass version, so you still might need required libraries.
For reading GUI file example you need to open the demo map.

Code (vJASS):

native UnitAlive takes unit u returns boolean
library PatrolPath
    //! runtextmacro DEFINE_STRUCT_VECTOR("", "PatrolPath", "PatrolPoint")
endlibrary
//! zinc
library Patrol  /* v1.5a --hiveworkshop.com/threads/patrol-waypoint-system.276391/

    */
requires VectorT,       /* hiveworkshop.com/threads/containers-vector-t.248942/
    */
         UnitDex,       /* wc3c.net/showthread.php?t=101322
    */
         TimerUtils,    /* hiveworkshop.com/threads/system-unitdex-unit-indexer.248209
    */
         PatrolPath     /*
       
       
    Information
    ¯¯¯¯¯¯¯¯¯¯¯
       
            Patrol allows units to walk certain patrol paths with certain behaviour.
            PatrolPaths can be simple routes, but a unit can also run complex routes.
           
            With "complex" patrols is meant that you can combine multiple patrol
            paths into one bigger, complex patrol path. See demo example.
           
           
    Applied method
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
           
            1. We create a PatrolPath which is a new vector type from VectorT.
           
            2. We create as many PatrolPoint(s) as we need and add them to a PatrolPath.
                (for adding PatrolPoints to a PatrolPath we use the common vector API, see VectorT)
               
            3. We can apply a unit anytime to a certain Path with using our PatrolUnit API.
           
            You are allowed to define a special PatrolType (defines the behaviour) for each patroling unit.
            You can also register your code which runs when ever a PatrolPoint is reached.
           

*/

//! novjass  
 //=================== --------- API --------- ====================

    struct PatrolPoint
           
        real x
        real y
       
        static method create(real x, real y)
       
        method destroy()
       
//  With this you can create and destroy PatrolPoints.
//  You always can change x/y coordinate of a PatrolPoint at any time.

// PatrolPoint is the type that you need to add to a PatrolPath, example:

        local PatrolPath path = PatrolPath.create()
        call path.push(PatrolPoint(0,0))
   
// In the example we create a PatrolPoint(0/0) and add it to our PatrolPath.
// If you wonder where I took the "push" method from, PatrolPath is actually nothing else
// than a vector type from VectorT library. So you can use all API powers from the
// vector library to manage the PatrolPath.
       
===

    struct PatrolType
   
        static constant integer NORMAL     // After patrol finished, the unit walks back to start Point.
        static constant integer BACKWARD   // After patrol finished, the unit walks all the patrol path backwards.
        static constant integer LOOP       // After patrol finished, the unit will instantly be moved to start.

===

    struct PatrolOrder
   
        static constant integer MOVE     // The unit will "move" to points.
        static constant integer ATTACK   // The unit will "attack" to points.

===

    struct PatrolUnit
   
   
        static method create(unit u, PatrolPath pp, PatrolType pt, PatrolOrder po, real guardDistance, boolean reverse) returns thistype
            // Applies a patrolpath with a patroltype to a unit
            // "reverse" means it would go the PatrolPath in reverse, so start at last position (when set to "true")
           
            // guardDistance is only important if the PatrolOrder is ATTACK.
            // it defines the maximum distance before the unit is forced to return patroling.
           
        method destroy()
            // Make the unit no PatrolUnit anymore
           
        method operator unit returns unit
            // the unit which patrols
           
        method operator pathPosition returns integer
        method operator pathPosition= (integer newPos)
            // This is the current position of the unit's PatrolPath.
            // You ONLY need this actually when you register a code (see later).
            // It's advised only to "read" it (see demo), change it only if you know what you're doing.
            // minimum index (start) = 0
            // maximum index (back)  = PatrolPath.size()-1
           
        method operator order returns PatrolOrder
        method operator order= (PatrolOrder p)
       
        method operator guardDistance returns real
        method operator guardDistance= (real r)
   
        method operator enabled  returns boolean
        method operator enabled= (boolean flag)
            // enables/disable an instance
           
        method operator reverse  returns boolean
        method operator reverse= (boolean flag)
            // "true" means the unit goes the PatrolPath in reverse way.
       
        method operator path  returns PatrolPath
        method operator path= (PatrolPath p)
            // you can apply a new path at any time
            // when newly applied, it will start at StartPosition,
            // or at respectivly at LastPosition, if "reverse" is "true".
       
        method operator patrolType  returns PatrolType
        method operator patrolType= (PatrolType p)
            // you can change patrol type at any time
           
        static method operator enabledAll= (boolean flag)
            // enable or disable all instances
           
        static method operator[] (unit u) returns thistype
            // for example:
                set PatrolUnit[unit].reverse = true
               
        static method register(boolexpr be) returns triggercondition
        static method unregister(triggercondition tc)
            // You can register and unregister boolexrepssions with these functions.
            // Registered code will run when ever a PatrolUnit reaches a PatrolPoint.
           
            // When the registered code returns "true", the system will normaly
            //     contitunue the unit's Patrol behaviour.
            // When the registered code returns "false", the system thinks that
            //     you made custom changes and will skip the current PatrolPoint.
           
        method registerPatrolUnit(boolexpr be) returns triggercondition
        method unregisterPatrolUnit(triggercondition tc)
            // same as above, but specified to your instance
           
// !! Just as info, first the globaly registered code runs, and then the instance-specified one.
           
        static method operator currentPatrolUnit returns thistype
            // use this operator to access the current instance inside registered code.
           
            // e.g: PatrolUnit currentInstance = PatrolUnit.currentPatrolUnit;
       
           
===

    function PatrolPathHardDestroy(PatrolPath p)
        // Of course you always can use the standard destructor .destroy()
        // The major difference to this, is that this "Hard Destroy" will also destroy
        // all to the PatrolPath binded PatrolPoints. With standard .destroy() you solely  
        // destroy the PatrolPath itself, but PatrolPoints would still exist.  
        // The reason for that is, because it is technicaly allowed to use one PatrolPoint
        // for multiple PatrolPaths.
        // You only should use this function if you truely don't need the binded points anymore.
       
       
// Look at demo for examples.
       
//! endnovjass    
{
//=================== --------- Config --------- ====================  
 
    private constant boolean DISABLE_ON_DEATH    = true;    // When a PatrolUnit dies, the instance will be automaticaly disabled
                                                            // You will need to enable it by your own again.
    private constant boolean DESTROY_ON_DEATH    = false;   // When a PatrolUnit dies, the instance will be automaticaly destroyed.
   
    private constant boolean FORMATION_ENABLED   = false;   // Set this to true in case players who control patrol units (can) have the
                                                            // formation enabled in their gameplay options.
                                                            // If you have formation enabled, you can bug patrol units with it,
                                                            // and they would start going crazy when you try to keep some units in format
                                                            // that try to reach the same patrol point. So when you set this to "true", the system
                                                            // will try to fix this with giving patrol orders to a point with random/minimal offset of
                                                            // the wanted x/ym so 2 units will likely never have the problem in being in formated movement
                                                            // with also trying to patrol to the very same x/y at the same time.
                                                           
                                                            // but it is just recommended that you leave the formation disabled and set it here to "false".
   
    private constant real    MAX_RANGE           = 20;      // The tolerance distance a unit may have to a patrol point.
   
                                           
//=================== -------------------------- ====================
   
    public struct PatrolPoint{
        real x;
        real y;
        private boolean p_exists;   // private_exists
        static method create(real x, real y) -> thistype{
            thistype this = allocate();
            this.x        = x;
            this.y        = y;
            this.p_exists = true;
            return this;
        }
        method destroy(){
            if (p_exists){
                deallocate();
                p_exists = false;
            }
        }
       
        // user doesn't need to know this exists,
        // but it makes no harm
        method operator exists() -> boolean{
            return p_exists;
        }
    }
   
    public struct PatrolType extends array{
        static constant integer NORMAL   = 1;   // After finished, the unit walks back to start Point.
        static constant integer BACKWARD = 2;   // After finished, the unit walks all the patrol path backwards.
        static constant integer LOOP     = 3;   // After finished, the unit will instantly be moved to start.
       
        static constant integer MAXIMUM_AMOUNT = 3;
    }
   
    public struct PatrolOrder extends array{
        static constant integer MOVE   = 851986;  
        static constant integer ATTACK = 851983;  
    }
   
    private constant real INTERVAL  = 0.031250000;
   
    public struct PatrolUnit extends array{
       
        private static group Group  = CreateGroup();
        private static group Backup = CreateGroup();
        private static group Temp;
       
        private static trigger  handler = CreateTrigger();  // global handler
        private static thistype p_patrolUnit;               // access in registered code
       
//      members
       
        private unit    p_u;               // private_unit
        private timer   clock;
        private integer incrementer;       // Defines if current position is going forward or backward in PatrolPath
        private integer p_pathPosition;    // Current position in PatrolPath vector
        private trigger p_handler;         // instance specific handler
        private boolean p_reverse;         // private_reverse
        private boolean p_enabled;         // private_enabled
       
        private real p_guardDistance;
        private trigger guardHandler;
       
        private PatrolType p_patrolType;
        private PatrolOrder p_order;
        private PatrolPath p_path;
       
        // x/y of the point the unit needs to return, in case it is chasing/fighting some unit
        private real xReturn;
        private real yReturn;
        private boolean isChasing;
       
        method destroy(){
            ReleaseTimer(clock);
           
            if(p_handler != null){
                DestroyTrigger(p_handler);
                p_handler = null;
            }
           
            if(guardHandler != null){
                DestroyTrigger(guardHandler);
                guardHandler = null;
            }
           
            p_enabled = false;
            GroupRemoveUnit(Group, p_u);
            p_u = null;
            clock = null;
        }
       
        private static method callback(){
            thistype this = GetTimerData(GetExpiredTimer());
            real distance, x, y;
           
            static if(DESTROY_ON_DEATH){
                if (!UnitAlive(p_u)){
                    destroy();
                    return;
                }
            }
            static if(DISABLE_ON_DEATH){
                if (!UnitAlive(p_u)){
                    PauseTimer(clock);
                    p_enabled = false;
                    return;
                }
            }
           
            if(p_path.empty()){
                debug BJDebugMsg(thistype.callback.name + ": instance " + I2S(this) + " has an invalid or empty PatrolPath.");
                return;
            }
            else if(!p_path[p_pathPosition].exists){
                debug BJDebugMsg(thistype.callback.name + ": instance " + I2S(this) + " tries to acces a removed/invalid PatrolPoint.");
                return;
            }
           
            x = GetUnitX(p_u); y = GetUnitY(p_u);
            distance = SquareRoot((p_path[p_pathPosition].x-x)*(p_path[p_pathPosition].x-x) + (p_path[p_pathPosition].y-y)*(p_path[p_pathPosition].y-y));
           
            if (distance <= MAX_RANGE) {
               
                p_patrolUnit = this;
                TriggerEvaluate(handler);
                if(p_handler != null)
                    TriggerEvaluate(p_handler);

                if (p_patrolType == PatrolType.NORMAL){
                    if(p_path[p_pathPosition] == p_path.back() && !reverse){
                        p_pathPosition = 0;
                    }
                    else if(reverse && p_path[p_pathPosition] == p_path.front()){
                        p_pathPosition = p_path.size()-1;
                    }
                    else {
                        p_pathPosition = p_pathPosition + incrementer;
                    }
                }
                else if(p_patrolType == PatrolType.BACKWARD){
                    if(p_path[p_pathPosition] == p_path.back()){
                        incrementer = -1;
                    }
                    else if (p_path[p_pathPosition] == p_path.front()){
                        incrementer = 1;
                    }
                    p_pathPosition = p_pathPosition + incrementer;
                }
                else if(p_patrolType == PatrolType.LOOP){
                    if(p_path[p_pathPosition] == p_path.back() && !reverse){
                        SetUnitX(p_u, p_path.front().x);
                        SetUnitY(p_u, p_path.front().y);
                        p_pathPosition = 1;
                       
                    }
                    else if(reverse && p_path[p_pathPosition] == p_path.front()){
                        SetUnitX(p_u, p_path.back().x);
                        SetUnitY(p_u, p_path.back().y);
                        p_pathPosition = p_path.size()-2;
                    }
                    else {
                        p_pathPosition = p_pathPosition + incrementer;
                    }
                }
               
                if (FORMATION_ENABLED) {
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x + GetRandomReal(0, 0.1), p_path[p_pathPosition].y + GetRandomReal(0, 0.1));
                }
                else{
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x , p_path[p_pathPosition].y);
                }
            }
           
            // when the unit got off the path for some reason (maybe after a fight with enemies),
            // and is being detected without a current order, we re-order it to continue patroling.
            if (GetUnitCurrentOrder(p_u) == null){
           
               if (FORMATION_ENABLED) {
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x + GetRandomReal(0, 0.1), p_path[p_pathPosition].y + GetRandomReal(0, 0.1));
                }
                else{
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x , p_path[p_pathPosition].y);
                }
               
            }
        }
       
       
        // callback for when a unit is chasing an other unit
        private static method callback_2(){
            thistype this = GetTimerData(GetExpiredTimer());
            real distance, x, y;
            static if(DESTROY_ON_DEATH){
                if (!UnitAlive(p_u)){
                    destroy();
                    return;
                }
            }
            static if(DISABLE_ON_DEATH){
                if (!UnitAlive(p_u)){
                    PauseTimer(clock);
                    p_enabled = false;
                    return;
                }
            }
           
            x = GetUnitX(p_u); y = GetUnitY(p_u);
            distance = SquareRoot((xReturn-x)*(xReturn-x) + (yReturn-y)*(yReturn-y));
           
            // unit returned to path, it can continue patroling
            if(!isChasing && distance <= MAX_RANGE){
                TimerStart(clock, INTERVAL, true, function thistype.callback);
            }
           
            // unit is out of max range, it gets ordered moving back
            if(distance > p_guardDistance){
                isChasing = false;
                IssuePointOrderById(p_u, PatrolOrder.MOVE, xReturn, yReturn);
            }
           
            // unit is not chasing anymore, so return
            if (GetUnitCurrentOrder(p_u) == null){
                isChasing = false;
                IssuePointOrderById(p_u, PatrolOrder.MOVE, xReturn, yReturn);
            }
        }
       
        private static method onTargetAquired() -> boolean{
            thistype this = GetUnitId(GetTriggerUnit());
            if (!isChasing){
                xReturn = GetUnitX(p_u);
                yReturn = GetUnitY(p_u);
            }
            isChasing = true;
            TimerStart(clock, INTERVAL, true, function thistype.callback_2);
            return false;
        }
       
        static method create(unit u, PatrolPath pp, PatrolType pt, PatrolOrder po, real gd, boolean r) -> thistype{
            thistype this;
           
            if(integer(pt) < 1 || integer(pt) > 3){
                debug BJDebugMsg(thistype.create.name + ": unit with id " + I2S(GetHandleId(u)) + " tries to apply an invalid PatrolType.");
                return 0;
            }
            if(!(po == PatrolOrder.MOVE || po == PatrolOrder.ATTACK)){
                debug BJDebugMsg(thistype.create.name + ": unit with id" + I2S(GetHandleId(u)) + " tries to apply an invalid PatrolOrder.");
                return 0;
            }
            if(IsUnitInGroup(u, Group)){
                debug BJDebugMsg(thistype.create.name + ": unit with id" + I2S(GetHandleId(u)) + " already is a PatrolUnit.");
                return 0;
            }
           
            this            = GetUnitId(u);
            clock           = NewTimerEx(this);
            p_u             = u;
            p_order         = po;
            p_path          = pp;
            p_patrolType    = pt;
            p_reverse       = r;
            p_enabled       = true;
            p_handler       = null;
            isChasing       = false;
            p_guardDistance = gd;
            GroupAddUnit(Group, p_u);
           
            if(po == PatrolOrder.ATTACK){
                guardHandler = CreateTrigger();
                TriggerAddCondition(guardHandler, Condition(function thistype.onTargetAquired));
                TriggerRegisterUnitEvent(guardHandler, u, EVENT_UNIT_ACQUIRED_TARGET);
            }
           
            if(r){
                p_pathPosition = p_path.size()-1;
                incrementer    = -1;
            }
            else{
                p_pathPosition = 0;
                incrementer    = 1;
            }
            TimerStart(clock, INTERVAL, true, function thistype.callback);
            return this;
        }
       
       
        method operator patrolType() -> PatrolType{
            return p_patrolType;
        }
        method operator patrolType=(PatrolType p){
            if(integer(p) > 0 && integer(p) <= PatrolType.MAXIMUM_AMOUNT){
                p_patrolType = p;
            }
            else{
                debug BJDebugMsg("Patrol: operator patrolType=: " + ": instance " + I2S(this) + " tries to apply an invalid PatrolType.");
            }
        }
       
        method operator reverse() -> boolean{
            return p_reverse;
        }
        method operator reverse=(boolean flag){
            if(flag != p_reverse){
                incrementer   = -incrementer;
                p_reverse = flag;
            }
        }
       
        method operator path() -> PatrolPath{
            return p_path;
        }
        method operator path=(PatrolPath p){
            p_path = p;
            if(reverse){
                p_pathPosition = p_path.size()-1;
                incrementer    = -1;
            }
            else{
                p_pathPosition = 0;
                incrementer    = 1;
            }
        }
       
        // this exists so the user has no chance to "null" the
        // unit variable in public access.
        method operator unit()-> unit{
            return p_u;
        }
       
        method operator pathPosition()-> integer{
            return p_pathPosition;
        }
        method operator pathPosition=(integer i){
            if (0 <= i && i <= (p_path.size()-1))
                p_pathPosition = i;
            else
                debug BJDebugMsg("Patrol: operator pathPosition=: " + ": instance " + I2S(this) + " tries to apply an invalid pathPosition.");
        }
       
        method operator guardDistance() -> real{
            return p_guardDistance;
        }
        method operator guardDistance=(real r){
            p_guardDistance = r;
        }
       
        method operator order()-> PatrolOrder{
            return p_order;
        }
        method operator order=(PatrolOrder p){
            if (p == PatrolOrder.MOVE || p == PatrolOrder.ATTACK){
                if(p == PatrolOrder.ATTACK && p_order != p){
                    guardHandler = CreateTrigger();
                    TriggerAddCondition(guardHandler, Condition(function thistype.onTargetAquired));
                    TriggerRegisterUnitEvent(guardHandler, p_u, EVENT_UNIT_ACQUIRED_TARGET);
                }
                else if(p_order == PatrolOrder.ATTACK && p == PatrolOrder.MOVE){
                    DestroyTrigger(p_handler);
                    p_handler = null;
                }
                p_order = p;
            }
            else{
                debug BJDebugMsg("Patrol: operator order=: " + ": instance " + I2S(this) + " tries to apply an invalid PatrolOrder.");
            }
        }
       
        method registerPatrolUnit(boolexpr be) -> triggercondition{
            if(p_handler == null){
                p_handler = CreateTrigger();
            }
            return TriggerAddCondition(p_handler, be);
        }
        method unregisterPatrolUnit(triggercondition tc){
            TriggerRemoveCondition(p_handler, tc);
        }
       
        method operator enabled() -> boolean{
            return p_enabled;
        }
        method operator enabled=(boolean flag){
            if(flag && !(p_enabled)){
                TimerStart(clock, INTERVAL, true, function thistype.callback);
                if(p_order == PatrolOrder.ATTACK){
                    EnableTrigger(guardHandler);
                }
            }
            else if(!(flag) && p_enabled){
                PauseTimer(clock);
                if(p_order == PatrolOrder.ATTACK){
                    DisableTrigger(guardHandler);
                }
            }
            p_enabled = flag;
        }
       
        static method operator enabledAll=(boolean flag){
            unit fog;
            fog = FirstOfGroup(Group);
            while(fog != null){
                thistype(GetUnitId(fog)).enabled = flag;
                GroupAddUnit(Backup,fog);
                GroupRemoveUnit(Group,fog);
                fog = FirstOfGroup(Group);
            }
            Temp = Group;
            Group = Backup;
            Backup = Temp;
        }
       
        static method operator[](unit u)-> thistype{
            if(!IsUnitInGroup(u, Group)){
                debug BJDebugMsg("Patrol: operator []: " + ": unit with id " + I2S(GetHandleId(u)) + " is not a PatrolUnit yet.");
                return 0;
            }
            return GetUnitId(u);
        }
       
        static method register(boolexpr be) -> triggercondition{
            return TriggerAddCondition(thistype.handler, be);
        }
        static method unregister(triggercondition tc){
            TriggerRemoveCondition(thistype.handler, tc);
        }
       
        static method operator currentPatrolUnit()->thistype{
            return p_patrolUnit;
        }
       
        // wow, static if's aren't allowed globaly in Zinc :(
        private static method onDeindex()->boolean{
            if (IsUnitInGroup(GetIndexedUnit(), Group)) {
                thistype(GetIndexedUnitId()).destroy();
            }
            return false;
        }
       
        // wow, static if's still aren't allowed globaly in Zinc :(
        private static method onInit(){
            OnUnitDeindex(function thistype.onDeindex);
        }
   
    } // end of struct
   
    public function PatrolPathHardDestroy (PatrolPath p){
        integer i = p.size() - 1;
        while(i >= 0){
            p[i].destroy();
            i = i - 1;
        }
        p.destroy();
    }
}
//! endzinc

 


Code (vJASS):

//! zinc
library PatrolGUI requires Patrol /* v1.1 -- hiveworkshop.com/threads/patrol-waypoint-system.276391/ */{

    PatrolPath tempPath = 0;
    Table table;
 
    function onRegister(){
        if(GetHandleId(udg_PATROL_Point) == 0){
            debug BJDebugMsg("ERROR: PatrolGUI - Register - Point is invalid or does not exist.");
            return;
        }
        if (tempPath == 0)
            tempPath = PatrolPath.create();
        tempPath.push(PatrolPoint.create(GetLocationX(udg_PATROL_Point), GetLocationY(udg_PATROL_Point)));
    }

    function onCreate(){
        if(udg_PATROL_Order != udg_PATROL_ORDER_MOVE && udg_PATROL_Order != udg_PATROL_ORDER_ATTACK){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - Invalid PatrolOrder");
            return;
        }
        if(udg_PATROL_PatrolType < 1 || udg_PATROL_PatrolType > 3){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - Invalid PatrolType.");
            return;
        }
        if(tempPath == 0){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - No Points are registered.");
            return;
        }
 
        PatrolUnit.create(udg_PATROL_Unit, tempPath, udg_PATROL_PatrolType, OrderId(udg_PATROL_Order), udg_PATROL_GuardDistance, false).enabled = udg_PATROL_Enabled;
        table.integer[GetHandleId(udg_PATROL_Unit)] = tempPath;
        tempPath = 0;
    }
 
    function onDestroy(){
        integer id = GetHandleId(udg_PATROL_Unit);
        if(table.integer.has(id)){
            PatrolPathHardDestroy(PatrolPath(table.integer[id]));
            table.integer.remove(id);
        }
        else
            debug BJDebugMsg("ERROR: PatrolGUI - Destroy - Unit is no PatrolUnit.");
    }
 
    function onGet(){
        PatrolUnit this = PatrolUnit[udg_PATROL_Unit];
        udg_PATROL_Enabled       = this.enabled;
        udg_PATROL_PatrolType    = this.patrolType;
        udg_PATROL_Order         = OrderId2String(this.order);
        udg_PATROL_GuardDistance = this.guardDistance;
    }
 
    function onSet(){
        PatrolUnit this;
 
        if(udg_PATROL_Order != udg_PATROL_ORDER_MOVE && udg_PATROL_Order != udg_PATROL_ORDER_ATTACK){
            debug BJDebugMsg("ERROR: PatrolGUI - SetData - Invalid PatrolOrder");
            return;
        }
        if(udg_PATROL_PatrolType < 1 || udg_PATROL_PatrolType > 3){
            debug BJDebugMsg("ERROR: PatrolGUI - SetData - Invalid PatrolType.");
            return;
        }
 
        this = PatrolUnit[udg_PATROL_Unit];
        this.enabled       = udg_PATROL_Enabled;
        this.patrolType    = udg_PATROL_PatrolType;
        this.order         = OrderId(udg_PATROL_Order);
        this.guardDistance = udg_PATROL_GuardDistance;
    }
 
    function init(){
        udg_PATROL_TYPE_NORMAL   = PatrolType.NORMAL;
        udg_PATROL_TYPE_BACKWARD = PatrolType.BACKWARD;
        udg_PATROL_TYPE_LOOP     = PatrolType.LOOP;
 
        udg_PATROL_ORDER_MOVE   = "move";
        udg_PATROL_ORDER_ATTACK = "attack";
 
        DestroyTimer(GetExpiredTimer());
    }
 
    function onInit(){
        table = Table.create();
        TimerStart(CreateTimer(), 0, false, function init);
        udg_PATROL_Create        = CreateTrigger();
        udg_PATROL_Destroy       = CreateTrigger();
        udg_PATROL_RegisterPoint = CreateTrigger();
        udg_PATROL_GetData       = CreateTrigger();
        udg_PATROL_SetData       = CreateTrigger();
 
        TriggerAddAction(udg_PATROL_Create, function onCreate);
        TriggerAddAction(udg_PATROL_RegisterPoint, function onRegister);
        TriggerAddAction(udg_PATROL_Destroy, function onDestroy);
        TriggerAddAction(udg_PATROL_GetData, function onGet);
        TriggerAddAction(udg_PATROL_SetData, function onSet);
    }
}
//! endzinc
 


Changelog

v1.4 to v1.5:
- unit collision is no longer considered
- formated movement is now allowed


Keywords:
Patrol, Waypoint, MUI, system, jass, vjass, point, walk
Previews
Contents

Patrol System (Map)

Reviews
Moderator
19:40, 15th Mar 2016 BPower: Almia mentioned a few important things. I'll write a review when soon TM takes place.
Wietlol
API Madness:static method register(boolexpr be) returns triggercondition static method unregister(triggercondition tc) register can take in a "code" variable instead (as long as the functions arent inlined) Optimization:private timer clock; clock...
Wietlol
I suppose we can trust the user to not mess up :D Status: Approved
  1. 19:40, 15th Mar 2016
    BPower: Almia mentioned a few important things.
    I'll write a review when soon TM takes place.
     
    Last edited by a moderator: Jul 12, 2016
  2. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Q: Do I need to null variables in the onDestory method?

    Never considered it might be needed.
     
  3. WereElf

    WereElf

    Joined:
    Jan 2, 2016
    Messages:
    953
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Ummmmmm....
    Have you ever tried to press the patrol button, hold shift, then select several points??
    Doesn't seem like you have :p
     
  4. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Nope, I haven't.

    Still's useful though since you can't make AI patrol since you can't force them to press shift. Can't make player owned units do it through triggers either.
    To my knowledge anyway.

    • Game - Force Player 1 (Red) to press the key A


    Not sure if shift works here. If it does, I'll delete this.

    edit: I tried
    • Melee Initialization
      • Events
        • Player - Player 1 (Red) skips a cinematic sequence
      • Conditions
      • Actions
        • Game - Force Player 1 (Red) to press the key SHIFT
        • Unit - Order Footman 0000 <gen> to Patrol To (Center of Region 001 <gen>)
        • Unit - Order Footman 0000 <gen> to Patrol To (Center of Region 002 <gen>)
        • Unit - Order Footman 0000 <gen> to Patrol To (Center of Region 000 <gen>)


    And it did not work.
     
  5. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,861
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    may i ask? why 0.03 instead of 0.031250000 on loop speed?

    also, just don't Sqrt the dist, instead just compare it like
    dist < range*range


    using OrderById is much better than just using strings. (attack = 851983, Order Ids)

    I think that using Table instead of pseudo-2d array(because you know, that's an attribute and you use arrays to it) for dx and dy is much better and it also gives a more waypoints.
    Code (vJASS):
    real cx = GetUnitX(u);
    real cy = GetUnitY(u);
    real tx = dx[current] - cx;
    real ty = dy[current] - cy;

    ->
    Code (vJASS):
    real tx = dx[current] - GetUnitX(u);
    real ty = dy[current] -  GetUnitY(u);


    and also a suggestion: how about using just 1 timer and then just use a list to iterate all the units. also how about Unit Indexer :)

    and for an optional lib : Alloc libs

    [edit]
    Some parts of the code should have an access modifier to it. e.g. Update should be private.
     
  6. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    • Because I am used to it in GUI I suppose. I will change it.
    • Clever, will do.
    • Never used Ids before but I will try to apply it.
    • Consider it done
    • Rejected :D
    • In zinc everything is private by default.
     
  7. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,861
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Performance wise, you will :D
     
  8. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    A big no in my opinion. This will greatly limit the number of parallel instances to 82. Also that means each 'Patrol' is limited to 99 waypoints.
    Workarounds would be:
    - Use a hashtable's parent key as
    this
    and child key as
    count

    - Use TableArray (so you can save a hashtable space)
    - Use another struct for the waypoints and access them via linked-list.
    EDITED PART:
    ( - Or follow Almia's suggestion using a Table struct member)


    I personally don't like the 1 Timer per instance. Why not use a single static timer where each timeout will pick all instance? (Note: You'll have to use a linked-list to pick all instance, pausing will remove it from the list, resuming/starting add it to the list)


    No because those are global variables (in an array) and according to Memory Leaks, global agent type leak is negligible. Personally, I still null it because reasons.


    You forgot to convert to orderid in
    method start
     
  9. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I am aware of the 99 waypoints. But I got no clue why there would be 82 instances at max.

    If I use a hashtable I could make the entire spell based on a hashtable.
    I suppose it's fine if I use table though since it does not take up a slot.
    Still feels wrong to use both hashtables and structs.

    And yes I could use a linked list but I don't like those. Then we're getting into what I hate the most. A resource that requires resources which requires more resources.
    Vector<T> for example.
     
    Last edited: Apr 24, 2016
  10. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    http://www.wc3c.net/vexorian/jasshelpermanual.html#arrmemb

    No it's not. I do that all the time.

    If you don't want it to require a linked-list resource, just manually implement an internal linked-list yourself, that's what I do. Also, if you do that, you will no longer need TimerUtils.
     
  11. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Okay fairly large update, the system now use a single timer and TimerUtils is now optional instead of required.
     
  12. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,861
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Please provide an API.

    also this:
    Code (vJASS):

    private static method deIndex(integer i)
                {
                    thistype this = thistype(i);
                    instances[i].deallocate();
                    instances[i] = instances[max];
                    max -= 1;
                    static if(LIBRARY_TimerUtils)
                    {
                        ReleaseTimer(t);
                    }
                    else
                    {
                        DestroyTimer(t);
                    }
                    u = null;
                }

    I would like to point out some things:
    - On what part of the system do you use this?
    - Instead of integer i, why not thistype i? or just use thistype this = i? (integer = struct instances)
     
  13. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Wops that method should not be private.
    It should be used to stop a unit from patrolling rather than pausing it.
     
  14. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,861
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    rename it as
    destroy

    deIndex is not that user-friendly :) (in terms of meanings)
     
  15. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Fair enough will do soon TM.
     
  16. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I noticed the destroy method was useless thus I removed it.
    Simple API added.
     
  17. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,491
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Sorry for the super long pending status. The system is pretty simple, but it can maybe find some use for RPG maps or cinematics. Here are just a few things I would like for you to fix:
    • I think the API should be moved to the top of the actual library code. Having it in a seperate trigger is just... more work. I thought lazy people find the easiest way of doing things!
    • A small description of what detectRange might be useful for some users. I did not know what this was until i read the Update method
    • (dist < detectRange * detectRange)
      I think this should be less than or equal to; not just less than.
    • All though I am aware that the order id 851983 is attack, random numbers look ugly in code. Could you add it as a configurable? An even better solution is to allow users to decide which order a unit will issue on each waypoint
    • I think method Pause and start can be combined if it takes a boolean as a parameter
    • Why in heavens name is there no destroy method?! The instance count is going to just keep climbing up with no recycling what so ever. You should be deallocating instances if a unit no longer has waypoints, dead, or is no longer in the game. All though the Pause method sort of acts as destroy, it seems really useless if a user wanted to remove all waypoints of a unit's instance. The loop will constantly keep iterating over it when it does not have to anymore
    Most of what I listed are really suggestions, but I would really like for you to add the destroy method. The Pause method seems like a really cheap way of doing it. Set to Awaiting Update.
     
    Last edited: Jul 12, 2016
  18. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,224
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    For
    real
    s it doesn't matter.
     
  19. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,573
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I'll hopefully remember to do some updating today..