How to: Slide

mems

Memory Lapse
Reaction score
24
Greetings,

In this turtorial I will try to help you to understand the basics of creating smooth sliding effects for push, pull units, creating custom missles ect... Also you will learn how to use game cache to carry local variables between callback functions which you cannot give arguments as normal functions in Jass. So with this you can create custom functions that will be executed periodically and indipendent from main trigger with using its local variables.

WEU users may think that there is already a slide function in advanced triggers. However this slide function has some bugs and memorial leaks. Also it is using tricky global variables and creates another trigger for slide loop which also couse leak and may couse global variables overwrite themselves. If you know and understand the nature of sliding you can create greate custom spells decorated with nice moving custom missles lightining effects. Also you can use the loop trick to create various effects on units such as demaging a unit overtime ect..

Normally, moving a unit from 1 point to another makes its model to play its walk animation. But while creating spells which fires custom moving missles or which makes units pushed, pulled or sliding we dont want this animation get played to have a better sliding effect. Or sometimes we need a spesific animation get played. As an example lets take a look at "spell channel" animation of hero Crypt Lord. That can be a good flight animation. Or sometimes we need units travel on a higher rate than normal specially on missles. Both of this reasons we use trigger help and some tricks to make a unit looks like sliding.

Before reading this turtorial i will think that you know what a trigger, variable is or basic jass for the jass section. If dont, read this turtorials: Triggers, Variable01, Variable02, Jass01, Jass02

We will going to make 2 slide spells for Crypt Lord one with GUI and one with Jass. This spells also attached to this post if you please you can download the map and see how they works.

Lets start creating a new map. I have created a custom unit from hero Crypt Lord, deleated all spells on him. I needded a dummy spell for dedection and starting the trigger actions for sliding and created a custom spell from channel for him. I named it as "Slide (GUI)" the attrirubes are

Art - Animation Names : stand (I dont want any spell animations get played)
Art - Caster : none
Art - Effect : None
Art - Target : None

Stats - Cast Range : 800

Data - Fallow Trough Time : 0.00 (I want to play spesific animations with triggers)
Data - Options : Visible
Data - Target Type : Point (We need a travel location)

Now we have a dummy spell for sliding the hero. Our spell will slide the Crypt Lord himself to the selected destination with some animations.
After giving this custom spell to our Insectish Lord we will going to deal with triggers.

Before all Lets take a look at what kind of variables we need.
- Caster(Unit)
- CasterPosition(Point)
- TargetPoint(Point)
- MoveToPoint(Point)
- Angle (Real)
- Distance (Real)
- N (Real)

Now create a trigger, I named it "Slide Init" This trigger will initialize the variables when the hero casts our dummy spell. This variables will be used in another trigger for sliding effect.
Then create another trigger, I named it "Slide Effect" Which will slide our caster. And turn Off this trigger by right clickling on it and unchecking "Initially on"

Lest start:
Code:
Slide Init
    Events
        Unit - A unit Starts the effect of an ability
    Conditions
        (Ability being cast) Equal to Slide (GUI) 
    Actions
        Set Caster = (Casting unit)
        Set CasterPosition = (Position of Caster)
        Set TargetPoint = (Target point of ability being cast)
        Set Distance = (Distance between CasterPosition and TargetPoint)
        Set Angle = (Angle from CasterPosition to TargetPoint)
        Set N = 0.00

Now we put the Catster into a variable so we can use it in other tirggers. We have calculated the Distance between 2 points so we can use this distance to define if our Caster reached its destination point to stop the slide effect. We have defined the angle between points for the movement function. Finally we have setted our counter variable "N" to zero which will help us to define the movement distance and the main distance to stop the spell effect.
After this we need to initialize our Caster for movement Lets look at our code again:

Code:
Slide Init
    Events
        Unit - A unit Starts the effect of an ability
    Conditions
        (Ability being cast) Equal to Slide (GUI) 
    Actions
        Set Caster = (Casting unit)
        Set CasterPosition = (Position of Caster)
        Set TargetPoint = (Target point of ability being cast)
        Set Distance = (Distance between CasterPosition and TargetPoint)
        Set Angle = (Angle from CasterPosition to TargetPoint)
        Set N = 0.00
        [b]Wait 0.20 seconds
        Unit - Pause Caster
        Animation - Play Caster's spell channel animation
        Unit - Turn collision for Caster Off
        Trigger - Turn on Slide Effect <gen>[/b]

