System Ultimate Slide System

n[u]ll

You can change this now in User CP.
Reaction score
93
Ultimate Slide System
v1.8
screen013zr.jpg

This is my version of the Ice Slide/Skate functions that have become so popular. I call this one the Ultimate because of a few key improvements over any others I've seen:

  • Simple to use, little to no JASS knowledge required
  • No leaks, and lag free
  • It uses only natives
  • Allows for either both region-check sliding or terrain-check sliding (explained in the readme)
  • Terrain check uses offsets for better paths
  • Support for multiple "slide tiles"
  • Highly customizable
  • RoC compatible
Credits to Koga73 for the original idea of ice sliding, and Limiter for the idea of ice skating.

Changelog
v1.0 - Initial Release

v1.1 - Addition of arrays/Collision and Off-path-death triggers.

v1.2 - Cleaned up some code/Minor bug fixes

v1.3 - Added testing functions

v1.4 - Added patrollers and other maze-like features.

v1.5 - Bug fix (thanks Aspard)/Rearranged the triggers/Clarified some triggers and functions.

v1.6 - Bug fixes, updated some code for aesthetics and effeciency
v.a - Documented a known bug, changed a testing trigger to avoid clicking an in-game hotkey.

v1.7 - Improved readability. Added TerrainCheck with changeable offset. Made changing the periodic timer in the Slide trigger easier.

v1.8 - Updated documentation. Changed tileset. Added support for multiple tile checks. Added more support for slower/faster sliding.
Readme
JASS:
//***************************************************************************************************
//                                                                                                  *
//                           ##### /    ##        #######         #######                           *
//                        ######  /  #####      /       ###     /       ###                         *
//                       /#   /  /     #####   /         ##    /         ##                         *
//                      /    /  ##     # ##    ##        #     ##        #                          *
//                          /  ###     #        ###             ###                                 *
//                         ##   ##     #       ## ###          ## ###                               *
//                         ##   ##     #        ### ###         ### ###                             *
//                         ##   ##     #          ### ###         ### ###                           *
//                         ##   ##     #            ### /##         ### /##                         *
//                         ##   ##     #              #/ /##          #/ /##                        *
//                          ##  ##     #               #/ ##           #/ ##                        *
//                           ## #      #                # /             # /                         *
//                            ###      /       ##        /     ##        /                          *
//                             #######/         ########/       ########/                           *
//                               ####             #####           #####                             *
//                                                                                                  *
//                                         Created By N[o]obleT                                     *
//                                                                                                  *
//__________________________________________________________________________________________________*
//__________________________________________________________________________________________________*
//                                                                                                  *
// It should be first noted that there are two ways this system can be used.                        *
//                                                                                                  *
// The first and easiest way is through a terrain check (found under the TerrainCheck               *
//  folder). This will check if any of the units are on your designated sliding terrain.            *
//  If a unit is, it will make them slideable. It's simple, but a major downside is                 *
//  that there little control over the area a unit can actually slide, and the path becomes very    *
//  strict (or "tight").                                                                            *
//                                                                                                  *
// The other way to slide a unit in this system is through region checks. This is more time-        *
//  consuming, but in the long run it is better due to the high customizability.                    *
//                                                                                                  *
//                                   IMPORTING:                                                     *
// When you import this system, it's recommended that you copy every folder except for "Misc" into  *
//  your map. Make sure you do not rearrange the orders of the triggers or folders, doing this may  *
//  cause an error when saving the map. Once you've copied the folders over to your map, save your  *
//  map. There shouldn't be any errors when you save, but if there are you should contact me.       *
//                                                                                                  *
//                                  HOW TO USE:                                                     *
//                                                                                                  *
// How to use the REGION CHECK way:                                                                 *
//  1.)If you'd like to use region checks, first disable every trigger under TerrainCheck.          *
//  2.)Next make sure all the triggers in the RegionCheck folder are enabled.                       *
//  3.)Make note of the regions I used (and where i put them) in the map:                           *
//      a. At any terrain touching ice, I made a region named SlideRect<#>. If a unit leaves these  *
//          regions, he will be able to slide.                                                      *
//                                                                                                  *
//      b. At any place where the safe path goes from touching snow and ice to ONLY touching snow,  *
//          I created a region named SlideFalse<#>. This is because if a unit leaves a SlideRect,   *
//          he will slide, but what if he leaves the region going away from the ice, like down a    *
//          grass path? Then these regions will catch him before he slides on grass, which would be *
//          f'd up. Make sure that when you create these regions that they are not touching (leave  *
//          a slight gap).                                                                          *
//                                                                                                  *
//  4.)Update all of the CheckLeave/CheckFalse/CheckEnter triggers with the new regions you added.  *
//                                                                                                  *
// How to use the TERRAIN CHECK way:                                                                *
//  1.)Disable every trigger under RegionCheck. Enable every trigger under TerrainCheck.            *
//  2.)Make sure the terrain type in TerrainSetup is the same terrain you're using to slide on.     *
//  3.)Also change the terrain type (it says 'Nsnw') in TerrainCheck to the terrain you're using    *
//      for the opposite terrain of the path. Read ALL the comments, they are there for a reason.   *
//                                                                                                  *
//                          I HOPE YOU ENJOY IT!                                                    *
//__________________________________________________________________________________________________*


