1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[vJASS] Starting to understand structs by making this system. Could use some advice!

Discussion in 'Triggers & Scripts' started by Sephalo, Aug 12, 2015.

  1. Sephalo

    Sephalo

    Joined:
    Oct 12, 2007
    Messages:
    1,791
    Resources:
    0
    Resources:
    0
    Hey there.

    I'm currently beginning on a system that's required for my map.
    Well 'system' might be a big word, but it's a function that's going to be pretty common in the game itself. I'll explain what it is first.

    There are Heros in the game that are inspiring other units with a specific ability. This means that the ability will be given to other units in range of the Hero, exactly like an aura. Let's say Keeper of the Grove has 'Inspire: Force of Nature'. Then this system should give units within his range the same ability, and remove that ability when they go out of range, or when Keeper of the Grove dies.
    (Ignoring the fact that Force of Nature is a hero ability, and the others are units. In my map all abilities are simpel unit abilities.)

    Now the tricky thing is that if a unit comes within range of the Keeper of the Grove, and he has the Force of Nature ability already, nothing should happen. But when that unit leaves range again, he should NOT lose the ability.


    Now I got to understand that using a struct here would be a good way to handle it, so that's why I started trying to understand structs.
    A good friend of mine made me a Floating Combat Text system once, and that's where I saw a basic struct being used. I'd like to base my self-made system on this way of coding so that I have an 'example' to look at.

    Example Code I'm using
    Code (vJASS):
    library CombatText initializer init needs DamageSystem

    globals
        private string S
    endglobals

    private struct Data
        texttag t
        string s
        real size
        real duration
        boolean bool
       
        static integer array Ar
        static integer Total = 0
        static timer Time = CreateTimer()

        static method create takes texttag t, string s returns Data
            local Data Dat = Data.allocate()

            set Dat.t = t
            set Dat.s = s
            set Dat.size = 0.019
            set Dat.bool = false
            set Dat.duration = 0
           
            if Dat.Total == 0 then
                call TimerStart(Dat.Time,.03,true,function Data.Loop)
            endif
           
            set Dat.Ar[Dat.Total] = Dat
            set Dat.Total = Dat.Total + 1
           
            return Dat
        endmethod

        static method Loop takes nothing returns nothing
            local Data Dat
            local integer i = 0
           
            loop
                exitwhen i >= Dat.Total
                set Dat = Dat.Ar[i]
               
                set Dat.duration = Dat.duration + 0.03
                if not Dat.bool then
                    set Dat.size = Dat.size + 0.0012
                elseif Dat.bool and Dat.size >= 0.022 then
                    set Dat.size = Dat.size - 0.0012
                endif
                if Dat.size >= 0.0285 then
                    set Dat.bool = true
                endif
                call SetTextTagText(Dat.t,Dat.s,Dat.size)
               
                if Dat.duration >= 2. then
                    set Dat.Total = Dat.Total - 1
                    set Dat.Ar[i] = Dat.Ar[Dat.Total]
                    set i = i - 1
                    call Dat.destroy()
                endif
               
                set i = i + 1
            endloop
        endmethod
       
    endstruct

    private function Actions takes nothing returns boolean
        local texttag T
        local unit u
        local unit t
        local integer dt = GetTriggerDamageType()
        local real d
        local integer R
        local integer B
        local integer G
        local real Size
        local integer id
        local integer idt
        local integer i
        local Data Dat

    if dt != DAMAGE_TYPE_EXTRA then
        set T = CreateTextTag()
        set u = GetTriggerDamageSource()
        set t = GetTriggerDamageTarget()
        set d = GetTriggerDamage()
        set id = GetPlayerId(GetOwningPlayer(u))
        set idt = GetPlayerId(GetOwningPlayer(t))
        set Size = .019
       
        if d != 0.00 then
            if dt == DAMAGE_TYPE_ATTACK then
                call SetWidgetLife(t,GetWidgetLife(t)+d)
            elseif dt == DAMAGE_TYPE_PHYSICAL then
                set S = I2S(R2I(d))
                call Dat.create(T,S)
                set R = 191
                set B = 0
                set G = 0
                call SetTextTagVelocity(T, 0, .0277 )
            elseif dt == DAMAGE_TYPE_MAGIC_HOLY then
                set S = I2S(R2I(d))
                call Dat.create(T,S)
                set R = 242
                set B = 57
                set G = 187
                call SetTextTagVelocity(T, 0, .0277 )
            elseif dt == DAMAGE_TYPE_MAGIC_UNHOLY then
                set S = I2S(R2I(d))
                call Dat.create(T,S)
                set R = 191
                set B = 191
                set G = 0
                call SetTextTagVelocity(T, 0, .0277 )
            endif
           
            call SetTextTagVisibility(T, true)
            call SetTextTagVisibility(T, false)
           
            call SetTextTagColor(T,R,G,B, 255 )
            call SetTextTagText(T,S,Size)
            call SetTextTagPosUnit(T,t, 0 )
            call SetTextTagPermanent(T, false)
            call SetTextTagLifespan(T, 2)
           
            set u = null
            set t = null
            set T = null
       
        endif
    endif
       
        return false
    endfunction

    public function init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerAddCondition(trg, Condition(function Actions))
        call TriggerRegisterDamageEvent(trg)
        set trg = null
    endfunction

    endlibrary


    I just see that as soon as
    Code (vJASS):
    local Data Dat
    is being called in the actions function, apperantly the struct gets some information sent to itself so that it can start looping for every specific case of (in this case) floating text.
    I'm not sure how to understand what that line
    Code (vJASS):
    local Data Dat
    exactly does and how I should interpret it.

    Would anyone like to give me some advice or info on this?
     
    Last edited: Aug 12, 2015
  2. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,033
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    local Data dat
    declares a local variable called dat of type Data but does not assign a value. Infact your snippet does not create a struct instance at all.

    @aura question: You can tag nearby units as "having the buff/ability" and assign them the source (Keeper of the Grove), so you can prevent other sources to repeat the effect as well as filter the ending condition. Or you make something like a ref count (count the sources a unit is affected by) but only add/remove the effect on first/last ref.
     
  3. Sephalo

    Sephalo

    Joined:
    Oct 12, 2007
    Messages:
    1,791
    Resources:
    0
    Resources:
    0
    My bad. I copied it wrong. Now there's a "call Dat.create(T,S)" line there too. And with that, I also understand it a bit better now haha.

    And about the aura thing. I was just thinking of creating a unit group for the source, and every time a unit (that doesn't have the shared ability from himself yet) enters range he will join the unit group and get the ability. Then whenever a unit from the group leaves range it will also lose the ability.
    Would that be a good way of doing this? Or is it bad to potentially end up with loads of unit groups at the same time because (maybe) they're a big heavy or something?
     
  4. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,033
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Rather than enter/leave range, it should be a periodic check because the aura ranges may overlap and you do want to have the 2nd aura take over when the unit leaves the 1st one but already is within the radius of the 2nd one at that time, do you not?

    The unit group is not a problem but not attaching the source unit to the target unit directly does implicate that if for example the target dies, you cannot directly access the aura caster to remove it from the unit group for example. You would have to iterate over all aura casters to find where the target is a member of or wait until the next check done by the caster itself.
     
  5. Sephalo

    Sephalo

    Joined:
    Oct 12, 2007
    Messages:
    1,791
    Resources:
    0
    Resources:
    0
    Well it's a bit complicated, but units wont leave/enter range by itself in the map. Units are stationary and only move with triggers, and if units near an 'aura' unit would move they would move together with the aura unit, so all in all I think it's no problem to add/remove units to a group whenever they enter/leave the hero's range since I can easily trigger that in the way the map works.
    In the map all units have a specific unit Id, so maybe I could use that in a way to connect it to an aura group or something? To check out which group belongs to which aura-unit? Hmmm...
     
  6. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,789
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    Aah, the age old custom aura problem...

    I want to be completely honest here: the "naive" approach of having periodic enumerations and a group is more or less the best solution in this case.

    Any further optimizations you could do on that go into diminishing returns, as:
    - the enter range event is more or less just a periodic enumeration with a fixed interval anyway (unlike 'unit enters rect', this one is not instant!)
    - you need to loop through all units anyway to compare the range towards your aura emitting units periodically to detect "leave range" dynamically, unless you use object data auras


    Tbh, I'd definitely use object data auras for this. It gets rid of the tracking and the distance calculations. You just check for the buff icon to exist periodically and add/remove your ability on demand. Any additional triggering would just make it slower.
     
  7. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,641
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Structs are simply a book from a library.
    Structs can have static functions which can be used like any other functions (but are just called using the name of the struct with a dot just before the function call).

    An instance of a struct (when you have called <StructName>.allocate()) creates "new" variables that are placed inside that struct.
    In the case of "Data", the instance would "create":
    Code (vJASS):
    texttag t
    string s
    real size
    real duration
    boolean bool

    One texttag, one string, two reals and a boolean.
    (Static variables are like globals with the same rule as with static functions.)

    Via that instance, you can call non-static functions declared inside the struct.
    However there are no functions so all what this struct does is create some data.
    The problem is that it doesnt "create" new variables, it just makes a new index because those texttag, string, reals and boolean are actually an array.
    The "local Data dat = Data.allocate()" is actually an integer used in that array.

    That is simply explained what a struct is.
    And as far as I know, there is only one real use for structs that I have found in all the time I have been here.
    Creating a static event with response without a trigger. (Ty muzzel.)



    About your aura problem... If you give the ability yourself using group enumerations and giving the ability, you can also set a source of the "aura buff". (Which is impossible with normal auras afaik.)
    When your unit is away from it's source (when it is out of range, when the source is dead, when whatever), you remove the effect.
    Immediately after that, you give the new buffs to units near the existing sources that havent got that buff yet.

    So triggerwise:
    trigger buffs
    - Every ... second of gametime.
    - Loop for group of units with buff.
    --- check if source is still active (should still give the buff).
    --- if not, then remove buff.
    - Loop for group of units known as source.
    --- check for units near and give buffs.
    endtrigger