• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Natives and Local variables ...

Status
Not open for further replies.
Level 22
Joined
Sep 24, 2005
Messages
4,821
Yes, because your giving the unit a new order, effectively replacing the current one, which is synonymous to interrupting the current order.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
I was wondering why...

This works...using global variable

JASS:
function dada takes nothing returns nothing
    set udg_count = udg_count + 1
    if udg_count == 5 then
        call BJDebugMsg("done")
    else
        call DisplayTextToForce(GetPlayersAll(), ( "Total is " + I2S(udg_count) ) )
    endif
endfunction
//===========================================================================
function InitTrig_counttest1 takes nothing returns nothing
    local trigger y = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( y, 0.50 )
    call TriggerAddAction(y,function dada)
    set y = null
endfunction

But this doesnt...local variable

JASS:
function dada takes nothing returns nothing
    local integer count
    set count = count + 1
    if count == 5 then
        call BJDebugMsg("done")
    else
        call DisplayTextToForce(GetPlayersAll(), ( "Total is " + I2S(count) ) )
    endif
endfunction
//===========================================================================
function InitTrig_counttest2 takes nothing returns nothing
    local trigger y = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( y, 0.50 )
    call TriggerAddAction(y,function dada)
    set y = null
endfunction

EDIT:

BTW how to stop the trigger?, by destroying it or turn it off?...
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Ok but isnt it that the count is 'in' the function?...

also, this is working...

Or am I just wrong coz this does only runs ONE TIME?...

JASS:
function WodSpell takes nothing returns boolean
    local unit u
    local unit o
    local integer spelllevel
    local integer spelldam
    if GetSpellAbilityId()=='A002' then
        set u = GetTriggerUnit()
        set o = CreateUnit (GetOwningPlayer(u), 'h000', GetUnitX(u), GetUnitY(u), 0)
        set spelllevel = GetUnitAbilityLevel(u, 'A002')
        set spelldam = spelllevel * 5
        call IssuePointOrder(o, "move", GetSpellTargetX(), GetSpellTargetY())
        set u = null
        set o = null
        call DisplayTextToForce(GetPlayersAll(), ( "Damage is " + I2S(spelldam) ) )
    endif
    return false
