System Slide System v1.00

emjlr3

Change can be a good thing
Reaction score
395
Slide System v1.00

slide.jpg

The goal here was to make an all encompassing, end all be all Slide/Knockback System. I think I have created that, minus any glaring slights on my behalf.

The main issue I had with the extended knockback system is that you can't have multiple slides on a single unit - I alleviate this issue by calculating the vector components of the current slide and the updated slide, which allows me to implement concurrent slides all into one instance. Also, a few of the scripting techniques in the previous are a little archaic (ex. no static ifs), that along with its heavy system requirements makes for a less then desirable system.

The slide struct should be treated as a unique object, and used as such.

Wrappers for the structs methods are also included for GUI users implementation.

Test map includes several examples, from simple through advanced.

I tried to incorporate interfaces, but I am not entirely sure how they really work. Regardless, I think my method ends up getting the job done.

The syntax may need to be tweaked a bit, but I think it fits pretty good.

Critiques and other thoughts welcome. I humbly submit for consideration.

Requirements:
  • UnitAlive native
Optional Requirements:

Implementation:

Read Me Part1:
Copy the Slide System trigger to your map, configure the options, and enjoy!

Requires, optionally, TimerUtils, GroupUtils, DestructableLib, and IsTerrainWalkable. The system will function fine without them, but it will work better and faster if they are present.
Also requires the UnitAlive native.

When creating slides, the angle to slide should be in degrees, not radians. Velocity and deceleration are in WC3 units, aka distance/second.

Typical GUI use:

//==Wrapper Functions==\\
// Create a new instance for a unit, deceleration=0 to use the DECEL global
function UnitStartSlide takes unit slider, real angle, real velocity, real deceleration returns slide

// Retrieve the units current slide information
function UnitGetSlide takes unit slider returns slide

// Is the unit currently sliding?
function IsUnitSliding takes unit slider returns boolean

// Stop the unit from sliding immediately
function UnitStopSlide takes unit slider returns boolean

// Incase you need to set a custom slide effect mid slide
// Use "" to restore automatic sfx updates
function UnitSetSlideEffect takes unit slider, string sfx returns boolean

// Kinematics
// Slide a unit for a specific duration, given the initial velocity
function UnitSlideTimed takes unit slider, real angle, real velocity, real time returns slide

// Slide a unit over a specific distance, given the initial velocity
function UnitSlideDistance takes unit slider, real angle, real velocity, real distance returns slide

// Collision utilities
// Remove a unit from the sliders collision group instantly
function RemoveTargetFromSlide takes slide d, unit target returns boolean

// Remove a unit from the sliders collision group after REHITTIME duration
function RemoveTargetFromSlideTimed takes slide d, unit target returns boolean

Read Me Part2:
Typical JASS use:

The structs name is slide, to use it, declare a local of the type slide.

// Create a new instance for a unit, deceleration=0 to use the DECEL global
static method create takes unit slider, real angle, real velocity, real deceleration returns slide

// Retrieve the units current slide information
static method unitgetslide takes unit slider returns slide

// Is the unit currently sliding?
static method isunitsliding takes unit slider returns boolean

// Stop the unit from sliding immediately
static method unitstopslide takes unit slider returns boolean

// Incase you need to set a custom slide effect mid slide
// Use "" to restore automatic sfx updates
static method unitsetslideeffect takes unit slider, string sfx returns boolean

// Kinematics
// Slide a unit for a specific duration, given the initial velocity
static method unitslidetimed takes unit slider, real angle, real velocity, real time returns slide

// Slide a unit over a specific distance, given the initial velocity
static method unitslidedistance takes unit slider, real angle, real velocity, real distance returns slide

// Collision utilities
// Remove a unit from sliders collision group instantly
static method removetargetfromslide takes slide d, unit target returns boolean

// Remove a unit from sliders collision group after REHITTIME duration
static method removetargetfromslidetimed takes slide d, unit target returns boolean

Read Me Part3
Advanced JASS use:

The struct has the following members, which can be accessed as desired:

private effect sfx
private integer marker
private real cos
private real decel
private real sin

