HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Jass Attribute System Request

11-22-2010, 01:13 PM#1
SHURA_BR
I'm trying to learn some Jass, but I think this system is currently beyond my capabilities. More specifically, I'm trying to make a system in which:
-Each of three characters has 4 custom attributes (the same four for all characters)
-Each attribute gives bonus damage, defense etc. based on a different formula depending on the character.
-Armor absorbs an integer amount of damage (Damage = Damage - (Armor/2)).

If anyone is interested in helping me, I can fill you in on more details.
11-22-2010, 05:40 PM#2
Anitarf
Note: the following code is in vJass, I hope that is all right for you because you will find it difficult to find anyone still working in plain JASS.

Let's start with the attributes themselves. This is a fairly simple task, we just need to store a few integer values on a per-unit basis. AutoIndex is useful for this sort of thing:
Collapse JASS:
library Attribute requires AutoIndex
    //! textmacro Attribute takes ATTRIBUTE
    globals
        private integer array $ATTRIBUTE$
    endglobals

    private function GetUnit$ATTRIBUTE$ takes unit u returns integer
        return $ATTRIBUTE$[GetUnitId(u)]
    endfunction
    private function SetUnit$ATTRIBUTE$ takes unit u, integer a returns nothing
        set $ATTRIBUTE$[GetUnitId(u)] = a
    endfunction
    //! endtextmacro

    // This is an example using WC3 attributes, you will declare your four attributes here isntead.
    //! runtextmacro Attribute("Strength")
    //! runtextmacro Attribute("Agility")
    //! runtextmacro Attribute("Intelligence")
endlibrary

Now, we could also add functions that get and set default attributes for each unit type and then automatically initialize those attributes whenever a unit enters the map, but since you have only three units you want to use this for we might as well hardcode it instead:

Collapse JASS:
library DefaultAttribute initializer Init requires Attribute, AutoIndex
    private function InitAttributes takes unit u returns nothing
        local integer i = GetUnitTypeId(u)
        if i=='u000' then // replace 'u000' with your character unit's rawcode.
            call SetUnitStrength(u, 15) // This is a function from the attribute system.
            // Repeat the above line for all four attributes.
        elseif i=='u001' then
            // Repeat for all units that use attributes.
        endif
    endfunction
    private function Init takes nothing returns nothing
        call OnUnitIndexed( InitAttributes )
    endfunction
endlibrary

For the actual damage/armour/life bonuses, we will use BonusMod. You didn't specify whether an attribute can modify multiple bonuses or if multiple attributes can affect the same bonus, so I will make the code as open as possible to allow for such things. Whenever any of the attributes changes, we need to update all the unit's bonuses accordingly, so we will need to add support for that to the Attribute library:

Collapse JASS:
library Attribute requires AutoIndex

    public function interface Update takes unit u returns nothing
    globals
        private Update array U
        private integer N=0
    endglobals

    private function AttributeUpdate takes unit u returns nothing
        local integer i=0
        loop
            exitwhen i==N
            call U[i].execute(u)
        endloop
    endfunction

    function OnAttributeUpdate takes Update u returns nothing
        set U[N]=u
        set N=N+1
    endfunction

    //! textmacro Attribute takes ATTRIBUTE
    globals
        private integer array $ATTRIBUTE$
    endglobals

    private function GetUnit$ATTRIBUTE$ takes unit u returns integer
        return $ATTRIBUTE$[GetUnitId(u)]
    endfunction
    private function SetUnit$ATTRIBUTE$ takes unit u, integer a returns nothing
        set $ATTRIBUTE$[GetUnitId(u)] = a
        call AttributeUpdate(u)
    endfunction
    //! endtextmacro

    // This is an example using WC3 attributes, you will declare your four attributes here isntead.
    //! runtextmacro Attribute("Strength")
    //! runtextmacro Attribute("Agility")
    //! runtextmacro Attribute("Intelligence")
endlibrary

And now, the AttributeBonus library:

Collapse JASS:
library AttributeBonus requires Attribute, AutoIndex, BonusMod
    globals
        // I will use 2D arrays to store attribute bonus data for each unit.
        // We must set the 2D array size big enough to avoid bugs.
        // The values set here should be sufficient, while still being small enough to not require multiple arrays.
        private constant integer MAX_UNIT_ID=400
        private constant integer MAX_BONUS_ID=20
        private integer array bonus [MAX_UNIT_ID][MAX_BONUS_ID]
    endglobals

    function SetUnitAttributeBonus takes unit u, Bonus bonusType, integer amount returns nothing
        local integer id=GetUnitId(u)
        if bonus[id][integer(bonusType)] != amount then
            // Using AddUnitBonus instead of SetUnitBonus allows the attribute bonuses to stack with bonuses from other sources:
            call AddUnitBonus(u, bonusType, amount-bonus[id][integer(bonusType)])
            set bonus[id][integer(bonusType)]=amount
        endif
    endfunction
endlibrary

The last thing we need is to actually calculate the bonuses based on attributes, I will add this functionality to the DefaultAttribute library:

Collapse JASS:
library DefaultAttribute initializer Init requires Attribute, AttributeBonus, AutoIndex


    private function InitAttributes takes unit u returns nothing
        local integer i = GetUnitTypeId(u)
        if i=='u000' then // replace 'u000' with your character unit's rawcode.
            call SetUnitStrength(u, 15) // This is a function from the attribute system.
            // Repeat the above line for all four attributes.
        elseif i=='u001' then
            // Repeat for all units that use attributes.
        endif
    endfunction

    private function UpdateAttributeBonus takes unit u returns nothing
        local integer i = GetUnitTypeId(u)
        local integer damage
        local integer armor // I added local variables for bonuses to make the code more readable.

        if i=='u000' then // replace 'u000' with your character unit's rawcode.
            set armor =  R2I(GetUnitStrength(u)*0.1+GetUnitAgility(u)*0.25)
            set damage = R2I(GetUnitAgility(u)*0.2+GetUnitIntelligence(u)*0.5)
        elseif i=='u001' then
            // Repeat for all units that use attributes.
        endif
        call SetUnitAttributeBonus(u, BONUS_ARMOR, armor)
        call SetUnitAttributeBonus(u, BONUS_DAMAGE, damage)
    endfunction

    private function Init takes nothing returns nothing
        call OnUnitIndexed( InitAttributes )
        call OnAttributeUpdate( UpdateAttributeBonus )
    endfunction
endlibrary


That should cover your attributes. Now, to make armour function differently, we must first disable the default armour functionality in the gameplay constants. Then, we need to write our own armour library using damage modifiers. I'll leave that for another post, though, try to first get the attribute system to work. If there are any errors in the code I posted, post about it here or you can also find me on the wc3c irc channel.
11-23-2010, 09:00 PM#3
SHURA_BR
Wow, I didn't expect to get a reply so soon! Thanks a lot, but I'm afraid it'll take until next friday until I have some spare time to code. I'll try to make the system work and then I'll post it here when I'm done (or completely bugging it).