Waiting 0.20 seconds will give us time to start the effect withouth getting disturbed by dummy spells casting time which makes us unable to touch our casting unit. However you cannot wait less then 0.2 seconds with wait function which is the reason that we use 2 triggers for this spell. Couse our movement loop needs faster loop then a for loop with 0.2 seconds decay to make the effect smooth. We have paused our caster becouse player must not give commands to caster such as casting another spell while sliding. Also we made Crypt Lord to play its "spell channel" animation Which makes him flap his wings. Thats optional you dont really need it but for a better looking i will use it.

Here is an important subject Unit - Turn collision for Caster Off This will make our caster to go trough cliffs, trees, units and structures. Our spell will be stopped when caster reaches his destination. So he must not disturbed by anything. Also it looks really bad when get stopped. And finally we have turned on our other trigger which will slide our hero. Lets look at the second trigger codes:

Code:
Slide Effect
    Events
        Time - Every 0.03 seconds of game time
    Conditions        
    Actions
        Set N = (N + 20.00)
        Set CasterPosition = (Position of Caster)
        Set MoveToPoint = (CasterPosition offset by 20.00 towards Angle degrees)        
        Unit - Move Caster instantly to MoveToPoint
        Special Effect - Create a special effect attached to the origin of Caster using Abilities\Weapons\AncientProtectorMissile\AncientProtectorMissile.mdl
        Special Effect - Destroy (Last created special effect)
        Custom script:   call RemoveLocation(udg_MoveToPoint)
        Custom script:   call RemoveLocation(udg_CasterPosition)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
               (N Greater than or equal to Distance) or ((Caster is dead) Equal to True)                
            Then - Actions
                Unit - Unpause Caster
                Unit - Turn collision for Caster On
                Animation - Reset Caster's animation
                Custom script:   set udg_Caster = null
                Custom script:   call RemoveLocation(udg_MoveToPoint)
                Trigger - Turn off (This trigger)
            Else - Actions
                Do nothing

Now everything goes complicated here lets go step by step

- Time - Every 0.03 seconds of game time This makes our trigger run every 0.03 seconds which is smaller than 0.2 limit. So we use this to have a smooth loop.

- Set N = (N + 20.00) We need to define how much we have moved each time this trigger runs the value of N will increase by 20.00 couse we move our unit 20.00 towards each time below. You can create another real variable and use this instead of 20.00 here and below; make it higher for a faster movement and lesser for a slow movement

- Set CasterPosition = (Position of Caster) We need Current position of caster to define the MoveToPoint.

- Set MoveToPoint = (CasterPosition offset by 20.00 towards Angle degrees)Point with polar offset deines a point from a given point towards or backwards to a given real destination by angle degrees. This will define a point that is 20.00 towards from current position of caster to Point of ability being cast. We have been defined the Angle at first trigger between casterposition and point of abilitiy being cast. People are using some aritmethic methods in loops to cahnge the angle by time to have custom movement effects. Forex a missle moves circular.

- Special effect functions will create ancient missle effect and kills it at origin of caster to have tear effect while moving. Our special effect is attached to origin of caster so it will move with our hero.

- Custom script: call RemoveLocation(udg_MoveToPoint) At here i need to say something about memorial leaks. Every time u say position of unit, Region centered special effects, ligtining effects.. ect WC engine creates an object there which will stored in game cache. If you do not destroy, remove this objects, They will stand still tehere and will leak the memory which couses lag in game. Specially such movement spells and on unit groups(pick all units ect) also in loops, it creates greate amounts of objcetcs which will couse big lags after casting alot of times. So we need to destroy or remove this objects. However there are no functions in GUI to remove them so i must use Jass in Custom Sctipt functions. call RemoveLocation() will remove the given location object from game. "udg_MoveToPoint" is the Jass name of MoveToPoint variable.

- (N Greater than or equal to Distance) or ((Caster is dead) Equal to True) if N greater or equal to Distance then this means Our caster has reached the target location. OR If our hero get killed before reacing his location we dont want him to continue his spell. So if any of this conditions will become true we cancel all effects on Caster such as unpauseing him turning his collision on and reset the movement animation on him. We remove the dont needed locations, units and turn this trigger off to finish the spell.