endfunction
//===========================================================================
function InitTrig_Wind_of_Decay takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ      (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition                (t, Condition(function WodSpell))
    set t = null
endfunction
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
1. Do I really need JNGP to play maps with vJASS in it?...
2. I have a NON vJASS map or map that has no vJASS spell/trigger...so if I use JNGP
to edit this map or add a vJASS trigger, it is OK?...
3. When creating a trigger what is BEST to use, local trigger OR gg_trg???...
4. Where do I put the line thing >>> 'scope' and 'endscope'?...
5. Can you save local variables to a hashtable and load latter?...
 
@1: you just need JNGP to edit and save the map...
@2: yup
@3: well, if you dont need to reference it again, local, or if you will be using gg_trg, since you're using vJASS, you might just want to have a global trigger variable
@4: normally, scope at the top of the code, endscope at the end

JASS:
scope A

/*
 put the codes here
*/

endscope
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
1. No.
2. Yes.
3. Depends on use. Most of the time you'll only use the local one.
5. Yes.

Think of the hashtable as a global variable, the thing that makes the hashtable flexible is that it can index data using a large integer as it's indexer, while jass arrays can't index data with an index higher than 8191.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
thanks...but how about the 5th one?...
5. Can you save local variables to a hashtable and load latter?...

3 more things...

1. why is this used as a parent key in HT?...

integer TIMER = StringHash("ho")

I mean can we use 'besides' integer or besides StringHash OR you can use
a simple integer number?...well I know childkey requires integer...

2. "Parent Key" in JASS/vJASS and "Mission key" in GUI?...

3. Private functions, I had a syntax error, why?...

Chobibo made the spell, I cant get it to work so I modified some...and
it's working now...

For the sake of argument, Im posting the spell here...CREDITS TO CHOBIBO...


JASS:
globals
    //===============================================
    //=     CONFIGURATION GLOBALS                   =
    //===============================================
    integer    SPELL_ID    = 'A001'
    real       SPELL_DPS   = 20.00/4
    real       SPELL_DUR   = 06.00
    real       UPDATE_RATE = 01.00/4
    string     SPECIAL_FX  = "Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeEmbers.mdl"
    string     ATTACH_POINT= "chest"
    //===============================================
 
    //===============================================
    //      SYSTEM ONLY GLOBALS - DO NOT EDIT       =
    //===============================================
    integer    TIMER       = 1 // <<< I just changed this, is it  OK?
    integer    CASTER      = 2
    integer    TARGET      = 3
    integer    DURATION    = 4
    hashtable  HASHTABLE   = InitHashtable()
    //===============================================
 
endglobals
//============================================================
//=     SPELL MAIN                                           =
//=     When creating DoT spells, you must consider the      =
//=     following:                                           =
//=     -caster                                              =
//=     -target                                              =
//=     -duration                                            =
//=     A good DoT spell must be able to distinguish if      =
//=     the targetted unit already has the DoT debuff, in    =
//=     order to be able to stack or not stack its effects.  =
//============================================================
function onCastCondition takes nothing returns boolean
    return (GetSpellAbilityId()==SPELL_ID)
endfunction
function onUpdate takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local integer childkey=GetHandleId(t)
    local unit caster=LoadUnitHandle(HASHTABLE,CASTER,childkey)
    local unit target=LoadUnitHandle(HASHTABLE,TARGET,childkey)
    local real duration=LoadReal(HASHTABLE,DURATION,childkey)-UPDATE_RATE
 
    // We need to check if the debuff has reached it's
    // intended lifetime, if so we remove the data we
    // saved in the hashtable. We also do the same if
    // the target is already dead.
 
    if (duration<0) or IsUnitType(target,UNIT_TYPE_DEAD) then
        // There is no longer any time remaining for the
        // debuff, so we clear the hashtable.
        debug call BJDebugMsg("Destroy debuff timer")
        call RemoveSavedHandle(HASHTABLE,CASTER,childkey)
        call RemoveSavedHandle(HASHTABLE,TARGET,childkey)
        call RemoveSavedReal(HASHTABLE,DURATION,childkey)
        call RemoveSavedHandle(HASHTABLE,TIMER,GetHandleId(target))
        call DestroyTimer(t)
    else
        // There's still timer left for the debuff, so we
        // apply the debuff effect.
        call SaveReal(HASHTABLE,DURATION,childkey,duration)
        call DestroyEffect(AddSpecialEffectTarget(SPECIAL_FX,target,ATTACH_POINT))
        call UnitDamageTarget(caster,target,SPELL_DPS,false,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_MAGIC,null)
    endif
 
    set t=null
    set caster=null
    set target=null
endfunction
function onCast takes nothing returns nothing
    local unit caster=GetTriggerUnit()
    local unit target=GetSpellTargetUnit()
    local integer childkey=GetHandleId(target)
    local timer t
 
    // Check if the unit already has a debuff timer, in
    // this spell's case, I wan't the duration to reset
    // to the maximum duration if the spell is recast
    // on an already debuffed unit.
 
    if (HaveSavedHandle(HASHTABLE,TIMER,childkey)) then
        // The target has a debuff already, we know this
        // because there is already an existing debuff
        // timer for the said unit.
        debug call BJDebugMsg("Target already has buff!")
        set t=LoadTimerHandle(HASHTABLE,TIMER,childkey)
        call SaveReal(HASHTABLE,DURATION,childkey,SPELL_DUR)
    else
        // The target has no debuff yet because there is
        // no beduff timer for it. We create a debuff timer
        // and save to the hashtable the necessary data.
        // These are the caster, the target, the duration,
        // and the debuff timer. You'll notice that the debuff
        // timer is attached to the target unit, this is
        // because we want to associate the unit with the
        // debuff timer, so that when we target an already
        // affected unit, we can reset the debuff duration.
        debug call BJDebugMsg("Target has no buff yet!")
        set t=CreateTimer()
        call SaveTimerHandle(HASHTABLE,TIMER,childkey,t)
        set childkey=GetHandleId(t)
        call SaveReal(HASHTABLE,DURATION,childkey,SPELL_DUR)
        call SaveUnitHandle(HASHTABLE,CASTER,childkey,caster)
        call SaveUnitHandle(HASHTABLE,TARGET,childkey,target)
        call TimerStart(t,UPDATE_RATE,true,function onUpdate)
endif
 
    set t=null
    set caster=null
    set target=null
endfunction
function InitTrig_Damage_Over_Time takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ  (t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition            (t,Condition(function onCastCondition))
    call TriggerAddAction               (t,function onCast)
    set t = null
endfunction


EDIT:

I'm really sorry for my ignorance, but I believe that when somebody reads
this thread, they will slowly be interested in JASS/vJASS...thanks to all who
took part on this...
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
1. Because StringHash returns a unique integer for a unique string, you could use any integer there as long as your not going to use that same number again for another purpose with the same childkey as the contents would be overwritten. You should read more about hashing if your interested to learn more about the said function.

2. Blizzard likes to confuse people.

3. jass is case-sensitive, use private instead of Private.

Note: I know my coding style sucks :D
 
Scopes and libraries are used to structure your code and to prevent it from interfering with other functions that you might have in your map. Functions and variables inside a scope or library can be declared private, which means that their names will become randomized, and all references to it inside the library will be changed to this new random name, while references outside of it won't. This means that code outside of the library won't know the names of your private members inside.

This is good when you have a lot of different systems in your map, and possibly with different creators (since you might be using libraries written by someone else). Let's say you want to have this system you just posted in your map, but also a spell made by someone else, which also has the function "onCast". You might not always want to go in and change the name of the function everywhere it is referenced, and even if you would, it could become very confusing to keep a track of all your variable or funciton names. To solve this, you can put each of those systems inside a library, which is basically saying "this function belongs to this system, and that function belongs to that system".

It would basically look like this:

JASS:
library MySpell

    private function onCast takes nothing returns nothing
        //blah blah blah
    endfunction

    private function TestCast takes nothing returns nothing
        call onCast() //this is legit
    endfunction

endlibrary

function TestCast takes nothing returns nothing  //this funciton can have the same name
    call onCast() //this is not legit, because onCast is private to mySpell.
endfunction

This way, the function onCast can ONLY be referenced by other functions inside the same library, and you are free to use the same name for functions in other scripts.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
TestCast can't have the same name, it's a public function; the other one inside the library block is.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
@Chobibo

No it's not, I've learned a lot from your spell...
But your spell did use 'private' but still I got an error, but it's OK now...maybe it needs
Library as Fingolfin said?...

@Fingolfin

1. So is library and scope the same?...
2. Can 'globals and endglobals' be IN a library/scopes?...
3. The function onCast can be used as a 'DIFFERENT' onCast function 'OUTSIDE' a library?...
4. Can you declare private without library/scope?...
5. Where did you get the 'MySpell', is it a name or an event?...
6. Can global declarations be used outside a library/scope?...

THANKS +rep to all
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
ZINC isn't harder to read if you're used to C as it got a C-liker syntax. Almost more common to normal scripting code languages.

1. No it isnt. Libraries are compiled first and can have "requires/uses/needs" + another library's name. This will 'cause the required library to be compiled before the other library thus the second library gaining access to the first one.

Scopes are compiled after libraries and cannot have this "needs" as you cannot require a scope. A scope is just a code block, usually for normal map code and spells, not systems which are prefered to put into a library (because of the compile order).

JASS:
library something requires anotherlibrarywhichhasthisname, thisisanotherlibraryseperatedwithacoma

scope something requires willgiveyouasyntaxerror

2. Can be declared anywhere while not inside a struct, function, method but can be inside scope and library.

3. I'm not sure about this one. As you usually make something private because it shouldnt interfere with other functions in other libraries. Make it public if you want to use it else where.

4. I think you can, not sure though.

5. The name of the library or scope is totally up to you. HAve to be unique and not be the same as any other scope or it will throw up a syntax error.

6. Same as 4.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
It needs to be inside a scope, since it's a spell.

1. No. Libraries has the highest priority when initializing script, scopes are initialized according to the order they are arranged on the trigger editor. This means that all libraries get initialized first before scopes, library initialization order is also different, a library required by another library takes higher priority.
2. Yes.
3. If onCast is private and inside a scope or a library, having a duplicate onCast function will be valid.
4. No. The point of having libraries and scopes is that you can encapsulate (hide) your code inside a block from the entire code.
5. User-defined library identifier. Just type what you want as a name, as long as the characters you're using are valid (underscores aren't valid).
6. Yes, as long as you're using NewGen to save the said map, or you're using the command line parser of jasshelper.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
@Baassee, Chobibo

But libraries and scopes have similarities?...meaning >>> blocking...

When I pressed Syntax check, it has errors but when I saved the map and
cast the spell, it's OK, it it always the case?...

EDIT:

this...

3. If onCast is private and inside a scope or a library, having a duplicate onCast function will be valid.

what if I use private function onCast in a library/scope and use another private function onCast in another library/scope...will it still be valid?...

hehe...sorry guys if I have too many questions :)...
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
The only similarities they have is the way they are declared and the ability to hide the script within itself from the whole script.