public boolean constvel
public group g
public real angle
public real maxdist
public real maxtime
public real velocity

readonly boolean customsfx
readonly boolean fly
readonly boolean land
readonly boolean trees
readonly real totaldist
readonly real totaltime
readonly unit slider

Setting constvel to true creates a non-acceleration slide - this can be useful for dash type spells. maxtime and maxdist can be used to create a maximum time or maximum distance slide for a unit, respectively.
When in constant velocity mode, units are not collided with, and new slide instances on that unit cannot be created. See Shockwave for a demo.

Struct interfaces have been developed for the following uses:

method onPeriodic takes nothing returns nothing defaults nothing
method onEnd takes nothing returns nothing defaults nothing
method onHit takes nothing returns nothing defaults nothing

onPeriodic fires every TIMEOUT interval, onEnd fires when the target finishes sliding, and onHit fires when a collision takes place. See Bash for a demo.

Script:
JASS:
library SlideSystem needs optional GroupUtils, optional TimerUtils, optional DestructableLib, optional IsTerrainWalkable

globals    
//==Configurables==\\
    private constant boolean        ALLOWMOVE   = false // Allow movement while units slide
    private constant boolean        COLLIDE     = true // Unit/Structure collision detection
    private constant boolean        CLIFF       = true // Cliff detection, stops unit movement
    private constant boolean        PRELOAD     = true // Preload slide effects
    private constant boolean        REHIT       = true // Can collide with the same target more then once during a single slide event
    private constant boolean        SINCELAST   = false // When tracking total slide time and distance, will reset value for each newly created slide
    private constant boolean        TREES       = true // Destroy trees in sliding path
    
    private constant integer        OFFSET      = 0x100000 // Id of the first handle in your map, if unsure don't change
    
    public  constant real           BREAK       = 10. // Velocity below which units stop sliding
    public  constant real           CLIFFBREAK  = 75. // Break point for a terrian height change to be considered a cliff, if not using IsTerrainWalkable library
    public  constant real           DECEL       = .95 // In the abscence of a linear deceleration set point, DECEL/TIMEOUT
    public  constant real           FACTORA     = .66 // Percentage of slide velocity transfered to colliding units
    public  constant real           FACTORB     = .66 // Percentage of slide velocity retained after a collision
    public  constant real           FLYBREAK    = 150. // Units above this fly height will not be struck by collisions or destroy trees
    public  constant real           RADIUS      = 180. // Collision detection radius
    public  constant real           REHITTIME   = .4 // Elapsed time required before a unit can be collided with again
    public  constant real           TIMEOUT     = .05 // Periodic timer interval
    
    private constant string         AIR         = "sfx\\AirSlide.mdx" // Flying units slide effect
    private constant string         ATTACH      = "origin" // Slide effects attachment point
    private constant string         HIT         = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl" // Collision effect
    private constant string         LAND        = "sfx\\LandSlide.mdx" // Land slide effect
    private constant string         SEA         = "sfx\\SeaSlide.mdx" // Water slide effect
endglobals
// Establish custom unit collision conditions
private function CollisionFilter takes unit slider, unit target returns boolean
    return IsUnitEnemy(target,GetOwningPlayer(slider))
endfunction

//==NO TOUCHING PAST THIS POINT==\\
globals
    private          boolexpr       TREEBOOL      
    private          boolexpr       UNITBOOL
    private          group          COLLISION   = CreateGroup()
    private          hashtable      HT          // Only needed if no TimerUtils
    private          integer        COUNT       = 0
    private          slide array    DATA
    private          slide array    DATA2       // Remove O(n) searches
    private          slide          TEMPDATA    
    private          location       LOC         = Location(0.,0.)
    public  constant real           FRAMERATE   = 1./TIMEOUT
    private constant real           HALFRADIUS  = RADIUS/2.
    public  constant real           REALBREAK   = BREAK/FRAMERATE
    private          rect           RECT    
    private          unit           T
    private          unit           TREECHECK   
    
    // Incase either of these need to be accessed
    public           group          GROUP       = CreateGroup()
    public           timer          TIMER       = CreateTimer()  
endglobals

