Jump to content

[HELP] inventory system


iwalidza

Recommended Posts

Hello iwalidza,

since you do not provide any design wishes and I have no experience with inventory system design, I will limit my contribution to the technical side of inventory systems. You may want to create new kinds of items for roleplay such as keys or identity certificates that players have to collect and treasure.

An inventory system is defined as a set of items that belong to peds or players. Items can be taken out or put in at wish. Thus we want to attach a table of items for at least each player on the server and synchronize the items with the clients.

server.Lua

-- We want to create items definitions here.
local item_definitions = {};

local function register_item(id, name, description)
  local def = {
    ["name"] = name,
    ["id"] = id,
    ["description"] = description
  };
  
  item_definitions[id] = def;
end

register_item(1, "Knife", "Sharp item to cut things");
register_item(2, "Sword", "Sharp weapon for slashing");
register_item(3, "Key", "Used to access secret areas in the game");

-- Item
local player_inventories = {};

addEventHandler("onPlayerQuit", root, function()
    player_inventories[source] = nil;
  end
);

local function find_existing_player_inv_slot(inv_items, item_id)
  for m,n in ipairs(inv_items) do
    if (n.item_id == item_id) then
      return n;
    end
  end
  
  return false;
end

local function give_item_to_player(player, item_id, count)
  local inv = player_inventories[player];
  
  local item_carry = find_existing_player_inv_slot(inv, item_id);
  
  if (item_carry) then
    item_carry.count = item_carry.count + count;
    return true;
  end
  
  local new_carry = {
    ["item_id"] = item_id,
    ["count"] = count
  };
  
  table.insert(inv, new_carry);
  return true;
end

addEventHandler("onPlayerJoin", root, function()
    local inventory = {};
    
    player_inventories[source] = {};
    
    give_item_to_player(source, 1, 1);
    give_item_to_player(source, 2, 1);
    give_item_to_player(source, 3, 1);
  end
);

(code has not been tested)

This is a sample system I just created. There is a knife, sword and key. At join time each player does receive a copy of any item. Now imagine that you have a marker that you can only enter with a key.

server.Lua

local function does_player_have_item(player, item_id)
  local inventory = player_inventories[player];
  
  local slot = find_existing_player_inv_slot(inventory, item_id);
  
  if (slot) and (slot.count > 0) then
    return true;
  end
  
  return false;
end

local function remove_player_item(player, item_id, count)
  local inventory = player_inventories[player];
  
  for m,n in ipairs(inventories) do
    if (n.item_id == item_id) and (n.count > 0) then
      local can_remove = math.min(count, n.count);
      
      n.count = n.count - can_remove;
      count = count - can_remove;
      
      if (count == 0) then
        break;
      end
    end
  end
end

local marker = createMarker(0, 0, 3, "cylinder");

addEventHandler("onMarkerHit", marker, function(hitElement)
    if not (getElementType(hitElement) == "player") then return; end;
    
    if not (does_player_have_item(hitElement, 3)) then
      outputChatBox("You cannot enter this zone because you have no key.");
      return;
    end
    
    outputChatBox("Using key to enter the zone.");
    
    remove_player_item(hitElement, 3, 1);
  end
);

This script creates a marker in the middle of the map. If you hit the marker then you use a key. Once you have used your starting key then you cannot successfully hit the marker anymore. The message should change the second time you hit the marker. The marker does only work with players.

I hope I could help you with this :) 

Edited by The_GTA
  • Thanks 1
Link to comment
52 minutes ago, iwalidza said:

Thanks, thanks and thanks again @The_GTA ,For this

And if you can help me with the organizing system inside gui or dx it would be good to get an idea about it

Glad to be of help.

Since you are asking so kindly I want to give you ideas how to continue with your inventory system. I absolutely recommend going with dx because it is more powerful than CEGUI. So these are the functions that should be used:

Here is how I imagine an inventory to look.

lKJmbIg.png

Code on requesting the inventory from the server:

server.Lua

addEvent("onRequestInventory", true);
addEventHandler("onRequestInventory", root, function()
    triggerClientEvent(client, "onClientReceiveInventory", root, player_inventories[client], item_definitions);
  end
);

client.Lua

addEvent("onClientReceiveInventory", true);
addEventHandler("onClientReceiveInventory", root, function(player_inventory, item_definitions)
    -- TODO: use the data somehow.
  end
);

local function send_inventory_request()
  triggerServerEvent("onRequestInventory", root);
end

Feel free to ask more questions when you have some code done already :)

Edited by The_GTA
  • Like 2
  • Thanks 1
Link to comment
  • 1 year later...
function Inventory:takeItem(itemID,count,slot)
    if not itemID  then
        return false
    end
    self.infos = { getItemInfos (itemID)}
    if not count then
        count = self.infos[2]
    end
    if not slot then
        for i = #self.items ,1 ,-1 do
            if self.items[i][1] == itemID then
                local canTake = math.min(count,self.items[i][2])
                self.items[i][2] = self.items[i][2] - canTake
                if self.items[i][2] == 0 then
                    table.remove(self.items,i)
                end
                break
            end
        end
    end
end

I want take 140 from the item and I have 3 of this item
124 124 100

I want take 100 from the last item and second item take just 40

how can do it

@The_GTA

Link to comment

Hello iwalidza,

let me ask you some questions about your code first:

    if not count then
        count = self.infos[2]
    end

Is this supposed to set "count" to the maximum value of currently held items? This would not make sense in your model because you have multiple slots with varying count of items between each slot.

    self.infos = { getItemInfos (itemID)}

What does the getItemInfos function do? Could you post the definition aswell?

Now let's get to the ideas. Let's assume you have slots 1, 2, 3, where slot 1 is itemID 124, slot 2 is itemID 124 and slot 3 is itemID 140. I don't understand which item you want to pick from? Slot 1, 2 or 3? Let's assume you pick from slot 1 with count = 140.

inv:takeItem(inv:getItemIDFromSlot(1), 140, 1)
inv:takeItem(inv:getItemIDFromSlot(2), 40, 2)
inv:takeItem(inv:getItemIDFromSlot(3), 100, 3)

 

Link to comment

@The_GTA

items = { 
    [1] = {name = "Appel",stack = "10",isDurability = false,isAmmo=false};
    [2] = {name = "Building Plan",stack = "1",isDurability = false,isAmmo=false};
    [3] = {name = "Assault Rifle",stack = "1",isDurability = true,isAmmo=true};
    [4] = {name = "5.56 Rifle ammo",stack = "128",isDurability = false,isAmmo=false};
    [5] = {name = "Longsleeve T-Shirt",stack = "1",isDurability = false,isAmmo=false};
    [6] = {name = "T-Shirt",stack = "1",isDurability = false,isAmmo=false};
    [7] = {name = "Boots",stack = "1",isDurability = false,isAmmo=false};
    [8] = {name = "Improvised Balaclava",stack = "1",isDurability = false,isAmmo=false};
    [9] = {name = "Heavy Plate Jacket",stack = "1",isDurability = false,isAmmo=false};
    [10] = {name = "Pants",stack = "1",isDurability = false,isAmmo=false};
    [11] = {name = "Coffee Can Helmet",stack = "1",isDurability = false,isAmmo=false};
    [12] = {name = "Revolver",stack = "1",isDurability = true,isAmmo=true};
    [13] = {name = "Pistol Bullet",stack = "128",isDurability = false,isAmmo=false};
    [14] = {name = "Hammer",stack = "1",isDurability = false,isAmmo=false};
    [15] = {name = "Pickaxe",stack = "1",isDurability = true,isAmmo=false};
    [16] = {name = "Hatchet",stack = "1",isDurability = true,isAmmo=false};
}

function getItemInfos (id)
    return items[id].name,items[id].stack,items[id].isDurability,items[id].isAmmo