Don't use the syntax check button, it uses pjass to parse the script, which doesn't recognize vjass. You need to save the map so that jasshelper (vjass parser) can check the script. vjass errors will pop up if there are any, intercepting the save command.

Yes, that's why it's called private, so that other libraries or scopes can't call it. If they can't call it, then it can contain the same function.
 
Last edited:
@baassee - well if you have background on C, yeah, but for someone like me who has no background in any programming language (other than JASS/vJASS/ZINC and well a bit of html, a long time ago...), its definitely harder to read than vJASS...

@mckill - well, if someone could... but i guess it needs permission from Vexorian first... ^_^
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
pjass (Syntax Check) is functional for normal jass. I still use it for non-vjass scripts.
Or we aren't * enough.
That was too harsh dude, he's just starting to learn.
 
Last edited by a moderator:
Level 20
Joined
Jul 6, 2009
Messages
1,885
Hmm..since you're discussing vJASS may i ask a question?

What's the difference between public function and just function?
If a function is in a library,you can call it from any trigger so i don't see the difference...

I assume though,that the public functions receive a different name (or a prefix) upon compiling so it doesn't interfere with other functions,but i'm not sure...

Also what's the difference when using libraries and scopes?
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Yes, it's the same pjass that the syntax check button uses to parse script. Also, Vexorian added an alternative script parser, clijasshelper.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Hmm..since you're discussing vJASS may i ask a question?

