The best system I've ever seen.
This is not the best system of Movement. I've seen better in xgm.ru
Credits and required systemsTimer32 by Jesus4Lyf: www.thehelper.net/forums/showthread.php/132538-Timer32
TimerUtils by Vexorian: www.wc3c.net/showthread.php?t=101322
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.0 | First 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 |
CodeJASS: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