private struct rehit 
    timer t         = null
    unit target     = null
    unit slider     = null
    group g         = null
    
    method destroy takes nothing returns nothing
        // Clean up
        static if LIBRARY_TimerUtils then
            call ReleaseTimer(.t)
        else
            call FlushChildHashtable(HT,GetHandleId(.t))
            call PauseTimer(.t)
            call DestroyTimer(.t)
        endif        
        call .deallocate()
    endmethod
    static method timeout takes nothing returns nothing
        local rehit this=GetTimerData(GetExpiredTimer())
        
        // Unit is still sliding
        if IsUnitInGroup(.slider,GROUP) then
            call GroupRemoveUnit(.g,.target)
        endif
        call .destroy()
    endmethod
    static method create takes unit target, unit slider, group g returns rehit
        local rehit this=rehit.allocate()
        
        static if LIBRARY_TimerUtils then
            set .t=NewTimer()
            call SetTimerData(.t,this)
        else
            set .t=CreateTimer()
            call SaveInteger(HT,GetHandleId(.t),0,this)
        endif
        set .target=target
        set .slider=slider
        set .g=g        
        // Remove target from sliders hit group after a duration
        call TimerStart(.t,REHITTIME,false,function rehit.timeout)
        
        return this
    endmethod
endstruct

private interface face
    method onPeriodic takes nothing returns nothing defaults nothing
    method onEnd takes nothing returns nothing defaults nothing
    method onHit takes nothing returns nothing defaults nothing