end

that about getItemInfos and take item firts arg its about item id in items ids i use it to select item waht i need

    if not count then
        count = self.infos[2]
    end

and that if i want to take stack just i don't add count

Link to comment
1 minute ago, iwalidza said:

@The_GTA

items = { 
    [1] = {name = "Appel",stack = "10",isDurability = false,isAmmo=false};
    [2] = {name = "Building Plan",stack = "1",isDurability = false,isAmmo=false};
    [3] = {name = "Assault Rifle",stack = "1",isDurability = true,isAmmo=true};
    [4] = {name = "5.56 Rifle ammo",stack = "128",isDurability = false,isAmmo=false};
    [5] = {name = "Longsleeve T-Shirt",stack = "1",isDurability = false,isAmmo=false};
    [6] = {name = "T-Shirt",stack = "1",isDurability = false,isAmmo=false};
    [7] = {name = "Boots",stack = "1",isDurability = false,isAmmo=false};
    [8] = {name = "Improvised Balaclava",stack = "1",isDurability = false,isAmmo=false};
    [9] = {name = "Heavy Plate Jacket",stack = "1",isDurability = false,isAmmo=false};
    [10] = {name = "Pants",stack = "1",isDurability = false,isAmmo=false};
    [11] = {name = "Coffee Can Helmet",stack = "1",isDurability = false,isAmmo=false};
    [12] = {name = "Revolver",stack = "1",isDurability = true,isAmmo=true};
    [13] = {name = "Pistol Bullet",stack = "128",isDurability = false,isAmmo=false};
    [14] = {name = "Hammer",stack = "1",isDurability = false,isAmmo=false};
    [15] = {name = "Pickaxe",stack = "1",isDurability = true,isAmmo=false};
    [16] = {name = "Hatchet",stack = "1",isDurability = true,isAmmo=false};
}

function getItemInfos (id)
    return items[id].name,items[id].stack,items[id].isDurability,items[id].isAmmo
end

You should either turn the "stack" key in the tables inside of the "item" table to Lua numbers or use tonumber inside of the getItemInfos function, for example...

items = { 
    [1] = {name = "Appel",stack = 10,isDurability = false,isAmmo=false};
    (...)
}

This is important because you are using numeric operations in your Inventory:takeItem method.

Link to comment
(...)
    if not slot then
        for i = #self.items ,1 ,-1 do
            if self.items[i][1] == itemID then
                slot = i
                break
            end
        end
        if not slot then return false end
    end
    local canTake = math.min(count,self.items[slot][2])
    self.items[slot][2] = self.items[slot][2] - canTake
    if self.items[slot][2] == 0 then
        table.remove(self.items,slot)
    end
(...)

 

Link to comment
2 minutes ago, iwalidza said:

@The_GTA

playerInventroy:takeItem(4,140)
that just take 100 from items
 

According to your stack definition you can only have 128 on each stack. I recommend you to change your algorithm to take items from multiple stacks until the requested take-count is exhausted. Are you familiar with this idea?

Link to comment

spacer.png

if i set count like 140
i want first take 100 and take 40 from 128

1 minute ago, The_GTA said:

According to your stack definition you can only have 128 on each stack. I recommend you to change your algorithm to take items from multiple stacks until the requested take-count is exhausted. Are you familiar with this idea?

Example please.

@The_GTA

Link to comment
    local slot = #self.items
    while (count > 0) and (slot > 0) do
        if self.items[slot][1] == itemID then
            local canTake = math.min(count,self.items[slot][2])
            self.items[slot][2] = self.items[slot][2] - canTake
            if self.items[slot][2] == 0 then
                table.remove(self.items,slot)
            end
            count = count - canTake
        end
        slot = slot - 1
    end
    if (count > 0) then
        outputDebugString("not taken entire count");
    end

slot parameter to Inventory:takeItem has been neglected.

