Making a spell in vJASS - Practice Session 1

Level 2
Joined
May 17, 2009
Messages
16
On the contrary Shanghai, I found the tutorial to be just the right length. I know I'm ressurecting this post but;

Phoenix, I liked how you went into detail into some of the topics, but I would have liked for you to cover the meanings of some of the things inside your Init Function. For instance,

JASS:
local trigger InstantJusticeTrg = CreateTrigger()        
call TriggerRegisterAnyUnitEventBJ(InstantJusticeTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) 
call TriggerAddCondition(InstantJusticeTrg, Condition( function Conditions ) )        
call TriggerAddAction( InstantJusticeTrg, function Actions )

Over all I really liked your tutorial, there were a few grammatical mistakes but I over looked those. As a C++ programmer for two years, the languages look similar so that might have been why I understood most of the meanings.

+Rep, a very good tutorial for vJass.
 
Thx for the rep++ guys, it's good to know hard work is appreciated.
local trigger InstantJusticeTrg = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(InstantJusticeTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(InstantJusticeTrg, Condition( function Conditions ) )
call TriggerAddAction( InstantJusticeTrg, function Actions )
Ok, you see, a when you click new trigger button, you are NOT really creating a trigger, you are instead creating a document of text, or a project (in C++ if you use Eclipse, or IDE::Blocks). Each text document can contain several triggers inside it. Triggers control what happens when a player does an action. Triggers fire on certain events. In this case, every time a player casts ANY spell, it will fire this trigger, and it will run the code you made.
However we don't want our code to run all the time, this is why we have a function called Conditions. If the conditions are met, then the trigger will move on to actions and it will execute the rest of the code, else the trigger will just stop there and nothing else will happen.
Conditions and Actions are not obligatory inside triggers, but they are a good way of dividing a problem and solving it.

I hope you understand this small explanation among with my other analogies.
 
Level 7
Joined
Mar 8, 2009
Messages
360
Nice tutorial, it was my first vJass tutorial and i could understand everything.

Btw, why the hate against dota. I often think people are just jealous that they haven't got a map that is so popular. Ok, i must admit some spells suck hard (like Return skill of centaur), but in overall the gameplay is better than wow IMO.
 
Level 10
Joined
Aug 19, 2008
Messages
492
Nice tutorial, it was my first vJass tutorial and i could understand everything.

Btw, why the hate against dota. I often think people are just jealous that they haven't got a map that is so popular. Ok, i must admit some spells suck hard (like Return skill of centaur), but in overall the gameplay is better than wow IMO.
Mostly because most map makers finds it boring to play.
Personally I can't stand how the players treat new people ("[All] Oh noes! One player on my team is newbie!" *ragequit*), but that might not be the case for everyone
 
Btw, why the hate against dota.
Because even this simple spell is better then half Dota spells. Morale ? Dota doesn't have enough quality for the reputation it has. Dota is popular because gamers are ignorants that can't distinguish a good quality spell from a rip-off spell.

I now officially say I will start on a new tutorial. I will start it on day 20 July and I intend to cover the basis of structures and timers as well as modularity. Wish me luck people !
 
I don't get your point, structs rock for making spells.
As for the rest, it is not a problem. Struct usage is meant to be covered for lesson 2. In lesson 2 I will also introduce the user to timers as well, and I plan to teach him a few tricks with timers.
I may also give a "little smell" about modularity =P

However lesson 2 will take it's time, I will only start making it after day 17 July, the date of (hopefully) my last exam.

Still, thx for your comment =D
 
Level 4
Joined
Jul 20, 2005
Messages
107
First of all thanks for the tutorial.
Question:
I don't understand how it is MUI when you put it as global for non-constant variables. Can you explain that? thanks
 
MUI -> Multi unit instanceable. It basicaly means that your spell can be cast by more than 1 unit at the same time without blowing your computer.

As for the global, happens that the wc3 engine runs using something called threads. A thread is a very low level mechanism that can execute an action or part of an action. Wc3 engine can not run 2 threads at the same time.
Because we are using the globals groups instantly (meaning we don't use them over time) wc3 engine will run the code 1 thread at a time, thus keeping your spell MUI.
A simple way of understand what happens that might work is this: You can use global variables such as groups to make your spell MUI as long as you do NOT involve those global variables in ANY time depending mechanisms such as waits. I am not using any time mechanism associated to the global variable, so it is MUI. Also, notice that I try to do all my group code very quickly and very close. All code is imperative and I have no useless instructions in the middle, this also helps.

I hope you understand why the spell is MUI.
 
Level 4
Joined
Jul 20, 2005
Messages
107
Oh ok... thanks for the explanation, I get what you mean =)

But what if I wanted to use Sleep function? I will have to use the local way which will cause a lot of lags? Is there any alternative way of doing this which will reduce the lag and leak?
 
to use timers... timers are complex, if you want to use them you will need to use an outisde system and make your spell dependant on that system. I recommend the TimerUtils. It has 3 flavors and you can pick the one you find best.

I was going to make a 2nd tutorial about timers ... but unfortunately it was not good enough and I never submitted it.
 
Level 4
Joined
Jul 20, 2005
Messages
107
Well at first I was planning on using a loop with a sleep function inside which will make a timer. Can you please show me an example by making a skill using timer also? When you are free that is.

Anyway, I notice that normal WE can't run vJass, and the programs you provide is it the latest version? As in 1.24b?
 
To run vJass you need a tool called JassNewGeneration Pack. You can download it over here:
http://www.wc3c.net/showthread.php?t=90999

As for timers, I am a retired coder and so I don't code any longer (I forgot to update my subtitle from "Map maker" to "Retired coder").
If you wish to use timers, you can see my submitted spells, they all use timers over waits and they are not hard to understand.

As for wc3c, Cheezman is right. However don't expect to be well treated, people there are usually very cold. If you have enough patience and will to learn you will make it there, however if you cannot receive criticism i recommend you stay at THW.
 
Level 4
Joined
Jul 20, 2005
Messages
107
Hmm.. I have programming basics but not familiar with Jass' syntax. Do you guys think is ok for me to go straight for vJass or should i learn Jass first before i start learning vJass. Cause from what I see here, vJass and Jass not much different just the way you write the syntax is a bit different only.
 
Level 10
Joined
Aug 19, 2008
Messages
492
Let me quote Vexorian on that:
JassHelper: Turns your simple code into something that is complicated enough to work.

vJass can't do anything Jass2 can't, but it's so much easier. I'd totally recommend it, even if you're not going to utilize all of its features (I myself only use free global declaration and scopes).
 
vJass can't do anything Jass2 can't, but it's so much easier. I'd totally recommend it, even if you're not going to utilize all of its features (I myself only use free global declaration and scopes).
Wrong? vJass supports structs, which is not supported on JASS2. Its basically converted back to JASS2, but its features get lost.
 
Level 4
Joined
Dec 26, 2009
Messages
64
JASS:
//===========================================================================
// I thought it was spelled effect, not effetc?
//A vJASS and JESP spell that will have an effetc depending on the number
//of units in the AOE. If there are more allies than enemies, we heal allies
//if there are more enemies that allies we damage enemies, if the number of
//allies equals the number of enemies we heal the allies and damage the enemies
//
//@author Flame_Phoenix
//
//@credits
//- Blade.dk, for the CopyCroup function
//
//@version 1.0
//===========================================================================
scope InstantJustice initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
// Why such long names? SPELL_ID is fine, but why use underscores? DummyID works fine.  I guess I`m a speed freak.
    globals
        private constant integer SPELL_ID = 'ANsi' //the rawcode of the spell
        private constant integer DUMMY_ID = 'h000' //rw of the dummy unit
        private constant string AOE_EFFECT = "Units\\NightElf\\Wisp\\WispExplode.mdl" //effect that will be created when we cast the spell on the AOE
        private constant string HEAL_EFFECT = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //effect that will be created when we heal units
        private constant string DAMAGE_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //effect that will be created when we damage units
        private constant damagetype D_TYPE = DAMAGE_TYPE_NORMAL //the attack type of the spell
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC //the damage type of the spell
    endglobals

    private function Range takes integer level returns real
    //returns the range the spell will affect
        return level * 150.
    endfunction

    private function Heal takes integer level returns real
    //returns the heal allies will take
        return level * 75.
    endfunction

    private function Damage takes integer level returns real
    //returns the damage enemies will take
        return level * 50.
    endfunction

    private function Targets takes unit target returns boolean
    //the units the spell will affect
        return (GetWidgetLife(target) > 0.405) and (IsUnitType(target, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false)
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
    globals
        private group all
        private group copy
        private boolexpr b
    endglobals
//===========================================================================
//Function made by Blade.dk; Search for [url=http://www.wc3campaigns.com]wc3campaigns.com[/url] for more info
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup(g, function GroupAddGroupEnum)
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets(GetFilterUnit())
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
// These variables are too long.  You need to shorten them, it kills me.
    private function Actions takes nothing returns nothing
        local location spellLoc = GetSpellTargetLoc()
        local real spellX = GetLocationX(spellLoc)
        local real spellY = GetLocationY(spellLoc)
        local unit caster = GetTriggerUnit()
        local integer level = GetUnitAbilityLevel(caster, SPELL_ID)
        local unit f
        local integer allies = 0
        local integer enemies = 0

        //create the AOE effect
        call DestroyEffect(AddSpecialEffect(AOE_EFFECT, spellX, spellY))

        //counting the units
        call GroupEnumUnitsInRange(all, spellX, spellY, Range(level), b)
        set copy = CopyGroup(all)
        loop
            set f = FirstOfGroup(copy)
            exitwhen(f == null)
            call GroupRemoveUnit(copy, f)
            if IsUnitAlly(f, GetOwningPlayer(caster)) then
                set allies = allies + 1
            else
                set enemies = enemies + 1
            endif
        endloop

        //making the effect of the spell
        if allies > enemies then


            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //heal allies
                if IsUnitAlly(f, GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, f, "origin")) //the healing effect
                    call SetWidgetLife(f, GetWidgetLife(f) + Heal(level))
                endif
            endloop
        elseif enemies > allies then
            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //damage enemies
                if IsUnitEnemy(f, GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, f, "origin")) //the damaging effect
                    call UnitDamageTarget(caster, f, Damage(level), true, false, A_TYPE, D_TYPE, null)
                endif
            endloop
        elseif allies == enemies then
            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //heal allies
                if IsUnitAlly(f, GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, f, "origin"))
                    call SetWidgetLife(f, GetWidgetLife(f) + Heal(level))
                //if an unit is not an ally than it is an enemy
                else
                    call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, f, "origin"))
                    call UnitDamageTarget(caster, f, Damage(level), true, false, A_TYPE, D_TYPE, null)
                endif
            endloop
        endif

        call RemoveLocation(spellLoc)
        set spellLoc = null
        set caster = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger InstantJusticeTrg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(InstantJusticeTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(InstantJusticeTrg, Condition( function Conditions ) )
        call TriggerAddAction( InstantJusticeTrg, function Actions )

        //setting globals
        set all = CreateGroup()
        set copy = CreateGroup()
        set b = Condition(function Pick)

        //preloading effects
        call Preload(AOE_EFFECT)
        call Preload(HEAL_EFFECT)
        call Preload(DAMAGE_EFFECT)

        //preloading the ability
// BJ...? I didn`t look that up, but I think it has a native replacement.
        set bj_lastCreatedUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, SPELL_ID)
        call KillUnit(bj_lastCreatedUnit)

    endfunction
