• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Advanced Combat and Movement System v 1.3

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Advanced Combat and Movement System
v 1.3 by 3yeballz




Credits and required systems


What do I need to use this system in my map?
To use this system in your map, you need the JNGP and the current jasshelper version.
You also require the systems, that are listed above.
Since the system is quite big, you also need some time to implement it and a bit of (v)Jass knowledge would be good.


What do I need to know about the system, when I use it in my map?
There are many things to know about this system since it is not just an arrow key system.
It can do much more.

You can make your character
jump by using an instant spell.
It is really important that the dummy skill is instant and that it has no target. That means you do not choose the target location of the jump. The jump direction is the current facing of the unit and the jump speed is the current movement speed of the unit.
The system ensures that the landing location of the unit is walkable and you do not have control of your character while it is in the air. The jump speed is constant. That means if you try to jump at any unwalkable location, the nearest walkable location is computed and a new jump speed will be applied. When your character lands, it will not stuck, even if any other unit stands at the landing location.

Since I do not like to use arrow keys and the mouse both, I tried to make something like a
combat system. It is unique but there are many things you need to know, if you want to use it.
You need a unit that sells other units, that trigger the abilitys from this system.
That means you cannot select the original unit, that is controlled with the arrow keys.
It is important to find an easy way to switch selection between the selling unit and the original unit like pressing escape.


How can I implement this system in my map?
Copy+Paste this trigger in your map. Make sure that you have all requirements.

If you do not use the combat system and jump ability, you do not need anything else. You just have to use the functions of this system to register the units. Every player can only have one unit registered at the same time.

If you do not use the combat system but you want to use the jump ability, then you need a skill based on one of the following spells: Locust Swarm, Starfall, Fan of Knives, Windwalk, Berserk. Maybe there are some other spells that you could use too. Change the global constant variable JUMPID to the rawcode of your new spell. Now you need a dummy unit that checks if a location is walkable by the registered unit. This unit must have walking type NONE and the collision size that you wish. You have to change the collision size of the registered unit to 0. Even if it is 0, that does not mean that your unit has no collision size. The collision detection unit is always at the location of the registered unit. Make sure that you register the correct collision detection unit when you register the original unit. It is explained in the API.

If you want to use the combat system (and I hope that you want to) then you require some more stuff. Like already explained you need a dummy unit that sells other units that trigger the abilitys of this system. You need at least 4-5 dummy units that can be sold.
Those are the functions of the sold units: Select next target, Select next friendly target, Unselect all targets, Start/Stop attacking mode, Jump ability (optional).
Go to the global constants area and change all raw codes. Now the jump ability does not need the rawcode of an ability but the rawcode of the sold unit.


Explanation of the arrow key system
First I want to mention that if you play in battle net there is a standard delay of
250ms. If you play on LAN it is about 100ms (not sure). To reduce the delay, it is important that the host uses a hosting program like GHost or LC.

After a unit is registered for a player, he has full control of it, even if he is not the owner. Now you can use up, down, left, right to walk/change facing. If you walk forward, the unit changes its facing by pressing left or right, but if you walk backward it does not change its facing. Instead of that it walks sidewards. If you doublepress left or right, your character can walk sidewards too. If you doublepress down, your character changes its facing by 180°. If you doublepress up, your character starts running.
If you collide with unwalkable terrain or another unit, you slide along them. Items are ignored.


Explanation of the jump system
When you use the jump ability, the system calculates the landing point. It depends on
the current movement speed, direction, z-axis difference and if the terrain is walkable.
That means you can jump much further if you jump down from a hill, but you cannot jump very far if you jump up a hill. The jump speed is constant. That means after calculating the landing point, your character can change its speed if it is blocked by a unit or unwalkable terrain. You can jump over unwalkable terrain or other units. You do not have any control of your character while he is jumping.


Explanation of the combat system
Like already explained you need a selling unit. There are 4 important abilitys for the combat system.
One to change your target and one to change your friendly target. If you use one of them it checks the nearest unit. If there are more units near you, already targeted units will be ignored until there is no other unit, that was not targeted yet. It stores the order of the targeted units and starts at the beginning after not finding a new unit.
There is also an ability to unselect all friendly and harmful target. It also deletes the order.
The last ability is to start/stop attacking mode. While your unit is in attacking mode, it moves circular around the selected target. There are two important effects to detect your current target: COMBAT_ON and COMBAT_OFF.


Changelog
v 1.0First release

v 1.0.1
Fixed a problem when walking sidewards

v 1.0.2
Replaced KeyTimers with TimerUtils + implemented function IsUnitRegisteredArrowKey

v 1.0.3
Fixed a problem using mouse events + implemented function IsUnitJumping

v 1.1
Jump ability is now considering z-axis

Fixed a bug using doubleclick down while moving sidewards, now disabled

v 1.2
Combat mode, function GetRegisteredUnitArrowKey, function CanUnitJump, function CanUnitJumpByPlayer

Changed name to "Advanded Combat and Movement System"

v 1.2.1
function GetUnitTarget, function GetUnitTargetByPlayer, using some more static ifs

Fixed a bug when unregistering a unit while jumping or walking

v 1.2.2
Using T32 for the main periodic method instead of TimerUtils

Removed Jump periodic method since T32 only allows one periodic method per struct

Fixed a bug to detect new target when using mouse events

Move and animation speed constants now are just a default value

function SetMoveSpeed, function SetMoveSpeedByPlayer

function SetAnimSpeed, function SetAnimSpeedByPlayer

v 1.2.3
Fixed a bug when jumping sidewards in attack mode

Arrows to detect target in combat mode are only shown to local player

Functions to set animation and move speed now work properly

If a targeted unit dies, all target data will automatically reset

function SetUnitXArrowKey, function SetUnitXByPlayer, function SetUnitYArrowKey, function SetUnitYByPlayer

function SetUnitFacingArrowKey, function SetUnitFacingByPlayer, function ResetTarget, function ResetTargetByPlayer

v 1.2.4
Added an ability to select friendly targets

function GetUnitFTarget, function GetUnitFTargetByPlayer

main struct, move and animation speed instance variables now are public

v 1.3
shortened names, improved documentation

jumpfacing initialized in radiant

You can no longer move or jump while dead

Using a trigger instead of periodic loop to check if target is dead

Using static location instead of local

function DisableMove, function DisableMoveByPlayer


