• 🏆 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!

[Solved] Problem with imbricated structures

Status
Not open for further replies.
Level 17
Joined
Oct 10, 2011
Messages
462
I got problems with my spell. Here it is an aura that increases the MaxHp of nearby allies. There is 2 structs Data and Buff.

Data checks the unit that are around the owner of the buff
Buff is a struct for each allies that got the bonus of life

To write the code clearly I made an attribute d on the Buff structure that contains the Data structure of the Aura.

I got the following error :

upload_2018-3-27_19-40-50.png


I think the problem is that the struct Data is below the struct Buff so when there is a call from one to te other one there is the bug.
I changed the order of the 2 structs and I had the same bug but for the calls from Data to Buff.

vJASS:
scope TaurenForce initializer Init_TaurenForce
    globals
        private constant integer BUFF_ID = 'AAAA'

        private constant real AREA = 900
        private constant real INTERVAL = 0.5

        private constant real INTERVAL_BUFF = 1
    endglobals

private function getBonusLife takes real lvl returns real
    return 100 * lvl
endfunction

private struct Buff
    static Buff array B
    static integer BT = 0
    static timer T = null
  
    unit target
    Data d
    real currentLvl
    boolean state

    private method onDestroy takes nothing returns nothing
        local real bonusLife = getBonusLife(.currentLvl)

        call GroupRemoveUnit(.d.G1, .target)  // <== THE BUG IS ON THAT LINE

        call UnitRemoveAbility(.target, BUFF_ID)

        call SetUnitMaxHP(.target, (GetUnitMaxHP(.target) - bonusLife))

        if GetUnitState(.target, UNIT_STATE_LIFE) > bonusLife then
            call regenLife(.target, -bonusLife)
        else
            call SetUnitState(.target, UNIT_STATE_LIFE, 1)
        endif

        set .target = null

    endmethod

    private method checksAura takes nothing returns nothing
        local real X = GetUnitX(.d.caster)
        local real Y = GetUnitY(.d.caster)
        local real X1 = GetUnitX(.target)
        local real Y1 = GetUnitY(.target)
        local real finalBonus = getBonusLife(.d.lvl)
        local real currentBonus = getBonusLife(.currentLvl)

        if distancePoints(X, Y, X1, Y1) > AREA then
            set .state = false
        else
            if currentBonus != finalBonus then
                set .currentLvl = .d.lvl

                call SetUnitMaxHP(.target, (GetUnitMaxHP(.target) + finalBonus - currentBonus))

                call regenLife(.target, (finalBonus - currentBonus))
            endif
        endif

    endmethod

    static method update takes nothing returns nothing
        local Buff b
        local integer I = 0

        loop
            set I = I + 1
            set b = .B[I]

            if isAlive(b.target) then
                if isAlive(b.d.caster) then
                    call b.checksAura()
                else
                    set b.state = false
                endif
            else
                if not(IsUnitType(b.target, UNIT_TYPE_HERO)) then
                    set b.state = false
                endif
            endif

            if not(b.state) then
                call b.destroy()

                set .B[I] = .B[.BT]
                set .BT = .BT - 1
                set I = I - 1
            endif

            exitwhen I >= .BT
        endloop

        if .BT <= 0 then
            call PauseTimer(.T)
            set .BT = 0
        endif

    endmethod
  
    static method addBuff takes unit U1, real lvl, Data d returns nothing
        local Buff b = Buff.create()

        set b.target = U1
        set b.currentLvl = lvl
        set b.d = d
        set b.state = true

        set .BT = .BT + 1
        set .B[.BT] = b
      
        if .BT == 1 then
            call TimerStart(.T, INTERVAL_BUFF, true, function Buff.update)
        endif

    endmethod
endstruct

private struct Data
    static Data array D
    static integer DT = 0
    static timer T = null
    static group G = null

    unit caster
    group G1
    real lvl

    private method onDestroy takes nothing returns nothing
        call DestroyGroup(.G1)

        set .caster = null
        set .G1 = null

    endmethod

    private method applyBuff takes nothing returns nothing
        local player P = GetOwningPlayer(.caster)
        local unit U2
        local real bonusLife = getBonusLife(.lvl)

        call GroupEnumUnitsInRange(.G, GetUnitX(.caster), GetUnitY(.caster), AREA, null)

        loop
            set U2 = FirstOfGroup(.G)
            exitwhen U2 == null
            call GroupRemoveUnit(.G, U2)

            if isAlive(U2) and IsUnitAlly(U2, P) and not(isBuilding(U2)) and IsUnitInGroup(U2, .G1) then
                call GroupAddUnit(.G1, U2)

                call Buff.addBuff(U2, lvl, this)

                call SetUnitMaxHP(U2, GetUnitMaxHP(U2) + bonusLife)
                call regenLife(U2, bonusLife)
            endif

            set U2 = null
        endloop

        set P = null

    endmethod

    static method update takes nothing returns nothing
        local Data d
        local integer I = 0
        local integer currentLvl

        loop
            set I = I + 1
            set d = .D[I]

            if isAlive(d.caster) then
                set currentLvl = GetUnitAbilityLvl(d.caster, BUFF_ID)

                if d.lvl != currentLvl then
                    set d.lvl = currentLvl
                endif

                call d.applyBuff()
            endif

            exitwhen I >= .DT
        endloop
    endmethod

endstruct

function Trig_TaurenForce_Conditions takes nothing returns boolean
    return GetLearnedSkill() == 'A020' and GetUnitAbilityLevel(GetTriggerUnit(), 'A020' ) == 1
endfunction

function Trig_TaurenForce_Actions takes nothing returns nothing
    local unit U = GetLearningHero()
    local real lvl = I2R(GetUnitAbilityLevel(U, GetLearningHeroSkill()))
    local Data d

    set d = Data.create()
    set d.caster = U
    set d.G1 = CreateGroup()
    set d.lvl = 1

    set Data.DT = Data.DT + 1
    set Data.D[Data.DT] = d

    if Data.DT == 1 then
        call TimerStart(Data.T, INTERVAL, true, function Data.update)
    endif

    set U = null
  
endfunction

//===========================================================================
function Init_TaurenForce takes nothing returns nothing
    local trigger T = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( T, EVENT_PLAYER_HERO_SKILL )
    call TriggerAddCondition( T, Condition( function Trig_TaurenForce_Conditions ) )
    call TriggerAddAction( T, function Trig_TaurenForce_Actions )
  
    set Data.T = CreateTimer()
    set Data.G = CreateGroup()
  
    set Buff.T = CreateTimer()
  
    set T = null
  
endfunction

endscope

I hope someone can help me
 
Last edited:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Since Data is a private member of a scope and you are declaring it above its definition, you should let the jasshelper know that it is private. Actually if you really think about think, the bug is not on the line that jasshelper mentioned, but here Data d when you declare a variable d of type Data. Since that is above the definition of your Data struct, then the parser doesn't know yet at that point that Data is actually private to the scope and so doesn't know that the Data you are referring to should have a proper prefix for the name.
To solve this, just add the line private keyword Data atop the struct Buff to let jasshelper know that the name Data should have a proper prefix ;).
 
Status
Not open for further replies.
Top