How would I make this kind of system?

So basically I am bored and dicided to make a card system.

Basically where you open up a pack of cards and it gives you random one depending on there rarity.

So lets say I something like this:

local cards = {
	ProCard = {
		rarity = 30
	},
	noobCard = {
		rarity = 50
	},
	ExtremeCard = {
		rarity = 0.5
	}
}

And I want to loop through all the cards and pick on depending on its rarity.
Basically the rarer it is the less chance you will get it(Like and loot box lol)
How would I do this?

1 Like

Well, the way you want it is not the way everyone makes something like that. But it’s close. Here is a small code which will give you the results you want.

local cards = {
	ProCard = {
		rarity = 30
	},
	noobCard = {
		rarity = 50
	},
	ExtremeCard = {
		rarity = 0.5
	}
}

local Max = 100

function PickRandomCard(Max)
   local Random = math.random(1,Max)
   local Rarity,MaxRarity
   for i,v in pairs(cards) do
      if v.rarity > MaxRarity then
         MaxRarity = v.rarity
         Rarity = v
      end
   end
   return Rarity
end

The higher the rarity number, the lower the chance of getting the card

EDIT: I fixed the error I think, try now

1 Like

Well how do I get is so it will pick a random one? Depending on how rare it is?

Cause I already can get how high the number is.
I just need to know how to pick the actual card. Depending on its rarity

1 Like

It will pick a random card depending on it’s rarity. Read the code it does exactly what you want

1 Like

I played around with it for a little while.
But it is not picking doubles.

This is what I have right now:

local cards = {
	ProCard = {
		rarity = 51
	},
	noobCard = {
		rarity = 50
	},
	ExtremeCard = {
		rarity = 10
	}
}

local Max = 100

function PickRandomCard(Max)
	local Random = math.random(1,Max)
	local Rarity = 0
	local maxrarity = 0
	for i,v in pairs(cards) do
		if v.rarity > maxrarity then
			maxrarity = v.rarity
			Rarity = v
			print(Rarity,maxrarity)
			if maxrarity == cards.ExtremeCard.rarity then
				warn('LEGENDARY CARD!!')
			end
		end
	end
end


while true do
	PickRandomCard(Max)
	task.wait()
end

And basically it always prints 50 then 51. It never does 50 then 50 again, or 51 then 51. So any way to fix this?

Also dont ask why I’m making it run multiple times. I just want to see what it will give me

The difference is in this:

   if maxrarity == cards.ExtremeCard.rarity then
	warn('LEGENDARY CARD!!')
   end

You use == operator, of course it will be way too rare so use < operator instead

1 Like

Its still doing the thing where it always prints 50 then 51. And never doubles. Like 50 and 50 ETC

1 Like

You are doing loop in loop; get rid of that, and it will be better. In fact, I designed this function to be used with return. The way you made just broke the entire purpose of what I did

1 Like

Assuming you want to throw in probability distribution, we can write a function to calculate that.

local cards = {
	ProCard = 30,
	noobCard = 50,
	ExtremeCard = 0.5
}

local function createDistribution(cards)
	local distribution = {}
	local total = 0

	for card, rarity in pairs(cards) do
		total = total + rarity
	end

	local accumulate = 0
	for card, rarity in pairs(cards) do
		accumulate = accumulate + rarity
		distribution[card] = accumulate / total
	end

	return distribution
end

Then we can make a simple function to draw a card using this distribution.

local function drawCard(distribution)
	local rand = math.random()
	for card, prob in pairs(distribution) do
		if rand <= prob then
			return card
		end
	end
end

Here is the usage:

local distribution = createDistribution(cards)
local card = drawCard(distribution)
print(card)

Please do note, the higher the rarity value, the more common the card (it occupies a larger portion of the probability distribution). If you want to make higher rarity values mean a card is less common, you could invert the rarity in the createDistribution function, for example by changing total = total + rarity to total = total + 1/rarity and accumulate = accumulate + rarity to accumulate = accumulate + 1/rarity .

Here is the output:

local cards = {
	ProCard = 30,
	noobCard = 50,
	ExtremeCard = 10
}

local function createDistribution(cards)
	local distribution = {}
	local total = 0

	for card, rarity in pairs(cards) do
		total = total + rarity
	end

	local accumulate = 0
	for card, rarity in pairs(cards) do
		accumulate = accumulate + rarity
		distribution[card] = accumulate / total
	end

	return distribution
end



local function drawCard(distribution)
	local rand = math.random()
	for card, prob in pairs(distribution) do
		if rand <= prob then
			return card
		end
	end
end


local distribution = createDistribution(cards)


local dict = {}

