Ep 32. Trading System

When completing a trade, the pets you’ve selected and traded will still appear in your Pet Inventory Gui.

When completing a trade, and then starting another trade, the Completed Frame will still be visible.

Solution

Inside the ServerScriptService/Utils/Utils module script update the PetDelete function:

ServerScriptService/Utils/Utils.lua
function Utils.PetDelete(player: Player, uuid: string)
    local pet = player.petInventory:FindFirstChild(uuid)
    if not pet then return end
    Utils.PetUnfollow(player, uuid)
    pet:Destroy()
    remotes.PetDelete:FireClient(player, uuid)
end

Inside the StarterGui/PetInventory/Manager local script add the following to the bottom of your script:

StarterGui/PetInventory/Manager.local.lua
Remotes.PetDelete.OnClientEvent:Connect(function(uuid: string)
    local petTemplateInstance = container:FindFirstChild(uuid)
    if petTemplateInstance then 
        petTemplateInstance:Destroy()
    end
end)

Here is the entire StarterGui/Trade/Manager module script:

StarterGui/Trade/Manager.lua
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Remotes = ReplicatedStorage:WaitForChild("Remotes")

local PetModels = ReplicatedStorage.Pets
local PetsHandler = require(script.Parent.Parent.PetInventory.Handler)

local player = Players.LocalPlayer

local screenGui = script.Parent 
local frame = screenGui.Frame
local container = frame.Container.Container
local title = frame.Title
local ourAvatar = frame.OurAvatar
local ourLabel = frame.OurLabel
local ourOffer = frame.OurOffer
local ourAccepted = frame.OurAccepted
local theirAvatar = frame.TheirAvatar
local theirLabel = frame.TheirLabel
local theirOffer = frame.TheirOffer
local theirAccepted = frame.TheirAccepted
local accept = frame.Accept
local decline = frame.Decline
local template = container.Template

local function UnacceptTrade()
	Remotes.TradeUnaccept:FireServer()
	ourAccepted.Visible = false
	accept.Text = "Accept"
end

local function Cleanup()
	for _, child in ipairs(ourOffer:GetChildren()) do
		if child:IsA("TextButton") then
			child:Destroy()
		end
	end
	for _, child in ipairs(theirOffer:GetChildren()) do
		if child:IsA("TextButton") then
			child:Destroy()
		end
	end
	for _, child in ipairs(container:GetChildren()) do
		if child:IsA("TextButton") and child.Visible then
			child:Destroy()
		end
	end
end

local function GeneratePet(petTable, parent: Instance)
	local clone = template:Clone()
	clone.Visible = true
	clone.Parent = parent
	clone.Name = petTable.UUID
	clone.Holder.PetName.Text = petTable.ID:gsub("_", " ")
	clone.Holder.Equipped.Visible = petTable.Equipped
	local petModel = PetModels[petTable.ID]:Clone()
	PetsHandler.GenerateViewportFrame(clone.Holder.ViewportFrame, petModel)
	return clone
end

local function SelectPet(button: TextButton, petTable)
	button:SetAttribute("Selected", true)
	button.Parent = ourOffer
	Remotes.TradeSelectPet:FireServer(petTable.UUID)
end

local function DeselectPet(button: TextButton, petTable)
	button:SetAttribute("Selected", false)
	button.Parent = container
	Remotes.TradeDeselectPet:FireServer(petTable.UUID)
end

local function GenerateOurPets()
	for _, pet in ipairs(player.petInventory:GetChildren()) do
		local petTable = PetsHandler.PetInstanceToTable(pet)
		local pet = GeneratePet(petTable, container)
		pet:SetAttribute("Selected", false)
		pet.MouseButton1Click:Connect(function()
			UnacceptTrade()
			if pet:GetAttribute("Selected") then
				DeselectPet(pet, petTable)
			else
				SelectPet(pet, petTable)
			end
		end)
	end
end

local function TraderSelectsPet(petTable)
	GeneratePet(petTable, theirOffer)
end

local function TraderDeselectsPet(uuid)
	local instance = theirOffer:FindFirstChild(uuid)
	if instance then
		instance:Destroy()
	end
