[Trigger] Custom Increment in Stats

Level 4
Joined
Dec 10, 2008
Messages
59
Hello Hive.

Once more, a dota-referenced question.

For those of you who play dota, the question is simple: How do I produce Ursa Warrior's Ulti? Or Barathrum's Empowering Haste's atk increment effect?

For those of you who don't, it means: How do I, via triggers, increase a unit's Attack stat? As of current, I do not see some kind of trigger that increases a unit's attack and displays it on the status bar.

Thanks for the help.
 
Level 3
Joined
Aug 9, 2008
Messages
60
REQUIREMENTS: JASS NEW GEN

You will need to create a few abilities. Firstly, copy the attack damage bonus ability from items section, make its object ID = 'DM+a', and set the damage bonus to +1. Now copy this item, set the object ID to 'DM+b', and set the damage bonus to +2. For the third time, you will copy the ability again, set the object ID to 'DM+c', and set the damage bonus to +4. I'm hoping that by now you've started to notice a patern, basically keep creating those abilities, progressing in the alphabet, and doubling the damage bonus. So the next few abilities ID's would be DM+d, DM+e, and damages +8, +16. Keep creating those abilities up to damage bonus of 256. This will let you set your units damage to anything from 0 up to 511.

This was the easy object editor part, time for some code.

Firstly, you will need to make a new trigger, after that convert it into text, and remove anything that's in there.

Now we'll type in "library DamageBonus", press enter, and type in "endlibrary."

We're finished with the library! Well not quite, it still needs a couple of things inside of it.

What your trigger should look like right now is:
JASS:
library DamageBonus
endlibrary

Next part to do is create a struct, which is simple. Firstly create the struct, the struct declaration is "struct" followed by a name (ex. struct DamageBonus), and ended with "endstruct"

Now declare a unit with a name of "obj", and an integer with a name of "dmg" inside of the struct.

Your code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    integer dmg
    unit obj
endstruct

endlibrary

Basically, now we have something to hold the units damage information in. You'll need to declare two new variables inside of your struct. Both of them are integer, name one pow, and the other cdmg. Make both of those last created variables into static variables. You do this by declaring the word "static" in front of them. Make the cdmg values equal to 'DM+a', and make pow equal to 8.

Right now your code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    integer         dmg = 0
    unit            obj = 0   
    static integer  pow = 8
    static integer cdmg = 'DM+a'
endstruct

endlibrary

What you want to do right now is make all of those variables private, you do so by placing the keyword "private" in front of them.

After you have made it private, create a static method called create, make it take a unit, and return DamageBonus.

Create a local DamageBonus, create it using allocate method, and let obj variable equal to the unit given, after you have done that, return the DamageBonus struct.

Right now your code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    private integer         dmg = 0
    private unit            obj = null
    private static integer  pow = 8
    private static integer cdmg = 'DM+a'
    
    static method create takes unit u returns DamageBonus
        local DamageBonus this = DamageBonus.allocate()
        set this.obj = u
        return this
    endmethod
    
endstruct

endlibrary

What you are going to do now, is creating a few method operators. You create them just like a regular method, but after the method keyword you place the word operator (ex. method operator damage takes nothing returns integer). You place the symbol "=" after the method name if you are adjusting a value, not receiving it (ex. method operator damage= takes integer s returns nothing).

Create a get method operator, call it object, and make it return the obj value. Create a duplicate set method, and instead of return the obj value, set obj value to the receiving value. Create two similar methods for dmg variable aswell.

Right now your code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    private integer         dmg = 0
    private unit            obj = null
    private static integer  pow = 8
    private static integer cdmg = 'DM+a'
    
    static method create takes unit u returns DamageBonus
        local DamageBonus this = DamageBonus.allocate()
        set this.obj = u
        return this
    endmethod
    
    method operator object takes nothing returns unit
        return .obj
    endmethod
    
    method operator object= takes unit s returns nothing
        set .obj = s
    endmethod
    
    method operator damage takes nothing returns integer
        return .dmg
    endmethod
    
    method operator damage= takes integer s returns nothing
        set .dmg = s
    endmethod
    
endstruct

endlibrary

We're almost done! All we need now is a method that will actually give the abilities to the units.

Create a method called update_dmg above all the method operator, but below the create method.

Make the last created method have 2 local integers, call one i, and the other num. Set i = .pow, and num = .dmg.

