HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Triggeractions.

09-02-2007, 08:56 PM#1
Here-b-Trollz
Hi. I was wondering, is it possible to attach structs to triggeractions (yes), and then reference the struct inside the triggeraction, based on what triggeraction is running?

Collapse Example:
struct Cache
    unit thing
    real stuff
endstruct

function Actions takes nothing returns nothing
    local Cache c=GetHandleInteger(GetRunningTriggerAction())
    call SetWidgetLife(c.thing,c.stuff)
endfunction

function AddtoTrig takes unit u, real r, trigger t returns nothing
    local Cache c=Cache.create()
    local triggeraction ta=TriggerAddAction(t,function Actions)
    set c.thing=u
    set c.stuff=r
    call SetHandleInteger(ta,c)
endfunction

So that whenever trigger 't' runs, it will run 'Actions' once for each unit added, and the unit will differ based on what the handle of the triggeraction is? Is this even possible?


-Thanks.
09-02-2007, 09:28 PM#2
DioD
you cant trace trigger actions if trigger have more then one.

you have to create personal triggers
09-02-2007, 09:32 PM#3
Here-b-Trollz
Damn. There is no way to replicate this, then?
09-02-2007, 09:33 PM#4
Pyrogasm
There's no way to get the running triggeraction, but you could store each TriggerAction added and then loop through each of them and the appropriate structs.
09-02-2007, 09:58 PM#5
Here-b-Trollz
That sounds impractical. This is supposed to be a way of attaching multiple structs of different types to an instance of a struct. Any way of doing that?
09-02-2007, 10:12 PM#6
Vexorian
The first code is seems to be logically equivalent to using separate triggers, not really sure why you have to use separate triggeractions when a separate trigger does exactly the same thing
09-02-2007, 11:03 PM#7
Here-b-Trollz
Errr.... let's see if I can explain this...

I have a struct, which stores a unit, its owner and 4 triggers.

On struct creation, the unit is created with the parameters given, and the 4 triggers are created. Each gets an event registered based on what the name of the trigger is:

Collapse JASS:
    struct hero
        unit hero
        player owner
        trigger pickup
        trigger use
        trigger order
        trigger drop
        
        static method Pickup takes nothing returns nothing
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            if(IsBag(GetManipulatedItem()))then
                call BagAddActions(h,GetManipulatedItem())
            endif
        endmethod
        
        static method Drop takes nothing returns nothing
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            if(IsBag(GetManipulatedItem()))then
                call BagRemoveActions(h,GetManipulatedItem())
            endif
        endmethod
        
        static method create takes player p, integer id, real x, real y, real face returns hero
            local hero h=hero.allocate()
            set h.hero=CreateUnit(p,id,x,y,face)
            set h.owner=p
            set h.pickup=CreateTrigger()
            set h.use=CreateTrigger()
            set h.order=CreateTrigger()
            set h.drop=CreateTrigger()
            call HashtableA.put(H2I(h.pickup),h)
            call HashtableA.put(H2I(h.use),h)
            call HashtableA.put(H2I(h.order),h)
            call HashtableA.put(H2I(h.drop),h)
            call TriggerRegisterUnitEvent(h.pickup,h.hero,EVENT_UNIT_PICKUP_ITEM)
            call TriggerRegisterUnitEvent(h.use,h.hero,EVENT_UNIT_USE_ITEM)
            call TriggerRegisterUnitEvent(h.order,h.hero,EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(h.drop,h.hero,EVENT_UNIT_DROP_ITEM)
            call TriggerAddAction(h.pickup,function hero.Pickup)
            call TriggerAddAction(h.drop,function hero.Drop)
            return h
        endmethod
    endstruct

pickup and drop both get actions added to them. When the hero picks up an item, then, if it is a bag, BagAddActions(hero,item) is run. This adds everything the bag needs to function into the hero's 4 triggers. The thing is, the triggers need to know what bag the actions are for to function correctly (the used/dropped item won't always be a bag), so, in order to keep all the information needed from the bag item, it must be attached to the created triggeractions, creating 'dynamic' actions. On Drop, if the item is a bag, BagRemoveActions(hero,item) is run. This removes the triggeractions from the hero's triggers.


I just need a workaround to get what triggeraction is running, and this whole thing will work.
09-02-2007, 11:18 PM#8
Vexorian
well, since you can't get triggering trigger action, just have some sort of array holding all bags, it will be healthier anyways since that way you won't need to get into more attach hell than you need to.
09-02-2007, 11:25 PM#9
Here-b-Trollz
I actually have an array of all bags. It's called a struct. Even if I had an array, how would I know which index to use, without attachment of some sort?

