• 🏆 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!

Exercise 1 - variables

Status
Not open for further replies.
Level 14
Joined
Mar 11, 2017
Messages
587
So here's a much lower level-of-skills submission than you're used to see nowadays. A Jass exercise, as simple as it can get. ;)

Part 1
Create a (JASS) trigger that runs when a player types "-" as exact string.
The trigger's goal is to create a footman on the map at a random position.
The unit must be created within the map's playable area.
Increase the "Counter" variable by 1 each time the trigger runs.

Part 2
Create a new trigger (in the same trigger sheet) that runs when a unit dies.
When the trigger runs, the "Counter" must be decreased by 1, furthermore
the "Counter" 's value must be displayed on screen to get to know how many footmen are alive.

Requirements

Following global variables must be declared and used (<type> , <name>):
  1. integer Counter
Following local variables must be declared and used (<type> , <name>):
  1. unit u
  2. string text
  3. real x
  4. real y

Proposed solution
Premise - known errors:
  • I fail at [requirements.1] making use of a local unit u
  • I fail at [Part 2]:"crate a trigger (in the same trigger sheet)"
  • [Part 1] I forgot to substitute the local "text" to the literal "-" inside of the event. So it remained:
    vJASS:
    call TriggerRegisterPlayerChatEvent(t, Player(i), "-", true)
  • (The testmap has a hero preplaced, merely to quickly kill footmen for testing, in a completely triggerless way)
Related Testmap: https://www.hiveworkshop.com/attachments/jass-class-crash-course-1-w3m.273901/

Part 1 - Random footman creation
vJASS:
globals
integer Counter = 0
integer FtUniTyp = 'hfoo'
endglobals

function RndmFootActions takes nothing returns boolean
    local player p = GetTriggerPlayer()
    local real x = GetRandomReal(GetRectMinX(bj_mapInitialPlayableArea), GetRectMaxX(bj_mapInitialPlayableArea))
    local real y = GetRandomReal(GetRectMinY(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea))

    //The trigger's goal is to create a footman on the map at a random position. The unit must be created within the map's playable area. Increase the "Counter" variable by 1 each time the trigger runs.
    // Counter isn't player-specific
    call CreateUnit(p, FtUniTyp, x, y, GetRandomReal(0., 359.))
    set Counter = Counter + 1
    call BJDebugMsg("A "+ GetObjectName(FtUniTyp) +" was created. There are now " + I2S(Counter) + " units of type: " + GetObjectName(FtUniTyp))
    return false
endfunction

function InitTrig_CraCo1 takes nothing returns nothing
    local trigger t = CreateTrigger()
    local string text = "-"
    local integer i = 0

    //runs when a player types "-" as exact string. any player
    loop
        exitwhen i > 11
        call TriggerRegisterPlayerChatEvent(t, Player(i), "-", true)
        set i = i + 1
    endloop

    call TriggerAddCondition(t, Condition(function RndmFootActions))
    set t = null
endfunction


Part 2- Footman death
vJASS:
function FootDeathActions takes nothing returns boolean
    if (GetUnitTypeId(GetTriggerUnit()) == FtUniTyp) then
        set Counter =  Counter - 1 //counter defined in CraCo1
        call BJDebugMsg("A "+ GetObjectName(FtUniTyp) +" died. There are now " + I2S(Counter) + " remaining.")
    return false
    endif
    return false
endfunction

function InitTrig_FootDeath takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t, Condition(function FootDeathActions))
    set t = null
endfunction

Log of issues and problems encountered during typing:

'unittype's in jass are completely different from 'Unit Type's in GUI. In jass they represent broad categories of unit types based on a property. Therefore I had some difficulties finding out how to obtain what I am used to know as "unit type" from GUI. Found help in the thread: How do I add a "Unit Type" condition to a Jass function?
vJASS:
return IsUnitType(u, 'hfoo') // doesn't do what i hope
vJASS:
return (GetUnitTypeId(GetTriggerUnit()) == 'hfoo') // unittypeID is the equivalent to a GUI unit type. It's an integer

