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!
No, I'm 100% Filipino...I use Jasscraft for help/reference...I will switch to JNGP someday...
Right now, I know a little bit of the structure of JASS as I have been reading tutorials also...
You should really switch to it right now. You can use the regular WE if you ever wanted to code in GUI (for hashtable support). But for all jass-related coding, you want JNGP.
It has syntax highlighting, additional capabilities, and comes with a function list that includes the vast majority of all the natives and BJs.
So let me explain a little further what DSG meant with the "Skip Remaining Actions" that is in GUI.
Basic it will look like this:
JASS:
function something takes nothing returns nothing
local integer i = GetRandomInt(1,2)
local boolean b = false
if i == 2 then
set b = true
endif
if b then
return //this is what in GUI is called, skip remaining actions
endif
//it will skip any action below this line so
call BJDebug("This will not show up if the integer i equals to 2")
endfunction
Sorry that I didn't align it but I'm just writing at free hand here.
Yet that was a function that returned nothing what DSG said is that it has a built-in return at the bottom line of the function. So what happens if we have a function that returns something else?
JASS:
function something2 takes nothing returns real
/* this will not work as there are no
returns and the function should return
a real value so this will give you
a syntax error with JNGP
*/
endfunction
This below will work as it returns a real variable although the returned value will be 0. as the real has just been declared. Yet we can have all these other real actions for no purpose. Let's give them one.
JASS:
function something3 takes nothing returns real
local real sum
local real a = GetRandomReal(5.,10.)
local real b = (GetRandomReal(0., 50.) / a) + a
local real c = GetRandomReal(0., 50.) + b - a
return sum
endfunction
This will return a completly random value.
JASS:
function something4 takes nothing returns real
local real sum
local real a = GetRandomReal(5.,10.)
local real b = (GetRandomReal(0., 50.) / a) + a
local real c = GetRandomReal(0., 50.) + b - a
set sum = a + b + c
return sum
endfunction
First of all, you can check out the function CreateNUnitsAtLoc() in jasscraft or in the JNGP function list. You will notice that it actually works much like a function you would create yourself, and is basically just made by blizzard to fit the parameters you enter in the GUI editor. This is how it looks:
JASS:
function CreateNUnitsAtLoc takes integer count, integer unitId, player whichPlayer, location loc, real face returns group
call GroupClear(bj_lastCreatedGroup)
loop
set count = count - 1
exitwhen count < 0
call CreateUnitAtLocSaveLast(whichPlayer, unitId, loc, face)
call GroupAddUnit(bj_lastCreatedGroup, bj_lastCreatedUnit)
endloop
return bj_lastCreatedGroup
endfunction
It assumes that you want to create more than one unit, and therefore commences a loop that calls another BJ, CreateUnitAtLocSaveLast(), which is made to store the "last created unit" variable that you can find in GUI. Inside this funciton, you can finally find the native function; "CreateUnit()".
CreateUnit requires the folowing parameters:
JASS:
native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit
As most things in JASS, it uses x/y values instead of locations, since they are much faster and does not leak (and since they're not handles, you don't even have to null the variables when you're done).
If instead you replace CreateNUnitsAtLoc with this function, you will get this:
JASS:
local unit u = CreateUnit(GetOwningPlayer(hero), 'h001', GetUnitX(hero), GetUnitY(hero), 0.)
Now GetLastCreatedUnit will not be saved, and neither will GetLastCreatedGroup, but instead you can use the local handle "u" to reference the unit you just created. Your computer also don't have to take the long way around, calling two other functions before it can call the native that actually creates the unit!
After this, we have the oh, so vain function UnitAddAbilityBJ().
This is how it looks:
JASS:
function UnitAddAbilityBJ takes integer abilityId, unit whichUnit returns boolean
return UnitAddAbility(whichUnit, abilityId)
endfunction
As you can see, it merely calls the native and does nothing more!
Instead, you should change it to this:
JASS:
call UnitAddAbility(u, 'A005')
And it already looks much more tidy.
JNGP will show BJ functions in red text as a way of sayong "OH YOU BAD BOY!", informing you that it has a native nested inside it. You can look up all the other functions you have that are displayed in red and see what natives they are calling, and try to replace them with theese, and your code will be much more efficient.
EDIT: Also notice that your condition is using two returns on top of eachother. This means that the function will ONLY return the first one, since the function automatically ends after returning.
In other words, it will never check if a unit has the buff 'B000', and only return wether the unit has a certain ability. You should place your "true" return inside an "if" statement instead, and have the "false" return at the end of the function. This means that the condition will return "true" if your "if" statement is passed, and otherwise read the "false" return if the "if" doesn't pass.
//===========================================================================
//= Blood Curse =
//= by mckill 2009 =
//===========================================================================
globals
//===========================================================================
//= Blood Curse Constants =
//===========================================================================
constant integer BC_SPELL_ID = 'A006'
constant integer BC_SPELL_BUFF_ID = 'B000'
constant integer BC_SPELL_DUMMY_SPELL = 'A005'
constant integer BC_SPELL_DUMMY_ORDER_ID = OrderId("unholyfrenzy")
constant integer BC_SPELL_DUMMY_ID = 'h001'
//===========================================================================
endglobals
//===========================================================================
//= Blood Curse Conditions =
//===========================================================================
function BloodCurseCondition takes nothing returns boolean
return (not (GetUnitAbilityLevel(GetTriggerUnit(),BC_SPELL_ID)==0)) and (not (GetUnitAbilityLevel(GetTriggerUnit(), BC_SPELL_BUFF_ID)==0))
endfunction
//===========================================================================
//===========================================================================
//= Blood Curse Actions =
//===========================================================================
function BloodCurseAction takes nothing returns nothing
local unit hero=GetTriggerUnit()
// Instead of using locations, you could just use reals
local real posX=GetUnitX(hero)
local real posY=GetUnitY(hero)
// I like using variables to easily understand its use, you can inline them for more speed.
local integer level=GetUnitAbilityLevel(hero,BC_SPELL_DUMMY_SPELL)
// CreateUnit returns a unit, so we could immediately set it to a local variable/pointer
local unit dummy=CreateUnit(GetOwningPlayer(hero),BC_SPELL_DUMMY_ID,posX,posY,0)
// Execute the dummy actions.
call UnitAddAbility(dummy,BC_SPELL_DUMMY_SPELL)
call SetUnitAbilityLevel(dummy,BC_SPELL_DUMMY_SPELL,level)
call IssueTargetOrderById(dummy,BC_SPELL_DUMMY_ORDER_ID,hero)
//==================================================
// Kill the dummy.
call SetUnitExploded(dummy,true)
call UnitApplyTimedLife(dummy,'BTLF',1.0)
//==================================================
// Destroy local handles and remove pointer to objects.
// Note: You forgot to remove the location in your original script.
set hero=null
set dummy=null
endfunction
//===========================================================================
//===========================================================================
//= Blood Curse Initialization =
//===========================================================================
function InitTrig_Blood_Curse takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(t,Condition(function BloodCurseCondition))
call TriggerAddAction(t,function BloodCurseAction)
// just in case you want the trigger referenced.
set gg_trg_Blood_Curse=t
endfunction
//===========================================================================
You do not cache the ability level of the hero spell.
To improve integrity, you should use global constants for types which are referenced more than once (like 'A006') as that means 1 change sufices to update all references unlike the current scheeme where bugs could be inroduced if someone forgets to update one of the references.
well, Im not at vJass yet...Bribe's explanation has more sense to me...
what I dont understand is when you create local triggers, it cannot be used outside, right?...so is it better to use "gg_trg_name"???...of course it depends on the situation...
The simple answer is, if you want to modify the trigger, perhaps you wanted to destroy or pause it, you need to use a global variable. If you don't want to modify it any longer, then there is no point of remembering where the said object is in memory.
You can't use local variables outside the function instance that declared them. When you call a function, a private space is provided for the function, after the execution of the said function, all that was inside that private space is removed, that's why local variables can no longer be used outside the function instance that declared it. Also, creating a trigger isn't the same as declaring a variable, the trigger handle is stored somewhere else and is independent of both global and local variables.
JASS:
function Taho takes nothing returns nothing
call CreateTrigger()
endfunction
Look at the code above, the CreateTrigger function creates a trigger handle in memory, which consumes memory, but we didn't reference it by using a variable, hence it is unreferenced and we cannot retrieve it from memory (imagine having to call a friend and forgetting his name, how would you call him? Hey ass!). This trigger in memory isn't useful, it just lies there and consumes space, we also can't remove it because the engine forgot where he put the trigger. That is called a memory leak.
Memory leaks exists not only in jass, but also on other programming languages, even in our brains lol. If you forgot something, that's a memory leak, or Alzheimer's[?].
native, scope, those are vJass. "constant function" is just a convention when writing in plain JASS to let users know that variable will always return the same thing.
Those functions returns the id of the spells in the object editor. Search them up yourself and press CTRL + D and you will see the 4 digit ID of your ability. Every single object in the object editor has one, check it out yourself.
In gui you're probably familiar with ability variables or unittype variables. In JASS those aren't necessary and in JASS we use Integers instead, put the 4 digit id between two 'yoid' and it will return the id of your object.
The constant stands for, constant. It will always return the same value all the time and therefore I *think* it will have better performance if you put them as constants. Seems like Bribe's answer was better
Native, in JASS there are two things about functions. Functions from the Blizzard.j and natives from the common.j (two files within the war3 mpq file). The natives are pure functions made in C which cannot be simplyfied no more. BJs are usually a bunch of natives collected together or even worse, they can be calling the real function straight away. Like PauseUnitBJ, it looks like this when you look up this function
JASS:
function PauseUnitBJ takes boolean pause, unit whichUnit returns nothing
call PauseUnit(whichUnit, pause)
endfunction
So it calls the real native straight away. Vex's optimizer fixes loads of these but not all so basic in JASS, do not use BJs but there are a few exceptions.
Like TriggerRegisterAnyUnitEventBJ which is constantly used in spellmaking. Or GroupPickRandomUnit, useless to remake as the output will be exactly the same.
Scopes are things we use in vJASS so if you're not going to learn that, I will not explain that.
Btw these
"scope function BloodCurseSpell takes nothing returns integer"
"native function BloodCurseSpell takes nothing returns integer"
I've only seen it in videos though. Alpha's special effects were better, the building scaling was realistic, it wasn't RTS, more like an Action RPG kind of game.
I've noticed that Hash GUI is this:
child key, parent key and Hash
But in JASS, it's reverse:
Hash first, the Parent and the child
my questions is this, is this ALWAYS the case? to save and load?...
Also I've experimented this:
JASS:
function hashon takes nothing returns nothing
local unit attacker = GetAttacker()
local hashtable hash1 = InitHashtable()
local integer ID = GetHandleId(attacker)
call SaveUnitHandle(hash1, StringHash("unit"), ID, attacker)
call KillUnit(LoadUnitHandle(hash1, StringHash("unit"), ID))
set attacker = null
//Is this the correct null?, or should I put also:
// set hash1 = null
// set ID = null
endfunction
//===========================================================================
function InitTrig_Hashtest takes nothing returns nothing
local trigger trigtest = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(trigtest, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddAction(trigtest, function hashon )
set trigtest = null
endfunction
Hashtable - Clear all child hashtables of child FCB_ID in Hash_01
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(FCB_Grp is empty) Equal to True
Then - Actions
Trigger - Turn off (This trigger)
Else - Actions
Fallen Corpse Bombardment Dies
Events
Unit - A unit Dies
Conditions
(Unit-type of (Triggering unit)) Equal to Falling Corpse
Actions
Custom script: set udg_FCB_CorpseFalling = GetTriggerUnit()
Custom script: set udg_FCB_CorpseID = GetHandleId(udg_FCB_CorpseFalling)
Custom script: set udg_FCB_Dam = LoadInteger(udg_Hash_01, StringHash("fcb2level"), udg_FCB_CorpseID)
Custom script: set udg_FCB_Unit = LoadUnitHandle(udg_Hash_01, StringHash("fcbcaster2"), udg_FCB_CorpseID)
Set FCB_Pt[3] = (Position of (Triggering unit))
Special Effect - Create a special effect at FCB_Pt[3] using Abilities\Spells\Other\Stampede\StampedeMissileDeath.mdl
Special Effect - Destroy (Last created special effect)
Custom script: set bj_wantDestroyGroup = true
Unit Group - Pick every unit in (Units within 100.00 of FCB_Pt[3] matching ((((Matching unit) belongs to an enemy of (Owner of FCB_Unit)) Equal to True) and (((Matching unit) is alive) Equal to True))) and do (Actions)
Loop - Actions
Unit - Cause FCB_Unit to damage (Picked unit), dealing (15.00 x (Real(FCB_Dam))) damage of attack type Chaos and damage type Divine
Custom script: call RemoveLocation(udg_FCB_Pt[3])
Hashtable - Clear all child hashtables of child FCB_CorpseID in Hash_01
Sorry to say this but don't use points that are arrayed unless you're going to use 100 of them "temporarly". Make 2 variables instead as arrayed takes much more space.
Custom script: set udg_FCB_CorpseFalling = GetLastCreatedUnit()
Converted GUI BJ, either use the GUI or make it real JASS.
Set FCB_Pt[1] = (Load (Key fcbloc) of FCB_ID in Hash_01)
Leaks as you don't remove it in the then part. You stilled saved it in the hash, don't worry.
Custom script: call ExplodeUnitBJ(udg_FCB_Dummy)
Use the GUI if you cannot remove the BJ proper. Read about what's behind the BJs.
Aha!, no wonder why "GetUnitAbilityLevel" did not work coz I used "GetUnitAbilityLevel('A02M', udg_FCB_Unit)...
Sorry to say this but don't use points that are arrayed unless you're going to use 100 of them "temporarly". Make 2 variables instead as arrayed takes much more space.
The reason why Im doin all this in custom script so that I can familiarize
the codes of saving & loading in hash without the help of JNGP (next time I will)...
Now I know a little like SaveUnitHandle, SaveInteger, etc... and how to
load them...
As I go step by step in doing this, I wil soon realize the importance of making it in REAL JASS...thanks to you guys!
Still better than World Editor JASS scripting, the one that has no initialization precedence, un-declarable global types and an accidental Ctrl + Z that can erase a day's work.
Another practice...Im studing coordinates...is this true JASS?...
JASS:
function WodSpell takes nothing returns boolean
return (GetSpellAbilityId()=='A002')
endfunction
function WodAct takes nothing returns nothing
local unit u = GetTriggerUnit()
local real x1 = GetUnitX(u)
local real y1 = GetUnitY(u)
local real x2 = GetSpellTargetX()
local real y2 = GetSpellTargetY()
local unit o = CreateUnit (GetOwningPlayer(u), 'hfoo', x1,y1, 0)
call IssuePointOrder(o, "move", x2, y2)
set u = null
set o = null
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))
call TriggerAddAction (t, function WodAct )
set t = null
endfunction
In JASS, you don't use any actions (you better learn this right away). You merge them with the conditions so instead of your trigger it will be like this:
JASS:
function WodSpell takes nothing returns boolean
local unit u
local unit o
if GetSpellAbilityId()=='A002' then
set u = GetTriggerUnit()
set o = CreateUnit(GetTriggerPlayer(), 'hfoo', GetUnitX(u), GetUnitY(u), 0.)
call IssuePointOrder(o, "move", GetSpellTargetX(), GetSpellTargetY())
set u = null
set o = null
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
In the end, everything is compiled to jass, even the doodad placing; what you should ask for is if your script is optimized. I meant no offense, just clarifying.
Now back on topic: that's a good start, using reals instead of location handles are better because they don't need allocation.
<EDIT>
Yeah baassee is right, triggercondition's execute faster because they don't support TriggerSleeps a.k.a Wait actions. It's like a one shot thread that the engine won't ever return to, think local variables. Also, it's not too bright to use waits when you're already scripting directly lol. I remembered one time... XD
SetUnitX/Y doesn't interrupt orders. SetUnitPosition does. Both are using coordinates.
It's the trigger condition. It has to return a boolean value and because we're not using any actions, it should return false always no matter what as it would else fire the actions when there are any. There are no else part in the if statement.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.