System PUI - Perfect Unit Indexing

Discussion in 'Systems and Snippets' started by Cohadar, Dec 1, 2007.

  1. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    New version: v6.2 (fully backwards compatible)
    Changes:
    * You can now get unit from an index GetIndexUnit(index) -> unit
    * Added PUI module that does the same stuff as PUI textmacro

    JASS:
    
    //===========================================================================
    //  PUI -- Perfect Unit Indexing by Cohadar -- v6.2
    //===========================================================================
    //
    //  PURPOUSE:
    //       * Extending UnitUserData()
    //       * This is basically perfect hashing algorithm for units
    //
    //  HOW TO USE:
    //       * You have two functions at your disposal: 
    //           GetUnitIndex(unit) -> index
    //           GetIndexUnit(index) -> unit
    //
    //       * Put you custom unit properties in struct that extends arrays.
    //         Use unit index as id of that struct.
    //         There is an example of this in demo map.
    //         
    //  PROS:
    //       * Unit indexes are assigned only to units that actually need them.
    //       * Unit index will be automatically recycled when unit is removed from the game.
    //       * Automatically detects null unit handles and removed unit handles (in debug mode)
    //
    //  CONS:
    //       * This system needs exclusive access to UnitUserData
    //
    //  DETAILS:
    //       * Uses internal vJass struct index allocation/deallocation algorithm.
    //       * Periodically checks and recycles unit indexes
    //
    //  THANKS TO:
    //       * Vexorian - for his help with PUI textmacro
    //       * Builder Bob - for testing and bugfinding
    //       * Joker(Div) - bugfinding
    //
    //  HOW TO IMPORT:
    //       * Just create a trigger named PUI
    //       * convert it to text and replace the whole trigger text with this one
    //
    //===========================================================================
    
    
    library PUI initializer Init
    
    //===========================================================================
    globals    
        // maximum number of indexed units on your map at a single moment of time
        private constant integer MAX_INDEXES = 1024  // up to 8192
        
        // period of recycling, 32 indexes per second
        private constant real PERIOD = 0.03125   
        
        // current check index
        private integer C = 0 
    endglobals
    
    //===========================================================================
    // Using internal struct algorithm for index allocation/dealocation
    // This way I don't have to write messy code of my own
    //===========================================================================
    private struct UnitIndex
        unit u
        
        //----------------------------------------------------------
        static method create takes unit whichUnit returns UnitIndex
            local UnitIndex index 
            
            // check for null unit handle
            debug if whichUnit == null then
            debug     call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
            debug     return 0
            debug endif
            
            set index = UnitIndex.allocate()
            set index.u = whichUnit
            call SetUnitUserData(whichUnit, index)
            
            // check for removed unit handle
            debug if GetUnitUserData(whichUnit) == 0 then
            debug     call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
            debug     return 0
            debug endif        
            
            return index
        endmethod
        
        //----------------------------------------------------------
        static method Recycler takes nothing returns boolean
            local UnitIndex index = C
            set C = C + 1
            if C == MAX_INDEXES then
                set C = 0
            endif
            if index.u != null then
                if (GetUnitUserData(index.u) == 0) then
                    set index.u = null
                    call index.destroy()
                endif
            endif
            return false
        endmethod
    endstruct
    
    //===========================================================================
    //  Returns index of some unit, if unit has no index it gets a new one.
    //===========================================================================
    function GetUnitIndex takes unit whichUnit returns integer
        local UnitIndex index = GetUnitUserData(whichUnit)
        if index == 0 then
            set index = UnitIndex.create(whichUnit)
        endif
        return index
    endfunction
    
    //===========================================================================
    //  Return unit that has specified index, or null in no such unit exists.
    //===========================================================================
    function GetIndexUnit takes integer index returns unit
        local UnitIndex i = index
        if i.u != null then
            if GetUnitUserData(i.u) == index then
                return i.u
            endif
        endif
        return null
    endfunction
    
    //===========================================================================
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        call TriggerRegisterTimerEvent(trig, PERIOD, true)
        call TriggerAddCondition(trig, Condition(function UnitIndex.Recycler))
    endfunction
    
    endlibrary
    
    //***************************************************************************
    //
    //  PUI TEXTMACROS
    // 
    //***************************************************************************
    
    //===========================================================================
    //  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
    //  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
    //  Instead put them in a struct and use PUI textmacro
    //===========================================================================
    //! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
    $VISIBILITY$ struct $NAME$
        private static unit   array pui_unit
        private static $TYPE$ array pui_data
        
        //-----------------------------------------------------------------------
        //  Returns default value when first time used
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns $TYPE$
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_unit[pui] != whichUnit then
                set .pui_unit[pui] = whichUnit
                set .pui_data[pui] = $DEFAULT$
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
        endmethod
    endstruct
    //! endtextmacro
    
    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //  The best option is never to release or destroy PUI structs manually
    //    because they will be recycled automatically with unit handle.
    //===========================================================================
    //! textmacro PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                if .pui_unit[pui] != whichUnit then
                    // recycled handle detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod
    
        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    //! endtextmacro
    
    //***************************************************************************
    //
    //  PUI MODULE  (does the same stuff as PUI textmacro)
    // 
    //***************************************************************************
    
    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //  The best option is never to release or destroy PUI structs manually
    //    because they will be recycled automatically with unit handle.
    //===========================================================================
    module PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                if .pui_unit[pui] != whichUnit then
                    // recycled handle detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod
    
        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    endmodule


    JASS:
    
    //==============================================================================
    //  PUI -- Perfect Unit Indexing by Cohadar -- v5.3
    //==============================================================================
    //
    //  PURPOUSE:
    //       * Extending UnitUserData()
    //       * This is basically perfect hashing algorithm for units
    //
    //  HOW TO USE:
    //       * You have only one function at your disposal GetUnitIndex(unit)
    //         It will return a unique index for each unit in range 1..8190
    //
    //       * What you will do with that index is all up to you
    //         Of course using global arrays is the most obvious choice
    //         Advanced jassers will think of a couple of more clever ones ofc.
    //
    //       * There are also 2 textmacros available at the end of library code
    //         They can be used for easier attaching to units
    //         PUI for structs 
    //         PUI_PROPERTY for unit, integer, real, boolean and string variables
    //
    //  PROS: 
    //       * You can use any number of systems that previously could not work together
    //         because they all required exclusive access of UnitUserData()
    //
    //       * You can also use this to attach spell data structs to casters
    //
    //       * There are no SetUnitIndex() or ClearUnitIndex() functions here
    //         Each unit gets assigned one index that cannot be changed
    //         That index will be automatically recycled when unit is removed from the game.
    //
    //  CONS:
    //       * This system uses UnitUserData() itself
    //         That means that if you want to use PUI you must recode 
    //         any other system that uses UnitUserData() to use GetUnitIndex() instead
    //
    //       * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
    //         you must check if timer on that index already exists before you create a new one.
    //         This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
    //         to the newly created unit for which you intended to use timer for
    //
    //       * All in all this is not a sys for newbies, it gives great power,
    //         but it requires knowledge and carefull handling
    //
    //  DETAILS:
    //       * System is using unit array to keep track of all units with an index.
    //         Array is periodically checked for removed units,
    //         when removed unit is found, index is recycled.
    //         Indexes have "decay time" to prevent bugs
    //         caused by attaching to "Can't Raise, Does not decay" type units,
    //         or by using RemoveUnit() function
    //
    //  SYSTEM COMMANDS: (debug mode only, red player only)
    //
    //       * type -pui to display indexes of currently selected units
    //       * type -puistats to display some stats
    //       * type -puitest to verify that all indexes are valid and unique
    //
    //  THANKS TO:
    //       * Vexorian - for his help with PUI textmacro
    //       * Builder Bob - for testing and bugfinding
    //       * Joker(Div) - bugfinding
    //
    //  HOW TO IMPORT:
    //       * Just create a trigger named PUI
    //       * convert it to text and replace the whole trigger text with this one
    //
    //==============================================================================
    
    
    library PUI initializer Init
    
    //==============================================================================
    globals    
        //-----------------------------------------------
        private constant real INDEX_DECAY_TIME = 5.  // seconds
        
        //-----------------------------------------------    
        private constant real PERIOD = 0.03125   // 32 fps
            
        //-----------------------------------------------
        private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
        
        //-----------------------------------------------
        private integer maxIndex = 0
        private integer usedIndexCount = 0
        private integer checker  = 0
        private integer array Indexz
        private unit    array Unitz
        
        //-----------------------------------------------
        private integer freeIndexCount = 0
        private integer array Freez
    
        //-----------------------------------------------
        private integer decayIndexCount = 0
        private integer decayer  = 0
        private integer array Decayz
        private integer array TimeEndz
        
        private integer tick = 0
    endglobals
    
    //==============================================================================
    private function PeriodicRecycler takes nothing returns boolean
        set tick = tick + 1
        
        // unit recycler
        if usedIndexCount > 0 then
            set checker = checker + 1
            if checker > usedIndexCount then
                set checker = 1
            endif
            if (GetUnitUserData(Unitz[checker]) == 0) then
                set decayIndexCount = decayIndexCount + 1
                set Decayz[decayIndexCount] = Indexz[checker]
                set TimeEndz[decayIndexCount] = tick + DECAY_TICKS
                
                set Indexz[checker] = Indexz[usedIndexCount]
                set Unitz[checker] = Unitz[usedIndexCount]
                set usedIndexCount = usedIndexCount - 1
            endif
        endif
    
        // index recycler
        if decayIndexCount > 0 then
            set decayer = decayer + 1
            if decayer > decayIndexCount then
                set decayer = 1
            endif
            if TimeEndz[decayer] <= tick then
                set freeIndexCount = freeIndexCount + 1
                set Freez[freeIndexCount] = Decayz[decayer]
    
                set Decayz[decayer] = Decayz[decayIndexCount]
                set TimeEndz[decayer] = TimeEndz[decayIndexCount]          
                set decayIndexCount = decayIndexCount - 1
            endif    
        endif
    	
        // for debugging 
        //if ModuloInteger(tick, 8) == 0 then
        //    call ClearTextMessages()
        //    call BJDebugMsg("Used/Free/Decaying: " + I2S(usedIndexCount) + "/" + I2S(freeIndexCount) + "/" + I2S(decayIndexCount))
        //endif
        
        return false
    endfunction
    
    //==============================================================================
    //  Main and only function exported by this library
    //==============================================================================
    function GetUnitIndex takes unit whichUnit returns integer
        local integer index
        
        debug if whichUnit == null then
        debug   call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
        debug   return 0
        debug endif
        
        set index = GetUnitUserData(whichUnit)
    
        if index == 0 then
            set usedIndexCount = usedIndexCount + 1
    
            if freeIndexCount > 0 then
                set Indexz[usedIndexCount] = Freez[freeIndexCount]
                set freeIndexCount = freeIndexCount - 1
            else
                set maxIndex = maxIndex + 1
                set Indexz[usedIndexCount] = maxIndex
            endif
            
            set Unitz[usedIndexCount] = whichUnit
            call SetUnitUserData(whichUnit, Indexz[usedIndexCount])
            set index = GetUnitUserData(whichUnit)
           
            // this happens when requesting unit index for removed unit
            debug if index == 0 then
            debug     call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
            debug endif
        endif
        
        return index
    endfunction
    
    //==============================================================================
    private function DisplayStats takes nothing returns nothing
        call BJDebugMsg("=============================================")    
        call BJDebugMsg("Biggest index ever = " + I2S(maxIndex))    
        call BJDebugMsg("Indexes in use = " + I2S(usedIndexCount))
        call BJDebugMsg("Decaying indexes = " + I2S(decayIndexCount))
        call BJDebugMsg("Released indexes = " + I2S(freeIndexCount))
        call BJDebugMsg("=============================================")    
    endfunction
    
    //===========================================================================
    private function DisplaySelectedEnum takes nothing returns nothing
        call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
    endfunction
    
    //===========================================================================
    private function DisplaySelected takes nothing returns nothing
        local group g = CreateGroup()
        call SyncSelections()
        call GroupEnumUnitsSelected(g, Player(0), null)
        call ForGroup(g, function DisplaySelectedEnum)
        call DestroyGroup(g)
        set  g = null
    endfunction
    
    
    //==============================================================================
    globals
        private integer testCounter = 0
        private integer array TestCountz
    endglobals
    
    //==============================================================================
    private function Test takes nothing returns nothing
        local integer i
        set testCounter = testCounter + 1
        
        set i = 1
        loop
            exitwhen i > usedIndexCount
            // happens when you abuse UnitUserData outside PUI
            if GetUnitTypeId(Unitz[i]) != 0 and GetUnitUserData(Unitz[i]) != Indexz[i] then
                call BJDebugMsg("|c00FF0000ERROR: PUI - Invalid index detected")
                return
            endif
            // if this error happens it means PUI is bugged
            if TestCountz[Indexz[i]] != testCounter then
                set TestCountz[Indexz[i]] = testCounter
            else
                call BJDebugMsg("|c00FF0000ERROR: PUI - Double index detected")
                return
            endif
            set i = i + 1
        endloop
        
        call BJDebugMsg("|c0000ff00OK: PUI - All indexes were valid and unique")
    endfunction
    
    //==============================================================================
    private function Init takes nothing returns nothing
        local trigger trig 
        
        set trig = CreateTrigger()
        call TriggerRegisterTimerEvent( trig, PERIOD, true )
        call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )
    
        debug set trig = CreateTrigger()
        debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-pui", true )
        debug call TriggerAddAction( trig, function DisplaySelected )      
        
        debug set trig = CreateTrigger()
        debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puistats", true )
        debug call TriggerAddAction( trig, function DisplayStats )
        
        debug set trig = CreateTrigger()
        debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puitest", true )
        debug call TriggerAddAction( trig, function Test )    
    endfunction
    
    endlibrary
    
    
    //===========================================================================
    //  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
    //  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
    //  Instead put them in a struct and use PUI textmacro
    //===========================================================================
    //! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
    $VISIBILITY$ struct $NAME$
        private static unit   array pui_unit
        private static $TYPE$ array pui_data
        
        //-----------------------------------------------------------------------
        //  Returns default value when first time used
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns $TYPE$
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_unit[pui] != whichUnit then
                set .pui_unit[pui] = whichUnit
                set .pui_data[pui] = $DEFAULT$
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
        endmethod
    endstruct
    //! endtextmacro
    
    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //===========================================================================
    //! textmacro PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                if .pui_unit[pui] != whichUnit then
                    // recycled handle detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod
    
        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    //! endtextmacro
     

    Attached Files:

    • Like Like x 9
  2. Uberplayer

    Uberplayer -

    Ratings:
    +454 / 0 / -0
    Why are those two functions, DisplayTopIndex() and RecycleIndex(), returning boolean values?
     
  3. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    Because they are Conditions, not Actions.
    (Standard trick in advanced vJass coding to increase speed of things)
     
  4. Uberplayer

    Uberplayer -

    Ratings:
    +454 / 0 / -0
    Okay, and shouldn't you null the trigger variable "trig" in the Init function?

    Off-Topic:
    In you signature in wc3campaigns, DD v1.0 -link leads to PUI thread. :p
     
  5. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    I should but I don't have to, it is only one handle so noone is going to notice,
    besides ABC is resistant to handle leaks.
     
  6. Mr.Tutorial

    Mr.Tutorial Hard in the Paint.

    Ratings:
    +42 / 0 / -0
    Mind explaining what this does? That would help :p

    EDIT: Nvm never noticed that in your trigger, +rep
     
  7. waaaks!

    waaaks! Zinctified

    Ratings:
    +256 / 0 / -0
    ok im confused...what method best uses this sytem?
    spell making?
    or what?
     
  8. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    Yes this is for spellmaking, I already posted 2 spells that use it.
    Soon will be 3.
     
  9. waaaks!

    waaaks! Zinctified

    Ratings:
    +256 / 0 / -0
    ill try it after approving the system, so that there will be no glitches after i used it
     
  10. hell_knight

    hell_knight Playing WoW

    Ratings:
    +126 / 0 / -0
    Does that give every single unit an unique custom value ? I am assuming GetUnitIndex(unit) is getting their custom value or something?

    Kind of confused. Because if it is I could use this and make my stuff mui
     
  11. Hero

    Hero ─║╣ero─

    Ratings:
    +251 / 0 / -0
    This is very useful..

    I shall start using this
     
  12. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    Exactly.
     
  13. Rheias

    Rheias New Helper (I got over 2000 posts)

    Ratings:
    +232 / 0 / -0
    Cohadar, I'm not exactly sure I understand how this system works... Do I set the custom value normally, and simply I get it by getting the unit index, or did I miss something?

    Thanks for sharing this. :)
     
  14. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    System sets indexes for you, do NOT set anything yourself, just use GetUnitIndex() instead of GetUnitUserData()
     
  15. Rheias

    Rheias New Helper (I got over 2000 posts)

    Ratings:
    +232 / 0 / -0
    It is obivious that I'm missing something, if the system sets indexes for me, how exactly am I supposed to attach a struct to a unit? Ususally what I would have done is:

    call SetUnitUserData(whichUnit,whichStruct)
    ...
    set whichStruct = GetUnitUserData(whichUnit)

    How would I do that using your system?
     
  16. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0



    By looking at the example map or any other spell I posted?
     
  17. hell_knight

    hell_knight Playing WoW

    Ratings:
    +126 / 0 / -0
    IN GUI custom values is GetUnitUserData(udg_unit) , so I can convert to JASS and replace that with GetUnitIndex(udg_unit)
    That correct? Will anything schrew up if I do this?
     
  18. phyrex1an

    phyrex1an Staff Member and irregular helper Staff Member

    Ratings:
    +446 / 0 / -0
    That will work as long as you doesn't use (eg, change) the custom value of the unit yourself somewhere else in the script. That's not a 'limitation' of GUI but rather of the system itself (and honestly, why use the custom value directly if you are using this?)
     
    • Like Like x 1
  19. Rheias

    Rheias New Helper (I got over 2000 posts)

    Ratings:
    +232 / 0 / -0
    Alright, I took a look, I think I get it, but from this part.

    set ret.PUI[GetUnitUserData(victim)] = ret

    I looked and I couldn't find any member in your struct called PUI. Further more, I tried to create a new struct and using PUI in it and it syntaxed me, so I'm a bit lost.
     
  20. Cohadar

    Cohadar master of fugue

    Ratings:
    +209 / 0 / -0
    Ok I extracted the core of PUI usage so you can see it.
    I will also write this:
    JASS:
    
    SetTimerStructA ~ static method create
    GetTimerStructA ~ static method Get
    ClearTimerStructA ~ method onDestroy

    Using PUI is inherently more complicated than using ABC simply because unit attaching is complicated than timer or trigger attaching,
    with units you have to check for previous attachments to make stuff MUI.

    I tried to make it simpler but all simplifications would include use of textmacros and besides if you are going to write spells with unit attaching you better know how to work with structs.
    So there is no easy way here, simply learn it and use it.


    JASS:
    
    scope YourSpell
    
    //===========================================================================
    private struct Data
        unit whichUnit
        private static Data array PUI  //<---------
    
        //-------------------------------------------------------------------------  
        static method create takes unit whichUnit returns Data
            local Data ret = Data.allocate()
            set ret.whichUnit = whichUnit
            set Data.PUI[GetUnitIndex(whichUnit)] = ret // create unit->Data link
            return ret
        endmethod
        
        //-------------------------------------------------------------------------  
        static method Get takes unit whichUnit returns Data
            local integer index
        
            set index = GetUnitIndex(whichUnit) // PUI
            if Data.PUI[index] == 0 then
                debug call BJDebugMsg("|c00FFCC00"+"Data struct created on first use for unit: " + GetUnitName(whichUnit))
                return Data.create(whichUnit)
            endif
            
            return Data.PUI[index]
        endmethod    
    
        //-------------------------------------------------------------------------  
        method onDestroy takes nothing returns nothing
            set Data.PUI[GetUnitIndex(.whichUnit)] = 0 // break unit->Data link
        endmethod    
    endstruct
    
    endscope
     

Share This Page