• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] How to use locals on another trigger?

Status
Not open for further replies.
Level 10
Joined
Sep 6, 2007
Messages
440
I defined some local's on a trigger. And then I wanted it to enable a trigger which would do actions of it every 1 second. But it doesn't do them because it doesn't know the variables I used for it. And when I declare the variables, it still does nothing. For those of you who read my other threads, sorry for about the same trigger but the problems never end! :cry: Any help is pretty much appreciated.

JASS:
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

function Trig_Mental_Effects_Func001C takes nothing returns boolean
    if ( not ( udg_IntegerM < 1000 ) ) then
        return false
    endif
    return true
endfunction

function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    call UnitAddAbilityBJ( 'A003', Victim )
    call UnitRemoveAbilityBJ( 'A003', Victim )
    call SetUnitFlyHeightBJ( Victim, 250.00, 75.00 )
    call EnableTrigger( gg_trg_Mental_Cancelled )
    call EnableTrigger( gg_trg_Mental_Looping )
endfunction

//===========================================================================
function InitTrig_Mental_Effects takes nothing returns nothing
    set gg_trg_Mental_Effects = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Effects, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Mental_Effects, Condition( function Trig_Mental_Effects_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Effects, function Trig_Mental_Effects_Actions )
endfunction
*AND*
JASS:
//===========================================================================
// Trigger: Mental Looping
//===========================================================================
function Trig_Mental_Looping_Actions takes nothing returns nothing     
    call UnitDamageTargetBJ( Caster, Victim, ( I2R(GetUnitAbilityLevelSwapped('A001', Caster)) * 12.00 ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND )
    call AddSpecialEffectTargetUnitBJ( "origin", Victim, "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )
    call PauseUnitBJ( true, Victim )
endfunction

//===========================================================================
function InitTrig_Mental_Looping takes nothing returns nothing
    set gg_trg_Mental_Looping = CreateTrigger(  )
    call DisableTrigger( gg_trg_Mental_Looping )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Mental_Looping, 1.00 )
    call TriggerAddAction( gg_trg_Mental_Looping, function Trig_Mental_Looping_Actions )
endfunction
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Er, you can't use locals on another trigger, that's the point of locals, they work only in a single function. But the way of transferring locals to different functions have been developed: Local Handle Vars. I don't feel like explaining how they work, so here, learn from these two tuts: 1, 2.

You have lot to learn..... :)
 
Level 10
Joined
Sep 6, 2007
Messages
440
So as far as I got, I need to set my Caster and Victim locals to a local using GetHandleUnit(<variable I'm going to set>, <NeededLocal>) but what exactly the type of the variable goes for "needed local" there. ( according to my spell ) But still have no clue on how to use it.
 
Level 10
Joined
Sep 6, 2007
Messages
440
I've decided to combine the functions on Mental Effects. Now I'm trying to call the Mental Looping function in Mental Effects ( Mental looping is not a different trigger anymore ). But how am I going to do the timer thing? I mean I want it to deal dmg to target once every a second for 10 seconds. How am I going to do that? ( Tried to make local timer t = CreateTimer() and then TimerStart function. But how exactly do I use the boolean and real side of it. As it says TimerStart(whichTimer,real,boolean,handleFunc) )

So this is how it looks like now
JASS:
//===========================================================================
// Trigger: Mental Effects
//===========================================================================
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

function MentalActions takes nothing returns nothing     
    call UnitDamageTargetBJ( Caster, Victim, ( I2R(GetUnitAbilityLevelSwapped('A001', Caster)) * 12.00 ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND )
    call AddSpecialEffectTargetUnitBJ( "origin", Victim, "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )
    call PauseUnitBJ( true, Victim )
endfunction

function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    local timer t = StartTimerBJ(t, true, 1.)
    call UnitAddAbilityBJ( 'A003', Victim )
    call UnitRemoveAbilityBJ( 'A003', Victim )
    call SetUnitFlyHeightBJ( Victim, 250.00, 75.00 )
endfunction
//===========================================================================
function InitTrig_Mental_Effects takes nothing returns nothing
    set gg_trg_Mental_Effects = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Effects, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Mental_Effects, Condition( function Trig_Mental_Effects_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Effects, function Trig_Mental_Effects_Actions )
endfunction
 
Last edited:
Level 27
Joined
Feb 22, 2006
Messages
3,052
You're still going to need handles, and if you put them in one trigger, put the child function above the parent function.
Now, for TimerStart, write
JASS:
call TimerStart(t, 1., true, function Trig_Mental_Looping_Actions)//Rename the function to whatever you name it, KEEP function.
But as I said before, you'll still need handles/globals/structs.
--donut3.5--
 
Level 10
Joined
Sep 6, 2007
Messages
440
I decided to try this because I thought I wouldn't need to declare them with handles if I do call it inside it. But I tried and I have to which I was wrong. Now I have 2 chances...
1) I can make a loop/timer which would do what Mental Loop do; or
2) Use HandleVars system which I didn't get.
 
Last edited:
Level 13
Joined
Nov 22, 2006
Messages
1,260
Be patient, Handles are really easy to understand, just take your time, read the tutorials couple of times and you'll get them.

What you're going to do with those variables you need to transfer is you're going to attach them to the timer:

JASS:
function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    local timer t = StartTimerBJ(t, true, 1.)
    call UnitAddAbilityBJ( 'A003', Victim )
    call UnitRemoveAbilityBJ( 'A003', Victim )
    call SetUnitFlyHeightBJ( Victim, 250.00, 75.00 )
    call SetHandleHandle(t, "Caster", Caster)
    call SetHandleHandle(t, "Victim", Victim)
endfunction

The first parameter of each SetHandle<something> is the thing you attach the variable to, the second is the name to which you store it to (it could be any string), the third is the actual variable you're attaching.

The thing is that you can get the timer you're attaching to with another function (by GetExpiredTimer() you'll get the timer which fired the function), which will allow you to also get the variables you attached to that timer (with GetHandleUnit in this case).

