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

Item Drop System 1.20


JASS:
/*

    ----------------------------------------------------------------------------------

    Item Drop System 1.20
        by Adiktuz
    
    ----------------------------------------------------------------------------------
    
    Description:
    
        A system that would allow the dynamic creation of item drop lists per UnitTypeId or
        per specific unit and handle the process of item dropping from killing monsters. 
    
   ----------------------------------------------------------------------------------
    
    Features:
        
        -You can add lots of items to the drop list of a unit ( max is defined by a global)*
        
        -You can dynamically specify the number of items that a single unit can drop upon death*
        
        -You can also add drop chance bonuses to a unit (direct or multiplier)
        
        -You can dynamically add or remove items from drop list of units*
        
        *can be both based on UnitTypeId or per specific unit
    
    ----------------------------------------------------------------------------------
    
    Requirements:
        - Wc3:TFT 1.24e
        - NewGenWE 
        - JassHelper (at least 0.A.2.A)
        - Ability to follow instructions
    
    ----------------------------------------------------------------------------------
    
    How to import:
        - Create a new trigger on your map, then convert to custom sript
        - Replace anything inside that trigger with the contents of this library
    
    ----------------------------------------------------------------------------------
    
    How to use the ItemDropSystem (general , UnitTypeId based):
        
        1) Initialize UnitTypeIds to use this system
            
            - use this call function to initialize each UnitTypeId that will use this system
                
                - call ItemDrop.InitUnit(integer UnitRawCode, integer DropItemsMax)
                
                *UnitRawCode is the rawcode of the unit
                
                *DropItemsMax is the maximum number of items from the pool that a dying unit of that
                 UnitTypeId can drop
                 
        2) Add items to the drop list of a UnitTypeId
            
            - use this call function to add items
                
                - call ItemDrop.AddItem(integer UnitRawCode, integer ItemRawCode, real Chance)
                
                *UnitRawCode is the rawcode of the unit where you want to add the item
                
                *ItemRawCode is the rawcode of the item
                
                *Chance is the percent chance to drop the item (from 0.01 up to 100.00)
            
            -Note: to avoid too much stress on computers, don't add so many items to a UnitTypeId
            
        3) To remove items from the drop list of a UnitTypeId
        
            - use this call function to remove items
            
                call ItemDrop.RemoveItems(integer UnitRawCode, integer ItemRawCode)
                
                *UnitRawCode is the rawcode of the unit where you want to add the item
                
                *ItemRawCode is the rawcode of the item
        
        4) To modify the max number of items that a single unit of a certain type can drop
            
            - use this call function
                
                call ItemDrop.ChangeDropMax(intger UnitRawCode, integer Max)
                
                *UnitRawCode is the rawcode of the unit where you want to modify the drop max
                
                *integer Max is the new maximum amount of items that a single unit of that type can drop
                 upon death
        
        5) To unregister a UnitTypeId
         
                call ItemDrop.Unregister(integer rawcode)
                
                *rawcode is the rawcode/UnitTypeId of the unit you want to unregister
    ----------------------------------------------------------------------------------
    
    How to use the ItemDropSystem (specific):
        
        1) Initialize the Unit to use this system
            
            - use this call function to initialize the unit that will use this system
                
                - call ItemDrop.InitUnitSpecific(unit u, integer DropItemsMax, boolean ProcessBoth)
                
                *u is the unit
                
                *DropItemsMax is the maximum number of items from the pool that the dying unit can drop
                
                *ProcessBoth checks if the system will process both the specific and general droplist for the unit
                if false, the system will only process the specific drop list
                 
        2) Add items to the drop list of a unit
            
            - use this call function to add items
                
                - call ItemDrop.AddItemSpecific(unit u, integer ItemRawCode, real Chance)
                
                *u is the unit where you want to add the item
                
                *ItemRawCode is the rawcode of the item
                
                *Chance is the percent chance to drop the item (from 0.01 up to 100.00)
            
            -Note: to avoid too much stress on computers, don't add so many items to a UnitTypeId
            
        3) To remove items from the drop list of a unit
        
            - use this call function to remove items
            
                call ItemDrop.RemoveItemSpecific(integer UnitRawCode, integer ItemRawCode)
                
                *u is the unit where you want to add the item
                
                *ItemRawCode is the rawcode of the item
        
        4) To modify the max number of items that a unit can drop upon death
            
            - use this call function
                
                call ItemDrop.ChangeDropMaxSpecific(unit u, integer Max)
                
                *u is the unit whom you want to modify the drop max
                
                *integer Max is the new maximum amount of items that a single unit of that type can drop
                 upon death
        
        5) To unregister a unit
         
                call ItemDrop.UnregisterSpecific(unit u)
                
                *u is the unit you want to unregister
    ----------------------------------------------------------------------------------
    If you want to force a drop process outside of unit death event use this
                
        call ItemDrop.ForceProcessDrop(unit dying, boolean IsSpecific, unit killer)
    ----------------------------------------------------------------------------------
    
*/

library ItemDropSystem requires BonusChance, RegisterPlayerUnitEvent