endinterface
struct slide extends face
    private     effect      sfx         = null
    private     integer     marker      = 0 // mark spot in movement loop stack
    private     real        cos         = 0.
    private     real        decel       = 0.
    private     real        sin         = 0.
    
    public      boolean     constvel    = false
    public      group       g           = null    
    public      real        angle       = 0.    
    public      real        maxdist     = 0. // Only used during constant velocity mode
    public      real        maxtime     = 0. // Only used during constant velocity mode
    public      real        velocity    = 0.       
    
    readonly    boolean     customsfx   = false
    readonly    boolean     fly         = false
    readonly    boolean     land        = true
    readonly    boolean     trees       = true
    readonly    real        totaldist   = 0.
    readonly    real        totaltime   = 0.
    readonly    unit        slider      = null
    
    method destroy takes nothing returns nothing
        // Clean up
        set .customsfx=false
        set .fly=false
        set .land=true
        set .trees=true
        set .constvel=false
        set .totaldist=0.
        set .totaltime=0.
        set .maxdist=0.
        set .maxtime=0.
        set .decel=0.
        
        if .onEnd.exists then
            call .onEnd.execute()
        endif
        call DestroyEffect(.sfx)
        static if LIBRARY_GroupUtils then
            call ReleaseGroup(.g)
        else
            call DestroyGroup(.g)
        endif
        call GroupRemoveUnit(GROUP,.slider)
        set DATA[.marker]=DATA[COUNT]
        set DATA[.marker].marker=.marker
        set COUNT=COUNT-1
        call .deallocate()
    endmethod
    private static method unitfilter takes nothing returns boolean
        local slide this=TEMPDATA
        local slide d
        local real xT
        local real yT
        local real x
        local real y
        local timer t
        set T=GetFilterUnit()
        
        // Make sure target is alive and not invulnerable, is not the slider and has not already been hit, and isn't flying too high, along with
        // conditions from the custom user edited filter
        if CollisionFilter(.slider,T) and GetUnitFlyHeight(T)<=FLYBREAK and GetUnitAbilityLevel(T,'Avul')==0 and UnitAlive(T) and not IsUnitInGroup(T,.g) and T!=.slider then
            if .onHit.exists then
                call .onHit.execute()
            endif
            set xT=GetUnitX(T)
            set yT=GetUnitY(T)
            set x=GetUnitX(.slider)
            set y=GetUnitY(.slider)
            // Only begin a slide on the target if its not a structure
            if IsUnitType(T,UNIT_TYPE_STRUCTURE)==false then
                // Start collision slide
                set d=slide.create(T,bj_RADTODEG*Atan2(yT-y,xT-x),(.velocity*FRAMERATE)*FACTORA,.decel*FRAMERATE) 
                call GroupAddUnit(d.g,.slider)
                call DestroyEffect(AddSpecialEffectTarget(HIT,T,ATTACH))
                
                // If allowed, start re-hit effects
                static if REHIT then
                    call rehit.create(T,.slider,.g)                    
                endif
            endif
            
            // Fancy maths for deflection, see my GetDeflectionAngle script
            set .angle=.angle+180.+(2.*((bj_RADTODEG*Atan2(yT-y,xT-x))-.angle))
            set .cos=Cos(.angle*bj_DEGTORAD)
            set .sin=Sin(.angle*bj_DEGTORAD)
            set .velocity=.velocity*FACTORB
            call DestroyEffect(AddSpecialEffectTarget(HIT,.slider,ATTACH))
            call GroupAddUnit(.g,T)
        endif
        return false
    endmethod
    private static method killtrees takes nothing returns boolean
        // Make sure the destructable is actually a tree
        static if not LIBRARY_DestructableLib then
            if IssueTargetOrder(TREECHECK,"harvest",GetFilterDestructable()) then
                call KillDestructable(GetFilterDestructable())
            endif
        else
            if IsDestructableTree(GetFilterDestructable()) then
                call KillDestructable(GetFilterDestructable())
            endif
        endif
        return false
    endmethod
    private static method movement takes nothing returns nothing
        local slide this
        local integer i=1
        local real x
        local real xc
        local real y
        local real yc
        local real z
        
        loop
            exitwhen i>COUNT
            set this=DATA<i>
        
            // Slide is over
            if .velocity&lt;=REALBREAK or not UnitAlive(.slider) or (.constvel and ((.totaltime&gt;=.maxtime and .maxtime!=0.) or (.totaldist&gt;=maxdist and .maxdist!=0.))) then
                // Clean up
                call .destroy()    
                set i=i-1
            // Slide is not over
            else
                // Caculate positioning
                set x=GetUnitX(.slider)+.velocity*.cos
                set y=GetUnitY(.slider)+.velocity*.sin
                
                if .onPeriodic.exists then
                    call .onPeriodic.execute()
                endif
                
                // Destroy trees
                static if TREES then
                    if .trees then
                        call MoveRectTo(RECT,x,y)
                        static if not LIBRARY_DestructableLib then
                            call PauseUnit(TREECHECK,false)
                        endif
                        call EnumDestructablesInRect(RECT,TREEBOOL,null)
                        static if not LIBRARY_DestructableLib then
                            call PauseUnit(TREECHECK,true)
                        endif
                    endif
                endif
                
                // Cliff detection
                static if CLIFF then
                    // Check out in front of the slider a bit in the direction it&#039;s traveling
                    set xc=x+HALFRADIUS*.cos
                    set yc=y+HALFRADIUS*.sin
                    static if LIBRARY_IsTerrainWalkable then
                        // Cliff found
                        if not IsTerrainWalkable(xc,yc) then
                            call .destroy()
                        endif
                    else
                        call MoveLocation(LOC,x,y)
                        set z=GetLocationZ(LOC)
                        call MoveLocation(LOC,xc,yc)
                        // Cliff found
                        if GetLocationZ(LOC)&gt;z+CLIFFBREAK or GetLocationZ(LOC)&lt;z-CLIFFBREAK then
                            call .destroy()
                        endif
                    endif
                endif
                
                // Update position and slide speed
                static if ALLOWMOVE then
                    call SetUnitX(.slider,x)
                    call SetUnitY(.slider,y)
                else
                    call SetUnitPosition(.slider,x,y)
                endif
                // Update total time and distance slid
                set .totaldist=.totaldist+.velocity
                set .totaltime=.totaltime+TIMEOUT
                // If a deceleration has been set, use it, so long as we aren&#039;t in a constant velocity mode
                if not .constvel then
                    if .decel!=0. then
                        set .velocity=.velocity-.decel
                    else
                        set .velocity=.velocity*DECEL
                    endif
                endif
                    
                // Update slide effects    
                if not .fly and not .customsfx then
                    // Ground            
                    if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) or not IsTerrainPathable(x,y,PATHING_TYPE_ANY) then
                        if not .land then
                            set .land=true
                            call DestroyEffect(.sfx)
                            set .sfx=AddSpecialEffectTarget(LAND,.slider,ATTACH)
                        endif
                    // Water
                    elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                        if .land then
                            set .land=false
                            call DestroyEffect(.sfx)
                            set .sfx=AddSpecialEffectTarget(SEA,.slider,ATTACH)
                        endif
                    endif
                endif               
                
                // Collision detection
                static if COLLIDE then
                    if not .constvel then
                        set TEMPDATA=this
                        call GroupEnumUnitsInRange(COLLISION,x,y,RADIUS,UNITBOOL)
                    endif
                endif
            endif
            
            set i=i+1
        endloop
        
        if COUNT==0 then
            call PauseTimer(TIMER)
        endif
    endmethod
    
