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!
Hi, i'm quite new to (v)jass and i want to save different informations (variables) for units. (Like a boolean and effect to create a attachment and other stuff).
My question is now what i should use to do it.
Currently im using hashtables, but i througt that there is maybe a way to create a struct, but how can i access the informations later?
Can i create structs with "custom" names (like the unitid) to acces them later?
Like:
JASS:
local unitstruct I2S(GetHandleId(GetTriggerUnit()))=unitstruct.create()
set hasattachmentbool= I2S(GetHandleID(GetEnteringUnit())).attachmentbool
(I know that this won't work, but i think you get the point)
PS: I don't just want to create a attachmentsystem, there are other informations to store for units, so plz don't post some attachmentsystems...thats not what i want
Structs are great for storing multiple values to one thing. When you "create" a struct, you are creating a new instance. Each instance can have its own members, and those members vary depending on which instance you have (that's what I meant by storing multiple values to one thing). So let's take this example:
JASS:
struct A
real x
real y
endstruct
function Example takes nothing returns nothing
local A myStruct = A.create()
set myStruct.x = 5
set myStruct.y = 10
endfunction
The A.create() will return a specific instance, and then you can assign the member values to that instance. In this case, x = 5 and y = 10. If you have another instance, those x and y values won't be the same unless you make it so.
Now, you want to assign some variables to a unit, correct? Well, that just means we have to associate a unit with the struct instance. After all, if each instance has its own variables with values, and we have an instance for each unit, then you will have a bunch of variables that you can assign for one unit! (which is what you seem to be trying to do)
The problem is the association. How do we get the struct instance from the unit? If you just create an instance like so:
JASS:
struct A
unit u
endstruct
/* .. some code later .. */
local A temp = A.create()
set A.u = GetTriggerUnit()
How will you get the instance later on? You have to have some way to point back to the instance. Some people will use unit indexing systems for this, in that you can have a struct instance that is equal to the unit's index. Whenever you need to get the struct instance, you would just do A(GetUnitIndex(<unit>)), or something similar.
I assume you don't want to use a custom system. Therefore, your best bet is to use hashtables. This is actually pretty efficient, so don't worry about it being slow or anything. You basically would assign it under the unit's handle ID:
So let me give you an example. I want to store a unit's last point order in a struct. I want to store the real x and y of it.
JASS:
scope Example initializer onInit
/* scopes are just wrappers */
/* I use it to declare an initialization function */
/* initializers are called on map initialization */
globals
private hashtable hash /* for associating unit with struct */
endglobals
struct LastOrder
real x
real y
endstruct
private function onPointOrder takes nothing returns boolean
/* this function is called when a unit is issued a point order */
/* see the function onInit for the setup */
/* first we declare the instance and create it */
local LastOrder node = LastOrder.create()
/* next we set the member values */
set node.x = GetOrderPointX()
set node.y = GetOrderPointY()
/*
now if we want to associate it with a unit
we can simply save it under a hashtable
*/
call SaveInteger(hash, 0, GetHandleId(GetTriggerUnit()), node)
/* you can save struct instances as integers */
endfunction
private function onInit takes nothing returns nothing
local trigger t = CreateTrigger()
set hash = InitHashtable() /* initialize our hashtable */
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
/* the function above registers when a unit is issued a point order */
call TriggerAddCondition(t, Condition(function onPointOrder))
endfunction
endscope
Now that we have it all saved, how do we retrieve it? That part is pretty simple:
JASS:
local LastOrder temp = LoadInteger(hash, 0, GetHandleId(<unit>))
That is all you need to do! Then you can refer to temp.x and temp.y freely. When you are done using the instance, you can call the .destroy() method if you are not going to use that specific instance at all anymore.
You may be wondering, if we can consider struct instances as integers, can't we just save under the handle ID and use that as the struct instance? Well, sadly the struct instance value must be a number between 0-8191. Of course, you can use subtraction or hash the value into that number, but it gets complex, messy, and it can be dangerous if you have too many handles and aren't careful. That's why a hashtable or unit indexer is the best route.
As an advanced tip -- if you think using LoadInteger(hash, 0, GetHandleId(<unit>) is ugly for each time you want to use it, you can take advantage of operators:
JASS:
globals
hashtable hash = InitHashtable()
endglobals
struct LastOrder
real x
real y
static method operator [] takes unit u returns thistype
return LoadInteger(hash, 0, GetHandleId(u))
endmethod
endstruct
If you used the same code above, then instead of:
JASS:
local LastOrder temp = LoadInteger(hash, 0, GetHandleId(GetTriggerUnit()))
You could simply do:
JASS:
local LastOrder temp = LastOrder[GetTriggerUnit()]
Pretty cool, huh? I won't go into too much detail about that, because it requires a bit more practice. It is just to show you what cool things you can do with vJASS.
If you want to know more, you should check out Vexorian's jasshelper manual, look at some tutorials around here, or just go out and start practicing with some code. It is daunting at first but very powerful.
----
Note: I did not compile or test any of this, so I apologize if there are some syntax errors.
Ah yes. In JASS we usually merge conditions and actions since conditions are faster. To simulate a condition, this is usually what you do:
JASS:
scope Test initializer Init
private function onSpellEffect takes nothing returns boolean
// we have to return boolean because we pass it under Condition(func..)
if GetSpellAbilityId() == 'A000' then // this is your condition
// actions here
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function onSpellEffect))
endfunction
endscope
Sometimes that becomes a hassle when you have so many locals, because you don't want to declare them outside the "if" block in case the condition isn't true. So you can alternatively do:
JASS:
scope Test initializer Init
private function onSpellEffectCond takes nothing returns boolean
return GetSpellAbilityId() == 'A000'
endfunction
private function onSpellEffect takes nothing returns nothing
// actions
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function onSpellEffectCond))
call TriggerAddAction(t, function onSpellEffect)
endfunction
endscope
The difference in speed is very minimal. It just comes down to which one you prefer.
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.