/*
    Version 1.20
    by Adiktuz
*/

    globals
        
        /*
            this sets the max number of items that a unit is allowed to have on its drop list
            
            the system will not allow you to add items to a drop list past this amount
            
            I suggest using a value of less than 100 since high numbers seem to cause the system
            to malfunction
        */
            
            private constant integer ITEMS_MAX = 10
            
        /*
            this is the color code of the error's text color
            
            *the default one here is red
        */
        
            private constant string ERROR_COLOR = "|cffff0000"
            private Table GenDrop
            private Table SpecDrop
            private Table DropBoth
    endglobals
    
    /*
        Do not edit below this line
    
        Start of main IDS codes
        
        Initializer module
    */
    
    private module IDS_initsystem
        
        /*
            this method registers the generic death event to trigger the start of the item drop processing
        */
        
        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.FilterDrop)
            set GenDrop = Table.create()
            set SpecDrop = Table.create()
            set DropBoth = Table.create()
        endmethod
        
    endmodule
    
    /*
        Struct which handles the IDS
    */
    
    struct ItemDrop
        
        static thistype data
        integer ItemsMax = 0
        integer DropItemsMax
        integer DropItemsMaxS
        integer array ItemList [ITEMS_MAX]
        real array Chance [ITEMS_MAX]
        
        /*
            Does the process of determining whether a unit will drop an item or items
        */
        
        method ProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            /*
                necessary locals
            */
            local integer i = 0
            local integer dropped = 0
            local integer looped = 0
            local integer dropmax = 0
            /*
                I randomized the chance here rather than inside the loop because I think
                its just a useless waste of power if we randomize this for each item in the list
            */
            local real random = GetRandomReal(0.00,100.00)
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local boolean array IsLooped
            /*
                sets whether the max to be used is the specific or the general one
            */
            if IsSpecific then
                set dropmax = this.DropItemsMaxS
            else
                set dropmax = this.DropItemsMax
            endif
            /*
                the main process of the IDS
            */
            loop
                /*
                    I do this to make the run-through of the droplist randomized
                */
                set i = GetRandomInt(0, this.ItemsMax - 1)
                /*
                    This checks if the Item in the index has already been looped
                */
                if not IsLooped[i] then
                    /*
                        This checks if the item will be dropped or not
                        if the item is dropped, we increase the drop count
                    */
                    if random <= (((this.Chance[i])*BonusGetMulti(killer)) + BonusGetDirect(killer)) then
                        set dropped = dropped + 1
                        call CreateItem(this.ItemList[i], x, y)
                    endif
                    /*
                        we increase the loop count
                    */
                    set looped = looped + 1
                    /*
                        this ensures that an item that is already looped, won't be looped again
                    */
                    set IsLooped[i] = true
                endif
                /*
                    Once all the items in the drop list is looped
                    or if the unit already reach the maximum number of item drops,
                    the loop ends
                */
                exitwhen dropped >= dropmax or looped >= this.ItemsMax 
            endloop
        endmethod
        
        /*
            Checks if the dying unit is registered with the IDS
        */
        
        static method FilterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local thistype dat = GenDrop[GetUnitTypeId(u)]
            /*
                this line determines if the killer has an instance with the drop chance bonus system
            */
            local integer Handle = GetHandleId(u)
            /*
              checks if the unit has a specific drop list  
            */
            if SpecDrop[Handle] != 0 then
                /*
                    if yes, checks if both the individual and general drop list will be processed
                */
                if DropBoth[Handle] == 1 then
                    /*
                        if yes, processes the general list first
                    */
                    if dat != 0 then
                    /*
                        if the dying unit is registered with the IDS, the system now performs a run-through of the
                        droplist and determine whether the unit will drop items or not
                    */
                        call dat.ProcessDrop( u,false,GetKillingUnit())
                    endif
                endif
                set dat = SpecDrop[Handle]
                call dat.ProcessDrop(u,true,GetKillingUnit())
                /*
                    destroys the struct instance allocated for the specific drop pool of the unit
                */
                call dat.destroy()
            else
            /*
                if not, process only the general drop list
            */
                if dat != 0 then
                    /*
                        if the dying unit is registered with the IDS, the system now performs a run-through of the
                        droplist and determine whether the unit will drop items or not
                    */
                    call dat.ProcessDrop( u,false,GetKillingUnit())
                endif
            endif
            
            /*
                we clear the hashtable entry for that unit in the Hash, if the unit is not a hero
                since if its a hero, the data might still be needed
            */
            set u = null
            return false
        endmethod
        
        //The method for drop processing outside of death events
        static method ForceProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            local integer i = 0
            local integer dropped = 0
            local integer looped = 0
            local integer dropmax = 0
            local real random = GetRandomReal(0.00,100.00)
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local boolean array IsLooped
            local thistype this = GenDrop[GetUnitTypeId(dying)]
            if IsSpecific then
                set this = SpecDrop[GetHandleId(dying)]
                set dropmax = this.DropItemsMaxS
            else
                set dropmax = this.DropItemsMax
            endif
            loop
                set i = GetRandomInt(0, this.ItemsMax - 1)
                if not IsLooped[i] then
                    if random <= (((this.Chance[i])*BonusGetMulti(killer)) + BonusGetDirect(killer)) then
                        set dropped = dropped + 1
                        call CreateItem(this.ItemList[i], x, y)
                    endif
                    set looped = looped + 1
                    set IsLooped[i] = true
                endif
                exitwhen dropped >= dropmax or looped >= this.ItemsMax 
            endloop
        endmethod
        
        /*
            The method for registering units into the IDS
        */
        
        static method InitUnit takes integer UnitRawCode, integer DropItemsMax returns nothing
            set data = .allocate()
            set GenDrop[UnitRawCode] = data
            set data.DropItemsMax = DropItemsMax
        endmethod
        
        /*
            The method for registering specific units into the IDS
        */
        
        static method InitUnitSpecific takes unit u, integer DropItemsMax, boolean ProcessBoth returns nothing
            local integer UnitCode = GetHandleId(u)
            set data = .allocate()
            set SpecDrop[UnitCode] = data
            if ProcessBoth then
                set DropBoth[UnitCode] = 1
            else
                set DropBoth[UnitCode] = 0
            endif
            set data.DropItemsMaxS = DropItemsMax
        endmethod
        
        /*
            The method for adding an ItemTypeId to the drop list of a UnitTypeId
        */
        
        static method AddItem takes integer UnitRawCode, integer ItemRawCode, real Chance returns nothing
            set data = GenDrop[UnitRawCode]
            /*
                The system will not allow addition of items to the drop list
                past the specified maximum to prevent malfunctions
            */
            if data.ItemsMax <= ITEMS_MAX then
                set data.ItemList[data.ItemsMax] = ItemRawCode
                set data.Chance[data.ItemsMax] = Chance
                set data.ItemsMax = data.ItemsMax + 1
            else
                static if DEBUG_MODE then
                    call BJDebugMsg(ERROR_COLOR + "Error: This unit has already reached the maximum number of items allowed in its droplist" + "|r")
                endif
            endif
        endmethod
        
        /*
            The method for adding an ItemTypeId to the drop list of a specific unit
        */
        
        static method AddItemSpecific takes unit u, integer ItemRawCode, real Chance returns nothing
            set data = SpecDrop[GetHandleId(u)]
            /*
                The system will not allow addition of items to the drop list
                past the specified maximum to prevent malfunctions
            */
            if data.ItemsMax <= ITEMS_MAX then
                set data.ItemList[data.ItemsMax] = ItemRawCode
                set data.Chance[data.ItemsMax] = Chance
                set data.ItemsMax = data.ItemsMax + 1
            else
                static if DEBUG_MODE then
                    call BJDebugMsg(ERROR_COLOR + "Error: This unit has already reached the maximum number of items allowed in its droplist" + "|r")
                endif
            endif
        endmethod
        
        /*
            The method for removing an ItemTypeId to the drop list of a UnitTypeId
        */
        
        static method RemoveItems takes integer UnitRawCode, integer ItemRawCode returns nothing
            local integer i = 0
            local boolean end = false
            set data = GenDrop[UnitRawCode]
            loop
                exitwhen end
                /*
                    if the rawcode saved on ItemList[i] is equal to the ItemRawCode,
                    the data on ItemList[i] is replaced with the data of the last saved item on the list
                    and the max number of items on the list is reduced by 1
                */
                if data.ItemList[i] == ItemRawCode then
                    set data.ItemsMax = data.ItemsMax - 1
                    set data.ItemList[i] = data.ItemList[data.ItemsMax]
                    set data.Chance[i] = data.Chance[data.ItemsMax]
                    set end = true
                else
                    set i = i + 1
                    if i >= data.ItemsMax then
                        set end = true
                        static if DEBUG_MODE then
                            call BJDebugMsg(ERROR_COLOR + "Error: The unit doesn't have the item in its drop list" + "|r")
                        endif
                    endif
                endif
            endloop
        endmethod
        
        /*
            The method for removing an ItemTypeId to the drop list of a specific unit
        */
        
        static method RemoveItemSpecific takes unit u, integer ItemRawCode returns nothing
            local integer i = 0
            local boolean end = false
            set data = SpecDrop[GetHandleId(u)]
            loop
                exitwhen end
                /*
                    if the rawcode saved on ItemList[i] is equal to the ItemRawCode,
                    the data on ItemList[i] is replaced with the data of the last saved item on the list
                    and the max number of items on the list is reduced by 1
                */
                if data.ItemList[i] == ItemRawCode then
                    set data.ItemsMax = data.ItemsMax - 1
                    set data.ItemList[i] = data.ItemList[data.ItemsMax]
                    set data.Chance[i] = data.Chance[data.ItemsMax]
                    set end = true
                else
                    set i = i + 1
                    if i >= data.ItemsMax then
                        set end = true
                        static if DEBUG_MODE then
                            call BJDebugMsg(ERROR_COLOR + "Error: The unit doesn't have the item in its drop list" + "|r")
                        endif
                    endif
                endif
            endloop
        endmethod
        
        /*
            this method is used to change the max number of items that a single unit of a UnitTypeId
            can drop upon death
        */
        
        static method ChangeDropMax takes integer UnitRawCode, integer Max returns nothing
            set data = GenDrop[UnitRawCode]
            set data.DropItemsMax = Max
        endmethod
        
        /*
            this method is used to change the max number of items that a single unit
            can drop upon death
        */
        
        static method ChangeDropMaxSpecific takes unit u, integer Max returns nothing
            set data = SpecDrop[GetHandleId(u)]
            set data.DropItemsMaxS = Max
        endmethod
        
        /*
            the following methods are used to unregister unittypeids or units from the system
        */
        static method Unregister takes integer rawcode returns nothing
            set data = GenDrop[rawcode]
            call data.deallocate()
        endmethod
        
        static method UnregisterSpecific takes unit u returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.deallocate()
        endmethod
        
        implement IDS_initsystem
        
    endstruct

