Name | Type | is_array | initial_value |
AnimationIndex | integer | No | |
CanReverse | boolean | No | true |
CollisionSize | real | No | |
Controlled | unit | No | |
DestroyStuff | boolean | No | true |
Player | player | No | |
Power | real | No | |
Turnspeed | real | No | |
Unit | unit | No | |
UsePhysics | boolean | No | true |
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Test_Arrow_Movement_Actions takes nothing returns nothing
call ArrowMovement_StartArrowControl(gg_unit_TANK_0000, Player(0), 1.00, 4.00, 96.00, 10.0, 10, PATHING_TYPE_WALKABILITY)
endfunction
//===========================================================================
function InitTrig_Init_Arrow_Movement takes nothing returns nothing
set gg_trg_Init_Arrow_Movement = CreateTrigger( )
call TriggerAddAction( gg_trg_Init_Arrow_Movement, function Trig_Test_Arrow_Movement_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library KeyDetection initializer Init
globals
private trigger array ArrowDetectors
private boolean array KeyDown
private constant integer MAX_NUMBER_OF_PLAYERS = 12
constant integer ARROW_LEFT = 0
constant integer ARROW_RIGHT = 1
constant integer ARROW_DOWN = 2
constant integer ARROW_UP = 3
endglobals
function IsKeyDown takes integer key, player id returns boolean
return KeyDown[MAX_NUMBER_OF_PLAYERS*key+GetPlayerId(id)]
endfunction
private function GetKeyEventDown takes integer i returns playerevent
if i == 0 then
return EVENT_PLAYER_ARROW_LEFT_DOWN
elseif i == 1 then
return EVENT_PLAYER_ARROW_RIGHT_DOWN
elseif i == 2 then
return EVENT_PLAYER_ARROW_DOWN_DOWN
elseif i == 3 then
return EVENT_PLAYER_ARROW_UP_DOWN
endif
return null
endfunction
private function GetKeyEventUp takes integer i returns playerevent
if i == 0 then
return EVENT_PLAYER_ARROW_LEFT_UP
elseif i == 1 then
return EVENT_PLAYER_ARROW_RIGHT_UP
elseif i == 2 then
return EVENT_PLAYER_ARROW_DOWN_UP
elseif i == 3 then
return EVENT_PLAYER_ARROW_UP_UP
endif
return null
endfunction
private function LeftDown takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = true
endfunction
private function LeftUp takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = false
endfunction
private function RightDown takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*1+GetPlayerId(GetTriggerPlayer())] = true
endfunction
private function RightUp takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*1+GetPlayerId(GetTriggerPlayer())] = false
endfunction
private function DownDown takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*2+GetPlayerId(GetTriggerPlayer())] = true
endfunction
private function DownUp takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*2+GetPlayerId(GetTriggerPlayer())] = false
endfunction
private function UpDown takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*3+GetPlayerId(GetTriggerPlayer())] = true
endfunction
private function UpUp takes nothing returns nothing
set KeyDown[MAX_NUMBER_OF_PLAYERS*3+GetPlayerId(GetTriggerPlayer())] = false
endfunction
private function GetActionFunc takes integer i returns code
if i == 0 then
return function LeftDown
elseif i == 1 then
return function RightDown
elseif i == 2 then
return function DownDown
elseif i == 3 then
return function UpDown
elseif i == 4 then
return function LeftUp
elseif i == 5 then
return function RightUp
elseif i == 6 then
return function DownUp
elseif i == 7 then
return function UpUp
endif
return null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local integer i = 0
local integer i2 = 0
loop
exitwhen i >= 4
set ArrowDetectors[i] = CreateTrigger()
set ArrowDetectors[i+4] = CreateTrigger()
loop
exitwhen i2 >= MAX_NUMBER_OF_PLAYERS
call TriggerRegisterPlayerEvent(ArrowDetectors[i], Player(i2), GetKeyEventDown(i))
call TriggerRegisterPlayerEvent(ArrowDetectors[i+4], Player(i2), GetKeyEventUp(i))
set i2 = i2 + 1
endloop
set i2 = 0
call TriggerAddAction(ArrowDetectors[i], GetActionFunc(i))
call TriggerAddAction(ArrowDetectors[i+4], GetActionFunc(i+4))
set i = i + 1
endloop
endfunction
endlibrary
//TESH.scrollpos=58
//TESH.alwaysfold=0
library ArrowMovement initializer Init requires KeyDetection
globals
private timer tim
private real EnumDestructableCenterX = 0
private real EnumDestructableCenterY = 0
private real EnumDestructableRadius = 0
private integer i = 0
private boolean b = true
private group dummygroup
private boolexpr dmgunits
private boolexpr reboundunit
private boolexpr stopunit
private constant integer MAX_PLAYERS = 12
private constant real TimerInterval = 0.04
endglobals
private function EnumDestructablesInCircleFilter takes nothing returns boolean
local real destx = GetDestructableX(GetFilterDestructable())
local real desty = GetDestructableY(GetFilterDestructable())
local boolean result
local real dx = EnumDestructableCenterX - destx
local real dy = EnumDestructableCenterY - desty
set result = SquareRoot(dx * dx + dy * dy) <= EnumDestructableRadius
return result
endfunction
private function EnumDestructablesInCircle takes real radius, real x, real y, code actionFunc returns nothing
local rect r
local boolexpr e = Condition(function EnumDestructablesInCircleFilter)
if radius >= 0 then
set EnumDestructableCenterX = x
set EnumDestructableCenterY = y
set EnumDestructableRadius = radius
set r = Rect(x-radius, y-radius, x+radius, y+radius)
call EnumDestructablesInRect(r, e, actionFunc)
endif
call RemoveRect(r)
set r = null
call DestroyBoolExpr(e)
set e = null
endfunction
private function KillDestructables takes nothing returns nothing
if GetDestructableLife(GetEnumDestructable()) > 0 then
call KillDestructable(GetEnumDestructable())
set ArrowMovement_Data.data[i].speed = ArrowMovement_Data.data[i].speed - ArrowMovement_Data.data[i].power*ArrowMovement_Data.data[i].speed/4
endif
endfunction
private function ReboundDestructable takes nothing returns nothing
if GetDestructableLife(GetEnumDestructable()) > 0 then
set ArrowMovement_Data.data[i].speed = ArrowMovement_Data.data[i].speed - ArrowMovement_Data.data[i].speed*2
set b = false
endif
endfunction
private function StopDestructable takes nothing returns nothing
if GetDestructableLife(GetEnumDestructable()) > 0 then
set b = false
endif
endfunction
private function DamageUnits takes nothing returns boolean
if GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and GetFilterUnit()!= ArrowMovement_Data.data[i].u and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) then
call UnitDamageTarget(ArrowMovement_Data.data[i].u, GetFilterUnit(), ArrowMovement_Data.data[i].speed*ArrowMovement_Data.data[i].dmg, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
set ArrowMovement_Data.data[i].speed = ArrowMovement_Data.data[i].speed - ArrowMovement_Data.data[i].speed*2
set b = false
endif
return false
endfunction
private function ReboundUnit takes nothing returns boolean
if GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and GetFilterUnit()!= ArrowMovement_Data.data[i].u and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) then
set ArrowMovement_Data.data[i].speed = ArrowMovement_Data.data[i].speed - ArrowMovement_Data.data[i].speed*2
set b = false
endif
return false
endfunction
private function StopUnit takes nothing returns boolean
if GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and GetFilterUnit()!= ArrowMovement_Data.data[i].u and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) then
set b = false
endif
return false
endfunction
private function execute takes nothing returns nothing
local player p = null
local real oldX
local real oldY
local ArrowMovement_Data dat = 0
set i = 0
//main loop, executes the movement
loop
exitwhen i >= MAX_PLAYERS
set dat = ArrowMovement_Data.data[i]
if dat != 0 then
set p = Player(i)
//main code section, executes the movement
//first, detect if unit should turn left
if IsKeyDown(ARROW_LEFT, p) then
set dat.angle = dat.angle + dat.turnspeed
//debug call BJDebugMsg("angle is "+R2S(dat.angle))
if dat.angle >= 360 then
set dat.angle = dat.angle - 360
endif
//next, detect if unit should turn right
elseif IsKeyDown(ARROW_RIGHT, p) then
set dat.angle = dat.angle - dat.turnspeed
//debug call BJDebugMsg("angle is "+R2S(dat.angle))
if dat.angle <= 0 then
set dat.angle = dat.angle + 360
endif
endif
//now detect if unit should move forward
if dat.usePhysics then
if IsKeyDown(ARROW_UP, p) then
set dat.speed = dat.speed + dat.power
//now detect if unit should move backwards
elseif IsKeyDown(ARROW_DOWN, p) then
set dat.speed = dat.speed - dat.power/2
endif
//all movements checked, now do last speed alterations
set dat.speed = dat.speed * ArrowMovement_Data.friction
if dat.speed <= dat.power/5 and dat.speed >= -dat.power/5 or (dat.speed < 0 and not dat.canReverse) then
set dat.speed = 0
endif
else
if IsKeyDown(ARROW_UP, p) then
set dat.speed = dat.power
//now detect if unit should move backwards
elseif IsKeyDown(ARROW_DOWN, p) and dat.canReverse then
set dat.speed = -dat.power/2
else
set dat.speed = 0
endif
endif
//speed altered as necessary, now move the unit
set oldX = dat.x
set oldY = dat.y
set dat.x = dat.x + dat.speed * Cos(dat.angle * bj_DEGTORAD)
set dat.y = dat.y + dat.speed * Sin(dat.angle * bj_DEGTORAD)
if not IsTerrainPathable(dat.x, dat.y, dat.pathingType) then
//terrain is pathable, proceeding to move unit
//no point in doing any collision checks if radius is 0 - they won't work anyway.
if dat.radius != 0 and dat.pathingType != PATHING_TYPE_FLYABILITY then
if dat.usePhysics then
if dat.canDestroyStuff then
call EnumDestructablesInCircle(dat.radius, dat.x, dat.y, function KillDestructables)
call GroupEnumUnitsInRange(dummygroup, dat.x, dat.y, dat.radius, dmgunits)
else
call EnumDestructablesInCircle(dat.radius, dat.x, dat.y, function ReboundDestructable)
call GroupEnumUnitsInRange(dummygroup, dat.x, dat.y, dat.radius, reboundunit)
endif
else
call EnumDestructablesInCircle(dat.radius, dat.x, dat.y, function StopDestructable)
call GroupEnumUnitsInRange(dummygroup, dat.x, dat.y, dat.radius, stopunit)
endif
endif
if dat.x != oldX and dat.y != oldY then
//call SetUnitPosition(dat.u, dat.x, dat.y) //this to cancel orders
call SetUnitX(dat.u, dat.x)
call SetUnitY(dat.u, dat.y)
call SetUnitFacing(dat.u, dat.angle)
if dat.moving == false then
set dat.moving = true
call SetUnitAnimationByIndex(dat.u, dat.animIndex)
endif
else
set dat.x = GetUnitX(dat.u)
set dat.y = GetUnitY(dat.u)
if dat.moving == true then
set dat.moving = false
call SetUnitAnimation(dat.u, "stand")
endif
endif
if not b then
set dat.x = oldX
set dat.y = oldY
set b = true
endif
else
set dat.x = oldX
set dat.y = oldY
endif
//unit has moved, now set anim speed
call SetUnitTimeScale(dat.u, dat.speed/12)
//animation speed set, one last thing - set camera's angle
if GetLocalPlayer() == p then
call SetCameraField(CAMERA_FIELD_ROTATION, dat.angle, TimerInterval)
endif
//clean leaks
set p = null
endif
set i = i + 1
endloop
endfunction
struct ArrowMovement_Data
unit u
real speed
real power
real turnspeed
real angle
real radius
real x
real y
real dmg
integer playerId
boolean canReverse
boolean canDestroyStuff
boolean usePhysics
integer animIndex
pathingtype pathingType
boolean moving = false
static real friction = 0.975
static ArrowMovement_Data array data
static integer count = 0
static method create takes unit whichUnit, player id, real power, real turnspeed, real radius, real dmg, integer AnimIndex, pathingtype whichPathing returns ArrowMovement_Data
local integer i = GetPlayerId(id)
local ArrowMovement_Data d = ArrowMovement_Data.allocate()
set d.power = power
set d.turnspeed = turnspeed
set d.speed = 0
set d.radius = radius
set d.x = GetUnitX(whichUnit)
set d.y = GetUnitY(whichUnit)
set d.dmg = dmg
set d.u = whichUnit
set d.angle = GetUnitFacing(whichUnit)
set d.canReverse = true
set d.canDestroyStuff = true
set d.playerId = i
set d.pathingType = whichPathing
set d.usePhysics = true
set d.animIndex = AnimIndex
set ArrowMovement_Data.data[i] = d
call SetUnitAnimationByIndex(whichUnit, AnimIndex)
if GetLocalPlayer() == id then
call SetCameraTargetController(whichUnit, 0, 0, false)
endif
if ArrowMovement_Data.count <= 0 then
call TimerStart(tim, TimerInterval, true, function execute)
endif
set ArrowMovement_Data.count = ArrowMovement_Data.count + 1
set whichUnit = null
set id = null
return d
endmethod
method onDestroy takes nothing returns nothing
if GetLocalPlayer() == Player(.playerId) then
call ResetToGameCamera(0)
endif
call SetUnitAnimation(.u, "stand")
set .u = null
set ArrowMovement_Data.count = ArrowMovement_Data.count - 1
if ArrowMovement_Data.count == 0 then
call TimerStart(tim, TimerInterval, true, function execute)
endif
endmethod
endstruct
public function StartArrowControl takes unit whichUnit, player id, real power, real turnspeed, real collisionsize, real dmg, integer AnimIndex, pathingtype whichPathing returns boolean
if ArrowMovement_Data.data[GetPlayerId(id)] == 0 then
call ArrowMovement_Data.create(whichUnit, id, power, turnspeed, collisionsize, dmg, AnimIndex, whichPathing)
return true
endif
return false
endfunction
public function StopArrowControl takes player id returns boolean
if ArrowMovement_Data.data[GetPlayerId(id)] != 0 then
call ArrowMovement_Data.data[GetPlayerId(id)].destroy()
set ArrowMovement_Data.data[GetPlayerId(id)] = 0
return true
endif
return false
endfunction
public function GetControlledUnit takes player id returns unit
return ArrowMovement_Data.data[GetPlayerId(id)].u
endfunction
public function SetCanDestroyStuff takes player id, boolean flag returns nothing
set ArrowMovement_Data.data[GetPlayerId(id)].canDestroyStuff = flag
endfunction
public function SetCanReverse takes player id, boolean flag returns nothing
set ArrowMovement_Data.data[GetPlayerId(id)].canReverse = flag
endfunction
public function SetUsePhysics takes player id, boolean flag returns nothing
set ArrowMovement_Data.data[GetPlayerId(id)].usePhysics = flag
endfunction
public function SetPower takes player id, real newPower returns nothing
set ArrowMovement_Data.data[GetPlayerId(id)].power = newPower
endfunction
public function SetTurnspeed takes player id, real newSpeed returns nothing
set ArrowMovement_Data.data[GetPlayerId(id)].turnspeed = newSpeed
endfunction
private function Init takes nothing returns nothing
set tim = CreateTimer()
set dummygroup = CreateGroup()
set dmgunits = Condition(function DamageUnits)
set reboundunit = Condition(function ReboundUnit)
set stopunit = Condition(function StopUnit)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
'What is this system?'
// This system is a very advanced Arrow Key Movement system featuring:
'Features:'
// -Advanced First/Third Person Camera system!
// -The first ever (as far as I am aware) JASS Arrow Key Movement system to incorporate
// advanced accelleration and friction!
// -Plough through trees and other destructables!
// -Leak-free (I think)
// -Very efficient coding can easily run on most computers for 12 arrow key controlled units at once!
// -Now supports flying/floating/amphibious units! Added a new parameter.
// -Unit's animation slows down/speeds up or even runs backwards depending on speed!
'Known Solvable Bugs'
'Known Unsolvable Bugs'
//
'Room for improvement:'
// -I tried (and failed) briefly to make the unit be able to run over other units. I hope to make this
// work in the future.
'System Requirements:'
// -This system uses vJASS so make sure to have JassNewGenPack installed before using the system.
'User Requirements:'
// -Decent JASS or GUI knowledge.
'How to Implement'
// To implement this system is very easy. Simply copy the whole 'Arrow Key Movement' folder into your trigger editor
// and voila! You have the system in your map.
'How To Use'
'JASSers:'
// To use this very advanced arrow movement system, simply call this function:
function ArrowMovement_StartArrowControl takes unit whichUnit, player id, real power, real turnspeed, real collisionsize, real dmg, integer AnimIndex, pathingtype whichPathing returns boolean
unit whichUnit //this is the unit you want the player to control
player id //this is the player you want to control the unit
real power //this is this accelleration AKA how much is added to the speed each 'frame'. If UsePhysics is false,
// this becomes the speed at which the unit moves
real turnspeed //this is how fast your unit will turn around
real collisionsize //this should probably be twice the unit's collision size in the object editor.
// a collision size of 0 means it will pass through trees
real dmg //how much damage it does when it hits a unit if canDestroyStuff is true. this is multiplied by speed on impact.
integer AnimIndex //this should be your unit's animation index of the 'walk' animation, but can be
// another if you wish (though I don't recommend it as it will prbably look stupid). There is a list of some models' indexes in the trigger
// "Animation Indexes"
pathingtype whichPathing //this is simple. If you want a flying unit, it is PATHING_TYPE_FLYABILITY, ground unit is
// PATHING_TYPE_WALKABILITY, amphibious is PATHING_TYPE_AMPHIBIOUSPATHING and floating is PATHING_TYPE_FLOATABILITY
returns boolean //if the player is already controlling a unit, the function won't work and false wil be returned
// And to cancel control of a unit is very simple:
function ArrowMovement_StopArrowControl takes player id returns boolean
player id //the player who you want to stop controlling a unit
returns boolean //if the player is not controlling a unit when this function calls anyway, the function will do nothing
// and return false
// If you want to know which unit a player is controlling use this:
function ArrowMovement_GetControlledUnit takes player id returns unit
player id //the player who's controlled unit you want to know
return unit //returns the unit controlled by the player
// this changes whether the unit uses physics. if this is flagged false, power is turned into speed and accelleration/friction are disabled.
function ArrowMovement_SetUsePhysics takes player id, boolean flag returns nothing
player id //the player who's controlled unit's physics you want to change
boolean flag //true or false
// if you want to be able to destroy trees/other destructibles by ramming into them,
// and damage units by running them over, this should be true
function ArrowMovement_SetCanDestroyStuff takes player id, boolean flag returns nothing
player id //the player who's controlled unit you want to change
boolean flag //true or false
//if you want to be able to move backwards, this should be true
function ArrowMovement_SetCanReverse takes player id, boolean flag returns nothing
player id //the player who's controlled unit you want to change
boolean flag //true or false
// Change the movement speed/accelleration of the unit
function ArrowMovement_SetSpeed takes player id, real newPower returns nothing
player id //player who's unit's speed you want to change
real newPower //the new speed/accelleration
function function ArrowMovement_SetTurnSpeed takes player id, real newSpeed returns nothing
player id //player who's unit's turn speed you want to change
real newSpeed //the new turn speed
'GUIers'
// Please follow instructions in the disabled trigger entitled 'Init Arrow Movement GUI Example'
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Following is a list of walk animation indexes for human melee models:
Sorceress: 2
Steam Engine: 2/3
Spell Breaker: 1
Priest: 1
Gryphon Rider: 3
Peasant: 10/11/12
Footman: 6
Knight: 0
Rifleman: 6
Dragonhawk Rider: 4
Mortar Team: 0
Flying Machine: 4/6
Paladin: 12
Archmage: 0
Mountain King: 7
Phoenix: 4
Blood Mage: 2
Water Elemental: 3