[WIP] FSInv

the Immortal

I know, I know...
Reaction score
51
A quick explanation: An early alpha of a full-screen inventory that will eventually support number of modules (socketing, randomstats, vendors, etc.), and has easy-to-use interface for the average JASS-er (sad, can't make such a thing in GUI, ynow...)

Some details: I felt we were missing a thing like that, as, afaik, there exist only two systems: Toadcop's (which uses gc and with its 13k lines of code is really.. hard to read / understand and kinda too complicated for the avg user) and the original FSGUI (which is outdated, uses gc again, and has many disadvantages, as well). Plus none of these uses OOP/vJASS syntax, which makes things much easier to implement n use.. so a month or two ago I began making (really casually) that system.

Once again, it's an early version, releasing it.. well.. just so you know a thing like that is being made, idk ^^.
A brief TODO is included in the testmap. No sfx-es, no vendor items or such shit - just two heroes that are given two items which they can equip (but not drop for ex) freely.

And yeh, everything is actually written in Zinc, except the config parts. Here's the code so far:
JASS:
 //! zinc
library FSInv requires SimError, Hexxer
{
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *
 * --------------------------- Full Screen Inventory v0.A   -------------------------- *
 * --------------------------------------------------------------------- by Ixtreon -- *
 * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *
 *                                                                                     *
 *   Effectively replaces the Warcraft III item system, taking use of an extended,     *
 *   full screen inventory system, using vJASS' structs to organize everything in a    *
 *   user-friendly (although still requiring vJASS knowledge) interface. Features:     *
 *    - Backpack system allowing heroes to carry unused items. Size limited only by    *
 *      the screen size (can make it over 100-150 items total).                        *
 *    - Dynamic equip and item system, allowing limiting different items to specific   *
 *      slots and changing all data for items and equipment slots during runtime       *
 *    - Different modules attachable to it:  /TODO/                                    *
 *          Vendor - Full Screen preview and selling/buying of items                   *
 *          Socket - Allows socketed items and jewel-type items (runes, gems, w/e)     *
 *          RandomStats - Different items have different 'bonus' stats (D2-like)       *
 *                                                                                     *
 *   Well, it has its (minor) cons aswell so lemme list them, too:                     *
 *    - Every item must be created in the Trigger Editor*. Item Editor has almost no   *
 *      usage here**.                                                                  *
 *    - Having to add a doodad for every different item icon.                          *
 *    - Requires a 1x1 square on your map with flat terrain and no objects on it.      *
 *      (OMG GAMEBREAKING!! END OF THE WORLD <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite12" alt="o_O" title="Er... what?    o_O" loading="lazy" data-shortname="o_O" />)                                      *
 *    - Restriction to up to 40 equipment slot types and 200 items per one ***.        *
 *    - Restriction for units using inventory: data can be saved only while            *
 *      [units on the map using FSInv] * [total backpack slots] &lt; ~8100 ***            *
 *    - Slight delay due to using trackables (unfixable; hardcoded wc3 engine).        *
 *    - Non-pleasing effect if the user begins to frantically scroll with his mouse D: *
 *        *   - API and technical shiet in the trigger named &#039;API&#039; (logic, eh?)        *
 *        **  - Read the instructions for more info                                    *
 *        *** - Give me a VERY good reason and I might make a hashtable version, which *
 *              although very slightly slower, will have no limits.                    *
 *              Though these numbers should be damn enough for every map out here...   *
 *                                                                                     *
 *   I&#039;d say nothing -that- hard or groundbreaking... =P                               *
 *   Example usage in the triggers in the &#039;Examples&#039; category.                         *
 *   A step-by-step guide describing how to setup the system in the trigger comment    *
 *   Configuration part follows                                                        *
 *                                                                                     *
 *   For bug reports, feedback and all that shiet:                                     *
 *       write in the topic @ TheHelper.net                                            *
 *                                                                                     *
 *   Njoy!                                                            Ixtreon          *
 *                                                            (a.k.a. the Immortal)    *
 * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
 
 
 // --------------------- \\
 // --- CONFIGURATION --- \\
 // --------------------- \\
    
        //The inventory ability ID
        constant integer AbilityID = &#039;A000&#039;;
        
        //The coordinates of the bottom-left corner of the mini-rectangle used for the inventory.
        //As mentioned, it must be flat ground with no objects on it (no units/doodads/items etc.!)
        //like in the middle of an unreachable forest, or maybe atop some hills *shrugs*
        //(required rect with size ~45x85 &#039;range&#039;)
        constant real MapPosition_X = 3000;
        constant real MapPosition_Y = 420;
        
        //The size of the backpack module
        constant integer Backpack_Rows = 6;
        constant integer Backpack_Columns = 6;
        //WARNING: Until new version of vJASS is released, fixing a damn bug
        //you MUST scroll down and manually edit the value of BACKPACK_SLOTS_TOTAL
        //a sample of the bug is in the trigger BugExample :x
        
        //The position of the backpack module; range [0;1], where:
        //(0;0) is the bottom-left corner of the FSInv screen
        //(1;1) is the top-right corner of the same FSInv screen
        constant real Backpack_Pos_X = 0.70;
        constant real Backpack_Pos_Y = 0.15;
        
        //TODO: Implement it
        //Same but for the usable items hotbar
        constant real Consumables_Pos_X = 0.50;
        constant real Consumables_Pos_Y = 0.95;
        
        //Same but for the tooltip window
        constant real Tooltip_Pos_X = 0.69;
        constant real Tooltip_Pos_Y = 0.67;

        //The number of equip slots your map will use
        //Please remember that:
        //Total units having inventory * max(&lt;number of equipment slots&gt;, &lt;number of backpack slots&gt;) &lt; ~8190
        //(if that seems small to you, once again, convince me, and I might make a hashtable version w/o restrictions)
        constant integer EQUIPMENT_SLOTS_TOTAL = 5;
        
        //Path for the Bazez.mdl file
        constant string TRACKABLE_PATH = &quot;war3mapImported\\Bazez.mdl&quot;;
        
        //ID of the &#039;no item&#039; destructable
        constant integer ITEM_NONE_DESTRID = &#039;BI01&#039;;
        
        //Whether to pause unit while browsing its inventory /recommended/
        constant boolean PauseUnits = true;
    
 // ---------------------------------------- \\
 // ---- Do not modify below this point ---- \\
 // -- ...unless you know what ye&#039;r doing -- \\
 // ---------------------------------------- \\
    
        timer CameraTimer = CreateTimer();
        hashtable ht = InitHashtable();
        destructable dBackground;

        //player properties
        fogmodifier  PVar_FogModifier[12];
        rect         PVar_CamBounds[12];
        unit         PVar_CurrentUnit[12];
        boolean      PVar_InvOpen[12];
        real         PVar_CamX[12];
        real         PVar_CamY[12];
        boolean      PVar_InitObj[12];


        constant real INV_WIDTH = 85;
        constant real INV_HEIGHT = 45;
        constant real INV_CENTER_X = MapPosition_X + INV_WIDTH / 2;
        constant real INV_CENTER_Y = MapPosition_Y + INV_HEIGHT / 2;
        constant integer BACKPACK_SLOTS_TOTAL = 36;//Backpack_Rows * Backpack_Columns

        //constants for hashtable keys.. no, I won&#039;t use vJASS keys, I prefer writing &#039;em on my own
        constant integer    HTABLE_EQUIPSLOTS = 0,
                            HTABLE_ITEMS = 1,
                            HTABLE_UNITINV = 2,
                            HTABLE_SLOTOBJS = 3;
    
    //equipment slots
    public struct EquipSlot
    {
        public
        {
            static integer count = 0; 
        
            integer thiscount = 0;
            Item thisArray[200];   //number of items per type. I assume no more than 200 item types for -each slot-, with 40 max slots
        
            string Description;
            real X;
            real Y;
            integer index;
        }
        private string Namez;
        
        public static method Create(string pName, string pDescription, real pX, real pY)
        {
            count += 1;
            thistype(count).Description = pDescription;
            thistype(count).Namez = pName;
            thistype(count).X = pX;
            thistype(count).Y = pY;
            thistype(count).index = count;
            SaveInteger(ht, HTABLE_EQUIPSLOTS, StringHash(pName), count);
        }
        
        method operator Name() -&gt; string
        {
            return Namez;
        }
        
        method operator Name=(string s)
        {
            SaveInteger(ht, HTABLE_EQUIPSLOTS, StringHash(s), index);
            Namez = s;
        }
        
        //(ArrayGet + HashtableGet) is faster than (NUMSLOTS * ArrayGet)
        static method operator[](string s) -&gt; thistype
        {
            return thistype(LoadInteger(ht, HTABLE_EQUIPSLOTS, StringHash(s)));
        }
        
        //ex: Item.Random(EquipSlot[&quot;head&quot;])
        method Random (EquipSlot es) -&gt; Item
        {
            return thisArray[GetRandomInt(0, thiscount)];
        }
    }
    
    public struct Item
    {
        static integer count = 0;
        
        string Description;
        string Color;

        EquipSlot Slot;
        integer index,
                DestructableId,
                LumberCost,
                GoldCost;


        integer Life = 0,
                Mana = 0,
                Strength = 0,
                Agility = 0,
                Intelligence = 0,
                Damage = 0,
                Armor = 0,
                AtkSpeed = 0;
        
        integer PassiveAbilities[10];
        
        //TODO: Implement &#039;em
        //integer itemlevel
        //integer Charges
        //boolean MustBeEquipped  //...to give bonuses
        //boolean CannotBePawned
        //boolean CannotBeDropped
        
        private string Namez;
        
        method operator Name() -&gt; string
        {
            return Namez;
        }
        
        method operator Name= (string s)
        {
            //RemoveSavedInteger(ht, HTABLE_ITEMS, StringHash(.Namez));     // - computation time, plus ability to access item via its old name isnt a minus
            Namez = s;
            SaveInteger(ht, HTABLE_ITEMS, StringHash(s), index);
        }
        
        static method Create(string pName, string pDescription, EquipSlot pSlot, integer pDestructableId)
        {
            count += 1;
            thistype(count).Namez = pName;
            thistype(count).Description = pDescription;
            thistype(count).DestructableId = pDestructableId;

            thistype(count).Slot = pSlot;
            thistype(count).index = count;
            
            thistype(count).Color = &quot;ffffff&quot;;
            SaveInteger(ht, HTABLE_ITEMS, StringHash(pName), count);
            pSlot.thisArray[pSlot.thiscount] = thistype(count);
            pSlot.thiscount = pSlot.thiscount + 1;
        }
        
        static method CreateEx(string pName, string pDescription, EquipSlot pSlot, integer pDestructableId, integer pGoldCost, integer pLumberCost, integer pHP, integer pMana, integer pStr, integer pAgi, integer pInt, integer pAS, integer pDmg, integer pArmor)
        {
            count += 1;
            thistype(count).Namez = pName;
            thistype(count).Description = pDescription;
            thistype(count).DestructableId = pDestructableId;
            thistype(count).GoldCost = pGoldCost;
            thistype(count).LumberCost = pLumberCost;
            
            thistype(count).Life = pHP;
            thistype(count).Mana = pMana;
            thistype(count).Strength = pStr;
            thistype(count).Agility = pAgi;
            thistype(count).Intelligence = pInt;
            thistype(count).Damage = pDmg;
            thistype(count).Armor = pArmor;
            thistype(count).AtkSpeed = pAS;
            
            thistype(count).Color = &quot;ffffff&quot;;
            thistype(count).Slot = pSlot;
            thistype(count).index = count;
            SaveInteger(ht, HTABLE_ITEMS, StringHash(pName), count);
            pSlot.thisArray[pSlot.thiscount] = thistype(count);
            pSlot.thiscount += 1;
        }
        
        //(ArrayGet + HashtableGet) is faster than (NUMSLOTS * ArrayGet)
        //(main reason for overloading &#039;Name&#039; set/get)
        //ex: Item[&quot;Shiny Breastplate&quot;]
        static method operator[] (string s) -&gt; thistype
        {
            return thistype(LoadInteger(ht, HTABLE_ITEMS, StringHash(s)));
        }
        
        method Bonuses (unit u, boolean add)
        {
            integer i;
            if (add)
                i = 1;
            else
                i = -1;
            
            Bonus_Life<u> = Bonus_Life<u> + i * Life / Bonus_Life.DELTA;
            Bonus_Mana<u> = Bonus_Mana<u> + i * Mana / Bonus_Mana.DELTA;
            Bonus_Armor<u> = Bonus_Armor<u> + i * Armor / Bonus_Armor.DELTA;
            Bonus_Damage<u> = Bonus_Damage<u> + i * Damage / Bonus_Damage.DELTA;
            Bonus_Str<u> = Bonus_Str<u> + i * Strength / Bonus_Str.DELTA;
            Bonus_Agi<u> = Bonus_Agi<u> + i * Agility / Bonus_Agi.DELTA;
            Bonus_Int<u> = Bonus_Int<u> + i * Intelligence / Bonus_Int.DELTA;
            Bonus_AttackSpeed<u> = Bonus_AttackSpeed<u> + i * AtkSpeed / Bonus_AttackSpeed.DELTA;
        }
    }
    
    struct TooltipWindow    //wanna-be class
    {
        static destructable GoldIcon;
        static texttag GoldText = CreateTextTag();
        static destructable LumberIcon;
        static texttag LumberText = CreateTextTag();
        static texttag HeaderText = CreateTextTag();
        static texttag BodyText = CreateTextTag();
        
        static constant real TOOLTIP_WIDTH = 0.25;
        static constant real TOOLTIP_HEIGHT = 0.20;
        static integer arr[];
        
        static method GetX(real i) -&gt; real
        {
            return INV_CENTER_X + INV_WIDTH * (Tooltip_Pos_X - 0.5) + INV_WIDTH * i * TOOLTIP_WIDTH;
        }
        
        static method GetY(real i) -&gt; real
        {
            return INV_CENTER_Y + INV_HEIGHT * (Tooltip_Pos_Y - 0.5) + INV_HEIGHT * i * TOOLTIP_HEIGHT;
        }
        
        static method SetText(ItemSlotObj obj) //string header, string body, string color, integer gold, integer lumber, integer p)
        {
            if (GetPlayerId(GetLocalPlayer()) == obj.Owner)
            {
                if(obj.CurItem == 0)
                {
                    SetTextTagColor(HeaderText, 127, 127, 127, 255);
                    if(obj.eq)
                    {
                        SetTextTagColor(BodyText, 127, 127, 127, 255);
                
                        SetTextTagText(HeaderText, EquipSlotObj(obj).EQSlot.Name, 0.022);
                        SetTextTagText(BodyText, EquipSlotObj(obj).EQSlot.Description, 0.022);
                    }
                    else
                    {
                        SetTextTagText(HeaderText, &quot;No item&quot;, 0.022);
                        SetTextTagText(BodyText, &quot;&quot;, 0.022);
                    }
                }
                else
                {
                    SetTextTagColor(HeaderText, HexTo255(SubString(obj.CurItem.Color, 0, 2)), HexTo255(SubString(obj.CurItem.Color, 2, 4)), HexTo255(SubString(obj.CurItem.Color, 4, 6)), 255);
                    SetTextTagColor(BodyText, 255, 255, 255, 255);
                    
                    SetTextTagText(HeaderText, obj.CurItem.Name, 0.022);
                    SetTextTagText(BodyText, obj.CurItem.Description, 0.022);
                }
                
                if (obj.CurItem.GoldCost == 0)
                {
                    ShowDestructable(GoldIcon, false);
                    SetTextTagVisibility(GoldText, false);
                }
                else
                {
                    ShowDestructable(GoldIcon, true);
                    SetTextTagVisibility(GoldText, true);
                    SetTextTagText(GoldText, I2S(obj.CurItem.GoldCost), 0.022);
                }
                
                if (obj.CurItem.LumberCost == 0)
                {
                    ShowDestructable(LumberIcon, false);
                    SetTextTagVisibility(LumberText, false);
                }
                else
                {
                    ShowDestructable(LumberIcon, true);
                    SetTextTagVisibility(LumberText, true);
                    SetTextTagText(LumberText, I2S(obj.CurItem.LumberCost), 0.022);
                }
            }
        }
        
        static method HideText(integer p)
        {
            if (GetLocalPlayer() == Player(p))
            {
                SetTextTagText(HeaderText, &quot;&quot;, 0.022);
                SetTextTagText(BodyText, &quot;&quot;, 0.022);
                SetTextTagText(LumberText, &quot;&quot;, 0.022);
                SetTextTagText(GoldText, &quot;&quot;, 0.022);
                ShowDestructable(GoldIcon, false);
                ShowDestructable(LumberIcon, false);
            }
        }
        
        static method Initialize()
        {
            SetTextTagPos(HeaderText, GetX(0), GetY(0.9), 0);
            SetTextTagPos(BodyText, GetX(0), GetY(0), 0);
            SetTextTagPos(GoldText, GetX(0.45), GetY(0.7), 0);
            SetTextTagPos(LumberText, GetX(0.8), GetY(0.7), 0);
            GoldIcon = CreateDestructable(&#039;B001&#039;, GetX(0.35), GetY(0.72), 0, 1, 0);
            LumberIcon = CreateDestructable(&#039;B002&#039;, GetX(0.7), GetY(0.72), 0, 1, 0);
            ShowDestructable(GoldIcon, false);
            ShowDestructable(LumberIcon, false);
        }
    }
    
    struct ItemSlotObj
    {
        static thistype Picked[12];
        
        trackable tkMain;
        destructable dMain;
        Item CurItem;
        boolean eq = false;
        integer Owner;
        trigger OnPointTrigger = CreateTrigger();
        trigger OnClickTrigger = CreateTrigger();
        //TODO: implement
        //destructable dBorder
        
        method operator Item= (Item it)
        {
            real x, y;            
            if (eq)
            {
                x = EquipSlotObj.GetX(EquipSlotObj(this).EQSlot);
                y = EquipSlotObj.GetY(EquipSlotObj(this).EQSlot);
            }
            else
            {
                x = BackpackSlotObj.GetX(BackpackSlotObj(this).id);
                y = BackpackSlotObj.GetY(BackpackSlotObj(this).id);
            }
            
            if (CurItem != it || (CurItem == it &amp;&amp; it == 0))
            {
                CurItem = it;
                if (dMain != null)
                    RemoveDestructable(dMain);
                if (it != 0)
                    dMain = CreateDestructable(it.DestructableId, x, y, 0, 1, 0);
                else
                    dMain = CreateDestructable(ITEM_NONE_DESTRID, x, y, 0, 1, 0);
                if (GetLocalPlayer() != Player(Owner))
                    ShowDestructable(dMain, false);
            }
        }
        
        method operator Item() -&gt; Item
        {
            return CurItem;
        }
        
        static method OnPoint() -&gt; boolean
        {
            thistype obj = LoadInteger(ht, HTABLE_SLOTOBJS, GetHandleId(GetTriggeringTrigger()));
            if (obj.CurItem == 0)
                if (obj.eq)
                    TooltipWindow.SetText(obj);
                else
                    TooltipWindow.SetText(obj);
            else
            {
                TooltipWindow.SetText(obj);
            }
            return false;
        }

        static method OnClick() -&gt; boolean
        {
            integer t;
            thistype obj = LoadInteger(ht, HTABLE_SLOTOBJS, GetHandleId(GetTriggeringTrigger()));
            debug ClearTextMessages();
            if (Picked[obj.Owner] == 0)
            {
                if (obj.CurItem != 0)
                {
                    debug BJDebugMsg(&quot;selected item &quot; + obj.CurItem.Name);
                    Picked[obj.Owner] = obj;
                    //add sfx
                }
            }
            else if (Picked[obj.Owner] == obj)
            {
                debug BJDebugMsg(&quot;unselected item&quot;);
                Picked[obj.Owner] = 0;
                //remove sfx
            }
            else if (Picked[obj.Owner].eq)
            {
                if (obj.eq)
                    if (EquipSlotObj(obj).EQSlot.Name == EquipSlotObj(Picked[obj.Owner]).EQSlot.Name)
                    {
                        TooltipWindow.SetText(Picked[obj.Owner]);
                        t = obj.CurItem;
                        obj.Item = Picked[obj.Owner].CurItem;
                        Picked[obj.Owner].Item = t;
                        debug BJDebugMsg(&quot;swapped / moved eq to eq &quot; + Picked[obj.Owner].CurItem.Name);
                        Picked[obj.Owner] = 0;
                    }
                    else
                        SimError(Player(obj.Owner), &quot;Invalid slot&quot;);
                else if (obj.CurItem == 0)
                {
                    TooltipWindow.SetText(Picked[obj.Owner]);
                    obj.Item = Picked[obj.Owner].CurItem;
                    Picked[obj.Owner].CurItem.Bonuses(PVar_CurrentUnit[obj.Owner], false);
                    Picked[obj.Owner].Item = 0;
                    debug BJDebugMsg(&quot;unequipped&quot;);
                    Picked[obj.Owner] = 0;
                    //remove sfx
                }
                else if (obj.CurItem.Slot.Name == Picked[obj.Owner].CurItem.Slot.Name)
                {
                    TooltipWindow.SetText(Picked[obj.Owner]);
                    Picked[obj.Owner].CurItem.Bonuses(PVar_CurrentUnit[obj.Owner], false);
                    obj.CurItem.Bonuses(PVar_CurrentUnit[obj.Owner], true);
                    t = obj.CurItem;
                    obj.Item = Picked[obj.Owner].CurItem;
                    Picked[obj.Owner].Item = t;
                    debug BJDebugMsg(&quot;swapped&quot;);
                    Picked[obj.Owner] = 0;
                    //remove sfx
                }
                else
                    SimError(Player(obj.Owner), &quot;The target item cannot go in that slot&quot;);
            }
            else if (obj.eq)
            {
                if (EquipSlotObj(obj).EQSlot.Name == Picked[obj.Owner].CurItem.Slot.Name)
                {
                    TooltipWindow.SetText(Picked[obj.Owner]);
                    Picked[obj.Owner].CurItem.Bonuses(PVar_CurrentUnit[obj.Owner], true);
                    obj.CurItem.Bonuses(PVar_CurrentUnit[obj.Owner], false);
                    t = obj.CurItem;
                    obj.Item = Picked[obj.Owner].CurItem;
                    Picked[obj.Owner].Item = t;
                    debug BJDebugMsg(&quot;swapped / moved&quot;);
                    Picked[obj.Owner] = 0;
                    //remove sfx
                }
                else
                    SimError(Player(obj.Owner), &quot;Invalid slot&quot;);
            }
            else
            {
                TooltipWindow.SetText(Picked[obj.Owner]);
                t = obj.CurItem;
                obj.Item = Picked[obj.Owner].CurItem;
                Picked[obj.Owner].Item = t;
                debug BJDebugMsg(&quot;swapped / moved backpack to backpack &quot; + Picked[obj.Owner].CurItem.Name);
                Picked[obj.Owner] = 0;
                //remove sfx
            }
            return false;
        }
    }
    
    //equip slots
    struct EquipSlotObj extends ItemSlotObj
    {
        public static thistype Array[EQUIPMENT_SLOTS_TOTAL][12];
        
        public EquipSlot EQSlot;
        
        static method GetX(EquipSlot eq) -&gt; real
        {
            return INV_CENTER_X + INV_WIDTH * (eq.X - 0.5);
        }
        
        static method GetY(EquipSlot eq) -&gt; real
        {
            return INV_CENTER_Y + INV_HEIGHT * (eq.Y - 0.5);
        }
        
        static method create (EquipSlot es, integer pid) -&gt; thistype
        {
            thistype t = thistype.allocate();
            t.EQSlot = es;
            t.Owner = pid;
            t.tkMain = CreateTrackable(TRACKABLE_PATH, GetX(es), GetY(es), 0);
            t.eq = true;
            
            TriggerRegisterTrackableTrackEvent(t.OnPointTrigger, t.tkMain);
            TriggerAddCondition(t.OnPointTrigger, Condition(function ItemSlotObj.OnPoint));
            SaveInteger(ht, HTABLE_SLOTOBJS, GetHandleId(t.OnPointTrigger), t);
            
            TriggerRegisterTrackableHitEvent(t.OnClickTrigger, t.tkMain);
            TriggerAddCondition(t.OnClickTrigger, Condition(function ItemSlotObj.OnClick));
            SaveInteger(ht, HTABLE_SLOTOBJS, GetHandleId(t.OnClickTrigger), t);
            return t;
        }
        
        static method InitForPlayer(integer pid)
        {
            integer i = 0;
            for(0 &lt;= i &lt; EQUIPMENT_SLOTS_TOTAL)
                thistype.Array<i>[pid] = thistype.create(EquipSlot(i+1), pid);
        }
    }
    
    //backpack slots 
    struct BackpackSlotObj extends ItemSlotObj
    {
        static thistype Array[BACKPACK_SLOTS_TOTAL][12];
        
        integer id;
        
        static method GetX(integer id) -&gt; real
        {
            return INV_CENTER_X + INV_WIDTH * (Backpack_Pos_X - 0.5) + 3.75 * (id - (id / Backpack_Columns) * Backpack_Columns);
        }
        
        static method GetY(integer id) -&gt; real
        {
            return INV_CENTER_Y + INV_HEIGHT * (Backpack_Pos_Y - 0.5) + 3.75 * (id / Backpack_Columns);
        }
        
        static method create (integer i, integer pid) -&gt; thistype
        {
            thistype t = thistype.allocate();
            t.id = i;
            t.Owner = pid;
            t.tkMain = CreateTrackable(TRACKABLE_PATH, thistype.GetX(i), thistype.GetY(i), 0);
            TriggerRegisterTrackableTrackEvent(t.OnPointTrigger, t.tkMain);
            TriggerAddCondition(t.OnPointTrigger, Condition(function ItemSlotObj.OnPoint));
            SaveInteger(ht, HTABLE_SLOTOBJS, GetHandleId(t.OnPointTrigger), t);
            
            TriggerRegisterTrackableHitEvent(t.OnClickTrigger, t.tkMain);
            TriggerAddCondition(t.OnClickTrigger, Condition(function ItemSlotObj.OnClick));
            SaveInteger(ht, HTABLE_SLOTOBJS, GetHandleId(t.OnClickTrigger), t);
            return t;
        }
        
        static method InitForPlayer (integer pid)
        {
            integer i = 0;
            for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
                thistype.Array<i>[pid] = thistype.create(i, pid);
        }
    }
    
    public struct Inventory
    {
        //struct responsible for saving every unit&#039;s inventory
        //and all inventory-related functions
        public
        {
            Item EquipSlots[EQUIPMENT_SLOTS_TOTAL];
            Item BackpackSlots[BACKPACK_SLOTS_TOTAL];
        }
        unit Unit;
        
        //u:Inventory
        static method operator[] (unit u) -&gt; thistype
        {
            thistype ret = LoadInteger(ht, HTABLE_UNITINV, GetHandleId(u));
            if (ret.Unit != u)   //unit has died or unit doesn&#039;t have inv
            {
                if (ret != 0)    //old unit with that id has died
                    ret.destroy(); //no fucking idea if that&#039;s how it should be done -.-
                ret = thistype.allocate();
                ret.Unit = u;
                SaveInteger(ht, HTABLE_UNITINV, GetHandleId(u), ret);
                debug BJDebugMsg(&quot;DEBUG: adding 2 sample items to inv[&quot; + I2S(ret) + &quot;]. To edit - Line ~660 in WE&quot;);
                debug ret.BackpackSlots[0] = Item[&quot;Shiny Breastplate&quot;];
                debug ret.BackpackSlots[1] = Item[&quot;Tarnished Breastplate&quot;];
            }
            return ret;
        }
        //u:Inventory.GetBackpackItem(0, 0).Name
        method GetBackpackItem(integer row, integer column) -&gt; Item
        {
            return BackpackSlots[Backpack_Rows * column + row];
        }
        
        //u:Inventory.GetEquippedItem(EquipSlot[&quot;head&quot;]).Name
        method GetEquippedItem(EquipSlot eq) -&gt; Item
        {
            return EquipSlots[eq - 1];
        }
        
        //u:Inventory.HasItem(Item[&quot;Breastplate&quot;], true, true)
        method HasItem (Item it, boolean inv, boolean eq) -&gt; boolean
        {
            integer i;
            if (inv)
                for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
                    if (BackpackSlots<i> == it)
                        return true;
            if (eq &amp;&amp; EquipSlots[it.Slot] == it)
                return true;
            return false;
        }
        
        //u:Inventory.RemoveItem(Item[&quot;Breastplate&quot;], true)
        method DeleteItem (Item it, boolean all)
        {
            integer i;
            if(EquipSlots[it.Slot - 1] == it)
            {
                EquipSlots[it.Slot - 1] = 0;
                if (!all) return;
            }
            else
                for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
                    if(BackpackSlots<i> == it)
                    {
                        BackpackSlots<i> = 0;
                        if (!all) return;
                    }
        }
        
        //u:Inventory.AddItem(Item[&quot;Breastplate&quot;], true)
        method AddItem(Item it, boolean drop)
        {
            integer i;
            if(EquipSlots[it.Slot - 1] == 0)
            {
                EquipSlots[it.Slot - 1] = it;
            }
            else
            {
                for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
                    if(BackpackSlots<i> == 0)
                    {
                        BackpackSlots<i> = it;
                        return;
                    }
                //create item on the ground
            }
        }
    }
    
    //END structs
    
    function OnLeaveInventory()
    {
        player p = GetTriggerPlayer();
        integer pn = GetPlayerId(p), i;
        Inventory inv;
        
        if (GetLocalPlayer() == p)
        {
            ShowDestructable(dBackground, false);
            SetCameraBoundsToRect(PVar_CamBounds[pn]);   //cba inlining it!...
            ResetToGameCamera(0);
            SetCameraPosition(PVar_CamX[pn], PVar_CamY[pn]);
        }
        DestroyFogModifier(PVar_FogModifier[pn]);
        PVar_InvOpen[pn] = false;
        PauseUnit(PVar_CurrentUnit[pn], false);
        TooltipWindow.HideText(pn);
        ItemSlotObj.Picked[pn] = 0;
        //save manipulated unit&#039;s items
        inv = PVar_CurrentUnit[pn]:Inventory;
        
        for(0 &lt;= i &lt; EQUIPMENT_SLOTS_TOTAL)
            inv.EquipSlots<i> = EquipSlotObj.Array<i>[pn].Item;
        
        for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
            inv.BackpackSlots<i> = BackpackSlotObj.Array<i>[pn].Item;
        
        p = null;
    }
    
    function OnEnterInventory()
    {
        player p = GetOwningPlayer(GetTriggerUnit());
        integer pn = GetPlayerId(p), i;
        real x, y;
        Inventory inv;
        PVar_CurrentUnit[pn] = GetTriggerUnit();
        //stop pause unit
        IssueImmediateOrder(PVar_CurrentUnit[pn], &quot;stop&quot;);
        PauseUnit(PVar_CurrentUnit[pn], PauseUnits);
        //create visibility modifier
        PVar_FogModifier[pn] = CreateFogModifierRect(p, FOG_OF_WAR_VISIBLE, Rect(MapPosition_X, MapPosition_Y, MapPosition_X + INV_WIDTH, MapPosition_Y + INV_HEIGHT), false, false);
        FogModifierStart(PVar_FogModifier[pn]);
        //set vars
        PVar_InvOpen[pn] = true;
        PVar_CamX[pn] = GetCameraTargetPositionX();
        PVar_CamY[pn] = GetCameraTargetPositionY();
        PVar_CamBounds[pn] = Rect(GetCameraBoundMinX(), GetCameraBoundMinY(), GetCameraBoundMaxX(), GetCameraBoundMaxY());
        //set camera / background
        if (GetLocalPlayer() == p)
        {
            SetCameraPosition(INV_CENTER_X,INV_CENTER_Y);
            SetCameraBounds(INV_CENTER_X,INV_CENTER_Y,INV_CENTER_X,INV_CENTER_Y,INV_CENTER_X,INV_CENTER_Y,INV_CENTER_X,INV_CENTER_Y);
            ShowDestructable(dBackground, true);
        }
        //init destructablez / trackablez 1st time
        if (!PVar_InitObj[pn])
        {
            BackpackSlotObj.InitForPlayer(pn);
            EquipSlotObj.InitForPlayer(pn);
            PVar_InitObj[pn] = true;
        }
        //load items from uinv struct
        inv = PVar_CurrentUnit[pn]:Inventory;
        for(0 &lt;= i &lt; EQUIPMENT_SLOTS_TOTAL)
            EquipSlotObj.Array<i>[pn].Item = inv.EquipSlots<i>;
        
        for(0 &lt;= i &lt; BACKPACK_SLOTS_TOTAL)
            BackpackSlotObj.Array<i>[pn].Item = inv.BackpackSlots<i>;

        p = null;
    }
    
    function CameraTimed()
    {
        if (PVar_InvOpen[GetPlayerId(GetLocalPlayer())])
        {
            //if (GetCameraField(CAMERA_FIELD_TARGET_DISTANCE) != 135)  //
            //{
                SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270, 0);
                SetCameraField(CAMERA_FIELD_ROTATION, 90, 0);
                SetCameraField(CAMERA_FIELD_FARZ, 5000, 0);
                SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 135, 0);
                SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW, 44, 0);
            //}
        }
    }
    
    function onInit()
    {
        integer i = 0, k = 0;
        trigger t = CreateTrigger(), t2 = CreateTrigger();
        TimerStart(CameraTimer, 0.025, true, function CameraTimed);
        dBackground = CreateDestructable(&#039;BX00&#039;, INV_CENTER_X, INV_CENTER_Y, 270., 1., 0);
        ShowDestructable(dBackground, false);
        for(0 &lt;= i &lt; 12)
        {
            TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC);
            TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null);
        }
        SetTerrainPathable(INV_CENTER_X, INV_CENTER_Y, PATHING_TYPE_ANY, false);
        TriggerAddCondition(t2, Condition(
                                            function() -&gt; boolean 
                                            { 
                                                if(GetSpellAbilityId() == AbilityID)
                                                    OnEnterInventory();
                                                return false;
                                            }
                                          ));
        TriggerAddCondition(t, Condition(
                                            function() -&gt; boolean 
                                            { 
                                                if(PVar_InvOpen[GetPlayerId(GetTriggerPlayer())])
                                                    OnLeaveInventory();
                                                return false;
                                            }
                                         ));        
        TooltipWindow.Initialize();
        
        t = null;
        t2 = null;
    }
}
//! endzinc</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u></u>
(~850 lines so far, be warned)