endlibrary
JASS:
/*

    ----------------------------------------------------------------------------------

    Item Drop System MultiTable 1.20
        by Adiktuz
    
    ----------------------------------------------------------------------------------
    
    Description:
    
        A system that would allow the dynamic addition/removal of item drop tables per UnitTypeId or
        per specific unit and handle the process of item dropping from killing monsters. 
        
        This system utilizes the DropTable library for the creation/modification of
        drop tables
    
   ----------------------------------------------------------------------------------
    
    Features:
        
        -You can add lots of drop tables of a unit
        
        -You can dynamically specify the number of items that a single unit can drop upon death
        
        -You can dynamically add or remove drop tables from units*
    
    ----------------------------------------------------------------------------------
    
    Requirements:
        - Wc3:TFT 1.24e
        - NewGenWE 
        - JassHelper (at least 0.A.2.A)
        - Ability to follow instructions
    
    ----------------------------------------------------------------------------------
    
    How to import:
        - Create a new trigger on your map, then convert to custom sript
        - Replace anything inside that trigger with the contents of this library
    
    ----------------------------------------------------------------------------------
    
    How to use the ItemDropSystemMT (general , UnitTypeId based):
        
        1) Initialize UnitTypeIds to use this system
            
            - use this call function to initialize each UnitTypeId that will use this system
                
                - call ItemDropMT.InitUnit(integer UnitRawCode, integer DropItemsMax)
                
                *UnitRawCode is the rawcode of the unit
                
                *DropItemsMax is the maximum number of items from the pool that a dying unit of that
                 UnitTypeId can drop
                
                 
        2) Add drop table to a UnitTypeId
            
            - use this call function to add items
                
                - call ItemDropMT.AddPool(integer UnitRawCode, DropTable dtable, integer weight)
                
                *UnitRawCode is the rawcode of the unit where you want to add the drop table
                
                *dtable is the DropTable to be added
                
                *weight is related to the chance to use this drop table (chance = weight/totalweight)
            
            -Note: to avoid too much stress on computers, don't add so many items to a UnitTypeId
            
        3) To remove drop table from a UnitTypeId
        
            - use this call function to remove drop table
            
                call ItemDropMT.RemovePool(integer UnitRawCode, DropTable dtable)
                
                *UnitRawCode is the rawcode of the unit where you want to add the drop table
                
                *dtable is the DropTable to be removed
        
        4) To modify the max number of items that a single unit of a certain type can drop
            
            - use this call function
                
                call ItemDropMT.ChangeDropMax(intger UnitRawCode, integer Max)
                
                *UnitRawCode is the rawcode of the unit where you want to modify the drop max
                
                *integer Max is the new maximum amount of items that a single unit of that type can drop
                 upon death
        
        5) To unregister a UnitTypeId
         
                call ItemDropMT.Unregister(integer rawcode)
                
                *rawcode is the rawcode/UnitTypeId of the unit you want to unregister
    ----------------------------------------------------------------------------------
    
    How to use the ItemDropSystemMT (specific):
        
        1) Initialize the Unit to use this system
            
            - use this call function to initialize the unit that will use this system
                
                - call ItemDropMT.InitUnitSpecific(unit u, integer DropItemsMax, boolean ProcessBoth)
                
                *u is the unit
                
                *DropItemsMax is the maximum number of items from the pool that the dying unit can drop
                
                *ProcessBoth checks if the system will process both the specific and general droplist for the unit
                if false, the system will only process the specific drop list
                 
        2) Add drop table to a unit
            
            - use this call function to add drop tables
                
                - call ItemDropMT.AddPoolSpecific(unit u, DropTable dtable, integer weight)
                
                *u is the unit where you want to add the drop table
                
                *dtable is the Drop Table to be added
                
                *weight is related to the chance to use this drop table (chance = weight/totalweight)
            
        3) To remove drop table from a unit
        
            - use this call function to remove items
            
                call ItemDrop.RemovePoolSpecific(integer UnitRawCode, DropTable dtable)
                
                *u is the unit where you want to add the item
                
                *dtable is the Drop Table to be removed
        
        4) To modify the max number of items that a unit can drop upon death
            
            - use this call function
                
                call ItemDrop.ChangeDropMaxSpecific(unit u, integer Max)
                
                *u is the unit whom you want to modify the drop max
                
                *integer Max is the new maximum amount of items that a single unit of that type can drop
                 upon death
        
        5) To unregister a unit
         
                call ItemDrop.UnregisterSpecific(unit u)
                
                *u is the unit you want to unregister
    ----------------------------------------------------------------------------------
    If you want to force a drop process outside of unit death event use this
                
        call ItemDrop.ForceProcessDrop(unit dying, boolean IsSpecific, unit killer)
    ----------------------------------------------------------------------------------
    
*/


library ItemDropSystemMT requires BonusChance, RegisterPlayerUnitEvent, DropTable

    globals
            private constant string ERROR_COLOR = "|cffff0000"
            private Table GenDrop
            private Table SpecDrop
            private Table DropBoth
    endglobals
    
    /*
        Do not edit below this line
    
        Start of main IDS codes
        
        Initializer module
    */
    
    private module IDS_initsystem
        
        /*
            this method registers the generic death event to trigger the start of the item drop processing
        */
        
        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.FilterDrop)
            set GenDrop = Table.create()
            set SpecDrop = Table.create()
            set DropBoth = Table.create()
        endmethod
        
    endmodule
    
    /*
        Struct which handles the IDS
    */
    
    struct ItemDropMT
        
        static thistype data
        integer DropItemsMax
        integer DropItemsMaxS
        IPool DropT
        
        
        method ProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            local integer i = 0
            local integer dropped = 0
            local integer looped = 0
            local integer dropmax = 0
            local real random = GetRandomReal(0.00,100.00)
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local boolean array IsLooped
            local DropTable itemlist = this.DropT.item
            if IsSpecific then
                set dropmax = this.DropItemsMaxS
            else
                set dropmax = this.DropItemsMax
            endif
            loop
                set i = GetRandomInt(0, itemlist.itemcount)
                if not IsLooped[i] then
                    if random <= (((itemlist.Chance[i])*BonusGetMulti(killer)) + BonusGetDirect(killer)) then
                        set dropped = dropped + 1
                        call CreateItem(itemlist.ItemList[i], x, y)
                    endif
                    set looped = looped + 1
                    set IsLooped[i] = true
                endif
                exitwhen dropped >= dropmax or looped >= itemlist.itemcount 
            endloop
        endmethod
        
        static method FilterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local thistype dat = GenDrop[GetUnitTypeId(u)]
            local integer Handle = GetHandleId(u)
            if SpecDrop[Handle] != 0 then
                if DropBoth[Handle] == 1 then
                    if dat != 0 then
                        call dat.ProcessDrop( u,false,GetKillingUnit())
                    endif
                endif
                set dat = SpecDrop[Handle]
                call dat.ProcessDrop(u,true,GetKillingUnit())
                call dat.destroy()
            else
                if dat != 0 then
                    call dat.ProcessDrop( u,false,GetKillingUnit())
                endif
            endif
            set u = null
            return false
        endmethod
        
        //The method for drop processing outside of death events
        static method ForceProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            local integer i = 0
            local integer dropped = 0
            local integer looped = 0
            local integer dropmax = 0
            local real random = GetRandomReal(0.00,100.00)
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local boolean array IsLooped
            local thistype this = GenDrop[GetUnitTypeId(dying)]
            local DropTable itemlist = this.DropT.item
            if IsSpecific then
                set this = SpecDrop[GetHandleId(dying)]
                set dropmax = this.DropItemsMaxS
            else
                set dropmax = this.DropItemsMax
            endif
            loop
                set i = GetRandomInt(0, itemlist.itemcount)
                if not IsLooped[i] then
                    if random <= (((itemlist.Chance[i])*BonusGetMulti(killer)) + BonusGetDirect(killer)) then
                        set dropped = dropped + 1
                        call CreateItem(itemlist.ItemList[i], x, y)
                    endif
                    set looped = looped + 1
                    set IsLooped[i] = true
                endif
                exitwhen dropped >= dropmax or looped >= itemlist.itemcount 
            endloop
        endmethod
        
        static method InitUnit takes integer UnitRawCode, integer DropItemsMax returns nothing
            set data = .allocate()
            set GenDrop[UnitRawCode] = data
            set data.DropT = IPool.create()
            set data.DropItemsMax = DropItemsMax
        endmethod
        
        static method InitUnitSpecific takes unit u, integer DropItemsMax, boolean ProcessBoth returns nothing
            local integer UnitCode = GetHandleId(u)
            set data = .allocate()
            set SpecDrop[UnitCode] = data
            set data.DropT = IPool.create()
            if ProcessBoth then
                set DropBoth[UnitCode] = 1
            else
                set DropBoth[UnitCode] = 0
            endif
            set data.DropItemsMaxS = DropItemsMax
        endmethod
        
        static method AddPool takes integer UnitRawCode, DropTable dtPool, integer weight returns nothing
            set data = GenDrop[UnitRawCode]
            call data.DropT.add(dtPool, weight)
        endmethod
        
        static method AddPoolSpecific takes unit u, DropTable dtPool, integer weight returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.add(dtPool, weight)
        endmethod
        
        static method RemovePool takes integer UnitRawCode, DropTable dtPool returns nothing
            set data = GenDrop[UnitRawCode]
            call data.DropT.remove(dtPool)
        endmethod
        
        static method RemovePoolSpecific takes unit u, DropTable dtPool returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.remove(dtPool)
        endmethod
        
        static method Unregister takes integer rawcode returns nothing
            set data = GenDrop[rawcode]
            call data.DropT.destroy()
            call data.deallocate()
        endmethod
        
        static method UnregisterSpecific takes unit u returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.destroy()
            call data.deallocate()
        endmethod
        
        implement IDS_initsystem
        
    endstruct