//==User Methods==\\
    // Create a new instance for a unit, deceleration=0 to use the DECEL global
    static method create takes unit slider, real angle, real velocity, real deceleration returns slide
        local slide this
        local integer id=GetHandleId(slider)-OFFSET
        local real x
        local real y
        
        // No need to continue...
        if slider==null or not UnitAlive(slider) then
            return 0
        endif
                
        // Unit wasn&#039;t previously sliding
        if not IsUnitInGroup(slider,GROUP) then  
            // Initial slide struct setup
            set this=slide.allocate()
            set .slider=slider
            set .angle=angle
            set .velocity=velocity/FRAMERATE
            set .cos=Cos(.angle*bj_DEGTORAD)
            set .sin=Sin(.angle*bj_DEGTORAD)
            // Don&#039;t want to speed up do we?
            if deceleration!=0 then
                set .decel=RAbsBJ(deceleration/FRAMERATE)
            endif
            static if LIBRARY_GroupUtils then
                set .g=NewGroup()
            else
                set .g=CreateGroup()
            endif
            set x=GetUnitX(.slider)
            set y=GetUnitY(.slider)
        
            call GroupAddUnit(GROUP,slider)
            // Unit is a flyer
            if IsUnitType(slider,UNIT_TYPE_FLYING)==true then
                set .fly=true
                set .sfx=AddSpecialEffectTarget(AIR,.slider,ATTACH)
                // Too high??
                if GetUnitFlyHeight(slider)&gt;FLYBREAK then
                    set .trees=false
                endif
            else
                // Ground                                                  Stairs don&#039;t have any pathing...
                if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) or not IsTerrainPathable(x,y,PATHING_TYPE_ANY) then
                    set .sfx=AddSpecialEffectTarget(LAND,.slider,ATTACH)
                // Water
                elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                    set .sfx=AddSpecialEffectTarget(SEA,.slider,ATTACH)
                    set .land=false
                endif
            endif
            
            // Store struct data
            set DATA2[id]=this
            set COUNT=COUNT+1
            set DATA[COUNT]=this    
            set .marker=COUNT
            // First sliding unit
            if COUNT==1 then
                call TimerStart(TIMER,TIMEOUT,true,function slide.movement)
            endif
        // Unit was already sliding
        else            
            // To avoid bugs and general preposterousness, when moving in constant velocity mode, we can&#039;t update slide attributes
            // I figure most likely the user wouldn&#039;t want the targets trajectory being changed anyhow
            if .constvel then
                return 0
            endif
        
            // Calculate vector components of slide velocity
            set this=DATA2[id]
            set velocity=velocity/FRAMERATE
            set x=.velocity*.cos+(velocity)*Cos(angle*bj_DEGTORAD)
            set y=.velocity*.sin+(velocity)*Sin(angle*bj_DEGTORAD)
            // Update slide direction and velocity
            set .velocity=SquareRoot(x*x+y*y)
            set .angle=Atan2(y,x)
            set .cos=Cos(.angle)
            set .sin=Sin(.angle)
            set .angle=.angle*bj_RADTODEG
            if deceleration!=0 then
                set .decel=RAbsBJ(deceleration/FRAMERATE)
            endif
            
            // Reset total slide time and distance
            static if SINCELAST then
                set .slidedist=0.
                set .totaltime=0.
            endif
        endif
        
        return this
    endmethod
    // Retrieve the units current slide information
    static method unitgetslide takes unit slider returns slide
        if IsUnitInGroup(slider,GROUP) then
            return DATA2[GetHandleId(slider)-OFFSET]
        else
            debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitGetSlide Error: Unit is not sliding.&quot;)
            return 0
        endif
    endmethod
    // Is the unit currently sliding?
    static method isunitsliding takes unit slider returns boolean
        return IsUnitInGroup(slider,GROUP)
    endmethod
    // Stop the unit from sliding immediately
    static method unitstopslide takes unit slider returns boolean
        if IsUnitInGroup(slider,GROUP) then
            call DATA2[GetHandleId(slider)-OFFSET].destroy()         
            return true
        else
            debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitStopSlide Error: Unit is not sliding.&quot;)
            return false
        endif
    endmethod
    // Incase you need to set a custom slide effect mid slide
    // Use &quot;&quot; to restore automatic sfx updates
    static method unitsetslideeffect takes unit slider, string sfx returns boolean
        local slide this
    
        if IsUnitInGroup(slider,GROUP) then
            set this=DATA2[GetHandleId(slider)-OFFSET] 
            call DestroyEffect(.sfx)
            if sfx!=&quot;&quot; then
                set .customsfx=true
                set .sfx=AddSpecialEffectTarget(sfx,slider,ATTACH)
            else
                set .customsfx=false
                if .fly then
                    set .sfx=AddSpecialEffectTarget(AIR,slider,ATTACH)
                endif
            endif
            return true
        else
            debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitSetSlideEffect Error: Unit is not sliding.&quot;)
            return false
        endif
    endmethod
    // Kinematics
    // Slide a unit for a specific duration, given the initial velocity
    static method unitslidetimed takes unit slider, real angle, real velocity, real time returns slide
        // a=Vi-Vo/t
        if slider!=null and time&gt;0. then
            return slide.create(slider,angle,velocity,(velocity-BREAK)/(time*FRAMERATE))
        else    
            debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitSlideTimed Error: Incorrect argument inputs.&quot;)
            return 0
        endif
    endmethod
    // Slide a unit over a specific distance, given the initial velocity
    static method unitslidedistance takes unit slider, real angle, real velocity, real distance returns slide
        // a=(Vf^2-Vi^2)/(2*d)
        if slider!=null and distance&gt;0. then
            return slide.create(slider,angle,velocity,(REALBREAK*REALBREAK-velocity*velocity*TIMEOUT)/(2.*distance))
        else    
            debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitSlideDistance Error: Incorrect argument inputs.&quot;)
            return 0
        endif
    endmethod
    // Remove a unit from sliders collision group instantly
    static method removetargetfromslide takes slide d, unit target returns boolean
        if IsUnitInGroup(target,d.g) and d.g!=null and d!=null and target!=null and UnitAlive(target) then
            call GroupRemoveUnit(d.g,target)
            return true
        else
            return false
        endif
    endmethod
    // Remove a unit from sliders collision group after REHITTIME duration
    static method removetargetfromslidetimed takes slide d, unit target returns boolean
        if d.g!=null and d!=null and target!=null and UnitAlive(target) then
            if not IsUnitInGroup(target,d.g) then
                call GroupAddUnit(d.g,target)
            endif
            call rehit.create(target,d.slider,d.g)  
            return true
        else        
            return false
        endif
    endmethod
    