Now create an if statement, make sure that the num value is less than 512. If that's true, than return, and no else statement.

Create a loop, and for the exit statement place i < 0. Inside of the loop check if Pow( 2, i) is less then or equal to num. If so, add an ability to the .obj, for the ability ID use .cdmg + i. After the add function call, set num = num - R2I(Pow(2,i)). Now for the else statement, remove an ability from the object, for the ability id use .cdmg + i. After the if statement, set i to itself minus one. End the loop.

Right now your code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    private integer         dmg = 0
    private unit            obj = null
    private static integer  pow = 8
    private static integer cdmg = 'DM+a'
    
    static method create takes unit u returns DamageBonus
        local DamageBonus this = DamageBonus.allocate()
        set this.obj = u
        return this
    endmethod
    
    private method update_dmg takes nothing returns nothing
        local integer i = .pow
        local integer num = .dmg
        if num > 511 then
            return
        endif
        loop
            exitwhen i < 0
            if Pow(2,i) <= num then
                call UnitAddAbility( .obj, .cdmg + i )
                set num = num - R2I(Pow(2,i))
            else
                call UnitRemoveAbility( .obj, .cdmg + i )
            endif
            set i = i - 1
        endloop
    endmethod
    
    method operator object takes nothing returns unit
        return .obj
    endmethod
    
    method operator object= takes unit s returns nothing
        set .obj = s
    endmethod
    
    method operator damage takes nothing returns integer
        return .dmg
    endmethod
    
    method operator damage= takes integer s returns nothing
        set .dmg = s
    endmethod
    
endstruct

endlibrary

Now what you're gonna have to do, is go back to the method operator where you adjusted the damage value, and after the value is adjusted, call .update_dmg().

Only thing left to do, is create an onDestroy method. Inside the method, create a local integer, with the name of i, and set it's value to .pow.
Than create a loop, exitwhen i is less than 0. Inside of the loop remove each of the abilities on the unit. Set i to equal itself minus one. After the loop, set .obj to null.

Now our code should look like this:
JASS:
library DamageBonus

struct DamageBonus
    private integer         dmg = 0
    private unit            obj = null
    private static integer  pow = 8
    private static integer cdmg = 'DM+a'
    
    static method create takes unit u returns DamageBonus
        local DamageBonus this = DamageBonus.allocate()
        set this.obj = u
        return this
    endmethod
    
    private method update_dmg takes nothing returns nothing
        local integer i = .pow
        local integer num = .dmg
        if num > 511 then
            return
        endif
        loop
            exitwhen i < 0
            if Pow(2,i) <= num then
                call UnitAddAbility( .obj, .cdmg + i )
                set num = num - R2I(Pow(2,i))
            else
                call UnitRemoveAbility( .obj, .cdmg + i )
            endif
            set i = i - 1
        endloop
    endmethod
    
    method operator object takes nothing returns unit
        return .obj
    endmethod
    
    method operator object= takes unit s returns nothing
        set .obj = s
    endmethod
    
    method operator damage takes nothing returns integer
        return .dmg
    endmethod
    
    method operator damage= takes integer s returns nothing
        set .dmg = s
        call .update_dmg()
    endmethod

    private method onDestroy takes nothing returns nothing
        local integer i = .pow
        loop
            exitwhen i < 0
            call UnitRemoveAbility(.obj, .cdmg + i)
            set i = i - 1
        endloop
        set .obj = null
    endmethod
    
endstruct

endlibrary

The system is finished!

Now what we have to do to use it, is create a DamageBonus struct, and set the struct damage to whatever you want. You can even do so in a GUI trigger.

  • Untitled Trigger 001
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: local DamageBonus this = DamageBonus.create(some_unit)
      • Custom script: set this.damage=200
Now, there is one issue with this code, it will leak when the units die, so what we will have to do, is attach the structs to the units user data, create a group, and when a unit with the struct attached dies his struct will be destroyed.

Here is the final code with self clean-up type of thing enabled:
JASS:
library DamageBonus initializer init

globals
    unit some_unit
endglobals

