• 🏆 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...

Moderator

M

Moderator

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 FirstOfGroup loops with a null filter, it will also cut much of the bulk and overhead out of the script.
 
Level 16
Joined
Jun 24, 2009
Messages
1,409
HOLY FUUUUUUUUUUUUU-
The best system I've ever seen. Such a nice job.
And I have a little question. I made a little non-target jump for my map but it can jump out of the map or on the bondaries. Its a long and low height jump. How can you make it that it don't has a collosion and can't jump on disabled places?
 
Level 13
Joined
Jun 9, 2009
Messages
1,129
"-because I've never seen an arrow key system where you can jump
-because I wanted to create a better movement system, this one is more intuitive"
LOL!!!!!!
Man there is better arrow key movement system, much and way better systems.... About your jump system :eek: there are better jump systems too... 3/5 but i won't rate, to give you low grade :)
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Nice to see some vJASS around here...

-Don't use KT, TU and T32 is all we should use at the moment. I think even J4L said that too.

-Why can't your unit have collision size? Kinda ruins a map.

-Way too low period for a system like this, use T32.

-What a
JASS:
            debug if thistype(id).inuse then
            debug    call call BJDebugMsg("RegisterUnitArrowKey: Warning! Multiple registries detected")
            debug endif
the double call?

-Why is the dummy collision check created for player 1?
set thistype(id).collisionunit = CreateUnit(Player(0), unitid, GetUnitX(u), GetUnitY(u), bj_UNIT_FACING)

-Use TU for goodness sake or create a simple timer recycler.

-Order Ids can be stored into a few constants or static members.

-Is it even necessary to have a timer with 0. period?
Just call the function instead and get rid off the method.
call IssueImmediateOrder(thistype(playerid).u, "stop")

-Don't use SetUnitPosition, use SetUnitX/Y instead, smoother.

Huge code indeed still this might be needed for someone, I guess.
 
Level 4
Joined
Feb 28, 2011
Messages
37
First, thanks for all your comments/ratings/+rep.

And I have a little question. I made a little non-target jump for my map but it can jump out of the map or on the bondaries. Its a long and low height jump. How can you make it that it don't has a collosion and can't jump on disabled places?

You must test if your unit can walk at the landing point. The essential part in this system is the following one:
JASS:
                        set newx = thistype(id).x + Cos(thistype(id).sidefacing * bj_DEGTORAD) * thistype(id).speed * JUMPCOUNT
                        set newy = thistype(id).y + Sin(thistype(id).sidefacing * bj_DEGTORAD) * thistype(id).speed * JUMPCOUNT
                        call SetUnitPosition(thistype(id).collisionunit, newx, newy)
                        set factor = 1.
                        loop
                            exitwhen (RAbsBJ(GetUnitX(thistype(id).collisionunit)-newx) < JUMPTOLERANCE) and (RAbsBJ(GetUnitY(thistype(id).collisionunit)-newy) < JUMPTOLERANCE)
                            set newx = thistype(id).x + Cos(thistype(id).sidefacing * bj_DEGTORAD) * thistype(id).speed * JUMPCOUNT * factor
                            set newy = thistype(id).y + Sin(thistype(id).sidefacing * bj_DEGTORAD) * thistype(id).speed * JUMPCOUNT * factor
                            call SetUnitPosition(thistype(id).collisionunit, newx, newy)
                            set factor = factor - 0.1
                        endloop

If it's not walkable by your specific unit, the jump length will be decreased by a factor of 0.1 and you have to test it again.

-Don't use KT, TU and T32 is all we should use at the moment. I think even J4L said that too.

Ok, didn't know that. I will test and update it.

-Why can't your unit have collision size? Kinda ruins a map.

Since there always exists the collision unit, all units that are registered have a collision size (but not in the object editor). Just try to walk to the unit by player 2.

-Why is the dummy collision check created for player 1?

Because it's totally unimportant and I think that in the most maps there exists a player 1.

-Is it even necessary to have a timer with 0. period?