What's the difference between public function and just function?
If a function is in a library,you can call it from any trigger so i don't see the difference...

I assume though,that the public functions receive a different name (or a prefix) upon compiling so it doesn't interfere with other functions,but i'm not sure...

Also what's the difference when using libraries and scopes?

a public function are prefixed NAME_functionname

normal functions can bug with the names that's why you usually go with public functions (although I don't)

libraries are usually for greater things such as systems, scopes are for the rest but you can make spells in a library though doesnt matter, it's just the order they compile that matters
 
As i might have said earlier, if you are unsure about anything regarding vJass, it is always best to read the JassHelper Manual first. It can be found in the JassHelper folder inside the JNGP directory, though i've attached it to this post incase you couldn't find it. It includes all basic information about how the features work and how you should use them.
 

Attachments

  • jasshelpermanual.rar
    47.3 KB · Views: 46
Level 15
Joined
Oct 16, 2010
Messages
941
1. In GUI when you save using hashtable, you set the ID and then add to a unit group...then set the ID again inside that unit group, then load...so how do you do it in JASS/vJASS?...

I don't understand, can you give an example?

2. When using HT, is it not necessary to use "TriggerRegisterTimerEventPeriodic"?...

HT? I'm assuming you meant a periodic timer. You can just use TimerStart(timer WhichTimer, real Duration, boolean Periodic, function Whichfunction). Which will call the specified function whenever the timer procs.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
I don't understand, can you give an example?

like this...


  • Timer
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Flame Strike
    • Actions
      • Hashtable - Save 13.00 as 1 of (Key (Triggering unit)) in Hash
      • Unit Group - Add (Triggering unit) to UGrp
      • Trigger - Turn on Timer Loop <gen>
  • Timer Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in UGrp and do (Actions)
        • Loop - Actions
          • Set Timer = (Load 1 of (Key (Picked unit)) from Hash)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Load 1 of (Key (Picked unit)) from Hash) Greater than 0.00
            • Then - Actions
              • Hashtable - Save (Timer - 1.00) as 1 of (Key (Picked unit)) in Hash
              • Set Timer = (Load 1 of (Key (Picked unit)) from Hash)
              • Floating Text - Create floating text that reads (String(Timer)) above (Picked unit) with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Set the velocity of (Last created floating text) to 100.00 towards 90.00 degrees
              • Floating Text - Change the lifespan of (Last created floating text) to 2.00 seconds
            • Else - Actions
              • Unit Group - Remove (Picked unit) from UGrp
              • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in Hash
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (UGrp is empty) Equal to True
                • Then - Actions
                  • Game - Display to (All players) the text: TRIG OFF
                  • Trigger - Turn off (This trigger)
                • Else - Actions