struct DamageBonus
    private integer         dmg = 0
    private unit            obj = null
    private static integer  pow = 8
    private static integer cdmg = 'DM+a'
    static group            grp = null
    
    static method create takes unit u returns DamageBonus
        local DamageBonus this = DamageBonus.allocate()
        if IsUnitInGroup(u, .grp) then 
            debug call BJDebugMsg("Trying to create multiple damage bonuses for unit " + GetUnitName(.obj))
            return null
        endif
        call GroupAddUnit( .grp, u)
        set this.obj = u
        return this
    endmethod
    
    private method update_dmg takes nothing returns nothing
        local integer i = .pow
        local integer num = .dmg
        if num > 511 then
            return
        endif
        loop
            exitwhen i < 0
            if Pow(2,i) <= num then
                call UnitAddAbility( .obj, .cdmg + i )
                set num = num - R2I(Pow(2,i))
            else
                call UnitRemoveAbility( .obj, .cdmg + i )
            endif
            set i = i - 1
        endloop
    endmethod
    
    method operator object takes nothing returns unit
        return .obj
    endmethod
    
    method operator object= takes unit s returns nothing
        set .obj = s
    endmethod
    
    method operator damage takes nothing returns integer
        return .dmg
    endmethod
    
    method operator damage= takes integer s returns nothing
        set .dmg = s
        call .update_dmg()
    endmethod
    
    private method onDestroy takes nothing returns nothing
        local integer i = .pow
        if .obj == null then
            debug call BJDebugMsg("Trying to double release a struct attached to unit " + GetUnitName(.obj))
            return
        endif
        loop
            exitwhen i < 0
            call UnitRemoveAbility(.obj, .cdmg + i)
            set i = i - 1
        endloop
        call GroupRemoveUnit(.grp, .obj)
        set .obj = null
    endmethod
    
    static method onInit takes nothing returns nothing
        set .grp = CreateGroup()
    endmethod
    
endstruct

private function condition takes nothing returns boolean
    local DamageBonus d = GetUnitUserData(GetTriggerUnit())
    if IsUnitInGroup(d.object, d.grp) then
        return true
    endif
    return false
endfunction

private function action takes nothing returns nothing
    local DamageBonus d = GetUnitUserData(GetTriggerUnit())
    call d.destroy()
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t,Condition(function condition))
    call TriggerAddAction(t, function action)
endfunction

endlibrary

To use the system above you would have to add one more line to your GUI trigger, here is how it would look.

  • Untitled Trigger 001
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: local DamageBonus this = DamageBonus.create(some_unit)
      • Custom script: set this.damage=200
      • Custom script: call SetUnitUserData(some_unit, this)
 
Level 3
Joined
Aug 9, 2008
Messages
60
@Feroc1ty

thanks for the long post and help. I'll take some time off to explore it. i really appreciate your help =D

Tried to make it a tutorial, so you learn as you make it, but if you're just a person who just wants to use the code, I guess you can just copy+paste the last example code given. What you WILL have to do is create those abilities tho.

EDIT: If anyone wants me to explain any part of the code, you can post and ask.
 
Level 4
Joined
Dec 10, 2008
Messages
59
Tried to make it a tutorial, so you learn as you make it, but if you're just a person who just wants to use the code, I guess you can just copy+paste the last example code given. What you WILL have to do is create those abilities tho.

EDIT: If anyone wants me to explain any part of the code, you can post and ask.

to be honest, I pretty much don't understand the overall method of handling Jass/vJASS.

But with this, I guess I'll use it as a benchmark if I were to finally take that leap into JASS (but I haven't even mastered GUI to a degree...oh well)
 

Cokemonkey11

Code Reviewer
Level 26
Joined
May 9, 2006
Messages
3,395
to be honest, I pretty much don't understand the overall method of handling Jass/vJASS.

But with this, I guess I'll use it as a benchmark if I were to finally take that leap into JASS (but I haven't even mastered GUI to a degree...oh well)

Read the last function first, which is almost always an initializer. If you want to know how to do something in jass, convert a gui function and then look at the BJ's used for info on how the natives work.

I'd recommend looking at some proper tutorials for beginners though, as we could spend weeks explaining stuff to you if you learn in an improper order.
 
Level 4
Joined
Dec 10, 2008
Messages
59
Read the last function first, which is almost always an initializer. If you want to know how to do something in jass, convert a gui function and then look at the BJ's used for info on how the natives work.

I'd recommend looking at some proper tutorials for beginners though, as we could spend weeks explaining stuff to you if you learn in an improper order.

hmm, wokay.

on a totally different note, what does BJ stand for and what does it do?

Also, those object = null and some_unit things, i should replace it with my own Object ID?

