Demo Map ItemMenu v3

weaaddar

New Member
Reaction score
6
Hi all,
Here is my latest rendition of itemMenu its included in a demo map. But it can work as a standalone script.

ItemMenu is out to make it easy to use items as a menu system. It does so by giving you a bunch of event handlers and having really drop dead simple implementation.

Included is a customizable Storage Menu (seriously try it out there are more variants then the ones I included), an Annoying Menu, and a Build menu.

Check it out let me know what you think. There is more documentation in the readme, but the premise is you implement an OnOpen, an OnInterupt, and any event handler you'd like and it sets up the rest. ItemMenus can be registed to state that they provide storage, and you get granular control on how things will be placed. If none of the default settings meet your need you can always write your own.

The system was coded in Zinc. To be fair and partisan to non-zinc users, I wrote the most "complicated" of the menus included (the build Menu) in Vjass to demo how easy it is to use.

Credit::
Vexorian -- For Zinc/VJass and SimError.

The map is using Table and Cohandlar's version of GetItemCostById. Neither are required for core operation.
It also includes a library I created call Collections
Edit since it was asked for here is a wall of code:

Code by request::
ItemMenu Library
JASS:

library ItemMenu requires Collections,SimError
{
    hashtable Table = InitHashtable();
    constant integer BlankID    = 'W001';
    constant integer NextID     = 'W000';
    constant integer InvSize    = 5;
    constant integer ActivatorID= 'WA01';
    function GetItemSlot(unit u, item it)->integer
    {
        integer i;
        for(i = 0; i < 6; i+=1)
        {
            if(UnitItemInSlot(u,i) == it) return i;
        }
        return -1;
    }


    function ItemUseEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;
        item it=GetManipulatedItem();
        integer slot; 
        if(handler == 0) return;
        slot = GetItemSlot(h.Hero,it);
        if(it==h.NextItem)  h.MenuNextPage(handler);
        else handler.OnItemUse(it,slot);
        t=null;
        it=null;
    }
    
    function TargetOrderEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;  
        integer order=GetIssuedOrderId();
        integer offset = (h.CurrentPage-1)*4;
        integer dragSlot;
        integer dropSlot;
        item dragItem;
        item dropItem;
        if(handler == 0) return;
        if(852001 < order && order < 852008 )
        {
            dropSlot=order-852002;
            dropItem=UnitItemInSlot(h.Hero,dropSlot);
            dragItem=GetOrderTargetItem();
            dragSlot=GetItemSlot(h.Hero,dragItem);
            if(order == 852007 || (handler.DoubleClickRemove && dragSlot == dropSlot))
            {
                h.DropItem(dragItem,dragSlot);
                h.StoreItem(dragItem,(dragSlot == dropSlot),dropSlot);
                if(!handler.RemoveOnDrop) h.PageAfterDelay(0);
            }
            
            else if(handler.OnItemDrag(dragItem,dropItem,dragSlot,dropSlot))
            {
                handler.Items[offset+dragSlot]=dropItem;
                handler.Items[offset+dropSlot]=dragItem;
            }
        }
        t=null;
        dragItem=null;
        dropItem=null;
    }
    
    function DropItemEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;  
        integer offset;
        item it = GetManipulatedItem();
        integer slot = GetItemSlot(h.Hero,it);
        if(handler == 0 || GetItemTypeId(it) == BlankID || slot == 5) return;
        h.DropItem(it,slot);
        t=null;
        it=null;

    }
    
    function NoTargetOrderEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;  
        integer order=GetIssuedOrderId();
        integer last;
        if(order != 852542 || handler == 0 || handler ==  h.ActivatorMenu) return;
        if(h.ItemMenus.Size == 2)
        {
            UnitRemoveAbility(h.Hero,ActivatorID);
            last = h.ItemMenus.IndexOf(handler);
            h.ChangeMenu(h.ItemMenus[1-last]);
            h.DelayExecution(function(Hero h){UnitAddAbility(h.Hero,ActivatorID);},0);
        }
        else
        {
            h.ChangeMenu(h.ActivatorMenu);
        }
        t=null;
    }
    
    function OrderItemEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;  
        integer order=GetIssuedOrderId();
        item it;
        integer slot;
        if(handler == 0) return;
        if(852007 < order && order < 852013 )
        {
            slot=order-852008;
            it = UnitItemInSlot(h.Hero,slot);
            if(it == h.NextItem) return;
            handler.OnItemOrder(it,slot);
        }
    }
    
    function PickUpItemEvent()
    {
        trigger t=GetTriggeringTrigger();
        Hero h = LoadInteger(Table,GetHandleId(t),0);
        ItemMenu handler= h.CurrentMenu;
        item it = GetManipulatedItem();
        integer slot = GetItemSlot(h.Hero,it);
        integer offset = (h.CurrentPage-1)*4;
        if(handler == 0 || GetItemTypeId(it)== BlankID) return;
        h.StoreItem(it,false,slot);
        t = null;
    }

    type Action_Hero extends function(Hero);


    
    public struct Hero
    {
        private unit m_hero;
        private ActivatorMenu m_activatorMenu;
        private ItemMenu m_currentMenu;
        private List_ItemMenu m_ItemMenus;
        private item m_Blanks[5];
        private item m_nextItem;
        private integer m_currentPage=0;
        private integer m_availableStorage = 0;
        method operator ItemMenus()-> List_ItemMenu
        {
            return m_ItemMenus;
        }
        
        method operator ActivatorMenu()-> ActivatorMenu
        {
            return m_activatorMenu;
        }
        
        method operator AvailableStorage()->integer
        {
            return m_availableStorage;
        }
        
        method operator AvailableStorage=(integer value)
        {
            m_availableStorage = value;
        }
        method operator Hero()->unit
        {
            return m_hero;
        }
        method operator CurrentMenu()->ItemMenu
        {
            return m_currentMenu;
        }
        
        method operator CurrentMenu=(ItemMenu value)
        {
            m_currentMenu = value;
        }
        method operator CurrentPage()->integer
        {
            return m_currentPage;
        }
        
        method operator NextItem()->item
        {
            return m_nextItem;
        }
        
        method operator CurrentPage=(integer value)
        {
            m_currentPage = value;
        }

        method RemoveBlanks()
        {
            integer i;
            item temp;
            integer end = 5;
            integer decrement;
            if(UnitItemInSlot(m_hero,4) == m_nextItem) end = 4;
            decrement = m_currentMenu.Items.Size - (m_currentPage * end);
            if(decrement< 0) end = end + decrement;
            for(i = 0; i < end; i+=1)
            {
                temp=UnitItemInSlot(m_hero,i);
                if(temp==m_Blanks<i>)
                {
                    UnitRemoveItem(m_hero,temp);
                    SetItemVisible(temp,false);
                }
            }
            temp=null;
        }
        
        //clear must be in escape state.
        method ClearInventory()
        {
            integer i;
            item temp;
            for(i = 0; i &lt; InvSize; i+=1)
            {
                temp=UnitRemoveItemFromSlot(m_hero,i);
                SetItemVisible(temp,false);
            }
            temp=null;
        }
        // must be run in escape state. Inventory is blank. 
        method NextPage(List_item iv)-&gt;integer
        {
            integer size= iv.Size;
            integer i = 0;
            item temp;
            integer start;
            integer end = 4;
            integer maxPage= (size/4);
            if(size &lt; 6) end = 5;
            if(size-(maxPage*end)&gt;0) maxPage=maxPage+1;
            if( m_currentPage &gt;= maxPage ) m_currentPage=0;
            start=(m_currentPage)*4;

            for(m_currentPage+=1; i &lt; end; i+=1)
            {
                temp=iv[start+i];
                if(temp==null) temp=m_Blanks<i>;
                UnitAddItem(m_hero,temp);
            }
            if(end == 4) UnitAddItem(m_hero,m_nextItem);
            temp=null;
            return m_currentPage;
        }
        
        method DropItem(item it,integer slot)
        {
            integer offset =(m_currentPage-1)*4;
            if(m_currentMenu.OnItemDrop(it,slot,(GetWidgetLife(it)&lt;.405)))
            {
                m_availableStorage += 1;
                m_currentMenu.StorageProvided+=1;
                if(m_currentMenu.RemoveOnDrop)
                {
                    m_currentMenu.Items.RemoveAt(offset+slot);
                    PageAfterDelay(0);
                }
                else m_currentMenu.Items[offset+slot] = null;
                if(GetItemTypeId(UnitItemInSlot(m_hero,5)) == BlankID &amp;&amp; m_availableStorage == 1)
                {
                    RemoveItem(UnitItemInSlot(m_hero,5));
                }
            }
        }
        
        // returns whether it worked or not.
        method StoreItem(item it, boolean excludeCurrent,integer slot)-&gt;boolean
        {
            integer offset = (m_currentPage-1)*4;
            ItemMenu handler = m_currentMenu;
            ItemMenu old = 0;
            integer result = 0;
            integer i;
            integer end = 4;
            if(!excludeCurrent &amp;&amp; handler.StorageProvided &gt; 0)
            {
                result = handler.AddItem(it,slot);
                if(result == 1)
                {
                    if(slot != 5)
                    {   
                        handler.Items[offset+slot] = it;
                    }
                    else
                    {
                        m_currentMenu = 0;
                        UnitRemoveItem(m_hero,it);
                        SetItemVisible(it,false);
                        m_currentMenu = handler;
                        if(UnitItemInSlot(m_hero,4) == m_nextItem) end = 4;
                        if(handler.RemoveOnDrop) m_currentMenu.Items.Add(it);
                        else handler.Items[handler.Items.IndexOf(null)] = it;
                        if(handler.RemoveOnDrop) PageAfterDelay(0);
                    }
                }
            }
            if(result == 0)
            {
                for(i = 0; i &lt; ItemMenus.Size; i+=1)
                {
                    handler = ItemMenus<i>;
                    if(handler!= m_currentMenu &amp;&amp; handler.StorageProvided &gt; 0 )
                    {
                        result = ItemMenus<i>.AddItem(it,slot);
                        if(result &gt; 0)
                        {
                            if(result &gt; 1) break;
                            old = m_currentMenu;
                            m_currentMenu = 0;
                            UnitRemoveItem(m_hero,it);
                            SetItemVisible(it,false);
                            m_currentMenu = old;
                            if(handler.RemoveOnDrop) m_currentMenu.Items.Add(it);
                            else handler.Items[handler.Items.IndexOf(null)] = it;
                            break;
                        }
                    }
                }
            }
            if(result &gt; 0)
            {
                m_availableStorage-=1;
                handler.StorageProvided-=1;
            }
            else
            {
                old = m_currentMenu;
                m_currentMenu = 0;
                UnitRemoveItem(m_hero,it);
                m_currentMenu = old;
                SimError(GetOwningPlayer(m_hero),&quot;Item can&#039;t be stored&quot;);
            }
            if(m_availableStorage == 0) UnitAddItemById(m_hero,BlankID);
            return (result &gt; 0);
        }
        
        method MenuNextPage(ItemMenu handler)
        {
            m_currentMenu=0;
            ClearInventory();
            NextPage(handler.Items);
            if(handler.ShowPage) SetItemCharges(m_nextItem,m_currentPage);
            m_currentMenu=handler;
            if(!handler.RemoveOnDrop) RemoveBlanks();
        }
        
        method PageAfterDelay(real interval)
        {
            m_currentPage-=1;
            DelayExecution(function(Hero h){h.MenuNextPage(h.CurrentMenu);},interval);
        }
        
        method DelayExecution(Action_Hero f,real duration)
        {
            timer ti=CreateTimer();
            SaveInteger(Table,GetHandleId(ti),0,this);
            SaveInteger(Table,GetHandleId(ti),1,f);
            TimerStart(ti,duration,false,function()
            {
                timer t=GetExpiredTimer();
                Hero h =LoadInteger(Table,GetHandleId(t),0);
                Action_Hero f=LoadInteger(Table,GetHandleId(t),1);
                f.execute(h);
                FlushChildHashtable(Table,GetHandleId(t));
                DestroyTimer(t);
                t=null;
            });
            ti=null;
        }
        
        
        method ChangeMenu(ItemMenu handler)
        {
            ItemMenu current=m_currentMenu;
            m_currentMenu=0;
            current.OnInterupt();
            ClearInventory();
            handler.OnOpen();
            m_currentPage=0;
            MenuNextPage(handler);
        }
        
        method RegisterMenu(ItemMenu menu)
        {
            m_ItemMenus.Add(menu);
            menu.Hero = this;
            m_availableStorage+=menu.StorageProvided;
            if(m_ItemMenus.Size == 2) UnitAddAbility(m_hero,ActivatorID);
        }
        static method create(unit u,ItemMenu defaultMenu)-&gt;thistype
        {
            thistype h=thistype.allocate();
            trigger t;
            item n = null;
            integer i;
            h.m_ItemMenus = List_ItemMenu.create(0);
            h.m_activatorMenu = ActivatorMenu.create();
            h.m_activatorMenu.Hero = h;
            h.m_hero = u;
            h.RegisterMenu(defaultMenu);
            for( i = 0; i &lt; m_Blanks.size; i+=1)
            {
                n = CreateItem(BlankID,0,0);
                SetItemVisible(n,false);
                h.m_Blanks<i> = n;
            }
            h.m_nextItem=CreateItem(NextID,0,0);
            SetItemVisible(h.m_nextItem,false);
            h.ChangeMenu(defaultMenu);
            t=CreateTrigger();
            TriggerRegisterUnitEvent(t,h.m_hero,EVENT_UNIT_USE_ITEM);
            TriggerAddAction(t,function ItemUseEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t=CreateTrigger();
            TriggerRegisterUnitEvent(t,h.m_hero,EVENT_UNIT_ISSUED_ORDER);
            TriggerAddAction(t,function NoTargetOrderEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t=CreateTrigger();
            TriggerRegisterUnitEvent(t,h.m_hero, EVENT_UNIT_DROP_ITEM );
            TriggerAddAction(t,function DropItemEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t=CreateTrigger();
            TriggerRegisterUnitEvent(t,h.m_hero,EVENT_UNIT_ISSUED_TARGET_ORDER);
            TriggerAddAction(t,function TargetOrderEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t= CreateTrigger();
            TriggerRegisterUnitEvent( t,h.m_hero, EVENT_UNIT_PICKUP_ITEM);
            TriggerAddAction(t,function PickUpItemEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t = CreateTrigger();
            TriggerRegisterUnitEvent( t, h.m_hero, EVENT_UNIT_ISSUED_ORDER);
            TriggerRegisterUnitEvent( t, h.m_hero, EVENT_UNIT_ISSUED_TARGET_ORDER);
            TriggerRegisterUnitEvent( t, h.m_hero, EVENT_UNIT_ISSUED_POINT_ORDER);
            TriggerAddAction(t,function OrderItemEvent);
            SaveInteger(Table,GetHandleId(t),0,h);
            t = null;
            return h;
        }
        
    }

    public interface IMenu
    {
        Hero m_hero;  //The owner of this itemMenu
        List_item m_items; //The itemvector that this itemMenu wraps and handles.
        boolean m_showPage;
        boolean m_removeOnDrop;
        boolean m_doubleClickRemove;
        integer m_storageProvided;
        item    m_OpenItem;
        
        
    // Required Methods.
        //The following methods must be implmented
        method OnOpen();
        method OnInterupt();
        
    // Optional Methods
        // called when this Menu is registered/unregister to a Hero.
        method OnRegister() = null;
        method OnUnregister() = null;
        // returns if this menu was able to store the item.
        // This method return type is tested for the following::
        // 0 means failure to store.
        // 1 means success and you&#039;d like the default storage code behavior.
        // 2 means success and you had your own storage solution.
        method AddItem(item manipulatedItem,integer slot)-&gt;integer = 1;
        
        //What action to take when an Item is used?
        method OnItemUse(item manipulatedItem,integer slot) = null;
        
        // What action to take when an Item is Dragged?
        method OnItemDrag(item dragItem,item dropItem,integer dragSlot,integer dropSlot)-&gt;boolean = true;
        
        // What action to take when an Item is Dropped?
        method OnItemDrop(item manipulatedItem,integer slot,boolean wasRemoved)-&gt;boolean = true;
        // what action to take on issueing a use order on the item
        method OnItemOrder(item manipulatedItem,integer slot) = null;
        
    }

    
    public struct ItemMenu extends IMenu
    {

        method operator Items()-&gt;List_item
        {
            return m_items;
        }
        method operator Hero()-&gt;Hero
        {
            return m_hero;
        }
        method operator Hero=(Hero hero)
        {
            if(m_hero != null) OnUnregister();
            m_hero=hero;
            if(hero == null) OnRegister();
            
        }
        method operator ShowPage()-&gt;boolean
        {
            return m_showPage;
        }
        method operator RemoveOnDrop()-&gt;boolean
        {
            return m_removeOnDrop;
        }
        method operator StorageProvided()-&gt;integer
        {
            return m_storageProvided;
        }
        method operator DoubleClickRemove()-&gt;boolean
        {
            return m_doubleClickRemove;
        }
        method operator StorageProvided=(integer storageProvided)
        {
            m_storageProvided= storageProvided;
        }
        method operator OpenItem()-&gt;item
        {
            return m_OpenItem;
        }
        

        // this is the stub ancestor but I still got to put them here.
        method OnOpen()
        {
        }
        
        method OnInterupt()
        {
        }
        
        
        static method create()-&gt;thistype
        {
            thistype m=thistype.allocate();
            return m;
        }
    }
    
    public struct ActivatorMenu extends ItemMenu
    {
        method OnOpen()
        {
            integer i;
            item temp;
            for(i = 0; i &lt; Hero.ItemMenus.Size ; i+=1)
            {
                temp =Hero.ItemMenus<i>.OpenItem;
                if(temp != null)
                {  
                    SetItemDroppable(temp,false);
                    m_items.Add(temp);
                }
            }
            UnitRemoveAbility(Hero.Hero,ActivatorID);
        }
        
        method OnInterupt()
        {
            m_items.Clear();
            UnitAddAbility(Hero.Hero,ActivatorID);
        }
        
        method OnItemUse(item manipulatedItem,integer slot)
        {
            ItemMenu target = GetItemUserData(manipulatedItem);
            Hero.ChangeMenu(target);
            
        }
        static method create()-&gt;thistype
        {
        
            thistype m = thistype.allocate();
            integer i;
            m.m_removeOnDrop = true;
            m.m_items = m_items.create(0);
            return m;
        }
    }
}
</i></i></i></i></i></i>


Collections and SimError
JASS:

library Collections
{
//! textmacro ListTemplate takes T,typename,baseName,DefaultVal
    public struct List_$T$
    {
        private integer m_Size;
        private integer m_Capacity;
        private static hashtable Table = InitHashtable();
        method operator Size()-&gt;integer
        {
            return m_Size;
        }
        method operator Capacity()-&gt;integer
        {
            return m_Capacity;
        }
        
        method operator[](integer index)-&gt;$T$
        {
            $T$ retVal = $DefaultVal$;
            if( - 1 &lt; index &amp;&amp; index &lt; m_Size)
            {
                retVal = Load$typename$(Table,integer(this),index);
            }
            return retVal;
        }
        method operator[]=(integer index,$T$ value)
        {
            if( -1 &lt; index &amp;&amp; index &lt; m_Size)
            {
                // Storing null doesn&#039;t actually do anything. It won&#039;t override values. So we have to remove things in that case.
                // Since staticif  currently does not support reading parameters from macros this is all that can be done.
                if(value == $DefaultVal$) RemoveSaved$baseName$(Table,integer(this),index);
                else Save$typename$(Table,integer(this),index,value);
            }
        }
        method Add($T$ obj)-&gt;boolean
        {
            boolean retVal = false;
            if(m_Capacity == 0 || m_Size &lt; m_Capacity)
            {
                m_Size+=1;
                this[m_Size-1]= obj;
                retVal = true;
            }
            return retVal;
        }
        
        method RemoveAt(integer index)-&gt;$T$
        {
            $T$ retVal = $DefaultVal$;
            integer i;
            if(-1 &lt; index &amp;&amp; index &lt; m_Size)
            {
                retVal = this[index];
                for(i = index; i &lt; m_Size-1; i+=1)
                {
                    this<i> = this[i+1];
                }
                m_Size-=1;
                RemoveSaved$baseName$(Table,integer(this),m_Size);
            }
            return retVal;
        }
        // Performs at O(1) time, but moves things about
        // in the process.
        method UnStableRemoveAt(integer index)-&gt; $T$ 
        {
            $T$ retVal = $DefaultVal$;
            if( - 1 &lt; index &amp;&amp; index &lt; m_Size)
            {
                retVal = this[index];
                this[index] = this[m_Size-1];
                m_Size-=1;
                RemoveSaved$baseName$(Table,integer(this),m_Size);
            }
            return retVal;
        }
        
        method IndexOf($T$ value)-&gt;integer
        {
            integer i = 0;
            integer retVal = -1;
            for(i = 0; i &lt; m_Size; i+=1)
            {
                if(this<i> == value)
                {
                    retVal = i;
                    break;
                }
            }
            return retVal;
        }
        
        method Contains($T$ value)-&gt;boolean
        {
            return IndexOf(value) &gt; -1;
        }
        
        method Remove($T$ value)-&gt;boolean
        {
            integer index = IndexOf(value);
            if(index &gt; -1) RemoveAt(index);
            return (index &gt; -1);
        }
        
        method Clear()
        {
            FlushChildHashtable(Table,integer(this));
            m_Size = 0;
        }
        
        private method onDestroy()
        {
            Clear();
        }
        
        // This is a shallow clone
        static method Clone(thistype list)-&gt;thistype
        {
            thistype retVal = thistype.create(list.m_Capacity);
            integer i;
            for(i = 0; i &lt; list.m_Size; i+=1)
            {
                retVal.Add(list<i>);
            }
            return retVal;
        }
        
        // Yes, I suppose I could&#039;ve wrote List(T) but hey thistype sweet!
        // passing 0 creates a list without a fixed cap.
        static method create(integer capacity)-&gt;thistype
        {
            thistype retVal = thistype.allocate();
            retVal.m_Capacity = capacity;
            return retVal;
        }
    }
//! endtextmacro

//! runtextmacro ListTemplate(&quot;item&quot;,&quot;ItemHandle&quot;,&quot;Handle&quot;,&quot;null&quot;)
//! runtextmacro ListTemplate(&quot;ItemMenu&quot;,&quot;Integer&quot;,&quot;Integer&quot;,&quot;0&quot;)

}
library SimError
{
    sound error;

    public function SimError(player ForPlayer, string msg)
    {
        msg=&quot;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00&quot;+msg+&quot;|r&quot;;
        if (GetLocalPlayer() == ForPlayer)
        {
            ClearTextMessages();
            DisplayTimedTextToPlayer( ForPlayer, 0.52, 0.96, 2.00, msg );
            StartSound( error );
        }
    }
    private function onInit()
    {
         error=CreateSoundFromLabel(&quot;InterfaceError&quot;,false,false,false,10,10);
         StartSound( error );
    }

}
</i></i></i>



Implementation guide text::
JASS:

This is the 3rd release of the ItemMenu engine. The goal of this system
is to make it easier to use item panel as a menu system. It does so by giving
abstracted event handlers so that you can create interfaces easily without having to worry
about interop between your menu systems.
The system is interupt based. Whenever a menu wants to take control of the gui, the current menu
fires its OnInterupt method. Afterwards the new menu can fire its OnOpen code.
OnOpen fires before the menu is displayed.

Includes is the activator menu. The activator menu is built in to the framework and allows you
to easily create a toggle dashboard between multiple menus without having to write your own.
it functions with the following behavior::
1 menu registered -- do nothing.
2 menus registered -- give the player the activator ability, which will be a toggle between the two.
3+ menus registered -- give the player the activator ability, which will open a menu of menus.
All heroes are given an activator menu.

Part of this release is an inventory management system. A menu may register to its hero that it provides storage,
and in doing so will allow the hero to use it as fallover storage, if the current menu is out of storage
or if currentMenus.AddItem() event returned 0, then it will fallover into the any menus that register storage on the hero.
However, this comes at a cost. The 6th item slot cannot be used at all. All menus can only display 5 items at a time,
or 4 once they need to resort to paging. There is no cancel button, the 6th slot is left empty to allow the logic to work.
Dragging an item to the 6th slot will try to reAdd the item to the current heroes storage. Useful if you want to consolidate pages.

Another big thing in this system is default behavior. The default behavior may not be your bag.
It is completely overwritable. If you do not like the assumption the code makes, don&#039;t use&#039;em!

The following are setting variables::
        Hero m_hero;  //The owner of this itemMenu
        List_item m_items; //The itemvector that this itemMenu wraps and handles.
        boolean m_showPage; // would you like next page to show the pageNumber?
        boolean m_removeOnDrop; // used if you are using array storage vs list storage.
        boolean m_doubleClickRemove; // would you like double right click to behave like remove from current?
        integer m_storageProvided; // how many slots does this menu provide?
        item    m_OpenItem; // what item should represent this in the activator menu?
        
        
The Required Methods.
        //The following methods must be implemented
        // On open fires before the menu actually opens.
        method OnOpen();
        // On interupt fires before the menu actually closes.
        method OnInterupt();
        
Optional Methods
        // called when this Menu is registered/unregister to a Hero.
        method OnRegister() = null;
        method OnUnregister() = null;
        
        // returns if this menu was able to store the item.
        // This method return type is tested for the following::
        // 0 means failure to store.
        // 1 means success and you&#039;d like the default storage code behavior.
        // 2 means success and you had your own storage solution.
        method AddItem(item manipulatedItem,integer slot)-&gt;integer = 1;
        
        //What action to take when an Item is used?
        method OnItemUse(item manipulatedItem,integer slot) = null;
        
        // What action to take when an Item is Dragged?
        // DOES NOT FIRE If the item is dragged onto itself and doubleClickRemove is set.
        method OnItemDrag(item dragItem,item dropItem,integer dragSlot,integer dropSlot)-&gt;boolean = true;
        
        // What action to take when an Item is Dropped?
        method OnItemDrop(item manipulatedItem,integer slot,boolean wasRemoved)-&gt;boolean = true;
        
        // what action to take on issueing a use order on the item
        method OnItemOrder(item manipulatedItem,integer slot) = null;

    A note that there are 3 event handlers that in theory could fire when a user uses an item:
    OnItemOrder -- before the item is used.
    OnItemUse   -- right after the item is used.
    OnItemDrop  -- after the item dies.

    Determine which of these meet your need.

Default Behavior:
    Finally, you will notice that the event handlers have returns. Most of them are with how to deal with action
    modifying the inventory. The behavior is the following::
    AddItem (Default success = 1, override = 2)::
        will find the first available slot if the inventory is array like (removeOnDrop = false), or add it to teh first slot
    if it is list like (removeOnDrop = true). It will also decrement the storage of the menu and the hero.
    OnItemDrag (Default = true)
        Will store the items in the slots that they end up in. If you marked DoubleClickRemove it will not fire when dropped on itself.
    
    OnItemDrop (Default = true)
        Will remove the item from the slot if it is list like. Or if it is array like it will set it to null.
        
Sample menus:
Included are some sample menus. 
The storage menu is pretty much out to just demonstrate how the default menu works. itemMenu makes an excellent bag system.
The annoying menu is out to be annoying. Just a simple event handler demo.
The buildMenu gives a build menu in the item panel.
Template is not actually a menu, its just there so that you can find all of the interface elements easily, as ItemMenu is scary long.
The map is probably better for hiding the wall-of-code factor.

It sort of hard to explain what the whole thing does, but it basically hijacks the items section of the gui, and allows you to overwrite it with "menus" like attributer, build menus, bag systems, equipment systems, or what not. As an enduser you'll create an ItemMenu by extending the ItemMenu class and then you implement the required event handlers. After that you can implement optional event handlers to get the behavior you want.
 

Attachments

  • itemmenu3.w3x
    48.4 KB · Views: 301

quraji

zap
Reaction score
144
I'm going to look at the demo map...but two things. First, I don't have an idea really of what it does from your description. And second, post the code.
 

quraji

zap
Reaction score
144
It's an inventory system. It works okay, but sometimes I'll have an empty slot (the backpack icon) that won't fill...

It needs documentation.
 

weaaddar

New Member
Reaction score
6
Yes its an inventory system at the minimum, but its also more. The goal is to basically make replacing the item panel easy, so you don't have to worry about losing warcraft 3 basic functionality while your trying to create spells that use the item panel. (See my build menu in the demo map and how I can still pick up items while the paladin is pretending t be a peasant).
Its supposed to make it easier as I use to write these really complicated systems like my old DT4a and drag and drop backpack which was a nightmare to get to interop.
I uploaded a newer version over at wc3c and I'll do the same here as I caught a silly bug. And I'll update the OP with the user guide. The user guide explains msot of how to implement as part of the goal of the system is you don't really have to know the nitty gritty.
 

Weep

Godspeed to the sound of the pounding
Reaction score
400
Are the bonuses from held items supposed to disappear when you switch to a different inventory page...?
 

weaaddar

New Member
Reaction score
6
I suppose with some form of bonusmod and a database you can get that magic. But the point here is this system is a menu not an inventory system.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>_> This is madness!
<_< This is threadjacking!
Err, carry on on-topic.
Like everything else such as synching camera positions, blocking players saving, bytecode and [LJASS]Preloader[/LJASS] calls (and "abstract player colours"), I'm sure this will be the next topic that floats around randomly in TH threads (usually half on-topic) until someone compiles it into a library and submits it (that being using negative item slots for reasons like crashing leavers or something). :p
 

weaaddar

New Member
Reaction score
6
is there anyway to detect when players quit? That could be quite useful. it crashes as soon as the stat screen goes away :/
 

Sevion

The DIY Ninja
Reaction score
413
is there anyway to detect when players quit? That could be quite useful. it crashes as soon as the stat screen goes away :/

On topic XD

Hmm... Player Leaves? >_> Not really sure when that fires though.
 
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