And here's the sample FSInvObjects (the interface for equipment slots/items):
JASS:
library FSInvObjects initializer Init_Vars requires FSInv
    public function Init_Vars takes nothing returns nothing
        // DESCRIPTION: Initializes a slot for equipping items with given parameters
        // SYNTAX:      EquipSlot.Create(SLOT_NAME, SLOT_DESCRIPTION, SCREEN_X, SCREEN_Y)
        //              SREEN_X and SCREEN_Y follow the same rules as backpack positioning, ranging [0;1]
        // WARNING:     You should only create and modify slots in this trigger. 
        //              EquipSlots with same names are treated as same slots
        // EXAMPLES:
        call EquipSlot.Create(&quot;Head&quot;, &quot;Headpieces from steel, leather or cloth are often made to protect the upper part of one&#039;s body.. the head&quot;, 0.3, 0.8)
        call EquipSlot.Create(&quot;Chest&quot;, &quot;Armor pieces made from steel, metal rings, leather, or even simple clothing are worn on the chest&quot;, 0.3, 0.7)
        call EquipSlot.Create(&quot;Chest&quot;, &quot;Armor pieces made from steel, metal rings, leather, or even simple clothing are worn on the chest&quot;, 0.4, 0.7)
        //    having 2 chest slots just cuz Im too lazy to make two rings or something like that to test double slots. 
        //    yes, it doesn&#039;t make any sense =D
        call EquipSlot.Create(&quot;Waist&quot;, &quot;Different pants - different belts!&quot;, 0.3, 0.6)
        call EquipSlot.Create(&quot;Legs&quot;, &quot;Panties - so others don&#039;t get frightened by your butt!&quot;, 0.3, 0.5)
        call EquipSlot.Create(&quot;Feet&quot;, &quot;You don&#039;t want to walk over these dead monsters without shoes, do you?&quot;, 0.3, 0.4)
        
        //DESCRIPTION: Creates an item with given name, description, equipment slot, gold/lumber cost, doodad file, stats modifiers
        //SYNTAX:      Item.Create(NAME, DESCRIPTION, SLOT, GOLD_COST, LUMBER_COST, DOODAD_FILE, HEALTH_MOD, MANA_MOD, STR_MOD, AGI_MOD, INT_MOD, ATK_SPEED_MOD, DAMAGE_MOD, ARMOR_MOD)
        //EXTRA:       In DESCRIPTION or NAME                              %n%,   %s%,    %g%,      %l%,      %hp%,     %mp%,     %str%,   %agi%,   %int%,      %as%,        %dmg%       %arm%
        //             are substituted with the values of correspondingly  NAME, SLOT, GOLD_COST, LUMBER_COST, HEALTH_MOD, MANA_MOD, STR_MOD, AGI_MOD, INT_MOD, ATK_SPEED_MOD, DAMAGE_MOD, ARMOR_MOD 
        call Item.CreateEx(&quot;Shiny Breastplate&quot;, &quot;A brightly glowing, lightweight piece of armor\n+10 Armor\n+5 Strength&quot;, EquipSlot[&quot;Chest&quot;], &#039;BI07&#039;, 2500, 1000, 500, 0, 5, 0, 0, 0, 0, 10)
        call Item.CreateEx(&quot;Tarnished Breastplate&quot;, &quot;A piece of armor seen many battles\n+5 Armor\n+5 Strength&quot;, EquipSlot[&quot;Chest&quot;], &#039;BI08&#039;, 1500, 500, 0, 0, 5, 0, 0, 0, 0, 5)
        
        //DESCRIPTION: Adds/Removes a passive ability from an item eventually disabling the abil for the player
        //             (wrapper for &lt;item&gt;.PassiveAbilities[&lt;num&gt;] = &lt;abil&gt;)
        //SYNTAX:      Item.AddPassive(ABIL_ID, DISABLE_FLAG)
        //SYNTAX:      Item.RemovePassive(ABIL_ID)
        //WARNING:     The ability will be visible unless it&#039;s based off spellbook, contains the wanted passive and is disabled. 
        //EXAMPLES:
        
    endfunction
endlibrary


Some other examples for the user syntax:
JASS:

.
    local Inventory inv = u:Inventory    //a pointer to unit&#039;s inventory
    if u2:Inventory.HasItem(Item[&quot;Shiny Breastplate&quot;]) then    //if u2 has item &quot;Shiny Breastplate&quot; in inventory...
        call inv.AddItem(Item[&quot;Shiny Breastplate&quot;], true)    //create that item in u&#039;s inventory and drop it on the ground if its inventory is full
    endif
    call inv.GetBackpackItem(0, 0).Name)    //returns the name of the item at backpack slot (0,0)
    inv.DeleteItem(Item[&quot;Shiny Breastplate&quot;], true)    //removes all instances of the &quot;Shiny Breastplate&quot; item from u&#039;s inventory. 
    //... and so on

Pfft, at last, would love comments and opinions. Would also like to hear any bugs / optimizations you find, although the map is still untested in multiplayer (it WILL be compatible, just atm it might not be :Р) and though I see some places where code can be significantly optimized / improved.

And, finally, the poor testmap (yes, the miniature inventory can be seen once you access it, but it's supposed to be put on a looonely place!!):
 

Attachments

  • FS_Inv.w3x
    64.4 KB · Views: 243

Viikuna

No Marlo no game.
Reaction score
265
I find FSGUIs method nicer, even though it requires some camera system to work properly.

Why?

Because this stuff is kinda only useful in RPG maps, most of which have somekind of camera system already.


But yea, this is pretty cool too.
 
Reaction score
341
I find FSGUIs method nicer, even though it requires some camera system to work properly.

Doesn't require a cam sys AFAIK.

The bag system from wc3c does though (if that's what you were thinking of).

Anyways, the interface is decent, I suppose your going to add borders and things to the inventory.

Also, none of the items showed up.
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
the origninal FSGUI is really awesome (at that time) and it's not too hard to learn, however, now I prefer Toadcop's :D.

Toadcop's system takes less space then the original FSGUI.
But avg users can use resources from Toadcop's system to minimize the "restriction areas" :) (maybe they aren't needed - no restriction areas)

You should use Multiboard to display the tooltips of the items instead of floating text because there are items with long tooltip so the tooltip could screw the interface up.
 

the Immortal

I know, I know...
Reaction score
51
First thanks for the replies, and sorry for being away fo' that much.. simply wasn't able to.

And second, the idea behind this is to be a fusion between FSInv and Toadcop's system (name btw?) which is modular, easily modifiable, fast, and as lightweight as possible. As you see it is pretty far from this at its current state. But for now I'd simply want to see if peepl are interested and maybe even feedback on coding (if someone has the time to look at it) as I (eventually) upgrade it /since I have free time so rarely these days/

Atm working on ground items, eventually switching item display to multiboard and cleaning up interface.
Also having probs with trackables not firing events ~_~ but it b fine I say!

bumpity!
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top