System IDEA - Item Death Event & Autoindexer

Azlier

Old World Ghost
Reaction score
461
IDEA. The best thing since sliced items, this autoindexer and item death event is here to do all the work for you!

IDEA Code
JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ IDEA ~~ By Azlier ~~ Version 2.0 ~~ 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is IDEA?
//         - Idea is an autoindexer for items.
//         - It also provides an item death event.
//
//    =Pros=
//         - Easy to use.
//         - Highly efficient.
//         - Automatically removes dead items (including tomes).
//
//    =Cons=
//         - You can no longer use item user data.
//         - Items cannot be perfectly autoindexed, so there is a very small dip in efficiency from unit indexing.
//
//  Functions:
//
//    GetItemIndex takes item whichItem returns integer
//      Returns the item's unique index for use as an index in an array
//
//    GetItemId takes item whichItem returns integer
//      Just like GetItemIndex, but faster and does NOT work on items you create
//      directly on the map via a trigger. Use with care.
//
//    TriggerRegisterItemDeathEvent takes trigger whichTrigger returns nothing
//      Causes the given trigger to fire when an item dies.
//
//    GetDyingItem takes nothing returns item
//      //Returns the item that died in an item death event.
//
//
//  IDEA structs (the fine print).
//
//      - Insert the line 'implement IDEA' into any struct to make it an IDEA struct.
//      - IDEA structs are not to be created or destroyed by the user. To get an item's 
//        unique IDEA struct, use StructName[item].
//      - You cannot give members default values (i.e. private integer member = 5).
//      - You cannot use array members.
//      - IDEA structs should 'extend array' in order to remove useless functions
//        and prevent mistakes.
//      - There are two optional methods you can use in IDEA structs:
//          IDEA_onCreate takes nothing returns nothing
//            This is called when the struct is 'created' for the unit.
//            In here you can assign members their default values, which
//            you would usually assign in their declarations.
//                (eg: set this.i=5)
//          IDEA_onDestroy takes nothing returns nothing
//            This is called when the struct is 'destroyed' for the item.
//            This is your substitute for the normal onDestroy method.
//      - You can use .item to get the item the struct is indexed for.
//      - You can typecast between IDEA structs freely because they all
//        share the same indexed unit.
//      - Please refrain from allocating or deallocating IDEA structs or an
//        ogre will eat your children.
//
//
//  Thanks:
//         - Jesus4Lyf: For Event, and the AIDS structs that IDEA structs are essentially rips of, and the documentation template.
//
//         - Vexorian: For TimerUtils.
//
//
//  How to import:
//         - Create a trigger named IDEA.
//         - Convert it to custom text and replace all the trigger text with this.
//         - Import IDEAZinc if you are a Zinc user.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

library IDEA initializer Init requires TimerUtils, Event

globals
    private constant real DECAY_PERIOD = 2
    //How long it takes for an item to be removed after death.
endglobals

//Please don't touch below.

globals
    private trigger onAlloc = CreateTrigger()
    private trigger onDealloc = CreateTrigger()
    private Event Ev
    private item DeadItem = null
endglobals

