• 🏆 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 Removal System v3.0.0.2

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: Kazeon
This system allows you to remove items after a certain period of that item being on the ground.

You can protect items from being removed in this way. All items currently in the map are protected from being removed.
If you want a safe area for items in your map simply create a rect/region. Then use a trigger and detect when a unit drops an item in that region. Then call the method to allow those items to be protected.

You can set a generic timer called TIME in the map. This variable allows you to set the timeout for items. They will be removed when this time period is met.

You can change the timeout of an item while the items timer is counting down.

If you use the setExpirationTimer it will remove that item from the protected list (if it is in that list) It will then set that item up to be removed.
When using the method above you can have items get removed from anywhere in game including the players inventory.

I uploaded the system as the last update seemed to bug.

JASS:
library ItemRemoval /* version 3.0.0.2

================================ Requirements =================================
               Requires JNGP 
    */uses Table, /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url] by bribe
    
================================ Installing =================================
    STEP 1: Install JNGP first if you dont have JNGP.
    STEP 2: Install Table by bribe the link is above.
    STEP 3: Install Alloc by nestharus the link is above. You may be able to use some other alloc systems.
    STEP 4: Create a rect and save it in the method below.
    STEP 5: Copy and paste this library to your map.
    
================================== How to use ===================================================
    Items that are created right after a unit dies whether its 
        through triggers( only works if item is placed at the position of the unit that died)
        or the item table are automatically stored.
    Items that are created through triggers that you want protected need to be added immediately using the protectItem method.
    Alternately u can add items to be removed manually but it is not needed unless u create an item through triggers and 
        do not place that item near a dead unit. These types of items should be added using the additem method.
    
================================== API ===================================================
struct MapItemCleanup 

    static method addItemToRemove takes item itm, real time returns nothing
        This function allows the user to add an item that they made using triggers or an item that was protected previously.
        
    static method protectItem takes item itm returns nothing
        This function allows the user to protect a specific item from being removed by this system.
        
    static method deProtectItem takes item itm returns nothing
        This function allows the user to stop protecting a previoulsy protected item from being removed by this system.
        
    static method isItemProtected takes item itm returns boolean
        This function allows the user to check if a specific item is protected from being removed by this system.
   
    static method protectItemType takes integer itemTypeId returns nothing
        This function allows the user to protect an item type from being removed by this system.
        
    static method deProtectItemType takes integer itemTypeId returns nothing
        This function allows the user to stop protecting a previoulsy protected item type from being removed by this system.
        
    static method isItemTypeProtected takes integer itemTypeId returns boolean
        This function allows the user to check if an item type is protected from being removed by this system.
        
    static method setItemExpiration takes item itm, real seconds returns nothing
        This function allows you to set an items expiration time.
        
    static method getItemExpiration takes item itm returns real
        This function allows you to get the time left till the item will get removed.
        
    static method changeItemExpiration takes item itm, integer timeToIncreaseBy returns nothing
        This function allows you to increase and decrease the timeout of a specific item.
        To increase by 5 seconds pass a 5. To decrease by 5 seconds pass a -5.
        
    static method forceItemExpiration takes item itm, real seconds returns nothing
        This function allows the user to force remove an item. This item will be removed from anwhere in the map when the timer runs out.
    
================================ Explanation =================================
    This system allows you to remove all items tht r on the ground for a certain amount of time. 
    If you pick an item up it will restart the timer.
    You should know that any preplaced items will not be removed by this system. They are all set to keep the items on the map.
    Once they are picked up and dropped. Then they will be removed when timer expires.

================================ Things You can change =================================
    You can change the TIME variable. It determines the time in which an item is removed
    If u use an inventory system you may need to change this value INVENTORY_CHECKER.
        It checks if the item is in the inventory of the unit so u dont have people
        pick up the item just to reset the items counter. 
        Should only have to be changed if u use an inventory system with a delay.
    
================================ Thanks to ppl tht helped =================================
    Magtheridon96
    Almia
    Maker
    Zwiebelchen
*/

globals
    private constant real TIME = 5  // how long the item will last after being dropped
    private constant real INVENTORY_CHECKER = 0.01 //how long an item has to be picked up b4 removed from the list
    private TableArray ItemTable
    
    private Table ProtectedItems // This keeps track of all the item types that should never be removed from the game by this system.
    private rect ItemRect