oh yeah, that initializer init, i'm guessing the init means the trigger name to initialize the system?
 
Last edited:

Cokemonkey11

Code Reviewer
Level 26
Joined
May 9, 2006
Messages
3,395
BJ stands for blizzard.j function, which are all the functions blizzard made to make programming "easier," they simply perform actions using the natives. It's generally considered to avoid them, but some have a use at certain times over straight natives.

init refers to the function, trigger is usually created in the initializer function.
 
Level 3
Joined
Aug 9, 2008
Messages
60
hmm, wokay.

on a totally different note, what does BJ stand for and what does it do?

Also, those object = null and some_unit things, i should replace it with my own Object ID?

oh yeah, that initializer init, i'm guessing the init means the trigger name to initialize the system?

You will remove the global block, the some_unit variable was there to make sure code compiled, and you will have to create 8 or 9 abilities (i forgot) with the pattern that i showed you in the very start of the mini tutorial, you will start with ability id o DM+a, than go down into DM+b, DM+c etc... Base the ability off the weapon damage bonus ability from items.

EDIT: Toward "Cokemonkey11," I tend to place initialization functions at end of code for personal reasons :).
 
Level 4
Joined
Dec 10, 2008
Messages
59
You will remove the global block, the some_unit variable was there to make sure code compiled, and you will have to create 8 or 9 abilities (i forgot) with the pattern that i showed you in the very start of the mini tutorial, you will start with ability id o DM+a, than go down into DM+b, DM+c etc... Base the ability off the weapon damage bonus ability from items.

EDIT: Toward "Cokemonkey11," I tend to place initialization functions at end of code for personal reasons :).

i see...

another question: the event trigger for the system u posted is Map initialization, can i change it to "unit begins the effect of an ability" so it happens when I use a skill? (provided I can find the part where I can modify it to % of my hp/mp etc....)

and I just have to say this out: my first time ever typing a JASS code (thx to Feroc1ty), and it feels...awesome. Lol

EDIT: oh wait, the part where I can modify it to % of my hp/mp is the damage struct in the GUI trigger right?
 
Level 3
Joined
Aug 9, 2008
Messages
60
i see...

another question: the event trigger for the system u posted is Map initialization, can i change it to "unit begins the effect of an ability" so it happens when I use a skill? (provided I can find the part where I can modify it to % of my hp/mp etc....)

and I just have to say this out: my first time ever typing a JASS code (thx to Feroc1ty), and it feels...awesome. Lol

EDIT: oh wait, the part where I can modify it to % of my hp/mp is the damage struct in the GUI trigger right?

I don't think you comprehend how this system works... Look at the trigger example, you type in 3 custom lines of code per unit you want to have custom damage. Any unit can have it.
 
Level 6
Joined
Sep 5, 2007
Messages
264
I honestly don't see why people use a system of "damage +2, +4, +8, etc."
I created my own little "damage bonus" system, using +1, +10, +100, +1000, +10000. Each with 9 lvls. So, With 5 abilities, I can have any number between 1-99,999. With another ability, I'd have 999,999.

Can anyone tell me why people use the binary way instead of the decimal way? :confused:
 
Level 4
Joined
Dec 10, 2008
Messages
59
I don't think you comprehend how this system works... Look at the trigger example, you type in 3 custom lines of code per unit you want to have custom damage. Any unit can have it.

The part I don't get is, which line should I edit in order to make a (or any) unit have it?

EDIT: You said something about removing the global block, why should i do that?
 
Level 3
Joined
Aug 9, 2008
Messages
60
The part I don't get is, which line should I edit in order to make a (or any) unit have it?

EDIT: You said something about removing the global block, why should i do that?

Because it was for the test trigger, you set the system up by doing "Custom script: local DamageBonus this = DamageBonus.create(some_unit)", and replacing some_unit with the unit which you want it to be.

I honestly don't see why people use a system of "damage +2, +4, +8, etc."
I created my own little "damage bonus" system, using +1, +10, +100, +1000, +10000. Each with 9 lvls. So, With 5 abilities, I can have any number between 1-99,999. With another ability, I'd have 999,999.

Can anyone tell me why people use the binary way instead of the decimal way?

