System Destructable Indexing and Death Event

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
JASS:
.

            ____  ___ ____
           |    \|_ _|    \|----|
           | || | _|_| || ||--|
           |____/|___|____/|----|
           
           Destructable Indexing and Death Event
                v1.0.0 by kingking
        
        What is it ?
        This library indexes every destructable in map and reports their death.
        
        ==============
        DIDE API :
        ==============
        function TriggerRegisterDestDeathEvent (trigger whichTrigger)
        function TriggerRegisterDestonIndexEvent (trigger whichTrigger)
        function GetEventDestructable () -> destructable
        
        function GetDestructableId (destructable dest) -> integer
        function GetDestructableById (integer id) -> destructable
        
        Notice : Please change all the CreateDestructable and CreateDestructableZ with :
        function CreateDestructableEx (-same with CreateDestructable)
        function CreateDestructableZEx (-same with CreateDestructableZ)
        
        Tweak :
        You can find "private function filter" in the library. It acts as default
        filter. If you dont want the types of destructables to be indexed, use it
        to eliminate.
        
        Modules :
        =======================
        module DIDE_Property //Thanks to Cohadar for this interface.
        =======================
        This module is used to attach struct to destructable.
        Usage :
        set *StructName*[destructable] = whichInstance
        *StructName*[destructable] -> Attached struct
        
        =======================
        module DIDE //Thanks to Jesus4Lyf for this interface.
        =======================
        This module is used when you want to attach struct to many destructable
        automatically. Struct which used this module must extends array.
        Usage :
        *StructName*[destructable] -> Attached struct
        
        Members :
        *instance*.dest -> destructable
        
        Optional methods :
        private static method DIDE_filter takes destructable dest returns boolean
        ~This method is used to filter. If the destructable does not meet the condition,
         the struct will not allocate an instance for it.
        
        private method DIDE_onCreate takes nothing returns nothing
        ~When the struct is allocated, this method will be fired.
        
        private method DIDE_onDestroy takes nothing returns nothing
        ~When the struct is deallocated, this method will be fired.
        
        private static method DIDE_onInit takes nothing returns nothing
        ~Because DIDE uses structs init, so you need to replace onInit with this.
        
        Requires :
        Jasshelper v0.A.2.7 or newer
        Event

