Creating a Crate/Spin System

Dude, awesome work! I’m kinda late, but I’m a combat / attack scripter, and I was looking to learn new things. This post is amazing man, thank you for this.

2 Likes

Hello, I need help for a similar system but with different boxes that have a certain cost and when you buy them I put the roulette and then it gives you a random item. I would like someone to help me. I don’t have to pay but I would appreciate the help (I am a designer and builder that’s why I don’t know how to program)

But what has to go in wich script? I have no idea

Late Response but I tried this and it cuts off after the first 5 boxes. I modified some of the code to match what my game should be like.

LocalScript:

-- << SERVICES >>
local players = game:GetService("Players")
local rs = game:GetService("ReplicatedStorage")
local contentProvider = game:GetService("ContentProvider")
local tweenService = game:GetService("TweenService")
local runService = game:GetService("RunService")

-- << VARIABLES >>
local player = players.LocalPlayer
local gui = player.PlayerGui.Main
local spinnerMain = gui.SpinFrame
local crates = spinnerMain.Crates
local holder = crates.Holder
local template = crates.Template
local templateX = template.AbsoluteSize.X
local crateGapX = 10
local crateTotalGapX = templateX + crateGapX
local line = spinnerMain.Line
local contents = require(rs.SpinningModule)
local itemTypes = contents:GetItems()
local spin = rs:FindFirstChild("Spin")
local spinDeb = true
local assetsToLoad = {}
local direction

local spinButton = script.Parent:WaitForChild("Spin")
local spins = player:WaitForChild("leaderstats"):WaitForChild("Spins")


-- << SETUP >>
--Setup crates
template.Visible = true
for i = 1, contents.Boxes do
	local pos = i-1
	local crate = template:Clone()
	crate.Name = "Crate"..i
	if string.lower(string.sub(contents.SpinDirection,1,1)) == "r" then
		direction = "Right"
		crate.Position = crate.Position + UDim2.new(0, crateTotalGapX*pos, 0, 0)
	else
		direction = "Left"
		crate.Position = crate.Position + UDim2.new(1, -(templateX+10)-(crateTotalGapX*pos), 0, 0)
	end
	crate.Parent = holder
end
template.Visible = false

--Preload items
coroutine.wrap(function()
	for rarityPos, group in pairs(itemTypes) do
		for i, item in pairs(group.Items) do
			local decal = Instance.new("Decal")
			decal.Texture = "rbxassetid://"..item.ImageId
			table.insert(assetsToLoad, decal)
		end
	end
	contentProvider:PreloadAsync(assetsToLoad)
	for i,v in pairs(assetsToLoad) do
		if v:IsA("Decal") then
			v:Destroy()
		end
	end
end)()

function SpinFunction(spinDetails)

	--Variables
	local items = spinDetails.Items
	local winningCrateId = spinDetails.WinningCrateId
	local winningItem = spinDetails.WinningItem
	local cratesRemaining = {}

	holder.Position = UDim2.new(0,0,0,0)
	for i, item in pairs (items) do
		local crate = holder["Crate"..i]
		local rarityGroup = itemTypes[item.GroupIndex].Rarity
		crate.About.ItemName.Text = item.Name
		crate.About.ItemRarity.Text = rarityGroup.Name
		crate.ImageLabel.Image = "rbxassetid://"..item.ImageId
		crate.About.BackgroundColor3 = rarityGroup.Color
		if i <= winningCrateId+1 then
			table.insert(cratesRemaining, crate)
		end
	end

	--Calculate distance to travel
	local endCrate = holder["Crate"..winningCrateId]
	local startX = crates.AbsolutePosition.X + crates.AbsoluteSize.X/2
	local endX = endCrate.AbsolutePosition.X + endCrate.AbsoluteSize.X/2
	local xDifference = endX - startX
	local yDifference = endCrate.AbsolutePosition.Y - holder.AbsolutePosition.Y
	local templateXBound = templateX/2.5
	local randomDepth = 1000
	local landOffset = 0--math.random(-templateXBound+randomDepth, templateXBound+randomDepth)-randomDepth
	local landPosition = holder.Position - UDim2.new(0, xDifference, 0, 0) --startCrate.Position - endCrate.Position

	--Setup tween
	local tweenTime = math.random(7, 8)
	local tweenInfo = TweenInfo.new(tweenTime, contents.SpinEasingStyle, contents.SpinEasingDirection)
	local tween = tweenService:Create(holder, tweenInfo, {Position = landPosition})

	--[[local tweenTime = math.random(7,8)
	local tweenInfo = TweenInfo.new(tweenTime, contents.SpinEasingStyle, contents.SpinEasingDirection)
	local tween = tweenService:Create(holder, tweenInfo, {Position = landPosition})]]

	--SpinClick sounds
	local lineX = line.AbsolutePosition.X
	coroutine.wrap(function()
		repeat runService.RenderStepped:Wait()
			local cratesRemainingInt = #cratesRemaining
			local nextCrate = cratesRemaining[1]
			if nextCrate then
				local nextCrateX = nextCrate.AbsolutePosition.X
				if (direction == "Right" and nextCrateX <= lineX) or (direction == "Left" and nextCrateX >= lineX) then
					table.remove(cratesRemaining, 1)
					local sound = nextCrate.SpinClick
					sound:Play()
				end
			end
		until cratesRemainingInt <= 1
	end)()

	spinnerMain.Visible = true

	--Run tween
	tween:Play()
	tween.Completed:Wait()

	--Display reward
	local winningItem = items[winningCrateId]
	local rewardColor
	local rarityGroup = itemTypes[winningItem.GroupIndex].Rarity