Code:

There's more to this system than the following, but I'm going to post just the most commonly used functions/triggers so this post won't get so cluttered.
Slide
JASS:
function SlideA takes nothing returns nothing
local unit       u = GetEnumUnit    (                  )                                               //Set the picked unit
local integer    i = GetPlayerId    ( GetOwningPlayer( u ) ) + 1                                       //Set the Player number of the unit.
local real       x = GetUnitX       ( u                )                                               //Set the units X
local real       y = GetUnitY       ( u                )                                               //Set the units Y

    if udg_onIce[ i ] == true and GetUnitState( u, UNIT_STATE_LIFE ) > 0 then                          //Check if the unit is on ice and is alive
        call SetUnitX( u , x + udg_Speed[ i ] * Cos( GetUnitFacing( u ) * 0.017453292 ) )              //Set the units x-axis position
        call SetUnitY( u , y + udg_Speed[ i ] * Sin( GetUnitFacing( u ) * 0.017453292 ) )              //Set the units y-axis position
        if udg_realisticSlide[ i ] == false then                                                       //If realisticSlide for that unit is turned off then
            call IssueImmediateOrder( u,"stop" )                                                       //    Order the unit to stop
        endif
    endif
    set u = null                                                                                       //Nulling the local unit variable (otherwise it will leak)
endfunction


function SlideG takes nothing returns nothing
    call ForGroup( udg_Slideables , function SlideA )                                                  //Pick every unit in Slideables and runs function SlideA for each picked unit
endfunction


function InitSlide takes nothing returns nothing
local timer t = CreateTimer(  )
    call TimerStart( t, udg_timeStep, true, function SlideG )                                          // Tell the timer to run SlideG every udg_timeStep seconds
    set t = null
endfunction





function InitTrig_Slide takes nothing returns nothing
//Ignore this function, it's required by the World Editor but I don't need anything done in it.
endfunction
Steer
JASS:
function SteerObj takes nothing returns nothing
local unit    u = GetTriggerUnit (                      )
local integer i = GetPlayerId    ( GetOwningPlayer( u ) ) + 1
local real    x = GetUnitX       ( u                    )
local real    y = GetUnitY       ( u                    )
local real    a = GetWidgetX     ( GetOrderTarget( )    )
local real    b = GetWidgetY     ( GetOrderTarget( )    )

    if udg_Steerable[ i ] == true then
        if udg_onIce[ i ] == true and IsUnitInGroup( u , udg_Slideables ) then
            call SetUnitFacing( u , 57.2957795 * Atan2( b - y , a - x ) )  //Set the unit facing to Angle between points (x,y) and (a,b)
        endif
    endif
    set u = null                                                           //Nulling the unit variable
endfunction


function SteerLoc takes nothing returns nothing
local unit    u = GetTriggerUnit (                      )
local integer i = GetPlayerId    ( GetOwningPlayer( u ) ) + 1
local real    x = GetUnitX       ( u                    )
local real    y = GetUnitY       ( u                    )
local real    a = GetOrderPointX (                      )
local real    b = GetOrderPointY (                      )

    if udg_Steerable[ i ] == true then
        if udg_onIce[ i ] == true and IsUnitInGroup( u , udg_Slideables ) then
            call SetUnitFacing( u , 57.2958279 * Atan2( b - y , a - x ) )  //Set the unit facing to Angle between points (x,y) and (a,b)
        endif
    endif
    set u = null                                                           //Nulling the unit variable
endfunction


function InitTrig_Steer takes nothing returns nothing
local integer i  = 0
local trigger SO = CreateTrigger( )
local trigger SL = CreateTrigger( )
	call TriggerAddAction( SO , function SteerObj )
	call TriggerAddAction( SL , function SteerLoc )
	loop
		exitwhen i > 11
		call TriggerRegisterPlayerUnitEvent( SO , Player( i ) , EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER , null )
		call TriggerRegisterPlayerUnitEvent( SL , Player( i ) , EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER  , null )
		set i = i + 1
	endloop
    set SO = null
    set SL = null