endlibrary



JASS:
/*

    ----------------------------------------------------------------------------------

    Item Drop System 1.20 (Pool Version)
        by Adiktuz
    
    ----------------------------------------------------------------------------------
    
    Description:
    
        A system that would allow the dynamic creation of item pools per UnitTypeId or
        per specific unit and handle the process of item dropping from killing monsters. 
        
        This version is for creating an item pool for each unit in which the drop rates 
        of each item in a pool is dependent on each other. 
        
        In this version, whether an item will be dropped or not is determined per unit 
        or unit-type, and then the item that will be dropped will depend on the weight of 
        each item in the poolversus the whole pool. The drop chance of an item is equal to 
        its weight over the total weight of the item pool.
        
        Example Scenario:
        
        -I registered crabs to have a drop chance of 15%
        -Then I registered 3 items, each having a weight of 1
        -Then I registered another item having a weight of 3
        -Now the total weight would be 6
        -Now on the case that a crab dies, a number will be randomized
         from 0.00-100, if it falls under or equal to 15, that would mean that
         an item should be dropped, else no item will be dropped.
        -Now to get which item will be dropped from the crab's item pool, it will be
         randomized based on its weight relative to the total
        -that would mean that the 3 items having a weight of 1, each has a 1/6 chance
         of dropping while the fourth item which has a weight of 3 has a chance of
         3/6 or 50% chance that it would be the item which will be dropped.
        
    
   ----------------------------------------------------------------------------------
    
    Features:
        
        -You can add lots of items to the drop list of a unit
        
        -You can also add drop chance bonuses to a unit (direct or multiplier)
        
        -You can dynamically add or remove items from drop list of units*
        
        *can be both based on UnitTypeId or per specific unit
    
    ----------------------------------------------------------------------------------
    
    Requirements:
        - Wc3:TFT 1.24e
        - NewGenWE 
        - JassHelper (at least 0.A.2.A)
        - Table and IPool by Bribe
        - Ability to follow instructions
    
    ----------------------------------------------------------------------------------
    
    How to import:
        - Create a new trigger on your map, then convert to custom sript
        - Replace anything inside that trigger with the contents of this library
    
    ----------------------------------------------------------------------------------
    
    How to use: StructName = ItemDropPool
        
        General 
        
        static method InitUnit takes integer UnitRawCode, real chance, integer amount 
        -> registers the UnitRawCode into the system and sets its chance of dropping
           an item to real chance
        -> amount is the number of items that can be dropped at a time
           
        static method AddItem takes integer UnitRawCode, integer ItemRawCode, integer weight, integer amount
        -> registers an item into the itempool of UnitRawCode
        -> chance of having this item upon a successful drop roll is equal to the
           value of weight over the total weight of all registered items for that UnitRawCode
        -> amount is the number of items that can be dropped at a time
           
        static method RemoveItems takes integer UnitRawCode, integer ItemRawCode
        -> removes the specified item from the itempool of UnitRawCode
        
        static method Unregister takes integer rawcode returns nothing
        ->unregisters the rawcode from the system
        
        Specific
        
        static method InitUnitSpecific takes unit u, boolean ProcessBoth, real chance returns nothing
        -> registers a specific unit to the system
        -> if ProcessBoth is true, the system would process the ItemPool registered to this
           unit upon death, and also the itempool registered to the rawcode of this unit,
           which if both are present, will lead to 2 item drops if this unit dies and
           the roll chance falls within this unit's range
        
        static method AddItemSpecific takes unit u, integer ItemRawCode, integer weight
        -> adds the item to the itempool of the specific unit
        
        static method RemoveItemSpecific takes unit u, integer ItemRawCode
        -> removes the item from the itempool of the specific unit
        
        static method UnregisterSpecific takes unit u returns nothing
        -> unregisters the specific unit from the pool
        
        ----------------------------------------------------------------------------------
        If you want to force a drop process outside of unit death event use this
                
            call ItemDropPool.ForceProcessDrop(unit dying, boolean IsSpecific, unit killer)
        ----------------------------------------------------------------------------------
        
*/
    
     /*
        Do not edit below this line
    */
    
library ItemDropSystemPool requires IPool, BonusChance, RegisterPlayerUnitEvent
    
    
    globals
        private Table GenDrop
        private Table SpecDrop
        private Table DropBoth
    endglobals
    
   
    private module IDS_initsystem
        
        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.FilterDrop)
            set GenDrop = Table.create()
            set SpecDrop = Table.create()
            set DropBoth = Table.create()
        endmethod
        
    endmodule
    
    struct ItemDropPool
        
        static thistype data
        real chance
        IPool droppool
        integer amount
        
        static method ForceProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local thistype this = GenDrop[GetUnitTypeId(dying)]
            local integer i = 1
            if IsSpecific then
                set this = SpecDrop[GetHandleId(dying)]
            endif
            if GetRandomReal(0.00,100.00) <= this.chance*BonusGetMulti(killer) + BonusGetDirect(killer) then
                loop
                    exitwhen i > this.amount
                    call CreateItem(this.droppool.item, x, y)
                    set i = i + 1
                endloop
            endif
        endmethod
        
        method ProcessDrop takes unit killer, real x, real y returns nothing
            local integer i = 1
            if GetRandomReal(0.00,100.00) <= this.chance*BonusGetMulti(killer) + BonusGetDirect(killer) then
                loop
                    exitwhen i > this.amount
                    call CreateItem(this.droppool.item, x, y)
                    set i = i + 1
                endloop
            endif
        endmethod

        static method FilterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local integer i = GetHandleId(u)
            local integer a = GetUnitTypeId(GetTriggerUnit())
            local thistype this = GenDrop[a]
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            if SpecDrop[i] != 0 then
                if DropBoth[i] == 1 and this != 0 then
                    call this.ProcessDrop(GetKillingUnit(), x, y)
                endif
                set this = SpecDrop[i]
                call this.ProcessDrop(GetKillingUnit(), x, y)
                if not IsUnitType(u, UNIT_TYPE_HERO) then
                    call this.droppool.destroy()
                endif
                call this.destroy()
            elseif this != 0 then
                call this.ProcessDrop(GetKillingUnit(), x, y)
            endif
            set u = null
            return false
        endmethod
        
        static method InitUnit takes integer UnitRawCode, real chance, integer amount returns nothing
            set data = .allocate()
            set data.droppool = IPool.create()
            set data.chance = chance
            set data.amount = amount
            set GenDrop[UnitRawCode] = data
        endmethod
        
        static method InitUnitSpecific takes unit u, boolean ProcessBoth, real chance, integer amount returns nothing
            local integer UnitCode = GetHandleId(u)
            set data = .allocate()
            set data.amount = amount
            set data.droppool = IPool.create()
            set data.chance = chance
            set SpecDrop[UnitCode] = data
            if ProcessBoth then
                set DropBoth[UnitCode] = 1
            else
                set DropBoth[UnitCode] = 0
            endif
        endmethod
        
        static method AddItem takes integer UnitRawCode, integer ItemRawCode, integer weight returns nothing
            set data = GenDrop[UnitRawCode]
            call data.droppool.add(ItemRawCode, weight)
        endmethod
        
        static method AddItemSpecific takes unit u, integer ItemRawCode, integer weight returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.add(ItemRawCode, weight)
        endmethod
        
        static method RemoveItems takes integer UnitRawCode, integer ItemRawCode returns nothing
            set data = GenDrop[UnitRawCode]
            call data.droppool.remove(ItemRawCode)
        endmethod
        
        static method RemoveItemSpecific takes unit u, integer ItemRawCode returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.remove(ItemRawCode)
        endmethod
        
        static method Unregister takes integer rawcode returns nothing
            set data = GenDrop[rawcode]
            call data.droppool.destroy()
            call data.deallocate()
        endmethod
        
        static method UnregisterSpecific takes unit u returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.destroy()
            call data.deallocate()
        endmethod
        
        implement IDS_initsystem
        
    endstruct

endlibrary