end

spinButton.MouseButton1Click:Connect(function()
	if spinDeb then
		spinDeb = false

		if spins.Value > 0 then
			spinButton.Visible = false
			local details, errMsg = spin:InvokeServer()
			if details then
				SpinFunction(details)
			else
				warn(errMsg)
			end
		else
			spinButton.Text = "Not enough spins!"
			task.wait(1)
			spinButton.Text = "SPIN"
		end
		
		spinButton.Visible = true
		spinDeb = true
	end
end)

ModuleScript:

local spinModule = {}

spinModule["Boxes"] = 45
spinModule["SpinEasingStyle"] = Enum.EasingStyle.Quint;
spinModule["SpinEasingDirection"] = Enum.EasingDirection.Out;
spinModule["SpinDirection"] = "Right";

function spinModule:GetItems()
	local itemTypes = {
		{
			Rarity = {Name = "Common", Chance = 0.5, Color = Color3.fromRGB(0,85,127)};
			Items = {

				{Name = "Nothing", ImageId = 2419083546};
			}

		},
		{
			Rarity = {Name = "Mythical" ,Chance = 0.5, Color = Color3.fromRGB(170,0,225)};
			Items = {

				{Name = "Secret", ImageId = 2987584671};
			}

		};
	};

	for groupIndex, group in pairs(itemTypes) do
		for i, item in pairs(group.Items) do
			item.GroupIndex = groupIndex
		end
	end

	return itemTypes
end


return spinModule

ServerScript:

--// SERVICES \\--
local rp = game:GetService("ReplicatedStorage")

--// INSTANCES \\--
local spinFunc = rp:WaitForChild("Spin")
local contents = require(rp.SpinningModule)
local chanceDepth = 100000000
local itemTypes = contents:GetItems()

--//[SCRIPT]\\--
function spinFunc.OnServerInvoke(player)
	if player.leaderstats.Spins.Value < 1 then
		return
	end

	local modifiedItemTypes = contents:GetItems()

	local items = {}
	for i = 1, contents.Boxes do
		local cumulativeChance = 0
		local rarityGroup
		local randomChance = math.random(1,chanceDepth)/chanceDepth
		for groupIndex, group in pairs(modifiedItemTypes) do
			local chance = group.Rarity.Chance
			cumulativeChance = cumulativeChance + chance
			if randomChance <= cumulativeChance and #group.Items > 0 then
				rarityGroup = group
				break
			end
		end
		local newItemsGroup = rarityGroup.Items
		local newItemPos = math.random(1,#newItemsGroup)
		local newItem = newItemsGroup[newItemPos]
		table.insert(items, newItem)
	end

	player.leaderstats.Spins.Value -= 1

	local winningCrateId = contents.Boxes - 5
	local winningItem = items[winningCrateId]
	
	print(items, winningCrateId, winningItem)

	return {
		["Items"] = items;
		["WinningCrateId"] = winningCrateId;
		["WinningItem"] = winningItem;
	}

end