Handle is each type that is not an integer, real, string or boolean (which are also called basic types), so it could be a unit, a doodad or whatever. So it's like a higher type of the types which are not basic.

Using the handle as the parameter just allows you more flexibility and you don't have to create a SetHandle function for each type (but for GetHandle functions you do, because you must tell the system exactly what do you need to retrieve).


Enough said, don't give up, give it a shot, you'll get it after a while (it took me ridiculously long, but I think you can get it faster).
 
Level 10
Joined
Sep 6, 2007
Messages
440
JASS:
//===========================================================================
// Trigger: Mental Effects
//===========================================================================
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

function Mental_Looping takes nothing returns nothing
    local timer time = GetExpiredTimer()
    local unit Caster = GetHandleUnit(time, "Caster")
    local unit Victim = GetHandleUnit(time, "Victim")
    call UnitDamageTargetBJ( Caster, Victim, ( I2R(GetUnitAbilityLevelSwapped('A001', Caster)) * 12.00 ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND )
    call AddSpecialEffectTargetUnitBJ( "origin", Victim, "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )
    call PauseUnitBJ( true, Victim )
endfunction

function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    local timer time = StartTimerBJ(time, true, 10.)
    call UnitAddAbilityBJ( 'A003', Victim )
    call UnitRemoveAbilityBJ( 'A003', Victim )
    call SetUnitFlyHeightBJ( Victim, 250.00, 75.00 )  
    call SetHandleHandle(time, "Caster", Caster)
    call SetHandleHandle(time, "Victim", Victim)
endfunction

//===========================================================================
function InitTrig_Mental_Effects takes nothing returns nothing
    set gg_trg_Mental_Effects = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Effects, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Mental_Effects, Condition( function Trig_Mental_Effects_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Effects, function Trig_Mental_Effects_Actions )
endfunction

So this is how it ended up. But a strange problem occurs. It gives me errors EVERYWHERE!!!! I think I screwed something up. It goes PLAYER SLOT things on <mapname>.j and many other strage ENDIF bugs which I didn't even use. What's the explanantion of this? I'm so pissed :ugly: ( No bug when I check Syntax with JassCraft, but there are a lot of bugs when I use WE. And yes I added KaTTaNa's HandleSystem)
 
Last edited:
Level 10
Joined
Jun 26, 2005
Messages
236
Okay, go download JASSNEWGEN from: http://wc3campaigns.net/showthread.php?t=90999

Now, use this improved (hopefully) version of your script:

JASS:
scope MentalEffects
private struct Data
    unit Caster
    unit Victim
endstruct
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction
private function Repeat takes integer tag returns boolean
    local Data data = tag
 
    call UnitDamageTarget(data.Caster, data.Victim, (I2R(GetUnitAbilityLevel(data.Caster, 'A001')) * 12.), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND, WEAPON_TYPE_WHOKNOWS)
    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", data.Victim, "origin"))
    call PauseUnit(data.Victim, true)
    return true
endfunction
private function Actions takes nothing returns nothing
    local Data data = Data.create()
 
    set data.Caster = GetSpellAbilityUnit()
    set data.Victim = GetSpellTargetUnit()
 
    call TimedEvent(10., integer(data), Callback.Repeat)
    call UnitAddAbility(data.Victim, 'A003')
    call SetUnitFlyHeight(data.Victim, 250., 75.)
    call UnitRemoveAbility(data.Victim, 'A003')

endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
    set gg_trg_MentalEffects = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_MentalEffects, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(gg_trg_MentalEffects, Condition(function Conditions))
    call TriggerAddAction(gg_trg_MentalEffects, function Actions)
endfunction
endscope

In an emtpy trigger, you MUST have this (Timed Event System - Make sure you give credits to grim001):

JASS:
library TimedEvent initializer Init
globals
 //***   Configuration   ***
 private integer NumberOfTimers = 100
 //*** End Configuration ***
 private integer array TAGS
 private Callback array CALLBACKS
 private timer array TIMERS
 private integer N = 0
endglobals
function interface Callback takes integer tag returns boolean
private function H2I takes handle h returns integer
    return h
    return 0
endfunction
private function TimedEventExpires takes nothing returns nothing
 local timer t = GetExpiredTimer()
 local integer i = H2I(t)-0x100000
    if not CALLBACKS[i].evaluate(TAGS[i]) then
        call PauseTimer(t)
        set N = N + 1
        set TIMERS[N] = t
    endif
endfunction
function TimedEvent takes real delay, integer tag, Callback callback returns nothing
 local timer t
 local integer i
    if N == 0 then
        call BJDebugMsg("TimedEvent Error: Maximum numbers of timers ("+I2S(NumberOfTimers)+") exceeded")
        return
    else
        set t = TIMERS[N]
        set N = N-1
    endif
    set i = H2I(t)-0x100000
    set TAGS[i] = tag
    set CALLBACKS[i] = callback
    call TimerStart(t, delay, true, function TimedEventExpires)
endfunction
private function Init takes nothing returns nothing
 loop
     set TIMERS[N] = CreateTimer()
     exitwhen N == NumberOfTimers-1
     set N = N + 1
 endloop
endfunction
endlibrary

Enjoy!
 
Last edited:
Level 13
Joined
Nov 22, 2006
Messages
1,260
Arghhhh! What's the point of doing all the fancy scripts for him if he doesn't understand handlevars yet?? I would've done it by now if that would be smart. He will never learn that way.

Look, NoNZealot, you must've messed something up with the importing of handle vars, have you pasted the system in the custom script section like it said in the description? I don't think so, because then everything would be fine.

I suppose you don't know what the custom script section is (I didn't know that before also). It is the top thing in your trigger tree, the one that has the wc3 icon and the name of the map. When you create the map, it is that thing above the Map Initialization folder. Paste the system there and make the map initialization trigger like I told you.