endscope

You also rely on a system in a tutorial. Why rely on a system when you are teaching? This isn`t a very good spell to teach, tbh.
 
You also rely on a system in a tutorial. Why rely on a system when you are teaching? This isn`t a very good spell to teach, tbh.
Because teaching modularity is also a good thing to do, and a good thing for you to learn.The great problem in THW is because of people like you that refuse to be modular and to follow community standards. At the time this tutorial was written, it compiled to all known standards of the coding community.

@Uzumaki.Minato: Thx :p
 
Level 1
Joined
Apr 4, 2009
Messages
4
Nice tutorial

I have just figured out Jass recently and now I find making skills in vjass is more interesting.I have a knowledge in c and a little in c++.but I just found to check out all the funtion names in programming jass is terrible.However,expecting your tutorial on timer(i did not find one good tutorial on this one in hive):grin:
 
Level 7
Joined
Mar 8, 2009
Messages
360
Flame Phoenix isn't writing that tutorial about timers, he said in 2009 he would write it during summer. But he still didn't write it, so he'll probably never write it.

In case you want even more c syntax in JASS you can also use cJASS (google it)
example:
JASS:
JASS:
function funcName takes type1 varName returns type2
    statements
endfunction

//would be in cJASS:
type2 funcName(type1 varName)
{
    statements
}
 