JASS:
/*
    ----------------------------------------------------------------------------------

    Item Drop System Multi Table 1.20 (Pool Version)
        by Adiktuz
    
    ----------------------------------------------------------------------------------
    
    Description:
    
        A system that would allow the dynamic addition/removal of Item pools per UnitTypeId or
        per specific unit and handle the process of item dropping from killing monsters. 
        
        Utilizes the IPool for creation/modification of item pools
        
    
   ----------------------------------------------------------------------------------
    
    Features:
        
        -You can add lots item pools to a unit
        
        -You can dynamically add or remove item pools from units
    
    ----------------------------------------------------------------------------------
    
    Requirements:
        - Wc3:TFT 1.24e
        - NewGenWE 
        - JassHelper (at least 0.A.2.A)
        - Table and IPool by Bribe
        - Ability to follow instructions
    
    ----------------------------------------------------------------------------------
    
    How to import:
        - Create a new trigger on your map, then convert to custom sript
        - Replace anything inside that trigger with the contents of this library
    
    ----------------------------------------------------------------------------------
    
    How to use: StructName = ItemDropPoolMT
        
        General 
        
        static method InitUnit takes integer UnitRawCode, real chance, integer amount 
        -> registers the UnitRawCode into the system and sets its chance of dropping
           an item to real chance
        -> amount is the number of items that can be dropped at a time
           
        static method AddPool takes integer UnitRawCode, IPool dtable, integer weight
        -> adds the item pool to the pools used by UnitRawCode
        -> chance of using this item pool upon a successful drop roll is equal to the
           value of weight over the total weight of all registered item pools for that UnitRawCode
           
        static method RemovePool takes integer UnitRawCode, IPool dtable
        -> removes the specified item pool from those used by the UnitRawCode
        
        static method Unregister takes integer rawcode returns nothing
        ->unregisters the rawcode from the system
        
        Specific
        
        static method InitUnitSpecific takes unit u, boolean ProcessBoth, real chance, integer amount returns nothing
        -> registers a specific unit to the system
        -> if ProcessBoth is true, the system would process the ItemPool registered to this
           unit upon death, and also the itempool registered to the rawcode of this unit,
           which if both are present, will lead to 2 item drops if this unit dies and
           the roll chance falls within this unit's range
        -> integer amount is the amount of items that the unit can drop upon death
        
        static method AddItemSpecific takes unit u, IPool dtable, integer weight
        -> adds the item pool to those used by the specific unit
        
        static method RemovePoolSpecific takes unit u, IPool dtable
        -> removes the item pool to those used by the specific unit
        
        static method UnregisterSpecific takes unit u returns nothing
        -> unregisters the specific unit from the pool
        
        ----------------------------------------------------------------------------------
        If you want to force a drop process outside of unit death event use this
                
            call ItemDropPool.ForceProcessDrop(unit dying, boolean IsSpecific, unit killer)
        ----------------------------------------------------------------------------------
        
*/
    
     /*
        Do not edit below this line
    */
    
library ItemDropSystemPoolMT requires BonusChance, RegisterPlayerUnitEvent
    
    
    globals
        private Table DropBoth
        private Table GenDrop
        private Table SpecDrop
    endglobals
    
   
    private module IDS_initsystem
        
        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.FilterDrop)
            set DropBoth = Table.create()
            set GenDrop = Table.create()
            set SpecDrop = Table.create()
        endmethod
        
    endmodule
    
    struct ItemDropPoolMT
        
        static thistype data
        static IPool dtable
        real chance
        IPool DropT
        integer amount
        
        static method ForceProcessDrop takes unit dying, boolean IsSpecific, unit killer returns nothing
            local real x = GetUnitX(dying)
            local real y = GetUnitY(dying)
            local thistype this = GenDrop[GetUnitTypeId(dying)]
            local integer i = 1
            set dtable = this.DropT.item
            if IsSpecific then
                set this = SpecDrop[GetHandleId(dying)]
                set dtable = this.DropT.item
            endif
            if GetRandomReal(0.00,100.00) <= this.chance*BonusGetMulti(killer) + BonusGetDirect(killer) then
                loop
                    exitwhen i > this.amount
                    call CreateItem(dtable.item, x, y)
                    set i = i + 1
                endloop
            endif
        endmethod
        
        method ProcessDrop takes unit killer, real x, real y returns nothing
            local integer i = 1
            set dtable = this.DropT.item
            if GetRandomReal(0.00,100.00) <= this.chance*BonusGetMulti(killer) + BonusGetDirect(killer) then
                loop
                    exitwhen i > this.amount
                    call CreateItem(dtable.item, x, y)
                    set i = i + 1
                endloop
            endif
        endmethod

        static method FilterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local integer i = GetHandleId(u)
            local integer a = GetUnitTypeId(GetTriggerUnit())
            local thistype this = GenDrop[a]
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            if SpecDrop[i] != 0 then
                if DropBoth[i] == 1 and this != 0 then
                    call this.ProcessDrop(GetKillingUnit(), x, y)
                endif
                set this = SpecDrop[i]
                call this.ProcessDrop(GetKillingUnit(), x, y)
                if not IsUnitType(u, UNIT_TYPE_HERO) then
                    call this.DropT.destroy()
                endif
                call this.destroy()
            elseif this != 0 then
                call this.ProcessDrop(GetKillingUnit(), x, y)
            endif
            set u = null
            return false
        endmethod
        
        static method InitUnit takes integer UnitRawCode, real chance, integer amount returns nothing
            set data = .allocate()
            set data.DropT = IPool.create
            set data.amount = amount
            set GenDrop[UnitRawCode] = data
            set data.chance = chance
        endmethod
        
        static method InitUnitSpecific takes unit u, boolean ProcessBoth, real chance, integer amount returns nothing
            local integer UnitCode = GetHandleId(u)
            set data = .allocate()
            set data.DropT = IPool.create
            set data.amount = amount
            set data.chance = chance
            set SpecDrop[UnitCode] = data
            if ProcessBoth then
                set DropBoth[UnitCode] = 1
            else
                set DropBoth[UnitCode] = 0
            endif
        endmethod
        
        static method AddPool takes integer UnitRawCode, IPool dtPool, integer weight returns nothing
            set data = GenDrop[UnitRawCode]
            call data.DropT.add(dtPool, weight)
        endmethod
        
        static method AddPoolSpecific takes unit u, IPool dtPool, integer weight returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.add(dtPool, weight)
        endmethod
        
        static method RemovePool takes integer UnitRawCode, IPool dtPool returns nothing
            set data = GenDrop[UnitRawCode]
            call data.DropT.remove(dtPool)
        endmethod
        
        static method RemovePoolSpecific takes unit u, IPool dtPool returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.remove(dtPool)
        endmethod
        
        static method Unregister takes integer rawcode returns nothing
            set data = GenDrop[rawcode]
            call data.DropT.destroy()
            call data.deallocate()
        endmethod
        
        static method UnregisterSpecific takes unit u returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.DropT.destroy()
            call data.deallocate()
        endmethod
        
        implement IDS_initsystem
        
    endstruct

endlibrary
JASS:
scope AddItemsPMT initializer Init
    
    private function ApplyIDS takes nothing returns nothing
        //creates a new Item pool
        local IPool dtable = IPool.create()
        call DestroyTimer(GetExpiredTimer())
        //Orb of darkness is added to the item pool
        call dtable.add('odef',1)
        //Periapt of Vitality is added to the item pool
        call dtable.add('prvt',1)
        //now since both of them have weight of 1, and total weight is 2
        //they both have 50% chance to be dropped
        //rogue unit
        call ItemDropPoolMT.InitUnit('nrog', 100.0,1)
        //add the Drop Table to the rogue unit
        call ItemDropPoolMT.AddPool('nrog',dtable,1)
        //creates a new Drop Table Pool
        set dtable = IPool.create()
        //Circlet of nobility is added to the item pool
        call dtable.add('cnob',1)
        //Legion-Doom horn is added to the item pool
        call dtable.add('lgdh',1)
        //now since both of them have weight of 1, and total weight is 2
        //they both have 50% chance to be dropped
        //add the Drop Table to the rogue unit
        call ItemDropPoolMT.AddPool('nrog',dtable,1)
        //Now since both drop tables are registered to the rogue unit with a weight of 1,
        //each of them has a 50% to be chosen as the drop table to be used
        //when a rogue unit dies and triggers a drop
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TimerStart(CreateTimer(), 0.01, false, function ApplyIDS)
    endfunction
    
endscope



