( accidently posted this too early, so im editing it )
Hi everybody. I’m working on a little side project.
It takes place in a mine… you can work with a team or alone, yadda yadda.
I need an inventory system. Problem? My brain is fried from lack of mental stimulus and working around in Blender and Roblox have been driving me to an impotent rage. I cannot think of a way to do this.
What I need:
- A guide to making a decent(ly) working inventory system.
- A guide to making a modular inventory system.
I got this little item manager (i am using OOP)
local Item = require(script.Parent.Item)
local ItemManager = {}
ItemManager.Items = {}
-- [[ HELPER ]] --
local function deepCopy(original)
local copy = {}
for k, v in pairs(original) do
if type(v) == "table" then
copy[k] = deepCopy(v)
else
copy[k] = v
end
end
return copy
end
--[[
Creates a new item class.
]]
function ItemManager:CreateItem(name: string, model: Instance, data: Item.ItemData)
local newItem = Item.new(name, model, deepCopy(data))
table.insert(self.Items, newItem)
return newItem
end
--[[
Creates and spawns a new item class.
]]
function ItemManager:SpawnItem(name: string, model: Instance, data: Item.ItemData, Cframe: CFrame, owner: Player?)
local item = self:CreateItem(name, model, deepCopy(data))
item:Spawn(Cframe, owner)
return item
end
--[[
Uses an existing item class to spawn.
]]
function ItemManager:SpawnExisting(item, Cframe: CFrame, owner: Player)
item:Spawn(Cframe, owner)
return item
end
--[[
If the item has a special interaction, trigger it.
Else, the object is picked up.
]]
function ItemManager:Interact(item, player)
if not item.Interaction then
self:PickUp(item, player)
else
item:Interact(player)
end
end
--[[
Picks up the item, and adds it to the player inventory.
]]
function ItemManager:PickUp(item, player: Player, amount: number)
if not item or not item.Data then
warn("Attempted pickup on nil item/data!")
return
end
if not item.Instance then
warn("Tried to pick up an item with no active instance!")
return
end
local newStack = math.clamp(item:GetStack() - amount, 0, math.huge)
item:TweakData("Stack", newStack)
if item:GetStack() <= 0 then
self:DestroyItem(item)
end
end
--[[
Destroys the item class.
]]
function ItemManager:DestroyItem(item)
for i, v in ipairs(self.Items) do
if v == item then
item:Destroy()
table.remove(self.Items, i)
break
end
end
end
--[[
Looks up the item name in the item list, returning the first value.
]]
function ItemManager:LookupByName(name: string)
for _, item in ipairs(self.Items) do
if item.Name == name then
return item
end
end
return nil
end
--[[
Looks up the item UUID in the item list, returning a match.
]]
function ItemManager:LookupByUUID(uuid: string)
for _, item in ipairs(self.Items) do
local itemUUID = item.Instance:GetAttribute("UUID")
if itemUUID == uuid then
return item
end
end
warn("LookupByUUID failed for", uuid)
return nil
end
--[[
Looks up the table for matching instances.
]]
function ItemManager:LookupByInstance(instance: Instance)
for _, item in ipairs(self.Items) do
if item.Instance == instance then
return item
end
end
warn("LookupByInstance failed for", instance:GetFullName())
return nil
end
return ItemManager
So, I need to somehow figure out a way to make items that are picked up - appear in the inventory depending on their type. (Item, apparel).
Tags, I’d like to be made applicable (chat tags) if the player has access to them, they appear in the specified tab.
Additionally, here’s the Item class:
local Item = {}
Item.__index = Item
export type ItemData = {
Description: string,
Stack: number,
Weight: number,
Rarity: string,
Value: number,
Interaction: (item: Instance, player: Player) -> (),
OnSpawn: (item: Instance, owner: Player) -> (),
Hooks: {Key}
}
--[[
Data:
<code>Description</code> -- Description of the item
<code>Stack</code> -- how large of a stack to form.
<code>Weight</code> -- how heavy one item is
<code>Value</code> -- how much one item is worth
<code>Type</code> -- what type of item the item is.
<code>Rarity</code> -- how rare an item is
<code>Interaction</code> -- what happens when it's interacted with (self, player)
<code>OnSpawn</code> -- what happens when it's spawned (self, owner/player)
<code>Hooks</code> -- a table of keys that listen for changes with the item's data.
]]
-- [[ CONSTRUCTOR ]] --
function Item.new(name: string, model: Instance, data: ItemData)
local self = setmetatable({}, Item)
self.Name = name
self.Model = model
self.Data = data
self.Data.Name = name or "Placeholder"
self.Interaction = data.Interaction
self.OnSpawn = data.OnSpawn
self.Data.Hooks = data.Hooks or {}
self.Hooks = self.Data.Hooks
self.Instance = nil
return self
end
-- [[ META AND HOOKS ]] --
local GlobalHooks = {
Weight = function(item, value)
if item.Instance then
Item.Instance:SetAttribute("Weight", item:GetWeight())
item.Instance:SetAttribute("StackWeight", item:GetStackWeight())
end
end,
Stack = function(item, value)
if item.Instance then
item.Instance:SetAttribute("Stack", item:GetStack())
item.Instance:SetAttribute("StackWeight", item:GetStackWeight())
item.Instance:SetAttribute("StackValue", item:GetStackValue())
end
end,
}
local function CheckForHook(key: string, item): ((Item, any) -> ())?
return item.Data.Hooks[key] or GlobalHooks[key]
end
local function CheckValueAttributeSupport(v)
local t = typeof(v)
if t == "function" or t == "table" then
return false
end
return true
end
function Item:TweakData(key: string, value: any)
if self.Data[key] == nil then
warn(`[Item:TweakData] "{key}" not originally in data of "{self.Name}". Adding it dynamically.`)
end
self.Data[key] = value
local hook = CheckForHook(key, self)
if hook then
hook(self, value)
end
if self.Instance then
self.Instance:SetAttribute(key, value)
end
end
function Item:BatchTweakData(tweaks: {[string]: any})
for key, value in pairs(tweaks) do
if self.Data[key] == nil then
warn(`[Item:BatchTweakData] "{key}" not originally in data of "{self.Name}". Adding it dynamically.`)
end
self.Data[key] = value
local hook = CheckForHook(key, self)
if hook then
hook(self, value)
end
if self.Instance then
self.Instance:SetAttribute(key, value)
end
end
end
function Item:AddHook(key: string, callback: (item, value) -> ())
self.Data.Hooks = self.Data.Hooks or {}
self.Data.Hooks[key] = callback
end
function Item:RemoveHook(key: string)
if self.Data.Hooks then
self.Data.Hooks[key] = nil
end
end
-- [[ GETTERS ]] --
function Item:GetStackWeight()
local weight = self.Data.Weight or 1
return weight * self.Data.Stack
end
function Item:GetStackValue()
local value = self.Data.Value or 1
return value * self.Data.Stack
end
function Item:GetDescription()
return self.Data.Description
end
function Item:GetWeight()
return self.Data.Weight
end
function Item:GetStack()
return self.Data.Stack
end
function Item:GetType()
return self.Data.Type
end
function Item:GetRarity()
return self.Data.Rarity
end
function Item:GenerateItemUUID()
return self.Name.."_"..HttpService:GenerateGUID(false)
end
-- [[ BASE FUNCTIONS ]] --
function Item:SetInteraction(func)
self.Interaction = func
end
function Item:Interact(player: Player)
if self.Interaction then
self.Interaction(self, player)
end
end
function Item:Spawn(cframe: CFrame, owner)
assert(self.Model, "Model for object "..self.Name.." does not exist.")
self.Instance = self.Model:Clone()
self.Instance:PivotTo(cframe)
print("Spawning item at", cframe)
self.Instance:SetAttribute("OwnedBy", owner or "Neutral")
self.Instance:SetAttribute("Stack", self:GetStack())
self.Instance:SetAttribute("Weight", self:GetWeight())
self.Instance:SetAttribute("StackWeight", self:GetStackWeight())
self.Instance:SetAttribute("StackValue", self:GetStackValue())
self.Instance:SetAttribute("UUID", self:GenerateItemUUID())
for k, v in pairs(self.Data) do
if k ~= "Weight" and k ~= "Stack" and CheckValueAttributeSupport(v) then
self.Instance:SetAttribute(k, v)
end
end
for _, part in self.Instance:GetDescendants() do
if part:IsA("BasePart") then
part.CollisionGroup = "Items"
end
end
local safeParent = workspace:FindFirstChild("Game") and workspace.Game:FindFirstChild("LooseItems")
self.Instance.Parent = safeParent or workspace
if self.OnSpawn then
self.OnSpawn(self, owner)
end
return self.Instance
end
function Item:Destroy()
if self.Instance then
self.Instance:Destroy()
self.Instance = nil
end
end
return Item