Yes, to overwrite the issued order you need such a timer.

Fixed the double call and yes, I know that there are some things that I can improve. This system isn't perfect at all and I just said that I WANT to create a better movement system, but not that I did.
 
I'm too lazy to check through all the code, but there are some things I noticed at first glance.

Instead of using a trigger per event, use one trigger for all the keyboard events and then just use GetTriggerEventId() == EVENT_PLAYER_ARROW_UP_UP //for example for all of them and use that to check which function to execute. For more info on what I'm talking about, see this system:
http://www.hiveworkshop.com/forums/spells-569/advanced-arrow-key-movement-system-v1-2-0-a-103695/

Also, make sure you do this:
JASS:
    if GetPlayerSlotState(Player(i))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i))==MAP_CONTROL_USER then
        //register the events
    endif

During the loop for registering the events. Since the event won't fire unless a player is using that slot, then you should check if they are playing before you register them, otherwise it is a bunch of events registered for no reason. :)
 
Level 20
Joined
Jul 12, 2010
Messages
1,717
i don't know anything about vJass so i can't critism the system...
but what i know is that your arrow movement system does exactly what this one does...
anyway pretty good system 5/5 :thumbs_up:

question:
why if order the unit to move to a point it doesn't?
also i can't attack other units...
maybe add attack ability to your system?
that would really be good for people like me...
 
Level 4
Joined
Feb 28, 2011
Messages
37
Since the event won't fire unless a player is using that slot, then you should check if they are playing before you register them, otherwise it is a bunch of events registered for no reason. :)

Good suggestion. Now implemented.

use one trigger for all the keyboard events

Are you sure that this would be faster? I'd have to check everytime when a key is pressed what key it is. Why not using more triggers?

why if order the unit to move to a point it doesn't?
also i can't attack other units...

Now it should work ;-)

And now using TU for the main timer. It's a mess using KT because unregistering a unit would be much more difficult.
 
Level 11
Joined
May 31, 2008
Messages
698
Just one thing you might consider, when you jump onto or off of cliffs, it kind of messes up the height. Like if theres a level 2 cliff in front of you, you can easily jump over it even though normally you cant jump that high.

Also, if there is high raised terrain in front of you (ex. a small hill), you jump super high over the hill. I think you should adjust the height based on the height of the terrain, so the jump is always even and you cant just jump over super high terrain that you shouldnt be able to jump over.

I just think this would make it look a little more realistic

But other than that awesome system :)
 
Level 11
Joined
May 31, 2008
Messages
698
Yes I know that ;-)

Just wrote in the description that it doesn't consider z-axis but the next version will have a much better jump system :grin:

ohhh okay haha
Are you going to be able to do that without using locations?
Because i have made similar systems with jumping and projectiles that worked perfect, but i have to use a location to get the z of where the unit is so i can change its flying height.
But i just use regular jass and i tried to find a way with just using x and y coordinates but i cant find out how :(
 
Level 4
Joined
Feb 28, 2011
Messages
37
*Updated*

I implemented a combat system to have more freedom while using arrow keys.
That means you have abilitys to attack/switch your target.
I'd like to get some feedback since the combat system is unique I think :)
 
Level 11
Joined
May 31, 2008
Messages
698
I like it alot! but i think that being able to circle the target while you attack them might be a little bit too much lol, you should probly make an option in the coding that can enable/disable attacking while circling around the target depending on the users preference.
Maybe if it was used for ranged units, it wouldnt be that bad because they are farther away, but it looks kinda weird circling around the target super fast and still being able to attack it xD
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
From what I've seen, this can use TimerQueue, but there's a lot of code to go over. Check it out in JASS section.


And yea, coupled resources generally aren't acceptable
->CombatAndMovementSystem

edit
Yea, this doesn't need TimerUtils at all. Use TimerQueue for dblclick instead.

http://www.hiveworkshop.com/forums/jass-functions-413/system-timerqueue-187502/

JASS:
struct Bleh extends array
    private static constant real INTERVAL = 30 //timers have a constant timeout of 30

    private method expire takes nothing returns nothing
        call deallocate()
    endmethod

    implement Tq
                
    static method create takes nothing returns thistype
        return allocate()
    endmethod

    method destroy takes nothing returns nothing
        call deallocate()
    endmethod
endstruct


For the movement and so on, use T32.



You should only be using TimerUtils if you need 100% dynamic times and or need to change the remaining time on your timers while the timer is running. You need neither of those in this, so you shouldn't be using TimerUtils at all.


Now, if Bribe updates his timer system to use a BST, it may actually be faster than regular timers given that it only executes 1 piece of dynamic code rather than many. If that is the case, then TimerUtils would only be needed for dynamic code (very rare) or for changing the remaining time on a timer.


edit
Now that I read more, there are too many problems for the script to be salvaged.

My suggestion is to trash the entire library and start over creating 2 resources instead of 1, removing virtually every single one of your constants and static ifs (probably all actually), and so on. If you want to make the other libraries extensions like I did with AdvDamageEvent because it's more efficient, go for it.

Also, your initialization is bad =P. Your onInit has to be private and it has to be in a module.


So here are just a few problems..

1. Do you think it makes sense for every player controlled unit on the map to have the same movement speed, turn rate, jump, and etc? It certainly doesn't. Almost all of your constants should be removed.

2. If someone just wanted to use your camera system and wanted their own combat system, you do have static ifs, but the thing is still in the library... this is why modularity is so dern important, you never ever couple resources : |. If you see a big And in your script, you already know you did something wrong. First try to make them as two entirely separate resources (the combat system might be using the camera system for example). If you can't do that, then make the second an extension of the first (UnitEvent is an extension of UnitIndexer, AdvDamageEvent is an extension of DamageEvent). I pretty much never use setup variables in my own scripts anymore because it's a very bad practice.

Well, those 2 problems already merit just about a total rewrite of the entire script.
 
Last edited:
Level 4
Joined
Feb 28, 2011
Messages
37
Use TimerQueue for dblclick instead.

Hm I don't understand why this should be better for doubleclick.
I didn't know the system before and read the concept.
I only use one timer per player to detect doubleclick and it doesn't execute code. Everytime a key is pressed, the timer just restarts to reset the elapsed time. And btw: how to get the elapsed time when using Tq? (I didn't go through the whole code :grin: )


For the movement and so on, use T32.

Ok, will change it but not this week-end.


1. Do you think it makes sense for every player controlled unit on the map to have the same movement speed, turn rate, jump, and etc? It certainly doesn't. Almost all of your constants should be removed.

Hm yeah right :D
I will change it.


2. If someone just wanted to use your camera system and wanted their own combat system, you do have static ifs, but the thing is still in the library...

First, the camera system is not mine. I think you mean the movement system.
Yes, it's still in the library but is it that bad to use static ifs instead of splitting it up?
I just know that it creates space if a static if is not used.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
My bad on dblclick. You are changing the remaining time on it, so you shouldn't use a timer queue ;P.

First, the camera system is not mine. I think you mean the movement system.
Yes, it's still in the library but is it that bad to use static ifs instead of splitting it up?
I just know that it creates space if a static if is not used.

It's just best to split libs up into separate resources if they can be split. You should never ever see an And in a resource name ;P.
 
Level 4
Joined
Feb 28, 2011
Messages
37
Do I really need a timer system for doubleclick?
Only thing I need is the elapsed time and every struct instance has its own timer for doubleclick so i don't need any data attachment.

Adiktuz said on page 1 that it's still better to use NewTimer even if I don't need any timer data but I don't understand that.
 
Level 4
Joined
Feb 28, 2011
Messages
37
*updated*

changelog v1.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
 
Level 4
Joined
Feb 28, 2011
Messages
37
you could have made two T32s or two structs... ^_^

Hm yes I could^^ But its not that important I think.
Btw new update:

changelog v1.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
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
As most people have already pointed out the system looks fantastic and demonstrates a severe knowledge of the game. Other than the fact that the "combat" and "movement" things are merged and would be better as two seperate resources, that's not going to stop this from getting an approval when the time comes.

Looking at the code, there is not too much that immediate stands out as areas in need of improvement. It will take me a long time to look over the whole thing to

JASS:
this.jumpfacing * bj_DEGTORAD

Instead of that, this.jumpfacing should be set to a radian when it was first initialized.

JASS:
            static if COMBAT_MODE then
                if this.target != null and GetWidgetLife(this.target)<0.405 then
                    set this.target = null
                    set this.targetcount = 1
                    set this.targetpointer = 1
                    call DestroyEffect(this.selection)
                    set this.selection = null
                    set this.atk = false
                    set this.attacking = false
                    set this.sidewards = false
                endif
            endif

This check does not need to happen 32x per second. Basically you intend to disengage when the unit dies... but shouldn't it also disengage when the unit is loaded into a transport, turn invulnerable or something else of that nature? There are good event responses to detect these things so that you don't have to keep checking out for them. AutoIndex/AutoEvents has good detection in these fields. Maybe someone should upload a UnitIndexer-compatible Transport system.
 
Level 13
Joined
Jun 9, 2009
Messages
1,129
W_A_7_E_X, can you give me more info please?
and yeah you will need some triggering skills and jass skills to use this.
And JNGP + latest version of Jasshelper.
Now let's back to importing.
You open Trigger Editor (F4) [big golden "a"]
You'll see a folder under it something like papers, copy them (copy whole folder). Open your map, open trigger editor, paste them (folder). and you're done. With JNGP you have to save your map always before testing, and you must add your units to system...
 
Level 6
Joined
May 20, 2010
Messages
274
could i use this in multiplayer??? and uhhm...could i use other things to toggle the combat mode other than the escape button (like a spell that toggles the combat mode) ???

pls answer my questions ^^
 
Level 6
Joined
May 20, 2010
Messages
274
you mean something like if there is an ability that toggles combat mode ON on the registered unit then there should be something in the selling unit the toggles it OFF right?

3 more questions

bec. im gonna use it on an ORPG , the units are gonna learn some abilities, but because i can't fit all the abilities in the selling unit, i just thought of something like three shorcut skill slots.. something like an event that can make the skills be on the 3 skill shorchuts on the selling unit so they can be used when the unit is in combat mode..is that possible? how?


and how do i register a unit and a selling unit to the system?

sorry for my noob questions i'm really am just very interested about this and i need it on my map (i'll make sure you get credits on my map) ^^
 
Level 4
Joined
Feb 28, 2011
Messages
37
you mean something like if there is an ability that toggles combat mode ON on the registered unit then there should be something in the selling unit the toggles it OFF right?
exactly.

You can't fit all the abilities in the selling unit? Wow. How many abilites does one character have? Do you use different selling units for different registered units? You should.

To register a unit use call RegisterUnitArrowKey(unit u, player p, integer anim, boolean jump, integer unitid, integer jumpanim, real atkrange)
u is the registered unit.
You cannot register a selling unit. The system just needs the rawcodes for the jump and combat abilitys so there is no need to register the selling unit.
 
Level 6
Joined
May 20, 2010
Messages
274
Thank you sir!

You can't fit all the abilities in the selling unit?
uhm.. yes. ^^
Wow. How many abilites does one character have?
i'll use a spellbook for the abilities so 11. 11 abilities.
Do you use different selling units for different registered units? You should.
ok.
To register a unit use call RegisterUnitArrowKey(unit u, player p, integer anim, boolean jump, integer unitid, integer jumpanim, real atkrange)
u is the registered unit.
You cannot register a selling unit. The system just needs the rawcodes for the jump and combat abilitys so there is no need to register the selling unit.

ok thank you for that i'll make sure i'll give you the credits in my game ^^

off-topic:and btw, i'll make some you kind of a GM account on my oorpg ( actually, i'll give it to all who helped me [the people in credits] )^^ :ogre_haosis:
 
Top