• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] [Solved] How to use struct with GUI UNIT EVENT?

Status
Not open for further replies.
Level 9
Joined
May 12, 2018
Messages
145
My purpose is to spawn ELEMENTUNIT immediately when structures called FORGESITEs are pre-placed on map or when I build them.
And, ELEMENTUNIT must also die together when FORGESITE dies.

The following is a script written for the test.

JASS:
scope ForgeSiteDevastator initializer init

    globals
        private constant integer ELEMENTUNIT = 'n033'
        private constant integer FORGESITE = 'o013'
        private constant real HEIGHT = 140
        private constant real RATED = 100
        private constant real FACING = 315

    endglobals
 
    private struct Data
   
        integer id
        unit source
        unit element
   
        method destroy takes nothing returns nothing
            call deallocate()
            call KillUnit(element)
            set source = null
            set element = null
        endmethod
   
        static method create takes unit du returns Data
            local Data D = Data.allocate()
       
            set D.source = du
            set D.element = CreateUnit(GetOwningPlayer(D.source), ELEMENTUNIT, GetUnitX(D.source), GetUnitY(D.source), FACING)
       
       
            return D
        endmethod
   
    endstruct
 
    private function Action1 takes nothing returns nothing
        local unit forge = udg_UDexUnits[udg_UDex]
        local Data D = GetUnitUserData(forge)
   
        call D.destroy()
    endfunction
 
    private function Condition0 takes nothing returns boolean
        return udg_UnitTypeOf[udg_UDex] == FORGESITE
    endfunction

    private function Action0 takes nothing returns nothing
        local unit forge = udg_UDexUnits[udg_UDex]
        local Data D = Data.create(forge)
   
        set forge = null
    endfunction

    private function init takes nothing returns nothing
        local trigger t0 = CreateTrigger()
        local trigger t1 = CreateTrigger()

        call TriggerRegisterVariableEvent( t0, "udg_UnitIndexEvent", EQUAL, 1.00 )
        call TriggerAddCondition(t0, Condition(function Condition0) )
        call TriggerAddAction( t0, function Action0 )

        call TriggerRegisterVariableEvent( t1, "udg_DeathEvent", EQUAL, 1.00 )
        call TriggerAddCondition( t1, Condition(function Condition0) )
        call TriggerAddAction( t1, function Action1 )
 
        set t0 = null
        set t1 = null
    endfunction

endscope
[CODE=JASS]

The DeathEvent part is currently not working for my purpose. Creating ELEMENTUNIT for each FORGESITE does not seem to make a problem (so far), but, When FORGESITE dies, ELEMENTUNIT does not die at the same time.
I would appreciate it more if you could provide me with checking a memory leak problem or solutions to it too.
 
Last edited:
JASS:
    private function Action1 takes nothing returns nothing
        local unit forge = udg_UDexUnits[udg_UDex]
        local Data D = GetUnitUserData(forge)
        call D.destroy()
    endfunction
First off. Your naming makes it hard to read, that's why I skipped it at first. Consider naming stuff like "OnForgeSiteDeath" and "OnForgerSiteSpawn" instead of Action0 and Action1.
But the issue you have it that you references the Data by the unit index. The Data-struct internal identifier starts at 1 and counts upwards on each allocate and down on each destroy. You unit index can be "whatever" (12, or 4711), and probably does not match this Data-struct internal identifier, and this is your problem.

What you need is some way of mapping you struct to the unit id.

In the Data struct, add a public static Data array dataArray. This is a place to store the "mapping" that I mentioned.
Then in the create, store this mapping by:

In the create, do:
JASS:
set dataArray[udg_UDex] = D
Then in the Action1 you can do something like:
JASS:
    private function Action1 takes nothing returns nothing
        local Data D = Data.dataArray[udg_UDex]
        call D.destroy()
    endfunction
 
Level 9
Joined
May 12, 2018
Messages
145
First off. Your naming makes it hard to read, that's why I skipped it at first. Consider naming stuff like "OnForgeSiteDeath" and "OnForgerSiteSpawn" instead of Action0 and Action1.
But the issue you have it that you references the Data by the unit index. The Data-struct internal identifier starts at 1 and counts upwards on each allocate and down on each destroy. You unit index can be "whatever" (12, or 4711), and probably does not match this Data-struct internal identifier, and this is your problem.

What you need is some way of mapping you struct to the unit id.

In the Data struct, add a public static Data array dataArray. This is a place to store the "mapping" that I mentioned.
Then in the create, store this mapping by:

In the create, do:
JASS:
set dataArray[udg_UDex] = D
Then in the Action1 you can do something like:
JASS:
    private function Action1 takes nothing returns nothing
        local Data D = Data.dataArray[udg_UDex]
        call D.destroy()
    endfunction