This spell is just to make you understand the basics of sliding a unit. However Becouse of usage of Global variables and leaks on conditional structure this type spells can only be used in single player games. On multi player game you need same variables for each possible player and if spell will be casted same time with 2 people this can couse problem. Such as you can turn off the trigger before other ones which will couse all off others stop their effect or turning the trigger on forever which will never stop the effect. We cannot use local variables becouse of 2 triggers So we strongly need Jass for a working spell on multiplayer maps even for single player maps for enabling this kind of spells can casable for multiple units at same time without any problem.

-------------------------------------------------------------------------------

Now we will going to make the same spell with jass. From now i will think that you know the basics of Jass and will not explane trigger structures, common functions such as PouseUnit() ect.. You can learn the basics from the links i have given at start of this turtorial. Also If you dont know how to do something with jass just make it with GUI and turn this trigger to custom text to see the basic functions.

Jass is alot much more flexible an useful then GUI. You can control the full actions, even you can create your own functions with it. Our new spell will be use only 1 trigger with totaly local variables which will enable multi catsing. But is a bit complicated and its hard to imagine which codes doing what instead of GUI.

We have a problem here. In 1 trigger how can we make a loop that has smaller waiting amount than 0.2 seconds? The answer is Countdown Timer. Countdown timer can work alone itself indipendent from main trigger. And with Jass we can give command to timer which will call a function when it expires. Also we can give smaller values than 0.2 seconds to a timer expiration rate!.

Again we will have a problem here. StartTimer function parameters are some Real, Boolean values and a callback function like ForGroup function. And we cannot send arguments to a callback function. At there some people find a way to use game cache to store local variables and carry them between that kind of functions. I will use Vexorian's Handle Variable Functions in this turtorial. Lets start to explane what is this and how to use it:

Handle Variable Functions are to store and get variables from game cache which allows to get same local variables with same names. Simply the usage is like that:


SetHandle<vartype>(handle_to_associate_to, string_Name_Stored_var,var_being_assiciated)
GetHandle<vartype>(handle_associated_to, string_Name_Stored_var)


Here is this functions, You need to copy the code below to the header of your map which you can see it by clicking on map name on trigger editor. You can see it from demo map too.

Code:
//**************************************************************************************************
//*
//*  Handle Variables Functions by Vexorian (http://jass.sourceforge.net/doc/)
//*  
//*  -------------------------------
//*  Enables usage of game cache for
//*  transphering local variables
//*  beetveen functions.
//*  -------------------------------
//*
//*  Requires:
//*  ¯¯¯¯¯¯¯¯¯
//*  A global variable with name "cache" and type "GameCache" as an exact match
//*  note that the "LocalVars" function must be placed before anything in this 
//*  map header expect this kind of comments
//**************************************************************************************************

function LocalVars takes nothing returns gamecache
    if udg_cache==null then
        set udg_cache=InitGameCache("cache")
    endif
    return udg_cache
endfunction

//-------------------------------------------------

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2U takes handle h returns unit
    return h
    return null
endfunction

//-------------------------------------------------

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

//-------------------------------------------------

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

//-------------------------------------------------

function SetHandleHandle takes handle subject, string name,  handle value returns nothing
    call SetHandleInt( subject, name, H2I(value) )
endfunction

//-------------------------------------------------

function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction

//-------------------------------------------------

function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction


//-------------------------------------------------

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

//-------------------------------------------------

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

You will see that there are only 3 types to store in handle

SetHandleHandle(),
SetHandleInt(),
SetHandleReal()


Instead of real and integer we can store this variable types with setHandleHandle() function:
ability, aidifficulty, alliancetype, attacktype, blendmode, boolexpr, button, camerafield, camerasetup, damagetype, defeatcondition, dialog, effect, effecttype, event, eventid, fogmodifier, fogstate, force, gamecache, gamedifficulty, gamespeed, gamestate, gametype, group, image, itempool, itemtype, leaderboard, lightning, location, mapcontrol, mapdensity, mapflag, mapsetting, mapvisibility, multiboard, multiboarditem, pathingtype, placement, player, playercolor, playergameresult, playerscore, playerslotstate, playerstate, quest, questitem, race, racepreference, raritycontrol, rect, region, sound, soundtype, startlocprio, terraindeformation, texmapflags, texttag, timer, timerdialog, trackable, trigger, triggeraction, triggercondition, ubersplat, unitpool, unitstate, unittype, version, volumegroup, weapontype, weathereffect, widget

