System PushUnit

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
JASS:

library PushUnit initializer OnInit requires Table
/*
    Push Unit v1.0.0
        by kingkingyyk
        
    - A library that supports multiple instances of sliding/knocking back on every single unit.
    
    =========
    APIs :
    =========
    PushUnit(unit,velocity,angle,time,func1) -> integer
    - Push the unit with deceleration.
    
    PushUnitLinear(unit,velocity,angle,time,func1) -> integer
    - Push the unit uniformly.
    
    *** Angle used must be in radian.
    *** func1 will be fired when the slide/knockback is finished.
    *** Function must have "take integer slide returns nothing" as parameter.
    *** integer "slide" refers to the instance of the slide.
    *** If you don't need the function(s), then just put 0 in it.
    
    PushUnit_AttachData(integer slide, integer data)
    - Attach data to the instance.
    
    PushUnit_GetData(integer slide) -> integer
    - Get the data from the instance.
    
    PushUnit_SetGlobalTimeScale(real scale)
    - Define the speed of time in this library. 
    - Lower scale will result in lower pushing.
    
    PushUnit_GetGlobalTimeScale() -> real
    - Get the current speed of time in this library.
    
    PushUnit_SetTimeScale(unit, real scale)
    - Define the speed of time for certain unit.
    
    PushUnit_GetTimeScale(unit) -> real
    - Get the current speed of time for certain unit.
    
    *** The default time scale is 1.0
    
    GetPushedUnit(integer slide) -> unit
    - Get the pushed unit from the instance
    
    IsUnitBeingPushed(unit) -> boolean
    - Is unit being pushed currently?
    
    GetUnitPushSpeed(unit) -> real
    - Return the current speed of pushing on the unit.
    
    GetUnitPushAngle(unit) -> real
    - Return the current angle of pushing on the unit.
    *** Angle returned is in radian form.
    
    ----------Calculations for non-linear pushing-----------
    GetTime_DispVel(displacement, velocity) -> real
    - Calculate the time with given displacement and velocity.
    
    GetDisp_VelTime(velocity, time) -> real
    - Calculate the displacement with given velocity and time.
    
    GetVel_DispTime(displacement, time) -> real
    - Calculate the velocity with given displacement and time.
    
    --------Calculations for linear pushing----------
    Formula : Displacement = Velocity x time
    - Arrange the equation and get your needs.
    
    ==================
    Story behind :
    ==================
    This library utilizes the very simplest of physics, vectors, motion, and energy.
    Equations of motion :
    v=u+at
    s=ut+0.5at^2
    v^2=u^2+2as
    s=0.5(u+v)t
    
    where u=initial velocity, v=final velocity, a=acceleration, t=time of motion, s=displacement
    
    Equations of energy :
    E=0.5mv^2 (For kinetic energy)
    E=mgh (For potential energy)
    
    where m=mass of body, g=gravitional acceleration, h=delta height
    
    ==================
    Configurables :
    ==================                                                                          */
    globals
        private constant real TIMER_PERIOD=0.03125
//         -> Period of timer. Default is 0.03125 / 32Hz
        private constant boolean HEIGHT_SENSITIVE=true
//          -> If enabled, this library will obey the principle of conservation of energy.
        private constant real GRAVITATIONAL_ACC=9.81
