NoobImbaPro
You can change this now in User CP.
- Reaction score
- 60
Advanced Unit Moving System
Hello thehelper.net, this is my first system as its title says it moves units in the 3D Wacraft world.
Requires:
JassHelper 0.A.2.B
T32
AutoFly
TerrainPathability
How to implement:
--Copy the code below to a trigger you just created and set it to custom text, from edit catalog on the trigger menu above.
--Change the period of T32 if you don't download the map to 0.025 to make the movement realistic.
--And you're done !
Any Feedback or Question will be recommended, oh and this system is updated regularly, so checkout once a while
Here is the code:
Example: Double Jump
CHANGELOG:
Parabola Helping Examples:
=========================================================================================
Hello thehelper.net, this is my first system as its title says it moves units in the 3D Wacraft world.
Requires:
JassHelper 0.A.2.B
T32
AutoFly
TerrainPathability
How to implement:
--Copy the code below to a trigger you just created and set it to custom text, from edit catalog on the trigger menu above.
--Change the period of T32 if you don't download the map to 0.025 to make the movement realistic.
--And you're done !
Any Feedback or Question will be recommended, oh and this system is updated regularly, so checkout once a while
Here is the code:
JASS:
library AUMS initializer Init requires T32, AutoFly, TerrainPathability
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// System Name: Advanced Unit Moving System (in brief AUMS)
//
// Version: 2.3.D
//
//
// *===============================================================================================================*
// *~What does it do: *
// * It moves units, like knockback, jump, homing missile, slide and so on... *
// * Can be used on special effect models attached to dummies, like simulating a better handled stampede *
// * I can't tell you everything you can do with that, as this system is the base of any movement type *
// *===============================================================================================================*
//
//
// *===============================================================================================================*
// *~Charateristics: *
// * Advanced Mode: *
// * This mode handles basically straight line movement tasks. Takes the unit that will move and apply a *
// * a moving task towards the point you need using polar offset. With this mode you can simulate a simple *
// * knockback, a jump and creating abilities like shockwave where the wave is a dummy and much more. *
// * Homing Mode: *
// * This mode helps you cover homing movement issues through a simple way. It takes the unit that is moved *
// * and the unit target and it terminates when the first unit reaches the other in radius given. *
// * It's helpful for creating homing missiles or attack projectiles. *
// *===============================================================================================================*
//
//
// *===============================================================================================================*
// *~How to use: *
// * Advanced Mode: *
// * If you don't want to do anything special with this mode there is a function wrapper called: *
// * function StartMovingTaskAdvanced takes whichUnit, distance, baseheight, height, angle, duration, checkPathing *
// * It starts a general moving task based on Advanced mode *
// * Now if you want to do anything special create a struct, extend it to AUMS_Interface and implement *
// * AUMS_Advanced. *
// * Add to struct any method you need, any variable you want and use the static method start(variables) *
// * The variables it takes are exactly the same with the function above *
// * Homing Mode: *
// * If you don't want to do anything special with this mode there is a function wrapper called: *
// * function StartMovingTaskHoming takes unit whichUnit, unit target, real baseheight, real speed, real radius *
// * It starts a general moving task based on Homing mode *
// * Add to struct any method you need, any variable you want and use the static method start(variables) *
// * The variables it takes are exactly the same with the function above *
// *===============================================================================================================*
//
//
// *===============================================================================================================*
// *~Interface methods: *
// * These methods are your helpful ones, as you need them to use any other method made by you *
// * You don't have to use all of them if you don't need them *
// * *
// * method onCreate takes nothing returns nothing *
// * Is called when a successfully struct is created with no errors, for anything you want to initialize use *
// * method, as you can't use the create() method. *
// * *
// * method onPeriodic takes nothing returns nothing *
// * Is called every T32 Period, this runs after onCollide() and onMapEnd() methods *
// * *
// * method onMapEnd takes nothing returns nothing *
// * Is called when your unit meets the map's max boundaries *
// * *
// * method onCollide takes nothing returns nothing defaults nothing *
// * Is called when your unit is on unwalkable terrain, if the task makes your unit fly, this method *
// * is never called *
// * *
// * method onPredestroy takes nothing returns nothing *
// * Is called when the moving task is done, it is executed right before destroy method, use this instead *
// *===============================================================================================================*
//
// *===============================================================================================================*
// *~Features: *
// * Features are the optional methods called right after the task start to make these modes more unique *
// * *
// * Acceleration: Used only in homing mode. It increases moving unit's speed over second. *
// * Calling method (structname).setAcceleration(percent) will increase unit's speed by the amount given over *
// * every second. *
// * *
// *===============================================================================================================*
//
// *=========================================*
// *~Pros: *
// * Safest movement without any bugs. *
// * Can be twisted using variables with *
// * /*Safe*/ tags *
// *=========================================*
// *~Cons: *
// * Ugly struct setup *
// *=========================================*
//
//
// <Bonus Functions Names>
// [Reminder] --> All angle functions return values in degrees format.
// --> Points Are in [x,y] format.
// --> Distance from Unit to Point and Point to Unit are same.
// GetUnitAngleFromUnitToUnit: Takes Unit A and Unit B, returns the angle from the first to second unit.
// GetUnitAngleFromUnitToPoint: Takes Unit A and Point B, returns the angle from the unit to the point inputed.
// GetUnitAngleFromPointToUnit: Takes Point A and Unit B, returns the angle from the point to the unit inputed.
// GetUnitDistanceFromUnitToUnit: Takes Unit A and Unit B, returns the distance between them.
// GetUnitDistanceFromUnitToPoint: Takes Unit A and Point B, returns the distance between them.
// valueConvert: takes real value and move task duration and attaches it to Timer32 standards,for onMove method.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//=================================================================================================================//
//=================================================================================================================//
function GetAngleFromUnitToUnit takes unit a, unit b returns real
local real x1 = GetWidgetX(a)
local real y1 = GetWidgetY(a)
local real x2 = GetWidgetX(b)
local real y2 = GetWidgetY(b)
return Atan2(y2-y1,x2-x1)
endfunction
//=================================================================================================================//
//=================================================================================================================//
function GetAngleFromUnitToPoint takes unit u, real x2, real y2 returns real
local real x1 = GetWidgetX(u)
local real y1 = GetWidgetY(u)
return Atan2(y2-y1,x2-x1)
endfunction
//=================================================================================================================//
//=================================================================================================================//
function GetAngleFromPointToUnit takes real x1, real y1, unit ut returns real
local real x2 = GetWidgetX(ut)
local real y2 = GetWidgetY(ut)
return Atan2(y2-y1,x2-x1)
endfunction
//=================================================================================================================//
//=================================================================================================================//
function GetDistanceFromUnitToUnit takes unit a, unit b returns real
local real x1 = GetWidgetX(a)
local real y1 = GetWidgetY(a)
local real x2 = GetWidgetX(b)
local real y2 = GetWidgetY(b)
return SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction
//=================================================================================================================//
//=================================================================================================================//
function GetDistanceFromUnitToPoint takes unit a1, real x2, real y2 returns real
local real x1 = GetWidgetX(a1)
local real y1 = GetWidgetY(a1)
return SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction
//=================================================================================================================//
//=================================================================================================================//
function valueConvert takes real x, real duration returns real
return x*T32_PERIOD/duration
endfunction
//=================================================================================================================//
//=================================================================================================================//
// Don't touch anything below, unless you know what are you doing
globals
private rect World
private region MapArea
private integer stop = 851972 //stop order
endglobals
//The Main Interface
interface AUMS_Interface
method onCreate takes nothing returns nothing defaults nothing
method onDelayPeriodic takes nothing returns nothing defaults nothing
method onStart takes nothing returns nothing defaults nothing
method onPeriodic takes nothing returns nothing defaults nothing
method onMapEnd takes nothing returns nothing defaults nothing
method onCollide takes nothing returns nothing defaults nothing
method onPredestroy takes nothing returns nothing defaults nothing
endinterface
//Your struct's setup, takes struct name and module implement, use "Advanced" and "Homing"
//That's done like this because we can't have anonymous structs in vJass
//! textmacro AUMS_struct takes name
struct $name$ extends AUMS_Interface
AUMS_Delay_$name$ delayNode
//! endtextmacro
//! textmacro AUMS_endstruct takes name, module
implement AUMS_$module$
endstruct
struct AUMS_Delay_$name$
$name$ callback
method destroy takes nothing returns nothing
call callback.startPeriodic()
call callback.onStart()
call this.stopPeriodic()
call this.deallocate()
endmethod
method periodic takes nothing returns nothing
if T32_Tick <= callback.delayTick then
call callback.onDelayPeriodic()
else
call this.destroy()
endif
endmethod
implement T32x
static method create takes $name$ node returns thistype
local thistype this = thistype.allocate()
set callback = node
call this.startPeriodic()
return this
endmethod
endstruct
//! endtextmacro
//! textmacro AUMSLinkNodeList
loop
exitwhen i == thistype.counter
set i = i + 1
if prime<i>.affected == whichUnit then
set base = prime<i>
loop
exitwhen base.next == 0
set base = base.next
endloop
set base.next = this
set this.prev = base
set this.next = 0
set this.index = 0
set find = true
endif
endloop
if not find then
set thistype.counter = thistype.counter + 1
set prime[thistype.counter] = this
set this.next = 0
set this.prev = 0
set this.index = thistype.counter
endif
//! endtextmacro
//! textmacro AUMSDeleteNodeMethods
private method destroy takes nothing returns nothing
call .stopPeriodic()
call .onPredestroy()
if .index == 0 then
set this.next.prev = this.prev
set this.prev.next = this.next
else
if this.next != 0 then
set this.next.index = this.index
set prime[.index] = this.next
set this.next.prev = 0
else
if .index != thistype.counter then
set prime[.index] = prime[thistype.counter]
endif
set thistype.counter = thistype.counter - 1
call IssueImmediateOrderById(this.affected, stop)
endif
endif
set this.affected = null
call this.deallocate()
endmethod
static method DeleteEachNode takes unit whichUnit returns nothing
local integer i = 0
local thistype node
loop
set i = i + 1
exitwhen i > thistype.counter
if prime<i>.affected == whichUnit then
set node = prime<i>
loop
exitwhen node.next == 0
call node.next.destroy()
endloop
call node.destroy()
return
endif
endloop
debug call BJDebugMsg("AUMS: No unit found for node destruction")
endmethod
//! endtextmacro
//=================================================================================================================//
//==================================================Advanced Mode==================================================//
//=================================================================================================================//
module AUMS_Advanced
static integer counter = 0
static thistype array prime //Prime nodes
private integer index //Number if it's a prime struct, 0 if not
readonly thistype next //next node
readonly thistype prev //previous node
private thistype base //base node for fly height check
readonly unit affected //currently moving unit
//Current positions
/*Safe*/ real x
/*Safe*/ real y
readonly real z
readonly real step //distance moved per T32 period seconds
/*Safe*/ real angle //current angle for movement direction
/*Safe*/ real height //the parabola height modifier, it represents the max height
readonly real baseheight //the height the unit will have at start of moving task
/*Safe*/ real distance //the overall distance the unit will do
/*Safe*/ real currentdistance //the current distance done from starting point
readonly real delayTick
private boolean IsFlyTask //Pathing checker modifier, used on movement task initialization
/*Safe*/ boolean CheckPathing //Variable that checks if unit's current location is walkable
//Parabola function, takes currentdistance from start point and returns height.
method getZ takes real x returns real
return (4*.height*x/.distance)*(1-x/.distance) + .baseheight
endmethod
//! runtextmacro AUMSDeleteNodeMethods()
//Moving task, executed every T32 period, and moves unit a "step" forwards current angle given
method periodic takes nothing returns nothing
if .currentdistance >= .distance then
call .destroy()
endif
set .currentdistance = .currentdistance + .step //Distance counter
set .x = GetWidgetX(.affected) + .step * Cos(.angle) //Is coded like this
set .y = GetWidgetY(.affected) + .step * Sin(.angle) //to prevent bugs
if .IsFlyTask then //Check if our task is a flying one, else prevent these not nessesary actions
set .z = .getZ(.currentdistance)
//Check if this fly height is less from another instance, to replace it
//Because greater fly height overrides lower one.
set base = this
loop
set base = base.next
exitwhen base == 0
if .z < base.z then
set .z = base.z
endif
endloop
call SetUnitFlyHeight(.affected, .z, 0)
endif
//Safe point checkings
if IsPointInRegion(MapArea, .x, .y) then
if .CheckPathing and not .IsFlyTask then
if IsTerrainWalkable(.x, .y) then
call SetUnitX(.affected, .x)
call SetUnitY(.affected, .y)
else
call .onCollide()
endif
else
call SetUnitX(.affected, .x)
call SetUnitY(.affected, .y)
endif
else
call .onMapEnd()
endif
call .onPeriodic()
endmethod
implement T32x
static method create takes unit whichUnit, real distance, real baseheight, real height, real angle, real duration, real delay, boolean checkPathing returns thistype
local thistype this = thistype.allocate()
local boolean find = false
local integer i = 0
if GetWidgetLife(whichUnit) < 0.405 or whichUnit == null then
call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFThe unit you want to move is dead or doesn't exist|r")
elseif duration < 0 or delay < 0 then
call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFDuration/Delay has negative value on this unit|r")
call SelectUnit(.affected, true)
elseif distance <= 0 then
call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFDistance has negative or zero value on this unit|r")
call SelectUnit(.affected, true)
elseif baseheight + height < 0 then
call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFThis unit's negative height is more than baseheight|r")
call SelectUnit(.affected, true)
else
//! runtextmacro AUMSLinkNodeList()
set this.affected = whichUnit
set this.distance = distance
set this.angle = angle
set this.baseheight = baseheight
set this.height = height
call SetUnitFlyHeight(this.affected, baseheight, 0)
set this.step = (distance/duration) * T32_PERIOD
set this.currentdistance = this.step
set this.z = 0
set this.IsFlyTask = (this.height != 0)
set this.CheckPathing = checkPathing
call this.onCreate()
if delay == 0 then
call .startPeriodic()
else
set this.delayTick = T32_Tick + R2I(delay*T32_FPS)
call delayNode.create(this)
endif
return this
endif
call this.deallocate()
return 0
endmethod
endmodule
//=================================================================================================================//
//==================================================Advanced Mode==================================================//
//=================================================================================================================//
//=================================================================================================================//
//===================================================Homing Mode===================================================//
//=================================================================================================================//
module AUMS_Homing
static integer counter = 0
static thistype array prime //Prime nodes
private integer index //Number if it's a prime struct, 0 if not
readonly thistype next //next node
readonly thistype prev //previous node
private thistype base //base node for fly height check
readonly unit affected //currently moving unit
readonly unit target //moving's target
//current positions
//Moving Unit
/*Safe*/ real x1
/*Safe*/ real y1
//Target Unit
/*Safe*/ real x2
/*Safe*/ real y2
readonly real radius //radius check
readonly real step //distance per T32_Period
readonly real stepCurr
/*Safe*/ real acceleration //movement increase per second
readonly real accelerationCurr
readonly real angle //current angle for movement direction
/*Safe*/ real currentdistance //the current distance done from starting point
readonly real delayTick
//Acceleration feature
method setAcceleration takes real increasePercent returns thistype
set acceleration = (increasePercent*T32_PERIOD)/100
return this
endmethod
//! runtextmacro AUMSDeleteNodeMethods()
method periodic takes nothing returns nothing
set .x2 = GetWidgetX(.target)
set .y2 = GetWidgetY(.target)
set .angle = Atan2(y2-y1,x2-x1)
set .x1 = GetWidgetX(.affected) + .stepCurr * Cos(.angle)
set .y1 = GetWidgetY(.affected) + .stepCurr * Sin(.angle)
set .currentdistance = SquareRoot((.x1-.x2)*(.x1-.x2) + (.y1-.y2)*(.y1-.y2))
set .accelerationCurr = .accelerationCurr + .acceleration
set .stepCurr = .step * (1+.accelerationCurr)
if .currentdistance > .radius then
//Safe point check
if IsPointInRegion(MapArea, .x1, .y1) then
call SetUnitX(.affected, .x1)
call SetUnitY(.affected, .y1)
call .onPeriodic()
else
call .onMapEnd()
endif
else
call .destroy()
endif
endmethod
implement T32x
static method create takes unit whichUnit, unit target, real baseheight, real speed, real delay, real radius returns thistype
local thistype this = thistype.allocate()
local boolean find = false
local integer i = 0
if GetWidgetLife(whichUnit) < 0.405 or whichUnit == null then
debug call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFThe unit you want to move is dead or doesn't exist|r")
elseif baseheight< 0 then
debug call BJDebugMsg("|cFFFF0000Error:|r |cFFC6E2FFBaseheight has negative value on this unit|r")
debug call SelectUnit(.affected, true)
else
//! runtextmacro AUMSLinkNodeList()
set this.affected = whichUnit
set this.target = target
call SetUnitFlyHeight(whichUnit, baseheight, 0)
set this.step = speed * T32_PERIOD
set this.stepCurr = step
set this.radius = radius
set this.x1 = GetWidgetX(whichUnit)
set this.y1 = GetWidgetY(whichUnit)
set this.acceleration = 0
set this.accelerationCurr = 0
call SetUnitFlyHeight(.affected, baseheight, 0)
call this.onCreate()
if delay == 0 then
call .startPeriodic()
else
set this.delayTick = T32_Tick + R2I(delay*T32_FPS)
call delayNode.create(this)
endif
return this
endif
call this.destroy()
return 0
endmethod
endmodule
//=================================================================================================================//
//===================================================Homing Mode===================================================//
//=================================================================================================================//
//Initialize Map Bounds
private function Init takes nothing returns nothing
set World = GetWorldBounds()
set MapArea = CreateRegion()
call RegionAddRect(MapArea, World)
endfunction
endlibrary</i></i></i></i>
Example: Double Jump
JASS:
scope jump initializer init
globals
private constant real mdistance = 700
private constant real baseheight = 0
private constant real height = 250
private constant real duration = 0.7
private constant integer id = 'AHds'
private string attach = "chest"
private string stringSFX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
endglobals
//! runtextmacro AUMS_struct("jump")
effect sfx
method onCreate takes nothing returns nothing
if this.prev == 0 then //Check if previous created struct exists
set sfx = AddSpecialEffectTarget(stringSFX, .affected, attach)
call PauseUnit(.affected, true)
endif
endmethod
method onPredestroy takes nothing returns nothing
if this.next == 0 then
call DestroyEffect(.sfx)
call PauseUnit(.affected, false)
set .sfx = null
else
set this.next.sfx = .sfx
endif
endmethod
//! runtextmacro AUMS_endstruct("jump", "Advanced")
private function action takes nothing returns nothing
local unit u = GetTriggerUnit()
local real angle = GetUnitFacing(u) * bj_DEGTORAD
if GetSpellAbilityId() == id then
call jump.create(u, mdistance, baseheight, height, angle, duration, 0, true)
//Just to see the override (you may delete this)
call jump.create(u, mdistance, baseheight, height+150, angle, duration, 0.6, true)
endif
set u = null
endfunction
private function init takes nothing returns nothing
local trigger tt = CreateTrigger()
call TriggerAddAction(tt, function action)
call TriggerRegisterAnyUnitEventBJ(tt, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set tt = null
endfunction
endscope
CHANGELOG:
Code:
v1.0: Initial release
v1.1.A: Optimised code a bit
v1.2.A: Respresent of a more normal system
v1.3.A: Reworked system to use textmacro and documentation
v1.4.A: Replaced duration in variables field for single line mode with speed, reworked documentation, replaced jump example with knockback one, because the first was too simple
v1.4.B: Changed system's set up from using textmacro's to modules, set .move() method private
v1.5.A: Added safety conditions, new method onCollide() runs when next point is not a safe one
v1.6.A: Implemented TerrainPathability Library
v1.6.B: Only IsPointInMap and IsTerrainWalkable now trigger on collide function.
v1.6.C: Point is in map area triggers onError function, and IsTerrainWalkable triggers onCollideFunction.
v1.6.D: Documentation rework, coding rework
v2.0.A: Major improvements, implement of homing module
v2.0.B: Trigger evaluations removed
v2.1.A: Improved documentation, delete of simple mode
v2.1.B: Reimplement of delay feature, implement of acceleration feature in homing mode
v2.1.C: Removed a,b parabola parameters and replaced with the original max height and max distance variables. Changed their scope type to safe so you can use the parabola dynamically without concequences.
v2.2.A: Changed stuct link type for better performance,accessibility and complexibility of node handling in advanced mode.
v2.3.A: Major improvements, once again sacrificing a tiny amount of speed for enchancing the AUMS system, implement of unit nodes delete.
v2.3.B: Complete of the delay feature, uglier but dynamic struct setup.
v2.3.C: Documentation update.
v2.5.A: Todo: Turn parabola method into feature.
...
v3.0.A: Todo: Fuse Advanced+Homing movement systems into one node link. Finish system. Change name.

=========================================================================================
