|
|
|
|
How to: Slide
Tutorial By mems
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:
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:
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
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>
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:
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.
//**************************************************************************************************
//*
//* 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
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:
//===================================================================================================
//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:
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.
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.
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
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 )
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:
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)
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:
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:
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:
//===================================================================================================
//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
Click here to comment on this tutorial.
|
|
|
|
|
Designed by Arkheno
2005 Blizzard
Entertainment®
Blizzard Entertainment is a trademark or registered trademark of Blizzard
Entertainment, Inc. in the U.S. and/or other countries. All rights
reserved. |
|