endfunction
Variables
Trigger:
  • Events
    • Map initialization
    • Conditions
    • Actions
      • Set timeStep = 0.02
      • Set offsetDist = 32.00
      • Set deathTerrain = 0
      • Set slideTerrain[0] = 0
      • For each (Integer A) from 1 to 12, do (Set realisticSlide[(Integer A)] = False)
      • -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
      • Custom script: set udg_slideTerrain[1] = 'Iice' //To change the terrain type, replace Iice with any of the terrains listed in the TTypes trigger
      • Custom script: set udg_slideTerrain[2] = 'Idki' //To change the terrain type, replace Idki with any of the terrains listed in the TTypes trigger
      • Custom script: set udg_slideTerrain[3] = 'Nice' //To change the terrain type, replace Nice with any of the terrains listed in the TTypes trigger
      • Set maxSlideTerrains = 3
      • Set speedMultiplier[1] = 1.00
      • Set speedMultiplier[2] = 0.75
      • Set speedMultiplier[3] = 1.25
      • Custom script: set udg_deathTerrain = 'Isnw' //To change the terrain type, replace Isnw with any of the terrains listed in the TTypes trigger
      • -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
      • Unit Group - Add Runner 0000 <gen> to Slideables
      • Unit Group - Add Runner 0004 <gen> to Slideables
      • -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        • For each (Integer A) from 1 to 12, do (Set Steerable[(Integer A)] = True)
        • -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        • Set initSpeed = 3.75
        • For each (Integer A) from 1 to 12, do (Set Speed[(Integer A)] = (initSpeed x (100.00 x timeStep)))
        • -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        • Custom script: call InitSlide( ) //And now we run the InitSlide trigger (it's important to run this anytime AFTER you setup your timeStep variabel)

TerrainCheckOffsetCheck
JASS:
function TerrainCheckOffsetCheck takes real x, real y, integer whichTerrain, boolean inverseCheck returns boolean
local integer terrain = 0
local integer degrees = 0
local real    a = 0
local real    b = 0
    loop
        exitwhen degrees > 360
        set a = x + udg_offsetDist * Cos( degrees * 0.017453292 )       //Set x = unit's x + offsetDist facing n degrees
        set b = y + udg_offsetDist * Sin( degrees * 0.017453292 )       //""  y "  ""    y "     ""       ""   "    ""
        set terrain = GetTerrainType( a, b )                            //Set terrain = terrain type at ( a, b )
        
        if inverseCheck == true then
            if terrain != whichTerrain then                             //Check if the terrain type at the unit's position offset by offsetDist facing n is NOT equal to the specified terrain
                return true                                             //    If so then return true and
                exitwhen true                                           //    exit the loop
            endif
        else
            if terrain == whichTerrain then                             //Check if the terrain type at the unit's position offset by offsetDist facing n is equal to the specified terrain
                return true                                             //    If so then return true and
                exitwhen true                                           //    exit the loop
            endif
        endif
        set degrees = degrees + 1                                       //Add 1 to degrees
    endloop
    return false                                                        //If nothing else was true, then return false
endfunction


function TerrainCheckOnIce takes integer whichPlayer, integer terrain, real x, real y returns boolean
local integer i = 1
    loop
        exitwhen i > udg_maxSlideTerrains                                                                                  
        if terrain == udg_slideTerrain[ i ] or  TerrainCheckOffsetCheck( x, y, udg_slideTerrain[ i ], false ) then      //If the terrain at the unit is one of the sliding terrains then:
            set udg_Speed[ whichPlayer ] = ( udg_initSpeed * 100 * udg_timeStep ) * udg_speedMultiplier[ i ]            //    Set that unit's speed to the original speed setting multiplied by the terrain's corresponding udg_speedMultiplier array
            return true                                                                                                 //    Return that yes, the given parameters are on ice.
        endif
        set i = i +1
    endloop
    return false
endfunction