//==Initialize System==\\
    private static method onInit takes nothing returns nothing
        set UNITBOOL=Condition(function slide.unitfilter)
        // Destroy trees in path of sliding units setup
        static if TREES then
            set RECT=Rect(-RADIUS,-RADIUS,RADIUS,RADIUS)
            set TREEBOOL=Condition(function slide.killtrees)
            // Don&#039;t have DestructableUtils
            static if not LIBRARY_DestructableLib then                
                set TREECHECK=CreateUnit(Player(15),&#039;hfoo&#039;, 0.0, 0.0, 0.0)
                call PauseUnit(TREECHECK,true)
                call ShowUnit(TREECHECK,false)
                call UnitAddAbility(TREECHECK,&#039;Ahrl&#039;)
                call UnitAddAbility(TREECHECK,&#039;Aloc&#039;)
            endif
        endif
        // If you don&#039;t have TimerUtils
        static if not LIBRARY_TimerUtils then
            set HT=InitHashtable()
        endif
        // Preload slide effects
        static if PRELOAD then
            call Preload(AIR)
            call Preload(HIT)
            call Preload(LAND)
            call Preload(SEA)
        endif
    endmethod
endstruct

//==Wrapper Functions==\\
// Create a new instance for a unit, deceleration=0 to use the DECEL global
function UnitStartSlide takes unit slider, real angle, real velocity, real deceleration returns slide
    debug if slider==null or not UnitAlive(slider) or IsUnitType(slider,UNIT_TYPE_DEAD)==true then
        debug call BJDebugMsg(SCOPE_PREFIX+&quot; UnitStartSlide Error: Invalid argument inputs.&quot;)
        debug return 0
    debug endif
    return slide.create(slider,angle,velocity,deceleration)
endfunction
// Retrieve the units current slide information
function UnitGetSlide takes unit slider returns slide
    return slide.unitgetslide(slider)
endfunction
// Is the unit currently sliding?
function IsUnitSliding takes unit slider returns boolean
    return slide.isunitsliding(slider)
endfunction
// Stop the unit from sliding immediately
function UnitStopSlide takes unit slider returns boolean
    return slide.unitstopslide(slider)
endfunction
// Incase you need to set a custom slide effect mid slide
// Use &quot;&quot; to restore automatic sfx updates
function UnitSetSlideEffect takes unit slider, string sfx returns boolean
    return slide.unitsetslideeffect(slider,sfx)
endfunction
// Kinematics
// Slide a unit for a specific duration, given the initial velocity
function UnitSlideTimed takes unit slider, real angle, real velocity, real time returns slide
    return slide.unitslidetimed(slider,angle,velocity,time)
endfunction
// Slide a unit over a specific distance, given the initial velocity
function UnitSlideDistance takes unit slider, real angle, real velocity, real distance returns slide
    return slide.unitslidedistance(slider,angle,velocity,distance)
endfunction
// Remove a unit from sliders collision group instantly
function RemoveTargetFromSlide takes slide d, unit target returns boolean
    return slide.removetargetfromslide(d,target)
endfunction
// Remove a unit from sliders collision group after REHITTIME duration
function RemoveTargetFromSlideTimed takes slide d, unit target returns boolean
    return slide.removetargetfromslidetimed(d,target)
endfunction

endlibrary</i>


Change Log:
v1.00 - Initial Release
 

Attachments

  • emjlr3 - SlideSystem v1.00.w3x
    138.5 KB · Views: 446
  • slide.jpg
    slide.jpg
    115.9 KB · Views: 425

Nestharus

o-o
Reaction score
84
Good idea, but this can undergo a lot of optimization. Whenever you deal with periodic stuff, you need to optimize the hell out of it ; ).

TimerQueue for rehit


This should have like 0 groups, be better on lists.

A unit's velocity should just be set (set unit.x.velocity = -5; set unit.y.velocity = -3)

This way people can control exactly how they want to do it. You should split this into a myriad of resources... all of it in one resource is just a huge jumbled mess. The most basic should be the setting unit velocity. From there, users can make it so that a unit's velocity gets set when they go on a certain terrain or when a knock back occurs or w/e.

From there, you can go up a level with things like rehit and all of these other options you have. Having them all together is just a terrible idea. It just limits the usability of the resource and makes people have to write identical code to do nearly identical things >.>. This is why modularity is just so important.

It looks like you put a lot of work into this lib, but you should just scrap it and start over.

One interesting thing is that if you just have a general sliding lib, you could run like anything off of it from projectiles to sliding on terrain in maze maps to knockback spells to w/e.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
I didn't look at the code yet, but in the test map, I used shockwave to knockback and it kept giving me an error message. (run it in debug mode)
"SlideSystem_UnitStopSlide Error: Unit is not sliding"
 

emjlr3

Change can be a good thing
Reaction score
395
Good idea, but this can undergo a lot of optimization. Whenever you deal with periodic stuff, you need to optimize the hell out of it ; ).

TimerQueue for rehit


This should have like 0 groups, be better on lists.

A unit's velocity should just be set (set unit.x.velocity = -5; set unit.y.velocity = -3)

This way people can control exactly how they want to do it. You should split this into a myriad of resources... all of it in one resource is just a huge jumbled mess. The most basic should be the setting unit velocity. From there, users can make it so that a unit's velocity gets set when they go on a certain terrain or when a knock back occurs or w/e.

From there, you can go up a level with things like rehit and all of these other options you have. Having them all together is just a terrible idea. It just limits the usability of the resource and makes people have to write identical code to do nearly identical things >.>. This is why modularity is just so important.

It looks like you put a lot of work into this lib, but you should just scrap it and start over.

One interesting thing is that if you just have a general sliding lib, you could run like anything off of it from projectiles to sliding on terrain in maze maps to knockback spells to w/e.

i will think on it - but I am not sure that is the best way to go

requiring users to input vector components directly may be more trouble then its worth.

I didn't look at the code yet, but in the test map, I used shockwave to knockback and it kept giving me an error message. (run it in debug mode)
"SlideSystem_UnitStopSlide Error: Unit is not sliding"

that's not a big deal - its only for debug purposes anyway - in the demos I don't check to make sure the unit is not sliding before I stop it, but at the end of the day it doesn't really mater
 

emjlr3

Change can be a good thing
Reaction score
395
I love it, the only other knockback system requires 5+ systems. :) +Rep