endglobals

    struct MapItemCleanup extends array
        private static Table unitDataX
        private static Table unitDataY
        private static Table unitItemCheck
        private static Table unitHasItem
        
        private static method cleanItemTable takes item itm returns nothing
            local integer id = GetHandleId(itm)
            call ItemTable[id].remove(0)
            call ItemTable[id].remove(1)
            call DestroyTimer(ItemTable[id].timer[2])
            call ItemTable[id].remove(2)
            call ItemTable[id].remove(3)
            call unitHasItem.remove(id)
        endmethod
        
        private static method removeItem takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local integer id = ItemTable[GetHandleId(t)][0]
            if ItemTable[id].boolean[0] or ItemTable[id].boolean[3] then
                call RemoveItem(ItemTable[id].item[1])
                call cleanItemTable(ItemTable[id].item[1])
            endif
            set t = null
        endmethod
        
        static method addItemToRemove takes item itm, real time returns nothing
            local integer id = GetHandleId(itm)
            local timer t = CreateTimer()
            set ItemTable[GetHandleId(t)][0] = id
            set ItemTable[id].boolean[0] = true
            set ItemTable[id].item[1] = itm
            set ItemTable[id].timer[2] = t
            set ItemTable[id].boolean[3] = false
            call TimerStart(t, time, false, function thistype.removeItem)
            set t = null
        endmethod
        
        static method protectItem takes item itm returns nothing
            local integer id = GetHandleId(itm)
            set ProtectedItems[id] = id
        endmethod
        
        static method deProtectItem takes item itm returns nothing
            call ProtectedItems.remove( GetHandleId(itm))
        endmethod
        
        static method isItemProtected takes item itm returns boolean
            return ProtectedItems.has(GetHandleId(itm))
        endmethod
        
        static method protectItemType takes integer itemTypeId returns nothing
            set ProtectedItems[itemTypeId] = itemTypeId
        endmethod
        
        static method deProtectItemType takes integer itemTypeId returns nothing
            call ProtectedItems.remove(itemTypeId)
        endmethod
        
        static method isItemTypeProtected takes integer itemTypeId returns boolean
            return ProtectedItems.has(itemTypeId)
        endmethod
        
        static method setItemExpiration takes item itm, real seconds returns nothing
            // * this function allows you to add items when created by a trigger and adds a specific time to the item * /
            local integer id = GetHandleId( itm)
            if ProtectedItems.has(id) then
                call ProtectedItems.remove( id)
            endif
            if not unitHasItem.boolean[GetHandleId(itm)] then
                call addItemToRemove(itm, seconds)
            endif
        endmethod
        
        static method getItemExpiration takes item itm returns real
            // * this function allows you to add items when created by a trigger and adds a specific time to the item * /
            return TimerGetRemaining(ItemTable[GetHandleId( itm)].timer[2])
        endmethod
        
        static method changeItemExpiration takes item itm, real timeToIncreaseBy returns nothing
            // * this function allows you to increase and decrease the timeout of a specific item. * /
            // * to increase by 5 seconds pass a 5. To decrease by 5 seconds pass a -5. * /
            local integer id = GetHandleId( itm)
            call TimerStart(ItemTable[id].timer[2], TimerGetRemaining(ItemTable[id].timer[2]) + timeToIncreaseBy, false, function thistype.removeItem)
        endmethod
        
        static method forceItemExpiration takes item itm, real seconds returns nothing
            set ItemTable[GetHandleId(itm)].boolean[3] = true
            call addItemToRemove(itm, seconds)
        endmethod
        
        private static method inventoryCheck takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local integer id = GetHandleId(t)
            local unit u = unitItemCheck.unit[id]
            local item itm = unitItemCheck.item[-1 * id]
            set unitHasItem.boolean[GetHandleId(itm)] = true
            if UnitHasItem( u, itm) then
                set ItemTable[GetHandleId(itm)].boolean[0] = false
            endif
            call unitItemCheck.remove(id)
            call unitItemCheck.remove(-1 * id)
            call DestroyTimer(t)
            set t = null
        endmethod
        
        private static method removePickedUpItem takes nothing returns boolean
            local item itm = GetManipulatedItem()
            local integer id = GetHandleId( itm)
            local timer t
            local integer id2
            if ItemTable[id].boolean[0] and not ItemTable[id].boolean[3] then
                set t = CreateTimer()
                set id2 = GetHandleId(t)
                set ItemTable[id2][0] = id
                set unitItemCheck.unit[id2] = GetTriggerUnit()
                set unitItemCheck.item[-1 * id2] = itm
                call TimerStart( t, INVENTORY_CHECKER, false, function thistype.inventoryCheck)
                set t = null
            endif
            set itm = null
            return false
        endmethod

        private static method isNotInTable takes integer itemTypeId, integer handleId returns boolean
            // * checks if enumerated item is not in ItemTable to be removed and is not in the protected items table * /
            return not ItemTable[handleId].boolean[0] and not ItemTable[itemTypeId].boolean[0] /*
            */ and not ProtectedItems.has(handleId) and not ProtectedItems.has(itemTypeId)
        endmethod

        private static method mapInitItems takes nothing returns nothing
            //* gets items that are preplaced on the map * /
            local item itm = GetEnumItem()
            local integer id = GetHandleId( itm)
            if not isNotInTable(GetItemTypeId(itm), id) then
                set ProtectedItems[id] = id
            endif
            set itm = null
        endmethod

        private static method getMapInitItems takes nothing returns nothing
            //* stores items that are preplaced on the map * /
            call EnumItemsInRect( bj_mapInitialPlayableArea, null, function thistype.mapInitItems)
        endmethod

        private static method addManipItem takes nothing returns boolean
            // * passes manipulated items to AddItem method and checks if timer is running * /
            local item itm = GetManipulatedItem()
            local integer id = GetHandleId( itm)
            if not isNotInTable(GetItemTypeId(itm), id) then
                call addItemToRemove( itm, TIME) // This adds the Manipulated item with a standard time.
            endif
            set unitHasItem.boolean[id] = false
            set itm = null
            return false
        endmethod

        private static method addEnumItems takes nothing returns nothing
            // * passes enumerated items to AddItem method and checks if timer is running * /
            local item itm = GetEnumItem()
            if isNotInTable(GetItemTypeId(itm), GetHandleId(itm)) then
                call addItemToRemove( itm, TIME) // This adds the Enumerated item with a standard time.
            endif
            set itm = null
        endmethod
        
        private static method getNewItems takes nothing returns nothing
            // * gets new items tht were dropped by killed units or tht were created in a trigger and placed in map * /
            local timer t = GetExpiredTimer()
            local integer id = GetHandleId( t)
            local real X = unitDataX.real[ id]
            local real Y = unitDataY.real[ id]
            call MoveRectTo( ItemRect, X, Y)
            call unitDataX.remove( id)
            call unitDataY.remove( id)
            call DestroyTimer( t)
            set t = null
            call EnumItemsInRect( ItemRect, null, function thistype.addEnumItems)
        endmethod

        private static method getNewItemDelay takes nothing returns boolean
            // * creates a delay after unit dies so tht the item has time to be created * /
            local timer t = CreateTimer()
            local integer id = GetHandleId( t)
            local unit u = GetTriggerUnit()
            set unitDataX.real[id] = GetUnitX( u)
            set unitDataY.real[id] = GetUnitY( u)
            call TimerStart( t, 0.00, false, function thistype.getNewItems)
            set t = null
            set u = null
            return false
        endmethod
        
        private static method itemSold takes nothing returns boolean
            // * removes sold items from Table * /
            local integer id = GetHandleId( GetSoldItem())
            if not ItemTable[id].boolean[0] then
                call ItemTable[id].remove(0)
            endif
            if ProtectedItems.has( id) then
                call ProtectedItems.remove( id)
            endif
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            local integer i = 0
            local trigger t = CreateTrigger()
            local trigger t1 = CreateTrigger()
            local trigger t2 = CreateTrigger()
            local trigger t3 = CreateTrigger()
            set ItemTable = TableArray[0x2000]
            set ProtectedItems = Table.create()
            set unitDataX = Table.create()
            set unitDataY = Table.create()
            set unitItemCheck = Table.create()
            set unitHasItem = Table.create()
            call thistype.getMapInitItems()
            set ItemRect =  Rect( 0, 0, 64, 64)
            loop
                exitwhen i > 15
                call TriggerRegisterPlayerUnitEvent( t, Player(i), EVENT_PLAYER_UNIT_DROP_ITEM, null)
                call TriggerRegisterPlayerUnitEvent( t1, Player(i), EVENT_PLAYER_UNIT_PICKUP_ITEM, null)
                call TriggerRegisterPlayerUnitEvent( t2, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
                call TriggerRegisterPlayerUnitEvent( t3, Player(i), EVENT_PLAYER_UNIT_SELL_ITEM, null)
                set i = i + 1
            endloop
            call TriggerAddCondition( t, function thistype.addManipItem)
            call TriggerAddCondition( t1, function thistype.removePickedUpItem)
            call TriggerAddCondition( t3, function thistype.itemSold)
            call TriggerAddCondition( t2, function thistype.getNewItemDelay)
            set t = null
            set t1 = null
            set t2 = null
            set t3 = null
        endmethod
    endstruct
    
endlibrary


version 3.0.0.2
Fixed small mistakes.​
version 3.0.0.1
Fixed a few bugs.
Removed Alloc as it was not necessary.​
version 3.0.0.0
Reworked system to be more compact and a lot more efficient.​
version 2.7.0.6
changed a mistake w call create() that i made​
version 2.7.0.5
changed the method addItem
changed the GetPlayableMapRect() to bj_mapInitialPlayableArea​
version 2.7.0.0
changed the API a little
removed the createitem method
you now do not need a preplaced rect
added double free protection in case an item was removed by another trigger
added protection from ppl abusing the system when picking up item when they dont have a free inventory space available​
version 2.6.0.0
redid system a bit now removes items when there time expires.
does not loop through all the items anymore
this is a lot more efficient than it was b4​
version 2.5.1.0
added an alternate method to adding items to system​
version 2.5.0.1
forgot to remove saved values from Table​
version 2.5.0.0
system now uses only a struct
API is now updated and improved
trigger add actions have been changed to conditions
a rect has been added to speed up the checking for items when unit dies​
version 2.1.0.0
changed the system to use structs
changed the special timer function. now easier to use just enter your time in seconds and your done​
version 2.0.5.0
fixed small bug with pausing item timer​
version 2.0.0.0
made changes to functionality
removes dead or removed items every second
adds all items that are preplaced into the table tht doesnt allow removal of those items
fixed a small bug with unpausing timer​
version 1.9.0.0
added description of what every function does​
version 1.8.5.0
made changes to functionality
added ability to put custom timer on different items
now removes sold items​
version 1.7.5.0
made changes to functionality
added a table for items u dont want to be removed
fixed some bugs​
version 1.6.0.0
made changes to functionality
added ability to pause item removal​
version 1.5.0.0
uses table by bribe​


Keywords:
item, item removal, item removal system, system, removal system, dimf, deathismyfriend
Contents

System Item Drop System (Map)

Reviews
12th Dec 2015 IcemanBo: For long time as NeedsFix. Rejected. IcemanBo: http://www.hiveworkshop.com/forums/spells-569/item-removal-system-v3-0-0-2-a-256794/#post2640239 11:04, 12th Sep 2014 PurgeandFire: Review...
Level 19
Joined
Mar 18, 2012
Messages
1,716
Alloc is not listed as requirement, it's just written as a comment. The url of Alloc is not up to date.
I recommend you use Alloc as an optional requirement, because the only valid link to it is on GitHub at the moment.
This block doesn't make any sense.
JASS:
            if not ProtectedItems.has( id) then
                call ProtectedItems.remove( id)
            endif
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Alloc is not listed as requirement, it's just written as a comment. The url of Alloc is not up to date.
I recommend you use Alloc as an optional requirement, because the only valid link to it is on GitHub at the moment.
This block doesn't make any sense.
JASS:
            if not ProtectedItems.has( id) then
                call ProtectedItems.remove( id)
            endif

Thanks BPower didn't know about the little mistake in my code.

I forgot to remove Alloc as I never use the actual struct instances in this system. I will update when I can with the fixes and any new features I can think of or anyone else thinks of.

Uh, is there any system like this before, I think I saw at least one before this...

No idea. I didn't look. My old version of this was on the jass section but I removed it. Then I put it here.

Vexorian's TomeSentinel? I think you never heard that one.

Not sure who you are referring that statement to ?
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
static method deProtectItem takes item itm returns nothing
deProtect seems childish. deprotect is better, personal taste.

JASS:
            local integer id = GetHandleId( itm)
            if not isNotInTable(GetItemTypeId(itm), id) then
                call addItemToRemove( itm, TIME) // This adds the Manipulated item with a standard time.
            endif
            set unitHasItem.boolean[GetHandleId(itm)] = false
You seem to forgot something there ;)