You see some eccentric functions like H2I(Handle to integer) or H2U(Handle to Unit) this functions are used to convert Handle types to various other variable types.
To convert this we use a bug in jass called return bug. The site is explaning what is this but i will give an example for it.

In this turtorial code there is only 1 retun bug function needed for our spell called H2U which takes handle returns unit.(H2I function also needed for handle variable functions itself too) Forexample if you want to store a special effect to game cache with using this functions you need such a code like this

Code:
function H2E takes handle h returns effect
   return h
   return null
endfunction

Now lets get return to our main subject i will explane how to store and get them while creating our spell.
first of all i have created a trigger and named it "Slide"(Case Sensitive) and turned it into custom text. Then copied my slide spell in object editor and called it "Slide (Jass)". After that i have changed the Base Order Id from "channel" to "loctusswarm". Couse I will use 2 same spells at same unit so they must not interrupt temselves. Then i pressed CTRL+D to see the Raw name of my new spell which is 4 digit codes at start of its name, in my map it says 'A001' in yours it can be different. we will use this raw code to define our spell in Jass. Then i have created the basic structure of my trigger here it is:

Code:
//===================================================================================================
//configuration
constant function Slide_SpellId takes nothing returns integer
    return 'A001' //Rawcode of our dummy ability
endfunction   

constant function Slide_StepDistance takes nothing returns real
    return 20.00 //will define the movement speed
endfunction

//===================================================================================================
//main trigger

function Slide_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Slide_SpellId()
endfunction


function Slide_Actions takes nothing returns nothing
    
endfunction

//===================================================================================================
function InitTrig_Slide takes nothing returns nothing
    set gg_trg_Slide = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Slide, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Slide, Condition( function Slide_Conditions ) )
    call TriggerAddAction( gg_trg_Slide, function Slide_Actions )
endfunction

Its an empty trigger which will start to run when our dummy spell being casted. Here I need to explane the constant functions. We use constant functions to create useful spell configurations u cant calla constant function in another constant function thats the only differance from normal function i just use them because people can easily find and edit them above main spell codes
our spell will use this:

Code:
constant function Slide_SpellId takes nothing returns integer
    return 'A001' //Rawcode of our dummy ability
endfunction

With this we can use Slide_SpellId() function everywhere and everytime we needed so we only need to change 1 thing to effect whole spell forex your raw code of your dummy spell can be different and you only need to change it at once. You can also use such constant functions to change spell effects to get level demages ect.

Code:
constant function Slide_StepDistance takes nothing returns real
    return 20.00 //will define the movement speed
endfunction

With this we can configure the movement speed. Each time this function called, a real value will return. So we can use it with the style that i have told above in GUI explenation of Slide Effect trigger.

Okay lets continue. What was our concept? we will define some variables that are needed to slide our unit and start the effect loop with using this variables. Lets define the variables first in Slide_Actions function.

Code:
function Slide_Actions takes nothing returns nothing
    local timer Loop = CreateTimer() 
    local unit Caster = GetSpellAbilityUnit()
    local location CasterPosition = GetUnitLoc(Caster)
    local location TargetPosition = GetSpellTargetLoc()
    local real Angle = AngleBetweenPoints(CasterPosition, TargetPosition)
    local real Distance = DistanceBetweenPoints(CasterPosition, TargetPosition)

endfunction

Here is the all same definations with our GUI trigger but this time all of them are local variables. Also we have a timer now which we use it as the second trigger that we made in GUI and we did not defined the real N variable which we dont need here but will be defined in slide function. Now lets make our Caster to get ready for sliding

Code:
function Slide_Actions takes nothing returns nothing
    local timer Loop = CreateTimer() 
    local unit Caster = GetSpellAbilityUnit()
    local location CasterPosition = GetUnitLoc(Caster)
    local location TargetPosition = GetSpellTargetLoc()
    local real Angle = AngleBetweenPoints(CasterPosition, TargetPosition)
    local real Distance = DistanceBetweenPoints(CasterPosition, TargetPosition)


    [b]call TriggerSleepAction( 0.20 )
    call PauseUnit( Caster, true )
    call SetUnitAnimation( Caster, "spell channel" )
    call SetUnitPathing( Caster, false )[/b]

endfunction

Now as we did before in GUI, our caster is paused, started to play his channel animation and his pathing is off so he can go trough everything now its time to store our needed variables into game cache.
as i said before our syntax is like that:

-SetHandle<vartype>(handle_to_associate_to, string_Name_Stored_var,var_being_assiciated)

handle_to_associate_to will be our timer "Loop".
To store integer values we will use SetHandleInt()
To store real values we will use SetHandleReal()
All other types will be stored with SetHandleHandle()

so lets do it:
Code:
function Slide_Actions takes nothing returns nothing
    local timer Loop = CreateTimer()
    local unit Caster = GetSpellAbilityUnit()
    local location CasterPosition = GetUnitLoc(Caster)
    local location TargetPosition = GetSpellTargetLoc()
    local real Angle = AngleBetweenPoints(CasterPosition, TargetPosition)
    local real Distance = DistanceBetweenPoints(CasterPosition, TargetPosition)

    call TriggerSleepAction( 0.20 )
    call PauseUnit( Caster, true )
    call SetUnitAnimation( Caster, "spell channel" )
    call SetUnitPathing( Caster, false )
    
    [b]call SetHandleHandle(Loop, "Caster", Caster)
    call SetHandleReal(Loop, "Angle", Angle)
    call SetHandleReal(Loop, "Distance", Distance)[/b]

endfunction

- call SetHandleHandle(Loop, "Caster", Caster) when we use such a function it stores the variable Caster into cache with the subject Loop, neame "Caster" and value Caster. So if we have the subject and if we know its name we can take our value anytime we want.

We have stored our needed variables into game cache. Now we need an a function that will be called each time our timer expires. We must define it between Slide_Conditions() and Slide_Actions() functions
here it is:

Code:
function Slide_Effect takes nothing returns nothing
    local timer Loop = GetExpiredTimer()
    local unit Caster = H2U(GetHandleHandle(Loop, "Caster"))
    local real Angle = GetHandleReal(Loop, "Angle")
    local real Distance = GetHandleReal(Loop, "Distance")
    local real N = GetHandleReal(Loop, "N")
    local location CasterPosition = GetUnitLoc(Caster)
    local location MoveToPoint
    local effect Effect

    if ((N <= Distance) and (not (IsUnitType(Caster,UNIT_TYPE_DEAD)))) then
        set N = N + Slide_StepDistance() 
        call SetHandleReal (Loop, "N", N)
        set MoveToPoint = PolarProjectionBJ(CasterPosition, Slide_StepDistance(), Angle)
        call SetUnitPositionLoc(Caster, MoveToPoint)
        set Effect = AddSpecialEffectTarget("Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",Caster,"origin")

        call DestroyEffect(Effect)
        call RemoveLocation(MoveToPoint)
        call RemoveLocation(CasterPosition)
        set Caster = null       
    else                        
        call PauseTimer(Loop)        
        call PauseUnit( Caster, false )
        call ResetUnitAnimation( Caster )                
        call SetUnitPathing( Caster, true )

        call RemoveLocation(MoveToPoint)
        call RemoveLocation(CasterPosition)
        set Caster = null        
        call FlushHandleLocals(Loop)
        call DestroyTimer(Loop) 
    endif
   
endfunction

Again we will go step by step and i will explane the important codes.

- local timer Loop = GetExpiredTimer() Slide_Effect() is a Callback function that has been called when our timer expired. So we can get the expired timer into a variable. Then we can get our handle variables stored in cache. You must note that you can get expired timer only in functions that are called in this function and their sub functions. If u try to get the expired timer in an unreleated function you cannot take it.

- local unit Caster = H2U(GetHandleHandle(Loop, "Caster")) As i said before instead of integer or real values we use handle type to store variables in game cache. And we take them back as handle type. our take back function is GetHandleHandle(). When we give the subject Loop and the name "Caster" it will return our value in this adress in cache. However it has been returned a Handle type so we need to change it to unit variable with H2U() function.

- local real Angle = GetHandleReal(Loop, "Angle") as you see, we dont convert types in real (and integer) types.

- local real N = GetHandleReal(Loop, "N") Here we define the counter variable N at this function. But we give the defoult value by taking it from cache. However we did not stored a variable named N into game cache so the defoult value will be 0.00 at first time this function runned. You will see that we store N to cache at below codes so at next time it will have a value and we will use this as a counter to define if our caster reached the destination.

- local effect Effect instead of GUI trigger we define an effect variable here to destroy the slide effect safely later.