Each one of those abilities will add around +1 second of loading time, and you have five of them, so you're looking at at least 5 extra seconds of loading time for a simple damage system (whilst level one abilities will get widgetized into an slk and turn the loading time into around 0.05 seconds). Another reason this system is better, is because I have one macro that does the binary, so in one line of code (and a few object editor abilities) I can add a completely new stat, such as strength, agility, intelligence, etc...
 
Level 4
Joined
Dec 10, 2008
Messages
59
Because it was for the test trigger, you set the system up by doing "Custom script: local DamageBonus this = DamageBonus.create(some_unit)", and replacing some_unit with the unit which you want it to be.

i replaced it with (in my case) a unit with a rawcode ID of h000, i was wondering if I should replaced the null in obj with the same rawcode ID?
 
Level 6
Joined
Sep 5, 2007
Messages
264
Each one of those abilities will add around +1 second of loading time, and you have five of them, so you're looking at at least 5 extra seconds of loading time for a simple damage system (whilst level one abilities will get widgetized into an slk and turn the loading time into around 0.05 seconds). Another reason this system is better, is because I have one macro that does the binary, so in one line of code (and a few object editor abilities) I can add a completely new stat, such as strength, agility, intelligence, etc...

I see. I've used optimisers before, but I don't know how they work. What is the deal with SLKs? I understand that optimising the SLK reduces the loading time, but how? And, why won't it work on abilities with more than 1 level?
 
Level 3
Joined
Aug 9, 2008
Messages
60
I see. I've used optimisers before, but I don't know how they work. What is the deal with SLKs? I understand that optimising the SLK reduces the loading time, but how? And, why won't it work on abilities with more than 1 level?

Object editor crap is placed in a shitty way, what slk does is display the information better, so warcraft gets all of the information quicker. It works on abilities that are level 3 at most.
 
Level 6
Joined
Sep 5, 2007
Messages
264
Well, you could still cut the number of abilities down then, right? You use single level abilities, you could be using triple level, and have 1/3 the number of required abilities.
Tell me if I'm wrong here.
 
Level 4
Joined
Dec 10, 2008
Messages
59
Yes, the <gen> thing, or a variable to the unit.

hmm, then i have another question.

since I've never understood what exactly those <gen> things were for, I tend to get dumbfounded when people use them.

correct me if I'm wrong, but the <gen> thing is only created when that unit is in a map initially, right?

So, if, per se, I include this skill for a hero in an AoS style map, which means I have to purchase that hero, I would I know what is its "name: #### <gen>"?

:con:

EDIT: a variable to the unit means...? Setting a unit-type variable or something in the GUI trigger that equals to "my unit"?
 
Level 3
Joined
Aug 9, 2008
Messages
60
hmm, then i have another question.

since I've never understood what exactly those <gen> things were for, I tend to get dumbfounded when people use them.

correct me if I'm wrong, but the <gen> thing is only created when that unit is in a map initially, right?

So, if, per se, I include this skill for a hero in an AoS style map, which means I have to purchase that hero, I would I know what is its "name: #### <gen>"?

:con:

EDIT: a variable to the unit means...? Setting a unit-type variable or something in the GUI trigger that equals to "my unit"?

You'll have to have a trigger that checks when a unit is bought, than set that unit up with the damage struct.

  • asd
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
    • Actions
      • Custom script: local DamageBonus this = DamageBonus.create(GetTriggerUnit())
      • Custom script: call SetUnitUserData(GetTriggerUnit(),this)

I just don't comprehend how you're going to make a competent AoS without any knowledge of triggering whatsoever.
 
Level 4
Joined
Dec 10, 2008
Messages
59
You'll have to have a trigger that checks when a unit is bought, than set that unit up with the damage struct.

  • asd
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
    • Actions
      • Custom script: local DamageBonus this = DamageBonus.create(GetTriggerUnit())
      • Custom script: call SetUnitUserData(GetTriggerUnit(),this)

I just don't comprehend how you're going to make a competent AoS without any knowledge of triggering whatsoever.

lol i'm not making any.

i'm just asking should it happen in the above event, how would i go about on it.
 
Level 6
Joined
Sep 5, 2007
Messages
264
Seriously though, why use binary? Being a european i'm more used to decimal, and it can be used easier generally, especially with a +5 to simplify it.
As it was stated to me above, using abilities with 3 or less levels means that they can be optimised for loading doing it my original way increases map loading time (by approximately 50x to 100x faster). But, if you really don't care about loading times, then feel free to use decimal instead of binary :thumbs_up:
 
Top