public struct s_Data
    static boolexpr onDeath
    static hashtable ht = InitHashtable()
    static thistype tempData = 0
    
    item i
    trigger t
    
    //! textmacro IDEA__RegisterItem takes ITEM
        set s_Data.tempData = s_Data.create()
        set s_Data.tempData.t = CreateTrigger()
        set s_Data.tempData.i = $ITEM$
        call SetItemUserData($ITEM$, s_Data.tempData)
        call TriggerRegisterDeathEvent(s_Data.tempData.t, $ITEM$)
        call TriggerAddCondition(s_Data.tempData.t, s_Data.onDeath)
        call TriggerEvaluate(onAlloc)
        set s_Data.tempData = 0
    //! endtextmacro
    
    static method finishDeath takes nothing returns nothing
        local timer tm = GetExpiredTimer()
        set tempData = GetTimerData(tm)
        call ReleaseTimer(tm)
        call RemoveItem(tempData.i)
        set s_Data.tempData = 0
    endmethod
    
    static method onDeathFunc takes nothing returns boolean
        local timer tm = NewTimer()
        call SaveWidgetHandle(ht, 0, 0, GetTriggerWidget())
        set DeadItem = LoadItemHandle(ht, 0, 0)
        call Ev.fire()
        call SetTimerData(tm, GetItemUserData(DeadItem))
        call TimerStart(tm, DECAY_PERIOD, false, function thistype.finishDeath)
        set DeadItem = null
        return false
    endmethod
    
    static method pickUp takes nothing returns boolean
        local item it = GetManipulatedItem()
        if GetItemUserData(it) == 0 then
            //! runtextmacro IDEA__RegisterItem("it")
        endif
        set it = null
        return false
    endmethod
    
    static method purchase takes nothing returns boolean
        local item it = GetSoldItem()
        if GetItemUserData(it) == 0 then
            //! runtextmacro IDEA__RegisterItem("it")
        endif
        set it = null
        return false
    endmethod
    
    static method pawn takes nothing returns boolean
        set tempData = GetItemUserData(GetSoldItem())
        call RemoveItem(tempData.i)
        set s_Data.tempData = 0
        return false
    endmethod
    
    static method onOrder takes nothing returns boolean
        local item it = GetOrderTargetItem()
        if it != null then
            if GetItemUserData(it) == 0 then
                //! runtextmacro IDEA__RegisterItem("it")
            endif
        endif
        return false
    endmethod
    
    static method initEnumUnits takes nothing returns boolean
        local item it
        local unit u = GetFilterUnit()
        local integer ind
        if UnitInventorySize(u) > 0 then
            set ind = 5
            loop
                set it = UnitItemInSlot(u, ind)
                if it != null then
                    //! runtextmacro IDEA__RegisterItem("it")
                endif
                exitwhen ind == 0
                set ind = ind - 1
            endloop
            set it = null
        endif
        set u = null
        return false
    endmethod
    
    static method initEnum takes nothing returns boolean
        local item it = GetFilterItem()
        //! runtextmacro IDEA__RegisterItem("it")
        set it = null
        return false
    endmethod
    
    static method onInit takes nothing returns nothing
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local trigger t3 = CreateTrigger()
        local trigger t4 = CreateTrigger()
        local integer i = 15
        
        loop
            call TriggerRegisterPlayerUnitEvent(t1, Player(i), EVENT_PLAYER_UNIT_PICKUP_ITEM, null)
            call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SELL_ITEM, null)
            call TriggerRegisterPlayerUnitEvent(t3, Player(i), EVENT_PLAYER_UNIT_PAWN_ITEM, null)
            call TriggerRegisterPlayerUnitEvent(t4, Player(i), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
            exitwhen i == 0
            set i = i - 1
        endloop
        call TriggerAddCondition(t1, function s_Data.pickUp)
        call TriggerAddCondition(t2, function s_Data.purchase)
        call TriggerAddCondition(t3, function s_Data.pawn)
        call TriggerAddCondition(t4, function s_Data.onOrder)
            
        set onDeath = Filter(function thistype.onDeathFunc)
        set Ev = Event.create()
    endmethod
    
endstruct

private function Init takes nothing returns nothing
    local group g = CreateGroup()
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, function s_Data.initEnumUnits)
    call DestroyGroup(g)
    set g = null
    call EnumItemsInRect(bj_mapInitialPlayableArea, function s_Data.initEnum, null)
    call DestroyBoolExpr(function s_Data.initEnum)
endfunction

function TriggerRegisterItemDeathEvent takes trigger t returns nothing
    call Ev.register(t)
endfunction

function GetTriggerItem takes nothing returns item //for compatibility
    return DeadItem
endfunction

function GetDyingItem takes nothing returns item
    return DeadItem
endfunction

//Hear that horrible ripping sound?
public struct DEFAULT extends array
    method IDEA_onCreate takes nothing returns nothing
    endmethod
    method IDEA_onDestroy takes nothing returns nothing
    endmethod
endstruct

public function RegisterOnCreate takes boolexpr b returns nothing
    call TriggerAddCondition(onAlloc, b)
endfunction

public function RegisterOnDestroy takes boolexpr b returns nothing
    call TriggerAddCondition(onDealloc, b)
endfunction

module IDEA
    private static delegate IDEA_DEFAULT IDEA_DELEGATE = 0
    
    static method operator[] takes item it returns thistype
        return GetItemIndex(it)
    endmethod
        
    method operator item takes nothing returns item
        return s_Data.tempData.i
    endmethod
    
    private static method IDEA_onAllocation takes nothing returns boolean
        call thistype(s_Data.tempData).IDEA_onCreate()
        return false
    endmethod
    
    private static method IDEA_onDeallocation takes nothing returns boolean
        call thistype(s_Data.tempData).IDEA_onDestroy()
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        call IDEA_RegisterOnCreate(Filter(function thistype.IDEA_onAllocation))
        call IDEA_RegisterOnDestroy(Filter(function thistype.IDEA_onDeallocation))
    endmethod