Code
JASS:
library CAMS requires T32, TimerUtils
/*
                            *********************************************
                            *    Advanced Combat and Movement System    *
                            *            v 1.3 by 3yeballz              *
                            *********************************************
    
    ***************************************************************************************************************
    *    latest version:                www.hiveworkshop.com/forums/spells-569/a-191033/                          *
    *    Timer32 by Jesus4Lyf:          www.thehelper.net/forums/showthread.php/132538-Timer32                    *
    *    TimerUtils by Vexorian:        www.wc3c.net/showthread.php?t=101322                                      *
    ***************************************************************************************************************

******************************************************* API *******************************************************************
*
*    function RegisterUnitArrowKey takes unit u, player p, integer anim, boolean jump, integer unitid, integer jumpanim, real atkrange returns nothing
*          u: unit that should register in the system
*          p: owner of registered unit
*          anim: animation id for walking
*          jump: enable/disable jump ability
*          unitid: rawcode of collision detection unit. only necessary if jump == true. set to 0 if not needed
*          jumpanim: animation id for jumping. only necessary if jump == true. set to 0 if not needed
*          atkrange : attack range of the unit. attack range in object editor must be bigger. only necessary if you use combat mode
*
*    function UnregisterUnitArrowKey takes player p returns nothing
*          Unregisters the currently registered unit for player p.
*          You MUST unregister a unit before registering a new one.
*
*    function GetRegisteredUnitArrowKey takes player p returns unit
*          Returns the registered unit from player p.
*          If there is no unit registered it returns null.
*
*    function GetUnitTarget takes unit u returns unit
*          Returns the targeted unit while using the combat system.
*          Returns null if unit u is not registered.
*
*    function GetUnitTargetByPlayer takes player p returns unit
*          Returns the targeted unit by the currently registered unit from player p.
*          Returns null if there is no registered unit for player p.
*
*    function GetUnitFTarget takes unit u returns unit
*          Returns the friendly targeted unit while using the combat system.
*          Returns null if unit u is not registered.
*
*    function GetUnitFTargetByPlayer takes player p returns unit
*          Returns the friendly targeted unit by the currently registered unit from player p.
*          Returns null if there is no registered unit for player p.
*
*    function IsUnitRegisteredArrowKey takes unit u returns boolean
*          Use this to check if a unit is registered in the system.
*
*    function IsUnitJumping takes unit u returns boolean
*          Returns true if the unit is jumping and if it is the registered one.
*          You should only unregister a unit if it's not jumping.
*
*    function CanUnitJump takes unit u returns boolean
*          Returns true if the unit is able to jump.
*          Returns false if it's not the registered one.
*
*    function CanUnitJumpByPlayer takes player p returns boolean
*          Returns true if the registered unit by player p is able to jump.
*          Returns false if there is no unit registered.
*
*    function SetMoveSpeed takes unit u, real r1, real r2, real r3, real r4, real t returns nothing
*          Changes the move speed of unit u to r1.
*          r2: move speed side, r3: move speed back (must be negative), r4: move speed run
*          t: turn speed in degrees per interval
*          Input of 0 will be ignored. Does nothing if unit u is not registered.
*
*    function SetMoveSpeedByPlayer takes player p, real r1, real r2, real r3, real r4, real t returns nothing
*          Changes the move speed of player p's registered unit to r1.
*          r2: move speed side, r3: move speed back (must be negative), r4: move speed run
*          t: turn speed in degrees per interval
*          Input of 0 will be ignored. Does nothing if there is no unit registered for player p.
*
*    function SetAnimSpeed takes unit u, real r1, real r2, real r3, real r4 returns nothing
*          Changes the animation speed of unit u to r1.
*          r2: animation speed side, r3: animation speed back, r4: animation speed run
*          Input of 0 will be ignored. Does nothing if unit u is not registered.
*
*    function SetAnimSpeedByPlayer takes player p, real r1, real r2, real r3, real r4 returns nothing
*          Changes the animation speed of player p's registered unit to r1.
*          r2: animation speed side, r3: animation speed back, r4: animation speed run
*          Input of 0 will be ignored. Does nothing if there is no unit registered for player p.
*
*    function SetUnitXArrowKey takes unit u, real r returns nothing
*          Sets the x-location of unit u, if it is registered.
*
*    function SetUnitXByPlayer takes player p, real r returns nothing
*          Sets the x-location of player p's registered unit, if there is a unit registered.
*
*    function SetUnitYArrowKey takes unit u, real r returns nothing
*          Sets the y-location of unit u, if it is registered.
*
*    function SetUnitYByPlayer takes player p, real r returns nothing
*          Sets the y-location of player p's registered unit, if there is a unit registered.
*
*    function SetUnitFacingArrowKey takes unit u, real r returns nothing
*          Sets the facing of unit u, if it is registered.
*
*    function SetUnitFacingByPlayer takes player p, real r returns nothing
*          Sets the facing of player p's registered unit, if there is a unit registered.
*
*    function ResetTarget takes unit u returns nothing
*          Resets all target data of unit u, if it is registered.
*
*    function ResetTargetByPlayer takes player p returns nothing
*          Resets all target data of player p's registered unit, if there is a unit registered.
*
*    function DisableMove takes unit u, real duration returns nothing
*          Disables the ability to move for unit u for a given period of time
*
*    function DisableMoveByPlayer takes player p, real duration returns nothing
*          Disables the ability to move for player p's registered unit for a given period of time
*
*******************************************************************************************************************************

************************************************ User Configuration **********************************************************/

    globals
        // if you want the system to deal with the whole movement then set this to true
        // notice that your character cannot e.g. move to a target if it detects one
        // and you cannot use any rightclicks on the ground or target
        private constant boolean NOMOUSE        = true
   
        // enables/disables combat mode and the use of all abilities that go with it
        private constant boolean COMBAT_MODE    = true
    
        // the timer interval to change facing
        private constant real    INTERVAL          = 0.03125
        
        // the default turn speed in degrees per interval
        private constant real    TURN_SPEED        = 4.
        
        // the default move speed per interval. unit's movement speed is MOVE_SPEED/INTERVAL
        private constant real    MOVE_SPEED        = 9.
        private constant real    MOVE_SPEED_SIDE   = 8.
        private constant real    MOVE_SPEED_BACK   = -6.
        private constant real    MOVE_SPEED_RUN    = 12.
        
        // default animation speed for different movement states
        private constant real    ANIM_SPEED        = 1.
        private constant real    ANIM_SPEED_SIDE   = 0.7
        private constant real    ANIM_SPEED_BACK   = -0.6
        private constant real    ANIM_SPEED_RUN    = 1.3
        
        // the angle facing change when moving sidewards + up/down
        // the bigger the value the less you can move up/down while moving sidewards
        // must be between 0 and 90
        private constant real    LEFT_SIDE_FACING  = -45.
        private constant real    RIGHT_SIDE_FACING = 45.
        
        // maximum interval to detect a doubleclick
        private constant real    DBLCLICKTIME      = 0.3
        
        // those constants are only important if you use the jump ability
            // if COMBAT_MODE: rawcode of sold unit to trigger jump
            // else: rawcode for jump ability
            private constant integer JUMPID          = 'e001'
            
            // maximum distance tolerance to detect landing point, the lower the more accurate
            // should be bigger than 1 to work correctly
            private constant real    JUMPTOLERANCE   = 3.
            
            // default callback count for one jump
            // increasing this will increase the time in air
            private constant integer JUMPCOUNT       = 40
            
            // default jump height after JUMPCOUNT/2 callbacks. local maximum of parabola
            private constant real    JUMPHEIGHT      = 220.
            
        // combat mode related constants
            // rawcode of sold unit to trigger selection
            private constant integer SELECTID        = 'e003'
            
            // rawcode of sold unit to trigger friendly selection
            private constant integer FSELECTID       = 'e006'
            
            // rawcode of sold unit to trigger unselection
            private constant integer UNSELECTID      = 'e004'
            
            // rawcode of sold unit to trigger attack on/off
            private constant integer ATTACKID        = 'e005'
            
            // maximum number of targets to switch
            private constant integer ARRAY_SIZE      = 100
            
            // maximum distance to check for new enemies
            private constant real    COMBATRADIUS    = 1400.
            
            // string used to detect current target while attack mode off
            private constant string  COMBAT_ON       = "Abilities\\Spells\\Orc\\SpiritLink\\SpiritLinkTarget.mdl"
            
            // string used to detect current target while attack mode on
            private constant string  COMBAT_OFF      = "war3mapImported\\Haste.mdx"
            
            // attachment point for the effect to detect current target
            private constant string  ATTACHPNT       = "chest"
            
    endglobals
    