for n = 0, 100 do
    local card = drawCard(distribution)
    if dict[card] then
        dict[card] = dict[card] + 1
    else
        dict[card] = 0
    end
end

for i, v in next, dict do
    print(i, v / 100, "\n")
end

ProCard 0.37

noobCard 0.51

ExtremeCard 0.1

2 Likes

Ok, I fixed the code. Should work fine now, sorry for the errors.

local cards = {
	ProCard = {
		rarity = 30
	},
	noobCard = {
		rarity = 50
	},
	ExtremeCard = {
		rarity = 0.5
	}
}

local Max = 100

function PickRandomCard(Max)
	local Random = math.random(1,Max)
	local Rarity
	for i,v in pairs(cards) do
		if v.rarity > Random then
			Rarity = v
		end
	end
	return Rarity
end

print(PickRandomCard(50).rarity)
2 Likes

Yeah this works perfectly.

Thank you @towerscripter1386 also for the help!

I noticed that It pulls the same card a lot sometimes. I got the noobCard 15 time lol.
I also noticed that yes it did give me 1 single extreme card so yes it is very rare lol!

2 Likes

As a side note, while it’s possible to store additional information with the card rarity (like the table structure you initially proposed), it’s generally better to keep the data structure simple and specific to its purpose to avoid unnecessary complexity. This follows the single responsibility principle, making your code more maintainable and scalable. For instance, a separate table or dictionary could be used to store the card stats, keeping the draw chance and card stats distinct.

To summarize that using code:

Not good

local cards = {
	ProCard = {
		rarity = 30
	},
	noobCard = {
		rarity = 50
	},
	ExtremeCard = {
		rarity = 0.5
	}
}

Good because it adheres to one principle and takes up less space in memory:

local cards = {
	ProCard = 30,
	noobCard = 50,
	ExtremeCard = 0.5
}

1 Like

Well yes true. the reason I did this is cause I was also going to have other values so thats why.

Also I changed it so it loops through cards in a folder in ServerStorage.
But for some reason it keeps picking the legendary card instead of picking the common card.
And I have no idea why this is happening.

heres my code.
I didnt really change anything. It still should be picking the smallest value the littlest amount of time

local cards = game:GetService('ServerStorage'):FindFirstChild('Cards'):GetChildren()
local cardFolder = game:GetService('ServerStorage'):FindFirstChild('Cards')



-- get right cards
local function createDistribution(cards)
	-- get the cards so we can draw from cards later
	local distribution = {}
	local total = 0

	for card, item in pairs(cards) do
		total = total + item.Rarity.Value
	end

	local accumulate = 0
	
	for card, item in pairs(cards) do 
		accumulate = accumulate + item.Rarity.Value
		
		local cardName = item.Name
		
		local cardDetails = {
			card = item,
			rarityValue = accumulate / total
		}
		
		distribution[cardName] = cardDetails
	end

	return distribution
end


local function drawCard(distribution) -- draw a card from the cards.
	local rand = math.random()
	for card, prob in pairs(distribution) do
		if rand <= prob.rarityValue then
			return card -- gets the card!
		end
	end
end

local function getCard()
	local distribution = createDistribution(cards)
	local card = drawCard(distribution)
	
	return card
end



local function displayCards(cards,player)
	
	local debris = game:GetService('Debris')
	
	
	local ui = player.PlayerGui:FindFirstChild('DisplayCards')
	local sf = ui:FindFirstChild('SF')
	
	local template = script:FindFirstChild('CardTemplate') -- the template of cards
	
	ui.Enabled = true
	
	
	
	for _, card in pairs(cards) do -- loop through the cards and display them
		local cardItem = cardFolder:FindFirstChild(card) -- get the card
		
		local Item = template:Clone() -- make the template
		Item.Visible = true
		
		-- set values
		Item.Rarity.Text = cardItem.Rarity.Value
		Item.ItemName.Text = cardItem.Name
		
		
		Item.Parent = sf
		debris:AddItem(Item,3)
	end
	wait(3)
	ui.Enabled = false
	
end



workspace.GetCard:FindFirstChild('Propmt').Triggered:Connect(function(player)
	local cards = {getCard(),getCard(),getCard()}
	print(cards)
	displayCards(cards,player)
end)


workspace.GetPack:FindFirstChild('Propmt').Triggered:Connect(function(player)
	local cards = {getCard(),getCard(),getCard(),getCard(),getCard(),getCard(),getCard(),getCard(),getCard(),getCard()}
	
	displayCards(cards,player)
end)

I need the place file with the cards because the code I sent earlier works.

Here ya go.
CardGameLol.rbxl (56.9 KB)
The game file.
DUN DUN DUN

1 Like

Here is the solution

CardGameLol.rbxl (57.4 KB)

1 Like

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