This thread: how to print out names of units, items, etc? was helpful to learn of GetObjectName() to do this task.
Question: is it an optimal way to do it?

I didn't null local triggers t inside the Init at first.
From Converting GUI into Efficient JASS : "created local triggers should be nulled (set t = null)"
Question: should all local nonnatives (i.e. handles) be nulled or otherwise explicitly destroyed at the end of a block?

From Converting GUI into Efficient JASS
Almia said:
merge the conditions and actions and use conditions rather than action for triggers.
Actions creates new thread, conditions don't.


Note: this might not be the best practice or it may even be an irrelevant measure. Please have a look at the discussion at: Mission Solved - Order Tracking - Submission and following interventions.

this was a really stupid issue. I didn't know what were the correct restrictions on the naming of the InitTrig_X function. It functioned correctly after I named it like InitTrig_[name of the trigger page].
This is also the reason why I couldn't find a way to have the triggers stay in the same page without resorting to different methods of initialization, offered by vjass, which I still need to learn how to use.

Question: asking for more clarification on this topic please.

I also made other mistakes of smaller significance that I corrected without describing them up here, and there's still those that I'm not aware of which will be caught by the reviewer. I thank in advance for the corrections and the possibility of learning something bit by bit.
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
If you don't know how to to do something in JASS, you can always convert it from GUI into JASS and see which natives are used. Create a trigger with (unit type of (triggering unit)), convert it and you can see how unit type of unit looks in JASS.

Question: should all local nonnatives (i.e. handles) be nulled or otherwise explicitly destroyed at the end of a block?
Null locals: yes, always
Explicitly destroy: depends
Every object that is created should also be destroyed, if you no longer need it. If you need it until the map ends, you don't have to destroy it. In most cases you want to remove locations/ destroy groups at the end of the function. A location/group is similar to a unit. You can have a reference to it with a variable and multiple variables point to the same object. If you have unit u = unit c and you do: RemoveUnit(u) the unit that u and c point to will be removed. It's the same for locations/groups and every other handle.
So if at the end of your function you have a handle, that is only referenced by the local variable, you want to destroy it, because after the function the local variable no longer exists and you can no longer reference the handle resulting in a memory leak.
Unit, Player and some other handles can be an exception, because with the use of events/unit(player) groups and maybe other things you can still get references to these handles. Also units can always be removed by decaying after getting killed.
If you have a handle, that is referenced by multiple things you can destroy it by using only one of your references. So now if your local variable no longer exists after the function, you can still access your handle with your other references, so you can destroy it later and don't have to destroy it at the end of the function.
So basically whenever you no longer need a handle, you should destroy it. If you lose the reference to a handle, you can no longer remove it and you will have a memory leak.

Question: asking for more clarification on this topic please.
JASS trigger naming is really bad in my opinion. vJASS really helps here. It's also easier to name your function, because you won't have many problems with duplicate function names.
I always have to create a trigger in GUI covert it to see how it works, if I have to do something in pure JASS.
The init trigger (InitTrig_<TriggerName>) is the trigger that is called at the beginning of the map by the map main function. It will create the main trigger gg_trg_<TriggerName> that is exectued when you use Run Trigger(...) .
If you only use local triggers you can't use that function, but that's only important in combination with GUI, because in JASS you can just use function calls.
It's really easy to create multiple triggers within one trigger sheet. You will always have one init trigger for your trigger sheet.
JASS:
function InitTrig_FootDeath takes nothing returns nothing
    local trigger t = CreateTrigger() //trigger1
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t, Condition(function FootDeathActions))
    set t = CreateTrigger() //trigger2
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    //would make sense to use a different event though
    call TriggerAddCondition(t, Condition(function FootDeathActions2))
    set t = null
endfunction
 
Status
Not open for further replies.
Top