//===========================================================================
function InitTrig_TerrainCheckFunctions takes nothing returns nothing
//Ignore this function, it's required by the World Editor but I don't need anything done in it.
endfunction
TerrainCheckSlide
JASS:
function TerrainCheckSlide_A2 takes nothing returns nothing
local unit       u = GetEnumUnit    (                      )                 //Set the picked unit as variable "u"
local integer    i = GetPlayerId    ( GetOwningPlayer( u ) ) + 1             //Get the Player number of the unit.
local real       x = GetUnitX       ( u                    )                 //Get the units X
local real       y = GetUnitY       ( u                    )                 //Get the units Y
local integer    t = GetTerrainType ( x , y                )                 //Get the terrain type at the location of the unit


    if GetUnitState( u , UNIT_STATE_LIFE ) > 0 then                          //Check if unit is alive
        if TerrainCheckOnIce( i, t, x, y ) then                              //Check the parameters against the function TerrainCheckOnIce (located in the TerrainCheckFunctions trigger)
            set udg_onIce[ i ] = true                                        //Set OnIce[OwningPlayer'sNumber] = true, allowing the unit to slide.
        else
            set udg_onIce[ i ] = false                                       //Set OnIce[OwningPlayer'sNumber] = false, disabling the unit's slide.
        endif
    endif
    set u = null                                                             //Nulling the unit variable
endfunction



function TerrainCheckSlide_A1 takes nothing returns nothing
    call ForGroup( udg_Slideables , function TerrainCheckSlide_A2 )          //Pick every unit in Slideables and runs function TerrainCheckSlide_A2 for each picked unit
endfunction


function InitTrig_TerrainCheckSlide takes nothing returns nothing
local timer t = CreateTimer(  )
    call TimerStart( t, 0.05, true, function TerrainCheckSlide_A1 )          //Tell the timer to run TerrainCheckSlide_A1 every 0.05 seconds
    set t = null
endfunction
TerrainCheckDeath
JASS:
function TerrainCheckDeath_A2 takes nothing returns nothing
local unit       u        = GetEnumUnit    (       )                                   //Sets the picked unit as variable "u"
local real       x        = GetUnitX       ( u     )                                   //Gets the units X
local real       y        = GetUnitY       ( u     )                                   //Gets the units Y
local integer    terrain  = GetTerrainType ( x , y )                                   //Gets the terrain type at the location of the unit


    if GetUnitState( u , UNIT_STATE_LIFE ) > 0 and terrain == udg_deathTerrain then    //Check if unit is alive and is on the specified death terrain, if he is then do:
        if not TerrainCheckOffsetCheck( x, y, udg_deathTerrain, true ) then            //Check if the unit is NOT close to any non-death terrain
            call KillUnit(u)                                                           //If he isn't then kill him
        endif
    endif
    set u = null                                                                       //Nulling the unit variable
endfunction 



function TerrainCheckDeath_A1 takes nothing returns nothing
    call ForGroup( udg_Slideables , function TerrainCheckDeath_A2 )                     //Pick every unit in Slideables and runs function TerrainCheckDeathA2 for each picked unit
endfunction


function InitTrig_TerrainCheckDeath takes nothing returns nothing
local timer t = CreateTimer(  )
    call TimerStart( t, 0.05, true, function TerrainCheckDeath_A1 )                    // Tell the timer to run TerrainCheckDeathA1 every 0.05 seconds
    set t = null
endfunction


alt download link: http://www.hiveworkshop.com/forums/spells-569/ultime-sliding-system-v1-8-a-144440/
 

Attachments

  • USS [v1.8].w3x
    42.1 KB · Views: 308

Azlier

Old World Ghost
Reaction score
461
Why is the code all separated and messy? Why is the interface... awful?

This should really be in vJass. It would be more efficient and easier to use, that way.
 

n[u]ll

You can change this now in User CP.
Reaction score
93
Why is the code all separated and messy? Why is the interface... awful?

This should really be in vJass. It would be more efficient and easier to use, that way.

I'd have loved to take the easy way out with vJass, but the majority of people who this system is made for don't have it. As for the code being "all seperated and messy", it looks better in the map than on the forum..
 

Azlier

Old World Ghost
Reaction score
461
>it looks better in the map than on the forum..

Somehow, I doubt that...

JASS:
function SlideA takes nothing returns nothing
local unit       u = GetEnumUnit    (                  )                                               //Set the picked unit
local integer    i = GetPlayerId    ( GetOwningPlayer( u ) ) + 1                                       //Set the Player number of the unit.
local real       x = GetUnitX       ( u                )                                               //Set the units X
local real       y = GetUnitY       ( u                )                                               //Set the units Y


	if udg_onIce[ i ] == true and GetUnitState( u, UNIT_STATE_LIFE ) > 0 then                      //Check if the unit is on ice and is alive
		call SetUnitX( u , x + udg_Speed[ i ] * Cos( GetUnitFacing( u ) * 0.017453292 ) )      //Set the units x-axis position
		call SetUnitY( u , y + udg_Speed[ i ] * Sin( GetUnitFacing( u ) * 0.017453292 ) )      //Set the units y-axis position
        call IssueImmediateOrder( u,"stop" )                                                           // !!!Add a "//" before this line to turn on realistic sliding!!!
	endif
    set u = null                                                                                       //Nulling the local unit variable (otherwise it will leak)