//          -> Needless to touch unless you know what exactly it is.
    endglobals                                                                                  /*
    
    ==================
    Requirements :
    ==================
    Table - Vexorian
    Latest version of Jasshelper (Working correctly with 7 January 2012, cohadar's version)
    
    ***It is recommended to use BoundSentinel when using this library.
*/    
        
        
    globals
        private real TimeScale=1.0
        private HandleTable UnitAttachment=0
        private location TempLoc=Location(0.0,0.0)
    endglobals
    
    private function GetLocZ takes real x, real y returns real
        call MoveLocation(TempLoc,x,y)
        return GetLocationZ(TempLoc)
    endfunction
    
    private struct UnitData
        private static timer Timer=CreateTimer()
        thistype prev
        thistype next
        unit unit
        real x
        real y
        real lastZ
        integer stackLevel
        real timeScale
        
        static method operator [] takes unit u returns thistype
            local thistype this=UnitAttachment<u>
            if this==0 then
                set this=thistype.create()
                set this.unit=u
                set this.lastZ=GetLocZ(GetUnitX(this.unit),GetUnitY(this.unit))
                set this.timeScale=TimeScale
                set UnitAttachment<u>=this
            endif
            return this
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this=thistype(0).next
            local real currZ
            local real dz
            local real vel
            local real angle
            local real gravity
            loop
            exitwhen this==0
                call SetUnitX(this.unit,GetUnitX(this.unit)+this.x)
                call SetUnitY(this.unit,GetUnitY(this.unit)+this.y)
                static if HEIGHT_SENSITIVE then
                    set currZ=GetLocZ(GetUnitX(this.unit),GetUnitY(this.unit))
                    set gravity=GRAVITATIONAL_ACC*TIMER_PERIOD*this.timeScale
                    if currZ!=this.lastZ then
                        set dz=currZ-this.lastZ
                        if dz&gt;0.0 then
                            set vel=SquareRoot(2*gravity*dz)
                            set angle=Atan2(this.y,this.x)
                            set this.x=this.x-(vel*Cos(angle))
                            set this.y=this.y-(vel*Sin(angle))
                        else
                            set vel=SquareRoot(2*gravity*-dz)
                            set this.x=this.x+(vel*Cos(angle))
                            set this.y=this.y+(vel*Sin(angle))
                        endif
                        set this.lastZ=currZ
                    endif
                endif
                set this=this.next
            endloop
        endmethod
        
        method increment takes nothing returns nothing
            set this.stackLevel=this.stackLevel+1
            if this.stackLevel==1 then
                set thistype(0).prev.next=this
                set this.prev=thistype(0).prev
                set this.next=thistype(0)
                set thistype(0).prev=this
                
                if thistype(0).next==this then
                    call TimerStart(thistype.Timer,TIMER_PERIOD,true,function thistype.periodic)
                endif
            endif
        endmethod
        
        method decrement takes nothing returns nothing
            set this.stackLevel=this.stackLevel-1
            if this.stackLevel==0 then
                set UnitAttachment[this.unit]=0
                set this.x=0.0
                set this.y=0.0
                set this.prev.next=this.next
                set this.next.prev=this.prev
                call this.destroy()
                
                if thistype(0).next==0 then
                    call PauseTimer(thistype.Timer)
                endif
            endif
        endmethod
        
    endstruct
    
    private function interface SlideFunc takes integer slide returns nothing defaults nothing
    
    private struct SlideData
        private static timer Timer=CreateTimer()
        thistype prev
        thistype next
        boolean isLinear
        real accX
        real accY
        integer tick
        unit target
        timer t
        SlideFunc onStop
        integer attachment
        real timeScale
        
        private static method periodic takes nothing returns nothing
            local thistype this=thistype(0).next
            loop
            exitwhen this==0
                set this.tick=this.tick-1
                if this.tick&gt;0 then
                    if this.isLinear==false then
                        set UnitData[this.target].x=UnitData[this.target].x-this.accX
                        set UnitData[this.target].y=UnitData[this.target].y-this.accY
                    endif
                else
                    call this.onStop.evaluate(this)
                    call UnitData[this.target].decrement()
                    set this.prev.next=this.next
                    set this.next.prev=this.prev
                    call this.destroy()
                endif
                set this=this.next
            endloop
            
            if thistype(0).next==0 then
                call PauseTimer(thistype.Timer)
            endif
        endmethod
        
        method startPeriodic takes nothing returns nothing
            set thistype(0).prev.next=this
            set this.prev=thistype(0).prev
            set this.next=thistype(0)
            set thistype(0).prev=this
            
            if thistype(0).next==this then
                call TimerStart(thistype.Timer,TIMER_PERIOD,true,function thistype.periodic)
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TimerStart(thistype.Timer,TIMER_PERIOD,true,function thistype.periodic)
        endmethod
    endstruct
    
    function GetPushedUnit takes SlideData dat returns unit
        return dat.target
    endfunction
    
    function IsUnitBeingPushed takes unit u returns boolean
        return UnitData<u>.stackLevel&gt;0
    endfunction
    
    public function GetData takes SlideData dat returns integer
        return dat.attachment
    endfunction
    
    public function AttachData takes SlideData dat, integer toAttach returns nothing
        set dat.attachment=toAttach
    endfunction
    
    public function SetGlobalTimeScale takes real scale returns nothing
        local SlideData dat=SlideData(0).next
        local UnitData ud=UnitData(0).next
        local real multiplier
        loop
        exitwhen dat==0
            set multiplier=scale/dat.timeScale
            set dat.timeScale=scale
            set dat.accX=dat.accX*multiplier
            set dat.accY=dat.accY*multiplier
            set dat.tick=R2I(I2R(dat.tick)/multiplier)
            set dat=dat.next
        endloop
        
        loop
        exitwhen ud==0
            set multiplier=scale/ud.timeScale
            set ud.timeScale=scale
            set ud.x=ud.x*multiplier
            set ud.y=ud.y*multiplier
            set ud=ud.next
        endloop
        
        set TimeScale=scale
    endfunction
    
    public function GetGlobalTimeScale takes nothing returns real
        return TimeScale
    endfunction
    
    public function SetTimeScale takes unit u, real scale returns nothing
        local SlideData dat=SlideData(0).next
        local UnitData ud=UnitData<u>
        local real multiplier
        loop
        exitwhen dat==0
            if dat.target==u then
                set multiplier=scale/dat.timeScale
                set dat.timeScale=scale
                set dat.accX=dat.accX*multiplier
                set dat.accY=dat.accY*multiplier
                set dat.tick=R2I(I2R(dat.tick)/multiplier)
            endif
            set dat=dat.next
        endloop
        
        set multiplier=scale/ud.timeScale
        set ud.timeScale=scale
        set ud.x=ud.x*multiplier
        set ud.y=ud.y*multiplier
    endfunction
    
    public function GetTimeScale takes unit u returns real
        return UnitData<u>.timeScale
    endfunction
    
    function GetUnitPushSpeed takes unit u returns real
        local real x=(UnitData<u>.x/TIMER_PERIOD)*UnitData<u>.timeScale
        local real y=(UnitData<u>.y/TIMER_PERIOD)*UnitData<u>.timeScale
        return SquareRoot(x*x+y*y) //Resultant vector.
    endfunction
    
    function GetUnitPushAngle takes unit u returns real
        return Atan2(UnitData<u>.y,UnitData<u>.x)
    endfunction
    
    function PushUnit takes unit u, real vel, real angle, real time, SlideFunc stop returns integer
        local SlideData dat=0
        local real x
        local real y
        if u!=null and vel!=0.0 and time&gt;0.0 then
            set dat=SlideData.create()
            set dat.timeScale=UnitData<u>.timeScale
            set x=((vel*Cos(angle))*TIMER_PERIOD)*dat.timeScale
            set y=((vel*Sin(angle))*TIMER_PERIOD)*dat.timeScale
            set dat.target=u
            set dat.tick=R2I((time/TIMER_PERIOD)/dat.timeScale)
            set dat.accX=(x/(time/TIMER_PERIOD))*dat.timeScale
            set dat.accY=(y/(time/TIMER_PERIOD))*dat.timeScale
            set dat.onStop=stop
            set dat.isLinear=false
            set UnitData<u>.x=UnitData<u>.x+x
            set UnitData<u>.y=UnitData<u>.y+y
            call UnitData<u>.increment()
            call dat.startPeriodic()
        endif
        return dat
    endfunction
    
    function PushUnitLinear takes unit u, real vel, real angle, real time, SlideFunc stop returns integer
        local SlideData dat=0
        if u!=null and vel!=0.0 and time&gt;0.0 then
            set dat=SlideData.create()
            set dat.timeScale=UnitData<u>.timeScale
            set dat.accX=((vel*Cos(angle))*TIMER_PERIOD)*dat.timeScale
            set dat.accY=((vel*Sin(angle))*TIMER_PERIOD)*dat.timeScale
            set dat.target=u
            set dat.tick=R2I((time/TIMER_PERIOD)/dat.timeScale)
            set dat.onStop=stop
            set dat.isLinear=true
            set UnitData<u>.x=UnitData<u>.x+dat.accX
            set UnitData<u>.y=UnitData<u>.y+dat.accY
            call UnitData<u>.increment()
            call dat.startPeriodic()
        endif
        return dat
    endfunction
    
    function GetTime_DispVel takes real disp, real vel returns real
        return (2*disp)/vel
    endfunction
    
    function GetDisp_VelTime takes real vel, real time returns real
        return (vel*time)/2
    endfunction
    
    function GetVel_DispTime takes real disp, real time returns real
        return (2*disp)/time
    endfunction
    
    private function OnInit takes nothing returns nothing
        set UnitAttachment=HandleTable.create()
    endfunction
    
endlibrary
</u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u>


Still feeling "what the heck it does?" after reading the documentation?
Try the demo map, it speaks all.

Changelog :
  • v1.0.0 - First release
 

Attachments

  • PushUnit.w3x
    103.2 KB · Views: 363
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top