Spoiler for Creating a Passive ability in Jass:
Thx to Romek for creating this guide credits for him
Introduction:
Welcome to my second tutorial. Today, you'll be learning how to make a simple passive spell in vJass. Specifically, we'll be making a spell which gives a chance to possess (Give the attacker permanent control over the target) the target.
As well as this, you'll learn how to make spells easily configurable, use scopes, and other Jass skills.
When creating passive spells with triggers, the actual spell will have to do nothing, and the chances will have to be triggered.
Creating the Spell:
Well, you obviously need a map and a spell. So open NewGen, make a new map, and go to the object editor.
Now you should note that when making passive abilities with triggers, the actual ability should have no effects whatsoever. It should just be a dummy spell which is used to check if the attacker has the spell.
I'll base my ability off of "Bash" (Human, Hero Ability)
I set the spell so that it has no chance to do no damage. And I removed the buffs. I also changed the tooltips and the icons
My spells Raw Code is 'Pwnt'. Yours will probably be different.
To check the raw code, go to the object editor and press CTRL + D. Everything will then be displayed in the Raw Codes.
Creating the Trigger:
Right. We have the spell, now all we need to do is make the triggers, the Hero, and the weaklings which will be possessed.
Open up the trigger editor (F4) and Create a new Trigger (CTRL + T).
Name the trigger whatever you want. It really doesn't matter.
Then go to Edit > Convert to Custom Text so that we can use Jass instead of GUI .
Now delete everything that appears in the text box. It's rubbish.
Creating the Initializer and the Condition:
Now that we have a blank Jass trigger, we need to start the actual triggering.
We'll begin with creating a scope by using the syntax:
Scopes allow the use of Public and Private functions (Which prevent the functions being accessed from any other trigger). The Initializer is the name of the function which will be ran at Map Initialization.
Now, if our initializer is called "Init" we'll obviously need a function called Init. So lets create that now. We'll also create a local trigger to detect when a unit is attacked.
For this tutorial, my scope will be called "Possession"
Next, we'll register the events using Player Events. To do this, create a local integer and loop through it until you stop at 16 (All Players, including Neutral). Then register the event for the Player you're currently looping for.
There! We now have a trigger with the events registered... But wait. We have a problem. When using "null" as an argument for a boolexpr, we get a leak. (For some weird reason). So we'll have to create a boolexpr to take care of that leak.
Make a new, private function called "True" that takes nothing, and returns true.
Now, use the function as an argument instead of null
Your complete trigger should now look like this:
We're nearly finished with the preparations. We just need a condition and an action for our new trigger.
We'll start off with the condition. Create a new function called "Con" which takes nothing and returns a boolean. In this function, we'll be checking if the attacked unit is an ally of the attacker, as well as making the spell random.
We'll start off with checking if the owners of the attacker and the target are enemies. To do this, we'll use a function called "IsPlayerEnemy". This function takes 2 arguments, both of which are players, and returns true if they are enemies. Another function we will use is "GetUnitOwner" which returns the owner of the unit given to the function.
So to check if the Owner of the Target and the Owner of the Attacker are enemies, we need to do:
Now we need to make sure that the spell is random. At the moment, we'll add a 2% chance of the spell happening.
To do this, we'll get a random integer between 0 and 100, and check if it is less than or equal to 2.
For this, we will use the function call:
Now we will return the value of both of these functions. As we will need both of these to be true (Enemy, and Random), we will use the and keyword so that it will return true only if both of the conditions are true.
Our condition should now look like this:
After that, we'll need to check if the target is a Hero or not. We don't want to possess heroes do we?
To do this, we use:
Finally, we'll need to make sure the attacking unit has the ability, or every unit would be possessing units with no end :p
To do this we use:
Your final conditions should look like this:
Now, we need to add the conditions to the trigger. To do this, we use the function
Making the Actions:
Time for the actions. This is where all the effects of the spell will happen.
In this function, we'll change the owner of the target, and create a special effect on it.
Create a new, private function called Possession which takes and returns nothing. In this function, create 2 local unit variables: t and u (For Target, and Attacker)
These will make it easier to use these units later on in the trigger without having to keep calling the same functions.
Next, we'll create an effect on the target to show the players that it's been possessed. I'll use the model "Abilities\Spells\Items\AIvi\AIviTarget.mdl" because it looks cool for something like this :p
As we won't be needing the effect except for showing it once, we'll have to destroy it instantly. We can do this in 1 line by using
So I'll add my effect to the "chest" of the target.
Next, we're going to change the owner of the target.
To achieve this, we'll use the function called "SetUnitOwner"
We'll need to change the owner of t (target unit) to the Owner of u (Attacker) and change the colour of the unit.
For this, we'll use:
Now we'll need to null the 2 unit variables to prevent them from leaking.
Finally, we'll need to add the actions to the trigger.
For this, we use the function:
Your code should now look like this:
Creating the Configurables:
Now, although you have a fully working spell, it still isn't up to a good standard. It has a 2% chance of happening no matter what happens, and it'll be very difficult for newbies to change the effect created when the conditions are met.
We'll start off by creating another global block above everything except the first scope keyword. This global block will be used for the configurables.
In this block, create 2 private, constant strings. One for the effect, and one for the place it's attached to.
As these are constants, they have to be initialized (Have a value assigned to them immediately) So we'll assign them to what they are in the code, and replace the strings in the code with the variable names.
Your entire code should now look like this:
Secondly, we'll create a private, constant function which returns the chances of the unit being possessed based on the level of the ability.
This will also go at the top of the script for the user to change as they like
I'll make it so that for every level of the spell, a 1% chance is added.
We'll also have to change the Con function so that it checks the chance based on this function.
Thirdly, what if the person using the spell made the Raw Code 'A000'. Then it could become difficult for them to configure the spell. So we create another constant integer called ID, which is the Raw Code of the spell. We'll also change the code so that it uses the constant.
Your code should now look like this:
Finishing Off:
Finally, we'll just need to add some comments to the code, and make a neat test map.
So, make a nice map header and place it above Everything (Or below the scope keyword if you want) and give a brief description of the spell, say who made it, and write how to import it.
Congratulations! You have just created a passive spell in vJass
Note: The DeathKnight in the Demo Map has superfast attack speed and low damage, so it's easier to notice the spells effect
Introduction:
Welcome to my second tutorial. Today, you'll be learning how to make a simple passive spell in vJass. Specifically, we'll be making a spell which gives a chance to possess (Give the attacker permanent control over the target) the target.
As well as this, you'll learn how to make spells easily configurable, use scopes, and other Jass skills.
When creating passive spells with triggers, the actual spell will have to do nothing, and the chances will have to be triggered.
Creating the Spell:
Well, you obviously need a map and a spell. So open NewGen, make a new map, and go to the object editor.
Now you should note that when making passive abilities with triggers, the actual ability should have no effects whatsoever. It should just be a dummy spell which is used to check if the attacker has the spell.
I'll base my ability off of "Bash" (Human, Hero Ability)
I set the spell so that it has no chance to do no damage. And I removed the buffs. I also changed the tooltips and the icons
My spells Raw Code is 'Pwnt'. Yours will probably be different.
To check the raw code, go to the object editor and press CTRL + D. Everything will then be displayed in the Raw Codes.
Creating the Trigger:
Right. We have the spell, now all we need to do is make the triggers, the Hero, and the weaklings which will be possessed.
Open up the trigger editor (F4) and Create a new Trigger (CTRL + T).
Name the trigger whatever you want. It really doesn't matter.
Then go to Edit > Convert to Custom Text so that we can use Jass instead of GUI .
Now delete everything that appears in the text box. It's rubbish.
Creating the Initializer and the Condition:
Now that we have a blank Jass trigger, we need to start the actual triggering.
We'll begin with creating a scope by using the syntax:
Code: Select all
scope NAME initializer Init
endscope
Now, if our initializer is called "Init" we'll obviously need a function called Init. So lets create that now. We'll also create a local trigger to detect when a unit is attacked.
For this tutorial, my scope will be called "Possession"
Next, we'll register the events using Player Events. To do this, create a local integer and loop through it until you stop at 16 (All Players, including Neutral). Then register the event for the Player you're currently looping for.
Code: Select all
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, null)
set i = i + 1
endloop
Make a new, private function called "True" that takes nothing, and returns true.
Code: Select all
private function True takes nothing returns boolean
return true
endfunction
Your complete trigger should now look like this:
Code: Select all
scope Possession initializer Init
private function True takes nothing returns boolean
return true
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, Filter(function True))
set i = i + 1
endloop
endfunction
endscope
We'll start off with the condition. Create a new function called "Con" which takes nothing and returns a boolean. In this function, we'll be checking if the attacked unit is an ally of the attacker, as well as making the spell random.
We'll start off with checking if the owners of the attacker and the target are enemies. To do this, we'll use a function called "IsPlayerEnemy". This function takes 2 arguments, both of which are players, and returns true if they are enemies. Another function we will use is "GetUnitOwner" which returns the owner of the unit given to the function.
So to check if the Owner of the Target and the Owner of the Attacker are enemies, we need to do:
Code: Select all
IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker()))
To do this, we'll get a random integer between 0 and 100, and check if it is less than or equal to 2.
For this, we will use the function call:
Code: Select all
GetRandomInt(0, 100)
Our condition should now look like this:
Code: Select all
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= 2
endfunction
To do this, we use:
Code: Select all
GetTriggerUnit(), UNIT_TYPE_HERO) == false // Compared to false so it ISN\'T a hero =
To do this we use:
Code: Select all
GetUnitAbilityLevel(GetAttacker(), 'Pwnt') > 0
Code: Select all
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker
Code: Select all
call TriggerAddCondition(t, Filter(function Cons))
Time for the actions. This is where all the effects of the spell will happen.
In this function, we'll change the owner of the target, and create a special effect on it.
Create a new, private function called Possession which takes and returns nothing. In this function, create 2 local unit variables: t and u (For Target, and Attacker)
Code: Select all
private function Possession takes nothing returns nothing
local unit u = GetAttacker()
local unit t = GetTriggerUnit()
endfunction
Next, we'll create an effect on the target to show the players that it's been possessed. I'll use the model "Abilities\Spells\Items\AIvi\AIviTarget.mdl" because it looks cool for something like this :p
As we won't be needing the effect except for showing it once, we'll have to destroy it instantly. We can do this in 1 line by using
Code: Select all
DestroyEffect(AddSpecialEffect(...))
Code: Select all
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl", t, "chest"))
To achieve this, we'll use the function called "SetUnitOwner"
We'll need to change the owner of t (target unit) to the Owner of u (Attacker) and change the colour of the unit.
For this, we'll use:
Code: Select all
call SetUnitOwner(t, GetOwningPlayer(u), true)
Code: Select all
set u = null
set t = null
For this, we use the function:
Code: Select all
call TriggerAddAction(t, function Possession)
Code: Select all
scope Possession initializer Init
private function True takes nothing returns boolean
return true
endfunction
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= 2 and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) and GetUnitAbilityLevel(GetAttacker(), 'Pwnt') > 0
endfunction
private function Possession takes nothing returns nothing
local unit u = GetAttacker()
local unit t = GetTriggerUnit()
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl", t, "chest"))
call SetUnitOwner(t, GetOwningPlayer(u), true)
set u = null
set t = null
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, Filter(function True))
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Cons))
call TriggerAddAction(t, function Possession)
endfunction
endscope
Now, although you have a fully working spell, it still isn't up to a good standard. It has a 2% chance of happening no matter what happens, and it'll be very difficult for newbies to change the effect created when the conditions are met.
We'll start off by creating another global block above everything except the first scope keyword. This global block will be used for the configurables.
In this block, create 2 private, constant strings. One for the effect, and one for the place it's attached to.
Code: Select all
private constant string EFFECT
private constant string EFFECT_POSITION
Your entire code should now look like this:
Code: Select all
scope Possession initializer Init
globals
private constant string EFFECT = "Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl"
private constant string EFFECT_POSITION = "chest"
endglobals
private function True takes nothing returns boolean
return true
endfunction
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= 2 and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false and GetUnitAbilityLevel(GetAttacker(), 'Pwnt') > 0
endfunction
private function Possession takes nothing returns nothing
local unit u = GetAttacker()
local unit t = GetTriggerUnit()
call DestroyEffect(AddSpecialEffectTarget(EFFECT, t, EFFECT_POSITION))
call SetUnitOwner(t, GetOwningPlayer(u), true)
set u = null
set t = null
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, Filter(function True))
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Cons))
call TriggerAddAction(t, function Possession)
endfunction
endscope
This will also go at the top of the script for the user to change as they like
I'll make it so that for every level of the spell, a 1% chance is added.
Code: Select all
private constant function CHANCE takes integer level returns integer
return level * 1
endfunction
Code: Select all
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= CHANCE(GetUnitAbilityLevel(GetAttacker(), 'Pwnt')) and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false and GetUnitAbilityLevel(GetAttacker(), 'Pwnt') > 0
endfunction
Your code should now look like this:
Code: Select all
scope Possession initializer Init
globals
private constant string EFFECT = "Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl"
private constant string EFFECT_POSITION = "chest"
private constant integer ID = 'Pwnt'
endglobals
private constant function CHANCE takes integer level returns integer
return level * 1
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= CHANCE(GetUnitAbilityLevel(GetAttacker(), ID)) and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false and GetUnitAbilityLevel(GetAttacker(), ID) > 0
endfunction
private function Possession takes nothing returns nothing
local unit u = GetAttacker()
local unit t = GetTriggerUnit()
call DestroyEffect(AddSpecialEffectTarget(EFFECT, t, EFFECT_POSITION))
call SetUnitOwner(t, GetOwningPlayer(u), true)
set u = null
set t = null
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, Filter(function True))
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Cons))
call TriggerAddAction(t, function Possession)
endfunction
endscope
Finally, we'll just need to add some comments to the code, and make a neat test map.
So, make a nice map header and place it above Everything (Or below the scope keyword if you want) and give a brief description of the spell, say who made it, and write how to import it.
Code: Select all
// +------------------------------------------------------------------+
// | Possession - Created by Romek. Requires a vJass preprocessor! |
// +------------------------------------------------------------------------+
// | Gives a small chance to permanently change control of the target unit |
// | to the owner of the attacker. |
// +------------------------------------------------------------------------+
// | How to Import: |
// | - Create a new trigger |
// | - Convert it to Custom text (Edit > Convert to Custom Text) |
// | - Replace everything there with this code |
// | - Change the constants to suit yourself |
// | - Enjoy! |
// +------------------------------------------------------------------------+
And then add some short, simple comments around the constants to explain what should be changed.
Your final code should look something like this:
[code]scope Possession initializer Init
// +------------------------------------------------------------------------+
// | Possession - Created by Romek. Requires a vJass preprocessor! |
// +------------------------------------------------------------------------+
// | Gives a small chance to permanently change control of the target unit |
// | to the owner of the attacker. |
// +------------------------------------------------------------------------+
// | How to Import: |
// | - Create a new trigger |
// | - Convert it to Custom text (Edit > Convert to Custom Text) |
// | - Replace everything there with this code |
// | - Change the constants to suit yourself |
// | - Enjoy! |
// +------------------------------------------------------------------------+
// |-------------|
// | Constants: |
// |-------------|
globals
// The effect created on the target when it is being possessed:
private constant string EFFECT = "Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl"
// Which is attached to the targets:
private constant string EFFECT_POSITION = "chest"
// The Raw code of the ability
private constant integer ID = 'Pwnt'
endglobals
private constant function CHANCE takes integer level returns integer
// The chance of a unit being possessed. "level" is the level of the ability.
return level * 1
endfunction
// |------------------|
// | End of Constants |
// |------------------|
// DO NOT EDIT BELOW THIS LINE!
private function True takes nothing returns boolean
return true
endfunction
private function Cons takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetAttacker())) and GetRandomInt(0, 100) <= CHANCE(GetUnitAbilityLevel(GetAttacker(), ID)) and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false and GetUnitAbilityLevel(GetAttacker(), ID) > 0
endfunction
private function Possession takes nothing returns nothing
local unit u = GetAttacker()
local unit t = GetTriggerUnit()
call DestroyEffect(AddSpecialEffectTarget(EFFECT, t, EFFECT_POSITION))
call SetUnitOwner(t, GetOwningPlayer(u), true)
set u = null
set t = null
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, Filter(function True))
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Cons))
call TriggerAddAction(t, function Possession)
endfunction
endscope
Note: The DeathKnight in the Demo Map has superfast attack speed and low damage, so it's easier to notice the spells effect