Link to comment
12 minutes ago, The_GTA said:
    local slot = #self.items
    while (count > 0) and (slot > 0) do
        if self.items[slot][1] == itemID then
            local canTake = math.min(count,self.items[slot][2])
            self.items[slot][2] = self.items[slot][2] - canTake
            if self.items[slot][2] == 0 then
                table.remove(self.items,slot)
            end
            count = count - canTake
        end
        slot = slot - 1
    end
    if (count > 0) then
        outputDebugString("not taken entire count");
    end

slot parameter to Inventory:takeItem has been neglected.

thank you.

Link to comment
1 minute ago, The_GTA said:

Calculate the rectangles of each slot in the mouse click handler and associate the rectangle with the slot data structures.

i have the problem with calculate

function Inventory:getSlot (x,y)
    for i = 1self.slots do
        local t = i - 1
        local xOffset = self.startX + (t*(self.iconSize + self.padding)) 
        local yOffset = self.startY + (math.floor(t/6)*(self.iconSize + self.padding))
        xOffset = xOffset - math.floor(t/6)*(6*(self.iconSize + self.padding))
 
        if isInBox(x,y,xOffset,xOffset + self.iconSize,yOffset,yOffset + self.iconSizethen
            slot = i
            return slot
        end
    end
end
its return
" "
" "
"slotid"
Link to comment
function Inventory:getSlot (x,y)
    for i = 1, #self.slots do -- table
        local t = i - 1
        local xOffset = self.startX + (t*(self.iconSize + self.padding)) 
        local yOffset = self.startY + (math.floor(t/6)*(self.iconSize + self.padding))
        xOffset = xOffset - math.floor(t/6)*(6*(self.iconSize + self.padding))
 
        if isInBox(x,y,xOffset,xOffset + self.iconSize,yOffset,yOffset + self.iconSize) then
            return i
        end
    end
    return false -- good idea
end

What is the definition of the isInBox function?

Edited by The_GTA
Link to comment

spacer.png
its work good but with down one just return false

beltInventory
not work why?
playerInventroy = Inventory:new('player',24,((screen.x / 2- ((DEFAULT_iconSize - DEFAULT_padding) * 3)),screen.y - ((DEFAULT_iconSize + (DEFAULT_padding * 2)) * 5),nil,nil,false)
clothInventory = Inventory:new('cloth',6,(0 + (DEFAULT_padding * 13)) ,screen.y - ((DEFAULT_iconSize + DEFAULT_padding) * 3.37),nil,nil,false)
beltInventory = Inventory:new('belt',6,((screen.x / 2- ((DEFAULT_iconSize - DEFAULT_padding) * 3)),screen.y - (DEFAULT_iconSize + DEFAULT_padding),nil,nil,true)
 
function Inventory:getSlot (x,y)
    print(self.type)
    for i = 1self.slots do
        local t = i - 1
        local xOffset = self.startX + (t*(self.iconSize + self.padding)) 
        local yOffset = self.startY + (math.floor(t/6)*(self.iconSize + self.padding))
        xOffset = xOffset - math.floor(t/6)*(6*(self.iconSize + self.padding))
 
        if isInBox(x,y,xOffset,xOffset + self.iconSize,yOffset,yOffset + self.iconSizethen
            return i
        end
    end
    return false
end
its return just cloth and player why?

@The_GTAi fix it

and how i can get the inventory player click

like if click in playerInventory its return playerInventory
or clothInventory

Link to comment
9 minutes ago, iwalidza said:

and how i can get the inventory player click

Put all inventories into a list, iterate over them in the mouse click handler and check which one has a defined rectangle item slot at the current mouse position. Store the successful inventory in a variable and retrieve it's type field to check which one it is.

Link to comment
58 minutes ago, The_GTA said:

Put all inventories into a list, iterate over them in the mouse click handler and check which one has a defined rectangle item slot at the current mouse position. Store the successful inventory in a variable and retrieve it's type field to check which one it is.

how i can chack if player mouse still click 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...