JASS:
/*

    ----------------------------------------------------------------------------------

    Item Drop System 1.20 (Destructable Version)
        by Adiktuz
    
    ----------------------------------------------------------------------------------
    
    -Works almost the same as the original Pool version but for destructables.
    -Also, you need to manually set when the drop is processed
    -for most of the functions here, when it takes the unitrawcode as parameter, its just replaced with
    the destructable raw code while if it takes a unit as parameter, its just replaced with a destructable

    example:
        call ItemDropPoolDest.InitDest(integer DestRawCode, real chance)
        call ItemDropPoolDest.InitDestSpecific(destructable u, boolean ProcessBoth, real chance)
        
    To call for processing of a drop: (look at the sample trigger for better understanding)
        call ItemDropPoolDest.ForceProcessDrop(destructable u, boolean IsSpecific, unit killer)
    
    To clear the drop allocated to a destructable:
        call ItemDropPoolDest.UnregisterSpecific(destructable u)
        call ItemDropPoolDest.Unregister(integer DestRawCode)
    
    Functions list:
        call ItemDropPoolDest.InitDest(integer DestRawCode, real chance, integer amount)
        call ItemDropPoolDest.InitDestSpecific(destructable u, boolean ProcessBoth, real chance, integer amount)
        call ItemDropPoolDest.ForceProcessDrop(destructable u, boolean IsSpecific, unit killer)
        call ItemDropPoolDest.AddItem(integer DestRawCode, integer ItemRawCode, integer weight)
        call ItemDropPoolDest.AddItemSpecific(destructable u, integer ItemRawCode, integer weight)
        call ItemDropPoolDest.RemoveItems(integer DestRawCode, integer ItemRawCode)
        call ItemDropPoolDest.RemoveItemSpecific(destructable u, integer ItemRawCode)
        call ItemDropPoolDest.UnregisterSpecific(destructable u)
        call ItemDropPoolDest.Unregister(integer DestRawCode)
        
    It would also be better if you will unregister specific drops allocated to a destructable
    when it dies.
*/
    
     /*
        Do not edit below this line
    */
    
library ItemDropSystemPoolDestructable requires IPool, BonusChance
    
    
    globals
        private Table GenDrop
        private Table SpecDrop
        private Table DropBoth
    endglobals
    
   
    private module IDS_initsystem
        
        static method onInit takes nothing returns nothing
            set GenDrop = Table.create()
            set SpecDrop = Table.create()
            set DropBoth = Table.create()
        endmethod
        
    endmodule
    
    struct ItemDropPoolDest
        
        static thistype data
        real chance
        integer amount
        IPool droppool
        
        static method ForceProcessDrop takes destructable u, boolean IsSpecific, unit killer returns nothing
            local real x = GetDestructableX(u)
            local real y = GetDestructableY(u)
            local thistype this = GenDrop[GetDestructableTypeId(u)]
            local integer i = 1
            if IsSpecific then
                set this = SpecDrop[GetHandleId(u)]
            endif
            if GetRandomReal(0.00,100.00) <= this.chance*BonusGetMulti(killer) + BonusGetDirect(killer) then
                loop
                    exitwhen i > this.amount
                    call CreateItem(this.droppool.item, x, y)
                    set i = i + 1
                endloop
            endif
        endmethod
        
        static method ClearDest takes destructable u returns nothing
            local thistype this = SpecDrop[GetHandleId(u)]
            call this.destroy()
            call this.droppool.destroy()
        endmethod
        
        static method InitDest takes integer DestRawCode, real chance, integer amount returns nothing
            set data = .allocate()
            set data.droppool = IPool.create()
            set data.chance = chance
            set data.amount = amount
            set GenDrop[DestRawCode] = data
        endmethod
        
        static method InitDestSpecific takes destructable u, boolean ProcessBoth, real chance, integer amount returns nothing
            local integer UnitCode = GetHandleId(u)
            set data = .allocate()
            set data.droppool = IPool.create()
            set data.chance = chance
            set data.amount = amount
            set SpecDrop[UnitCode] = data
            if ProcessBoth then
                set DropBoth[UnitCode] = 1
            else
                set DropBoth[UnitCode] = 0
            endif
        endmethod
        
        static method AddItem takes integer DestRawCode, integer ItemRawCode, integer weight returns nothing
            set data = GenDrop[DestRawCode]
            call data.droppool.add(ItemRawCode, weight)
        endmethod
        
        static method AddItemSpecific takes destructable u, integer ItemRawCode, integer weight returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.add(ItemRawCode, weight)
        endmethod
        
        static method RemoveItems takes integer DestRawCode, integer ItemRawCode returns nothing
            set data = GenDrop[DestRawCode]
            call data.droppool.remove(ItemRawCode)
        endmethod
        
        static method RemoveItemSpecific takes destructable u, integer ItemRawCode returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.remove(ItemRawCode)
        endmethod
        
        static method Unregister takes integer rawcode returns nothing
            set data = GenDrop[rawcode]
            call data.droppool.destroy()
            call data.deallocate()
        endmethod
        
        static method UnregisterSpecific takes destructable u returns nothing
            set data = SpecDrop[GetHandleId(u)]
            call data.droppool.destroy()
            call data.deallocate()
        endmethod
        
        implement IDS_initsystem
        
    endstruct

endlibrary


I recommend using the Pool versions...



Changelog
JASS:
/*
    ----------------------------------------------------------------------------------

Version 1.10

-Saved the instance index into a hashtable

-replaced the Get intances with Hashtable function calls

-Added the functionality to use the IDS for specific units

    ----------------------------------------------------------------------------------

Version 1.11

-removed unnecessary variables

    ----------------------------------------------------------------------------------

Version 1.12

-it now destroys the struct instance for specific unit item pools when the unit dies

    ----------------------------------------------------------------------------------

Version 1.13
-changed the debug messages into static ifs
-renamed the struct to IDS_Adik
-removed the local boolean used to exit the drop loop
-fixed errors on the How To Use

    ----------------------------------------------------------------------------------

Version 1.13b
-fixed a flaw which reduces the chances of having an item drop (by making the loop run through an unused index)
Note: The code in the map might actually say 1.13 or 1.12... ignore it...


    ----------------------------------------------------------------------------------

Version 1.13c
-added Unregister methods. I'm not sure if it would be totally useful though, but added it anyway.

    ----------------------------------------------------------------------------------

Version 1.14
-optimized the code, removed unnecessary things
-separated the BonusChance functions into another library
-created another version which uses IPool (read code for details)

    ----------------------------------------------------------------------------------

Version 1.14b
-some minor optimizations as pointed out by Magtheridon96

    ----------------------------------------------------------------------------------

Version 1.14c
-changed the struct names (just use ctrl+H to change them on your codes which uses this system)
-Made it use RegisterPlayerEvent (by Magtheridon)
-Removed the hashtables and used Tables instead (3 tables per System as I realized that one of the booleans I save on the hashtable is unnecessary)

    ----------------------------------------------------------------------------------

Version 1.14d
-removed the boolean SHOW_ERROR and used DEBUG_MODE instead
-also fixed the internal version number of the ItemDropSystem
-Removed the InitHashtable call from the Pool version and updated the code on the description

    ----------------------------------------------------------------------------------

Version 1.15
-added functions for forcing the drop process outside of death events for the unit drops
-added a library for processing drops for destructable (pool version only)
-fixed a flaw on the bonuschance library which causes the drop chance to be 0.0 if the killing unit is not registered in the bonuschance library

    ----------------------------------------------------------------------------------

Version 1.20
-Added a multi-table versions
 *This update has actually been long done on my comp, just forgot to upload it.
*/

Why have I made it?
JASS:
/*
   
    well, I was thinking of a new system to make and then this idea got to my head. Then I

browsed thru the spells section looking if there is already one but the ones which showed 

up on the search were unflexible so I decided to do it... ^_^


    I think this could be really useful for maps especially RPGs and the like...

*/

Credits to Bribe for his moderation and for his two libraries (Table and IPool) and for Magtheridon96 for RegisterPlayerEvent

Keywords:
system, inventory, items, drop, death, spawn, mob, jass, vjass, codes, triggers, we, warcraft, ragnarok, rpg, role palying, games, orpg, destructables
Contents

ItemDropSystem 1.20 (Map)