endmodule

function RemoveItemEx takes item i returns nothing //For compatibility.
    if s_Data.tempData == 0 then
        set s_Data.tempData = GetItemUserData(i)
    endif
    call TriggerEvaluate(onDealloc)
    call DestroyTrigger(s_Data.tempData.t)
    set s_Data.tempData.t = null
    set s_Data.tempData.i = null
    call s_Data.tempData.destroy()
    set s_Data.tempData = 0
endfunction

hook RemoveItem RemoveItemEx

function GetItemIndex takes item i returns integer
    set s_Data.tempData = GetItemUserData(i)
    if s_Data.tempData == 0 then
        //! runtextmacro IDEA__RegisterItem("i")
    endif
    return s_Data.tempData
endfunction

function GetItemId takes item i returns integer
    return GetItemUserData(i)
endfunction

endlibrary

IDEAZinc Code
JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ IDEAZinc ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  IDEAZinc is just an addition that lets you use IDEA structs in Zinc.
//  They are constructed and used exactly like vJass IDEA structs, but the
//  module implementation should look like:
//      module IDEAZinc;
//
//
//
//  How to import:
//         - Create a trigger named IDEAZinc.
//         - Convert it to custom text and replace all the trigger text with this..
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//! zinc
library IDEAZinc requires IDEA {

public module IDEAZinc {
    private static delegate IDEA_DEFAULT IDEA_DELEGATE = 0;
    
    static method operator[] (item it) -> thistype {
        return GetItemIndex(it);
    }
    
    method operator item () -> item {
        return IDEA_s_Data.tempData.i;
    }
    
    private static method IDEA_onAllocation () -> boolean {
        thistype(IDEA_s_Data.tempData).IDEA_onCreate();
        return false;
    }
    
    private static method IDEA_onDeallocation () -> boolean {
        thistype(IDEA_s_Data.tempData).IDEA_onDestroy();
        return false;
    }
    
    private static method onInit () {
        IDEA_RegisterOnCreate(Filter(function thistype.IDEA_onAllocation));
        IDEA_RegisterOnDestroy(Filter(function thistype.IDEA_onDeallocation));
    }
}

}
//! endzinc

Here's an example to talk for me.
JASS:
scope Test initializer Init

private struct IDEAStruct

    implement IDEA

    method IDEA_onCreate takes nothing returns nothing
        call BJDebugMsg("Index allocated: " + I2S(this))
    endmethod
    
    method IDEA_onDestroy takes nothing returns nothing
        call BJDebugMsg("Index deallocated: " + I2S(this))
    endmethod
    
endstruct

private function Actions takes nothing returns nothing
    call BJDebugMsg("Item died.")
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterItemDeathEvent(t)
    call TriggerAddAction(t, function Actions)
endfunction

endscope
 

Attachments

  • IDEA Test Map.w3x
    45 KB · Views: 265

Jesus4Lyf

Good Idea™
Reaction score
397
This looks really cool. I think even I might use this, if I was to attach data to items. :)

No H2I, inlining GetItemData function, uses Event appropriately... This is good stuff for any RPG style map with a custom item system! :D (Or any map that uses a custom item system, really... You could do cool things with this.)

I have no idea what the module is for, though.
>private thistype array Datas
Shouldn't this be static? Just a guess.

>(I fear I am using Event too much for my own good.)
Event is actually a really -good- thing to use. It's free functionality, and rather well written. :)
You did inspire its creation too, of course. XD

One thing:
There's this assumption that typecasting can't be done in WC3 without the return bug, and that assumption is wrong. Certain types are typecast-happy, it seems. Widgets are one.
JASS:
constant function GetTriggerItem takes nothing returns item
    return GetTriggerWidget()
    return null
endfunction

Can be...
JASS:
constant function GetTriggerItem takes nothing returns item
    return GetTriggerWidget()
endfunction

BUT! Before you rush off and change this, JassHelper has a bug in it. It will inline this, which is cannot do, because that will throw a syntax error.