And does it automatically remove items that are created by trigger?
 
Review:
  • unitDataX/Y isn't really needed. You should just store the unit in the getNewItemDelay, and then use GetUnitX() and GetUnitY() in getNewItems. Better yet--you can avoid using hashtables at all, and just use an array and a static timer (which is better than having to create/destroy a timer each time):
    JASS:
            globals
                private timer newItems = CreateTimer()
                private unit array newItemUnits
                private integer newItemCount = 0
            endglobals
    
            //... later in the code ...
    
            private static method getNewItems takes nothing returns nothing
                loop
                    exitwhen newItemCount == 0
                    call MoveRectTo(ItemRect, GetUnitX(newItemUnits[newItemCount]), GetUnitY(newItemUnits[newItemCount]))
                    call EnumItemsInRect(ItemRect, null, function thistype.addEnumItems)
                    set newItemCount = newItemCount - 1
                endloop
            endmethod
    
            private static method getNewItemDelay takes nothing returns boolean
                set newItemCount = newItemCount + 1
                set newItemUnits[newItemCount] = GetTriggerUnit()
                call TimerStart(newItems, 0, false, function thistype.getNewItems)
            endmethod
  • I'm a little iffy on the addItemToRemove/removeItem/cleanItemTable. Is it necessary to use all those hashtable functions? Couldn't you use a struct to store it, wait the duration, and then clean up? (correct me if I'm wrong though, I'm only looking at a glance).