end

local function UpdateLabels(target: Player)
	title.Text = "TRADE WITH "..target.DisplayName
	theirLabel.Text = target.DisplayName.."'s Offer"
	theirAvatar.Image = Players:GetUserThumbnailAsync(target.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size60x60)
end

local function BeginTrade(targetId: number)
	local target = Players:GetPlayerByUserId(targetId)
	if not target then return end
	
	GenerateOurPets()
	UpdateLabels(target)
	
	ourAccepted.Visible = false
	theirAccepted.Visible = false
	
	screenGui.Enabled = true
end

local function Startup()
	ourLabel.Text = player.DisplayName.."'s Offer"
	ourAvatar.Image = Players:GetUserThumbnailAsync(player.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size60x60)
end

Startup()

local function Decline()
	Cleanup()
	screenGui.Enabled = false
end

local function TraderAccepts()
	theirAccepted.Visible = true
end

local function TraderUnaccepts()
	theirAccepted.Visible = false
end

accept.MouseButton1Click:Connect(function()
	if accept.Text == "Accept" then
		Remotes.TradeAccept:FireServer()
		ourAccepted.Visible = true
		accept.Text = "Unaccept"
	else
		UnacceptTrade()
	end
end)

decline.MouseButton1Click:Connect(function()
	Remotes.TradeDeclined:FireServer()
	Decline()
end)

Remotes.TradeBegan.OnClientEvent:Connect(BeginTrade)
Remotes.TradeDeclined.OnClientEvent:Connect(Decline)
Remotes.TradeSelectPet.OnClientEvent:Connect(TraderSelectsPet)
Remotes.TradeDeselectPet.OnClientEvent:Connect(TraderDeselectsPet)
Remotes.TradeAccept.OnClientEvent:Connect(TraderAccepts)
Remotes.TradeUnaccept.OnClientEvent:Connect(TraderUnaccepts)
Remotes.TradeCompleted.OnClientEvent:Connect(Decline)
EXPAND

Here in the entire ServerScriptService/Trade server script:

ServerScriptService/Trade.lua
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")

local Rewards = require(ServerScriptService.Utils.Rewards)
local Utils = require(ServerScriptService.Utils.Utils)

local Remotes = ReplicatedStorage.Remotes

local TradeAccepted = {}
local TradeSelected = {}
local TradeRequests = {}
local Trading = {}

local function IsTrading(player: Player)
	return Trading[player.UserId]
end

local function IsOnCooldown(playerId, targetId)
	local requests = TradeRequests[playerId]
	if not requests then return end
	return table.find(requests, targetId)
end

local function HasSentRequest(accepterID: number, senderID: number)
	local requestTable = TradeRequests[senderID]
	if not requestTable then return end
	if table.find(requestTable, accepterID) then
		return true
	end
	return false
end

local function SendTradeRequest(player: Player, targetID: number)
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	if IsOnCooldown(player.UserId, targetID) then return end
	
	local playerTable = TradeRequests[player.UserId]
	if not playerTable then
		TradeRequests[player.UserId] = {}
		playerTable = TradeRequests[player.UserId]
	end
	
	Remotes.TradeRequestReceived:FireClient(target, player)
	table.insert(playerTable, targetID)
	task.delay(15, function()
		local index = table.find(TradeRequests[player.UserId], targetID)
		table.remove(TradeRequests[player.UserId], index)
	end)
	return true
end

local function AcceptTradeReuqest(player: Player, targetID: number)
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	if IsTrading(player) or IsTrading(target) then return end
	if not HasSentRequest(player.UserId, targetID) then return end
	
	Trading[player.UserId] = targetID
	Trading[targetID] = player.UserId
	Remotes.TradeBegan:FireClient(player, targetID)
	Remotes.TradeBegan:FireClient(target, player.UserId)
end

local function TradeDeclined(player: Player)
	local targetID = IsTrading(player)
	
	TradeAccepted[player.UserId] = nil
	TradeRequests[player.UserId] = nil
	Trading[player.UserId] = nil
	if not targetID then return end
	
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	
	TradeAccepted[targetID] = nil
	TradeRequests[targetID] = nil
	Trading[targetID] = nil
	
	Remotes.TradeDeclined:FireClient(target, player.UserId)	