I wrote data arrays as you guided, but this Death part does not work well. In some cases, linked units do not die. I think the array is twisted.
I might not have understood it well.
Is there a good example or something?
JASS:
scope ForgeSiteDevastator initializer init

    globals
        private constant integer ELEMENTUNIT = 'n033'
        private constant integer FORGESITE = 'o013'
        private constant real FACING = 315

    endglobals
 
    private struct Data
     
        public static Data array dataArray
        unit source
        unit element
     
        method destroy takes nothing returns nothing
            call KillUnit(element)
            call deallocate()
         
            set source = null
            set element = null
        endmethod
     
        static method create takes unit du returns Data
            local Data D = Data.allocate()

            set D.dataArray[udg_UDex] = D
            set D.source = du
            set D.element = CreateUnit(GetOwningPlayer(D.source), ELEMENTUNIT, GetUnitX(D.source), GetUnitY(D.source), FACING)
         
         
            return D
        endmethod
     
    endstruct
 
    private function ForgeDeath takes nothing returns nothing
        local unit forge = udg_UDexUnits[udg_UDex]
        local Data D = Data.dataArray[udg_UDex]
     
        call D.destroy()
        set forge = null
    endfunction
 

    private function ForgeSpawn takes nothing returns nothing
        local unit forge = udg_UDexUnits[udg_UDex]
        local Data D = Data.create(forge)

        set D.dataArray[udg_UDex] = D
     
        set forge = null
    endfunction

    private function ForgeUpgraded takes nothing returns nothing
        local unit forge = GetTriggerUnit()
        local Data D = Data.create(forge)

        set D.dataArray[udg_UDex] = D

        set forge = null
    endfunction

    private function CondForgeUDex takes nothing returns boolean
        return udg_UnitTypeOf[udg_UDex] == FORGESITE
    endfunction

    private function CondForgeId takes nothing returns boolean
        return GetUnitTypeId(GetTriggerUnit()) == FORGESITE
    endfunction

    private function init takes nothing returns nothing
        local trigger t0 = CreateTrigger()
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()

        call TriggerRegisterVariableEvent( t0, "udg_UnitIndexEvent", EQUAL, 1.00 )
        call TriggerAddCondition(t0, Condition(function CondForgeUDex) )
        call TriggerAddAction( t0, function ForgeSpawn )

        call TriggerRegisterVariableEvent( t1, "udg_DeathEvent", EQUAL, 1.00 )
        call TriggerAddCondition( t1, Condition(function CondForgeUDex) )
        call TriggerAddAction( t1, function ForgeDeath )
 
        call TriggerRegisterAnyUnitEventBJ( t2, EVENT_PLAYER_UNIT_UPGRADE_FINISH )
        call TriggerAddCondition( t2, Condition(function CondForgeId) )
        call TriggerAddAction( t2, function ForgeUpgraded )
        set t0 = null
        set t1 = null
        set t2 = null
    endfunction

endscope
 
Last edited:
Your current issues are:
  1. You set the dataArray from outside the create-function of the struct. My intension was that create was supposed to handle that and you only get your Data using the array from the outside.
  2. With the upgrade, you seem to create multiple Data for the same unit, something that my fix suggestion didn't take into account and neither does your code.
    1. If this is intended, split the create from the "spawn elemental", and store a unit-group for each forge that you add your created units to. Note that you need to destroy and null that group on destroy. You can call this new method similar to how you call Destroy currently.
  3. You need to keep using the "correct" index for the same unit. Previously, you only create data on index-events, meaning that the UDex was the correct index for the unit. Now you also have "native events" that does not set this, so you need to modify my suggestion to handle that.
    1. In the create-function, change the udg_UDex to GetUnitUserData(du).
This is actually mostly just understanding how indexing works and knowing that Data is an integer under the hood and that index is independent of the unit index and the udg_UDex is only updated for the index-events provided by that system and you need to use the GetUnitUserData if you have a unit outside of these indexing-events.

Knowing the gist of Visualize: Dynamic Indexing is a good idea. You probably can use arrays "manually" using that method, but often structs are pretty nice to use.
 
Level 9
Joined
May 12, 2018
Messages
145
Your current issues are:
  1. You set the dataArray from outside the create-function of the struct. My intension was that create was supposed to handle that and you only get your Data using the array from the outside.
  2. With the upgrade, you seem to create multiple Data for the same unit, something that my fix suggestion didn't take into account and neither does your code.
    1. If this is intended, split the create from the "spawn elemental", and store a unit-group for each forge that you add your created units to. Note that you need to destroy and null that group on destroy. You can call this new method similar to how you call Destroy currently.
  3. You need to keep using the "correct" index for the same unit. Previously, you only create data on index-events, meaning that the UDex was the correct index for the unit. Now you also have "native events" that does not set this, so you need to modify my suggestion to handle that.
    1. In the create-function, change the udg_UDex to GetUnitUserData(du).
This is actually mostly just understanding how indexing works and knowing that Data is an integer under the hood and that index is independent of the unit index and the udg_UDex is only updated for the index-events provided by that system and you need to use the GetUnitUserData if you have a unit outside of these indexing-events.

Knowing the gist of Visualize: Dynamic Indexing is a good idea. You probably can use arrays "manually" using that method, but often structs are pretty nice to use.
Thank you so much! it works well as I intended.

bbbb.gif
 
Status
Not open for further replies.
Top