Otherwise, it looks good.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
An array with static timer would mean another table to keep track of several items going off at the same time. Also it would mean extra checks and a faster timer to make sure no items were missed.

UnitX/Y is needed. When I add the unit and try to use the unit it causes a bug. Whenever a unit gets removed before the item is checked its location reverts to 0,0. I need the time to make sure that the item gets dropped since an item being dropped is not immediate. I can use the structs in this case though. So I will switch it out to get rid of the tables that will no longer be needed.

Also it seems that there was a bug when uploading. The old map was uploaded not the new one. I hope to make changes soon. I think it would be better to stick with multiple timers though.
 
JASS:
static method setItemExpiration takes item itm, real seconds returns nothing
    // * this function allows you to add items when created by a trigger and adds a specific time to the item * /
    local integer id = GetHandleId( itm)
    if ProtectedItems.has(id) then
        call ProtectedItems.remove( id)
    endif
    if not unitHasItem.boolean[GetHandleId(itm)] then
        call addItemToRemove(itm, seconds)
    endif
endmethod

...

static method forceItemExpiration takes item itm, real seconds returns nothing
    set ItemTable[GetHandleId(itm)].boolean[3] = true
    call addItemToRemove(itm, seconds)
endmethod
^There is not explained when to use which one.

JASS:
static method getItemExpiration takes item itm returns real
    // * this function allows you to add items when created by a trigger and   adds a specific time to the item * /
    return TimerGetRemaining(ItemTable[GetHandleId( itm)].timer[2])
endmethod
^Read the comment. It is wrong.

Is it explained at a place what these booleans should state? ItemTable[id].boolean[0/3]

At the moment you potentially create/destroy many timers, so you could consider using TimerUtils.
But why not just index needed items with help of an array and decrement a countdown for each item[index]? Then only one global timer was needed.
Table still could be used to work with itemTypes, but the whole removal structure will probably be improved with looping through an array.

^If not, you still could take actual usage of structs to bind data, like PnF suggested.

I'm certain using an ItemIndexer would also have benefits. (especially the usage of onIndex())

onInit it's okay that you do, but the triggers don't need to be nulled.
onInit you could store the player into a local variable when iterating.

It is more readable writing the whole name instead of making abbreviation and cutting 1 or 2 letters for names.

As this is not the jass section, it would be helpful for most users to provide a short demo.
 
Top