end

local function TradeSelectPet(player: Player, uuid: string)
	local targetID = IsTrading(player)
	if not targetID then return end
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	local petInstance = player.petInventory:FindFirstChild(uuid)
	if not petInstance then return end
	
	local petTable = {}
	petTable.UUID = petInstance.Name
	petTable.ID = petInstance.ID.Value
	petTable.Rarity = petInstance.Rarity.Value
	local selectedPets = TradeSelected[player.UserId]
	if not selectedPets then
		TradeSelected[player.UserId] = {}
	end
	
	table.insert(TradeSelected[player.UserId], uuid)
	
	Remotes.TradeSelectPet:FireClient(target, petTable)
end

local function TradeDeselectPet(player: Player, uuid: string)
	local targetID = IsTrading(player)
	if not targetID then return end
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	local petInstance = player.petInventory:FindFirstChild(uuid)
	if not petInstance then return end
	local selectedPets = TradeSelected[player.UserId]
	if not selectedPets then return end
	
	local pet = table.find(selectedPets, uuid)
	if not pet then return end
	
	table.remove(selectedPets, pet)
	Remotes.TradeDeselectPet:FireClient(target, uuid)
end

local function CompleteTrade(player: Player, target: Player)
	local playerPets = TradeSelected[player.UserId]
	local targetPets = TradeSelected[target.UserId]
	
	if playerPets then
		for _, uuid in ipairs(playerPets) do
			local petInstance = player.petInventory:FindFirstChild(uuid)
			Rewards.Pet(target, petInstance.ID.Value, petInstance.Rarity.Value)
			Utils.PetDelete(player, uuid)
		end
		TradeSelected[player.UserId] = nil
	end
	
	if targetPets then
		for _, uuid in ipairs(targetPets) do
			local petInstance = target.petInventory:FindFirstChild(uuid)
			Rewards.Pet(player, petInstance.ID.Value, petInstance.Rarity.Value)
			Remotes.PetHatched:FireClient(player, nil, petInstance)
			Utils.PetDelete(target, uuid)
		end
		TradeSelected[target.UserId] = nil
	end
	
	TradeAccepted[player.UserId] = nil
	TradeRequests[player.UserId] = nil
	Trading[player.UserId] = nil
	
	TradeAccepted[target.UserId] = nil
	TradeRequests[target.UserId] = nil
	Trading[target.UserId] = nil
	
	Remotes.TradeCompleted:FireClient(player)
	Remotes.TradeCompleted:FireClient(target)
end

local function TradeAccept(player: Player)
	local targetID = IsTrading(player)
	if not targetID then return end
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	
	TradeAccepted[player.UserId] = true
	local targetAcceptance = TradeAccepted[targetID]
	if targetAcceptance and targetAcceptance == true then
		CompleteTrade(player, target)
	end
	Remotes.TradeAccept:FireClient(target)
end

local function TradeUnaccept(player: Player)
	local targetID = IsTrading(player)
	if not targetID then return end
	local target = Players:GetPlayerByUserId(targetID)
	if not target then return end
	
	local acceptance = TradeAccepted[player.UserId]
	if acceptance then
		TradeAccepted[player.UserId] = nil
	end
	
	Remotes.TradeUnaccept:FireClient(target)
end

Players.PlayerRemoving:Connect(TradeDeclined)
Remotes.SendTradeRequest.OnServerInvoke = SendTradeRequest
Remotes.AcceptTradeRequest.OnServerEvent:Connect(AcceptTradeReuqest)
Remotes.TradeDeclined.OnServerEvent:Connect(TradeDeclined)
Remotes.TradeSelectPet.OnServerEvent:Connect(TradeSelectPet)
Remotes.TradeDeselectPet.OnServerEvent:Connect(TradeDeselectPet)
Remotes.TradeAccept.OnServerEvent:Connect(TradeAccept)
Remotes.TradeUnaccept.OnServerEvent:Connect(TradeUnaccept)
EXPAND
Contents