Btw, I really suggest reading some tutorials, because not only you have trouble with understanding handlevars, you have trouble understanding Jass in general. There are some good tutorials here, on www.wc3campaigns.net and on www.thehelper.net
 
Level 10
Joined
Sep 6, 2007
Messages
440
Well I copied the system on the RIGHT place. Still don't know what's the problem with that. Here, I posted the map also. Can someone check it if there's a bug... or something
 

Attachments

  • Mind Tricks.w3x
    23.9 KB · Views: 47
Level 10
Joined
Jun 26, 2005
Messages
236
Instead of

JASS:
    local timer t = StartTimerBJ(t, true, 1.)

Do

JASS:
    local timer t
    call StartTimerBJ(t, true, 1.)

By the way, I would suggest using the code I posted. ;)
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
And you have to create the timer, right? Arg, I hate BJs, get rid of the habit of using them as soon as possible, trust me. When you stop using them, your code will look more like real JASS and not like GUI converted to JASS.

Yeah, use BoneBreaker's code, but after that I suggest you learn how to code on your own.
 
Level 10
Joined
Sep 6, 2007
Messages
440
Hmm how can I convert the TICKS of the timer. I'll make a loop which does the Mental Looping and will exit on 10. It's just what do I use to convert it to ticks. ( I know timed event uses that but I can't open my jass Newgen. )
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
JASS:
function callback takes nothing returns nothing
     local timer t = GetExpiredTimer()
     local integer i = GetHandleInt(t, "yourString")
     if i >= wantedNumber then
          call FlushHandles(t) // don't remember if this is the correct syntax
          call DestroyTimer(t)
     else
          call SetHandleInt(t, "yourString", i + 1)
     endif
endfunction
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Lets examine the StartTimerBJ function a bit:

JASS:
function StartTimerBJ takes timer t, boolean periodic, real timeout returns timer
    set bj_lastStartedTimer = t
    call TimerStart(t, timeout, periodic, null)
    return bj_lastStartedTimer
endfunction

StartTimerBJ is a BJ variable (duh), most of the BJ variables have the BJ suffix, but not all of them. BJs are functions calling other functions, which is slower than calling that second function in the first place. But there is one thing about StartTimerBJ which really sucks: it doesn't have the function parameter (as you can see, it gives to TimerStart null as the parameter, meaning no value, zero, nothing). Ignore that bj_lastStartedTimer thing, it is just a global variable which is used for getting the last started timer (GUI - Last started timer), but in JASS we don't need such things because....... well we just don't.

We're going to use TimerStart function instead of StartTimerBJ. Now, what does that function parameter do? Well, when the timer expires, it fires that function (and if the timer is periodical, it fires the function each time it expires). Example:

JASS:
function Execute takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleInt(t, "i") + 1
    if i == 5 then
        call SetHandleInt(t, "i", 0)
        call DestroyTimer(t)
    else
        call SetHandleInt(t, "i", i + 1)
    endif
    set t = null
endfunction

function Func takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, 1, true, function Execute)
    set t = null
endfunction