Level 7
Joined
Mar 8, 2009
Messages
360
Flame Phoenix isn't writing that tutorial about timers, he said in 2009 he would write it during summer. But he still didn't write it, so he'll probably never write it.

In case you want even more c syntax in JASS you can also use cJASS (google it)
example:
JASS:
JASS:
function funcName takes type1 varName returns type2
    statements
    return bla
endfunction

//would be in cJASS:
type2 funcName(type1 varName)
{
    statements
    return bla
}
 
Level 19
Joined
Oct 15, 2008
Messages
3,231
But how do you apply this trigger to a specific unit?

PS: Erm... I'm really sorry for asking this but, what does vJASS mean? Like, what does the 'v' stand for and why is it there? And, what is the difference between vJASS and JASS, I'm really sorry...
 
Level 16
Joined
Oct 10, 2009
Messages
1,425
For some reason it doesn't work,

I changed all the constants in the setup area, and copied the dummy unit, and the spell to get it to work, but it just won't.

If it matters, trigger name = InstantJustice (same as test map)

Anyways, The error is that it won't let me run the map in wc3. (indicates syntax)


JASS:
//===========================================================================
//A vJASS and JESP spell that will have an effetc depending on the number
//of units in the AOE. If there are more allies than enemies, we heal allies
//if there are more enemies that allies we damage enemies, if the number of 
//allies equals the number of enemies we heal the allies and damage the enemies
//
//@author Flame_Phoenix 
//
//@credits
//- Blade.dk, for the CopyCroup function 
//
//@version 1.0
//===========================================================================
scope InstantJustice initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
    globals
        private constant integer SPELL_ID = 'A00W'  //the rawcode of the spell
        private constant integer DUMMY_ID = 'h002'  //rw of the dummy unit
        private constant string AOE_EFFECT = "Units\\NightElf\\Wisp\\WispExplode.mdl"  //effect that will be created when we cast the spell on the AOE
        private constant string HEAL_EFFECT = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"  //effect that will be created when we heal units
        private constant string DAMAGE_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"  //effect that will be created when we damage units
        private constant damagetype D_TYPE = DAMAGE_TYPE_NORMAL //the attack type of the spell
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC  //the damage type of the spell
    endglobals
    
    private function Range takes integer level returns real
    //returns the range the spell will affect
        return level * 300.
    endfunction
    
    private function Heal takes integer level returns real 
    //returns the heal allies will take
        return level * 500.
    endfunction
    
    private function Damage takes integer level returns real
    //returns the damage enemies will take
        return level * 500.
    endfunction
    
    private function Targets takes unit target returns boolean
    //the units the spell will affect
        return (GetWidgetLife(target) > 0.405) and (IsUnitType(target, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false)
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//=========================================================================== 
    globals
        private group all
        private group copy
        private boolexpr b
    endglobals
//===========================================================================  
//Function made by Blade.dk; Search for [url]www.wc3campaigns.com[/url] for more info   
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup(g, function GroupAddGroupEnum)
        return bj_groupAddGroupDest
    endfunction
//===========================================================================  
    private function Pick takes nothing returns boolean
        return Targets(GetFilterUnit())
    endfunction
//===========================================================================    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local location spellLoc = GetSpellTargetLoc()
        local real spellX = GetLocationX(spellLoc)
        local real spellY = GetLocationY(spellLoc)
        local unit caster = GetTriggerUnit()
        local integer level = GetUnitAbilityLevel(caster, SPELL_ID)
        local unit f
        local integer allies = 0
        local integer enemies = 0
        
        //create the AOE effect
        call DestroyEffect(AddSpecialEffect(AOE_EFFECT, spellX, spellY))
        
        //counting the units
        call GroupEnumUnitsInRange(all, spellX, spellY, Range(level), b)
        set copy = CopyGroup(all)
        loop
            set f = FirstOfGroup(copy)
            exitwhen(f == null)
            call GroupRemoveUnit(copy, f)
            if IsUnitAlly(f, GetOwningPlayer(caster)) then
                set allies = allies + 1
            else
                set enemies = enemies + 1
            endif
        endloop
        
        //making the effect of the spell
        if allies > enemies then
            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //heal allies
                if IsUnitAlly(f, GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, f, "origin")) //the healing effect
                    call SetWidgetLife(f, GetWidgetLife(f) + Heal(level))
                endif
            endloop
        elseif enemies > allies then
            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //damage enemies
                if IsUnitEnemy(f, GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, f, "origin"))  //the damaging effect
                    call UnitDamageTarget(caster, f, Damage(level), true, false, A_TYPE, D_TYPE, null)
                endif
            endloop
        elseif allies == enemies then
            loop
                set f = FirstOfGroup(all)
                exitwhen (f == null)
                call GroupRemoveUnit(all, f)
                //heal allies 
                if IsUnitAlly(f,  GetOwningPlayer(caster)) then
                    call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, f, "origin"))
                    call SetWidgetLife(f, GetWidgetLife(f) + Heal(level))
                //if an unit is not an ally than it is an enemy
                else
                    call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, f, "origin"))
                    call UnitDamageTarget(caster, f, Damage(level), true, false, A_TYPE, D_TYPE, null)
                endif
            endloop
        endif
        
        call RemoveLocation(spellLoc)
        set spellLoc = null
        set caster = null
    endfunction    
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger InstantJusticeTrg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(InstantJusticeTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(InstantJusticeTrg, Condition( function Conditions ) )
        call TriggerAddAction( InstantJusticeTrg, function Actions )
        
        //setting globals
        set all = CreateGroup()
        set copy = CreateGroup()
        set b = Condition(function Pick)
        
        //preloading effects
        call Preload(AOE_EFFECT)
        call Preload(HEAL_EFFECT)
        call Preload(DAMAGE_EFFECT)
        
        //preloading the ability
        set bj_lastCreatedUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, SPELL_ID)
        call KillUnit(bj_lastCreatedUnit)
        
    endfunction
endscope

I bet I just fucked something up >.<, anyways, let me know please :/
 
Top