well this could require almost as many - but again only if you want them, or already use them to begin with, as they are not absolutely a must
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
that's not a big deal - its only for debug purposes anyway - in the demos I don't check to make sure the unit is not sliding before I stop it, but at the end of the day it doesn't really mater

Ah, okay.

Anyway, one thing I noticed in the code was that you initialize a lot of the struct members like so:
JASS:
struct A
    real x = 0
    real y = 0
    real z = 0
    ///.... etc
endstruct


Normally, that is probably good coding practice, but providing an initializer will set that member to that value when it is allocated. So in the alloc method, it will do:
JASS:
set x[this] = 0
set y[this] = 0
set z[this] = 0
//... etc


That is also fine, but in the .create() method, you set them to different values. So then it would end up being like this:
JASS:
set x[this] = 0
set y[this] = 0
set z[this] = 0
//... etc
set x[this] = 5
set y[this] = 254
set z[this] = 3426
//... etc


So I suggest that you either remove the initial values (for the ones that get set in the .create() method), or you can set them to what they would be in the .create() method, for whichever members warrant that. Just a suggestion, it will probably reduce the output code by a nice amount. =D
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
there's one thing no knockback/slide system has, but I would definitely find it useful, especially for my map

it's to detect when the slide ends
something like a custom event: UNIT_STOPS_SLIDING

currently I have to make a timer and check every time if the unit is sliding or not

if it is codable, it would be nice to have it :)
 

Laiev

Hey Listen!!
Reaction score
188
if you use Event from Jesus4Lyf, would be easy...


here:

method destroy takes nothing returns nothing


after create/declare the event, just fire the event inside this method (read the document of Event to understand what i'm talking about)

Its easy done by yourself... but if you don't get it or Emj don't want to implement it, i can do it for you :) feel free to pm me at anytime
 

tooltiperror

Super Moderator
Reaction score
231
This doesn't need the event, but it'd be a nice touch.

Implements knockback in a nice way.

Approved.
 

TheOverWhelm

Member
Reaction score
16
I sent you a PM, but I tested it again for something and it happened..
ANyways,
So I was playin with the Test Map and when I did a Stomp the units didn't fly back but the slide effect appeared (looped and never went away) and they got stunned.
Is this a testmap bug or a War Stomp bug? Sorry, not pro at this stuff.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top