I am reluctant to post absolutely all of the code in which this is needed - even I think it gets confusing sometimes. Meh. View at your own risk:

HAZARDOUS CODING

Collapse JASS:
scope WMI
 
    globals
        private constant integer Nextpage = 'I002'
        private constant integer Cancel = 'I001'
        private constant integer Blank = 'I003'
    endglobals
 
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
 
    //! runtextmacro HASHTABLE("Mitems","private")
    //! runtextmacro HASHTABLE("PUAs","private")
    
    struct pitem
        item main
        item PUA
        integer PUAid
        
        method getmain takes nothing returns item
            return .main
        endmethod
        
        method getPUA takes nothing returns item
            return .PUA
        endmethod
        
        static method get takes item i returns pitem
            local real l=GetWidgetLife(i)
            if(l==74.)then //It's a main.
                return HashtableMitems.get(H2I(i))
            elseif(l==73.)then //PUA!!!!!
                return HashtablePUAs.get(H2I(i))
            endif
            return 0
        endmethod
        
        static method create takes integer id, integer puaid, real x, real y returns pitem
            local pitem p=pitem.allocate()
            set p.main=CreateItem(id,0.,0.)
            call SetWidgetLife(p.main,74.)
            call SetItemInvulnerable(p.main,true)
            call SetItemVisible(p.main,false)
            call HashtableMitems.put(H2I(p.main),p)
            set p.PUA=CreateItem(puaid,x,y)
            set p.PUAid=puaid
            call SetWidgetLife(p.PUA,73.)
            call SetItemInvulnerable(p.PUA,true)
            call SetItemVisible(p.PUA,false)
            call HashtablePUAs.put(H2I(p.PUA),p)
            return p
        endmethod
    endstruct
    
    //! runtextmacro HASHTABLE("A","private")
    
    struct hero
        unit hero
        player owner
        trigger pickup
        trigger use
        trigger order
        trigger drop
        item nextpage
        item cancel
        pitem array pitems[6]
        
        
        static method Pickup takes nothing returns nothing
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local pitem p=pitem.get(GetManipulatedItem())
            if(bag.is(p) and bag.addactions(h,p))then //It was a bag
                //Actions were added
            //elseif(book.is(p) and book.addactions(h,p))then //It was a book
                //Actions were added
            //elseif(shop.is(p) and shop.addactions(h,p))then //It was a shop
                //Actions were added
            endif
        endmethod
        
        static method Drop takes nothing returns nothing
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local pitem p=pitem.get(GetManipulatedItem())
            local bag b
            if(bag.is(p) and bag.removeactions(h,p))then //It was a bag
                //Actions were added
            //elseif(book.is(p) and book.removeactions(h,p))then //It was a book
                //Actions were added
            //elseif(shop.is(p) and shop.removeactions(h,p))then //It was a shop
                //Actions were added
            endif
        endmethod
        
        static method create takes player p, integer id, real x, real y, real face returns hero
            local hero h=hero.allocate()
            set h.hero=CreateUnit(p,id,x,y,face)
            set h.owner=p
            set h.pickup=CreateTrigger()
            set h.use=CreateTrigger()
            set h.order=CreateTrigger()
            set h.drop=CreateTrigger()
            call HashtableA.put(H2I(h.pickup),h)
            call HashtableA.put(H2I(h.use),h)
            call HashtableA.put(H2I(h.order),h)
            call HashtableA.put(H2I(h.drop),h)
            call TriggerRegisterUnitEvent(h.pickup,h.hero,EVENT_UNIT_PICKUP_ITEM)
            call TriggerRegisterUnitEvent(h.use,h.hero,EVENT_UNIT_USE_ITEM)
            call TriggerRegisterUnitEvent(h.order,h.hero,EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(h.drop,h.hero,EVENT_UNIT_DROP_ITEM)
            call TriggerAddAction(h.pickup,function hero.Pickup)
            call TriggerAddAction(h.drop,function hero.Drop)
            set h.nextpage=CreateItem(Nextpage,0,0)
            set h.cancel=CreateItem(Cancel,0,0)
            return h
        endmethod
    endstruct
    
    //! runtextmacro HASHTABLE("Bags","private")
    //! runtextmacro HASHTABLE("BagUse","private")
    //! runtextmacro HASHTABLE("BagOrder","private")
    
    struct bag
        pitem bag
        pitem array pitems[20]
        triggeraction use
        triggeraction order
        
        static method is takes pitem p returns boolean
            return HashtableBags.containsKey(H2I(p.main))
        endmethod
        
        static method get takes pitem p returns bag
            return HashtableBags.get(H2I(p.main))
        endmethod
        
        static method Use takes nothing returns nothing //Actions for when a hero uses an item.
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger())) //Get the hero from the trigger
            local pitem p=pitem.get(GetManipulatedItem()) //Get the pitem from the item
            local bag b=HashtableBagUse.get(H2I(h.use)) //<--- I want the triggeraction here
        endmethod
        
        static method Order takes nothing returns nothing //Actions for when a hero issued an order.
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local pitem p=pitem.get(GetManipulatedItem())
            local bag b=HashtableBagOrder.get(H2I(h.order)) //<--- I want the triggeraction here
        endmethod
        
        static method addactions takes hero h, pitem p returns boolean
            local bag b
            if(bag.is(p))then    
                set b=bag.get(p)
                set b.use=TriggerAddAction(h.use,function bag.Use)
                call HashtableBagUse.put(H2I(h.use),b)
                set b.order=TriggerAddAction(h.order,function bag.Order)
                call HashtableBagOrder.put(H2I(h.order),b)
                return true
            endif
            return false
        endmethod
        
        static method removeactions takes hero h, pitem p returns boolean
            return true
        endmethod
        
        static method create takes integer id, integer puaid, real x, real y returns bag
            local bag b=bag.allocate()
            set b.bag=pitem.create(id,puaid,x,y)
            call HashtableBags.put(H2I(b.bag.main),b)
            return b
        endmethod
    endstruct
    
    //===========================================================================
    public function InitTrig takes nothing returns nothing
    endfunction