Reviews
12 Nov 2011 Bribe: You should delete this line from ItemDropSystemPool: "private hashtable Hash = InitHashtable()" (since you don't use it). Other than that, this is a really great item drop system. Approved.

Moderator

M

Moderator

12 Nov 2011
Bribe: You should delete this line from ItemDropSystemPool:

"private hashtable Hash = InitHashtable()" (since you don't use it).

Other than that, this is a really great item drop system. Approved.
 
Not bad. A nice, simple, useful system. However, there is one thing to note:
JASS:
    function IDSB_GetInstance takes unit u returns integer
        local integer i = 0
        loop
            exitwhen i > BU_Total
            if BonusUnit[i] == u then
                return i
            else
                set i = i + 1
            endif
        endloop
        return 0 
    endfunction

or:
JASS:
        static method GetInstance takes integer u returns thistype
            local thistype datum = 0
            local integer i = 0
            loop
                exitwhen i == thistype.UnitMax
                set datum = thistype.Stack[i]
                if datum.UnitRawCode == u then
                    set i = thistype.UnitMax
                else
                    set i = i + 1 
                endif
            endloop
            return datum
        endmethod

For functions like these, you will probably not want to use arrays. Imagine that you have 50 unit types or whatever. That means you have to loop through 50 unit types and check them to one integer. However, with a hashtable it is set and get.

Also, using hashtables could even allow you to add item drops to specific units (assigning structs to their handle id) regardless of their unit type. (although, you should still keep that feature) An indexing system would probably work too.
 
Level 16
Joined
May 1, 2008
Messages
1,605
Moin moin =)

Bah, if I see your keywords for your system, I know why I always get something different when I search something. This shouldn't be allowed -_-

Also, yes I tell the truth I don't know if that system is that useful. Yes it's a cool system but if I see the how to do list, it's much work, so at the end I will use the normal Item drop system.

About the triggering I can't say anything, sounds like Japanese stuff for me, but you systems/spell are always cool, so I think this can be approved, EXCEPT THE KEYWORDS!

Greetings and Peace
Dr. Boom
 
well, it says there put as many related as possible... ^_^

what normal Item drop? the one which you double click a unit to set the items? its just about the same work... ^_^

anyway, this system is for those who want some flexibility or cannot use the method above (like, no preplaced units or unit revives) or for those who don't want to create 1 trigger per unit-type... ^_^

anyway, thanks for the comments... ^_^
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I've written a Pool library (along with a List library and a Stack library) which I will be releasing after some more testing. It will make the process of "random drops" or "random unit" or random w/e extremely easy to work with. And with... unlimited instances. It's a wrapper for the Table library. Which also means, no handles are used except for the one hashtable in Table.

The structure is quite complex, but O(1) in complexity to retrieve a random value, making it extremely efficient to use over and over.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I have a lot of "chores" that I am currently working on, working 53 hours a week if you count travel time, preparing for my wedding, reviewing the Spells & Systems contest and tending to my own projects as a programmer.

Some notes:

  1. Use static-ifs for constant booleans
  2. Public/private variables are so you don't have to manually prefix things.
  3. IDS is a bad name for a public struct (too generic)
  4. Instead of "exitwhen end" and setting "end" to true, just use "exitwhen true" instead of the "set end = true" statement.
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
I think this is one of the best systems for the map i'm making, but i'm not sure of how to use it (Really a JASS noob), Could you help me with the basics, so I can go on from there?
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Hi Adiktus! Took me a while to read it... it's confusing, so mani lines and comments between codes, and, somehow, my JNPG doesn't turn the comments 'green' but anyway:

I don't know how to use JASS... the most I do is use custom scripts to call functions in some implemented JASS system. Though I read, I don't know how to set it (calling functions in GUI) for the rpg i'm making where almost every unit has a different drop table...

The system looks understandable only for people who knows JASS, even in the demo map, but i'll give it a shot :p

JASS:
call IDS_Adik.Init_Unit('ngno', 1)
//Circlet of nobility

It inits the unit 'ngno' and sets the DropItemsMax to 1.

JASS:
//DropItemsMax is the maximum number of items from the pool that a dying unit of that UnitTypeId can drop

The maximum number of items that the unit can drop AT THE SAME TIME when it dies? Or whenever it dies? I killed everything in your demomap and they all dropped 1, even when the murlocs have 3...
JASS:
call IDS_Adik.Init_Unit('nmrl', 3)

Honestly, I don't get the part of the DropItemsMax...

This one is easier...
JASS:
call IDS_Adik.Add_Item('nmrl', 'cnob', 1.00)
Adds 'cnob' item to the item pool of 'nmrl' unit, with a 1% chance to drop.

I don't get the difference between Specific Unit Drop and Generic/General unit drop xD
JASS:
        call IDS_Adik.Init_Unit_Specific(gnollblue, 1, true)
        call IDS_Adik.Add_Item_Specific(gnollblue, 'prvt', 100.00)
Where's the gnollblue in the map? This doesn't use the rawcode of the unit, then how does it know wich unit is?
 
The murlocs CAN drop three, but it will take a lot of luck to do that since each drop on the item pool has its own drop chance calculated separately...

so the chance of having all three drops is really small...

for example, if it has only three items in its pool having a percent drop chance of 1/2/3% respectively, and can drop a maximum of three items, the probability of acquiring three items in one kill would be

.01*.02*.03*100 = .0006 %, which is way lower than the percentage of acquiring only one item...

as for gnollblue, gnollblue is a unit in the map (gnollblue is a specific unit variable)

Specific Unit Drop is used to make only a specific unit drop an item, while other units of that type will not be affected...

On the gnollblue example, gnollblue is just one of the gnolls in the map... It is the same as the normal gnoll, but it has a different drop pool than the other gnolls since I used the Specific Item Drop for it...

And I tried my best to write an understandable How To Use which explains each and every function that can be used by the user... ^.^
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Hehe, I can see! I've been a lot of hours in front of the PC trying to find a good way to manage the item drops, and I see your system over and over and just seems too hard for my mind xD

I have implemented other systems in JASS, but it confuses me :p I hope you can help me. (I like your attitude by the way)

Lets say I have a Wolf creep [n00P], that has an item pool of 6, but can drop up to 2 items at the same time.

JASS:
call IDS_Adik.Init_Unit('n00P', 2)
call IDS_Adik.Add_Item('n00P', 'I00W', 10.00) // Ironwood Branch - 1
call IDS_Adik.Add_Item('n00P', 'I007', 10.00) // Health Potion - 2 
call IDS_Adik.Add_Item('n00P', 'I00C', 10.00) // Mana Potion - 3
call IDS_Adik.Add_Item('n00P', 'I006', 5.00) // Wolf Cap - 4
call IDS_Adik.Add_Item('n00P', 'I004', 5.00) // Wolf Claws - 5
call IDS_Adik.Add_Item('n00P', 'I00V', 5.00) // Wolf Vest - 6

Is that correct? Off course, I know that the unit is 'Init' in one trigger, and the items in the other.
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Your example triggers begins creating the gnollblue wich I don't need :p so i delete it, but there's also the exp timer...

JASS:
scope AddItems initializer Init
    
    
    private function ApplyIDS takes nothing returns nothing
        call DestroyTimer(GetExpiredTimer())

// Here I begin adding a bunch of units and items.   
    
        // Fox (Lvl 2)
        call IDS_Adik.Init_Unit('n00G', 2)
        call IDS_Adik.Add_Item('n00G', 'I007', 12.00) // Health Potion (1)
        etc...

Is this right or I have to delete the timer too?
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Adiktus, is there a way to make random item drops from variables with your system?... As an example:

JASS:
set GameItems[1] = 'I001'
set GameItems[2] = 'I002'
set GameItems[3] = 'I003'

        call IDS_Adik.Init_Unit('n00G', 2)
        call IDS_Adik.Add_Item('n00G', '/GameItem[Random number between 1 and 3]\', 15.00)

Otherwise, I have to add the same item group to each creep.

Example: I have 10 charms, and all the creeps have the chance to drop those 10 charms, with the same drop chance, I would have to add those 10 charms to each unit, instead of One 'random drop' between the list of those 10 charms.

Like this:

JASS:
        // Fox (Lvl 2)
        call IDS_Adik.Init_Unit('n00G', 2)
        call IDS_Adik.Add_Item('n00G', 'I007', 15.00) // Health Potion (1)
        call IDS_Adik.Add_Item('n00G', 'I00W', 9.00) // Ironwood Branch
        call IDS_Adik.Add_Item('n00G', 'I00O', 9.00) // Clothes
        call IDS_Adik.Add_Item('n00G', 'I00S', 9.00) // Hat
        call IDS_Adik.Add_Item('n00G', 'I00U', 9.00) // Thin Wood Log
        call IDS_Adik.Add_Item('n00G', 'I00T', 9.00) // Sandals
        call IDS_Adik.Add_Item('n00G', 'I00I', 5.00) // Charm of Strength
        call IDS_Adik.Add_Item('n00G', 'I00J', 5.00) // Charm of Agility
        call IDS_Adik.Add_Item('n00G', 'I00K', 5.00) // Charm of Intelligence
        call IDS_Adik.Add_Item('n00G', 'I01C', 5.00) // Charm of Lesser Attack Speed
        call IDS_Adik.Add_Item('n00G', 'I01B', 5.00) // Charm of Lesser Corruption
        call IDS_Adik.Add_Item('n00G', 'I00L', 5.00) // Charm of Lesser Daze
        call IDS_Adik.Add_Item('n00G', 'I018', 5.00) // Charm of Lesser Defense
        call IDS_Adik.Add_Item('n00G', 'I01D', 5.00) // Charm of Lesser Life Bonus
        call IDS_Adik.Add_Item('n00G', 'I01E', 5.00) // Charm of Lesser Mana Bonus
        call IDS_Adik.Add_Item('n00G', 'I01A', 5.00) // Charm of Lesser Power
        call IDS_Adik.Add_Item('n00G', 'I017', 5.00) // Charm of Lesser Precision
        
        
        // Pig (Lvl 2)
        call IDS_Adik.Init_Unit('n00H', 2)
        call IDS_Adik.Add_Item('n00H', 'I007', 15.00) // Health Potion (1)
        call IDS_Adik.Add_Item('n00H', 'I00W', 9.00) // Ironwood Branch
        call IDS_Adik.Add_Item('n00H', 'I00O', 9.00) // Clothes
        call IDS_Adik.Add_Item('n00H', 'I00S', 9.00) // Hat
        call IDS_Adik.Add_Item('n00H', 'I00U', 9.00) // Thin Wood Log
        call IDS_Adik.Add_Item('n00H', 'I00I', 5.00) // Charm of Strength
        call IDS_Adik.Add_Item('n00H', 'I00J', 5.00) // Charm of Agility
        call IDS_Adik.Add_Item('n00H', 'I00K', 5.00) // Charm of Intelligence
        call IDS_Adik.Add_Item('n00H', 'I01C', 5.00) // Charm of Lesser Attack Speed
        call IDS_Adik.Add_Item('n00H', 'I01B', 5.00) // Charm of Lesser Corruption
        call IDS_Adik.Add_Item('n00H', 'I00L', 5.00) // Charm of Lesser Daze
        call IDS_Adik.Add_Item('n00H', 'I018', 5.00) // Charm of Lesser Defense
        call IDS_Adik.Add_Item('n00H', 'I01D', 5.00) // Charm of Lesser Life Bonus
        call IDS_Adik.Add_Item('n00H', 'I01E', 5.00) // Charm of Lesser Mana Bonus
        call IDS_Adik.Add_Item('n00H', 'I01A', 5.00) // Charm of Lesser Power
        call IDS_Adik.Add_Item('n00H', 'I017', 5.00) // Charm of Lesser Precision

BTW: A small comment bug in your system.
JASS:
library ItemDropSystem 

/*
    Version 1.12
    by Adiktuz
*/
Should say Version 1.13b right? :p

BTW, I think that, if a unit has 2 items with the same drop rate, it drops both. I always get two drops of the same drop rate =/
 
Last edited:
Level 20
Joined
Jul 14, 2011
Messages
3,213
I didn't get the 'variable retuns an item rawcode' thing... Could you please show me how to do the random thing to avoid repeating the same items in all creeps?
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Another question... Every time a registered unit dies, the system checks through the Items added to that unit, and picks the drops based on chances?... Or all the items of each unit are pre-set at the Map Init...

If that's so, I can't use ItemType Variable Drops lists...
JASS:
        // Pig (Lvl 2)
        call IDS_Adik.Init_Unit('n00H', 2)
        call IDS_Adik.Add_Item('n00H', 'I007', 20.00) // Health Potion (1)
        call IDS_Adik.Add_Item('n00H', udg_GameItems[GetRandomInt(1, 7)], 12.00)
        call IDS_Adik.Add_Item('n00H', 'I01F', 12.00) // Gloves of Haste
        call IDS_Adik.Add_Item('n00H', udg_CharmsT1[GetRandomInt(6, 7)], 10.00)

I would just pick one of the randoms and set it, leaving the others out.

EDIT: Sadly, works the 2nd way.
 
Last edited:
Item type in GUI is just plain rawcode integers in jass you know... so you can use them as far as I know...

when a unit dies, it checks thru all items registered onto that unit, RANDOMLY, no matter when that item was registered (you can still manipulate the drop tables during game time)... then it randomizes a number and if it falls within the chance range of that item, the item is created... the system stops when all of the items on the drop list of that unit is looped, or when it already dropped items equal to its max drop amount...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Yeah, it drops randomly, but I can't add a random GameItem[Index] to the code, since it will set that random raw code and will never go through that code again.

I tested with the example I posted changing the chance to 100%: I got a lot of of items, but of the same kind, because the GameItem[RandomIndex] was set on MapInit, and not everytime the unit dies :)
 
Ahhh... I see... yeah since the code is only run once... well, you can play with the unit's drop list BEFORE it dies... that is the only way, though you will need a way to know if its going to die...

or if you can code, you can whip up a short add-on which will allow you to insert the random drop right when a unit dies, before the drop list is looped, and get it removed right after...

or if you can wait for maybe one or two weeks, I can whip up a specific add-on for you I think... its just that I'm on my final exams week right now so I can't afford to do it...
 
Last edited:
Level 22
Joined
Nov 14, 2008
Messages
3,256
-Initialization modules have to be private or else they will act like library initializers.

-Nice thing would be making Bribe's Table optional for this library.

-Show error constant? What happent with the usage of debug keyword :p

-No need to prefix a private variable (the hashtable).

-I'm not sure about this one but setting array sizes will not work as an array will always be of a size of 2^13 - 1 (we usually don't use [0] so -1).
 
