• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

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

Status
Not open for further replies.
Level 19
Joined
Oct 12, 2007
Messages
1,821
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.

JASS:
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
JASS:
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
JASS:
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:
Level 26
Joined
Aug 18, 2009
Messages
4,097
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.
 
Level 19
Joined
Oct 12, 2007
Messages
1,821
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.

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?
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
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?

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.
 
Level 19
Joined
Oct 12, 2007
Messages
1,821
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.

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...
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
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.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
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":
JASS:
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
 
Status
Not open for further replies.
Top