endscope

09-02-2007, 11:32 PM#10
Vexorian
Quote:
I actually have an array of all bags. It's called a struct[. Even if I had an array, how would I know which index to use, without attachment of some sort?

I am reluctant to post absolutely all of the code in which this is needed - even I think it gets confusing sometimes. Meh. View at your own risk:

When a trigger is executed it will run all its triggeractions without any discrimination.

If you have an array that holds all the bags owned by the hero then ... just a single loop that will process all the bags owned by the hero when the trigger is executed will do the work, it is exactly the same as having a trigger action per bag and attaching a bag id for each trigger action, only that it will be much faster
09-02-2007, 11:44 PM#11
Here-b-Trollz
But, hero is a struct. Bag is a struct. I could have a bag array, but there really isn't a limit to the number of bags a hero can have (actually, I guess it would be 126, since each bag can hold 20 items, and the hero has 6 slots, but that slices the instance limit of hero down to 65, which is really dumb), and there are other types of item holders too.

The idea of using static method and trigger actions was to have everything needed for a bag to be contained within the struct bag.


EDIT: Argh. I hate it when Vex is right. Now, I am simply disregarding any sort of bag looping - everything is done dynamically. Although perhaps it is not the fastest thing in the world, my instances have jumped up from 65 to 1365, which is much less dumb. I am kinda happy with my ability to be uber, and devise clever yet simple methods of solving problems.
09-03-2007, 11:21 AM#12
weaaddar
Holy crap somebody copied my insane idea of pitems. I'm impressed. I'm not even mad. I thought pitems were a terrible idea, but somebody bothered to try it.