But! The syntax error is only thrown because of a bug in the custom syntax checker that comes with NewGen. It was not written to accomodate these typecastable types (Widget may actually be the only one, but I'm not sure). You will actually find that even if you go ahead and let this be a one-liner, get inlined, throw a syntax error due to a type mismatch and then test the map, the map will work anyway, even with a syntax error. This needs to be fixed in the syntax checker. ;)

So my verdict? Uhhh... Be userfriendly and make it not inline by keeping the redundant return null, but I'll probably let the syntax error show up every time I save the map by removing it. :)

Oh, so on that note, why doesn't W2I have return null, too? I mean... it throws a syntax error for me due to the inline. Was this fixed in latest NewGen or something? Hmm...
 

Azlier

Old World Ghost
Reaction score
461
>Shouldn't this be static? Just a guess.
Oops. Stupid mistake.

>I have no idea what the module is for, though.
It's this system's equivalent of //! runtextmacro PUI()

>It will inline this, which is cannot do
I foresaw that.

EDIT: Wait, what's that? A useless PickUpTrig? It seems I forgot to remove that.
 

Azlier

Old World Ghost
Reaction score
461
It makes the struct act like an array of itself. That uses the item as an index. Or something like that. I need a better explanation.

Remember, this is PUI for items. With one extra feature.
 

Viikuna

No Marlo no game.
Reaction score
265
I remember I once asked in wc3c what they tought about using Widget Death Event with dynamic triggers for items.

But yea, this is cool. It seems to be exactly how it is supposed to be actually.



Jesus4Lyf.

Oh fuck, give it a break already. There is nothing wrong with H2I, if you use it in situations where it is a good option. ( Timers )

Unit&Item UserDatas exist for a reason, and yea there is no need to use H2I with either of those.



And about Event. You should add RegisterPriorityEvent, because it can be usefull sometimes. ( It would take some integer priority, and the actions with highest priority would be executed first in the loop. )
 

Azlier

Old World Ghost
Reaction score
461
Minor update. Fixed a logic failure that caused the system to ignore items in slot 1 of inventories, and added the RegisterItem function.

Oh, and items that are all used up in the inventory trigger the death event. Found that out while testing. Huh.
 

Trollvottel

never aging title
Reaction score
262
1 Trigger for each item? Very nice, indeed. Oh and could you add a "why to use this"?
 

Azlier

Old World Ghost
Reaction score
461
Does not the features section speak for itself? It does semi-autoindexing. And item deaths. That is all.

EDIT: Another update. Now uses fully automatic indexing. No more user dependency. Of course, there is an efficiency loss. I feel that people who like manual indexing *cough* Jesus4Lyf *cough* would like the old version.
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
You can't use RemoveItem. Hide and kill the item, or use RemoveItemEx.
Or just kill + remove, it's good for units (decay of corpse) but anyway i guess it doesn't matter for items.
 
Reaction score
341
You should do this..

JASS:
//! textmacro IDEA
module IDEA
    
    private static thistype array Datas
    
    static method operator [] takes item i returns thistype
        return .Datas[GetItemIndex(i)]
    endmethod
    
    static method operator []= takes item i, thistype d returns nothing
        set .Datas[GetItemIndex(i)] = d
    endmethod
    
endmodule
//! endtextmacro
//! runtextmacro IDEA()


That way people can use a textmacro and a module. :p
 

Azlier

Old World Ghost
Reaction score
461
And the point of that is...? More clutter with 0 gain?
 

Azlier

Old World Ghost
Reaction score
461
I don't care what you like, you take what I give :rolleyes:.
 

Azlier

Old World Ghost
Reaction score
461
What?! And I've been needing such a thing forever? Bah. Well, there's always the death event :p.'

EDIT: Cannot find this ItemDex anywhere on the face of the Intranets :\.
 

Kenny

Back for now.
Reaction score
202
Itemdex.

On a more related note. This system is pretty cool, seems like it could come in handy for a wide variety of maps.
 

Jesus4Lyf

Good Idea™
Reaction score
397
</3

Yeah, I would've liked the older version more, that's why I didn't complain about RemoveItemEx - I'm sure with a timer looping through each item you could accomplish Remove detection just fine (which should not fire the event, users can kill/remove to fire it). Note that if you do this, you should NOT loop through all items at once, instead please loop through one every 0.03125 of a second or something. The detection of removal for recycling the index doesn't have to be fast, just happen some time. :p

I'm pretty sure ItemDex isn't released here anyway. Which is the logic people have used to copy my stuff on WC3C, last I checked.

What's with the ItemFinder timer thing, anyway? Why do you need it? Can't say I like it. :( Didn't this work before without it?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top