DuckieKing
Elitist Through and Through
- Reaction score
- 51
Structs
A basic tutorial by Duckieking
#T#
Table of Contents
#T# Table of ContentsA basic tutorial by Duckieking
#T#
Table of Contents
#O1# Overview
#L1# Why use structs?
#L2# A glance at structs.
#O2# Syntax
#L3# Declaring structs.
#L4# Calling structs in functions.
#O3# Conclusion
#L5# Questions? Answers.
#L6# Links and attachments.
#C# Changelog
#O1#
Overview
#L1# Why use structs?Overview
The purpose of structs and classes in real programming is encapsulation, the idea that tasks should be broken down into simpler tasks. The point of this is simpler, cleaner code that is easier to maintain.
The purpose of structs in WC3 is additional multi-instanceability. They are used for any real physics systems, and they aren't just good for that.
Structs are useful for creating MUI effects such as stacking abilities, stat systems, kill counters, timed abilities, item systems, and anything else you can think of. Normally, these things would confine you to one instance per player or else force you to use multiple arrays for each effect you wish to implement. Structs allow you to attach multiple variables to a unit or item, thus easing these things as much as cleaning leaks is easier in Jass.
#L2# A glance at structs.
Structs are not native to the World Editor. With his awesome programming skills, Vexorian has allowed us to create structs, or custom variable types. Structs can be declared to have member data (variables) and member functions (methods). Well, kind of...
Structs is a misnomer. To be entirely technical, structs can only have member data, but with Vexorian's World Editor hack, structs have methods.
A method is just a function able to be called from any function that has access to a struct. Member data are variables contained in a struct. What I mean by that is when a struct is created, memory is set aside for each of its variables, which in general are only set by the struct's member functions.
#O2#
Syntax
#L3# Declaring structs.Syntax
Structs are declared a lot like functions, but are really unique. I'll just show you:
JASS:
struct unitData //"struct" plus the name of the struct to be declared; since this tutorial is meant to show MUI struct-making it is named unitData
unit u //this will be the subject of the struct, just to cut down on call arguments; there's no need to type "local"
real statCredits=3 //this variable holds the number of stats a hero has left to spend
integer kills=0 //this variable holds how many kills the hero has made, so that kills can be rewarded with extra stats; normally this would limit you to one hero per player, but with structs you can do more
method ModifyStatCredits takes real statBonus returns nothing //this is our first method; it is used to add or remove stat credits; this value can be negative but won't remove already-bought stats
if this.statCredits <= 0 and this.statCredits+statBonus > 0 then //"this." is a pointer to the struct, this will be elaborated on in the next section, but for now accept that you reach a variable from within the same struct with "this."
call SetPlayerAbilityAvailable(GetOwningPlayer(this.u), 039;A000039;, true) //note that a function is called in a struct, this is legal; this call reactivates the spellbook if it wasn't active and if credits are now positive
endif
set this.statCredits=this.statCredits+statBonus //the real purpose of the method
endmethod
method BuyStat takes integer whichStat returns nothing //this method is called when a hero spends a stat credit; this will remove one stat credit and raise the stat that was bought
set this.statCredits=this.statCredits-1 //minus one credit...
if whichStat == 039;A001039; then //obviously the buy agility spell
call this.RaiseAgi()
endif
if whichStat == 039;A002039; then //buy int spell
call this.RaiseInt()
endif
if whichStat == 039;A003039; then //buy str spell
call this.RaiseStr()
endif
if this.statCredits < 1 then
call SetPlayerAbilityAvailable(GetOwningPlayer(this.u), 039;A000039;, false) //here we disable the spellbook if there are no more credits to spend
endif
endmethod
method RaiseAgi takes nothing returns nothing //encapsulation specifies that we let functions do as little as possible on their own
call SetHeroAgi(this.u, GetHeroAgi(this.u, false)+1, true) //the "false" says do not evaluate bonus agi; the "true" says do make it permanent
endmethod
method RaiseInt takes nothing returns nothing //no need for another empty line, all three of these methods do the same thing
call SetHeroInt(this.u, GetHeroInt(this.u, false)+1, true)
endmethod
method RaiseStr takes nothing returns nothing
call SetHeroStr(this.u, GetHeroStr(this.u, false)+1, true)
endmethod
method AddKill takes nothing returns nothing
set this.kills=this.kills+1
call this.StatBonusForKills(3) //aha, bonus stats for kills...
endmethod
method StatBonusForKills takes integer statBonusModifier returns nothing
if this.kills != 0 and ModuloInteger(this.kills, 10) == 0 then //we don't want any crashes due to dividing by zero, so we check if this.kills is 0 before checking its modulo
call this.ModifyStatCredits(this.KillsStatBonusModifier()) //encapsulation's a cur sometimes
endif
endmethod
method KillsStatBonusModifier takes nothing returns real
return Pow(this.kills/20) //.25 stats at 10, 1 stat at 20, 2.25 stats at 30, 4 extra stats at 40 kills
endmethod
endstruct
Note that by itself, this code does nothing. It just tells the game engine how much memory a "unitData" needs, and supplies methods to affect that memory. You need a function to create a unitData and call the methods or it's just a wasted 2 kb in your map file.
#L4# Calling structs in functions.
Now we get to the real stuff.
To attach a struct to a unit and be able to use it in other functions, all you do is assign the struct to the unit's custom value, but first you have to know how to create the struct. The following function will create a new hero with the Id given and attach a struct to it.
(I know, it leaks a unit variable, but it's a miniscule leak and unavoidable for this demonstration. )
JASS:
function CreateHero takes integer playerId, integer heroTypeId, real x, real y returns unit
local unit u=CreateUnit(playerId, heroTypeId, x, y, 270) //creates the hero and puts it in the variable to be returned
local unitData uD=unitData.create() //the only time you will put the full struct name on either side of the period; "create" is a standard constructor and you don't have to write this method
call SetUnitUserData(u, uD) //that's it; you just set the unit's custom value to the unitData you just created
endfunction
Remember how you used "this." to call methods and change data? In a function, you would replace "this" with the name you gave the struct when you created or copied it. The following function would be the action of a trigger fired when any hero on the map levels.
JASS:
function HeroLevelsUp takes nothing returns nothing
local unitData uD=GetUnitUserData(GetTriggerUnit()) //it's assumed every hero in the map has this struct attached
call uD.ModifyStatCredits(3) //all methods except "create" are called by the variable name, not the variable type, and if you wish to set member data in a function it is true for them, too, but this is bad practice, it's convention to use accessor functions to modify member data
endfunction
You now have everything you need to know to make a basic struct.
#O3#
Conclusion
#L5# Questions? Answers.Conclusion
For one, the syntax for using gamecaches can be bulky and confusing. They're also considered to use very slow functions. Like gamecaches, structs allow you to continuously redeclare variables of the same name like locals but still access these variables from any function you need to.Whats better at structs then at local vars?
#L6# Links and attachments.
Jass NewGen Pack v3d -- is necessary to use structs. It also includes bunches of other nifty features like a Jass parser that doesn't crash.
TESH for NewGen -- is a syntax highlighter for the above World Editor hack. It's almost as pretty and easy-to-use as JassCraft with the added bonus of writing directly into WE.
Wc3mapoptimizer 4.3 -- can reduce your map's size by a huge amount. A bit out of place in this tutorial but an amazingly useful tool nonetheless.
I've lost my CD-key for RoC. I can't log onto b.net or even open the world editor. If you have something to add to the tutorial, PM me and I'll read it as soon as I can.
#C#
Changelog
v1.01 Fixed some minor syntax errors in the struct declaration.Changelog