Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
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 *
***************************************************************************************************************
*************************************************** User Manual ***************************************************************
*
* ********** 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. *
* ***********************************************************************************************
*
*******************************************************************************************************************************
******************************************************* 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
*
*******************************************************************************************************************************
**************************************************** 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
*
*******************************************************************************************************************************
************************************************ 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
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Init initializer Init
globals
private boolean combatmode = false
endglobals
private function Selection takes nothing returns boolean
if GetTriggerUnit()==gg_unit_Ekee_0017 and not IsUnitRegisteredArrowKey(gg_unit_Ekee_0017) then
call UnregisterUnitArrowKey(GetTriggerPlayer())
call RegisterUnitArrowKey(gg_unit_Ekee_0017, Player(0), 2, false, 0, 0, 550.)
call EnableThirdPersonCam(Player(0), gg_unit_Ekee_0017, 0.1)
set combatmode = false
elseif GetTriggerUnit()==gg_unit_Edem_0001 and not IsUnitRegisteredArrowKey(gg_unit_Edem_0001) then
call UnregisterUnitArrowKey(GetTriggerPlayer())
call RegisterUnitArrowKey(gg_unit_Edem_0001, Player(0), 8, true, 'e000', 2, 120.)
call EnableThirdPersonCam(Player(0), gg_unit_Edem_0001, 0.1)
set combatmode = false
endif
return false
endfunction
private function ToggleCombatMode takes nothing returns boolean
if combatmode then
call ClearSelection()
call SelectUnit(GetRegisteredUnitArrowKey(Player(0)), true)
set combatmode = false
else
call ClearSelection()
call SelectUnit(gg_unit_nshp_0010, true)
set combatmode = true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t1 = CreateTrigger()
call TriggerRegisterUnitEvent(t, gg_unit_Ekee_0017, EVENT_UNIT_SELECTED)
call TriggerRegisterUnitEvent(t, gg_unit_Edem_0001, EVENT_UNIT_SELECTED)
call TriggerAddCondition(t, Filter(function Selection))
call TriggerRegisterPlayerEvent(t1, Player(0), EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t1, Filter(function ToggleCombatMode))
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 8.)
call SuspendTimeOfDay(false)
call FogEnable(false)
call FogMaskEnable(false)
call RegisterUnitArrowKey(gg_unit_Edem_0001, Player(0), 8, true, 'e000', 2, 120.)
call EnableThirdPersonCam(Player(0), gg_unit_Edem_0001, 0.1)
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cffdd3333Advanced Combat and Movement System")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cffdd3333v 1.3 by 3yeballz")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., " ")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cffffd700use arrow keys (up, down, left, right)")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cffffd700to move your character")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., " ")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cff888888doubleclick up: run")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cff888888doubleclick down: 180° turn")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cff888888doubleclick left/right: side walk")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., " ")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cff007fffselect another character to control him")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., " ")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "|cffa545d0press \"esc\" to toggle combat mode")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., " ")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "press \"|cff33ddddV|r\" to jump")
call DisplayTimedTextToPlayer(Player(0), 0., 0., 3600., "while in combat mode")
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ThirdPersonCam initializer Init
globals
//*****************************************************************************
// CONSTANTS:
private constant real DISTANCE_AOA_MIN = -15
private constant real DISTANCE_DISTANCE_MIN = 500
private constant real DISTANCE_AOA_MAX = -65
private constant real DISTANCE_DISTANCE_MAX = 700
private constant real OFFSET_AOA_MIN = -35
private constant real OFFSET_OFFSET_MIN = 0
private constant real OFFSET_AOA_MAX = -70
private constant real OFFSET_OFFSET_MAX = 150
private constant real Z_OFFSET = 160
private constant real TIMEOUT = 0.0625
private constant real TERRAIN_SAMPLING = 32
private constant real PAN_DURATION = 0.3
private constant real ANGLE_ABOVE_TERRAIN = 20
private constant real DEFAULT_AOA = -30
private constant real DEFAULT_ROT = 0
private constant real FIELD_OF_VIEW = 140
private constant real FAR_Z = 5000
private constant real CLIFF_DISTANCE = 400
private constant real RCT_X = 250
private constant real RCT_Y = 250
private constant real DESTR_AOA = 30 * bj_DEGTORAD
//
//*****************************************************************************
//*****************************************************************************
// Public variables.
real array ThirdPersonCamAoA
real array ThirdPersonCamRot
//
//*****************************************************************************
//*****************************************************************************
// Internal variables. These should not be changed:
private location Loc = Location(0,0)
private unit array Unit
private timer array FirstPan
private real DistanceM = (DISTANCE_DISTANCE_MAX-DISTANCE_DISTANCE_MIN)/((DISTANCE_AOA_MAX-DISTANCE_AOA_MIN)*bj_DEGTORAD)
private real DistanceT = DISTANCE_DISTANCE_MIN-DISTANCE_AOA_MIN*bj_DEGTORAD*DistanceM
private real OffsetM = (OFFSET_OFFSET_MAX-OFFSET_OFFSET_MIN)/((OFFSET_AOA_MAX-OFFSET_AOA_MIN)*bj_DEGTORAD)
private real OffsetT = OFFSET_OFFSET_MIN-OFFSET_AOA_MIN*bj_DEGTORAD*OffsetM
//
//*****************************************************************************
endglobals
//*****************************************************************************
// Functions for distance and offset. These are linear mathematical functions y = mx+t.
private function InterpolateDistance takes real angleOfAttack returns real
if angleOfAttack <= DISTANCE_AOA_MAX*bj_DEGTORAD then
return DISTANCE_DISTANCE_MAX
elseif angleOfAttack >= DISTANCE_AOA_MIN*bj_DEGTORAD then
return DISTANCE_DISTANCE_MIN
endif
return DistanceM * angleOfAttack + DistanceT
endfunction
private function InterpolateOffset takes real angleOfAttack returns real
if angleOfAttack <= OFFSET_AOA_MAX*bj_DEGTORAD then
return OFFSET_OFFSET_MAX
elseif angleOfAttack >= OFFSET_AOA_MIN*bj_DEGTORAD then
return OFFSET_OFFSET_MIN
endif
return OffsetM * angleOfAttack + OffsetT
endfunction
//
//*****************************************************************************
//*****************************************************************************
// Functions for camera settings.
private function ApplyCam takes integer p, real duration returns nothing
local real aoa = GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK) - 2*bj_PI
local real offset = InterpolateOffset(aoa)
local real newaoa
local real maxd
local real tarz
local real newx
local real newy
local real newm
local real maxm = -1
local real r = TERRAIN_SAMPLING
local real dx
local real dy
local real height
local integer i = 0
call SetCameraField(CAMERA_FIELD_ROTATION, GetUnitFacing(Unit[p]) + ThirdPersonCamRot[p], duration)
call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW, FIELD_OF_VIEW, duration)
call SetCameraField(CAMERA_FIELD_FARZ, FAR_Z, duration)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, InterpolateDistance(aoa), duration)
set newx = GetUnitX(Unit[p])
set newy = GetUnitY(Unit[p])
call PanCameraToTimed(newx + offset * Cos(bj_DEGTORAD*GetUnitFacing(Unit[p])), newy + offset * Sin(bj_DEGTORAD*GetUnitFacing(Unit[p])), duration)
set maxd = CLIFF_DISTANCE+GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
set dx = -Cos(GetCameraField(CAMERA_FIELD_ROTATION))*r
set dy = -Sin(GetCameraField(CAMERA_FIELD_ROTATION))*r
call MoveLocation(Loc, newx, newy)
set tarz = GetLocationZ(Loc)
set height = GetUnitFlyHeight(Unit[p])
call SetCameraField(CAMERA_FIELD_ZOFFSET, Z_OFFSET + height, duration)
loop
exitwhen r > maxd
set newx = newx+dx
set newy = newy+dy
call MoveLocation(Loc, newx, newy)
set newm = (GetLocationZ(Loc)-tarz-height)/r
if newm > maxm then
set maxm = newm
endif
set r = r+TERRAIN_SAMPLING
set i = i + 1
endloop
set newx = newx-dx*i/1.3
set newy = newy-dy*i/1.3
set newaoa = - Atan(maxm) * bj_RADTODEG - ANGLE_ABOVE_TERRAIN
if ThirdPersonCamAoA[p] < newaoa then
set newaoa = ThirdPersonCamAoA[p]
endif
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, newaoa, duration)
endfunction
private function Periodic takes nothing returns nothing
local integer p = GetPlayerId(GetLocalPlayer())
if Unit[p] != null then
if TimerGetRemaining(FirstPan[p]) <= PAN_DURATION then
call ApplyCam(p, PAN_DURATION)
else
call ApplyCam(p, TimerGetRemaining(FirstPan[p]))
endif
endif
endfunction
//
//*****************************************************************************
//*****************************************************************************
// Public functions.
function EnableThirdPersonCam takes player whichPlayer, unit whichUnit, real firstPan returns nothing
local integer p = GetPlayerId(whichPlayer)
set Unit[p] = whichUnit
call TimerStart(FirstPan[p], firstPan, false, null)
if GetLocalPlayer() == Player(p) then
call StopCamera()
if whichUnit != null then
call ApplyCam(p, firstPan)
endif
endif
endfunction
function IsThirdPersonCamActive takes integer p returns boolean
return Unit[p] != null
endfunction
function ResetThirdPersonCamAoA takes integer p returns nothing
set ThirdPersonCamAoA[p] = DEFAULT_AOA
endfunction
function ResetThirdPersonCamRot takes integer p returns nothing
set ThirdPersonCamRot[p] = DEFAULT_ROT
endfunction
//
//*****************************************************************************
//*****************************************************************************
//Init function for timer and arrays.
private function Init takes nothing returns nothing
local timer t = CreateTimer()
local integer i = 0
loop
exitwhen i == 12
set ThirdPersonCamAoA[i] = DEFAULT_AOA
set ThirdPersonCamRot[i] = DEFAULT_ROT
set Unit[i] = null
set FirstPan[i] = CreateTimer()
set i = i+1
endloop
call TimerStart(t, TIMEOUT, true, function Periodic)
set t = null
endfunction
//
//*****************************************************************************
endlibrary