- if ((N <= Distance) and (not (IsUnitType(Caster,UNIT_TYPE_DEAD)))) then This time i have changed the condition style our movement will occur when counter N smaller than distance and Our caster is alive.

- set N = N + Slide_StepDistance() Slide_StepDistance() is our constant function it returns real value of 20.00 as i said i will use this function here and defining the MoveToPoint variable.

- call SetHandleReal (Loop, "N", N) N is a local variable. We cannot use local variable values here as a counter. After function is executed, it will be removed. Our loop concept is executing a function several times. So we need to store the counter value in game cache to reach it again at definition of the local variable.

- set MoveToPoint = PolarProjectionBJ(CasterPosition, Slide_StepDistance(), Angle) again you see we use point with polar offset and movement distance is defining with our constant function Slide_StepDistance() So when i cahnge the value in Slide_StepDistance function it will effect the movement step and the counter increament at same time. So i can use higher values for a fast movement and lesser values for slow movements.

- set Effect = AddSpecialEffectTarget and call DestroyEffect() are to create and kill the ancient protector effects but i use a variable instead of killing last created effects here. In my opinion it is better. Our loop is fast anything can happen while casting same spells if a lil lag or anyother thing happens we can see some non destroyed flying ancient missles around map. I use a local variable to adress it so i gaurantee to not see such bugs.

- else statements will executed when our caster reaches his destination or get killed which will stop the spell effect.

- call PauseTimer(Loop) First of all we have a fast loop it MUST be paused before doing anything.

- call FlushHandleLocals(Loop) After getting our caster resetted and removed leaking objects we also need to get rid of variables that we have stored in cache. FlushHandleLocals() function will destroy all values and names with given handle subject. Our subcejt was Loop and we destroy all variables by giving its name to this function. So we get rid of memorial leaks.

- call DestroyTimer(Loop) We have destroyed the values in game cache but our subjet Loop is still here and continue running even we paused it. So we need to get rid of this object too.

we have finished our our slide function. Now we need to start this timer at main function:

Code:
call TimerStart(Loop, 0.03, true, function Slide_Effect)

We start our timer Loop as a continuous timer and expire in 0.03 second and each time it expired it calls function Slide_Effect. With this we have a periodic event loop that runs each 0.03 seconds game time like the second trigger we did before in GUI. So we can say that we have created a tricky triggerlike tingie. And the actions of this tricky trigger is defined in Slide_Effect function. Also we used game cache to store local variables and accessed them from there in our second tricky trigger.

Finally we have finished our spell. This is our compleated trigger code:

Code:
//===================================================================================================
//configuration

constant function Slide_SpellId takes nothing returns integer
    return 'A001' //Rawcode of our dummy ability
endfunction   

constant function Slide_StepDistance takes nothing returns real
    return 20.00 //will define the movement speed
endfunction

//===================================================================================================
//main trigger

function Slide_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Slide_SpellId()
endfunction

//This function is slides our unit.
function Slide_Effect takes nothing returns nothing
    local timer Loop = GetExpiredTimer()
    local unit Caster = H2U(GetHandleHandle(Loop, "Caster"))
    local real Angle = GetHandleReal(Loop, "Angle")
    local real Distance = GetHandleReal(Loop, "Distance")
    local real N = GetHandleReal(Loop, "N")
    local location CasterPosition = GetUnitLoc(Caster)
    local location MoveToPoint
    local effect Effect

    if ((N <= Distance) and (not (IsUnitType(Caster,UNIT_TYPE_DEAD)))) then
        set N = N + Slide_StepDistance() 
        call SetHandleReal (Loop, "N", N)
        set MoveToPoint = PolarProjectionBJ(CasterPosition, Slide_StepDistance(), Angle)
        call SetUnitPositionLoc(Caster, MoveToPoint)
        set Effect = AddSpecialEffectTarget("Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",Caster,"origin")

        call DestroyEffect(Effect)
        call RemoveLocation(MoveToPoint)
        call RemoveLocation(CasterPosition)
        set Caster = null       
    else                        
        call PauseTimer(Loop)        
        call PauseUnit( Caster, false )
        call ResetUnitAnimation( Caster )                
        call SetUnitPathing( Caster, true )

        call RemoveLocation(MoveToPoint)
        call RemoveLocation(CasterPosition)
        set Caster = null        
        call FlushHandleLocals(Loop)
        call DestroyTimer(Loop) 
    endif
   
endfunction

