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
2 Likes

Anyone looking to recreate this, I highly recommend downloading their place file and comparing as what’s in the forum post and place file are different, and then incorporating it using ScrollingFrames, and using AbsoluteValue for the spin sound. Their code is outdated but the concepts are not, very helpful thanks!

Hello,

Nice to hear a 2019 post still has value.

Did you want to share your modifications?

Thanks!

Very very excellent, can you tell me the secret of how to increase the ideas and logic when you want to make a project like when you wanted to make a spinner, you said To find the mid-point of a UI object, divide it’s X and Y size values ​​by 2 then add these to the absolute position, Calculate the distance between the center-line and the mid-point of the 40th [spinDetails.WinningCrateId] crate, then subtract this from the holder’s position to get the ‘land position’. How did you come up with these ideas? It did not occur to me to calculate the distance, and so I did not even understand or comprehend what you did. Please explain how to increase the logic,brainthinking and reason in the ideas of making projects. This will help me a lot.( pls i need this answer so much )(I miss this part a lot and this makes me unable to do any project despite my mastery of the basics and reading the documents. The idea is the idea of ​​how to do the project like what i explained above what i mean with idea . The solutuion pls