Event
JASS:
library DIDE initializer Init requires Event

    private function filter takes destructable dest returns boolean
        return true
    endfunction
    
    globals
        private hashtable hasht
        private Event DeathEvent
        private Event onIndexEvent
        private destructable dest = null
        private trigger onIndexStructAllocator = CreateTrigger()
        private trigger onDeathStructDeallocator = CreateTrigger()
    endglobals
    
    function GetEventDestructable takes nothing returns destructable
        return dest
    endfunction
    
    function TriggerRegisterDestDeathEvent takes trigger whichTrigger returns EventReg
        return DeathEvent.register(whichTrigger)
    endfunction
    
    function TriggerRegisterDestOnIndexEvent takes trigger whichTrigger returns EventReg
        return onIndexEvent.register(whichTrigger)
    endfunction
    
    private keyword DestructableIndex
    
    function GetDestructableId takes destructable dest returns integer
        return LoadInteger(hasht,GetHandleId(dest),0)
    endfunction
    
    function GetDestructableById takes integer whichId returns destructable
        return DestructableIndex(whichId).d
    endfunction
    
    function CreateDestructableEx takes integer objectid, real x, real y, real face, real scale, integer variation returns destructable
        set bj_lastCreatedDestructable = CreateDestructable(objectid,x,y,face,scale,variation)
        call DestructableIndex.new(bj_lastCreatedDestructable)
        return bj_lastCreatedDestructable
    endfunction
    
    function CreateDestructableZEx takes integer objectid, real x, real y, real z, real face, real scale, integer variation returns destructable
        set bj_lastCreatedDestructable = CreateDestructableZ(objectid,x,y,z,face,scale,variation)
        call DestructableIndex.new(bj_lastCreatedDestructable)
        return bj_lastCreatedDestructable
    endfunction
    
    public function RegisterStruct takes code cf1, code cf2 returns nothing
        call TriggerAddCondition(onIndexStructAllocator,Condition(cf1))
        call TriggerAddCondition(onDeathStructDeallocator,Condition(cf2))
    endfunction
    
    private struct DestructableIndex
        static conditionfunc cond
        destructable d
        trigger trig
        triggercondition trigCond
        integer id
        
        static method removeIndex takes nothing returns boolean
            local thistype this = LoadInteger(hasht,GetHandleId(GetTriggerDestructable()),0)
            call TriggerEvaluate(onDeathStructDeallocator)
            set dest = this.d
            call DeathEvent.fire()
            call TriggerRemoveCondition(this.trig,this.trigCond)
            call DisableTrigger(this.trig)
            call DestroyTrigger(this.trig)
            set this.d = null
            set this.trig = null
            set this.trigCond = null
            call RemoveSavedInteger(hasht,this.id,0)
            call this.destroy()
            return false
        endmethod
        
        static method new takes destructable dest returns nothing
            local thistype this = thistype.allocate()
            set this.d = dest
            set this.trig = CreateTrigger()
            set this.id = GetHandleId(dest)
            call TriggerRegisterDeathEvent(this.trig,this.d)
            set this.trigCond = TriggerAddCondition(this.trig,thistype.cond)
            call SaveInteger(hasht,this.id,0,this)
        endmethod
        
        static method onInit takes nothing returns nothing
            set thistype.cond = Condition(function thistype.removeIndex)
        endmethod
    endstruct
    
    private function AllocateIndex takes nothing returns boolean
        set dest = GetFilterDestructable()
        if filter(dest) then
            call DestructableIndex.new(dest)
            call TriggerEvaluate(onIndexStructAllocator)
            call onIndexEvent.fire()
        endif
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        set hasht = InitHashtable()
        set DeathEvent = Event.create()
        set onIndexEvent = Event.create()
        call EnumDestructablesInRect(GetWorldBounds(),Condition(function AllocateIndex),null)
    endfunction
    
    public struct StructEx extends array
            
        static method DIDE_filter takes destructable dest returns boolean
            return filter(dest)
        endmethod
        
        method DIDE_onCreate takes nothing returns nothing
        endmethod
        
        method DIDE_onDestroy takes nothing returns nothing
        endmethod
        
        static method DIDE_onInit takes nothing returns nothing
        endmethod
    endstruct
    
    module DIDE_Property
        private static destructable array DIDE_dest
        private static thistype array DIDE_data
        
        static method [] takes destructable dest returns thistype
            local integer DIDE_id = GetDestructableId(dest)
            if DIDE_dest[DIDE_id] == dest then
                return DIDE_data[DIDE_id]
            endif
            set DIDE_dest[DIDE_id] = null
            set DIDE_data[DIDE_id] = 0
            return 0
        endmethod
        
        static method []= takes destructable dest, thistype i returns nothing
            local integer DIDE_id = GetDestructableId(dest)
            set DIDE_dest[DIDE_id] = dest
            set DIDE_data[DIDE_id] = i
        endmethod
        
        static method release takes destructable dest returns nothing
            local integer DIDE_id = GetDestructableId(dest)
            if DIDE_dest[DIDE_id] == dest then
                call DIDE_data[DIDE_id].destroy()
                set DIDE_dest[DIDE_id] = null
                set DIDE_data[DIDE_id] = 0
            endif
        endmethod
        
    endmodule
endlibrary

    module DIDE
        private static delegate DIDE_StructEx DELEGATED=0
        private boolean DIDE_IsUsed = false
        
        static method operator [] takes destructable dest returns thistype
            local integer id = GetDestructableId(dest)
            if thistype(id).DIDE_IsUsed == true then
                return id
            endif
            return 0
        endmethod
        
        method operator dest takes nothing returns destructable
            return GetDestructableById(this)
        endmethod
        
        static method DIDE_Create takes nothing returns boolean
            local destructable dest = GetEventDestructable()
            local thistype this = GetDestructableId(dest)
            if thistype.DIDE_filter(dest) == true then
                set this.DIDE_IsUsed = true
                call this.DIDE_onCreate()
            endif
            set dest = null
            return false
        endmethod
        
        static method DIDE_Destroy takes nothing returns boolean
            local destructable dest = GetEventDestructable()
            local thistype this = GetDestructableId(dest)
            if this.DIDE_IsUsed == true then
                set this.DIDE_IsUsed = false
                call this.DIDE_onDestroy()
            endif
            set dest = null
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            call DIDE_RegisterStruct(function thistype.DIDE_Create,function thistype.DIDE_Destroy)
            call DIDE_onInit()
        endmethod
    endmodule
 