endfunction


>[lJASS]// !!!Add a "//" before this line to turn on realistic sliding!!![/lJASS]

That's terrible. Who wants to leaf through the code to change what a single boolean could fix?
 

n[u]ll

You can change this now in User CP.
Reaction score
93
indentation was a typo when i was CnP the triggers, thx for catching it.

>Who wants to leaf through the code to change what a single boolean could fix?
"realistic sliding" isn't really something used by most escape makers because it usually doesn't look right. It's not worth a global, barely worth mentioning in the comment alone.
 

Azlier

Old World Ghost
Reaction score
461
>It's not worth a global, barely worth mentioning in the comment alone.

It's definitely worth a global. If this is the "ultimate" sliding system, it should at least support "realistic" sliding.

Every escape map I've ever played has had this "realistic" sliding.

In fact, it should be able to support both "realistic" and "unrealistic" sliding at the same time.
 

Jesus4Lyf

Good Idea™
Reaction score
397
I found it amusing kind of.

  • [LJASS]* 0.017453292[/LJASS] --> Use the bj constant?
  • [LJASS]local integer i = GetPlayerId ( GetOwningPlayer( u ) ) + 1 [/LJASS] --> This is absurd because players may have more than one unit?
  • You use a group enum to store the units instead of a linked list timer loop (you have to attach through an integer anyway, you may as well use a faster iteration method).
This could be accomplished with a single AIDS/T32 struct. With maybe 30-40 lines of code. Actually, I already did it, essentially. (I understand that it is different, premise is the same though - I'm referring to the constantly moving flying units system there.)

Ok, so it has this one thing going for it - it doesn't use vJass. :)

I'll have to be honest thought - I'm not really amused by the name.

By the way, that varied spacing is because TH.net interprets tabs as 8 spaces instead of 4 in JASS code (I assume).
 

n[u]ll

You can change this now in User CP.
Reaction score
93
In fact, it should be able to support both "realistic" and "unrealistic" sliding at the same time.

Alright, added/updated.

>Use the bj constant?
would it not be faster to just do the math beforehand?

I get that there's faster ways to do this system as a whole, but this is the fastest without using vJass.
Again, vJass would be great to use, but almost anyone who knows vJass can make something like this by themselves. This is for the ones who don't know much about vJass or JASS.

Thanks, I appreciate the comments :)
 
Reaction score
341
Some thoughts..

  • In my opinion vanilla Jass should never be used for public resources. They are hard to configure and use. Global variables are a pain to copy over and the code is generally unreadable.
  • Just because someone knows vJass doesn't mean they know anything about the maths going into these kinds of systems. As well as the proper most efficient ways of doing them.
  • Your system is also hard to configure, I mean all these comments telling the user to change the code is a bad idea. The majority of the people who will be using this system are those new to Jass or GUI'ers. Each will most likely screw something up by editing the code. Which leads me back to the point of vJass (code is easier to configure) would be better for this.
  • Please learn to indent, seeing a bunch of [ljass](__________var_________)[/ljass]'s are very annoying.
  • I understand you are trying to make it easier for people that don't want to use vJass, but just make it in GUI.
 

Lyerae

I keep popping up on this site from time to time.
Reaction score
105
This definately needs vJASS. Maybe a nice struct/method interface... (I don't know! I just like the way structs work with calling functions and setting variables and whatnot.)
The indentation is pretty bad too. Can't stand to read more than a few lines.

Oh, and just because we know vJASS, doesn't mean we can make this ourselves. I would have no idea where to begin on something like this.
 

Lyerae

I keep popping up on this site from time to time.
Reaction score
105
-.-

I know OOP. I've been studying it (C#) for the past few days now.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>I get that there's faster ways to do this system as a whole, but this is the fastest without using vJass.
That is not true. Use GUI AIDS and make a linked list using arrays - eliminates the need for the unit group and the enum calls (but this is optional and up to you, depends if this is meant to be the "ultimate" or not). Currently, this system has undocumented flaws as I previously mentioned, and won't be approved (this is not optional to fix, at worst you must document the flaws in the system).

Referring to:
[LJASS]local integer i = GetPlayerId ( GetOwningPlayer( u ) ) + 1 [/LJASS] --> This is absurd because players may have more than one unit?
 
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