/*************************************************** System Code *************************************************************/
    
    private module CAMSInit
        private static method onInit takes nothing returns nothing
            local trigger t1 = CreateTrigger()
            local trigger t2 = CreateTrigger()
            local trigger t3 = CreateTrigger()
            local trigger t4 = CreateTrigger()
            local trigger t5 = CreateTrigger()
            local trigger t6 = CreateTrigger()
            local trigger t7 = CreateTrigger()
            local trigger t8 = CreateTrigger()
            local trigger t9 = CreateTrigger()
            static if COMBAT_MODE then
                local trigger t10 = CreateTrigger()
                local trigger t11 = CreateTrigger()
                local trigger t12 = CreateTrigger()
                local trigger t13 = CreateTrigger()
            endif
            local integer i = 0
            loop
                exitwhen i > 11
                    if GetPlayerSlotState(Player(i))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i))==MAP_CONTROL_USER then
                        call TriggerRegisterPlayerEvent(t1, Player(i), EVENT_PLAYER_ARROW_UP_DOWN)            
                        call TriggerRegisterPlayerEvent(t2, Player(i), EVENT_PLAYER_ARROW_UP_UP)            
                        call TriggerRegisterPlayerEvent(t3, Player(i), EVENT_PLAYER_ARROW_LEFT_DOWN)            
                        call TriggerRegisterPlayerEvent(t4, Player(i), EVENT_PLAYER_ARROW_LEFT_UP)            
                        call TriggerRegisterPlayerEvent(t5, Player(i), EVENT_PLAYER_ARROW_RIGHT_DOWN)            
                        call TriggerRegisterPlayerEvent(t6, Player(i), EVENT_PLAYER_ARROW_RIGHT_UP)            
                        call TriggerRegisterPlayerEvent(t7, Player(i), EVENT_PLAYER_ARROW_DOWN_DOWN)            
                        call TriggerRegisterPlayerEvent(t8, Player(i), EVENT_PLAYER_ARROW_DOWN_UP) 
                        static if COMBAT_MODE then
                            call TriggerRegisterPlayerUnitEvent(t9, Player(i), EVENT_PLAYER_UNIT_SELL, null)
                            call TriggerRegisterPlayerUnitEvent(t10, Player(i), EVENT_PLAYER_UNIT_SELL, null)
                            call TriggerRegisterPlayerUnitEvent(t11, Player(i), EVENT_PLAYER_UNIT_SELL, null)
                            call TriggerRegisterPlayerUnitEvent(t12, Player(i), EVENT_PLAYER_UNIT_SELL, null)
                            call TriggerRegisterPlayerUnitEvent(t13, Player(i), EVENT_PLAYER_UNIT_SELL, null)
                        else
                            call TriggerRegisterPlayerUnitEvent(t9, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
                        endif
                    endif
                set i = i + 1
            endloop
            call TriggerAddCondition(t1, Filter(function thistype.UpDown))
            call TriggerAddCondition(t2, Filter(function thistype.UpUp))
            call TriggerAddCondition(t3, Filter(function thistype.LeftDown))
            call TriggerAddCondition(t4, Filter(function thistype.LeftUp))
            call TriggerAddCondition(t5, Filter(function thistype.RightDown))
            call TriggerAddCondition(t6, Filter(function thistype.RightUp))
            call TriggerAddCondition(t7, Filter(function thistype.DownDown))
            call TriggerAddCondition(t8, Filter(function thistype.DownUp))
            call TriggerAddCondition(t9, Filter(function thistype.JumpInit))
            static if COMBAT_MODE then
                call TriggerAddCondition(t10, Filter(function thistype.SelectInit))
                call TriggerAddCondition(t11, Filter(function thistype.FSelectInit))
                call TriggerAddCondition(t12, Filter(function thistype.Unselect))
                call TriggerAddCondition(t13, Filter(function thistype.StartAttack))
            endif
            set t1 = null
            set t2 = null
            set t3 = null
            set t4 = null
            set t5 = null
            set t6 = null
            set t7 = null
            set t8 = null
            set t9 = null
            static if COMBAT_MODE then
                set t10 = null
                set t11 = null
                set t12 = null
                set t13 = null
                set cfilter = Filter(function thistype.CheckForNewEnemy)
                set fcfilter = Filter(function thistype.CheckForNewAlly)
                set tfilter = Filter(function thistype.TargetDies)
                set ftfilter = Filter(function thistype.FTargetDies)
            endif
            static if NOMOUSE then
                set filter = Filter(function thistype.DisableMove)
            else
                set filter = Filter(function thistype.NewOrder)
            endif
            set dfilter = Filter(function thistype.UnitDies)
            set rfilter = Filter(function thistype.UnitRevives)
            static if LIBRARY_Event then
                set MOVE = CreateEvent()
            endif
        endmethod
    endmodule

    struct CAMS
        private static boolexpr filter
        private static boolexpr dfilter
        private static boolexpr rfilter
        private static group    g = CreateGroup()
        private static boolean  dblclicked = false
        private static real     newx = 0.
        private static real     newy = 0.
        private static real     newz = 0.
        private static real     jumpfactor = JUMPHEIGHT / (JUMPCOUNT/2 * JUMPCOUNT/2)
        private static boolean  boolx = false
        private static boolean  booly = false
        private static integer  playerid = 0
        private static location Loc = Location(0,0)
        static if COMBAT_MODE then
            private static real newfacing = 0.
            private static boolexpr cfilter
            private static boolexpr fcfilter
            private static boolexpr tfilter
            private static boolexpr ftfilter
            private static unit     selectedunit = null
            private static integer  selectcount = 0
            private static unit array selectable [8191]
        endif
        
        // general boolean instance variables
        readonly boolean inuse
        private boolean up
        private boolean left
        private boolean right
        private boolean down
        private boolean break
        private boolean disabled
        
        // movement system instance variables
        private unit u
        private player p
        private integer anim
        private real x
        private real y
        private real z
        private real facing
        readonly real speed
        readonly real ms
        readonly real mss
        readonly real msb
        readonly real msr
        readonly real ts
        readonly real as
        readonly real ass
        readonly real asb
        readonly real asr
        static if not NOMOUSE then
            private boolean order
        endif
        private boolean running
        private boolean sidewards
        private real sfacing
        private trigger trg
        private trigger death
        private trigger revive
        
        // jump system instance variables
        private unit cunit
        private boolean jump
        private boolean isjumping
        private integer janim
        private real jx
        private real jy
        private real jh
        private real jf
        private integer jc
        private integer jcmax
        
        // combat system instance variables
        static if COMBAT_MODE then
            private unit target
            private unit ftarget
            private unit array targeted [ARRAY_SIZE]
            private unit array ftargeted [ARRAY_SIZE]
            private integer tc
            private integer ftc
            private integer tp
            private integer ftp
            private effect selection
            private boolean atk
            private real atkrange
            private boolean attacking
            private trigger td
            private trigger ftd
        endif
        
        // timer related instance variables
        private timer t
        private timer sidetimer
        private timer dtimer
        private timer dblclick
        private integer timerstate
        // timerstate:
        //          1 -> Up
        //          2 -> Left
        //          3 -> Right
        //          4 -> Down
        
        static if NOMOUSE then
            private static method DisableMove takes nothing returns boolean
                if GetIssuedOrderId() == OrderId("move") or GetIssuedOrderId() == OrderId("smart") or GetIssuedOrderId() == OrderId("attack") then
                    set playerid = GetPlayerId(GetTriggerPlayer())
                    call TimerStart(CreateTimer(), 0., false, function thistype.Order)
                endif
                return false
            endmethod
 
            private static method Order takes nothing returns nothing
                call IssueImmediateOrder(thistype(playerid).u, "stop")
            endmethod
        else
            private static method NewOrder takes nothing returns boolean
                local thistype this
                if GetIssuedOrderId() == OrderId("move") or GetIssuedOrderId() == OrderId("smart") or GetIssuedOrderId() == OrderId("attack") then
                    set this = thistype(GetPlayerId(GetTriggerPlayer())+1)
                    if .isjumping then
                        return false
                    endif
                    set .order = true
                    static if COMBAT_MODE then
                        set .atk = false
                        set .sidewards = false
                        if .attacking then
                            set .target = null
                            set .tc = 1
                            set .tp = 1
                            call DestroyEffect(.selection)
                            set .selection = null
                            set .attacking = false
                        endif
                        if GetOrderTargetUnit() != null then
                            if IsUnitEnemy(GetOrderTargetUnit(), Player(id-1)) then
                                set .target = GetOrderTargetUnit()
                                set .atk = true
                                set .sidewards = true
                                call DestroyEffect(.selection)
                                if GetLocalPlayer() == Player(id-1) then
                                    set .selection = AddSpecialEffectTarget(COMBAT_ON, .target, ATTACHPNT)
                                else
                                    set .selection = AddSpecialEffectTarget("", .target, ATTACHPNT)
                                endif
                            endif
                        endif
                    endif
                endif
                return false
            endmethod
        endif
        
        private static method UnitDies takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(GetTriggerUnit()))+1)
            set .inuse = false
            set .speed = 0.
            set .running = false
            set .sidewards = false
            call SetUnitFlyHeight(.u, 0., 0.)
            set .isjumping = false
            static if COMBAT_MODE then
                if .target != null then
                    set .target = null
                    set .tc = 1
                    set .tp = 1
                    call DestroyEffect(.selection)
                    set .selection = null
                    set .atk = false
                    set .attacking = false
                    call DestroyTrigger(.td)
                    call DestroyTrigger(.ftd)
                endif
                if .ftarget != null then
                    set .ftarget = null
                    set .ftc = 1
                    set .ftp = 1
                endif
            endif
            return false
        endmethod
        
        private static method UnitRevives takes nothing returns boolean
            set thistype(GetPlayerId(GetOwningPlayer(GetTriggerUnit()))+1).inuse = true
            return false
        endmethod
        
        private method periodic takes nothing returns nothing
        
            if not .inuse then
                return
            endif
        
            if .isjumping then
            
                set .jc = .jc + 1
                
                set .x = .x + Cos(.jf) * .speed
                set .y = .y + Sin(.jf) * .speed
                call SetUnitX(.u, .x)
                call SetUnitY(.u, .y)
                call MoveLocation(Loc, .x, .y)
                set .jh = -jumpfactor * (.jc - JUMPCOUNT/2) * (.jc - JUMPCOUNT/2) + JUMPHEIGHT - GetLocationZ(Loc) + .z
                call SetUnitFlyHeight(.u, .jh, 0.)
                
                static if COMBAT_MODE then
                    if .atk then
                        set newx = .x - GetUnitX(.target)
                        set newy = .y - GetUnitY(.target)
                        if .atkrange > SquareRoot(newx*newx+newy*newy) then
                            call DisableTrigger(.trg)
                            call IssueTargetOrder(.u, "attack", .target)
                            call EnableTrigger(.trg)
                            set .attacking = true
                        else
                            set .attacking = false
                            call IssueImmediateOrder(.u, "stop")
                        endif
                    else
                        call IssueImmediateOrder(.u, "stop")
                    endif
                else
                    call IssueImmediateOrder(.u, "stop")
                endif
                
                if .jc == .jcmax then
                    call SetUnitFlyHeight(.u, 0., 0.)
                    call SetUnitPosition(.cunit, .x, .y)
                    set .x = GetUnitX(.cunit)
                    set .y = GetUnitY(.cunit)
                    call SetUnitX(.u, .x)
                    call SetUnitY(.u, .y)
                    set .jc = 0
                    set .isjumping = false
                    //! textmacro InitMovement
                    if .sidewards then
                        if .right then
                            if .up then
                                set .speed = .mss
                                set .sfacing = .facing - RIGHT_SIDE_FACING
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .ass)
                            elseif .down then
                                set .speed = .msb
                                set .sfacing = .facing + RIGHT_SIDE_FACING
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .asb)
                            else
                                set .speed = .mss
                                set .sfacing = .facing - 90.
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .ass)
                            endif
                        elseif .left then
                            if .up then
                                set .speed = .mss
                                set .sfacing = .facing - LEFT_SIDE_FACING
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .ass)
                            elseif .down then
                                set .speed = .msb
                                set .sfacing = .facing + LEFT_SIDE_FACING
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .asb)
                            else
                                set .speed = .mss
                                set .sfacing = .facing + 90.
                                call SetUnitAnimationByIndex(.u, .anim)
                                call SetUnitTimeScale(.u, .ass)
                            endif
                        elseif .up then
                            set .sidewards = false
                            set .speed = .ms
                            call SetUnitAnimationByIndex(.u, .anim)
                            call SetUnitTimeScale(.u, .as)
                        elseif .down then
                            set .sidewards = false
                            set .speed = .msb
                            call SetUnitAnimationByIndex(.u, .anim)
                            call SetUnitTimeScale(.u, .asb)
                        else
                            set .sidewards = false
                            set .speed = 0.
                            call SetUnitAnimation(.u, "stand")
                            call SetUnitTimeScale(.u, .as)
                        endif
                    else
                        if .up then
                            call SetUnitAnimationByIndex(.u, .anim)
                            if .running then
                                set .speed = .msr
                                call SetUnitTimeScale(.u, .asr)
                            else
                                set .speed = .ms
                                call SetUnitTimeScale(.u, .as)
                            endif
                            if .right then
                                set .break = false
                                call TimerStart(.sidetimer, INTERVAL, true, function thistype.RightPeriodic)
                            elseif .left then
                                set .break = false
                                call TimerStart(.sidetimer, INTERVAL, true, function thistype.LeftPeriodic)
                            endif
                        elseif .down then
                            set .speed = .msb
                            call SetUnitAnimationByIndex(.u, .anim)
                            call SetUnitTimeScale(.u, .asb)
                            if .right then
                                set .sidewards = true
                                set .sfacing = .facing + RIGHT_SIDE_FACING
                            elseif .left then
                                set .sidewards = true
                                set .sfacing = .facing + LEFT_SIDE_FACING
                            endif
                        else
                            set .speed = 0.
                            set .running = false
                            call SetUnitAnimation(.u, "stand")
                            if .right then
                                set .break = false
                                call TimerStart(.sidetimer, INTERVAL, true, function thistype.RightPeriodic)
                            elseif .left then
                                set .break = false
                                call TimerStart(.sidetimer, INTERVAL, true, function thistype.LeftPeriodic)
                            endif
                        endif
                    endif
                    if .atk then
                        set .sidewards = true
                    endif
                    //! endtextmacro
                    //! runtextmacro InitMovement()
                endif
            
            else
            
                if .disabled then
                    return
                endif
            
                static if NOMOUSE then
                    //! textmacro Movement
                    static if COMBAT_MODE then
                        if .atk then
                            if .right then
                                if .up then
                                    if .speed < (.mss-0.3) or .speed > (.mss+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .mss
                                    set .sfacing = .facing - RIGHT_SIDE_FACING
                                elseif .down then
                                    if .speed < (.msb-0.3) or .speed > (.msb+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .msb
                                    set .sfacing = .facing + RIGHT_SIDE_FACING
                                else
                                    if .speed < (.mss-0.3) or .speed > (.mss+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .mss
                                    set .sfacing = .facing - 90.
                                endif
                            elseif .left then
                                if .up then
                                    if .speed < (.mss-0.3) or .speed > (.mss+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .mss
                                    set .sfacing = .facing - LEFT_SIDE_FACING
                                elseif .down then
                                    if .speed < (.msb-0.3) or .speed > (.msb+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .msb
                                    set .sfacing = .facing + LEFT_SIDE_FACING
                                else
                                    if .speed < (.mss-0.3) or .speed > (.mss+0.3) then
                                        call SetUnitAnimationByIndex(.u, .anim)
                                        call SetUnitTimeScale(.u, .ass)
                                    endif
                                    set .speed = .mss
                                    set .sfacing = .facing + 90.
                                endif
                            elseif .up then
                                if .speed < (.ms-0.3) or .speed > (.ms+0.3) then
                                    call SetUnitAnimationByIndex(.u, .anim)
                                    call SetUnitTimeScale(.u, .as)
                                endif
                                set .speed = .ms
                                set .sfacing = .facing
                            elseif .down then
                                if .speed < (.msb-0.3) or .speed > (.msb+0.3) then
                                    call SetUnitAnimationByIndex(.u, .anim)
                                    call SetUnitTimeScale(.u, .asb)
                                endif
                                set .speed = .msb
                                set .sfacing = .facing
                            else
                                if .speed < -0.3 or .speed > 0.3 then
                                    call SetUnitAnimation(.u, "stand")
                                    call SetUnitTimeScale(.u, .as)
                                endif
                                set .speed = 0.
                            endif
                            if .speed > 0. or .speed < 0. then
                                set newx = .x + Cos(.sfacing * bj_DEGTORAD) * .speed
                                set newy = .y + Sin(.sfacing * bj_DEGTORAD) * .speed
                                if .jump then
                                    call SetUnitPosition(.cunit, newx, newy)
                                    if (RAbsBJ(GetUnitX(.cunit)-newx) > 0.5) or (RAbsBJ(GetUnitY(.cunit)-newy) > 0.5) then
                                        call SetUnitPosition(.cunit, newx, .y)
                                        set boolx = RAbsBJ(GetUnitX(.cunit)-newx) <= 0.5
                                        call SetUnitPosition(.cunit, .x, newy)
                                        set booly = RAbsBJ(GetUnitY(.cunit)-newy) <= 0.5
                                        if boolx then
                                            set .x = newx
                                        elseif booly then
                                            set .y = newy
                                        endif
                                    else
                                        set .x = newx
                                        set .y = newy
                                    endif
                                    call SetUnitX(.u, .x)
                                    call SetUnitY(.u, .y)
                                else
                                    call SetUnitPosition(.u, newx, newy)
                                    if (RAbsBJ(GetUnitX(.u)-newx) > 0.5) or (RAbsBJ(GetUnitY(.u)-newy) > 0.5) then
                                        call SetUnitPosition(.u, newx, .y)
                                        set boolx = RAbsBJ(GetUnitX(.u)-newx) <= 0.5
                                        call SetUnitPosition(.u, .x, newy)
                                        set booly = RAbsBJ(GetUnitY(.u)-newy) <= 0.5
                                        if boolx then
                                            set .x = newx
                                        elseif booly then
                                            set .y = newy
                                        endif
                                        call SetUnitX(.u, .x)
                                        call SetUnitY(.u, .y)
                                    else
                                        set .x = newx
                                        set .y = newy
                                    endif
                                endif
                            endif
                            set newfacing = Atan2(GetUnitY(.target) - .y, GetUnitX(.target) - .x) * bj_RADTODEG
                            set .sfacing = .sfacing + newfacing - .facing
                            set .facing = newfacing
                            call SetUnitFacing(.u, .facing)
                            
                            set newx = .x - GetUnitX(.target)
                            set newy = .y - GetUnitY(.target)
                            if .atkrange > SquareRoot(newx*newx+newy*newy) then
                                call DisableTrigger(.trg)
                                call IssueTargetOrder(.u, "attack", .target)
                                call EnableTrigger(.trg)
                                call SetUnitTimeScale(.u, .as)
                                set .attacking = true
                            else
                                set .attacking = false
                                call IssueImmediateOrder(.u, "stop")
                            endif
                        else
                            if .speed > 0. or .speed < 0. then
                                if .sidewards then
                                    set newx = .x + Cos(.sfacing * bj_DEGTORAD) * .speed
                                    set newy = .y + Sin(.sfacing * bj_DEGTORAD) * .speed
                                else
                                    set newx = .x + Cos(.facing * bj_DEGTORAD) * .speed
                                    set newy = .y + Sin(.facing * bj_DEGTORAD) * .speed
                                    call SetUnitFacing(.u, .facing)
                                endif
                                if .jump then
                                    call SetUnitPosition(.cunit, newx, newy)
                                    if (RAbsBJ(GetUnitX(.cunit)-newx) > 0.5) or (RAbsBJ(GetUnitY(.cunit)-newy) > 0.5) then
                                        call SetUnitPosition(.cunit, newx, .y)
                                        set boolx = RAbsBJ(GetUnitX(.cunit)-newx) <= 0.5
                                        call SetUnitPosition(.cunit, .x, newy)
                                        set booly = RAbsBJ(GetUnitY(.cunit)-newy) <= 0.5
                                        if boolx then
                                            set .x = newx
                                        elseif booly then
                                            set .y = newy
                                        endif
                                    else
                                        set .x = newx
                                        set .y = newy
                                    endif
                                    call SetUnitX(.u, .x)
                                    call SetUnitY(.u, .y)
                                else
                                    call SetUnitPosition(.u, newx, newy)
                                    if (RAbsBJ(GetUnitX(.u)-newx) > 0.5) or (RAbsBJ(GetUnitY(.u)-newy) > 0.5) then
                                        call SetUnitPosition(.u, newx, .y)
                                        set boolx = RAbsBJ(GetUnitX(.u)-newx) <= 0.5
                                        call SetUnitPosition(.u, .x, newy)
                                        set booly = RAbsBJ(GetUnitY(.u)-newy) <= 0.5
                                        if boolx then
                                            set .x = newx
                                        elseif booly then
                                            set .y = newy
                                        endif
                                        call SetUnitX(.u, .x)
                                        call SetUnitY(.u, .y)
                                    else
                                        set .x = newx
                                        set .y = newy
                                    endif
                                endif
                            else
                                call SetUnitFacing(.u, .facing)
                            endif
                            call IssueImmediateOrder(.u, "stop")
                        endif
                    else
                        if .speed > 0. or .speed < 0. then
                            if .sidewards then
                                set newx = .x + Cos(.sfacing * bj_DEGTORAD) * .speed
                                set newy = .y + Sin(.sfacing * bj_DEGTORAD) * .speed
                            else
                                set newx = .x + Cos(.facing * bj_DEGTORAD) * .speed
                                set newy = .y + Sin(.facing * bj_DEGTORAD) * .speed
                                call SetUnitFacing(.u, .facing)
                            endif
                            if .jump then
                                call SetUnitPosition(.cunit, newx, newy)
                                if (RAbsBJ(GetUnitX(.cunit)-newx) > 0.5) or (RAbsBJ(GetUnitY(.cunit)-newy) > 0.5) then
                                    call SetUnitPosition(.cunit, newx, .y)
                                    set boolx = RAbsBJ(GetUnitX(.cunit)-newx) <= 0.5
                                    call SetUnitPosition(.cunit, .x, newy)
                                    set booly = RAbsBJ(GetUnitY(.cunit)-newy) <= 0.5
                                    if boolx then
                                        set .x = newx
                                    elseif booly then
                                        set .y = newy
                                    endif
                                else
                                    set .x = newx
                                    set .y = newy
                                endif
                                call SetUnitX(.u, .x)
                                call SetUnitY(.u, .y)
                            else
                                call SetUnitPosition(.u, newx, newy)
                                if (RAbsBJ(GetUnitX(.u)-newx) > 0.5) or (RAbsBJ(GetUnitY(.u)-newy) > 0.5) then
                                    call SetUnitPosition(.u, newx, .y)
                                    set boolx = RAbsBJ(GetUnitX(.u)-newx) <= 0.5
                                    call SetUnitPosition(.u, .x, newy)
                                    set booly = RAbsBJ(GetUnitY(.u)-newy) <= 0.5
                                    if boolx then
                                        set .x = newx
                                    elseif booly then
                                        set .y = newy
                                    endif
                                    call SetUnitX(.u, .x)
                                    call SetUnitY(.u, .y)
                                else
                                    set .x = newx
                                    set .y = newy
                                endif
                            endif
                        else
                            call SetUnitFacing(.u, .facing)
                        endif
                        call IssueImmediateOrder(.u, "stop")
                    endif
                    //! endtextmacro
                    //! runtextmacro Movement()
                else
                    if .order then
                        if .jump then
                            set .x = GetUnitX(.u)
                            set .y = GetUnitY(.u)
                            set .facing = GetUnitFacing(.u)
                            call SetUnitX(.cunit, .x)
                            call SetUnitY(.cunit, .y)
                        else
                            set .x = GetUnitX(.u)
                            set .y = GetUnitY(.u)
                            set .facing = GetUnitFacing(.u)
                        endif
                    else
                        //! runtextmacro Movement()
                    endif
                endif
            endif
        endmethod
        
        private static method JumpInit takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(GetTriggerUnit()))+1)
            local real x
            local real y
            local real deltaz
            local real parabolaz
            local real factor = 1.
            local boolean toggle = false
            local boolean increase = false
            local boolean decrease = false
            static if COMBAT_MODE then
                call RemoveUnit(GetSoldUnit())
                if not (.jump and .inuse) then
                    return false
                endif
                //! textmacro JumpInit takes EVENT
                if .disabled then
                    return false
                endif
                if $EVENT$ == JUMPID then
                    if not .isjumping then
                        set .isjumping = true
                        set .break = true
                        static if not NOMOUSE then
                            set .order = false
                            call IssueImmediateOrder(.cunit, "stop")
                        endif
                        call SetUnitAnimationByIndex(.u, .janim)
                        call SetUnitTimeScale(.u, .as)
                        if .sidewards then
                            set newx = .x + Cos(.sfacing * bj_DEGTORAD) * .speed * JUMPCOUNT
                            set newy = .y + Sin(.sfacing * bj_DEGTORAD) * .speed * JUMPCOUNT
                            call SetUnitPosition(.cunit, newx, newy)
                            call MoveLocation(Loc, .x, .y)
                            set .z = GetLocationZ(Loc)
                            call MoveLocation(Loc, newx, newy)
                            set newz = GetLocationZ(Loc)
                            set parabolaz = -jumpfactor * JUMPCOUNT * JUMPCOUNT / 4 + JUMPHEIGHT
                            set deltaz = newz - .z
                            loop
                                if (RAbsBJ(GetUnitX(.cunit)-newx) < JUMPTOLERANCE) and (RAbsBJ(GetUnitY(.cunit)-newy) < JUMPTOLERANCE) then
                                    exitwhen (increase or (not increase and not decrease)) and RAbsBJ(deltaz-parabolaz)<JUMPTOLERANCE
                                    exitwhen decrease and ((deltaz-parabolaz)<JUMPTOLERANCE)
                                endif
                                if deltaz-parabolaz>0. or toggle then
                                    set factor = factor - 0.02
                                    if increase then
                                        set toggle = true
                                    endif
                                    set decrease = true
                                else
                                    set factor = factor + 0.02
                                    if decrease then
                                        set toggle = true
                                        set factor = 0.98
                                    endif
                                    set increase = true
                                endif
                                set newx = .x + Cos(.sfacing * bj_DEGTORAD) * .speed * JUMPCOUNT * factor
                                set newy = .y + Sin(.sfacing * bj_DEGTORAD) * .speed * JUMPCOUNT * factor
                                call SetUnitPosition(.cunit, newx, newy)
                                call MoveLocation(Loc, GetUnitX(.cunit), GetUnitY(.cunit))
                                set newz = GetLocationZ(Loc)
                                set parabolaz = -jumpfactor * (JUMPCOUNT*factor - JUMPCOUNT/2) * (JUMPCOUNT*factor - JUMPCOUNT/2) + JUMPHEIGHT
                                set deltaz = newz - .z
                            endloop
                        else
                            set newx = .x + Cos(.facing * bj_DEGTORAD) * .speed * JUMPCOUNT
                            set newy = .y + Sin(.facing * bj_DEGTORAD) * .speed * JUMPCOUNT
                            call SetUnitPosition(.cunit, newx, newy)
                            call MoveLocation(Loc, .x, .y)
                            set .z = GetLocationZ(Loc)
                            call MoveLocation(Loc, newx, newy)
                            set newz = GetLocationZ(Loc)
                            set parabolaz = -jumpfactor * JUMPCOUNT * JUMPCOUNT / 4 + JUMPHEIGHT
                            set deltaz = newz - .z
                            loop
                                if (RAbsBJ(GetUnitX(.cunit)-newx) < JUMPTOLERANCE) and (RAbsBJ(GetUnitY(.cunit)-newy) < JUMPTOLERANCE) then
                                    exitwhen (increase or (not increase and not decrease)) and RAbsBJ(deltaz-parabolaz)<JUMPTOLERANCE
                                    exitwhen decrease and ((deltaz-parabolaz)<JUMPTOLERANCE)
                                endif
                                if deltaz-parabolaz>0. or toggle then
                                    set factor = factor - 0.02
                                    if increase then
                                        set toggle = true
                                    endif
                                    set decrease = true
                                else
                                    set factor = factor + 0.02
                                    if decrease then
                                        set toggle = true
                                        set factor = 0.98
                                    endif
                                    set increase = true
                                endif
                                set newx = .x + Cos(.facing * bj_DEGTORAD) * .speed * JUMPCOUNT * factor
                                set newy = .y + Sin(.facing * bj_DEGTORAD) * .speed * JUMPCOUNT * factor
                                call SetUnitPosition(.cunit, newx, newy)
                                call MoveLocation(Loc, GetUnitX(.cunit), GetUnitY(.cunit))
                                set newz = GetLocationZ(Loc)
                                set parabolaz = -jumpfactor * (JUMPCOUNT*factor - JUMPCOUNT/2) * (JUMPCOUNT*factor - JUMPCOUNT/2) + JUMPHEIGHT
                                set deltaz = newz - .z
                            endloop
                        endif
                        set .jx = GetUnitX(.cunit)
                        set .jy = GetUnitY(.cunit)
                        set x = .jx - .x
                        set y = .jy - .y
                        set .jf = Atan2(y, x)
                        set .speed = SquareRoot(x * x + y * y) / JUMPCOUNT
                        set .jcmax = R2I(SquareRoot((deltaz-JUMPHEIGHT) / -(JUMPHEIGHT*4 / (JUMPCOUNT*JUMPCOUNT))) + JUMPCOUNT/2)
                    endif
                endif
                //! endtextmacro
                //! runtextmacro JumpInit("GetUnitTypeId(GetSoldUnit())")
            else
                //! runtextmacro JumpInit("GetSpellAbilityId()")
            endif
            return false
        endmethod
        
        static if COMBAT_MODE then
            //! textmacro SelectInit takes FRIENDLY, friendly
                private static method $FRIENDLY$SelectInit takes nothing returns boolean
                    local integer i = 1
                    local integer j = 1
                    local real x
                    local real y
                    local real X
                    local real Y
                    if GetUnitTypeId(GetSoldUnit()) != $FRIENDLY$SELECTID then
                        return false
                    endif
                    set playerid = GetPlayerId(GetTriggerPlayer())+1
                    if not thistype(playerid).inuse then
                        return false
                    endif
                    call DestroyTrigger(thistype(playerid).$friendly$td)
                    loop
                        exitwhen i == thistype(playerid).$friendly$tc
                        set x = GetUnitX(thistype(playerid).$friendly$targeted[i]) - thistype(playerid).x
                        set y = GetUnitY(thistype(playerid).$friendly$targeted[i]) - thistype(playerid).y
                        if GetWidgetLife(thistype(playerid).$friendly$targeted[i]) < 0.405 or SquareRoot(x*x+y*y) > COMBATRADIUS then
                            set j = i
                            loop
                                exitwhen j+1 == thistype(playerid).$friendly$tc
                                set thistype(playerid).$friendly$targeted[j] = thistype(playerid).$friendly$targeted[j+1]
                                set j = j + 1
                            endloop
                            set thistype(playerid).$friendly$targeted[j] = null
                            set thistype(playerid).$friendly$tc = thistype(playerid).$friendly$tc - 1
                            if thistype(playerid).$friendly$tp > thistype(playerid).$friendly$tc then
                                set thistype(playerid).$friendly$tp = thistype(playerid).$friendly$tp - 1
                            endif
                        else
                            set i = i + 1
                        endif
                    endloop
                    set selectcount = 0
                    call GroupEnumUnitsInRange(g, thistype(playerid).x, thistype(playerid).y, COMBATRADIUS, $friendly$cfilter)
                    if selectcount == 0 then
                        if thistype(playerid).$friendly$tc == 1 then
                            set thistype(playerid).$friendly$target = null
                            if thistype(playerid).selection != null then
                                call DestroyEffect(thistype(playerid).selection)
                                set thistype(playerid).selection = null
                            endif
                            set thistype(playerid).atk = false
                            set thistype(playerid).attacking = false
                            set thistype(playerid).sidewards = false
                            return false
                        endif
                        if thistype(playerid).$friendly$tp == thistype(playerid).$friendly$tc then
                            set thistype(playerid).$friendly$tp = 1
                        endif
                        set thistype(playerid).$friendly$target = thistype(playerid).$friendly$targeted[thistype(playerid).$friendly$tp]
                        set thistype(playerid).$friendly$td = CreateTrigger()
                        call TriggerRegisterUnitEvent(thistype(playerid).$friendly$td, thistype(playerid).$friendly$target, EVENT_UNIT_DEATH)
                        call TriggerAddCondition(thistype(playerid).$friendly$td, $friendly$tfilter)
                    else
                        set i = 2
                        set x = GetUnitX(selectable[1]) - thistype(playerid).x
                        set y = GetUnitY(selectable[1]) - thistype(playerid).y
                        set selectedunit = selectable[1]
                        loop
                            exitwhen i > selectcount
                            set X = GetUnitX(selectable[i]) - thistype(playerid).x
                            set Y = GetUnitY(selectable[i]) - thistype(playerid).y
                            if SquareRoot(x*x+y*y) > SquareRoot(X*X+Y*Y) then
                                set selectedunit = selectable[i]
                                set x = X
                                set y = Y
                            endif
                            set i = i + 1
                        endloop
                        set thistype(playerid).$friendly$target = selectedunit
                        set thistype(playerid).$friendly$td = CreateTrigger()
                        call TriggerRegisterUnitEvent(thistype(playerid).$friendly$td, thistype(playerid).$friendly$target, EVENT_UNIT_DEATH)
                        call TriggerAddCondition(thistype(playerid).$friendly$td, $friendly$tfilter)
                        set i = thistype(playerid).$friendly$tc
                        loop
                            exitwhen i == thistype(playerid).$friendly$tp
                            set thistype(playerid).$friendly$targeted[i] = thistype(playerid).$friendly$targeted[i-1]
                            set i = i - 1
                        endloop
                        set thistype(playerid).$friendly$targeted[thistype(playerid).$friendly$tp] = selectedunit
                        set thistype(playerid).$friendly$tc = thistype(playerid).$friendly$tc + 1
                    endif
                    set thistype(playerid).$friendly$tp = thistype(playerid).$friendly$tp + 1
                    if thistype(playerid).selection != null then
                        call DestroyEffect(thistype(playerid).selection)
                        set thistype(playerid).selection = null
                    endif
                    if thistype(playerid).attacking then
                        call DisableTrigger(thistype(playerid).trg)
                        call IssueTargetOrder(thistype(playerid).u, "attack", thistype(playerid).$friendly$target)
                        call EnableTrigger(thistype(playerid).trg)
                    endif
                    if thistype(playerid).atk then
                        if GetLocalPlayer() == Player(playerid-1) then
                            set thistype(playerid).selection = AddSpecialEffectTarget(COMBAT_ON, thistype(playerid).$friendly$target, ATTACHPNT)
                        else
                            set thistype(playerid).selection = AddSpecialEffectTarget("", thistype(playerid).$friendly$target, ATTACHPNT)
                        endif
                        static if not NOMOUSE then
                            set thistype(playerid).order = false
                        endif
                    else
                        if GetLocalPlayer() == Player(playerid-1) then
                            set thistype(playerid).selection = AddSpecialEffectTarget(COMBAT_OFF, thistype(playerid).$friendly$target, ATTACHPNT)
                        else
                            set thistype(playerid).selection = AddSpecialEffectTarget("", thistype(playerid).$friendly$target, ATTACHPNT)
                        endif
                    endif
                    return false
                endmethod
            //! endtextmacro
            //! runtextmacro SelectInit("","")
            
            //! runtextmacro SelectInit("F","f")
            
            private static method CheckForNewEnemy takes nothing returns boolean
                local unit u = GetFilterUnit()
                local integer i = 1
                if not IsUnitEnemy(u, Player(playerid-1)) or GetWidgetLife(u) < 0.405 or GetUnitAbilityLevel(u, 'Aloc') == 1 then
                    set u = null
                    return false
                endif
                loop
                    exitwhen i == thistype(playerid).tc
                    if thistype(playerid).targeted[i] == u then
                        set u = null
                        return false
                    endif
                    set i = i + 1
                endloop
                set selectcount = selectcount + 1
                set selectable[selectcount] = u
                set u = null
                return false
            endmethod
            
            private static method CheckForNewAlly takes nothing returns boolean
                local unit u = GetFilterUnit()
                local integer i = 1
                if not IsUnitAlly(u, Player(playerid-1)) or GetWidgetLife(u) < 0.405 or GetUnitAbilityLevel(u, 'Aloc') == 1 or GetUnitAbilityLevel(u, 'Avul') == 1 then
                    set u = null
                    return false
                endif
                loop
                    exitwhen i == thistype(playerid).ftc
                    if thistype(playerid).ftargeted[i] == u then
                        set u = null
                        return false
                    endif
                    set i = i + 1
                endloop
                set selectcount = selectcount + 1
                set selectable[selectcount] = u
                set u = null
                return false
            endmethod
            
            private static method Unselect takes nothing returns boolean
                local thistype this
                if GetUnitTypeId(GetSoldUnit()) != UNSELECTID then
                    return false
                endif
                set this = thistype(GetPlayerId(GetTriggerPlayer())+1)
                if not .inuse then
                    return false
                endif
                set .target = null
                set .ftarget = null
                set .tc = 1
                set .ftc = 1
                set .tp = 1
                set .ftp = 1
                call DestroyEffect(.selection)
                set .selection = null
                set .atk = false
                set .attacking = false
                set .sidewards = false
                call DestroyTrigger(.td)
                call DestroyTrigger(.ftd)
                if not .isjumping then
                    //! runtextmacro InitMovement()
                endif
                return false
            endmethod
            
            private static method StartAttack takes nothing returns boolean
                local thistype this
                if GetUnitTypeId(GetSoldUnit()) != ATTACKID then
                    return false
                endif
                set this = thistype(GetPlayerId(GetTriggerPlayer())+1)
                if not .inuse or .target == null then
                    return false
                endif
                call DestroyEffect(.selection)
                if .atk then
                    set .atk = false
                    set .attacking = false
                    set .sidewards = false
                    if GetLocalPlayer() == Player(this-1) then
                        set .selection = AddSpecialEffectTarget(COMBAT_OFF, .target, ATTACHPNT)
                    else
                        set .selection = AddSpecialEffectTarget("", .target, ATTACHPNT)
                    endif
                    if not .isjumping then
                        //! runtextmacro InitMovement()
                    endif
                else
                    set .atk = true
                    set .sidewards = true
                    if GetLocalPlayer() == Player(this-1) then
                        set .selection = AddSpecialEffectTarget(COMBAT_ON, .target, ATTACHPNT)
                    else
                        set .selection = AddSpecialEffectTarget("", .target, ATTACHPNT)
                    endif
                    static if not NOMOUSE then
                        set .order = false
                    endif
                endif
                return false
            endmethod
        endif
        
        private static method TargetDies takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(GetKillingUnit()))+1)
            set .target = null
            set .tc = 1
            set .tp = 1
            call DestroyEffect(.selection)
            set .selection = null
            set .atk = false
            set .attacking = false
            set .sidewards = false
            //! runtextmacro InitMovement()
            return false
        endmethod
        
        private static method FTargetDies takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(GetKillingUnit()))+1)
            set .ftarget = null
            set .ftc = 1
            set .ftp = 1
            call DestroyEffect(.selection)
            set .selection = null
            return false
        endmethod
        
        private static method UpDown takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .up = true
                static if not NOMOUSE then
                    set .order = false
                    call IssueImmediateOrder(.u, "stop")
                endif
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                if .timerstate == 1 then
                    if TimerGetElapsed(.dblclick) < DBLCLICKTIME then
                        set dblclicked = true
                    else
                        call TimerStart(.dblclick, 3600., false, null)
                        set dblclicked = false
                    endif
                else
                    set .timerstate = 1
                    call TimerStart(.dblclick, 3600., false, null)
                    set dblclicked = false
                endif
                if not .down then
                    if dblclicked then
                        set .running = true
                        set .speed = .msr
                        set .sidewards = false
                        call SetUnitTimeScale(.u, .asr)
                        call SetUnitAnimationByIndex(.u, .anim)
                        if .right then
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.RightPeriodic)
                        elseif .left then
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.LeftPeriodic)
                        endif
                    else
                        if .sidewards then
                            if .right then
                                set .sfacing = .facing - RIGHT_SIDE_FACING
                            elseif .left then
                                set .sfacing = .facing - LEFT_SIDE_FACING
                            endif
                        else
                            set .speed = .ms
                            call SetUnitTimeScale(.u, .as)
                            call SetUnitAnimationByIndex(.u, .anim)
                        endif
                    endif
                endif
            endif
            return false
        endmethod
        
        private static method UpUp takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .up = false
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                set .running = false
                if .down then
                    set .speed = .msb
                    call SetUnitAnimationByIndex(.u, .anim)
                    call SetUnitTimeScale(.u, .asb)
                elseif .sidewards then
                    if .right then
                        set .sfacing = .facing - 90.
                    elseif .left then
                        set .sfacing = .facing + 90.
                    endif
                else
                    set .speed = 0.
                    call SetUnitAnimation(.u, "stand")
                    call SetUnitTimeScale(.u, .as)
                endif
            endif
            return false
        endmethod

        private static method LeftDown takes nothing returns boolean
            //! textmacro SideDown takes SIDE, SIDECAPS, SIDE1, SIDE2, TIMERSTATE, OP
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .$SIDE1$ = true
                static if not NOMOUSE then
                    set .order = false
                    call IssueImmediateOrder(.u, "stop")
                endif
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                if not .running then
                    if .timerstate == $TIMERSTATE$ then
                        if TimerGetElapsed(.dblclick) < DBLCLICKTIME then
                            set dblclicked = true
                            set .break = true
                        else
                            call TimerStart(.dblclick, 3600., false, null)
                            set dblclicked = false
                        endif
                    else
                        set .timerstate = $TIMERSTATE$
                        call TimerStart(.dblclick, 3600., false, null)
                        set dblclicked = false
                    endif
                else
                    set dblclicked = false
                endif
                if dblclicked then
                    set .speed = .mss
                    call SetUnitAnimationByIndex(.u, .anim)
                    call SetUnitTimeScale(.u, .ass)
                    set .sidewards = true
                    set .sfacing = .facing $OP$ 90.
                else
                    if not .$SIDE2$ then
                        if .up then
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.$SIDE$Periodic)
                        elseif .down then
                            set .sidewards = true
                            set .sfacing = .facing + $SIDECAPS$_SIDE_FACING
                        else
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.$SIDE$Periodic)
                        endif
                    endif
                endif
            endif
            return false
            //! endtextmacro
            //! runtextmacro SideDown("Left", "LEFT", "left", "right", "2", "+")
        endmethod
        
        private static method LeftUp takes nothing returns boolean
            //! textmacro SideUp takes SIDE1, SIDE2, SIDE
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .$SIDE1$ = false
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                if .$SIDE2$ then
                    if .down then
                        set .sidewards = true
                        set .sfacing = .facing + $SIDE$_SIDE_FACING
                    else
                        if .sidewards then
                            static if COMBAT_MODE then
                                if not .atk then
                                    set .sidewards = false
                                endif
                            endif
                            set .speed = 0.
                        endif
                    endif
                else
                    static if COMBAT_MODE then
                        if not .atk then
                            set .sidewards = false
                        endif
                    endif
                    if not .down and not .up then
                        set .speed = 0.
                        call SetUnitAnimation(.u, "stand")
                        call SetUnitTimeScale(.u, .as)
                    elseif .up then
                        if .running then
                            set .speed = .msr
                            call SetUnitTimeScale(.u, .asr)
                        else
                            set .speed = .ms
                            call SetUnitTimeScale(.u, .as)
                        endif
                        call SetUnitAnimationByIndex(.u, .anim)
                    elseif .down then 
                        set .speed = .msb
                        call SetUnitAnimationByIndex(.u, .anim)
                        call SetUnitTimeScale(.u, .asb)
                    endif
                endif
            endif
            return false
            //! endtextmacro
            //! runtextmacro SideUp("left", "right", "RIGHT")
        endmethod
        
        private static method LeftPeriodic takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            if not this.disabled then
                set this.facing = this.facing + this.ts
            endif
            if not this.left or this.break then
                set this.break = false
                call PauseTimer(this.sidetimer)
            endif
        endmethod
      
        private static method RightDown takes nothing returns boolean
            //! runtextmacro SideDown("Right", "RIGHT", "right", "left", "3", "-")
        endmethod
        
        private static method RightUp takes nothing returns boolean
            //! runtextmacro SideUp("right", "left", "LEFT")
        endmethod
        
        private static method RightPeriodic takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            if not this.disabled then
                set this.facing = this.facing - this.ts
            endif
            if not this.right or this.break then
                set this.break = false
                call PauseTimer(this.sidetimer)
            endif
        endmethod

        private static method DownDown takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .down = true
                static if not NOMOUSE then
                    set .order = false
                    call IssueImmediateOrder(.u, "stop")
                endif
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                if .timerstate == 4 then
                    if (TimerGetElapsed(.dblclick)<DBLCLICKTIME) and not .sidewards then
                        set dblclicked = true
                    else
                        call TimerStart(.dblclick, 3600., false, null)
                        set dblclicked = false
                    endif
                else
                    set .timerstate = 4
                    call TimerStart(.dblclick, 3600., false, null)
                    set dblclicked = false
                endif
                if dblclicked then
                    set .facing = .facing + 180.
                    return false
                endif
                if not .up then
                    if .sidewards then
                        set .speed = .msb
                        call SetUnitAnimationByIndex(.u, .anim)
                        call SetUnitTimeScale(.u, .asb)
                        if .left then
                            set .sfacing = .facing - RIGHT_SIDE_FACING
                        elseif .right then
                            set .sfacing = .facing - LEFT_SIDE_FACING
                        endif
                    else
                        set .speed = .msb
                        call SetUnitAnimationByIndex(.u, .anim)
                        call SetUnitTimeScale(.u, .asb)
                        if .left then
                            set .break = true
                            set .sidewards = true
                            set .sfacing = .facing + LEFT_SIDE_FACING
                        elseif .right then
                            set .break = true
                            set .sidewards = true
                            set .sfacing = .facing + RIGHT_SIDE_FACING
                        endif
                    endif
                endif
            endif
            return false
        endmethod
        
        private static method DownUp takes nothing returns boolean
            local thistype this = thistype(GetPlayerId(GetTriggerPlayer())+1)
            if .inuse then
                set .down = false
                static if COMBAT_MODE then
                    if .isjumping or .atk then
                        return false
                    endif
                else
                    if .isjumping then
                        return false
                    endif
                endif
                if .up then
                    static if COMBAT_MODE then
                        if (.right or .left) and not .atk then
                            set .sidewards = false
                        endif
                    else
                        if .right or .left then
                            set .sidewards = false
                        endif
                    endif
                    if .running then
                        set .speed = .msr
                        call SetUnitAnimationByIndex(.u, .anim)
                        call SetUnitTimeScale(.u, .asr)
                    else
                        set .speed = .ms
                        call SetUnitAnimationByIndex(.u, .anim)
                        call SetUnitTimeScale(.u, .as)
                    endif
                else
                    if .sidewards then
                        if .right then
                            set .sfacing = .facing - 90.
                        else
                            set .sfacing = .facing + 90.
                        endif
                        set .speed = .mss
                        call SetUnitTimeScale(.u, .ass)
                    else
                        set .speed = 0.
                        call SetUnitAnimation(.u, "stand")
                        call SetUnitTimeScale(.u, .as)
                        if .left then
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.LeftPeriodic)
                        elseif .right then
                            set .break = false
                            call TimerStart(.sidetimer, INTERVAL, true, function thistype.RightPeriodic)
                        endif
                    endif
                endif
            endif
            return false
        endmethod
        
        static method Unregister takes player p returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            set .inuse = false
            set .break = true
            static if not COMBAT_MODE then
                call UnitRemoveAbility(.u, JUMPID)
            endif
            call .stopPeriodic()
            if .jump then
                call SetUnitPosition(.cunit, .x, .y)
                call SetUnitX(.u, GetUnitX(.cunit))
                call SetUnitY(.u, GetUnitY(.cunit))
                call RemoveUnit(.cunit)
                if .isjumping then
                    call SetUnitFlyHeight(.u, 0., 0.)
                endif
            endif
            call SetUnitAnimation(.u, "stand")
            call IssueImmediateOrder(.u, "stop")
            set .u = null
            static if COMBAT_MODE then
                call DestroyEffect(.selection)
            endif
            call ReleaseTimer(.t)
            call ReleaseTimer(.sidetimer)
            call ReleaseTimer(.dtimer)
            call ReleaseTimer(.dblclick)
            call DestroyTrigger(.trg)
            call DestroyTrigger(.death)
            call DestroyTrigger(.revive)
            if .target != null then
                call DestroyTrigger(.td)
                call DestroyTrigger(.ftd)
            endif
        endmethod
       
        static method GetRegisteredUnit takes player p returns unit
            local thistype this = thistype(GetPlayerId(p)+1)
            if .inuse then
                return .u
            endif
            return null
        endmethod
        
        static if COMBAT_MODE then
            static method GetUnitTarget takes unit u returns unit
                local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
                if .inuse and .u == u then
                    return .target
                endif
                return null
            endmethod
            
            static method GetUnitTargetByPlayer takes player p returns unit
                local thistype this = thistype(GetPlayerId(p)+1)
                if .inuse then
                    return .target
                endif
                return null
            endmethod
            
            static method GetUnitFTarget takes unit u returns unit
                local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
                if .inuse and .u == u then
                    return .ftarget
                endif
                return null
            endmethod
            
            static method GetUnitFTargetByPlayer takes player p returns unit
                local thistype this = thistype(GetPlayerId(p)+1)
                if .inuse then
                    return .ftarget
                endif
                return null
            endmethod
        endif
       
        static method IsUnitRegistered takes unit u returns boolean
            return thistype(GetPlayerId(GetOwningPlayer(u))+1).u == u
        endmethod
        
        static method IsUnitJumping takes unit u returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            return .isjumping and .u == u
        endmethod
        
        static method CanUnitJump takes unit u returns boolean
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            return .jump and .u == u
        endmethod
        
        static method CanUnitJumpByPlayer takes player p returns boolean
            local thistype this = thistype(GetPlayerId(p)+1)
            return .jump and .inuse
        endmethod
        
        static method SetMoveSpeed takes unit u, real r1, real r2, real r3, real r4, real t returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .u == u then
                if r1 != 0. then
                    set .ms = r1
                    call SetUnitMoveSpeed(u, r1 * 32)
                endif
                if r2 != 0. then
                    set .mss = r2
                endif
                if r3 != 0. then
                    set .msb = r3
                endif
                if r4 != 0. then
                    set .msr = r4
                endif
                if t != 0. then
                    set .ts = t
                endif
                //! textmacro InitSpeed takes TYPE, SPEED
                    if .sidewards then
                        if .atk then
                            if .up then
                                set .$SPEED$ = .$TYPE$
                            elseif .down then
                                set .$SPEED$ = .$TYPE$b
                            elseif .left or .right then
                                set .$SPEED$ = .$TYPE$s
                            endif
                        else
                            set .$SPEED$ = .$TYPE$s
                        endif
                    else
                        if .running then
                            set .$SPEED$ = .$TYPE$r
                        else
                            if .up then
                                set .$SPEED$ = .$TYPE$
                            elseif .down then
                                set .$SPEED$ = .$TYPE$b
                            elseif .left or .right then
                                set .$SPEED$ = .$TYPE$s
                            endif
                        endif
                    endif
                //! endtextmacro
                //! runtextmacro InitSpeed("ms", "speed")
            endif
        endmethod
        
        static method SetMoveSpeedByPlayer takes player p, real r1, real r2, real r3, real r4, real t returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if r1 != 0. then
                set .ms = r1
                call SetUnitMoveSpeed(.u, r1 * 32)
            endif
            if r2 != 0. then
                set .mss = r2
            endif
            if r3 != 0. then
                set .msb = r3
            endif
            if r4 != 0. then
                set .msr = r4
            endif
            if t != 0. then
                set .ts = t
            endif
            //! runtextmacro InitSpeed("ms", "speed")
        endmethod
        
        static method SetAnimSpeed takes unit u, real r1, real r2, real r3, real r4 returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .u == u then
                if r1 != 0. then
                    set .as = r1
                endif
                if r2 != 0. then
                    set .ass = r2
                endif
                if r3 != 0. then
                    set .asb = r3
                endif
                if r4 != 0. then
                    set .asr = r4
                endif
                //! runtextmacro InitSpeed("as", "as")
            endif
        endmethod
        
        static method SetAnimSpeedByPlayer takes player p, real r1, real r2, real r3, real r4 returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if r1 != 0. then
                set .as = r1
            endif
            if r2 != 0. then
                set .ass = r2
            endif
            if r3 != 0. then
                set .asb = r3
            endif
            if r4 != 0. then
                set .asr = r4
            endif
            //! runtextmacro InitSpeed("as", "as")
        endmethod
        
        static method SetX takes unit u, real r returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .inuse and .u == u then
                set .x = r
                call SetUnitX(.u, r)
            endif
        endmethod
        
        static method SetXPlayer takes player p, real r returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if .inuse then
                set .x = r
                call SetUnitX(.u, r)
            endif
        endmethod
        
        static method SetY takes unit u, real r returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .inuse and .u == u then
                set .y = r
                call SetUnitY(.u, r)
            endif
        endmethod
        
        static method SetYPlayer takes player p, real r returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if .inuse then
                set .y = r
                call SetUnitY(.u, r)
            endif
        endmethod
        
        static method SetFacing takes unit u, real r returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .inuse and .u == u then
                set .facing = r
                call SetUnitFacing(.u, r)
            endif
        endmethod
        
        static method SetFacingPlayer takes player p, real r returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if .inuse then
                set .facing = r
                call SetUnitFacing(.u, r)
            endif
        endmethod
        
        static method ResetTarget takes unit u returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            if .inuse and .u == u then
                set .target = null
                set .tc = 1
                set .tp = 1
                call DestroyEffect(.selection)
                set .selection = null
                set .atk = false
                set .attacking = false
                set .sidewards = false
                call DestroyTrigger(.td)
                if not .isjumping then
                    //! runtextmacro InitMovement()
                endif
            endif
        endmethod
        
        static method ResetTargetPlayer takes player p returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            if .inuse then
                set .target = null
                set .tc = 1
                set .tp = 1
                call DestroyEffect(.selection)
                set .selection = null
                set .atk = false
                set .attacking = false
                set .sidewards = false
                call DestroyTrigger(.td)
                if not .isjumping then
                    //! runtextmacro InitMovement()
                endif
            endif
        endmethod
        
        private static method Enable takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            set this.disabled = false
        endmethod
        
        static method DisableMoving takes unit u, real dur returns nothing
            local thistype this = thistype(GetPlayerId(GetOwningPlayer(u))+1)
            set .disabled = true
            call TimerStart(.dtimer, dur, false, function thistype.Enable)
        endmethod
        
        static method DisableMovingPlayer takes player p, real dur returns nothing
            local thistype this = thistype(GetPlayerId(p)+1)
            set .disabled = true
            call TimerStart(.dtimer, dur, false, function thistype.Enable)
        endmethod
        
        implement T32x
        
        static method create takes unit u, player p, integer animation, boolean jump, integer unitid, integer jumpanim, real atkrange returns thistype
            local thistype this = thistype(GetPlayerId(p)+1)
            local integer i = 1
            debug if .inuse then
            debug    call BJDebugMsg("RegisterUnitArrowKey: Warning! Multiple registries detected")
            debug endif
            
            // init general boolean instance variables
            set .inuse = true
            set .up = false
            set .left = false
            set .right = false
            set .down = false
            set .break = false
            set .disabled = false
            
            // init movement system instance variables
            set .u = u
            set .p = p
            call UnitAddAbility(u, 'Arav')
            call UnitRemoveAbility(u, 'Arav')
            set .anim = animation
            set .x = GetUnitX(u)
            set .y = GetUnitY(u)
            set .z = 0.
            set .facing = GetUnitFacing(u)
            set .speed = 0.
            set .ms = MOVE_SPEED
            set .mss = MOVE_SPEED_SIDE
            set .msb = MOVE_SPEED_BACK
            set .msr = MOVE_SPEED_RUN
            set .ts = TURN_SPEED
            set .as = ANIM_SPEED
            set .ass = ANIM_SPEED_SIDE
            set .asb = ANIM_SPEED_BACK
            set .asr = ANIM_SPEED_RUN
            static if not NOMOUSE then
                set .order = false
            endif
            set .running = false
            set .sidewards = false
            set .sfacing = 0.
            set .trg = CreateTrigger()
            call TriggerRegisterUnitEvent(.trg, u, EVENT_UNIT_ISSUED_POINT_ORDER)
            call TriggerRegisterUnitEvent(.trg, u, EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerAddCondition(.trg, filter)
            set .death = CreateTrigger()
            call TriggerRegisterUnitEvent(.death, u, EVENT_UNIT_DEATH)
            call TriggerAddCondition(.death, dfilter)
            set .revive = CreateTrigger()
            call TriggerRegisterUnitEvent(.revive, u, EVENT_UNIT_HERO_REVIVE_FINISH)
            call TriggerAddCondition(.revive, rfilter)
            
            // init jump system instance variables
            if jump then
                static if not COMBAT_MODE then
                    call UnitAddAbility(u, JUMPID)
                endif
                set .cunit = CreateUnit(Player(15), unitid, GetUnitX(u), GetUnitY(u), bj_UNIT_FACING)
            endif
            set .jump = jump
            set .isjumping = false
            set .janim = jumpanim
            set .jx = 0.
            set .jy = 0.
            set .jh = 0.
            set .jf = 0.
            set .jc = 0
            set .jcmax = 0
            
            // init combat system instance variables
            static if COMBAT_MODE then
                set .target = null
                set .ftarget = null
                loop
                    exitwhen i > ARRAY_SIZE
                    set .targeted[i] = null
                    set .ftargeted[i] = null
                    set i = i + 1
                endloop
                set .tc = 1
                set .ftc = 1
                set .tp = 1
                set .ftp = 1
                set .selection = null
                set .atk = false
                set .atkrange = atkrange
                set .attacking = false
            endif
            
            // init timer related instance variables
            set .sidetimer = NewTimer()
            call SetTimerData(.sidetimer, this)
            set .dtimer = NewTimer()
            call SetTimerData(.dtimer, this)
            set .dblclick = NewTimer()
            set .timerstate = 0
            call TimerStart(.dblclick, 3600., false, null)
            
            return this
        endmethod
        
        implement CAMSInit
        
    endstruct

    function RegisterUnitArrowKey takes unit u, player p, integer anim, boolean jump, integer unitid, integer jumpanim, real atkrange returns nothing
        call CAMS.create(u, p, anim, jump, unitid, jumpanim, atkrange).startPeriodic()
    endfunction
    
    function UnregisterUnitArrowKey takes player p returns nothing
        call CAMS.Unregister(p)
    endfunction
    
    function GetRegisteredUnitArrowKey takes player p returns unit
        return CAMS.GetRegisteredUnit(p)
    endfunction
    
    static if COMBAT_MODE then
        function GetUnitTarget takes unit u returns unit
            return CAMS.GetUnitTarget(u)
        endfunction

        function GetUnitTargetByPlayer takes player p returns unit
            return CAMS.GetUnitTargetByPlayer(p)
        endfunction
        
        function GetUnitFTarget takes unit u returns unit
            return CAMS.GetUnitFTarget(u)
        endfunction

        function GetUnitFTargetByPlayer takes player p returns unit
            return CAMS.GetUnitFTargetByPlayer(p)
        endfunction
    endif
    
    function IsUnitRegisteredArrowKey takes unit u returns boolean
        return CAMS.IsUnitRegistered(u)
    endfunction
    
    function IsUnitJumping takes unit u returns boolean
        return CAMS.IsUnitJumping(u)
    endfunction
    
    function CanUnitJump takes unit u returns boolean
        return CAMS.CanUnitJump(u)
    endfunction
    
    function CanUnitJumpByPlayer takes player p returns boolean
        return CAMS.CanUnitJumpByPlayer(p)
    endfunction
    
    function SetMoveSpeed takes unit u, real r1, real r2, real r3, real r4, real t returns nothing
        call CAMS.SetMoveSpeed(u, r1, r2, r3, r4, t)
    endfunction
  
    function SetMoveSpeedByPlayer takes player p, real r1, real r2, real r3, real r4, real t returns nothing
        call CAMS.SetMoveSpeedByPlayer(p, r1, r2, r3, r4, t)
    endfunction
    
    function SetAnimSpeed takes unit u, real r1, real r2, real r3, real r4 returns nothing
        call CAMS.SetAnimSpeed(u, r1, r2, r3, r4)
    endfunction
  
    function SetAnimSpeedByPlayer takes player p, real r1, real r2, real r3, real r4 returns nothing
        call CAMS.SetAnimSpeedByPlayer(p, r1, r2, r3, r4)
    endfunction
    
    function SetUnitXArrowKey takes unit u, real r returns nothing
        call CAMS.SetX(u, r)
    endfunction
 
    function SetUnitXByPlayer takes player p, real r returns nothing
        call CAMS.SetXPlayer(p, r)
    endfunction
 
    function SetUnitYArrowKey takes unit u, real r returns nothing
        call CAMS.SetY(u, r)
    endfunction
    
    function SetUnitYByPlayer takes player p, real r returns nothing
        call CAMS.SetYPlayer(p, r)
    endfunction
    
    function SetUnitFacingArrowKey takes unit u, real r returns nothing
        call CAMS.SetFacing(u, r)
    endfunction
    
    function SetUnitFacingByPlayer takes player p, real r returns nothing
        call CAMS.SetFacingPlayer(p, r)
    endfunction
    
    function ResetTarget takes unit u returns nothing
        call CAMS.ResetTarget(u)
    endfunction
 
    function ResetTargetByPlayer takes player p returns nothing
        call CAMS.ResetTargetPlayer(p)
    endfunction
    
    function DisableMove takes unit u, real duration returns nothing
        call CAMS.DisableMoving(u, duration)
    endfunction
 
    function DisableMoveByPlayer takes player p, real duration returns nothing
        call CAMS.DisableMovingPlayer(p, duration)
    endfunction
 
endlibrary

Keywords:
arrow, key, jump, system, keyboard, movement, up, down, left, right, combat, attack, press, release, target, switch
Contents

Advanced Combat and Movement System (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 12 Nov 2011 Bribe: This should use ArrowKeyEvent which will cut a lot of the bulk out of your script. There is no point registering 5 item-sell events for all players. Use...
Level 16
Joined
Sep 19, 2011
Messages
829
This is the best combat and movement system i've seen and you can jump +x2 = rating 5/5
It's a breath of fresh air!:thumbs_up:

Only one question: Can I use the jump system separately and can you post the code separately here so that I can understand what only the jump code is, I want to use it in my map!
 
Top