The way I'm handling bags now is using my itemMenu interface engine. Since all bags act exactly the same the only thing that really changes is the addCondition, and the size. Which I don't handle yet as itemVector is a static 12 until I think of a better way to do IVs. (I'm guessing link-lists of 4 items at a node but thats still terrible). I still could do gc but its much more humanely readable code wise.

Alternatively you could just add a simple currentBag variable. And on open just do...
Collapse JASS:
local hero h=GetTriggerUnit()
set h.currentBag=GetManipulatedItem()
09-03-2007, 01:26 PM#13
Vexorian
If you were worried about instance limits you could have just used a linked list.

Get real, no player is ever gonna appreciate the experience of owning 126 bags. Even a limit of 5 bags per hero is going to work...

edit: 65 heroes is yet another huge instance limit anyways... If each player had 3 heroes that would be 36 heroes? You would need to think of giving each player 6 heroes to break this limit... SIX heroes!
09-03-2007, 01:29 PM#14
Here-b-Trollz
Actually yeah, I love pitems. They make most of this code really easy for me to write.

Hero is still an object, and, when you create it, he still gets the four triggers. Each of the triggers is a single unit event, so, I know which hero ran it, so I attach the hero directly to each trigger.

I do have a current bag variable. It's called openbag. It only applies if the hero is currently in the bag. Otherwise, it's 0 (since bag is a struct as well).


The reason I don't use GetManipulatedItem() to get the bag is because stuff happens with the bag even if the item isn't the bag. Yay!


Note: Bag size is static for me too. I am thinking of wrapping bag with textmacros, so you can call:
//! runtextmacro CreateBag("name","cap")

EDIT: Yeah, originally, I didn't see a problem with an instance limit for heros. It just had to at maybe 12-24. Any more, and the players aren't getting the experience I wanted for a hero - lots of managing.

Bag was a bigger issue. I was worried about failures. Sure, nobody would ever WANT 126 bags, but, if they got them all, then 'bag' would start failing. And that's something I don't want.



EDIT 2: Gaaargh. Just for the sake of posting code, I am posting my revised code:

HAZARDOUS CODING

Collapse JASS:
struct RubberBathToy
    item Duckie
    item Boat
    item Soap
endstruct

Real Code

Collapse JASS:
scope WMI
 
    globals
        private constant integer Nextpage = 'I002'
        private constant integer Cancel = 'I001'
        private constant integer Blank = 'I003'
    endglobals
 
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    globals
        private sound aSimError
    endglobals
    
    private function SimError takes player p, string msg returns nothing
        if(aSimError==null)then
            set aSimError=CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
            call StartSound(aSimError)
        endif
        if(GetLocalPlayer()==p)then
            call ClearTextMessages()
            call DisplayTimedTextToPlayer(p,0.52,-1.00,2.00,"|cffffcc00"+msg+"|r")
            call StartSound(aSimError)
        endif
    endfunction
    
    private function UnitGetItemSlot takes unit u,item i returns integer
        local integer a=0
        loop
            exitwhen a>5
            if(i==UnitItemInSlot(u,a))then //Found it
                return a
            endif
            set a=a+1
        endloop
        return -1 //Not found
    endfunction
 
    //! runtextmacro HASHTABLE("Mitems","private")
    //! runtextmacro HASHTABLE("PUAs","private")
    //! runtextmacro HASHTABLE("A","private")
    
    struct pitem
    
        //The pitem is basically an improved version of the item.
        //It contains information about the item, as well as a
        //group of methods for enhancing the functionality of the
        //pitem. There are two purposes for the pitem in WMI:
        //1) It is passed through methods as its own type
        //2) It allows an item to be picked up at any time, even
        //if inventory is full.
        
        item main
        item PUA
        integer PUAid
        
        method getmain takes nothing returns item
            return .main
        endmethod
        
        method getPUA takes nothing returns item
            return .PUA
        endmethod
        
        static method is takes item i returns boolean
            local real l=GetWidgetLife(i)
            if(l==74.)then //It's a main.
                return HashtableMitems.containsKey(H2I(i))
            elseif(l==73.)then //PUA!!!!!
                return HashtablePUAs.containsKey(H2I(i))
            endif
            return false
        endmethod
        
        static method get takes item i returns pitem
            local real l=GetWidgetLife(i)
            if(l==74.)then //It's a main.
                return HashtableMitems.get(H2I(i))
            elseif(l==73.)then //PUA!!!!!
                return HashtablePUAs.get(H2I(i))
            endif
            return 0
        endmethod
        
        static method TryStack takes hero h, pitem p returns boolean
            local integer a=0
            local item temp
            local integer id=GetItemTypeId(p.main)
            local integer max=GetItemLevel(p.main)
            local integer charges
            local integer tempcharges
            loop
                exitwhen a>5
                set temp=UnitItemInSlot(h.hero,a)
                if(GetItemTypeId(temp)==id)then //Matching types
                    set charges=GetItemCharges(p.main)
                    set tempcharges=GetItemCharges(temp)
                    if(tempcharges+charges>max)then //Too many!
                        if(tempcharges==max)then //Full
                        else //Add remainder
                            set tempcharges=max-tempcharges //The number left to add
                            call SetItemCharges(p.main,charges-tempcharges)
                            call SetItemCharges(temp,max)
                        endif
                    else //Just add them all
                        call SetItemCharges(temp,tempcharges+charges)
                        call p.destroy()
                        return true
                    endif
                endif
                set a=a+1
            endloop
            return false
        endmethod
        
        static method TryAdd takes hero h, pitem p returns boolean
            local integer a=0
            local item temp
            local integer state=h.state
            set h.state=-1
            if(state==0)then //MAIN!!!
                call SetItemVisible(p.main,true)
                if(not UnitAddItem(h.hero,p.main))then //Needs to be hidden again
                    call SetItemVisible(p.main,false)
                else
                    call TriggerSleepAction(0)
                    set h.state=state
                    return true
                endif
            elseif(state==1)then //BAG!!!
                loop
                    exitwhen a>3
                    set temp=UnitItemInSlot(h.hero,a)
                    if(GetItemTypeId(temp)==Blank)then //It's a blank
                        call UnitRemoveItem(h.hero,temp)
                        call RemoveItem(temp)
                        call SetItemVisible(p.main,true)
                        call UnitAddItem(h.hero,p.main)
                        call TriggerSleepAction(0)
                        set h.state=state
                        return true
                    endif
                    set a=a+1
                endloop
            endif
            set h.state=state
            return false
        endmethod
        
        static method TryBag takes hero h, pitem p returns boolean
            local bag b=h.openbag
            return b.addpitem(p)
        endmethod
        
        static method TryMain takes hero h, pitem p returns boolean
            local integer a=1
            local integer id=GetItemTypeId(p.main)
            local integer max=GetItemLevel(p.main)
            local integer charges
            local integer tempcharges
            loop
                exitwhen a>6
                if(GetItemTypeId(h.pitems[a].main)==id)then //Matching types
                    set charges=GetItemCharges(p.main)
                    set tempcharges=GetItemCharges(h.pitems[a].main)
                    if(tempcharges+charges>max)then //Too many!
                        if(tempcharges==max)then //Full
                        else //Add remainder
                            set tempcharges=max-tempcharges //The number left to add
                            call SetItemCharges(p.main,charges-tempcharges)
                            call SetItemCharges(h.pitems[a].main,max)
                        endif
                    else //Just add them all
                        call SetItemCharges(h.pitems[a].main,tempcharges+charges)
                        call p.destroy()
                        return true
                    endif
                endif
                set a=a+1
            endloop
            set a=1
            loop
                exitwhen a>6
                if(h.pitems[a]==0)then //It's empty
                    set h.pitems[a]=p
                endif
                set a=a+1
            endloop
            return false
        endmethod
        
        static method TryBags takes hero h, pitem p returns boolean
            local integer a=1
            loop
                exitwhen a>6
                if(bag.is(h.pitems[a]) and bag.get(h.pitems[a]).addpitem(p))then //It was added to the bag!
                    return true
                endif
                set a=a+1
            endloop
            return false
        endmethod
        
        static method Pickup takes nothing returns nothing
            local pitem p=pitem.get(GetManipulatedItem())
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local real x
            local real y
            if(GetManipulatedItem()==p.PUA)then //A PUA was picked
                set x=GetItemX(p.PUA)
                set y=GetItemY(p.PUA)
                call HashtablePUAs.remove(H2I(p.PUA))
                set p.PUA=CreateItem(p.PUAid,x,y)
                call SetWidgetLife(p.PUA,73.)
                call SetItemInvulnerable(p.PUA,true)
                call SetItemVisible(p.PUA,false)
                if(not pitem.TryStack(h,p))then //Try again
                if(not pitem.TryAdd(h,p))then //Try again
                if(h.state==1 and not pitem.TryBag(h,p))then //Try once more
                if(h.state>1 and not pitem.TryMain(h,p))then //One more time
                if(h.state>1 and not pitem.TryBags(h,p))then //FAILURE.
                    call SetItemVisible(p.PUA,true) //Make the PUA visible again.
                endif //Lines up nice xD
                endif //Lines up nice xD
                endif //Lines up nice xD <---- PUA Comments Here
                endif //Lines up nice xD
                endif //Lines up nice xD
            elseif(GetManipulatedItem()==p.main and h.state==0)then //MAIN AHOY!!!
                set h.pitems[UnitGetItemSlot(h.hero,p.main)+1]=p
            endif
        endmethod
        
        static method create takes integer id, integer puaid, real x, real y returns pitem
            local pitem p=pitem.allocate()
            set p.main=CreateItem(id,0.,0.)
            call SetWidgetLife(p.main,74.)
            call SetItemInvulnerable(p.main,true)
            call SetItemVisible(p.main,false)
            call HashtableMitems.put(H2I(p.main),p)
            set p.PUA=CreateItem(puaid,x,y)
            set p.PUAid=puaid
            call SetWidgetLife(p.PUA,73.)
            call SetItemInvulnerable(p.PUA,true)
            call SetItemVisible(p.PUA,false)
            call HashtablePUAs.put(H2I(p.PUA),p)
            return p
        endmethod
        
        method onDestroy takes nothing returns nothing
            call HashtableMitems.remove(H2I(.main))
            call HashtablePUAs.remove(H2I(.PUA))
            call RemoveItem(.main)
            call RemoveItem(.PUA)
        endmethod
    endstruct
 
 
    struct hero
        
        //The hero is the frame upon which WMI is built. 
        //Only heroes can access the advanced features
        //of the system, and creating one is as simple
        //as creating a unit. Just call:
        //
        //hero.create(player p,integer id, real x, real y, real facing)
        //
        //to create a hero with full WMI enhancement.
        
        unit hero
        player owner
        integer state //0=main 1=bag 2=book 3=shop
        trigger pickup
        trigger use
        trigger order
        trigger drop
        item nextpage
        item cancel
        bag openbag
        //book openbook
        //shop openshop
        pitem array pitems[6]
        
        
        method clearinv takes nothing returns nothing
            local integer a=0
            local item temp
            local integer state=.state
            set .state=-1
            if(state==0)then //MAIN
                loop
                    exitwhen a==.pitems.size
                    set temp=UnitItemInSlot(.hero,a)
                    if(GetItemTypeId(temp)==Blank)then
                        set .pitems[a]=0
                        call RemoveItem(temp)
                    else
                        set .pitems[a]=pitem.get(temp)
                        call UnitRemoveItem(.hero,temp)
                        call SetItemVisible(temp,false)
                    endif
                    set a=a+1
                endloop
            elseif(state==1)then //BAG
                loop
                    exitwhen a==.pitems.size
                    set temp=UnitItemInSlot(.hero,a)
                    if(GetItemTypeId(temp)==Blank)then
                        set .openbag.pitems[(.openbag.currentpage-1)*4+a]=0
                        call RemoveItem(temp)
                    else
                        if(a<5)then
                            set .openbag.pitems[(.openbag.currentpage-1)*4+a]=pitem.get(temp)
                        endif
                        call UnitRemoveItem(.hero,temp)
                        call SetItemVisible(temp,false)
                    endif
                    set a=a+1
                endloop
            elseif(state>1)then //OTHERS
                loop
                    exitwhen a==.pitems.size
                    set temp=UnitItemInSlot(.hero,a)
                    if(GetItemTypeId(temp)==Blank)then
                        call RemoveItem(temp)
                    else
                        call UnitRemoveItem(.hero,temp)
                        call SetItemVisible(temp,false)
                    endif
                    set a=a+1
                endloop
            endif
            call TriggerSleepAction(0)
            set .state=state
        endmethod
        
        method restoreinv takes nothing returns nothing
            local integer a=0
            set .state=-1
            loop
                exitwhen a==.pitems.size
                call SetItemVisible(.pitems[a].main,true)
                call UnitAddItem(.hero,.pitems[a].main)
                set a=a+1
            endloop
            call TriggerSleepAction(0)
            set .state=0
        endmethod
        
        static method create takes player p, integer id, real x, real y, real face returns hero
            local hero h=hero.allocate()
            set h.hero=CreateUnit(p,id,x,y,face)
            set h.owner=p
            set h.state=0
            set h.pickup=CreateTrigger()
            set h.use=CreateTrigger()
            set h.order=CreateTrigger()
            set h.drop=CreateTrigger()
            call HashtableA.put(H2I(h.pickup),h)
            call HashtableA.put(H2I(h.use),h)
            call HashtableA.put(H2I(h.order),h)
            call HashtableA.put(H2I(h.drop),h)
            call TriggerRegisterUnitEvent(h.pickup,h.hero,EVENT_UNIT_PICKUP_ITEM)
            call TriggerRegisterUnitEvent(h.use,h.hero,EVENT_UNIT_USE_ITEM)
            call TriggerRegisterUnitEvent(h.order,h.hero,EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(h.drop,h.hero,EVENT_UNIT_DROP_ITEM)
            call TriggerAddAction(h.use,function bag.Use)
            call TriggerAddAction(h.order,function bag.Order)
            set h.nextpage=CreateItem(Nextpage,0,0)
            set h.cancel=CreateItem(Cancel,0,0)
            set h.openbag=0
            return h
        endmethod
    endstruct
    
    //! runtextmacro HASHTABLE("Bags","private")
    //! runtextmacro HASHTABLE("BagUse","private")
    //! runtextmacro HASHTABLE("BagOrder","private")
    
    struct bag
        pitem bag
        pitem array pitems[20]
        integer index
        integer currentpage
        triggeraction use
        triggeraction order
        hero owner
        
        static method is takes pitem p returns boolean
            return HashtableBags.containsKey(H2I(p.main))
        endmethod
        
        static method get takes pitem p returns bag
            return HashtableBags.get(H2I(p.main))
        endmethod
        
        static method Pickup takes nothing returns nothing
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local pitem p=pitem.get(GetManipulatedItem())
            if(bag.is(p))then
                set bag.get(p).owner=h
            endif
        endmethod
        
        static method Use takes nothing returns nothing //Actions for when a hero uses an item.
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger())) //Get the hero from the trigger
            local item it=GetManipulatedItem()
            local pitem p
            local bag b
            if(h.state==1 and it==h.nextpage)then //Nextpage button
                call h.clearinv()
                call h.openbag.nextpage()
            elseif(h.state==1 and it==h.cancel)then //Cancel the bag
                call h.clearinv()
                call h.restoreinv()
            else
                set p=pitem.get(it)
                if(bag.is(p))then //A bag was used
                    set b=bag.get(p)
                    call h.clearinv()
                    call b.showpage(b.currentpage)
                endif
            endif
        endmethod
        
        static method Order takes nothing returns nothing //Actions for when a hero issued an order.
            local hero h=HashtableA.get(H2I(GetTriggeringTrigger()))
            local bag b
            local integer order=GetIssuedOrderId()
            local integer dropslot=order-852002
            local pitem dragged=pitem.get(GetOrderTargetItem())
            local pitem dropped=pitem.get(UnitItemInSlot(h.hero,dropslot))
            local integer dragslot=UnitGetItemSlot(h.hero,dragged.main)
            local integer state=h.state
            set h.state=-1
            if(state==0)then
                return
            endif
            if(bag.is(dragged) and state==0)then //Add it to the bag.
                if(not bag.get(dragged).addpitem(dropped))then
                    call SimError(h.owner,"That bag is full.")
                else
                    call UnitRemoveItem(h.hero,dropped.main)
                    call SetItemVisible(dropped.main,false)
                endif
                call UnitDropItemSlot(h.hero,dragged.main,dropslot)
            elseif(dragged.main==h.cancel and state==1)then
                set b=h.openbag
                if(dragged.main!=dropped.main)then //We needs to be dropping that item out.
                    set dropped=b.removepitem((b.currentpage-1)*4+dragslot)
                    if(UnitDropItemSlot(h.hero,dragged.main,dropslot))then
                        call UnitAddItemById(h.hero,Blank)
                    endif
                else //Double Cancel Drop. Go to last page.
                    call h.clearinv()
                    call b.showpage(100) //Should max out the page. If not, you're crazy.
                endif
            elseif(dragged.main==h.nextpage and state==1)then
                set b=h.openbag
                if(dragged.main!=dropped.main)then //It was dropped on nextpage. WTF do we do?
                    if(b.readdpitem(dropped))then //This should always work
                        call UnitDropItemSlot(h.hero,dragged.main,dropslot)
                        call h.clearinv()
                        call b.showpage(b.currentpage)
                    else
                        call BJDebugMsg("ReAdd Failure")
                    endif
                else //I delcare a double nextpage drop!!! Go to first page.
                    call h.clearinv()
                    call b.showpage(-1) //Should get the first page
                endif
            endif
                
        endmethod
        
        static method create takes integer id, integer puaid, real x, real y returns bag
            local bag b=bag.allocate()
            set b.bag=pitem.create(id,puaid,x,y)
            call HashtableBags.put(H2I(b.bag.main),b)
            set b.index=0
            return b
        endmethod
        
        method addpitem takes pitem p returns boolean
            local integer a=0
            local integer id=GetItemTypeId(p.main)
            local integer max=GetItemLevel(p.main)
            local integer charges
            local integer tempcharges
            set .index=.index+1
            loop //Check for pre-existing pitem.
                exitwhen a>.index
                if(.pitems[a]==p)then //Already there, bitch!
                    if(.index>=.pitems.size)then
                        set .index=.pitems.size-1
                    endif
                    return false
                endif
                set a=a+1
            endloop
            set a=1
            loop
                exitwhen a>.index
                if(GetItemTypeId(.pitems[a].main)==id)then //Same type
                    set charges=GetItemCharges(p.main)
                    set tempcharges=GetItemCharges(.pitems[a].main)
                    if(tempcharges+charges>max)then //Too many!
                        if(tempcharges==max)then //Full
                        else //Add remainder
                            set tempcharges=max-tempcharges //The number left to add
                            call SetItemCharges(p.main,charges-tempcharges)
                            call SetItemCharges(.pitems[a].main,max)
                        endif
                    else //Just add them all
                        call SetItemCharges(.pitems[a].main,tempcharges+charges)
                        call p.destroy()
                        if(.index>=.pitems.size)then
                            set .index=.pitems.size-1
                        endif
                        return true
                    endif
                endif
                set a=a+1
            endloop
            if(.index>=.pitems.size)then
                set .index=.pitems.size-1
                return false
            endif
            //Made it past all the shit. Just add the pitem!
            set .pitems[.index]=p
            return true
        endmethod
        
        method removepitem takes integer index returns pitem
            local pitem p=.pitems[index]
            local integer a=0
            local integer id=GetItemTypeId(p.main)
            local integer max=GetItemLevel(p.main)
            local integer charges
            local integer tempcharges
            if(.index>.pitems.size-1)then
                set .index=.pitems.size-1
            endif
            loop
                exitwhen index==.pitems.size
                if(index<.pitems.size-1)then
                    set .pitems[index]=.pitems[index+1]
                else
                    set .pitems[.pitems.size-1]=0
                endif
                set index=index+1
            endloop
            set .index=.index-1
            loop
                exitwhen a==.owner.pitems.size
                if(GetItemTypeId(.pitems[a].main)==id)then //Same type
                    set charges=GetItemCharges(p.main)
                    set tempcharges=GetItemCharges(.owner.pitems[a].main)
                    if(tempcharges+charges>max)then //Too many!
                        if(tempcharges==max)then //Full
                        else //Add remainder
                            set tempcharges=max-tempcharges //The number left to add
                            call SetItemCharges(p.main,charges-tempcharges)
                            call SetItemCharges(.owner.pitems[a].main,max)
                        endif
                    else //Just add them all
                        call SetItemCharges(.owner.pitems[a].main,tempcharges+charges)
                        call p.destroy()
                        return 0
                    endif
                endif
                set a=a+1
            endloop
            set a=0
            loop
                exitwhen a==.owner.pitems.size
                if(.owner.pitems[a]==0)then //Blank. Add it.
                    set .owner.pitems[a]=p
                    call UnitRemoveItem(.owner.hero,p.main)
                    call SetItemVisible(p.main,false)
                    return p
                endif
                set a=a+1
            endloop
            call UnitRemoveItem(.owner.hero,p.main)
            call SetItemVisible(p.main,false)
            call SetItemVisible(p.PUA,true)
            call SetItemPosition(p.PUA,GetUnitX(.owner.hero),GetUnitY(.owner.hero))
            call SimError(.owner.owner,"No room left in inventory.")
            return p
        endmethod
        
        method readdpitem takes pitem p returns boolean
            local integer a=0
            local boolean bool=false
            loop
                exitwhen a==.pitems.size
                if(.pitems[a]==p)then //Found it.
                    set bool=true
                endif
                if(bool)then
                    if(a<.pitems.size-1)then //Set it to the one above
                        set .pitems[a]=.pitems[a+1]
                    else //Null it
                        set .pitems[a]=0
                    endif
                endif
                set a=a+1
            endloop
            if(bool)then
                return .addpitem(p)
            endif
            return false
        endmethod
        
        method showpage takes integer page returns nothing
            local hero h=.owner
            local integer a=0
            if(h==0)then
                return
            endif
            if(page>5)then
                set page=5
            elseif(page<1)then
                set page=1
            endif
            set h.state=-1
            set .currentpage=page
            set page=(page-1)*4
            loop
                exitwhen a>3
                if(.pitems[page+a]==0)then //It's blank
                    call UnitAddItemById(h.hero,Blank)
                else //pitem is there, so show it!
                    call UnitAddItem(h.hero,.pitems[page+a].main)
                endif
                set a=a+1
            endloop
            call UnitAddItem(h.hero,h.nextpage)
            call SetItemCharges(h.nextpage,.currentpage)
            call UnitAddItem(h.hero,h.cancel)
            call SetItemCharges(h.nextpage,5)
            call TriggerSleepAction(0)
            set h.state=1
        endmethod
        
        method nextpage takes nothing returns nothing
            if(.currentpage<5)then
                call .showpage(.currentpage+1)
            else
                call .showpage(1)
            endif
        endmethod
    endstruct
    
    //===========================================================================
    public function InitTrig takes nothing returns nothing
    endfunction

endscope