I'll fix first the other points then keep the structure sine IPool does not currently support the independent drop rates that this one does...

But I would probably add another version which uses IPool for those who wants an item pool, which means that the item drop rates are dependent of each other, and in which the rate of item drop is fixed per unit...

So something like, a unit has 15% chance to drop an item... then if the chance rolled is within that, the item will be obtained from the item pool and would now depend on their relative weights... so for example, for this unit, if there are 3 items in its pool, all with a drop weight of 1, then it would mean that the chances to get each item is 1/3... now if one of them has a weight of 2, then that would make the total to 4, which means that item has a 50% drop chance while the other 2 items have a chance of 25%...
 
Last edited:
If u'll update you need to replace the function calls you used with the new ones since I edited the function names... You can geasily do this via ctrl+h...

Since ur uaing the original version, copy paste the original from this over the old one and also copy the bonus library, I think you also need to copy the Table library... Cannot check muh as I'm using my phone...

Btw, if you did the suggestion on my pm lasttime you need to reapply it...
 
JASS:
        static method FilterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local integer a = GetUnitTypeId(GetTriggerUnit())
            local thistype dat = LoadInteger(Hash, a, 1)
            local integer Handle = GetHandleId(u)
            if LoadBoolean(Hash, Handle, 2) then
                if LoadBoolean(Hash, Handle, 3) then
                    if dat != 0 then
                        call dat.ProcessDrop(GetKillingUnit(), GetUnitX(u), GetUnitY(u))
                    endif
                endif
                set dat = LoadInteger(Hash, Handle, 1)
                call dat.ProcessDrop(GetKillingUnit(), GetUnitX(u), GetUnitY(u))
                call dat.destroy()
            else
                if dat != 0 then
                    call dat.ProcessDrop( GetKillingUnit(), GetUnitX(u), GetUnitY(u))
                endif
            endif
            if not IsUnitType(u, UNIT_TYPE_HERO) then
                call FlushChildHashtable(Hash, Handle) 
                call dat.droppool.destroy()
            endif
            return false
        endmethod

This function can be optimized greatly.

You can cache some things into local variables, you have to null local handles, and you have to rethink the if/else/endif block's structure.
Also, you have a useless local variable declaration.

It should look like this:

JASS:
        static method filterDrop takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local integer i = GetHandleId(u)
            local thistype this = LoadInteger(Hash, a, 1)
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            
            if LoadBoolean(Hash, i, 2) then
                if LoadBoolean(Hash, i, 3) and this != 0 then
                    call this.ProcessDrop(GetKillingUnit(), x, y)
                endif
                set this = LoadInteger(Hash, i, 1)
                call this.ProcessDrop(GetKillingUnit(), x, y)
                call this.destroy()
            elseif this != 0 then
                call this.ProcessDrop(GetKillingUnit(), x, y)
            endif
            
            if not IsUnitType(u, UNIT_TYPE_HERO) then
                call FlushChildHashtable(Hash, i) 
                call this.droppool.destroy()
            endif
            
            set u = null
            
            return false
        endmethod
 
The events would be set-up by you... though my first idea was just for units [meaning just this system as it is]... I will think about adding something for destructables...

For the destructables, I might leave the setting-up of the event to the users instead of making the system run with every destructable death... and I might just support the pool type drops for the dest and not the other version...

EDIT: Hmm... since there is no native for destructable hit, if the users want the system to run with a destructable hit, then they must find a workaround themselves...

uploaded the map...

and oh, I think the getkiilingunit does not work with destructable death events...


well, since units are much more modifiable and there are lots more event for them, then I guess its better that you just use units for destructable/destroyable objects in your maps...
 
Last edited:
Top