Inventory System for my Game

Hello!

I believe this is my fifth post here. Anyways, I want to try attempt to make an inventory system for my Hello Neighbor type game. (don’t worry the game isn’t identical). I have code written in my local script and server script.

Let’s also begin with the Gui.

image
Here’s the Gui.
image
Here’s the Gui’s descendants.
I’m trying to make it so when you equip a tool, it’s put in the WorldModel, and each time you unequip a tool, its removed from the WorldModel and put in the workspace.Tools folder.

Now, lets go to the server script.

Here’s the EquipTools script. It’s located in StarterCharacterScripts

local tvt = script.Parent:WaitForChild("ThrowObjectEvent")
local evt = script.Parent:WaitForChild("EquipToolsEvent")
local etp = script.Parent:WaitForChild("EquippedToolPos")
local eqt

local function putToolInInv(plr, tool, slot)
	local gui = plr.PlayerGui.Inventory
	local slots = {gui.Slot1, gui.Slot2, gui.Slot3, gui.Slot4}

	local clonedTool = tool:Clone()
	clonedTool.Parent = slots[slot]
	clonedTool.CFrame = CFrame.new(0, 0, 0)
	clonedTool.Anchored = true
end

local function equipTool(plr, mse, tgt, hit)
	if tgt.Parent.Name == "Tools" and etp.Parent.ToolEquipped.Value == false and (plr.Character.HumanoidRootPart.Position - hit.p).magnitude <= 8 then
		local tool = tgt
		local weld = Instance.new("WeldConstraint", etp)
		eqt = tool
		etp.Parent.ToolEquipped.Value = true
		tool.Parent = etp
		tool.CFrame = etp.CFrame
		weld.Part0 = etp
		weld.Part1 = tool

		for i, v in pairs(tool:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = false
			end
		end
		
		putToolInInv(plr, tool, 1)
	end
end

evt.OnServerEvent:Connect(function(plr, mse, tgt, hit)
	if tgt then
		equipTool(plr, mse, tgt, hit)
	end

	plr.Character.Humanoid.Died:Connect(function()
		if etp.Parent.ToolEquipped.Value == true then
			for i, v in pairs(etp:GetChildren()) do
				if v:IsA("BasePart") or v:IsA("MeshPart") then
					v.Parent = workspace.Tools
					for i, v in pairs(eqt:GetDescendants()) do
						if v:IsA("MeshPart") or v:IsA("BasePart") then
							v.CanCollide = true
						end
					end
					eqt = nil
					etp:ClearAllChildren()
					etp.Parent.ToolEquipped.Value = false
				end
			end
		else
			etp:ClearAllChildren()
		end
	end)
end)

tvt.OnServerEvent:Connect(function(plr, mse, tgt, hit, thw)
	if thw and etp.Parent.ToolEquipped.Value == true then
		eqt.Parent = workspace.Tools
		etp:ClearAllChildren()

		for _, v in pairs(eqt:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = true
			end
		end

		local throwDirection = (hit.p - etp.Position).unit

		local bodyVelocity = Instance.new("BodyVelocity")
		bodyVelocity.Velocity = throwDirection * 48
		bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
		bodyVelocity.P = 5000

		bodyVelocity.Parent = eqt

		task.wait(.1)

		bodyVelocity:Destroy()

		eqt = nil
		etp.Parent.ToolEquipped.Value = false
	elseif etp.Parent.ToolEquipped.Value == true then
		eqt.Parent = workspace.Tools
		etp:ClearAllChildren()
		for i, v in pairs(eqt:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = true
			end
		end
		eqt = nil
		etp.Parent.ToolEquipped.Value = false
	end
end)

Now the local script, also located in StarterCharacterScripts

local plr = game:GetService("Players").LocalPlayer
local uis = game:GetService("UserInputService")
local mse = plr:GetMouse()

local tse = script.Parent.InventoryEvent

local ticket = 0
local timeHeld = .4
local isHolding = false

local debugMode = workspace.debugMode.Value

function btnHeld()
	isHolding = true
end

function btnRels()
	isHolding = false
	script.Parent.ThrowObjectEvent:FireServer(mse, mse.Target, mse.Hit, false)
end

uis.InputBegan:Connect(function(i, g)
	if not g then
		if i.KeyCode == Enum.KeyCode.F or i.KeyCode == Enum.KeyCode.ButtonY then	
			script.Parent.EquipToolsEvent:FireServer(mse, mse.Target, mse.Hit)
		elseif i.UserInputType == Enum.UserInputType.MouseButton2 or i.KeyCode == Enum.KeyCode.ButtonX then
			ticket = ticket + 1
			local currentTicket = ticket

			if timeHeld > 0 then
				delay(timeHeld, function()
					if ticket == currentTicket then
						btnHeld()
						script.Parent.ThrowObjectEvent:FireServer(mse, mse.Target, mse.Hit, true)
					end
				end)
			else
				btnHeld()
			end
		elseif i.KeyCode == Enum.KeyCode.One then
			tse:FireServer(1)
		elseif i.KeyCode == Enum.KeyCode.Two then
			tse:FireServer(2)
		elseif i.KeyCode == Enum.KeyCode.Three then
			tse:FireServer(3)
		elseif i.KeyCode == Enum.KeyCode.Four then
			tse:FireServer(4)
		end
	end
end)

uis.InputEnded:Connect(function(i, g)
	if not g then
		if i.UserInputType == Enum.UserInputType.MouseButton2 or i.KeyCode == Enum.KeyCode.ButtonX then
			ticket = ticket + 1
			btnRels()
		end
	end
end)

Alright, here’s a summary of what I would like.

1, 2, 3 and 4 on the keyboard change between equipped tools. (same with the scrollwheel if possible)
The tools are stored in workspace.Tools

That’s pretty much it. Would be nice if you guys on the Forum helped me out!

1 Like

What have you tried achieving so far when creating the Inventory System? What problems have you not been able to get across/around? The reason I am saying this, is because we aren’t supposed create brand new scripts for you, unfortunately.

Do you currently have a script that you can show us so far, for the Inventory System?

I’ve tried to make it so the tools automatically go into the inventory system. this doesn’t work and i don’t know how i can make it work

Can you share us the script for that; if you haven’t done it already?

I have,

local tvt = script.Parent:WaitForChild("ThrowObjectEvent")
local evt = script.Parent:WaitForChild("EquipToolsEvent")
local etp = script.Parent:WaitForChild("EquippedToolPos")
local eqt

local function putToolInInv(plr, tool, slot)
	local gui = plr.PlayerGui.Inventory
	local slots = {gui.Slot1, gui.Slot2, gui.Slot3, gui.Slot4}

	local clonedTool = tool:Clone()
	clonedTool.Parent = slots[slot]
	clonedTool.CFrame = CFrame.new(0, 0, 0)
	clonedTool.Anchored = true
end

local function equipTool(plr, mse, tgt, hit)
	if tgt.Parent.Name == "Tools" and etp.Parent.ToolEquipped.Value == false and (plr.Character.HumanoidRootPart.Position - hit.p).magnitude <= 8 then
		local tool = tgt
		local weld = Instance.new("WeldConstraint", etp)
		eqt = tool
		etp.Parent.ToolEquipped.Value = true
		tool.Parent = etp
		tool.CFrame = etp.CFrame
		weld.Part0 = etp
		weld.Part1 = tool

		for i, v in pairs(tool:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = false
			end
		end
		
		putToolInInv(plr, tool, 1)
	end
end

evt.OnServerEvent:Connect(function(plr, mse, tgt, hit)
	if tgt then
		equipTool(plr, mse, tgt, hit)
	end

	plr.Character.Humanoid.Died:Connect(function()
		if etp.Parent.ToolEquipped.Value == true then
			for i, v in pairs(etp:GetChildren()) do
				if v:IsA("BasePart") or v:IsA("MeshPart") then
					v.Parent = workspace.Tools
					for i, v in pairs(eqt:GetDescendants()) do
						if v:IsA("MeshPart") or v:IsA("BasePart") then
							v.CanCollide = true
						end
					end
					eqt = nil
					etp:ClearAllChildren()
					etp.Parent.ToolEquipped.Value = false
				end
			end
		else
			etp:ClearAllChildren()
		end
	end)
end)

tvt.OnServerEvent:Connect(function(plr, mse, tgt, hit, thw)
	if thw and etp.Parent.ToolEquipped.Value == true then
		eqt.Parent = workspace.Tools
		etp:ClearAllChildren()

		for _, v in pairs(eqt:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = true
			end
		end

		local throwDirection = (hit.p - etp.Position).unit

		local bodyVelocity = Instance.new("BodyVelocity")
		bodyVelocity.Velocity = throwDirection * 48
		bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
		bodyVelocity.P = 5000

		bodyVelocity.Parent = eqt

		task.wait(.1)

		bodyVelocity:Destroy()

		eqt = nil
		etp.Parent.ToolEquipped.Value = false
	elseif etp.Parent.ToolEquipped.Value == true then
		eqt.Parent = workspace.Tools
		etp:ClearAllChildren()
		for i, v in pairs(eqt:GetDescendants()) do
			if v:IsA("MeshPart") or v:IsA("BasePart") then
				v.CanCollide = true
			end
		end
		eqt = nil
		etp.Parent.ToolEquipped.Value = false
	end
end)

Any reason in particular you’re doing this on the server? (The gui part)
My best suggestion would be to fire a RemoteEvent to the player with the model, and do the rest of the calculations (slot, clonedtool, etc.) on the client.

:slight_smile:

1 Like

Not sure actually, might move it to the client. I don’t know if I’ll be using images instead of viewports. Either way, I have no clue on how to make the tools in the inventory equippable, nor do I know where to store the tools once they are in the inventory. Might use the Player’s backpack but I’m not sure yet.

Viewports could probably be easier as it only involves duplicating the tool and placing the tool inside the player’s Inventory UI, without having to do any other extra work.

In terms of storing the tools, you would already have the tools being stored inside the player’s Inventory UI, so it might not be necessary to store them anywhere else?

I rather store them elsewhere to make it easier to access incase the player dies.

1 Like

As far as I am aware of, the tools wouldn’t be removed when the player dies if they are located within the player’s Inventory UI? Correct me if I am wrong though.

Definitely going to want to move it to the client.

Anyway, if you want to make sure the player won’t lose tools on death, store the tools they have in a folder somewhere the player cannot access like ServerStorage, and when they click xyz number equip xyz tool.

(Make sure to set ResetOnSpawn to false in the gui!)

1 Like

Also don’t know if I want to move it to the client because I might add a 2 player mode.

What you seem to be trying to do here, is trying to run before you can walk. Right now, you should probably follow the advice to separate some of your current server-sided code (regarding the duplication of the tool inside one of the player’s viewports, inside the related player’s UI), into a separate Localscript.


Instead of having to copy the tools from the workspace, you could duplicate all possibly obtainable tools within your game and place them inside ReplicatedStorage. The server can send any player-related information and what tool they have picked up through using a RemoteEvent to the client, and the client can then duplicate that same tool with the same name, inside ReplicatedStorage and place it inside one of the viewports, inside the player’s Inventory UI. That was just an idea though, as yours could eventually look very different.

Even if the exploiters try to manipulate the data being sent to the client through the RemoteEvent, it won’t do much other than placing tools inside the player’s UI, but this doesn’t mean that they will automatically receive the tool though, meaning that this concern wouldn’t really matter so I believe this would be safe.

I’ll try to recreate this idea, thanks!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.