//Main function of our trigger
function Slide_Actions takes nothing returns nothing
    local timer Loop = CreateTimer()
    local unit Caster = GetSpellAbilityUnit()
    local location CasterPosition = GetUnitLoc(Caster)
    local location TargetPosition = GetSpellTargetLoc()
    local real Angle = AngleBetweenPoints(CasterPosition, TargetPosition)
    local real Distance = DistanceBetweenPoints(CasterPosition, TargetPosition)


    call TriggerSleepAction( 0.20 )
    call PauseUnit( Caster, true )
    call SetUnitAnimation( Caster, "spell channel" )
    call SetUnitPathing( Caster, false )
    
    call SetHandleHandle(Loop, "Caster", Caster)
    call SetHandleReal(Loop, "Angle", Angle)
    call SetHandleReal(Loop, "Distance", Distance)

    // Initialize the slide loop
    call TimerStart(Loop, 0.03, true, function Slide_Effect)

endfunction

//===================================================================================================
function InitTrig_Slide takes nothing returns nothing
    set gg_trg_Slide = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Slide, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Slide, Condition( function Slide_Conditions ) )
    call TriggerAddAction( gg_trg_Slide, function Slide_Actions )
endfunction

The spells in this turtorial are just examples to understand the issue you can use them by modifiying them such destroying trees on his way or demaging units near him while sliding.

With this i hope u get the point on creating sliding units, missles ect. Even you can create periodic events by using this style such as demagin a unit overtime or growing it like bloodlust or making it small like opposite of bloodlust effect (lol nice idea) ect. So you can create advanced spells or events in your maps by using this tricks.

I am sorry if my English is not enough to tell the events here and sorry again for all possible grammar leaks. Just reply the bugs and errors i will edit this post or try to help you about this subject.

Have a nice map making,

mems
 

Attachments

  • Demo Map.w3x
    25.6 KB · Views: 1,031

Red Beard

Part-Time helper.... Full Time Lurker
Reaction score
38
BY THE HAIR ON MY CHIN AND AS SURE AS DOCKYARD WENCH... THIS BE GOOD

CLIMB ABOARD MEMSY ME BOY, :nuts:


(+rep is in the post)
 
S

skinny77

Guest
Er i did the GUI one, and followed yout tutorial exactly. But I have no experience with custom scripts, so i just copy and pasted the ones u have on this tutorial. But when i try and test the map, it says there is an error with line 58. It says "Type mismatch in assignment" and i have to disable the trigger. This is line 58 "set udg_Caster = null." Any ideas what is wrong? I thought maybe i need a "Position" next to caster, for like "CasterPosition" but i have no knowledge of custum scripts, so what wud i know?

BTW ur english was good on that tutorial.
 

mems

Memory Lapse
Reaction score
24
- if you have let some spaces around the script that can be happen
- that can happen becouse of the other line before 58 i mean line 57
- U may have been assigned wrong type to variable Caster

and

Try to take a look at them if you could not find anything try to download the attached demo map and compare it with yours. If you still could not find anything again please upload your map here to let us have a look
 
S

skinny77

Guest
Well, i compared them, and there was only one difference i found. Where u have "Pause Caster" or "Turn Collision On for Caster," and so on, i have "Pause (Casting unit)" or "Turn Collision On for (Casting unit)," and so on. Could this be the problem?
 

mems

Memory Lapse
Reaction score
24
yes we have 0.2 waiting time while waiting there if another unit casts a spell then it becmoes casting unit not your hero

also there is no problem with Pause Caster and other function which uses global variables. Also have u checked variables are case sensitive if your variable is caster, not Caster u may have problems

note: i have not see any downloads on attachment hope you have downloaded and compared..
 
S

skinny77

Guest
Dude, i caompared them and checked EVRYTHING! but that was th only difference i found.
 

mems

Memory Lapse
Reaction score
24
okay please upload your map here then let me have a look whats the problem
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Wow! This is extremely good! I learned quite a few things from it, such as waits being unable to be less than 0.2 seconds. (That kinda sucks for Integer A actions). Also found out I can make a non-laggy slide spell! This glitches less than one of my simplest triggers!
 

Korolen

New User (Why do I keep getting those red bars?)
Reaction score
69
Nice spell/slide/thing! I made a thing kinda like this about a month ago, but it moved the ends of Lightning Effects. It kinda looks wierd, though, because every time you move a Lightning Effect, it skrewes up it's animation, so it is flying all over the place, untill it is finished moving, and then it instantly settles down, for a strange effect.