Reaction score
341
Why would you need to index destructables?

They are hardly, if ever modified throughout the game via the script.

This apprently also does not index destructables as they enter the map.

You group the ones at map init then force the user to use your "Ex" functions.
 

Azlier

Old World Ghost
Reaction score
461
What is the point of destructable indexing if you're going to attach to the handle id anyway?!
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
They are hardly, if ever modified throughout the game via the script.
You don't need to use CreateDestructableEx for preplaced destructable. The library will enum all preplaced destructable and index them.

What is the point of destructable indexing if you're going to attach to the handle id anyway?!
If the destructable died, and another destructable took it's handle id. The storage is getting messed up, soon.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Most of that is actually pretty good.
Here's the problems:
JASS:
    function GetEventDestructable takes nothing returns destructable
        return dest
    endfunction

That does not support recursion - what if CreateDestructibleEx is called in DIDE_onCreate? Take a look at this:
JASS:
    //===========================================================================
    // Recursion stack
    //
    private module Stack
        static thistype top=0 // thistype(0) throws a syntax error, does not compile
        static method increment takes nothing returns nothing
            set thistype.top=thistype(thistype.top+1)
        endmethod
        static method decrement takes nothing returns nothing
            set thistype.top=thistype(thistype.top-1)
        endmethod
    endmodule

That's the module I've been using in all my recent systems, find SpellStruct or Transport and take a look at what I do and figure out why...

Death is not necessarily the end of a destructible, is it?
CreateDestructible is not the only source of destructibles, is it?
Even GUI can make destructibles.

I don't think this is useful.

Looks like you did fairly well anyway. :)

Edit:
JASS:
        static method operator [] takes destructable dest returns thistype
            local integer id = GetDestructableId(dest)
            if thistype(id).DIDE_IsUsed == true then
                return id
            endif
            return 0
        endmethod

Isn't quite AIDS, AIDS inlines to the native. :)

Edit again:
Best leave detection method for this would probably be a periodic timer checking GetDestructibleTypeId(dest[index]) to see if an index should be freed. But having to use a CreateEx function is kinda lame.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
>That does not support recursion - what if CreateDestructibleEx is called in DIDE_onCreate? Take a look at this:
Inifnity loop. :)

>Best leave detection method for this would probably be a periodic timer checking GetDestructibleTypeId(dest[index]) to see if an index should be freed.
Should I make a Blue and Red version?

>CreateEx
I can only think of this to get new created destructables besides EnumDestructablesInRect. But if I scan for new destructable by using this method, the map is unplayable. lol.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>Inifnity loop.
Recursive functions are not infinite loops. I was actually taught that the inner problem must be smalled than the outer problem, so by definition an infinite loop is not recursion (but I'd still personally call infinite recursion recursion, although it should never be done).

Imagine a map where every gate must have a switch on each side, and that switch is a destructible (very practical example). That would make this system fail, as the event response would be overridden.

>Should I make a Blue and Red version?
No, make a working version that people can use.

>I can only think of this to get new created destructables besides EnumDestructablesInRect.
Don't think so, which means this is pretty lame.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
JASS:
globals
    integer StackLevel = 0
    destructable array DestructableStack
endglobals

//Hmm..
set StackLevel = StackLevel + 1
set DestructibleStack[StackLevel] = dest
call Event.fire()
set StackLevel = StackLevel - 1

Like this?
 

Jesus4Lyf

Good Idea™
Reaction score
397
Yep. Does it make sense to you? :)
One of my most used algorithms, to support recursion. :p

Back on track...
Main purpose of this: Detecting the life span of a destructible.
Main thing this is not good for right now: Detecting the life span of a destructible.

We have a problem.
Also, Azlier is right that since this uses a hashtable anyway, people may as well use it that way themselves (especially if all this does is death detect, and that way you wouldn't need CreateEx probably). Also, usually the point of an indexing system is to monopolise on Blizzard's UserData natives in a way that allows them to be used broadly. This doesn't achieve that, since destructibles don't have 'em...

So I'm leaning towards graveyarding this.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top