Explanation: lets take a look at the Func function, first you initialize the variable, meaning you give it a value. CreateTimer() function creates a timer and it returns it, so you can use that value and store it in the variable. Then we call the TimerStart function, setting the timeout to 1 second, making the boolean true means that it's periodical (if it's false, then it's a one-shot timer) and setting the last parameter to the Execute function. Observe the code type, it doesn't leak. You have to understand how it works:

JASS:
function SomeFunc takes nothing returns nothing
endfunction

function Func takes nothing returns nothing
    local code c = function SomeFunc
endfunction

It's really easy to understand, you just add the function bit to the function name and you have your code. Note that you can do that only for functions that are above the calling function.

Now lets get back to the TimerStart. This means that after each second it (the timer) will fire the Execute function (and then after each second expired he will start the countdown again to 1 second because it's periodical).

Lets look at the Execute function now. When a timer fires a function, you can get that timer in that function via GetExpiredTimer(). That function will return the timer that fired the function. This way you can get the variables attached to it, because you got the timer from another function, allowing you to get the variables attached to it also.

You're probably looking at that GetHandleInt function, let me explain: first time the GetHandleInt will try to get the integer that is attached to the timer (GetExpiredTimer()), but there is no integer attached to the timer first time the function is called, that's why GetHandleInt will return 0, but we increase it by one. This integer tells us how many times the timers has fired, the first time the integer will obviously be 1 (0 + 1 = 1).

The third line is an if, checking if the integer is 5 (five is just an example, it could be any integer). When the integer becomes 5, it does that SetHandleInt thing, which is easy to understand. Handle functions have the "ability" to flush the variable slot if the given value is 0, 0.00, "", false or null (depending on the variable type - integer, real, string, boolean or a handle). You always have to clear all the attached variables from the timer (it's a timer in this case, but it could be anything else) before destroying it, otherwise it leaks (I think), anyways, it's bad. Always remember: before you destroy a handle you attach stuff to, you have to clear all the attached variables. If there are multiple attached variables, you can use just call FlushHandleLocals(t) which gets rid of all the attached variables. I used SetHandleInt with 0 as the parameter because that way it is more efficient than using FlushHandleLocals just because of one variable. Now, after cleaning the attached integer, we can safely destroy the timer (with DestroyTimer function)

Lets get back to our if: you can see that else line, right? There is a function there, that SetHandleInt variable. That function under else will fire if the integer is not 5. It will attach the variable to the timer, but increasing it by one (which has the effect of counting the number of timer execution). Also, one thing about handles, if you attach a variable to a handle, but there is another variable already attached on the same name, it will overwrite the previous variable. So in this case, it will overwrite the previous integer attached to that timer, but we don't need it so we don't care.

Finally, the last line is just nulling the timer (to prevent leaks).

Ok, I think I explained pretty much everything, so some things should be clearer now. I hope I helped.

EDIT: I tried to highlight all the function/variable names so it's easier to read. Also, I corrected a false statement.
 
Level 10
Joined
Sep 6, 2007
Messages
440
JASS:
//===========================================================================
// Trigger: Mental Effects
//===========================================================================
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

function Mental_Looping takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit Caster = GetHandleUnit(t, "Caster")
    local unit Victim = GetHandleUnit(t, "Victim")
    call UnitDamageTargetBJ( Caster, Victim, ( I2R(GetUnitAbilityLevelSwapped('A001', Caster)) * 12.00 ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND )
    call AddSpecialEffectTargetUnitBJ( "origin", Victim, "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" )
    call DestroyEffect(GetLastCreatedEffectBJ())
    call PauseUnit (Victim, true)
endfunction

function Apply takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleInt(t, "i") + 1
    if i == 10 then
        call SetHandleInt(t, "i", 0)
        call DestroyTimer(t)
    else
        call SetHandleInt(t, "i", i + 1)
        call Mental_Looping()
    endif
    set t = null
endfunction    

function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    local timer t = CreateTimer()
    call UnitAddAbilityBJ( 'A003', Victim )
    call UnitRemoveAbilityBJ( 'A003', Victim )
    call SetUnitFlyHeightBJ( Victim, 250.00, 75.00 )  
    call SetHandleHandle(t, "Caster", Caster)
    call SetHandleHandle(t, "Victim", Victim)
    call TimerStart(t, 1, true, function Apply)
    set t = null
endfunction
So now, it looks like this. BTW Silvenon, thanks for your great help. You'll have some very big rep in " not so far future ". I can really understand what you say. It's great. But now I only need to stop the function <Mental_Looping> as it continues to execute even if the timer is expired.(I set the i to 10 because I want it to expire at it.)

---AND---
JASS:
//===========================================================================
// Trigger: Mental Cancelled
//===========================================================================
function Trig_Mental_Cancelled_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

function Trig_Mental_Cancelled_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetHandleUnit(Caster, "Victim")  
    local location APos1 = GetUnitLoc(Victim)
    call SetUnitFlyHeightBJ( Victim, 0.00, 400.00 )
    call AddSpecialEffectLocBJ( APos1, "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl" )
    call AddSpecialEffectLocBJ( APos1, "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl" )
    call CreateNUnitsAtLoc( 1, 'h000', GetOwningPlayer(Caster), APos1, bj_UNIT_FACING )
    call SetUnitAbilityLevelSwapped( 'A002', GetLastCreatedUnit(), GetUnitAbilityLevelSwapped('A001', Caster) )
    call UnitApplyTimedLifeBJ( 1.00, 'BTLF', GetLastCreatedUnit() )
    call IssueTargetOrderBJ( GetLastCreatedUnit(), "thunderbolt", Victim )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )
    call PauseUnitBJ( false, Victim )
    call DisableTrigger( GetTriggeringTrigger() )
endfunction

//===========================================================================
function InitTrig_Mental_Cancelled takes nothing returns nothing
    set gg_trg_Mental_Cancelled = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Cancelled, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddCondition( gg_trg_Mental_Cancelled, Condition( function Trig_Mental_Cancelled_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Cancelled, function Trig_Mental_Cancelled_Actions )
endfunction
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
While we're at this, I can show you how to improve your code. This sucks:

JASS:
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A001' ) ) then
        return false
    endif
    return true
endfunction

It is just a condition converted from GUI to JASS, it takes a lot of space and it's slower. Take a look at the stupidity of boolean checks - he checks if the spell is not the wanted one, and if that is true it returns false, so the trigger won't execute, because the spell isn't Mental Effects. But if the return false line doesn't execute, meaning the spell is not non-Mental Effects (that means that it is, example: -(-x) = x), then the return true line will execute and the trigger will fire.

So why don't we just put this??

JASS:
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

This is a very simple and more efficient version. It checks if the spell ability is the right one, that's all, no unnecessary stuff.

Btw, in the case you don't know:

Code:
==  equal to
!=  not equal to
>   greater than
>=  greater than or equal to
<   less than
<=  less than or equal to

I want to teach you to use constant functions, at least for spell IDs (those things between '', like 'A003' and 'A001'). A constant function is almost the same as the normal one, but it cannot call non-constant functions and it is slightly faster. They are often used for values that repeat through the code multiple times, so when you want to change that value, instead of changing each part of the code that contains that value, you can simply change the constant. It is useful to have a constant for spell IDs, because (for example) when you import a spell to another map, it will most probably change its ID, so you will have to change every ID in your code. Constants don't have to be used just in case a value repeat itself, they are often used when you don't feel like searching that value in your code. This is what you're going to do, create these on top of your script (constants should always be created on top, that way it's easier to find them and call them from other functions):

JASS:
constant function Mental_SpellID takes nothing returns integer
    return 'A001'
endfunction

constant function Target_SpellID takes nothing returns integer
    return 'A003'
endfunction

Now lets replace the parts in the code with the corresponding constant:

JASS:
call UnitAddAbilityBJ( 'A003', Victim )
call UnitRemoveAbilityBJ( 'A003', Victim )

becomes:

JASS:
call UnitAddAbilityBJ( Target_SpellID(), Victim )
call UnitRemoveAbilityBJ( Target_SpellID(), Victim )

And:

GetUnitAbilityLevelSwapped('A001', Caster)


becomes:

GetUnitAbilityLevelSwapped(Mental_SpellID(), Caster)


I hope that's clear, you just call a function which returns the spell ID, it is practically the same.

Now, next up are BJs. Some BJs have the BJ (or Swapped) suffix, but some don't, some of them are disguised as normal functions. BJs are functions that call other functions and sometimes they don't take all the parameters they can, that's why they are inflexible. A function that doesn't call other function is a native, that's what people should mostly use. There are useful BJs that do a lot of work for you, but most of them are pretty retarded (and slow).

Random advice: get JassCraft, it is the best jass editor I know, it is very useful for cleaning up BJs and many other stuff (for example, viewing parameters, which is quite useful in coding).

Lets look at the AddUnitAbilityBJ for example:

JASS:
function UnitAddAbilityBJ takes integer abilityId, unit whichUnit returns boolean
    return UnitAddAbility(whichUnit, abilityId)
endfunction

What does it do? It calls another function which is practically the same, but with swapped parameters, so instead of calling UnitAddAbilityBJ, you can just directly call UnitAddAbility (just with different parameter order). So let's turn these:

JASS:
call UnitAddAbilityBJ( Target_SpellID(), Victim )
call UnitRemoveAbilityBJ( Target_SpellID(), Victim )

into this:

JASS:
call UnitAddAbility(Victim, Target_SpellID())
call UnitRemoveAbility(Victim, Target_SpellID())

Btw, I'm also irritated by those unnecessary GUI spacings, they just take space and look ugly to me so I clean them. You can leave them if you want. Also, this:

GetUnitAbilityLevelSwapped(Mental_SpellID(), Caster)


into this:

GetUnitAbilityLevel(Caster, Mental_SpellID())


The Swapped suffix practically tells you that the function is a BJ and that it calls a native with swapped parameters. Swapped are mostly (or always) the functions with two parameters.

There are also natives that do exactly the same as some other natives, but are slower and inflexible. For example GetSpellAbilityUnit() is same as GetTriggerUnit(), but GetTriggerUnit() is faster and it can return any triggering unit when GetSpellAbilityUnit() can return only the casting one.

There are few more BJs in your code, for example that UnitDamageTargetBJ. This is an example of a BJ that doesn't take all the parameters:

JASS:
function UnitDamageTargetBJ takes unit whichUnit, unit target, real amount, attacktype whichAttack, damagetype whichDamage returns boolean
    return UnitDamageTarget(whichUnit, target, amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)
endfunction

Missing arguments (parameters and arguments are synonyms) are boolean attack, boolean ranged and weapontype weaponType. This is how the original native looks like:

native UnitDamageTarget takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean


I never knew what those two booleans and weapon type exactly is, but we can see what the BJ puts there as arguments, and we can follow the same:

call UnitDamageTargetBJ( Caster, Victim, ( I2R(GetUnitAbilityLevel(Caster, Mental_SpellID())) * 12.00 ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND )


Becomes:

call UnitDamageTarget(Caster, Victim, (I2R(GetUnitAbilityLevel(Caster, Mental_SpellID())) * 12.00), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND, WEAPON_TYPE_WHOKNOWS)


I have also found out that WEAPON_TYPE_WHOKNOWS is equal to null (no value), so you can just put null instead of that:

call UnitDamageTarget(Caster, Victim, (I2R(GetUnitAbilityLevel(Caster, Mental_SpellID())) * 12.00), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND, null)


The last one is:

JASS:
call AddSpecialEffectTargetUnitBJ( "origin", Victim, "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" )
call DestroyEffect(GetLastCreatedEffectBJ())

AddSpecialEffectTargetUnitBJ is same as AddSpecialEffectTarget, but with shuffled parameters, this would be the native version:

JASS:
call AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", Victim, "origin")
call DestroyEffect(GetLastCreatedEffectBJ())

But what about GetLastCreatedEffectBJ()? One advice: forget that function, forget any GetLastCreated functions, those things are for GUI and the suck. Let me explain what that function actually is:

JASS:
function GetLastCreatedEffectBJ takes nothing returns effect
    return bj_lastCreatedEffect
endfunction

It returns a global variable (of type effect), but what is that variable? Where is it set? Right here:

JASS:
function AddSpecialEffectTargetUnitBJ takes string attachPointName, widget targetWidget, string modelName returns effect
    set bj_lastCreatedEffect = AddSpecialEffectTarget(modelName, targetWidget, attachPointName)
    return bj_lastCreatedEffect
endfunction

Are things starting to get clearer? The BJ effect function just sets the global to that created effect, which can be later retrieved with GetLastCreatedEffectBJ. If you want to make it efficient, you can just use the bj_lastCreatedEffect variable directly, but if you want to make it even more efficient, you don't use it at all. You can make this:

call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", Victim, "origin"))


AddSpecialEffectTarget returns the effect and you can use that effect directly because it's unnecessary to store it in a variable first. It would be the same if you did this:

JASS:
local effect e = AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", Victim, "origin")
call DestroyEffect(e)
set e = null

But this way we have two extra lines and it is slightly less efficient. So we should stick to the first version (except if you want to use the same effect multiple times, then you would have to store it in a variable).


Question: why are you calling Mental_Loop function when you can just do the actions directly? I'm referring to this part:

JASS:
if i == 10 then
    call SetHandleInt(t, "i", 0)
    call DestroyTimer(t)
else
    call SetHandleInt(t, "i", i + 1)
    call Mental_Looping()
endif

You can just do this:

JASS:
function Apply takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleInt(t, "i") + 1
    local unit Caster
    local unit Victim
    if i == 10 then
        call SetHandleInt(t, "i", 0)
        call DestroyTimer(t)
    else
        call SetHandleInt(t, "i", i + 1)
        set Caster = GetHandleUnit(t, "Caster")
        set Victim = GetHandleUnit(t, "Victim")
        
        call UnitDamageTarget(Caster, Victim, (I2R(GetUnitAbilityLevel(Caster, Mental_SpellID())) * 12.00), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND, null)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", Victim, "origin"))
        call PauseUnit(Victim, true)
        
        set Caster = null
        set Victim = null
    endif
set t = null

One more reason why your way is wrong: it won't work, because you're calling GetExpiredTimer() function from Mental_Looping. That function won't work, well, it will, but it will return null because it isn't the timer that called Mental_Looping, it's the Apply function.

Please try to correct the Mental Canceled trigger using the things that I taught you. I wonder if you got it right.
 
Last edited:
Level 10
Joined
Sep 6, 2007
Messages
440
Well to your questions,
There are many GUI to JASS convert parts because I did convert... I thought it would be a good enhancement to make my GUI made spell MUI. And your explanations about BJ's are very logical. It keeps calling other functions which makes the game slower. Thanks for support, I'm going to post the fixed version soon.

"GUI spacings" what are they?...

Also I know the " Equation symbols " yeah thanks anyway.
JASS:
//===========================================================================
// Trigger: Mental Cancelled
//===========================================================================
function Trig_Mental_Cancelled_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

constant function Cancel_SpellID takes nothing returns integer
    return 'A001'
endfunction

function Trig_Mental_Cancelled_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()   
    local unit Victim = GetHandleUnit(Caster, "Victim") 
    local location APos1 = GetUnitLoc(Victim)
    call SetUnitFlyHeight( Victim, 0.00, 400.00 )
    call AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl", Victim, "origin")
    call AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl", Victim, "origin" )
    call CreateNUnitsAtLoc( 1, 'h000', GetOwningPlayer(Caster), APos1, bj_UNIT_FACING )
    call SetUnitAbilityLevel(Caster, Cancel_SpellID(), GetUnitAbilityLevel(Caster, Cancel_SpellID() ) )
    call UnitApplyTimedLife( GetLastCreatedUnit(), 'BTLF', 1.00)
    call IssueTargetOrder( GetLastCreatedUnit(), "thunderbolt", Victim )
    call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl", Victim, "origin" ) )
    call PauseUnit(Victim, false)
    call RemoveLocation(APos1)
endfunction

//===========================================================================
function InitTrig_Mental_Cancelled takes nothing returns nothing
    set gg_trg_Mental_Cancelled = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Cancelled, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddCondition( gg_trg_Mental_Cancelled, Condition( function Trig_Mental_Cancelled_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Cancelled, function Trig_Mental_Cancelled_Actions )
endfunction

Now this is the advanced one. But now, still got problems with declaring the Victim. I think I should use " Event Timer Expires "
 
Last edited:
Level 13
Joined
Nov 22, 2006
Messages
1,260
Remember, GetTriggeringUnit() goes instead of GetSpellAbilityUnit().

Also remember to null every handle variable, otherwise it leaks (variables you should null: Caster, Victim, APos1). Whoa...

local unit Victim = GetHandleUnit(Caster, "Victim")


No no no... why did you put Caster as the parameter? Victim isn't attached to Caster. I forgot to tell you how to do this, it's very simple: in the Trig_Mental_Effects_Actions function insert call SetHandleHandle(Caster, "t", t) after call SetHandleHandle(t, "Victim", Victim) (so before the TimerStart line).

Then you can retrieve the timer in the Trig_Mental_Cancelled_Actions function like this (do it after declaring the Caster variable):

local timer t = GetHandleTimer(Caster, "t")


Then get the Victim variable from that timer:

local unit Victim = GetHandleUnit(t, "Victim")


Easy as pie. Just don't forget to detach the variables from the timer in the Trig_Mental_Cancelled_Actions function, just use call FlushHandleLocals(t) before destroying it (because if you want to stop the looping, meaning you cancel your spell, you have to destroy the timer). Also remember to nullify the timer when you destroy it (don't null timers before destroying them, that's not right). Whoops, there is something wrong in the Apply function, sorry, just change this:

call SetHandleInt(t, "i", 0)


into this:

call FlushHandleLocals(t)


I forgot there are multiple variables attached to the timer, so you need to detach them all.


P.S. Lol, check my last rep :p
 
Level 10
Joined
Sep 6, 2007
Messages
440
LOL @ Earth-Fury's rep comment. I got it now. You attach caster to timer and then you get the attached variable to attach again to Victim. I got it... Thanks a lot.
Also whats the reason of using TriggeringUnit instead of SpellAbilityUnit?
 
Level 10
Joined
Sep 6, 2007
Messages
440
JASS:
//===========================================================================
// Trigger: Mental Cancelled
//===========================================================================
function Trig_Mental_Cancelled_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

constant function Cancel_SpellID takes nothing returns integer
    return 'A001'
endfunction

function Trig_Mental_Cancelled_Actions takes nothing returns nothing
    local unit Caster = GetTriggerUnit()
    local timer t = GetHandleTimer(Caster, "t")   
    local unit Victim = GetHandleUnit(t, "Victim") 
    local location APos1 = GetUnitLoc(Victim)
    call SetUnitFlyHeight( Victim, 0.00, 400.00 )
    call AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl", Victim, "origin")
    call CreateNUnitsAtLoc( 1, 'h000', GetOwningPlayer(Caster), APos1, bj_UNIT_FACING )
    call SetUnitAbilityLevel(Caster, Cancel_SpellID(), GetUnitAbilityLevel(Caster, Cancel_SpellID() ) )
    call UnitApplyTimedLife( GetLastCreatedUnit(), 'BTLF', 1.00)
    call IssueTargetOrder( GetLastCreatedUnit(), "thunderbolt", Victim )
    call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl", Victim, "origin" ) )
    call PauseUnit(Victim, false)
    call RemoveLocation(APos1)
    set Caster = null
    set Victim = null
endfunction

//===========================================================================
function InitTrig_Mental_Cancelled takes nothing returns nothing
    set gg_trg_Mental_Cancelled = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Cancelled, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddCondition( gg_trg_Mental_Cancelled, Condition( function Trig_Mental_Cancelled_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Cancelled, function Trig_Mental_Cancelled_Actions )
endfunction

*******
JASS:
//===========================================================================
// Trigger: Mental Effects
//===========================================================================
function Trig_Mental_Effects_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

constant function Mental_SpellID takes nothing returns integer
    return 'A001'
endfunction

constant function Target_SpellID takes nothing returns integer
    return 'A003'
endfunction

function Apply takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleInt(t, "i") + 1
    local unit Caster
    local unit Victim
    if i == 10 then
        call FlushHandleLocals(t)
        call DestroyTimer(t)
    else
        call SetHandleInt(t, "i", i + 1)
        call SetHandleInt(t, "i", i + 1)
        set Caster = GetHandleUnit(t, "Caster")
        set Victim = GetHandleUnit(t, "Victim")
        call UnitDamageTarget(Caster, Victim, (I2R(GetUnitAbilityLevel(Caster, Mental_SpellID())) * 12.00), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MIND, null)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl", Victim, "origin"))
        call PauseUnit (Victim, true)
        set Caster = null
        set Victim = null
    endif
set t = null
endfunction    

function Trig_Mental_Effects_Actions takes nothing returns nothing
    local unit Caster = GetSpellAbilityUnit()
    local unit Victim = GetSpellTargetUnit()
    local timer t = CreateTimer()
    call UnitAddAbility( Victim, Target_SpellID() )
    call UnitRemoveAbility( Victim, Target_SpellID() )
    call SetUnitFlyHeight( Victim, 250.00, 75.00 )  
    call SetHandleHandle(t, "Caster", Caster)
    call SetHandleHandle(t, "Victim", Victim)
    call SetHandleHandle(Caster, "t", t)
    call TimerStart(t, 1, true, function Apply)
    set t = null
endfunction

//===========================================================================
function InitTrig_Mental_Effects takes nothing returns nothing
    set gg_trg_Mental_Effects = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Mental_Effects, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Mental_Effects, Condition( function Trig_Mental_Effects_Conditions ) )
    call TriggerAddAction( gg_trg_Mental_Effects, function Trig_Mental_Effects_Actions )
endfunction

Now I only have a bug left. The APPLY function still keeps occuring. I mean even if I stop casting, it still does damage. How can I disable it from occuring?
 
Level 12
Joined
Apr 27, 2008
Messages
1,228
call TimerStart(t, 1, true, function Apply)
This will call the function apply every second.
Unless that is what you wish you should change the true to false
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
I told you you need to destroy the timer in the Trig_Mental_Cancelled_Actions function. Also you didn't detach the timer from Caster. This is how it should be (I just put spacings so you see what I inserted):

JASS:
function Trig_Mental_Cancelled_Actions takes nothing returns nothing
    local unit Caster = GetTriggerUnit()
    local timer t = GetHandleTimer(Caster, "t")
    local unit Victim = GetHandleUnit(t, "Victim")
    local location APos1 = GetUnitLoc(Victim)
    call SetUnitFlyHeight( Victim, 0.00, 400.00 )
    call AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl", Victim, "origin")
call CreateNUnitsAtLoc( 1, 'h000', GetOwningPlayer(Caster), APos1, bj_UNIT_FACING )
    call SetUnitAbilityLevel(Caster, Cancel_SpellID(), GetUnitAbilityLevel(Caster, Cancel_SpellID() ) )
    call UnitApplyTimedLife( GetLastCreatedUnit(), 'BTLF', 1.00)
    call IssueTargetOrder( GetLastCreatedUnit(), "thunderbolt", Victim )
    call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl", Victim, "origin" ) )
    call PauseUnit(Victim, false)
    call RemoveLocation(APos1)
    
    call FlushHandleLocals(t)
    call SetHandleHandle(Caster, "t", null)
    call DestroyTimer(t)
    
    set Caster = null
    set Victim = null

    set t = null
endfunction

You didn't null the timer also (I added it in the last line).

Uhm, why does Apply function contain call SetHandleInt(t, "i", i + 1) twice? Remove one of them.

"GUI spacings" what are they?...

Sorry, didn't see this one. GUI spacings are unnecessary spacings after parenthesis. Example of GUI converted to JASS function:

call TimerStart( t, 1, true, function Apply )


A typed JASS function:

call TimerStart(t, 1, true, function Apply)


Some people like adding that spacing, but it irritates me.

Also, yeah, I know you mostly convert from GUI to JASS, that's ok, every beginner does that. I hope that you will soon learn to type your own functions, it's a lot easier and faster, believe me. And you learn how to type fast, which is very useful :)
 
Status
Not open for further replies.
Top