For this spell, I think it would be cool, to instead of creating thousands on the Ancient Protecter missiles, make a 1k particle emitter, for each type of ground, and so it looks like he kicks up dust from where he currently is. You could also make some grounds not kick up dust, because it would look strange if you sprinted on some Dalaran White Marble, and he kicks up all this Ancient Protecter dust.
 

XXXconanXXX

Cocktails anyone?
Reaction score
284
Most tutorials that have periodic triggers in them suffer from horrible amounts of memory leaks, but yours does not! Good job!
 

mems

Memory Lapse
Reaction score
24
Korolen said:
For this spell, I think it would be cool, to instead of creating thousands on the Ancient Protecter missiles, make a 1k particle emitter, for each type of ground, and so it looks like he kicks up dust from where he currently is. You could also make some grounds not kick up dust, because it would look strange if you sprinted on some Dalaran White Marble, and he kicks up all this Ancient Protecter dust.

This Tutorial was for to teach people how to make such things in a very basic way. So all the stuff in it is at its simplest, easiest formation.

Creating thousents of missles is not a bad thing unless u are removing them from cathce. Missles has the lovest amount of lag and modelling issues on WC3. Anyway if u dont want to have an effect on snow or want to have a different slide effect on water forex just put there an if and check the casters current location and put there other effects with elseif's and elese's..
 

Korolen

New User (Why do I keep getting those red bars?)
Reaction score
69
Kubanator said:
caster should be unit not unit-type
I don't exactly know what you mean, but a "unit-type" is just an integer in JASS.
Kubanator said:
is there any reason my char should tele to the middle of the map?
That is probably because you are passing a bad location ("point" in GUI) variable to the MoveUnit function. Make sure you are getting a good location, and that you don't skrew it up.
 

Rad

...
Reaction score
228
Eh hey guys just so you know this tutorial is way to long for the amount of information the average person needs to make a slide trigger.

Put it simply, the .03 periodic trigger is to slide the unit. Have another trigger set the variables and start the trigger. The easiest way to do it involves just a few variables, store units, angle, and one that is checked to turn off the trigger.

I guess I'm a bit late at pointing this out, I havent even read the whole tutorial once I figured out why using .02 second waits in loops (They set to .2, as minimum) didnt work it was easy. Remember - Polar offset is your friend :)

is there any reason my char should tele to the middle of the map?
That is probably because you are passing a bad location ("point" in GUI) variable to the MoveUnit function. Make sure you are getting a good location, and that you don't skrew it up.

My best guess would be your using "Target unit of ability being cast" as a point, just note that if you have jass lines to remove the memory leak for "Position of..." it will void the position to default 0,0. Also the "Target unit..." is reset after the first wait - So set it to a variable before all waits then use the variable instead.
 

Korolen

New User (Why do I keep getting those red bars?)
Reaction score
69
Rad said:
it will void the position to default 0,0.
Acually, it doesn't "void it to 0,0", it just destroys the variable. It is gone. Dissapeared. Null. When you pass a "destroyed location ("Point" in GUI)" (Acually, you are just passing "null". it just sets the variable to be null, but you can pass null anyways) to a function (Like "SetUnitPosition"), then it just uses the center of the map.

PS: Ignore all the parenthisies lol.
PPS: I think that's the first time I've ever said "lol" on TH, lol.
 
K

Kubanator

Guest
what i meant was that in GUI unit-type is a class of a unit eg. footman, knight, death knight, ect. while a unit is a specific unit on the map
 

Rad

...
Reaction score
228
Korolen said:
Acually, it doesn't "void it to 0,0", it just destroys the variable. It is gone. Dissapeared. Null. When you pass a "destroyed location ("Point" in GUI)" (Acually, you are just passing "null". it just sets the variable to be null, but you can pass null anyways) to a function (Like "SetUnitPosition"), then it just uses the center of the map.

Well would voiding it to 0,0 (empty) be the same as nullifying it (which is the same meaning as it sets to 0,0) :s
 

Korolen

New User (Why do I keep getting those red bars?)
Reaction score
69
Well, my point is that it doesn't set it to anything. It just removes the variable, and if you pass a removed variable, it uses the default, or the middle of the map (Which I don't think is 0,0, but width/2,height/2)
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      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