Pls dont make the trigger in JASS, I just want to know how you save/load, what do I use as 'ID' (triggering unit, picked or just integer?)...

HT? I'm assuming you meant a periodic timer. You can just use TimerStart(timer WhichTimer, real Duration, boolean Periodic, function Whichfunction). Which will call the specified function whenever the timer procs.

yeah that's what I meant, thanks!...
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
1. Depends on what your trying to store. If you want to attach data to a timer, then you would use the timer's handle id, if you want to attach data to a unit the the unit's handle id.
2. Yes
 
If you want to store several types of information to a single timer, you might be better off making a struct and store the struct handle to the timer.

Example:

JASS:
struct myStruct
    real x
    real y
    unit u
endstruct

//and then make theese functions:

function myCallback takes nothing returns nothing
local myStruct S = LoadInteger(myHash, 0, GetHandleId(GetExpiredTimer()))
//now you can retain your variables through the struct, i.e. "S.u" to get the stored unit!

//Remember to type the following lines once you're done witn the struct, or the struct will not be recycled.
call S.destroy()
call RemoveSavedInteger(myHash, 0, GetHandleId(GetExpiredTimer())) 
endfunction

function AttachStructToTimer takes unit u, real x, real y returns nothing
local timer t = CreateTimer() 
local myStruct S = myStruct.create()
set S.u = u
set S.x = x
set S.y = y
call SaveInteger(MyHash, 0, GetHandleId(t), S) //This attaches our struct "S" to the timer. 
call TimerStart(t, false, false, function myCallback)
endfunction

I am just writing out of my head now so this might not compile, but i hope you get the point. Structs are excellent for storing bundles of information like this.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Usually you don't use hashtables for making spells in vJASS, we got structs.

In plain JASS it's better to use a timer's handle id to attach your data instead of the caster's id thus it will always be a unique id every single time which will never clash with anyother spell instance (as they won't use that timer). That's the easiest way in JASS what I know of. If you remember the pounce spell I made, it uses this technique pretty well.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Usually you don't use hashtables for making spells in vJASS, we got structs.

In plain JASS it's better to use a timer's handle id to attach your data instead of the caster's id thus it will always be a unique id every single time which will never clash with anyother spell instance (as they won't use that timer). That's the easiest way in JASS what I know of. If you remember the pounce spell I made, it uses this technique pretty well.

...which makes a spell SUMI.
If you had attached data to caster's id,it would be MUI but not SUMI. Just saying the term SUMI isn't useless.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Now this is the answer I'm looking forward to know...It enlightens me up
because I've seen scripts with timer as ID, so Im confirming if that's the best way...

In plain JASS it's better to use a timer's handle id to attach your data instead of the caster's id...

@Fingolfin

From the looks of it, it's much easier than hashtables...lol!
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
I hope you don't mind if i ask few questions too,mckill :eek:

What's the ExecuteFunc() native for? Why not just use call [function name]? According to Jasshelper manual,the ExecuteFunc() is used to initialize libraries; can it be done by calling the initializer too?

And another thing is,are functions from scope added to map header like functions from libraries so they can be used elsewhere?
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,197
ExecuteFunc() takes a string representing the name of the function to execute. This means you can use it to run variable code (pass it a variable of type string).

Calls on the otherhand compile to call instructions, and as such can not take a variable as a target as they reference a specific point in the code.

ExecuteFunc() has other useful properties.
1. I believe it starts a new thread (new stack) and thus can avoid oplimit.
2. Can run functions below the call to this function (unlike real calls which are limited to above only).
3. Can be used to provide variable function flow control.


Scopes in Vjass are placed in the script in the order you place them in the trigger editor, thus libaries are for global functions and scopes for map specific functions. A spell should be in a scope but all its used (recyclable) components in a library.
 
Status
Not open for further replies.
Top